[nautilus/wip/antoniof/new-list-view-without-expanders: 12/23] view-icon-controller: Abstract sharable code




commit 9927698149081cff443a2f105c10c5efe6510bad
Author: António Fernandes <antoniof gnome org>
Date:   Mon Jun 6 11:31:12 2022 +0100

    view-icon-controller: Abstract sharable code
    
    The new list view is going to be GtkColumnView-based, so it's going to
    share some code with the GtkGridView-based view.
    
    In order to avoid code duplication and still keep the NautilusFilesView
    class agnostic of the widgets used by final classes, create an abstract
    NautilusListBase class.
    
    But this abstract class needs to interact with the item widgets, which
    are going to be different between views. To resolve this, an abstract
    NautilusViewCell class is created for the item widgets, which is also
    going to be used for the new list view column cells.
    
    Also, bump GLib version requirement now that we use GSignalGroup.

 meson.build                                    |    2 +-
 src/meson.build                                |    4 +
 src/nautilus-list-base-private.h               |   33 +
 src/nautilus-list-base.c                       | 1321 ++++++++++++++++++++++++
 src/nautilus-list-base.h                       |   27 +
 src/nautilus-types.h                           |    3 +-
 src/nautilus-view-cell.c                       |  189 ++++
 src/nautilus-view-cell.h                       |   31 +
 src/nautilus-view-icon-controller.c            | 1271 ++---------------------
 src/nautilus-view-icon-controller.h            |    5 +-
 src/nautilus-view-icon-item-ui.c               |  197 +---
 src/nautilus-view-icon-item-ui.h               |   10 +-
 src/nautilus-view-model.c                      |    8 +-
 src/nautilus-view-model.h                      |    1 +
 src/resources/ui/nautilus-view-icon-item-ui.ui |    2 +-
 15 files changed, 1727 insertions(+), 1377 deletions(-)
---
diff --git a/meson.build b/meson.build
index 154c7b763..7af5a3ef0 100644
--- a/meson.build
+++ b/meson.build
@@ -92,7 +92,7 @@ pkgconfig = import('pkgconfig')
 ################
 # Dependencies #
 ################
-glib_ver = '>= 2.67.1'
+glib_ver = '>= 2.72.0'
 
 libm = cc.find_library('m')
 
diff --git a/src/meson.build b/src/meson.build
index 35a0dce98..3525c2f7c 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -83,6 +83,8 @@ libnautilus_sources = [
   'nautilus-floating-bar.h',
   'nautilus-freedesktop-dbus.c',
   'nautilus-freedesktop-dbus.h',
+  'nautilus-list-base.c',
+  'nautilus-list-base.h',
   'nautilus-list-model.c',
   'nautilus-list-model.h',
   'nautilus-list-view.c',
@@ -122,6 +124,8 @@ libnautilus_sources = [
   'nautilus-trash-bar.h',
   'nautilus-view.c',
   'nautilus-view.h',
+  'nautilus-view-cell.c',
+  'nautilus-view-cell.h',
   'nautilus-view-icon-controller.c',
   'nautilus-view-icon-controller.h',
   'nautilus-view-icon-item-ui.c',
diff --git a/src/nautilus-list-base-private.h b/src/nautilus-list-base-private.h
new file mode 100644
index 000000000..96944d52e
--- /dev/null
+++ b/src/nautilus-list-base-private.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The GNOME project contributors
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "nautilus-list-base.h"
+#include "nautilus-view-model.h"
+#include "nautilus-view-cell.h"
+
+/*
+ * Private header to be included only by subclasses.
+ */
+
+G_BEGIN_DECLS
+
+/* Methods */
+NautilusViewModel *nautilus_list_base_get_model     (NautilusListBase *self);
+void               nautilus_list_base_set_icon_size (NautilusListBase *self,
+                                                            gint                    icon_size);
+void               nautilus_list_base_setup_gestures (NautilusListBase *self);
+
+/* Shareable helpers */
+void                          set_directory_sort_metadata       (NautilusFile *file,
+                                                                 const gchar  *metadata_name,
+                                                                 gboolean      reversed);
+const NautilusFileSortType    get_sorts_type_from_metadata_text (const char   *metadata_name);
+void                          setup_cell_common                 (GtkListItem      *listitem,
+                                                                 NautilusViewCell *cell);
+
+G_END_DECLS
diff --git a/src/nautilus-list-base.c b/src/nautilus-list-base.c
new file mode 100644
index 000000000..4d35df869
--- /dev/null
+++ b/src/nautilus-list-base.c
@@ -0,0 +1,1321 @@
+/*
+ * Copyright (C) 2022 The GNOME project contributors
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "nautilus-list-base-private.h"
+
+#include "nautilus-clipboard.h"
+#include "nautilus-view-cell.h"
+#include "nautilus-view-item-model.h"
+#include "nautilus-view-model.h"
+#include "nautilus-files-view.h"
+#include "nautilus-file.h"
+#include "nautilus-metadata.h"
+#include "nautilus-global-preferences.h"
+#include "nautilus-thumbnails.h"
+
+/**
+ * NautilusListBase:
+ *
+ * Abstract class containing shared code for #NautilusFilesView implementations
+ * using a #GtkListBase-derived widget (e.g. GtkGridView, GtkColumnView) which
+ * takes a #NautilusViewModel instance as its model and and a #NautilusViewCell
+ * instance as #GtkListItem:child.
+ *
+ * It has been has been created to avoid code duplication in implementations,
+ * while keeping #NautilusFilesView implementation-agnostic (should the need for
+ * non-#GtkListBase views arise).
+ */
+
+typedef struct _NautilusListBasePrivate NautilusListBasePrivate;
+struct _NautilusListBasePrivate
+{
+    NautilusViewModel *model;
+
+    GList *cut_files;
+
+    guint scroll_to_file_handle_id;
+    guint prioritize_thumbnailing_handle_id;
+    GtkAdjustment *vadjustment;
+
+    gboolean single_click_mode;
+    gboolean activate_on_release;
+    gboolean deny_background_click;
+};
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (NautilusListBase, nautilus_list_base, NAUTILUS_TYPE_FILES_VIEW)
+
+typedef struct
+{
+    const NautilusFileSortType sort_type;
+    const gchar *metadata_name;
+} SortConstants;
+
+static const SortConstants sorts_constants[] =
+{
+    {
+        NAUTILUS_FILE_SORT_BY_DISPLAY_NAME,
+        "name",
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_SIZE,
+        "size",
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_TYPE,
+        "type",
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_MTIME,
+        "modification date",
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_ATIME,
+        "access date",
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_BTIME,
+        "creation date",
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_TRASHED_TIME,
+        "trashed",
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_SEARCH_RELEVANCE,
+        "search_relevance",
+    },
+    {
+        NAUTILUS_FILE_SORT_BY_RECENCY,
+        "recency",
+    },
+};
+
+static inline NautilusViewItemModel *
+get_view_item (GListModel *model,
+               guint       position)
+{
+    return NAUTILUS_VIEW_ITEM_MODEL (g_list_model_get_item (model, position));
+}
+
+static const SortConstants *
+get_sorts_constants_from_sort_type (NautilusFileSortType sort_type)
+{
+    guint i;
+
+    for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++)
+    {
+        if (sort_type == sorts_constants[i].sort_type)
+        {
+            return &sorts_constants[i];
+        }
+    }
+
+    return &sorts_constants[0];
+}
+
+static const SortConstants *
+get_sorts_constants_from_metadata_text (const char *metadata_name)
+{
+    guint i;
+
+    for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++)
+    {
+        if (g_strcmp0 (sorts_constants[i].metadata_name, metadata_name) == 0)
+        {
+            return &sorts_constants[i];
+        }
+    }
+
+    return &sorts_constants[0];
+}
+
+const NautilusFileSortType
+get_sorts_type_from_metadata_text (const char *metadata_name)
+{
+    return get_sorts_constants_from_metadata_text (metadata_name)->sort_type;
+}
+
+static const SortConstants *
+get_default_sort_order (NautilusFile *file,
+                        gboolean     *reversed)
+{
+    NautilusFileSortType sort_type;
+
+    sort_type = nautilus_file_get_default_sort_type (file, reversed);
+
+    return get_sorts_constants_from_sort_type (sort_type);
+}
+
+static const SortConstants *
+get_directory_sort_by (NautilusFile *file,
+                       gboolean     *reversed)
+{
+    const SortConstants *default_sort;
+    g_autofree char *sort_by = NULL;
+
+    default_sort = get_default_sort_order (file, reversed);
+    g_return_val_if_fail (default_sort != NULL, NULL);
+
+    sort_by = nautilus_file_get_metadata (file,
+                                          NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY,
+                                          default_sort->metadata_name);
+
+    *reversed = nautilus_file_get_boolean_metadata (file,
+                                                    NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED,
+                                                    *reversed);
+
+    return get_sorts_constants_from_metadata_text (sort_by);
+}
+
+void
+set_directory_sort_metadata (NautilusFile *file,
+                             const gchar  *metadata_name,
+                             gboolean      reversed)
+{
+    const SortConstants *default_sort;
+    gboolean default_reversed;
+
+    default_sort = get_default_sort_order (file, &default_reversed);
+
+    nautilus_file_set_metadata (file,
+                                NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY,
+                                default_sort->metadata_name,
+                                metadata_name);
+    nautilus_file_set_boolean_metadata (file,
+                                        NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED,
+                                        default_reversed,
+                                        reversed);
+}
+
+static void
+update_sort_order_from_metadata_and_preferences (NautilusListBase *self)
+{
+    const SortConstants *default_directory_sort;
+    GActionGroup *view_action_group;
+    gboolean reversed;
+
+    default_directory_sort = get_directory_sort_by (nautilus_files_view_get_directory_as_file 
(NAUTILUS_FILES_VIEW (self)),
+                                                    &reversed);
+    view_action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self));
+    g_action_group_change_action_state (view_action_group,
+                                        "sort",
+                                        g_variant_new ("(sb)",
+                                                       default_directory_sort->metadata_name,
+                                                       reversed));
+}
+
+void
+nautilus_list_base_set_icon_size (NautilusListBase *self,
+                                  gint              icon_size)
+{
+    GListModel *model;
+    guint n_items;
+
+    model = G_LIST_MODEL (nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self)));
+
+    n_items = g_list_model_get_n_items (model);
+    for (guint i = 0; i < n_items; i++)
+    {
+        g_autoptr (NautilusViewItemModel) current_item_model = NULL;
+
+        current_item_model = get_view_item (model, i);
+        nautilus_view_item_model_set_icon_size (current_item_model, icon_size);
+    }
+}
+
+/* GtkListBase changes selection only with the primary button, and only after
+ * release. But we need to antecipate selection earlier if we are to activate it
+ * or open its context menu. This helper should be used in these situations if
+ * it's desirable to act on a multi-item selection, because it preserves it. */
+static void
+select_single_item_if_not_selected (NautilusListBase      *self,
+                                    NautilusViewItemModel *item)
+{
+    NautilusViewModel *model;
+    guint position;
+
+    model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self));
+    position = nautilus_view_model_get_index (model, item);
+    if (!gtk_selection_model_is_selected (GTK_SELECTION_MODEL (model), position))
+    {
+        gtk_selection_model_select_item (GTK_SELECTION_MODEL (model), position, TRUE);
+    }
+}
+
+static void
+activate_selection_on_click (NautilusListBase *self,
+                             gboolean          open_in_new_tab)
+{
+    g_autolist (NautilusFile) selection = NULL;
+    NautilusOpenFlags flags = 0;
+    NautilusFilesView *files_view = NAUTILUS_FILES_VIEW (self);
+
+    selection = nautilus_view_get_selection (NAUTILUS_VIEW (self));
+    if (open_in_new_tab)
+    {
+        flags |= NAUTILUS_OPEN_FLAG_NEW_TAB;
+        flags |= NAUTILUS_OPEN_FLAG_DONT_MAKE_ACTIVE;
+    }
+    nautilus_files_view_activate_files (files_view, selection, flags, TRUE);
+}
+
+static void
+on_item_click_pressed (GtkGestureClick *gesture,
+                       gint             n_press,
+                       gdouble          x,
+                       gdouble          y,
+                       gpointer         user_data)
+{
+    NautilusViewCell *cell = user_data;
+    NautilusListBase *self = NAUTILUS_LIST_BASE (nautilus_view_cell_get_view (cell));
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    NautilusViewItemModel *item;
+    guint button;
+    GdkModifierType modifiers;
+    gboolean selection_mode;
+
+    item = nautilus_view_cell_get_item (cell);
+    g_return_if_fail (item != NULL);
+    button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
+    modifiers = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (gesture));
+    selection_mode = (modifiers & (GDK_CONTROL_MASK | GDK_SHIFT_MASK));
+
+    /* Before anything else, store event state to be read by other handlers. */
+    priv->deny_background_click = TRUE;
+    priv->activate_on_release = (priv->single_click_mode &&
+                                 button == GDK_BUTTON_PRIMARY &&
+                                 n_press == 1 &&
+                                 !selection_mode);
+
+    /* It's safe to claim event sequence on press in the following cases because
+     * they don't interfere with touch scrolling. */
+    if (button == GDK_BUTTON_PRIMARY && n_press == 2 && !priv->single_click_mode)
+    {
+        activate_selection_on_click (self, modifiers & GDK_SHIFT_MASK);
+        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+    }
+    else if (button == GDK_BUTTON_MIDDLE && n_press == 1)
+    {
+        /* Antecipate selection, if necessary, to activate it. */
+        select_single_item_if_not_selected (self, item);
+        activate_selection_on_click (self, TRUE);
+        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+    }
+    else if (button == GDK_BUTTON_SECONDARY && n_press == 1)
+    {
+        gdouble view_x, view_y;
+
+        /* Antecipate selection, if necessary, for the context menu. */
+        select_single_item_if_not_selected (self, item);
+
+        gtk_widget_translate_coordinates (GTK_WIDGET (cell), GTK_WIDGET (self),
+                                          x, y,
+                                          &view_x, &view_y);
+        nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (self),
+                                                           view_x, view_y);
+        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+    }
+}
+
+static void
+on_item_click_released (GtkGestureClick *gesture,
+                        gint             n_press,
+                        gdouble          x,
+                        gdouble          y,
+                        gpointer         user_data)
+{
+    NautilusViewCell *cell = user_data;
+    NautilusListBase *self = NAUTILUS_LIST_BASE (nautilus_view_cell_get_view (cell));
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+
+    if (priv->activate_on_release)
+    {
+        NautilusViewModel *model;
+        NautilusViewItemModel *item;
+        guint i;
+
+        model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self));
+        item = nautilus_view_cell_get_item (cell);
+        g_return_if_fail (item != NULL);
+        i = nautilus_view_model_get_index (model, item);
+
+        /* Antecipate selection, enforcing single selection of target item. */
+        gtk_selection_model_select_item (GTK_SELECTION_MODEL (model), i, TRUE);
+
+        activate_selection_on_click (self, FALSE);
+    }
+
+    priv->activate_on_release = FALSE;
+    priv->deny_background_click = FALSE;
+}
+
+static void
+on_item_click_stopped (GtkGestureClick *gesture,
+                       gpointer         user_data)
+{
+    NautilusViewCell *cell = user_data;
+    NautilusListBase *self = NAUTILUS_LIST_BASE (nautilus_view_cell_get_view (cell));
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+
+    priv->activate_on_release = FALSE;
+    priv->deny_background_click = FALSE;
+}
+
+static void
+on_view_click_pressed (GtkGestureClick *gesture,
+                       gint             n_press,
+                       gdouble          x,
+                       gdouble          y,
+                       gpointer         user_data)
+{
+    NautilusListBase *self = user_data;
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    guint button;
+    GdkModifierType modifiers;
+    gboolean selection_mode;
+
+    if (priv->deny_background_click)
+    {
+        /* Item was clicked. */
+        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
+        return;
+    }
+
+    /* Don't interfere with GtkListBase default selection handling when
+     * holding Ctrl and Shift. */
+    modifiers = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (gesture));
+    selection_mode = (modifiers & (GDK_CONTROL_MASK | GDK_SHIFT_MASK));
+    if (!selection_mode)
+    {
+        nautilus_view_set_selection (NAUTILUS_VIEW (self), NULL);
+    }
+
+    button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
+    if (button == GDK_BUTTON_SECONDARY)
+    {
+        GtkWidget *event_widget;
+        gdouble view_x;
+        gdouble view_y;
+
+        event_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
+        gtk_widget_translate_coordinates (event_widget, GTK_WIDGET (self),
+                                          x, y,
+                                          &view_x, &view_y);
+        nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (self),
+                                                            view_x, view_y);
+    }
+}
+
+static void
+on_item_longpress_pressed (GtkGestureLongPress *gesture,
+                           gdouble              x,
+                           gdouble              y,
+                           gpointer             user_data)
+{
+    NautilusViewCell *cell = user_data;
+    NautilusListBase *self = NAUTILUS_LIST_BASE (nautilus_view_cell_get_view (cell));
+    GtkWidget *event_widget;
+    gdouble view_x;
+    gdouble view_y;
+
+    event_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
+
+    gtk_widget_translate_coordinates (event_widget,
+                                      GTK_WIDGET (self),
+                                      x, y, &view_x, &view_y);
+
+    nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (self),
+                                                       view_x, view_y);
+    gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+}
+
+static void
+on_view_longpress_pressed (GtkGestureLongPress *gesture,
+                           gdouble              x,
+                           gdouble              y,
+                           gpointer             user_data)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (user_data);
+    GtkWidget *event_widget;
+    gdouble view_x;
+    gdouble view_y;
+
+    event_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
+
+    gtk_widget_translate_coordinates (event_widget,
+                                      GTK_WIDGET (self),
+                                      x, y, &view_x, &view_y);
+
+    nautilus_view_set_selection (NAUTILUS_VIEW (self), NULL);
+    nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (self),
+                                                        view_x, view_y);
+}
+
+void
+setup_cell_common (GtkListItem      *listitem,
+                   NautilusViewCell *cell)
+{
+    GtkEventController *controller;
+
+    g_object_bind_property (listitem, "item",
+                            cell, "item",
+                            G_BINDING_SYNC_CREATE);
+    gtk_list_item_set_child (listitem, GTK_WIDGET (cell));
+
+    controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ());
+    gtk_widget_add_controller (GTK_WIDGET (cell), controller);
+    gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE);
+    gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), 0);
+    g_signal_connect (controller, "pressed", G_CALLBACK (on_item_click_pressed), cell);
+    g_signal_connect (controller, "stopped", G_CALLBACK (on_item_click_stopped), cell);
+    g_signal_connect (controller, "released", G_CALLBACK (on_item_click_released), cell);
+
+    controller = GTK_EVENT_CONTROLLER (gtk_gesture_long_press_new ());
+    gtk_widget_add_controller (GTK_WIDGET (cell), controller);
+    gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE);
+    gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (controller), TRUE);
+    g_signal_connect (controller, "pressed", G_CALLBACK (on_item_longpress_pressed), cell);
+}
+
+static void
+nautilus_list_base_scroll_to_item (NautilusListBase *self,
+                                   guint             position)
+{
+    NAUTILUS_LIST_BASE_CLASS (G_OBJECT_GET_CLASS (self))->scroll_to_item (self, position);
+}
+
+static guint
+nautilus_list_base_get_icon_size (NautilusListBase *self)
+{
+    return NAUTILUS_LIST_BASE_CLASS (G_OBJECT_GET_CLASS (self))->get_icon_size (self);
+}
+
+static GtkWidget *
+nautilus_list_base_get_view_ui (NautilusListBase *self)
+{
+    return NAUTILUS_LIST_BASE_CLASS (G_OBJECT_GET_CLASS (self))->get_view_ui (self);
+}
+
+typedef struct
+{
+    NautilusListBase *self;
+    GQuark attribute_q;
+} NautilusListBaseSortData;
+
+static void
+real_begin_loading (NautilusFilesView *files_view)
+{
+    /*TODO move this to the files view class begin_loading and hook up? */
+
+
+    /* TODO: This calls sort once, and update_context_menus calls update_actions
+     * which calls the action again
+     */
+    update_sort_order_from_metadata_and_preferences (NAUTILUS_LIST_BASE (files_view));
+
+    /* We could have changed to the trash directory or to searching, and then
+     * we need to update the menus */
+    nautilus_files_view_update_context_menus (files_view);
+    nautilus_files_view_update_toolbar_menus (files_view);
+}
+
+static void
+real_clear (NautilusFilesView *files_view)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+
+    nautilus_view_model_remove_all_items (priv->model);
+}
+
+static void
+set_click_mode_from_settings (NautilusListBase *self)
+{
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    int click_policy;
+
+    click_policy = g_settings_get_enum (nautilus_preferences,
+                                        NAUTILUS_PREFERENCES_CLICK_POLICY);
+
+    priv->single_click_mode = (click_policy == NAUTILUS_CLICK_POLICY_SINGLE);
+}
+
+static void
+real_click_policy_changed (NautilusFilesView *files_view)
+{
+    set_click_mode_from_settings (NAUTILUS_LIST_BASE (files_view));
+}
+
+static void
+real_file_changed (NautilusFilesView *files_view,
+                   NautilusFile      *file,
+                   NautilusDirectory *directory)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    NautilusViewItemModel *item_model;
+
+    item_model = nautilus_view_model_get_item_from_file (priv->model, file);
+    nautilus_view_item_model_file_changed (item_model);
+}
+
+static GList *
+real_get_selection (NautilusFilesView *files_view)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    g_autoptr (GtkSelectionFilterModel) selection = NULL;
+    guint n_selected;
+    GList *selected_files = NULL;
+
+    selection = gtk_selection_filter_model_new (GTK_SELECTION_MODEL (priv->model));
+    n_selected = g_list_model_get_n_items (G_LIST_MODEL (selection));
+    for (guint i = 0; i < n_selected; i++)
+    {
+        g_autoptr (NautilusViewItemModel) item_model = NULL;
+
+        item_model = get_view_item (G_LIST_MODEL (selection), i);
+        selected_files = g_list_prepend (selected_files,
+                                         g_object_ref (nautilus_view_item_model_get_file (item_model)));
+    }
+
+    return selected_files;
+}
+
+static gboolean
+real_is_empty (NautilusFilesView *files_view)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+
+    return g_list_model_get_n_items (G_LIST_MODEL (priv->model)) == 0;
+}
+
+static void
+real_end_file_changes (NautilusFilesView *files_view)
+{
+}
+
+static void
+real_remove_file (NautilusFilesView *files_view,
+                  NautilusFile      *file,
+                  NautilusDirectory *directory)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    NautilusViewItemModel *item_model;
+
+    item_model = nautilus_view_model_get_item_from_file (priv->model, file);
+    if (item_model != NULL)
+    {
+        nautilus_view_model_remove_item (priv->model, item_model);
+    }
+}
+
+static GQueue *
+convert_glist_to_queue (GList *list)
+{
+    GList *l;
+    GQueue *queue;
+
+    queue = g_queue_new ();
+    for (l = list; l != NULL; l = l->next)
+    {
+        g_queue_push_tail (queue, l->data);
+    }
+
+    return queue;
+}
+
+static GQueue *
+convert_files_to_item_models (NautilusListBase *self,
+                              GQueue           *files)
+{
+    GList *l;
+    GQueue *models;
+
+    models = g_queue_new ();
+    for (l = g_queue_peek_head_link (files); l != NULL; l = l->next)
+    {
+        NautilusViewItemModel *item_model;
+
+        item_model = nautilus_view_item_model_new (NAUTILUS_FILE (l->data),
+                                                   nautilus_list_base_get_icon_size (self));
+        g_queue_push_tail (models, item_model);
+    }
+
+    return models;
+}
+
+static void
+real_set_selection (NautilusFilesView *files_view,
+                    GList             *selection)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    g_autoptr (GQueue) selection_files = NULL;
+    g_autoptr (GQueue) selection_item_models = NULL;
+    g_autoptr (GtkBitset) update_set = NULL;
+    g_autoptr (GtkBitset) selection_set = NULL;
+
+    update_set = gtk_selection_model_get_selection (GTK_SELECTION_MODEL (priv->model));
+    selection_set = gtk_bitset_new_empty ();
+
+    /* Convert file list into set of model indices */
+    selection_files = convert_glist_to_queue (selection);
+    selection_item_models = nautilus_view_model_get_items_from_files (priv->model, selection_files);
+    for (GList *l = g_queue_peek_head_link (selection_item_models); l != NULL; l = l->next)
+    {
+        gtk_bitset_add (selection_set,
+                        nautilus_view_model_get_index (priv->model, l->data));
+    }
+
+    gtk_bitset_union (update_set, selection_set);
+    gtk_selection_model_set_selection (GTK_SELECTION_MODEL (priv->model),
+                                       selection_set,
+                                       update_set);
+}
+
+static void
+real_select_all (NautilusFilesView *files_view)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+
+    gtk_selection_model_select_all (GTK_SELECTION_MODEL (priv->model));
+}
+
+static void
+real_invert_selection (NautilusFilesView *files_view)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    GtkSelectionModel *selection_model = GTK_SELECTION_MODEL (priv->model);
+    g_autoptr (GtkBitset) selected = NULL;
+    g_autoptr (GtkBitset) all = NULL;
+    g_autoptr (GtkBitset) new_selected = NULL;
+
+    selected = gtk_selection_model_get_selection (selection_model);
+
+    /* We are going to flip the selection state of every item in the model. */
+    all = gtk_bitset_new_range (0, g_list_model_get_n_items (G_LIST_MODEL (priv->model)));
+
+    /* The new selection is all items minus the ones currently selected. */
+    new_selected = gtk_bitset_copy (all);
+    gtk_bitset_subtract (new_selected, selected);
+
+    gtk_selection_model_set_selection (selection_model, new_selected, all);
+}
+
+static guint
+get_first_selected_item (NautilusListBase *self)
+{
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    g_autolist (NautilusFile) selection = NULL;
+    NautilusFile *file;
+    NautilusViewItemModel *item_model;
+
+    selection = nautilus_view_get_selection (NAUTILUS_VIEW (self));
+    if (selection == NULL)
+    {
+        return G_MAXUINT;
+    }
+
+    file = NAUTILUS_FILE (selection->data);
+    item_model = nautilus_view_model_get_item_from_file (priv->model, file);
+
+    return nautilus_view_model_get_index (priv->model, item_model);
+}
+
+static void
+real_reveal_selection (NautilusFilesView *files_view)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+
+    nautilus_list_base_scroll_to_item (self, get_first_selected_item (self));
+}
+
+static gboolean
+showing_recent_directory (NautilusFilesView *view)
+{
+    NautilusFile *file;
+
+    file = nautilus_files_view_get_directory_as_file (view);
+    if (file != NULL)
+    {
+        return nautilus_file_is_in_recent (file);
+    }
+    return FALSE;
+}
+
+static gboolean
+showing_search_directory (NautilusFilesView *view)
+{
+    NautilusFile *file;
+
+    file = nautilus_files_view_get_directory_as_file (view);
+    if (file != NULL)
+    {
+        return nautilus_file_is_in_search (file);
+    }
+    return FALSE;
+}
+
+static void
+real_update_actions_state (NautilusFilesView *files_view)
+{
+    GAction *action;
+    GActionGroup *view_action_group;
+
+    NAUTILUS_FILES_VIEW_CLASS (nautilus_list_base_parent_class)->update_actions_state (files_view);
+
+    view_action_group = nautilus_files_view_get_action_group (files_view);
+    action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group), "sort");
+    g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
+                                 !showing_recent_directory (files_view) &&
+                                 !showing_search_directory (files_view));
+}
+
+
+static int
+real_compare_files (NautilusFilesView *files_view,
+                    NautilusFile      *file1,
+                    NautilusFile      *file2)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    GtkSorter *sorter;
+    g_autoptr (NautilusViewItemModel) item1 = NULL;
+    g_autoptr (NautilusViewItemModel) item2 = NULL;
+
+    sorter = nautilus_view_model_get_sorter (priv->model);
+    if (sorter == NULL)
+    {
+        return 0;
+    }
+
+    /* Generate fake model items for sorter use only. */
+    item1 = nautilus_view_item_model_new (file1, NAUTILUS_GRID_ICON_SIZE_SMALL);
+    item2 = nautilus_view_item_model_new (file2, NAUTILUS_GRID_ICON_SIZE_SMALL);
+
+    return gtk_sorter_compare (sorter, item1, item2);
+}
+
+static void
+on_clipboard_contents_received (GObject      *source_object,
+                                GAsyncResult *res,
+                                gpointer      user_data)
+{
+    NautilusFilesView *files_view = NAUTILUS_FILES_VIEW (source_object);
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    NautilusClipboard *clip;
+    NautilusViewItemModel *item;
+
+    for (GList *l = priv->cut_files; l != NULL; l = l->next)
+    {
+        item = nautilus_view_model_get_item_from_file (priv->model, l->data);
+        if (item != NULL)
+        {
+            nautilus_view_item_model_set_cut (item, FALSE);
+        }
+    }
+    g_clear_list (&priv->cut_files, g_object_unref);
+
+    clip = nautilus_files_view_get_clipboard_finish (files_view, res, NULL);
+    if (clip != NULL && nautilus_clipboard_is_cut (clip))
+    {
+        priv->cut_files = g_list_copy_deep (nautilus_clipboard_peek_files (clip),
+                                            (GCopyFunc) g_object_ref,
+                                            NULL);
+    }
+
+    for (GList *l = priv->cut_files; l != NULL; l = l->next)
+    {
+        item = nautilus_view_model_get_item_from_file (priv->model, l->data);
+        if (item != NULL)
+        {
+            nautilus_view_item_model_set_cut (item, TRUE);
+        }
+    }
+}
+
+static void
+update_clipboard_status (NautilusFilesView *view)
+{
+    nautilus_files_view_get_clipboard_async (view,
+                                             on_clipboard_contents_received,
+                                             NULL);
+}
+
+static void
+on_clipboard_owner_changed (GdkClipboard *clipboard,
+                            gpointer      user_data)
+{
+    update_clipboard_status (NAUTILUS_FILES_VIEW (user_data));
+}
+
+static void
+real_end_loading (NautilusFilesView *files_view,
+                  gboolean           all_files_seen)
+{
+    update_clipboard_status (files_view);
+}
+
+static guint
+get_first_visible_item (NautilusListBase *self)
+{
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    guint n_items;
+    gdouble scrolled_y;
+    GtkWidget *view_ui;
+
+    n_items = g_list_model_get_n_items (G_LIST_MODEL (priv->model));
+    scrolled_y = gtk_adjustment_get_value (priv->vadjustment);
+    view_ui = nautilus_list_base_get_view_ui (self);
+    for (guint i = 0; i < n_items; i++)
+    {
+        g_autoptr (NautilusViewItemModel) item = NULL;
+        GtkWidget *item_ui;
+
+        item = get_view_item (G_LIST_MODEL (priv->model), i);
+        item_ui = nautilus_view_item_model_get_item_ui (item);
+        if (item_ui != NULL)
+        {
+            gdouble y;
+
+            gtk_widget_translate_coordinates (item_ui, view_ui,
+                                              0, 0, NULL, &y);
+            if (gtk_widget_is_visible (item_ui) && y >= scrolled_y)
+            {
+                return i;
+            }
+        }
+    }
+
+    return G_MAXUINT;
+}
+
+static char *
+real_get_first_visible_file (NautilusFilesView *files_view)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    guint i;
+    g_autoptr (NautilusViewItemModel) item = NULL;
+    gchar *uri = NULL;
+
+    i = get_first_visible_item (self);
+    if (i < G_MAXUINT)
+    {
+        item = get_view_item (G_LIST_MODEL (priv->model), i);
+        uri = nautilus_file_get_uri (nautilus_view_item_model_get_file (item));
+    }
+    return uri;
+}
+
+typedef struct
+{
+    NautilusListBase *view;
+    char *uri;
+} ScrollToFileData;
+
+static void
+scroll_to_file_data_free (ScrollToFileData *data)
+{
+    g_free (data->uri);
+    g_free (data);
+}
+
+static gboolean
+scroll_to_file_on_idle (ScrollToFileData *data)
+{
+    NautilusListBase *self = data->view;
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    g_autoptr (NautilusFile) file = NULL;
+    NautilusViewItemModel *item;
+    guint i;
+
+    file = nautilus_file_get_existing_by_uri (data->uri);
+    item = nautilus_view_model_get_item_from_file (priv->model, file);
+    i = nautilus_view_model_get_index (priv->model, item);
+
+    nautilus_list_base_scroll_to_item (self, i);
+
+    priv->scroll_to_file_handle_id = 0;
+    return G_SOURCE_REMOVE;
+}
+
+static void
+real_scroll_to_file (NautilusFilesView *files_view,
+                     const char        *uri)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    ScrollToFileData *data;
+    guint handle_id;
+
+    data = g_new (ScrollToFileData, 1);
+    data->view = self;
+    data->uri = g_strdup (uri);
+    handle_id = g_idle_add_full (G_PRIORITY_LOW,
+                                 (GSourceFunc) scroll_to_file_on_idle,
+                                 data,
+                                 (GDestroyNotify) scroll_to_file_data_free);
+    priv->scroll_to_file_handle_id = handle_id;
+}
+
+static void
+real_add_files (NautilusFilesView *files_view,
+                GList             *files)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    g_autoptr (GQueue) files_queue = NULL;
+    g_autoptr (GQueue) item_models = NULL;
+    gdouble adjustment_value;
+
+    files_queue = convert_glist_to_queue (files);
+    item_models = convert_files_to_item_models (self, files_queue);
+    nautilus_view_model_add_items (priv->model, item_models);
+
+    /* GtkListBase anchoring doesn't cope well with our lazy loading.
+     * Assuming that GtkListBase|list.scroll-to-item resets the anchor to 0, use
+     * that as a workaround to prevent scrolling while we are at the top. */
+    adjustment_value = gtk_adjustment_get_value (priv->vadjustment);
+    if (G_APPROX_VALUE (adjustment_value, 0.0, DBL_EPSILON))
+    {
+        nautilus_list_base_scroll_to_item (self, 0);
+    }
+}
+
+static void
+real_select_first (NautilusFilesView *files_view)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    g_autoptr (NautilusViewItemModel) item = NULL;
+    NautilusFile *file;
+    g_autoptr (GList) selection = NULL;
+
+    item = get_view_item (G_LIST_MODEL (priv->model), 0);
+    if (item == NULL)
+    {
+        return;
+    }
+    file = nautilus_view_item_model_get_file (item);
+    selection = g_list_prepend (selection, file);
+    nautilus_view_set_selection (NAUTILUS_VIEW (files_view), selection);
+}
+
+static GdkRectangle *
+get_rectangle_for_item_ui (NautilusListBase *self,
+                           GtkWidget        *item_ui)
+{
+    GdkRectangle *rectangle;
+    GtkWidget *content_widget;
+    gdouble view_x;
+    gdouble view_y;
+
+    rectangle = g_new0 (GdkRectangle, 1);
+    gtk_widget_get_allocation (item_ui, rectangle);
+
+    content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self));
+    gtk_widget_translate_coordinates (item_ui, content_widget,
+                                      rectangle->x, rectangle->y,
+                                      &view_x, &view_y);
+    rectangle->x = view_x;
+    rectangle->y = view_y;
+
+    return rectangle;
+}
+
+static GdkRectangle *
+real_compute_rename_popover_pointing_to (NautilusFilesView *files_view)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    g_autoptr (NautilusViewItemModel) item = NULL;
+    GtkWidget *item_ui;
+
+    /* We only allow one item to be renamed with a popover */
+    item = get_view_item (G_LIST_MODEL (priv->model), get_first_selected_item (self));
+    item_ui = nautilus_view_item_model_get_item_ui (item);
+    g_return_val_if_fail (item_ui != NULL, NULL);
+
+    return get_rectangle_for_item_ui (self, item_ui);
+}
+
+static GdkRectangle *
+real_reveal_for_selection_context_menu (NautilusFilesView *files_view)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    g_autoptr (GtkSelectionFilterModel) selection = NULL;
+    guint n_selected;
+    GtkWidget *focus_child;
+    guint i;
+    GtkWidget *item_ui;
+
+    selection = gtk_selection_filter_model_new (GTK_SELECTION_MODEL (priv->model));
+    n_selected = g_list_model_get_n_items (G_LIST_MODEL (selection));
+    g_return_val_if_fail (n_selected > 0, NULL);
+
+    /* Get the focused item_ui, if selected.
+     * Otherwise, get the selected item_ui which is sorted the lowest.*/
+    focus_child = gtk_widget_get_focus_child (nautilus_list_base_get_view_ui (self));
+    for (i = 0; i < n_selected; i++)
+    {
+        g_autoptr (NautilusViewItemModel) item = NULL;
+
+        item = get_view_item (G_LIST_MODEL (selection), i);
+        item_ui = nautilus_view_item_model_get_item_ui (item);
+        if (item_ui != NULL && gtk_widget_get_parent (item_ui) == focus_child)
+        {
+            break;
+        }
+    }
+    nautilus_list_base_scroll_to_item (self, i);
+
+    return get_rectangle_for_item_ui (self, item_ui);
+}
+
+static void
+real_preview_selection_event (NautilusFilesView *files_view,
+                              GtkDirectionType   direction)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (files_view);
+    GtkMovementStep step;
+    gint count;
+    gboolean handled;
+
+    step = (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN) ?
+           GTK_MOVEMENT_DISPLAY_LINES : GTK_MOVEMENT_VISUAL_POSITIONS;
+    count = (direction == GTK_DIR_RIGHT || direction == GTK_DIR_DOWN) ?
+            1 : -1;
+
+    g_signal_emit_by_name (nautilus_list_base_get_view_ui (self),
+                           "move-cursor", step, count, &handled);
+}
+
+static void
+default_sort_order_changed_callback (NautilusListBase *self)
+{
+    update_sort_order_from_metadata_and_preferences (self);
+}
+
+static void
+nautilus_list_base_dispose (GObject *object)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (object);
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+
+    g_clear_handle_id (&priv->scroll_to_file_handle_id, g_source_remove);
+    g_clear_handle_id (&priv->prioritize_thumbnailing_handle_id, g_source_remove);
+
+    G_OBJECT_CLASS (nautilus_list_base_parent_class)->dispose (object);
+}
+
+static void
+nautilus_list_base_finalize (GObject *object)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (object);
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+
+    g_clear_list (&priv->cut_files, g_object_unref);
+
+    G_OBJECT_CLASS (nautilus_list_base_parent_class)->finalize (object);
+}
+
+static gboolean
+prioritize_thumbnailing_on_idle (NautilusListBase *self)
+{
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    gdouble page_size;
+    GtkWidget *first_visible_child;
+    GtkWidget *next_child;
+    guint first_index;
+    guint next_index;
+    gdouble y;
+    guint last_index;
+    g_autoptr (NautilusViewItemModel) first_item = NULL;
+    NautilusFile *file;
+
+    priv->prioritize_thumbnailing_handle_id = 0;
+
+    page_size = gtk_adjustment_get_page_size (priv->vadjustment);
+    first_index = get_first_visible_item (self);
+    if (first_index == G_MAXUINT)
+    {
+        return G_SOURCE_REMOVE;
+    }
+
+    first_item = get_view_item (G_LIST_MODEL (priv->model), first_index);
+
+    first_visible_child = nautilus_view_item_model_get_item_ui (first_item);
+
+    for (next_index = first_index + 1; next_index < g_list_model_get_n_items (G_LIST_MODEL (priv->model)); 
next_index++)
+    {
+        g_autoptr (NautilusViewItemModel) next_item = NULL;
+
+        next_item = get_view_item (G_LIST_MODEL (priv->model), next_index);
+        next_child = nautilus_view_item_model_get_item_ui (next_item);
+        if (next_child == NULL)
+        {
+            break;
+        }
+        if (gtk_widget_translate_coordinates (next_child, first_visible_child,
+                                              0, 0, NULL, &y))
+        {
+            if (y > page_size)
+            {
+                break;
+            }
+        }
+    }
+    last_index = next_index - 1;
+
+    /* Do the iteration in reverse to give higher priority to the top */
+    for (gint i = 0; i <= last_index - first_index; i++)
+    {
+        g_autoptr (NautilusViewItemModel) item = NULL;
+
+        item = get_view_item (G_LIST_MODEL (priv->model), last_index - i);
+        g_return_val_if_fail (item != NULL, G_SOURCE_REMOVE);
+
+        file = nautilus_view_item_model_get_file (NAUTILUS_VIEW_ITEM_MODEL (item));
+        if (file != NULL && nautilus_file_is_thumbnailing (file))
+        {
+            g_autofree gchar *uri = nautilus_file_get_uri (file);
+            nautilus_thumbnail_prioritize (uri);
+        }
+    }
+
+    return G_SOURCE_REMOVE;
+}
+
+static void
+on_vadjustment_changed (GtkAdjustment *adjustment,
+                        gpointer       user_data)
+{
+    NautilusListBase *self = NAUTILUS_LIST_BASE (user_data);
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    guint handle_id;
+
+    /* Schedule on idle to rate limit and to avoid delaying scrolling. */
+    if (priv->prioritize_thumbnailing_handle_id == 0)
+    {
+        handle_id = g_idle_add ((GSourceFunc) prioritize_thumbnailing_on_idle, self);
+        priv->prioritize_thumbnailing_handle_id = handle_id;
+    }
+}
+
+static void
+nautilus_list_base_class_init (NautilusListBaseClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+    NautilusFilesViewClass *files_view_class = NAUTILUS_FILES_VIEW_CLASS (klass);
+
+    object_class->dispose = nautilus_list_base_dispose;
+    object_class->finalize = nautilus_list_base_finalize;
+
+    files_view_class->add_files = real_add_files;
+    files_view_class->begin_loading = real_begin_loading;
+    files_view_class->clear = real_clear;
+    files_view_class->click_policy_changed = real_click_policy_changed;
+    files_view_class->file_changed = real_file_changed;
+    files_view_class->get_selection = real_get_selection;
+    /* TODO: remove this get_selection_for_file_transfer, this doesn't even
+     * take into account we could us the view for recursive search :/
+     * CanvasView has the same issue. */
+    files_view_class->get_selection_for_file_transfer = real_get_selection;
+    files_view_class->is_empty = real_is_empty;
+    files_view_class->remove_file = real_remove_file;
+    files_view_class->select_all = real_select_all;
+    files_view_class->set_selection = real_set_selection;
+    files_view_class->invert_selection = real_invert_selection;
+    files_view_class->compare_files = real_compare_files;
+    files_view_class->end_file_changes = real_end_file_changes;
+    files_view_class->end_loading = real_end_loading;
+    files_view_class->get_first_visible_file = real_get_first_visible_file;
+    files_view_class->reveal_selection = real_reveal_selection;
+    files_view_class->update_actions_state = real_update_actions_state;
+    files_view_class->scroll_to_file = real_scroll_to_file;
+    files_view_class->select_first = real_select_first;
+    files_view_class->compute_rename_popover_pointing_to = real_compute_rename_popover_pointing_to;
+    files_view_class->reveal_for_selection_context_menu = real_reveal_for_selection_context_menu;
+    files_view_class->preview_selection_event = real_preview_selection_event;
+}
+
+static void
+nautilus_list_base_init (NautilusListBase *self)
+{
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+    GtkWidget *content_widget;
+    GtkAdjustment *vadjustment;
+
+    gtk_widget_add_css_class (GTK_WIDGET (self), "view");
+
+    g_signal_connect_object (nautilus_preferences,
+                             "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_ORDER,
+                             G_CALLBACK (default_sort_order_changed_callback),
+                             self,
+                             G_CONNECT_SWAPPED);
+    g_signal_connect_object (nautilus_preferences,
+                             "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_IN_REVERSE_ORDER,
+                             G_CALLBACK (default_sort_order_changed_callback),
+                             self,
+                             G_CONNECT_SWAPPED);
+
+    /* React to clipboard changes */
+    g_signal_connect_object (gdk_display_get_clipboard (gdk_display_get_default ()),
+                             "changed",
+                             G_CALLBACK (on_clipboard_owner_changed), self, 0);
+
+    content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self));
+    vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (content_widget));
+
+    priv->vadjustment = vadjustment;
+    g_signal_connect (vadjustment, "changed", (GCallback) on_vadjustment_changed, self);
+    g_signal_connect (vadjustment, "value-changed", (GCallback) on_vadjustment_changed, self);
+
+    priv->model = nautilus_view_model_new ();
+
+    g_signal_connect_object (GTK_SELECTION_MODEL (priv->model),
+                             "selection-changed",
+                             G_CALLBACK (nautilus_files_view_notify_selection_changed),
+                             NAUTILUS_FILES_VIEW (self),
+                             G_CONNECT_SWAPPED);
+
+    set_click_mode_from_settings (self);
+}
+
+NautilusViewModel *
+nautilus_list_base_get_model (NautilusListBase *self)
+{
+    NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self);
+
+    return priv->model;
+}
+
+void
+nautilus_list_base_setup_gestures (NautilusListBase *self)
+{
+    GtkWidget *view_ui = nautilus_list_base_get_view_ui (self);
+    GtkEventController *controller;
+
+    controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ());
+    gtk_widget_add_controller (view_ui, controller);
+    gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), 0);
+    g_signal_connect (controller, "pressed",
+                      G_CALLBACK (on_view_click_pressed), self);
+
+    controller = GTK_EVENT_CONTROLLER (gtk_gesture_long_press_new ());
+    gtk_widget_add_controller (view_ui, controller);
+    gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (controller), TRUE);
+    g_signal_connect (controller, "pressed",
+                      G_CALLBACK (on_view_longpress_pressed), self);
+}
diff --git a/src/nautilus-list-base.h b/src/nautilus-list-base.h
new file mode 100644
index 000000000..007ab07a6
--- /dev/null
+++ b/src/nautilus-list-base.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 The GNOME project contributors
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "nautilus-files-view.h"
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_LIST_BASE (nautilus_list_base_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (NautilusListBase, nautilus_list_base, NAUTILUS, LIST_BASE, NautilusFilesView)
+
+struct _NautilusListBaseClass
+{
+        NautilusFilesViewClass parent_class;
+
+        guint      (*get_icon_size)  (NautilusListBase *self);
+        GtkWidget *(*get_view_ui)    (NautilusListBase *self);
+        void       (*scroll_to_item) (NautilusListBase *self,
+                                      guint                   position);
+};
+
+G_END_DECLS
diff --git a/src/nautilus-types.h b/src/nautilus-types.h
index 073a1576d..1fe935449 100644
--- a/src/nautilus-types.h
+++ b/src/nautilus-types.h
@@ -36,8 +36,9 @@ typedef struct _NautilusClipboard           NautilusClipboard;
 typedef struct _NautilusDirectory           NautilusDirectory;
 typedef struct  NautilusFile                NautilusFile;
 typedef struct  NautilusFileQueue           NautilusFileQueue;
-typedef struct _NautilusFilesView           NautilusFilesView;
+typedef struct _NautilusFilesModelView      NautilusFilesModelView;
 typedef struct _NautilusIconInfo            NautilusIconInfo;
+typedef struct _NautilusListBase            NautilusListBase;
 typedef struct  NautilusMonitor             NautilusMonitor;
 typedef struct _NautilusQuery               NautilusQuery;
 typedef struct _NautilusQueryEditor         NautilusQueryEditor;
diff --git a/src/nautilus-view-cell.c b/src/nautilus-view-cell.c
new file mode 100644
index 000000000..0a184f0d0
--- /dev/null
+++ b/src/nautilus-view-cell.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2022 2022 António Fernandes <antoniof gnome org>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "nautilus-view-cell.h"
+#include "nautilus-list-base.h"
+
+/**
+ * NautilusViewCell:
+ *
+ * Abstract class of widgets tailored to be set as #GtkListItem:child in a view
+ * which subclasses #NautilusListBase.
+ *
+ * Subclass constructors should take a pointer to the #NautilusListBase view.
+ *
+ * The view is responsible for setting #NautilusViewCell:item. This can be done
+ * using a GBinding from #GtkListItem:item to #NautilusViewCell:item.
+ */
+
+typedef struct _NautilusViewCellPrivate NautilusViewCellPrivate;
+struct _NautilusViewCellPrivate
+{
+    AdwBin parent_instance;
+
+    NautilusListBase *view; /* Unowned */
+    NautilusViewItemModel *item; /* Owned reference */
+
+    gboolean called_once;
+};
+
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (NautilusViewCell, nautilus_view_cell, ADW_TYPE_BIN)
+
+enum
+{
+    PROP_0,
+    PROP_VIEW,
+    PROP_ITEM,
+    N_PROPS
+};
+
+static GParamSpec *properties[N_PROPS] = { NULL, };
+
+static void
+nautilus_view_cell_get_property (GObject    *object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+    NautilusViewCell *self = NAUTILUS_VIEW_CELL (object);
+    NautilusViewCellPrivate *priv = nautilus_view_cell_get_instance_private (self);
+
+    switch (prop_id)
+    {
+        case PROP_VIEW:
+        {
+            g_value_set_object (value, priv->view);
+        }
+        break;
+
+        case PROP_ITEM:
+        {
+            g_value_set_object (value, priv->item);
+        }
+        break;
+
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+    }
+}
+
+static void
+nautilus_view_cell_set_property (GObject      *object,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+    NautilusViewCell *self = NAUTILUS_VIEW_CELL (object);
+    NautilusViewCellPrivate *priv = nautilus_view_cell_get_instance_private (self);
+
+    switch (prop_id)
+    {
+        case PROP_VIEW:
+        {
+            priv->view = g_value_get_object (value);
+        }
+        break;
+
+        case PROP_ITEM:
+        {
+            g_set_object (&priv->item, g_value_get_object (value));
+        }
+        break;
+
+        default:
+        {
+            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+    }
+}
+
+static void
+nautilus_view_cell_init (NautilusViewCell *self)
+{
+}
+
+static void
+nautilus_view_cell_finalize (GObject *object)
+{
+    NautilusViewCell *self = NAUTILUS_VIEW_CELL (object);
+    NautilusViewCellPrivate *priv = nautilus_view_cell_get_instance_private (self);
+
+    g_clear_object (&priv->item);
+
+    G_OBJECT_CLASS (nautilus_view_cell_parent_class)->finalize (object);
+}
+
+static void
+nautilus_view_cell_class_init (NautilusViewCellClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    object_class->finalize = nautilus_view_cell_finalize;
+    object_class->get_property = nautilus_view_cell_get_property;
+    object_class->set_property = nautilus_view_cell_set_property;
+
+    properties[PROP_VIEW] = g_param_spec_object ("view",
+                                                 "", "",
+                                                 NAUTILUS_TYPE_LIST_BASE,
+                                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | 
G_PARAM_STATIC_STRINGS);
+    properties[PROP_ITEM] = g_param_spec_object ("item",
+                                                 "", "",
+                                                 NAUTILUS_TYPE_VIEW_ITEM_MODEL,
+                                                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+    g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+gboolean
+nautilus_view_cell_once (NautilusViewCell *self)
+{
+    NautilusViewCellPrivate *priv = nautilus_view_cell_get_instance_private (self);
+
+    if (priv->called_once)
+    {
+        return FALSE;
+    }
+    priv->called_once = TRUE;
+
+    return TRUE;
+}
+
+NautilusListBase *
+nautilus_view_cell_get_view (NautilusViewCell *self)
+{
+    NautilusListBase *view;
+
+    g_return_val_if_fail (NAUTILUS_IS_VIEW_CELL (self), NULL);
+
+    g_object_get (self, "view", &view, NULL);
+
+    return view;
+}
+
+void
+nautilus_view_cell_set_item (NautilusViewCell      *self,
+                             NautilusViewItemModel *item)
+{
+    g_return_if_fail (NAUTILUS_IS_VIEW_CELL (self));
+    g_return_if_fail (item == NULL || NAUTILUS_IS_VIEW_ITEM_MODEL (item));
+
+    g_object_set (self, "item", item, NULL);
+}
+
+NautilusViewItemModel *
+nautilus_view_cell_get_item (NautilusViewCell *self)
+{
+    NautilusViewItemModel *item;
+
+    g_return_val_if_fail (NAUTILUS_IS_VIEW_CELL (self), NULL);
+
+    g_object_get (self, "item", &item, NULL);
+
+    return item;
+}
diff --git a/src/nautilus-view-cell.h b/src/nautilus-view-cell.h
new file mode 100644
index 000000000..6093752eb
--- /dev/null
+++ b/src/nautilus-view-cell.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 António Fernandes <antoniof gnome org>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libadwaita-1/adwaita.h>
+
+#include "nautilus-types.h"
+#include "nautilus-view-item-model.h"
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_VIEW_CELL (nautilus_view_cell_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (NautilusViewCell, nautilus_view_cell, NAUTILUS, VIEW_CELL, AdwBin)
+
+struct _NautilusViewCellClass
+{
+    AdwBinClass parent_class;
+};
+
+NautilusListBase *nautilus_view_cell_get_view (NautilusViewCell *self);
+void nautilus_view_cell_set_item (NautilusViewCell      *self,
+                                  NautilusViewItemModel *item);
+NautilusViewItemModel *nautilus_view_cell_get_item (NautilusViewCell *self);
+gboolean nautilus_view_cell_once (NautilusViewCell *self);
+
+G_END_DECLS
diff --git a/src/nautilus-view-icon-controller.c b/src/nautilus-view-icon-controller.c
index f21cb681f..78652a019 100644
--- a/src/nautilus-view-icon-controller.c
+++ b/src/nautilus-view-icon-controller.c
@@ -1,193 +1,31 @@
+#include "nautilus-list-base-private.h"
 #include "nautilus-view-icon-controller.h"
 #include "nautilus-view-item-model.h"
 #include "nautilus-view-icon-item-ui.h"
-#include "nautilus-view-model.h"
-#include "nautilus-files-view.h"
 #include "nautilus-file.h"
-#include "nautilus-metadata.h"
-#include "nautilus-window-slot.h"
-#include "nautilus-directory.h"
-#include "nautilus-clipboard.h"
 #include "nautilus-global-preferences.h"
-#include "nautilus-thumbnails.h"
 
 struct _NautilusViewIconController
 {
-    NautilusFilesView parent_instance;
+    NautilusListBase parent_instance;
 
     GtkGridView *view_ui;
-    NautilusViewModel *model;
-
-    GList *cut_files;
 
     GActionGroup *action_group;
     gint zoom_level;
-    GQuark caption_attributes[NAUTILUS_VIEW_ICON_N_CAPTIONS];
 
-    gboolean single_click_mode;
-    gboolean activate_on_release;
-    gboolean deny_background_click;
+    gboolean directories_first;
 
-    guint scroll_to_file_handle_id;
-    guint prioritize_thumbnailing_handle_id;
-    GtkAdjustment *vadjustment;
+    GQuark caption_attributes[NAUTILUS_VIEW_ICON_N_CAPTIONS];
 
     NautilusFileSortType sort_type;
-    gboolean directories_first;
     gboolean reversed;
 };
 
-G_DEFINE_TYPE (NautilusViewIconController, nautilus_view_icon_controller, NAUTILUS_TYPE_FILES_VIEW)
-
-typedef struct
-{
-    const NautilusFileSortType sort_type;
-    const gchar *metadata_name;
-} SortConstants;
-
-static const SortConstants sorts_constants[] =
-{
-    {
-        NAUTILUS_FILE_SORT_BY_DISPLAY_NAME,
-        "name",
-    },
-    {
-        NAUTILUS_FILE_SORT_BY_SIZE,
-        "size",
-    },
-    {
-        NAUTILUS_FILE_SORT_BY_TYPE,
-        "type",
-    },
-    {
-        NAUTILUS_FILE_SORT_BY_MTIME,
-        "modification date",
-    },
-    {
-        NAUTILUS_FILE_SORT_BY_ATIME,
-        "access date",
-    },
-    {
-        NAUTILUS_FILE_SORT_BY_BTIME,
-        "creation date",
-    },
-    {
-        NAUTILUS_FILE_SORT_BY_TRASHED_TIME,
-        "trashed",
-    },
-    {
-        NAUTILUS_FILE_SORT_BY_SEARCH_RELEVANCE,
-        "search_relevance",
-    },
-    {
-        NAUTILUS_FILE_SORT_BY_RECENCY,
-        "recency",
-    },
-};
+G_DEFINE_TYPE (NautilusViewIconController, nautilus_view_icon_controller, NAUTILUS_TYPE_LIST_BASE)
 
 static guint get_icon_size_for_zoom_level (NautilusGridZoomLevel zoom_level);
 
-static const SortConstants *
-get_sorts_constants_from_sort_type (NautilusFileSortType sort_type)
-{
-    guint i;
-
-    for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++)
-    {
-        if (sort_type == sorts_constants[i].sort_type)
-        {
-            return &sorts_constants[i];
-        }
-    }
-
-    return &sorts_constants[0];
-}
-
-static const SortConstants *
-get_sorts_constants_from_metadata_text (const char *metadata_name)
-{
-    guint i;
-
-    for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++)
-    {
-        if (g_strcmp0 (sorts_constants[i].metadata_name, metadata_name) == 0)
-        {
-            return &sorts_constants[i];
-        }
-    }
-
-    return &sorts_constants[0];
-}
-
-static const SortConstants *
-get_default_sort_order (NautilusFile *file,
-                        gboolean     *reversed)
-{
-    NautilusFileSortType sort_type;
-
-    sort_type = nautilus_file_get_default_sort_type (file, reversed);
-
-    return get_sorts_constants_from_sort_type (sort_type);
-}
-
-static const SortConstants *
-get_directory_sort_by (NautilusFile *file,
-                       gboolean     *reversed)
-{
-    const SortConstants *default_sort;
-    g_autofree char *sort_by = NULL;
-
-    default_sort = get_default_sort_order (file, reversed);
-    g_return_val_if_fail (default_sort != NULL, NULL);
-
-    sort_by = nautilus_file_get_metadata (file,
-                                          NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY,
-                                          default_sort->metadata_name);
-
-    *reversed = nautilus_file_get_boolean_metadata (file,
-                                                    NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED,
-                                                    *reversed);
-
-    return get_sorts_constants_from_metadata_text (sort_by);
-}
-
-static void
-set_directory_sort_metadata (NautilusFile        *file,
-                             const SortConstants *sort,
-                             gboolean             reversed)
-{
-    const SortConstants *default_sort;
-    gboolean default_reversed;
-
-    default_sort = get_default_sort_order (file, &default_reversed);
-
-    nautilus_file_set_metadata (file,
-                                NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY,
-                                default_sort->metadata_name,
-                                sort->metadata_name);
-    nautilus_file_set_boolean_metadata (file,
-                                        NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED,
-                                        default_reversed,
-                                        reversed);
-}
-
-static void
-update_sort_order_from_metadata_and_preferences (NautilusViewIconController *self)
-{
-    const SortConstants *default_directory_sort;
-    GActionGroup *view_action_group;
-    gboolean reversed;
-
-    default_directory_sort = get_directory_sort_by (nautilus_files_view_get_directory_as_file 
(NAUTILUS_FILES_VIEW (self)),
-                                                    &reversed);
-    view_action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self));
-    g_action_group_change_action_state (view_action_group,
-                                        "sort",
-                                        g_variant_new ("(sb)",
-                                                       default_directory_sort->metadata_name,
-                                                       reversed));
-}
-
 static gint
 nautilus_view_icon_controller_sort (gconstpointer a,
                                     gconstpointer b,
@@ -206,258 +44,6 @@ nautilus_view_icon_controller_sort (gconstpointer a,
                                            self->reversed);
 }
 
-static void
-real_begin_loading (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-
-    /* TODO: This calls sort once, and update_context_menus calls update_actions which calls
-     * the action again
-     */
-    update_sort_order_from_metadata_and_preferences (self);
-
-    /*TODO move this to the files view class begin_loading and hook up? */
-
-    /* We could have changed to the trash directory or to searching, and then
-     * we need to update the menus */
-    nautilus_files_view_update_context_menus (files_view);
-    nautilus_files_view_update_toolbar_menus (files_view);
-}
-
-static void
-real_clear (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-
-    nautilus_view_model_remove_all_items (self->model);
-}
-
-static void
-real_file_changed (NautilusFilesView *files_view,
-                   NautilusFile      *file,
-                   NautilusDirectory *directory)
-{
-    NautilusViewIconController *self;
-    NautilusViewItemModel *item_model;
-
-    self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    item_model = nautilus_view_model_get_item_from_file (self->model, file);
-    nautilus_view_item_model_file_changed (item_model);
-}
-
-static GList *
-real_get_selection (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self;
-    g_autoptr (GtkSelectionFilterModel) selection = NULL;
-    guint n_selected;
-    GList *selected_files = NULL;
-
-    self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    selection = gtk_selection_filter_model_new (GTK_SELECTION_MODEL (self->model));
-    n_selected = g_list_model_get_n_items (G_LIST_MODEL (selection));
-    for (guint i = 0; i < n_selected; i++)
-    {
-        g_autoptr (NautilusViewItemModel) item_model = NULL;
-
-        item_model = g_list_model_get_item (G_LIST_MODEL (selection), i);
-        selected_files = g_list_prepend (selected_files,
-                                         g_object_ref (nautilus_view_item_model_get_file (item_model)));
-    }
-
-    return selected_files;
-}
-
-static gboolean
-real_is_empty (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-
-    return g_list_model_get_n_items (G_LIST_MODEL (self->model)) == 0;
-}
-
-static void
-real_end_file_changes (NautilusFilesView *files_view)
-{
-}
-
-static void
-real_remove_file (NautilusFilesView *files_view,
-                  NautilusFile      *file,
-                  NautilusDirectory *directory)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    NautilusViewItemModel *item_model;
-
-    item_model = nautilus_view_model_get_item_from_file (self->model, file);
-    if (item_model != NULL)
-    {
-        nautilus_view_model_remove_item (self->model, item_model);
-    }
-}
-
-static GQueue *
-convert_glist_to_queue (GList *list)
-{
-    GList *l;
-    GQueue *queue;
-
-    queue = g_queue_new ();
-    for (l = list; l != NULL; l = l->next)
-    {
-        g_queue_push_tail (queue, l->data);
-    }
-
-    return queue;
-}
-
-static GQueue *
-convert_files_to_item_models (NautilusViewIconController *self,
-                              GQueue                     *files)
-{
-    GList *l;
-    GQueue *models;
-
-    models = g_queue_new ();
-    for (l = g_queue_peek_head_link (files); l != NULL; l = l->next)
-    {
-        NautilusViewItemModel *item_model;
-
-        item_model = nautilus_view_item_model_new (NAUTILUS_FILE (l->data),
-                                                   get_icon_size_for_zoom_level (self->zoom_level));
-        g_queue_push_tail (models, item_model);
-    }
-
-    return models;
-}
-
-static void
-real_set_selection (NautilusFilesView *files_view,
-                    GList             *selection)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    g_autoptr (GQueue) selection_files = NULL;
-    g_autoptr (GQueue) selection_item_models = NULL;
-    g_autoptr (GtkBitset) update_set = NULL;
-    g_autoptr (GtkBitset) selection_set = NULL;
-
-    update_set = gtk_selection_model_get_selection (GTK_SELECTION_MODEL (self->model));
-    selection_set = gtk_bitset_new_empty ();
-
-    /* Convert file list into set of model indices */
-    selection_files = convert_glist_to_queue (selection);
-    selection_item_models = nautilus_view_model_get_items_from_files (self->model, selection_files);
-    for (GList *l = g_queue_peek_head_link (selection_item_models); l != NULL ; l = l->next)
-    {
-        gtk_bitset_add (selection_set,
-                        nautilus_view_model_get_index (self->model, l->data));
-    }
-
-    gtk_bitset_union (update_set, selection_set);
-    gtk_selection_model_set_selection (GTK_SELECTION_MODEL (self->model),
-                                       selection_set,
-                                       update_set);
-}
-
-static void
-real_select_all (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    gtk_selection_model_select_all (GTK_SELECTION_MODEL (self->model));
-}
-
-static void
-real_invert_selection (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    GtkSelectionModel *selection_model = GTK_SELECTION_MODEL (self->model);
-    g_autoptr (GtkBitset) selected = NULL;
-    g_autoptr (GtkBitset) all = NULL;
-    g_autoptr (GtkBitset) new_selected = NULL;
-
-    selected = gtk_selection_model_get_selection (selection_model);
-
-    /* We are going to flip the selection state of every item in the model. */
-    all = gtk_bitset_new_range (0, g_list_model_get_n_items (G_LIST_MODEL (self->model)));
-
-    /* The new selection is all items minus the ones currently selected. */
-    new_selected = gtk_bitset_copy (all);
-    gtk_bitset_subtract (new_selected, selected);
-
-    gtk_selection_model_set_selection (selection_model, new_selected, all);
-}
-
-static guint
-get_first_selected_item (NautilusViewIconController *self)
-{
-    g_autolist (NautilusFile) selection = NULL;
-    NautilusFile *file;
-    NautilusViewItemModel *item_model;
-
-    selection = nautilus_view_get_selection (NAUTILUS_VIEW (self));
-    if (selection == NULL)
-    {
-        return G_MAXUINT;
-    }
-
-    file = NAUTILUS_FILE (selection->data);
-    item_model = nautilus_view_model_get_item_from_file (self->model, file);
-
-    return nautilus_view_model_get_index (self->model, item_model);
-}
-
-static void
-real_reveal_selection (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-
-    gtk_widget_activate_action (GTK_WIDGET (self->view_ui),
-                                "list.scroll-to-item",
-                                "u",
-                                get_first_selected_item (self));
-}
-
-static gboolean
-showing_recent_directory (NautilusFilesView *view)
-{
-    NautilusFile *file;
-
-    file = nautilus_files_view_get_directory_as_file (view);
-    if (file != NULL)
-    {
-        return nautilus_file_is_in_recent (file);
-    }
-    return FALSE;
-}
-
-static gboolean
-showing_search_directory (NautilusFilesView *view)
-{
-    NautilusFile *file;
-
-    file = nautilus_files_view_get_directory_as_file (view);
-    if (file != NULL)
-    {
-        return nautilus_file_is_in_search (file);
-    }
-    return FALSE;
-}
-
-static void
-real_update_actions_state (NautilusFilesView *files_view)
-{
-    GAction *action;
-    GActionGroup *view_action_group;
-
-    NAUTILUS_FILES_VIEW_CLASS (nautilus_view_icon_controller_parent_class)->update_actions_state 
(files_view);
-
-    view_action_group = nautilus_files_view_get_action_group (files_view);
-    action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group), "sort");
-    g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
-                                 !showing_recent_directory (files_view) &&
-                                 !showing_search_directory (files_view));
-}
-
 static void
 real_bump_zoom_level (NautilusFilesView *files_view,
                       int                zoom_increment)
@@ -556,23 +142,6 @@ set_captions_from_preferences (NautilusViewIconController *self)
     }
 }
 
-static void
-set_icon_size (NautilusViewIconController *self,
-               gint                        icon_size)
-{
-    guint n_items;
-
-    n_items = g_list_model_get_n_items (G_LIST_MODEL (self->model));
-    for (guint i = 0; i < n_items; i++)
-    {
-        g_autoptr (NautilusViewItemModel) current_item_model = NULL;
-
-        current_item_model = g_list_model_get_item (G_LIST_MODEL (self->model), i);
-        nautilus_view_item_model_set_icon_size (current_item_model,
-                                                get_icon_size_for_zoom_level (self->zoom_level));
-    }
-}
-
 static void
 set_zoom_level (NautilusViewIconController *self,
                 guint                       new_level)
@@ -584,7 +153,8 @@ set_zoom_level (NautilusViewIconController *self,
      * updates captions whenever the icon size is set*/
     set_captions_from_preferences (self);
 
-    set_icon_size (self, get_icon_size_for_zoom_level (new_level));
+    nautilus_list_base_set_icon_size (NAUTILUS_LIST_BASE (self),
+                                      get_icon_size_for_zoom_level (new_level));
 
     nautilus_files_view_update_toolbar_menus (NAUTILUS_FILES_VIEW (self));
 }
@@ -628,134 +198,6 @@ real_can_zoom_out (NautilusFilesView *files_view)
     return self->zoom_level > NAUTILUS_GRID_ZOOM_LEVEL_SMALL;
 }
 
-static GdkRectangle *
-get_rectangle_for_item_ui (NautilusViewIconController *self,
-                           GtkWidget                  *item_ui)
-{
-    GdkRectangle *rectangle;
-    GtkWidget *content_widget;
-    gdouble view_x;
-    gdouble view_y;
-
-    rectangle = g_new0 (GdkRectangle, 1);
-    gtk_widget_get_allocation (item_ui, rectangle);
-
-    content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self));
-    gtk_widget_translate_coordinates (item_ui, content_widget,
-                                      rectangle->x, rectangle->y,
-                                      &view_x, &view_y);
-    rectangle->x = view_x;
-    rectangle->y = view_y;
-
-    return rectangle;
-}
-
-static GdkRectangle *
-real_compute_rename_popover_pointing_to (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    g_autoptr (NautilusViewItemModel) item = NULL;
-    GtkWidget *item_ui;
-
-    /* We only allow one item to be renamed with a popover */
-    item = g_list_model_get_item (G_LIST_MODEL (self->model),
-                                  get_first_selected_item (self));
-    item_ui = nautilus_view_item_model_get_item_ui (item);
-    g_return_val_if_fail (item_ui != NULL, NULL);
-
-    return get_rectangle_for_item_ui (self, item_ui);
-}
-
-static GdkRectangle *
-real_reveal_for_selection_context_menu (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    g_autoptr (GtkSelectionFilterModel) selection = NULL;
-    guint n_selected;
-    GtkWidget *focus_child;
-    guint i;
-    GtkWidget *item_ui;
-
-    selection = gtk_selection_filter_model_new (GTK_SELECTION_MODEL (self->model));
-    n_selected = g_list_model_get_n_items (G_LIST_MODEL (selection));
-    g_return_val_if_fail (n_selected > 0, NULL);
-
-    /* Get the focused item_ui, if selected.
-     * Otherwise, get the selected item_ui which is sorted the lowest.*/
-    focus_child = gtk_widget_get_focus_child (GTK_WIDGET (self->view_ui));
-    for (i = 0; i < n_selected; i++)
-    {
-        g_autoptr (NautilusViewItemModel) item = NULL;
-
-        item = g_list_model_get_item (G_LIST_MODEL (selection), i);
-        item_ui = nautilus_view_item_model_get_item_ui (item);
-        if (item_ui != NULL && gtk_widget_get_parent (item_ui) == focus_child)
-        {
-            break;
-        }
-    }
-
-    gtk_widget_activate_action (GTK_WIDGET (self->view_ui),
-                                "list.scroll-to-item",
-                                "u",
-                                i);
-
-    return get_rectangle_for_item_ui (self, item_ui);
-}
-
-static void
-set_click_mode_from_settings (NautilusViewIconController *self)
-{
-    int click_policy;
-
-    click_policy = g_settings_get_enum (nautilus_preferences,
-                                        NAUTILUS_PREFERENCES_CLICK_POLICY);
-
-    self->single_click_mode = (click_policy == NAUTILUS_CLICK_POLICY_SINGLE);
-}
-
-static void
-real_click_policy_changed (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    set_click_mode_from_settings (self);
-}
-
-/* GtkListBase changes selection only with the primary button, and only after
- * release. But we need to antecipate selection earlier if we are to activate it
- * or open its context menu. This helper should be used in these situations if
- * it's desirable to act on a multi-item selection, because it preserves it. */
-static void
-select_single_item_if_not_selected (NautilusViewIconController *self,
-                                    NautilusViewItemModel      *item)
-{
-    GtkSelectionModel *selection_model = GTK_SELECTION_MODEL (self->model);
-    guint position;
-
-    position = nautilus_view_model_get_index (self->model, item);
-    if (!gtk_selection_model_is_selected (selection_model, position))
-    {
-        gtk_selection_model_select_item (selection_model, position, TRUE);
-    }
-}
-
-static void
-activate_selection_on_click (NautilusViewIconController *self,
-                             gboolean                    open_in_new_tab)
-{
-    g_autolist (NautilusFile) selection = NULL;
-    NautilusOpenFlags flags = 0;
-    NautilusFilesView *files_view = NAUTILUS_FILES_VIEW (self);
-
-    selection = nautilus_view_get_selection (NAUTILUS_VIEW (self));
-    if (open_in_new_tab)
-    {
-        flags |= NAUTILUS_OPEN_FLAG_NEW_TAB;
-        flags |= NAUTILUS_OPEN_FLAG_DONT_MAKE_ACTIVE;
-    }
-    nautilus_files_view_activate_files (files_view, selection, flags, TRUE);
-}
-
 /* We only care about the keyboard activation part that GtkGridView provides,
  * but we don't need any special filtering here. Indeed, we ask GtkGridView
  * to not activate on single click, and we get to handle double clicks before
@@ -774,378 +216,47 @@ on_grid_view_item_activated (GtkGridView *grid_view,
     nautilus_files_view_activate_selection (NAUTILUS_FILES_VIEW (self));
 }
 
-static void
-on_item_click_pressed (GtkGestureClick *gesture,
-                       gint             n_press,
-                       gdouble          x,
-                       gdouble          y,
-                       gpointer         user_data)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
-    GtkWidget *event_widget;
-    NautilusViewItemModel *item_model;
-    guint button;
-    GdkModifierType modifiers;
-    gboolean selection_mode;
-
-    event_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
-    item_model = nautilus_view_icon_item_ui_get_model (NAUTILUS_VIEW_ICON_ITEM_UI (event_widget));
-    button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
-    modifiers = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (gesture));
-    selection_mode = (modifiers & (GDK_CONTROL_MASK | GDK_SHIFT_MASK));
-
-    /* Before anything else, store event state to be read by other handlers. */
-    self->deny_background_click = TRUE;
-    self->activate_on_release = (self->single_click_mode &&
-                                 button == GDK_BUTTON_PRIMARY &&
-                                 n_press == 1 &&
-                                 !selection_mode);
-
-    /* It's safe to claim event sequence on press in the following cases because
-     * they don't interfere with touch scrolling. */
-    if (button == GDK_BUTTON_PRIMARY && n_press == 2 && !self->single_click_mode)
-    {
-        activate_selection_on_click (self, modifiers & GDK_SHIFT_MASK);
-        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
-    }
-    else if (button == GDK_BUTTON_MIDDLE && n_press == 1)
-    {
-        /* Antecipate selection, if necessary, to activate it. */
-        select_single_item_if_not_selected (self, item_model);
-        activate_selection_on_click (self, TRUE);
-        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
-    }
-    else if (button == GDK_BUTTON_SECONDARY && n_press == 1)
-    {
-        gdouble view_x, view_y;
-
-        /* Antecipate selection, if necessary, for the context menu. */
-        select_single_item_if_not_selected (self, item_model);
-
-        gtk_widget_translate_coordinates (event_widget, GTK_WIDGET (self),
-                                          x, y,
-                                          &view_x, &view_y);
-        nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (self),
-                                                           view_x, view_y);
-        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
-    }
-}
-
-static void
-on_item_click_released (GtkGestureClick *gesture,
-                        gint             n_press,
-                        gdouble          x,
-                        gdouble          y,
-                        gpointer         user_data)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
-
-    if (self->activate_on_release)
-    {
-        GtkWidget *event_widget;
-        NautilusViewItemModel *item_model;
-        guint i;
-
-        event_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
-        item_model = nautilus_view_icon_item_ui_get_model (NAUTILUS_VIEW_ICON_ITEM_UI (event_widget));
-        i = nautilus_view_model_get_index (self->model, item_model);
-
-        /* Antecipate selection, enforcing single selection of target item. */
-        gtk_selection_model_select_item (GTK_SELECTION_MODEL (self->model), i, TRUE);
-
-        activate_selection_on_click (self, FALSE);
-    }
-
-    self->activate_on_release = FALSE;
-    self->deny_background_click = FALSE;
-}
-
-static void
-on_item_click_stopped (GtkGestureClick *gesture,
-                       gpointer         user_data)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
-
-    self->activate_on_release = FALSE;
-    self->deny_background_click = FALSE;
-}
-
-static void
-on_view_click_pressed (GtkGestureClick *gesture,
-                       gint             n_press,
-                       gdouble          x,
-                       gdouble          y,
-                       gpointer         user_data)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
-    guint button;
-    GdkModifierType modifiers;
-    gboolean selection_mode;
-
-    if (self->deny_background_click)
-    {
-        /* Item was clicked. */
-        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
-        return;
-    }
-
-    /* Don't interfere with GtkGridView default selection handling when
-     * holding Ctrl and Shift. */
-    modifiers = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (gesture));
-    selection_mode = (modifiers & (GDK_CONTROL_MASK | GDK_SHIFT_MASK));
-    if (!selection_mode)
-    {
-        nautilus_view_set_selection (NAUTILUS_VIEW (self), NULL);
-    }
-
-    button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
-    if (button == GDK_BUTTON_SECONDARY)
-    {
-        GtkWidget *event_widget;
-        gdouble view_x;
-        gdouble view_y;
-
-        event_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
-        gtk_widget_translate_coordinates (event_widget, GTK_WIDGET (self),
-                                          x, y,
-                                          &view_x, &view_y);
-        nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (self),
-                                                            view_x, view_y);
-    }
-}
-
-static void
-on_longpress_gesture_pressed_callback (GtkGestureLongPress *gesture,
-                                       gdouble              x,
-                                       gdouble              y,
-                                       gpointer             user_data)
-{
-    NautilusViewIconController *self;
-    GtkWidget *event_widget;
-    gdouble view_x;
-    gdouble view_y;
-
-    self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
-    event_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
-
-    gtk_widget_translate_coordinates (event_widget,
-                                      GTK_WIDGET (self),
-                                      x, y, &view_x, &view_y);
-    if (NAUTILUS_IS_VIEW_ICON_ITEM_UI (event_widget))
-    {
-        nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (self),
-                                                           view_x, view_y);
-        gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
-    }
-    else
-    {
-        nautilus_view_set_selection (NAUTILUS_VIEW (self), NULL);
-        nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (self),
-                                                            view_x, view_y);
-    }
-}
-
-static int
-real_compare_files (NautilusFilesView *files_view,
-                    NautilusFile      *file1,
-                    NautilusFile      *file2)
-{
-    GActionGroup *view_action_group;
-    GAction *action;
-    const gchar *target_name;
-    gboolean reversed;
-    const SortConstants *sort_constants;
-    gboolean directories_first;
-
-    view_action_group = nautilus_files_view_get_action_group (files_view);
-    action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group), "sort");
-    g_variant_get (g_action_get_state (action), "(&sb)", &target_name, &reversed);
-    sort_constants = get_sorts_constants_from_metadata_text (target_name);
-    directories_first = nautilus_files_view_should_sort_directories_first (files_view);
-
-    return nautilus_file_compare_for_sort (file1, file2,
-                                           sort_constants->sort_type,
-                                           directories_first,
-                                           reversed);
-}
-
-static void
-on_clipboard_contents_received (GObject      *source_object,
-                                GAsyncResult *res,
-                                gpointer      user_data)
-{
-    NautilusFilesView *files_view = NAUTILUS_FILES_VIEW (source_object);
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    NautilusClipboard *clip;
-    NautilusViewItemModel *item;
-
-    for (GList *l = self->cut_files; l != NULL; l = l->next)
-    {
-        item = nautilus_view_model_get_item_from_file (self->model, l->data);
-        if (item != NULL)
-        {
-            nautilus_view_item_model_set_cut (item, FALSE);
-        }
-    }
-    g_clear_list (&self->cut_files, g_object_unref);
-
-    clip = nautilus_files_view_get_clipboard_finish (files_view, res, NULL);
-    if (clip != NULL && nautilus_clipboard_is_cut (clip))
-    {
-        self->cut_files = g_list_copy_deep (nautilus_clipboard_peek_files (clip),
-                                            (GCopyFunc) g_object_ref,
-                                            NULL);
-    }
-
-    for (GList *l = self->cut_files; l != NULL; l = l->next)
-    {
-        item = nautilus_view_model_get_item_from_file (self->model, l->data);
-        if (item != NULL)
-        {
-            nautilus_view_item_model_set_cut (item, TRUE);
-        }
-    }
-}
-
-static void
-update_clipboard_status (NautilusFilesView *files_view)
-{
-    nautilus_files_view_get_clipboard_async (files_view,
-                                             on_clipboard_contents_received,
-                                             NULL);
-}
-
-static void
-on_clipboard_owner_changed (GdkClipboard *clipboard,
-                            gpointer      user_data)
-{
-    update_clipboard_status (NAUTILUS_FILES_VIEW (user_data));
-}
-
-
-static void
-real_end_loading (NautilusFilesView *files_view,
-                  gboolean           all_files_seen)
-{
-    update_clipboard_status (files_view);
-}
-
 static guint
-get_first_visible_item (NautilusViewIconController *self)
+real_get_icon_size (NautilusListBase *files_model_view)
 {
-    guint n_items;
-    gdouble scrolled_y;
-
-    n_items = g_list_model_get_n_items (G_LIST_MODEL (self->model));
-    scrolled_y = gtk_adjustment_get_value (self->vadjustment);
-    for (guint i = 0; i < n_items; i++)
-    {
-        g_autoptr (NautilusViewItemModel) item = NULL;
-        GtkWidget *item_ui;
-
-        item = g_list_model_get_item (G_LIST_MODEL (self->model), i);
-        item_ui = nautilus_view_item_model_get_item_ui (item);
-        if (item_ui != NULL)
-        {
-            gdouble y;
-
-            gtk_widget_translate_coordinates (item_ui, GTK_WIDGET (self->view_ui),
-                                              0, 0, NULL, &y);
-            if (gtk_widget_is_visible (item_ui) && y >= scrolled_y)
-            {
-                return i;
-            }
-        }
-    }
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_model_view);
 
-    return G_MAXUINT;
+    return get_icon_size_for_zoom_level (self->zoom_level);
 }
 
-static char *
-real_get_first_visible_file (NautilusFilesView *files_view)
+static GtkWidget *
+real_get_view_ui (NautilusListBase *files_model_view)
 {
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    guint i;
-    g_autoptr (NautilusViewItemModel) item = NULL;
-    gchar *uri = NULL;
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_model_view);
 
-    i = get_first_visible_item (self);
-    if (i < G_MAXUINT)
-    {
-        item = g_list_model_get_item (G_LIST_MODEL (self->model), i);
-        uri = nautilus_file_get_uri (nautilus_view_item_model_get_file (item));
-    }
-    return uri;
+    return GTK_WIDGET (self->view_ui);
 }
 
-typedef struct
-{
-    NautilusViewIconController *view;
-    char *uri;
-} ScrollToFileData;
-
 static void
-scroll_to_file_data_free (ScrollToFileData *data)
+real_scroll_to_item (NautilusListBase *files_model_view,
+                     guint             position)
 {
-    g_free (data->uri);
-    g_free (data);
-}
-
-static gboolean
-scroll_to_file_on_idle (ScrollToFileData *data)
-{
-    NautilusViewIconController *self = data->view;
-    g_autoptr (NautilusFile) file = NULL;
-    NautilusViewItemModel *item;
-    guint i;
-
-    file = nautilus_file_get_existing_by_uri (data->uri);
-    item = nautilus_view_model_get_item_from_file (self->model, file);
-    i = nautilus_view_model_get_index (self->model, item);
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_model_view);
 
     gtk_widget_activate_action (GTK_WIDGET (self->view_ui),
                                 "list.scroll-to-item",
                                 "u",
-                                i);
-
-    self->scroll_to_file_handle_id = 0;
-    return G_SOURCE_REMOVE;
-}
-
-static void
-real_scroll_to_file (NautilusFilesView *files_view,
-                     const char        *uri)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    ScrollToFileData *data;
-    guint handle_id;
-
-    /* Not exactly sure why, but the child widgets are not yet realized when
-     * this is usually called (which is when view finishes loading. Maybe
-     * because GtkFlowBox only generates children at the next GMainContext
-     * iteration? Anyway, doing it on idle as low priority works. */
-
-    data = g_new (ScrollToFileData, 1);
-    data->view = self;
-    data->uri = g_strdup (uri);
-    handle_id = g_idle_add_full (G_PRIORITY_LOW,
-                                 (GSourceFunc) scroll_to_file_on_idle,
-                                 data,
-                                 (GDestroyNotify) scroll_to_file_data_free);
-    self->scroll_to_file_handle_id = handle_id;
+                                position);
 }
 
 static void
 real_sort_directories_first_changed (NautilusFilesView *files_view)
 {
     NautilusViewIconController *self;
+    NautilusViewModel *model;
     g_autoptr (GtkCustomSorter) sorter = NULL;
 
     self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
     self->directories_first = nautilus_files_view_should_sort_directories_first (NAUTILUS_FILES_VIEW (self));
 
+    model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self));
     sorter = gtk_custom_sorter_new (nautilus_view_icon_controller_sort, self, NULL);
-    nautilus_view_model_set_sorter (self->model, GTK_SORTER (sorter));
+    nautilus_view_model_set_sorter (model, GTK_SORTER (sorter));
 }
 
 static void
@@ -1154,8 +265,8 @@ action_sort_order_changed (GSimpleAction *action,
                            gpointer       user_data)
 {
     const gchar *target_name;
-    const SortConstants *sort_constants;
-    NautilusViewIconController *self;
+    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
+    NautilusViewModel *model;
     g_autoptr (GtkCustomSorter) sorter = NULL;
 
     /* Don't resort if the action is in the same state as before */
@@ -1164,86 +275,25 @@ action_sort_order_changed (GSimpleAction *action,
         return;
     }
 
-    self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
     g_variant_get (value, "(&sb)", &target_name, &self->reversed);
-    sort_constants = get_sorts_constants_from_metadata_text (target_name);
-    self->sort_type = sort_constants->sort_type;
-    self->directories_first = nautilus_files_view_should_sort_directories_first (NAUTILUS_FILES_VIEW (self));
+    self->sort_type = get_sorts_type_from_metadata_text (target_name);
 
     sorter = gtk_custom_sorter_new (nautilus_view_icon_controller_sort, self, NULL);
-    nautilus_view_model_set_sorter (self->model, GTK_SORTER (sorter));
+    model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self));
+    nautilus_view_model_set_sorter (model, GTK_SORTER (sorter));
     set_directory_sort_metadata (nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self)),
-                                 sort_constants,
+                                 target_name,
                                  self->reversed);
 
     g_simple_action_set_state (action, value);
 }
 
-static void
-real_add_files (NautilusFilesView *files_view,
-                GList             *files)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    g_autoptr (GQueue) files_queue = NULL;
-    g_autoptr (GQueue) item_models = NULL;
-    gdouble adjustment_value;
-
-    files_queue = convert_glist_to_queue (files);
-    item_models = convert_files_to_item_models (self, files_queue);
-    nautilus_view_model_add_items (self->model, item_models);
-
-    /* GtkListBase anchoring doesn't cope well with our lazy loading.
-     * Assuming that GtkListBase|list.scroll-to-item resets the anchor to 0, use
-     * that as a workaround to prevent scrolling while we are at the top. */
-    adjustment_value = gtk_adjustment_get_value (self->vadjustment);
-    if (G_APPROX_VALUE (adjustment_value, 0.0, DBL_EPSILON))
-    {
-        gtk_widget_activate_action (GTK_WIDGET (self->view_ui), "list.scroll-to-item", "u", 0);
-    }
-}
-
-
 static guint
 real_get_view_id (NautilusFilesView *files_view)
 {
     return NAUTILUS_VIEW_GRID_ID;
 }
 
-static void
-real_select_first (NautilusFilesView *files_view)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    g_autoptr (NautilusViewItemModel) item = NULL;
-    NautilusFile *file;
-    g_autoptr (GList) selection = NULL;
-
-    item = NAUTILUS_VIEW_ITEM_MODEL (g_list_model_get_item (G_LIST_MODEL (self->model), 0));
-    if (item == NULL)
-    {
-        return;
-    }
-    file = nautilus_view_item_model_get_file (item);
-    selection = g_list_prepend (selection, file);
-    nautilus_view_set_selection (NAUTILUS_VIEW (files_view), selection);
-}
-
-static void
-real_preview_selection_event (NautilusFilesView *files_view,
-                              GtkDirectionType   direction)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
-    GtkMovementStep step;
-    gint count;
-    gboolean handled;
-
-    step = (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN) ?
-           GTK_MOVEMENT_DISPLAY_LINES : GTK_MOVEMENT_VISUAL_POSITIONS;
-    count = (direction == GTK_DIR_RIGHT || direction == GTK_DIR_DOWN) ?
-            1 : -1;
-
-    g_signal_emit_by_name (self->view_ui, "move-cursor", step, count, &handled);
-}
-
 static void
 action_zoom_to_level (GSimpleAction *action,
                       GVariant      *state,
@@ -1272,119 +322,22 @@ on_captions_preferences_changed (NautilusViewIconController *self)
 
     /* Hack: this relies on the assumption that NautilusViewIconItemUi updates
      * captions whenever the icon size is set (even if it's the same value). */
-    set_icon_size (self, get_icon_size_for_zoom_level (self->zoom_level));
-}
-
-static void
-on_default_sort_order_changed (NautilusViewIconController *self)
-{
-    update_sort_order_from_metadata_and_preferences (self);
+    nautilus_list_base_set_icon_size (NAUTILUS_LIST_BASE (self),
+                                      get_icon_size_for_zoom_level (self->zoom_level));
 }
 
 static void
 dispose (GObject *object)
 {
-    NautilusViewIconController *self;
-
-    self = NAUTILUS_VIEW_ICON_CONTROLLER (object);
-
-    g_clear_handle_id (&self->scroll_to_file_handle_id, g_source_remove);
-    g_clear_handle_id (&self->prioritize_thumbnailing_handle_id, g_source_remove);
-
     G_OBJECT_CLASS (nautilus_view_icon_controller_parent_class)->dispose (object);
 }
 
 static void
 finalize (GObject *object)
 {
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (object);
-
-    g_clear_list (&self->cut_files, g_object_unref);
-
     G_OBJECT_CLASS (nautilus_view_icon_controller_parent_class)->finalize (object);
 }
 
-static gboolean
-prioritize_thumbnailing_on_idle (NautilusViewIconController *self)
-{
-    gdouble page_size;
-    GtkWidget *first_visible_child;
-    GtkWidget *next_child;
-    guint first_index;
-    guint next_index;
-    gdouble y;
-    guint last_index;
-    g_autoptr (NautilusViewItemModel) first_item = NULL;
-    NautilusFile *file;
-
-    self->prioritize_thumbnailing_handle_id = 0;
-
-    page_size = gtk_adjustment_get_page_size (self->vadjustment);
-    first_index = get_first_visible_item (self);
-    if (first_index == G_MAXUINT)
-    {
-        return G_SOURCE_REMOVE;
-    }
-
-    first_item = g_list_model_get_item (G_LIST_MODEL (self->model), first_index);
-
-    first_visible_child = nautilus_view_item_model_get_item_ui (first_item);
-
-    for (next_index = first_index + 1; next_index < g_list_model_get_n_items (G_LIST_MODEL (self->model)); 
next_index++)
-    {
-        g_autoptr (NautilusViewItemModel) next_item = NULL;
-
-        next_item = g_list_model_get_item (G_LIST_MODEL (self->model), next_index);
-        next_child = nautilus_view_item_model_get_item_ui (next_item);
-        if (next_child == NULL)
-        {
-            break;
-        }
-        if (gtk_widget_translate_coordinates (next_child, first_visible_child,
-                                              0, 0, NULL, &y))
-        {
-            if (y > page_size)
-            {
-                break;
-            }
-        }
-    }
-    last_index = next_index - 1;
-
-    /* Do the iteration in reverse to give higher priority to the top */
-    for (gint i = 0; i <= last_index - first_index; i++)
-    {
-        g_autoptr (NautilusViewItemModel) item = NULL;
-
-        item = g_list_model_get_item (G_LIST_MODEL (self->model), last_index - i);
-        g_return_val_if_fail (item != NULL, G_SOURCE_REMOVE);
-
-        file = nautilus_view_item_model_get_file (NAUTILUS_VIEW_ITEM_MODEL (item));
-        if (file != NULL && nautilus_file_is_thumbnailing (file))
-        {
-            g_autofree gchar *uri = nautilus_file_get_uri (file);
-            nautilus_thumbnail_prioritize (uri);
-        }
-    }
-
-    return G_SOURCE_REMOVE;
-}
-
-static void
-on_vadjustment_changed (GtkAdjustment *adjustment,
-                        gpointer       user_data)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
-    guint handle_id;
-
-    /* Schedule on idle to rate limit and to avoid delaying scrolling. */
-    if (self->prioritize_thumbnailing_handle_id == 0)
-    {
-        handle_id = g_idle_add ((GSourceFunc) prioritize_thumbnailing_on_idle, self);
-        self->prioritize_thumbnailing_handle_id = handle_id;
-    }
-}
-
 static void
 bind_item_ui (GtkSignalListItemFactory *factory,
               GtkListItem              *listitem,
@@ -1396,11 +349,9 @@ bind_item_ui (GtkSignalListItemFactory *factory,
     item_ui = gtk_list_item_get_child (listitem);
     item_model = NAUTILUS_VIEW_ITEM_MODEL (gtk_list_item_get_item (listitem));
 
-    nautilus_view_icon_item_ui_set_model (NAUTILUS_VIEW_ICON_ITEM_UI (item_ui),
-                                          item_model);
     nautilus_view_item_model_set_item_ui (item_model, item_ui);
 
-    if (nautilus_view_icon_item_ui_once (NAUTILUS_VIEW_ICON_ITEM_UI (item_ui)))
+    if (nautilus_view_cell_once (NAUTILUS_VIEW_CELL (item_ui)))
     {
         GtkWidget *parent;
 
@@ -1422,13 +373,10 @@ unbind_item_ui (GtkSignalListItemFactory *factory,
                 GtkListItem              *listitem,
                 gpointer                  user_data)
 {
-    NautilusViewIconItemUi *item_ui;
     NautilusViewItemModel *item_model;
 
-    item_ui = NAUTILUS_VIEW_ICON_ITEM_UI (gtk_list_item_get_child (listitem));
     item_model = NAUTILUS_VIEW_ITEM_MODEL (gtk_list_item_get_item (listitem));
 
-    nautilus_view_icon_item_ui_set_model (item_ui, NULL);
     nautilus_view_item_model_set_item_ui (item_model, NULL);
 }
 
@@ -1439,39 +387,28 @@ setup_item_ui (GtkSignalListItemFactory *factory,
 {
     NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
     NautilusViewIconItemUi *item_ui;
-    GtkEventController *controller;
 
-    item_ui = nautilus_view_icon_item_ui_new ();
+    item_ui = nautilus_view_icon_item_ui_new (NAUTILUS_LIST_BASE (self));
+    setup_cell_common (listitem, NAUTILUS_VIEW_CELL (item_ui));
+
     nautilus_view_item_ui_set_caption_attributes (item_ui, self->caption_attributes);
-    gtk_list_item_set_child (listitem, GTK_WIDGET (item_ui));
-
-    controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ());
-    gtk_widget_add_controller (GTK_WIDGET (item_ui), controller);
-    gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE);
-    gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), 0);
-    g_signal_connect (controller, "pressed", G_CALLBACK (on_item_click_pressed), self);
-    g_signal_connect (controller, "released", G_CALLBACK (on_item_click_released), self);
-    g_signal_connect (controller, "stopped", G_CALLBACK (on_item_click_stopped), self);
-
-    controller = GTK_EVENT_CONTROLLER (gtk_gesture_long_press_new ());
-    gtk_widget_add_controller (GTK_WIDGET (item_ui), controller);
-    gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE);
-    gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (controller), TRUE);
-    g_signal_connect (controller, "pressed", G_CALLBACK (on_longpress_gesture_pressed_callback), self);
 }
 
 static GtkGridView *
 create_view_ui (NautilusViewIconController *self)
 {
+    NautilusViewModel *model;
     GtkListItemFactory *factory;
     GtkWidget *widget;
 
+    model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self));
+
     factory = gtk_signal_list_item_factory_new ();
     g_signal_connect (factory, "setup", G_CALLBACK (setup_item_ui), self);
     g_signal_connect (factory, "bind", G_CALLBACK (bind_item_ui), self);
     g_signal_connect (factory, "unbind", G_CALLBACK (unbind_item_ui), self);
 
-    widget = gtk_grid_view_new (GTK_SELECTION_MODEL (self->model), factory);
+    widget = gtk_grid_view_new (GTK_SELECTION_MODEL (model), factory);
     gtk_widget_set_focusable (widget, TRUE);
     gtk_widget_set_valign (widget, GTK_ALIGN_START);
 
@@ -1499,130 +436,35 @@ const GActionEntry view_icon_actions[] =
     { "zoom-to-level", NULL, NULL, "100", action_zoom_to_level }
 };
 
-static void
-constructed (GObject *object)
-{
-    NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (object);
-    GtkWidget *content_widget;
-    GtkAdjustment *vadjustment;
-    GActionGroup *view_action_group;
-    GtkEventController *controller;
-
-    content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self));
-    vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (content_widget));
-
-    self->vadjustment = vadjustment;
-    g_signal_connect (vadjustment, "changed", (GCallback) on_vadjustment_changed, self);
-    g_signal_connect (vadjustment, "value-changed", (GCallback) on_vadjustment_changed, self);
-
-    self->model = nautilus_view_model_new ();
-
-    self->view_ui = create_view_ui (self);
-    gtk_widget_show (GTK_WIDGET (self->view_ui));
-
-    g_signal_connect_swapped (GTK_SELECTION_MODEL (self->model),
-                              "selection-changed",
-                              G_CALLBACK (nautilus_files_view_notify_selection_changed),
-                              NAUTILUS_FILES_VIEW (self));
-
-    controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ());
-    gtk_widget_add_controller (GTK_WIDGET (content_widget), controller);
-    gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE);
-    gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), 0);
-    g_signal_connect (controller, "pressed",
-                      G_CALLBACK (on_view_click_pressed), self);
-
-    controller = GTK_EVENT_CONTROLLER (gtk_gesture_long_press_new ());
-    gtk_widget_add_controller (GTK_WIDGET (self->view_ui), controller);
-    gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE);
-    gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (controller), TRUE);
-    g_signal_connect (controller, "pressed",
-                      (GCallback) on_longpress_gesture_pressed_callback, self);
-
-    gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (content_widget),
-                                   GTK_WIDGET (self->view_ui));
-
-    self->action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self));
-    g_action_map_add_action_entries (G_ACTION_MAP (self->action_group),
-                                     view_icon_actions,
-                                     G_N_ELEMENTS (view_icon_actions),
-                                     self);
-
-    gtk_widget_show (GTK_WIDGET (self));
-
-    view_action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self));
-    g_action_map_add_action_entries (G_ACTION_MAP (view_action_group),
-                                     view_icon_actions,
-                                     G_N_ELEMENTS (view_icon_actions),
-                                     self);
-    self->zoom_level = get_default_zoom_level ();
-    /* Keep the action synced with the actual value, so the toolbar can poll it */
-    g_action_group_change_action_state (nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self)),
-                                        "zoom-to-level", g_variant_new_int32 (self->zoom_level));
-}
-
 static void
 nautilus_view_icon_controller_class_init (NautilusViewIconControllerClass *klass)
 {
     GObjectClass *object_class = G_OBJECT_CLASS (klass);
     NautilusFilesViewClass *files_view_class = NAUTILUS_FILES_VIEW_CLASS (klass);
+    NautilusListBaseClass *files_model_view_class = NAUTILUS_LIST_BASE_CLASS (klass);
 
     object_class->dispose = dispose;
     object_class->finalize = finalize;
-    object_class->constructed = constructed;
 
-    files_view_class->add_files = real_add_files;
-    files_view_class->begin_loading = real_begin_loading;
     files_view_class->bump_zoom_level = real_bump_zoom_level;
     files_view_class->can_zoom_in = real_can_zoom_in;
     files_view_class->can_zoom_out = real_can_zoom_out;
-    files_view_class->click_policy_changed = real_click_policy_changed;
-    files_view_class->clear = real_clear;
-    files_view_class->file_changed = real_file_changed;
-    files_view_class->get_selection = real_get_selection;
-    /* TODO: remove this get_selection_for_file_transfer, this doesn't even
-     * take into account we could us the view for recursive search :/
-     * CanvasView has the same issue. */
-    files_view_class->get_selection_for_file_transfer = real_get_selection;
-    files_view_class->is_empty = real_is_empty;
-    files_view_class->remove_file = real_remove_file;
-    files_view_class->update_actions_state = real_update_actions_state;
-    files_view_class->reveal_selection = real_reveal_selection;
-    files_view_class->select_all = real_select_all;
-    files_view_class->invert_selection = real_invert_selection;
-    files_view_class->set_selection = real_set_selection;
-    files_view_class->compare_files = real_compare_files;
     files_view_class->sort_directories_first_changed = real_sort_directories_first_changed;
-    files_view_class->end_file_changes = real_end_file_changes;
-    files_view_class->end_loading = real_end_loading;
     files_view_class->get_view_id = real_get_view_id;
-    files_view_class->get_first_visible_file = real_get_first_visible_file;
-    files_view_class->scroll_to_file = real_scroll_to_file;
-    files_view_class->select_first = real_select_first;
     files_view_class->restore_standard_zoom_level = real_restore_standard_zoom_level;
     files_view_class->is_zoom_level_default = real_is_zoom_level_default;
-    files_view_class->compute_rename_popover_pointing_to = real_compute_rename_popover_pointing_to;
-    files_view_class->reveal_for_selection_context_menu = real_reveal_for_selection_context_menu;
-    files_view_class->preview_selection_event = real_preview_selection_event;
+
+    files_model_view_class->get_icon_size = real_get_icon_size;
+    files_model_view_class->get_view_ui = real_get_view_ui;
+    files_model_view_class->scroll_to_item = real_scroll_to_item;
 }
 
 static void
 nautilus_view_icon_controller_init (NautilusViewIconController *self)
 {
-    gtk_widget_add_css_class (GTK_WIDGET (self), "view");
-    gtk_widget_add_css_class (GTK_WIDGET (self), "nautilus-grid-view");
-    set_click_mode_from_settings (self);
+    GtkWidget *content_widget;
 
-    g_signal_connect_object (nautilus_preferences,
-                             "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_ORDER,
-                             G_CALLBACK (on_default_sort_order_changed),
-                             self,
-                             G_CONNECT_SWAPPED);
-    g_signal_connect_object (nautilus_preferences,
-                             "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_IN_REVERSE_ORDER,
-                             G_CALLBACK (on_default_sort_order_changed),
-                             self,
-                             G_CONNECT_SWAPPED);
+    gtk_widget_add_css_class (GTK_WIDGET (self), "nautilus-grid-view");
 
     set_captions_from_preferences (self);
     g_signal_connect_object (nautilus_icon_view_preferences,
@@ -1631,11 +473,25 @@ nautilus_view_icon_controller_init (NautilusViewIconController *self)
                              self,
                              G_CONNECT_SWAPPED);
 
-    g_signal_connect_object (gdk_display_get_clipboard (gdk_display_get_default ()),
-                             "changed",
-                             G_CALLBACK (on_clipboard_owner_changed),
-                             self,
-                             0);
+
+    content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self));
+
+    self->view_ui = create_view_ui (self);
+    nautilus_list_base_setup_gestures (NAUTILUS_LIST_BASE (self));
+
+    gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (content_widget),
+                                   GTK_WIDGET (self->view_ui));
+
+    self->action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self));
+    g_action_map_add_action_entries (G_ACTION_MAP (self->action_group),
+                                     view_icon_actions,
+                                     G_N_ELEMENTS (view_icon_actions),
+                                     self);
+
+    self->zoom_level = get_default_zoom_level ();
+    /* Keep the action synced with the actual value, so the toolbar can poll it */
+    g_action_group_change_action_state (nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self)),
+                                        "zoom-to-level", g_variant_new_int32 (self->zoom_level));
 }
 
 NautilusViewIconController *
@@ -1645,3 +501,4 @@ nautilus_view_icon_controller_new (NautilusWindowSlot *slot)
                          "window-slot", slot,
                          NULL);
 }
+
diff --git a/src/nautilus-view-icon-controller.h b/src/nautilus-view-icon-controller.h
index be70c94bd..0dfa878e0 100644
--- a/src/nautilus-view-icon-controller.h
+++ b/src/nautilus-view-icon-controller.h
@@ -3,15 +3,14 @@
 #include <glib.h>
 #include <gtk/gtk.h>
 
-#include "nautilus-files-view.h"
+#include "nautilus-list-base.h"
 #include "nautilus-window-slot.h"
-#include "nautilus-view-model.h"
 
 G_BEGIN_DECLS
 
 #define NAUTILUS_TYPE_VIEW_ICON_CONTROLLER (nautilus_view_icon_controller_get_type())
 
-G_DECLARE_FINAL_TYPE (NautilusViewIconController, nautilus_view_icon_controller, NAUTILUS, 
VIEW_ICON_CONTROLLER, NautilusFilesView)
+G_DECLARE_FINAL_TYPE (NautilusViewIconController, nautilus_view_icon_controller, NAUTILUS, 
VIEW_ICON_CONTROLLER, NautilusListBase)
 
 NautilusViewIconController *nautilus_view_icon_controller_new (NautilusWindowSlot *slot);
 
diff --git a/src/nautilus-view-icon-item-ui.c b/src/nautilus-view-icon-item-ui.c
index d0ebff7b4..4c5897a89 100644
--- a/src/nautilus-view-icon-item-ui.c
+++ b/src/nautilus-view-icon-item-ui.c
@@ -1,13 +1,11 @@
 #include "nautilus-view-icon-item-ui.h"
-#include "nautilus-view-item-model.h"
-#include "nautilus-file.h"
-#include "nautilus-thumbnails.h"
 
 struct _NautilusViewIconItemUi
 {
-    GtkBox parent_instance;
+    NautilusViewCell parent_instance;
+
+    GSignalGroup *item_signal_group;
 
-    NautilusViewItemModel *model;
     GQuark *caption_attributes;
 
     GtkWidget *fixed_height_box;
@@ -16,24 +14,16 @@ struct _NautilusViewIconItemUi
     GtkWidget *first_caption;
     GtkWidget *second_caption;
     GtkWidget *third_caption;
-
-    gboolean called_once;
 };
 
-G_DEFINE_TYPE (NautilusViewIconItemUi, nautilus_view_icon_item_ui, GTK_TYPE_BOX)
-
-enum
-{
-    PROP_0,
-    PROP_MODEL,
-    N_PROPS
-};
+G_DEFINE_TYPE (NautilusViewIconItemUi, nautilus_view_icon_item_ui, NAUTILUS_TYPE_VIEW_CELL)
 
 #define EXTRA_WIDTH_FOR_TEXT 36
 
 static void
 update_icon (NautilusViewIconItemUi *self)
 {
+    NautilusViewItemModel *item;
     NautilusFileIconFlags flags;
     g_autoptr (GdkPaintable) icon_paintable = NULL;
     GtkStyleContext *style_context;
@@ -41,8 +31,10 @@ update_icon (NautilusViewIconItemUi *self)
     guint icon_size;
     g_autofree gchar *thumbnail_path = NULL;
 
-    file = nautilus_view_item_model_get_file (self->model);
-    icon_size = nautilus_view_item_model_get_icon_size (self->model);
+    item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self));
+    g_return_if_fail (item != NULL);
+    file = nautilus_view_item_model_get_file (item);
+    icon_size = nautilus_view_item_model_get_icon_size (item);
     flags = NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS |
             NAUTILUS_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE |
             NAUTILUS_FILE_ICON_FLAGS_USE_EMBLEMS |
@@ -76,6 +68,7 @@ update_icon (NautilusViewIconItemUi *self)
 static void
 update_captions (NautilusViewIconItemUi *self)
 {
+    NautilusViewItemModel *item;
     NautilusFile *file;
     GtkWidget * const caption_labels[] =
     {
@@ -85,7 +78,9 @@ update_captions (NautilusViewIconItemUi *self)
     };
     G_STATIC_ASSERT (G_N_ELEMENTS (caption_labels) == NAUTILUS_VIEW_ICON_N_CAPTIONS);
 
-    file = nautilus_view_item_model_get_file (self->model);
+    item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self));
+    g_return_if_fail (item != NULL);
+    file = nautilus_view_item_model_get_file (item);
     for (guint i = 0; i < NAUTILUS_VIEW_ICON_N_CAPTIONS; i++)
     {
         GQuark attribute_q = self->caption_attributes[i];
@@ -105,9 +100,12 @@ update_captions (NautilusViewIconItemUi *self)
 static void
 on_file_changed (NautilusViewIconItemUi *self)
 {
+    NautilusViewItemModel *item;
     NautilusFile *file;
 
-    file = nautilus_view_item_model_get_file (self->model);
+    item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self));
+    g_return_if_fail (item != NULL);
+    file = nautilus_view_item_model_get_file (item);
 
     update_icon (self);
 
@@ -117,25 +115,18 @@ on_file_changed (NautilusViewIconItemUi *self)
 }
 
 static void
-on_view_item_size_changed (GObject    *object,
-                           GParamSpec *pspec,
-                           gpointer    user_data)
+on_item_size_changed (NautilusViewIconItemUi *self)
 {
-    NautilusViewIconItemUi *self = NAUTILUS_VIEW_ICON_ITEM_UI (user_data);
-
     update_icon (self);
     update_captions (self);
 }
 
 static void
-on_view_item_is_cut_changed (GObject    *object,
-                             GParamSpec *pspec,
-                             gpointer    user_data)
+on_item_is_cut_changed (NautilusViewIconItemUi *self)
 {
-    NautilusViewIconItemUi *self = NAUTILUS_VIEW_ICON_ITEM_UI (user_data);
     gboolean is_cut;
 
-    g_object_get (object, "is-cut", &is_cut, NULL);
+    g_object_get (self, "is-cut", &is_cut, NULL);
     if (is_cut)
     {
         gtk_widget_add_css_class (self->icon, "cut");
@@ -146,100 +137,15 @@ on_view_item_is_cut_changed (GObject    *object,
     }
 }
 
-static void
-set_model (NautilusViewIconItemUi *self,
-           NautilusViewItemModel  *model);
-
 static void
 finalize (GObject *object)
 {
     NautilusViewIconItemUi *self = (NautilusViewIconItemUi *) object;
 
-    set_model (self, NULL);
+    g_object_unref (self->item_signal_group);
     G_OBJECT_CLASS (nautilus_view_icon_item_ui_parent_class)->finalize (object);
 }
 
-static void
-get_property (GObject    *object,
-              guint       prop_id,
-              GValue     *value,
-              GParamSpec *pspec)
-{
-    NautilusViewIconItemUi *self = NAUTILUS_VIEW_ICON_ITEM_UI (object);
-
-    switch (prop_id)
-    {
-        case PROP_MODEL:
-        {
-            g_value_set_object (value, self->model);
-        }
-        break;
-
-        default:
-            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
-}
-
-static void
-set_model (NautilusViewIconItemUi *self,
-           NautilusViewItemModel  *model)
-{
-    NautilusFile *file;
-
-    if (self->model == model)
-    {
-        return;
-    }
-
-    if (self->model != NULL)
-    {
-        g_signal_handlers_disconnect_by_data (self->model, self);
-        g_clear_object (&self->model);
-    }
-
-    if (model == NULL)
-    {
-        return;
-    }
-
-    self->model = g_object_ref (model);
-
-    file = nautilus_view_item_model_get_file (self->model);
-
-    update_icon (self);
-    gtk_label_set_text (GTK_LABEL (self->label),
-                        nautilus_file_get_display_name (file));
-    update_captions (self);
-
-    g_signal_connect (self->model, "notify::icon-size",
-                      (GCallback) on_view_item_size_changed, self);
-    g_signal_connect (self->model, "notify::is-cut",
-                      (GCallback) on_view_item_is_cut_changed, self);
-    g_signal_connect_swapped (self->model, "file-changed",
-                              (GCallback) on_file_changed, self);
-}
-
-static void
-set_property (GObject      *object,
-              guint         prop_id,
-              const GValue *value,
-              GParamSpec   *pspec)
-{
-    NautilusViewIconItemUi *self = NAUTILUS_VIEW_ICON_ITEM_UI (object);
-
-    switch (prop_id)
-    {
-        case PROP_MODEL:
-        {
-            set_model (self, g_value_get_object (value));
-        }
-        break;
-
-        default:
-            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
-}
-
 static void
 nautilus_view_icon_item_ui_class_init (NautilusViewIconItemUiClass *klass)
 {
@@ -247,16 +153,6 @@ nautilus_view_icon_item_ui_class_init (NautilusViewIconItemUiClass *klass)
     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
     object_class->finalize = finalize;
-    object_class->get_property = get_property;
-    object_class->set_property = set_property;
-
-    g_object_class_install_property (object_class,
-                                     PROP_MODEL,
-                                     g_param_spec_object ("model",
-                                                          "Item model",
-                                                          "The item model that this UI reprensents",
-                                                          NAUTILUS_TYPE_VIEW_ITEM_MODEL,
-                                                          G_PARAM_READWRITE));
 
     gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/nautilus/ui/nautilus-view-icon-item-ui.ui");
 
@@ -273,6 +169,22 @@ nautilus_view_icon_item_ui_init (NautilusViewIconItemUi *self)
 {
     gtk_widget_init_template (GTK_WIDGET (self));
 
+    /* Connect automatically to an item. */
+    self->item_signal_group = g_signal_group_new (NAUTILUS_TYPE_VIEW_ITEM_MODEL);
+    g_signal_group_connect_swapped (self->item_signal_group, "notify::icon-size",
+                                    (GCallback) on_item_size_changed, self);
+    g_signal_group_connect_swapped (self->item_signal_group, "notify::is-cut",
+                                    (GCallback) on_item_is_cut_changed, self);
+    g_signal_group_connect_swapped (self->item_signal_group, "file-changed",
+                                    (GCallback) on_file_changed, self);
+    g_signal_connect_object (self->item_signal_group, "bind",
+                             (GCallback) on_file_changed, self,
+                             G_CONNECT_SWAPPED);
+
+    g_object_bind_property (self, "item",
+                            self->item_signal_group, "target",
+                            G_BINDING_SYNC_CREATE);
+
 #if PANGO_VERSION_CHECK (1, 44, 4)
     {
         PangoAttrList *attr_list;
@@ -288,26 +200,11 @@ nautilus_view_icon_item_ui_init (NautilusViewIconItemUi *self)
 }
 
 NautilusViewIconItemUi *
-nautilus_view_icon_item_ui_new (void)
-{
-    return g_object_new (NAUTILUS_TYPE_VIEW_ICON_ITEM_UI, NULL);
-}
-
-void
-nautilus_view_icon_item_ui_set_model (NautilusViewIconItemUi *self,
-                                      NautilusViewItemModel  *model)
-{
-    g_object_set (self, "model", model, NULL);
-}
-
-NautilusViewItemModel *
-nautilus_view_icon_item_ui_get_model (NautilusViewIconItemUi *self)
+nautilus_view_icon_item_ui_new (NautilusListBase *view)
 {
-    NautilusViewItemModel *model = NULL;
-
-    g_object_get (self, "model", &model, NULL);
-
-    return model;
+    return g_object_new (NAUTILUS_TYPE_VIEW_ICON_ITEM_UI,
+                         "view", view,
+                         NULL);
 }
 
 void
@@ -316,15 +213,3 @@ nautilus_view_item_ui_set_caption_attributes (NautilusViewIconItemUi *self,
 {
     self->caption_attributes = attrs;
 }
-
-gboolean
-nautilus_view_icon_item_ui_once (NautilusViewIconItemUi *self)
-{
-    if (self->called_once)
-    {
-        return FALSE;
-    }
-
-    self->called_once = TRUE;
-    return TRUE;
-}
diff --git a/src/nautilus-view-icon-item-ui.h b/src/nautilus-view-icon-item-ui.h
index edae77dc8..5f6d1ad93 100644
--- a/src/nautilus-view-icon-item-ui.h
+++ b/src/nautilus-view-icon-item-ui.h
@@ -3,7 +3,7 @@
 #include <glib.h>
 #include <gtk/gtk.h>
 
-#include "nautilus-view-item-model.h"
+#include "nautilus-view-cell.h"
 
 G_BEGIN_DECLS
 
@@ -17,14 +17,10 @@ enum
 
 #define NAUTILUS_TYPE_VIEW_ICON_ITEM_UI (nautilus_view_icon_item_ui_get_type())
 
-G_DECLARE_FINAL_TYPE (NautilusViewIconItemUi, nautilus_view_icon_item_ui, NAUTILUS, VIEW_ICON_ITEM_UI, 
GtkBox)
+G_DECLARE_FINAL_TYPE (NautilusViewIconItemUi, nautilus_view_icon_item_ui, NAUTILUS, VIEW_ICON_ITEM_UI, 
NautilusViewCell)
 
-NautilusViewIconItemUi * nautilus_view_icon_item_ui_new (void);
-void nautilus_view_icon_item_ui_set_model (NautilusViewIconItemUi *self,
-                                           NautilusViewItemModel  *model);
-NautilusViewItemModel *nautilus_view_icon_item_ui_get_model (NautilusViewIconItemUi *self);
+NautilusViewIconItemUi * nautilus_view_icon_item_ui_new (NautilusListBase *view);
 void nautilus_view_item_ui_set_caption_attributes (NautilusViewIconItemUi *self,
                                                    GQuark                 *attrs);
-gboolean nautilus_view_icon_item_ui_once (NautilusViewIconItemUi *self);
 
 G_END_DECLS
diff --git a/src/nautilus-view-model.c b/src/nautilus-view-model.c
index 595d24c0f..3b7c793cd 100644
--- a/src/nautilus-view-model.c
+++ b/src/nautilus-view-model.c
@@ -174,7 +174,7 @@ get_property (GObject    *object,
     {
         case PROP_SORTER:
         {
-            g_value_set_object (value, self->sorter);
+            g_value_set_object (value, nautilus_view_model_get_sorter (self));
         }
         break;
 
@@ -278,6 +278,12 @@ nautilus_view_model_new ()
     return g_object_new (NAUTILUS_TYPE_VIEW_MODEL, NULL);
 }
 
+GtkSorter *
+nautilus_view_model_get_sorter (NautilusViewModel *self)
+{
+    return self->sorter;
+}
+
 void
 nautilus_view_model_set_sorter (NautilusViewModel *self,
                                 GtkSorter         *sorter)
diff --git a/src/nautilus-view-model.h b/src/nautilus-view-model.h
index 1771cd6e2..d76c3987d 100644
--- a/src/nautilus-view-model.h
+++ b/src/nautilus-view-model.h
@@ -12,6 +12,7 @@ G_DECLARE_FINAL_TYPE (NautilusViewModel, nautilus_view_model, NAUTILUS, VIEW_MOD
 
 NautilusViewModel * nautilus_view_model_new (void);
 
+GtkSorter *nautilus_view_model_get_sorter (NautilusViewModel *self);
 void nautilus_view_model_set_sorter (NautilusViewModel *self,
                                      GtkSorter         *sorter);
 NautilusViewItemModel * nautilus_view_model_get_item_from_file (NautilusViewModel *self,
diff --git a/src/resources/ui/nautilus-view-icon-item-ui.ui b/src/resources/ui/nautilus-view-icon-item-ui.ui
index 0b0f18d26..e614c538f 100644
--- a/src/resources/ui/nautilus-view-icon-item-ui.ui
+++ b/src/resources/ui/nautilus-view-icon-item-ui.ui
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk" version="4.0"/>
-  <template class="NautilusViewIconItemUi" parent="GtkBox">
+  <template class="NautilusViewIconItemUi" parent="NautilusViewCell">
     <child>
       <object class="AdwClamp">
         <property name="maximum-size">0</property>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]