From bd2214e2952c095d80c62fdba60f49d45f2a1f7a Mon Sep 17 00:00:00 2001 From: Corey Berla Date: Tue, 27 Sep 2022 08:08:34 -0700 Subject: [PATCH 01/10] general: Stop leaking NautilusViewItem --- src/nautilus-grid-cell.c | 12 +++++++----- src/nautilus-label-cell.c | 2 +- src/nautilus-list-view.c | 2 +- src/nautilus-name-cell.c | 12 +++++++----- src/nautilus-star-cell.c | 6 +++--- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/nautilus-grid-cell.c b/src/nautilus-grid-cell.c index bf19a36a10..475d849904 100644 --- a/src/nautilus-grid-cell.c +++ b/src/nautilus-grid-cell.c @@ -28,7 +28,7 @@ G_DEFINE_TYPE (NautilusGridCell, nautilus_grid_cell, NAUTILUS_TYPE_VIEW_CELL) static void update_icon (NautilusGridCell *self) { - NautilusViewItem *item; + g_autoptr (NautilusViewItem) item = NULL; NautilusFileIconFlags flags; g_autoptr (GdkPaintable) icon_paintable = NULL; GtkStyleContext *style_context; @@ -66,7 +66,7 @@ update_icon (NautilusGridCell *self) static void update_captions (NautilusGridCell *self) { - NautilusViewItem *item; + g_autoptr (NautilusViewItem) item = NULL; NautilusFile *file; GtkWidget * const caption_labels[] = { @@ -98,7 +98,7 @@ update_captions (NautilusGridCell *self) static void update_emblems (NautilusGridCell *self) { - NautilusViewItem *item; + g_autoptr (NautilusViewItem) item = NULL; NautilusFile *file; GtkWidget *child; g_autolist (GIcon) emblems = NULL; @@ -125,7 +125,7 @@ update_emblems (NautilusGridCell *self) static void on_file_changed (NautilusGridCell *self) { - NautilusViewItem *item; + g_autoptr (NautilusViewItem) item = NULL; NautilusFile *file; g_autofree gchar *name = NULL; @@ -153,8 +153,10 @@ static void on_item_is_cut_changed (NautilusGridCell *self) { gboolean is_cut; + g_autoptr (NautilusViewItem) item = NULL; - g_object_get (nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)), + item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); + g_object_get (item, "is-cut", &is_cut, NULL); if (is_cut) diff --git a/src/nautilus-label-cell.c b/src/nautilus-label-cell.c index 7d3319dba1..e8b62dfb78 100644 --- a/src/nautilus-label-cell.c +++ b/src/nautilus-label-cell.c @@ -37,7 +37,7 @@ static GParamSpec *properties[N_PROPS] = { NULL, }; static void on_file_changed (NautilusLabelCell *self) { - NautilusViewItem *item; + g_autoptr (NautilusViewItem) item = NULL; NautilusFile *file; g_autofree gchar *string = NULL; diff --git a/src/nautilus-list-view.c b/src/nautilus-list-view.c index 4b7a5f2e1d..032e3a9da1 100644 --- a/src/nautilus-list-view.c +++ b/src/nautilus-list-view.c @@ -880,7 +880,7 @@ on_item_click_released_workaround (GtkGestureClick *gesture, modifiers & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) { NautilusViewModel *model; - NautilusViewItem *item; + g_autoptr (NautilusViewItem) item = NULL; guint i; model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self)); diff --git a/src/nautilus-name-cell.c b/src/nautilus-name-cell.c index a58415f9ee..23e57ed22d 100644 --- a/src/nautilus-name-cell.c +++ b/src/nautilus-name-cell.c @@ -92,7 +92,7 @@ get_path_text (NautilusFile *file, static void update_labels (NautilusNameCell *self) { - NautilusViewItem *item; + g_autoptr (NautilusViewItem) item = NULL; NautilusFile *file; g_autofree gchar *display_name = NULL; g_autofree gchar *path_text = NULL; @@ -125,7 +125,7 @@ update_icon (NautilusNameCell *self) NautilusFileIconFlags flags; g_autoptr (GdkPaintable) icon_paintable = NULL; GtkStyleContext *style_context; - NautilusViewItem *item; + g_autoptr (NautilusViewItem) item = NULL; NautilusFile *file; guint icon_size; gint scale_factor; @@ -177,7 +177,7 @@ update_icon (NautilusNameCell *self) static void update_emblems (NautilusNameCell *self) { - NautilusViewItem *item; + g_autoptr (NautilusViewItem) item = NULL; NautilusFile *file; GtkWidget *child; g_autolist (GIcon) emblems = NULL; @@ -218,7 +218,7 @@ static void on_item_drag_accept_changed (NautilusNameCell *self) { gboolean drag_accept; - NautilusViewItem *item; + g_autoptr (NautilusViewItem) item = NULL; GtkWidget *list_row = gtk_widget_get_parent (gtk_widget_get_parent (GTK_WIDGET (self))); item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); @@ -237,8 +237,10 @@ static void on_item_is_cut_changed (NautilusNameCell *self) { gboolean is_cut; + g_autoptr (NautilusViewItem) item = NULL; - g_object_get (nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)), + item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); + g_object_get (item, "is-cut", &is_cut, NULL); if (is_cut) diff --git a/src/nautilus-star-cell.c b/src/nautilus-star-cell.c index a47a6b2ffe..9f21028089 100644 --- a/src/nautilus-star-cell.c +++ b/src/nautilus-star-cell.c @@ -27,7 +27,7 @@ on_star_click_released (GtkGestureClick *gesture, { NautilusStarCell *self = user_data; NautilusTagManager *tag_manager = nautilus_tag_manager_get (); - NautilusViewItem *item; + g_autoptr (NautilusViewItem) item = NULL; NautilusFile *file; g_autofree gchar *uri = NULL; @@ -77,7 +77,7 @@ update_star (GtkImage *star, static void on_file_changed (NautilusStarCell *self) { - NautilusViewItem *item; + g_autoptr (NautilusViewItem) item = NULL; NautilusFile *file; g_autofree gchar *string = NULL; @@ -94,7 +94,7 @@ on_starred_changed (NautilusTagManager *tag_manager, gpointer user_data) { NautilusStarCell *self = user_data; - NautilusViewItem *item; + g_autoptr (NautilusViewItem) item = NULL; NautilusFile *file; item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); -- GitLab From 62d375898555f2199e98e28465197915e6efb850 Mon Sep 17 00:00:00 2001 From: Corey Berla Date: Tue, 27 Sep 2022 08:21:09 -0700 Subject: [PATCH 02/10] list-view: Stop adding NameCell child twice The child of the list item is already set in setup_cell_common() --- src/nautilus-list-view.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/nautilus-list-view.c b/src/nautilus-list-view.c index 032e3a9da1..2715e7e0e7 100644 --- a/src/nautilus-list-view.c +++ b/src/nautilus-list-view.c @@ -935,8 +935,6 @@ setup_name_cell (GtkSignalListItemFactory *factory, nautilus_name_cell_show_snippet (NAUTILUS_NAME_CELL (cell)); } - gtk_list_item_set_child (listitem, GTK_WIDGET (cell)); - setup_selection_click_workaround (cell); } -- GitLab From a2f20d1c841cdb76b06a5ab6037f8b4d53622495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Fernandes?= Date: Thu, 20 Oct 2022 17:51:29 +0100 Subject: [PATCH 03/10] files-view: Drop .compare_files() vfunc It gets called at a time when the new files haven't made it into the view mode yet. So, we create a pair of dummy items for the sorter to work on, and then throw them away. Creating and destroying object instances at every step in the sorting algorithm wastes a lot of cycles while loading large folders, with visible performance impact. So, instead of sorting before the view items are created, do it when they are created, right before they are added to the model's internal list store. --- src/nautilus-files-view.c | 38 -------------------------------------- src/nautilus-files-view.h | 8 -------- src/nautilus-list-base.c | 25 ------------------------- src/nautilus-view-model.c | 4 ++++ 4 files changed, 4 insertions(+), 71 deletions(-) diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c index f19fab7123..afc57e0974 100644 --- a/src/nautilus-files-view.c +++ b/src/nautilus-files-view.c @@ -4085,38 +4085,6 @@ ready_to_load (NautilusFile *file) NAUTILUS_FILE_ATTRIBUTES_FOR_ICON); } -static int -compare_files_cover (gconstpointer a, - gconstpointer b, - gpointer callback_data) -{ - const FileAndDirectory *fad1, *fad2; - NautilusFilesView *view; - - view = callback_data; - fad1 = a; - fad2 = b; - - if (fad1->directory < fad2->directory) - { - return -1; - } - else if (fad1->directory > fad2->directory) - { - return 1; - } - else - { - return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->compare_files (view, fad1->file, fad2->file); - } -} -static void -sort_files (NautilusFilesView *view, - GList **list) -{ - *list = g_list_sort_with_data (*list, compare_files_cover, view); -} - /* Go through all the new added and changed files. * Put any that are not ready to load in the non_ready_files hash table. * Add all the rest to the old_added_files and old_changed_files lists. @@ -4203,20 +4171,14 @@ process_new_files (NautilusFilesView *view) } } - /* If any files were added to old_added_files, then resort it. */ if (old_added_files != priv->old_added_files) { priv->old_added_files = old_added_files; - sort_files (view, &priv->old_added_files); } - /* Resort old_changed_files too, since file attributes - * relevant to sorting could have changed. - */ if (old_changed_files != priv->old_changed_files) { priv->old_changed_files = old_changed_files; - sort_files (view, &priv->old_changed_files); } } diff --git a/src/nautilus-files-view.h b/src/nautilus-files-view.h index e1882b7ba0..74375a7ccc 100644 --- a/src/nautilus-files-view.h +++ b/src/nautilus-files-view.h @@ -183,14 +183,6 @@ struct _NautilusFilesViewClass { void (* update_actions_state) (NautilusFilesView *view); - /* sort_files is a function pointer that subclasses can override - * to provide a sorting order to determine which files should be - * presented when only a partial list is provided. - */ - int (* compare_files) (NautilusFilesView *view, - NautilusFile *a, - NautilusFile *b); - /* is_empty is a function pointer that subclasses must * override to report whether the view contains any items. */ diff --git a/src/nautilus-list-base.c b/src/nautilus-list-base.c index c1f85e6d12..04a02cb2ae 100644 --- a/src/nautilus-list-base.c +++ b/src/nautilus-list-base.c @@ -1299,30 +1299,6 @@ real_reveal_selection (NautilusFilesView *files_view) nautilus_list_base_scroll_to_item (self, get_first_selected_item (self)); } -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 (NautilusViewItem) item1 = NULL; - g_autoptr (NautilusViewItem) 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_new (file1, NAUTILUS_GRID_ICON_SIZE_SMALL); - item2 = nautilus_view_item_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, @@ -1737,7 +1713,6 @@ nautilus_list_base_class_init (NautilusListBaseClass *klass) 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; diff --git a/src/nautilus-view-model.c b/src/nautilus-view-model.c index 0ce8067139..ba59329f2a 100644 --- a/src/nautilus-view-model.c +++ b/src/nautilus-view-model.c @@ -391,6 +391,10 @@ nautilus_view_model_add_items (NautilusViewModel *self, GList *l; int i = 0; + /* Sort items before adding them to the internal model. This ensures that + * the first sorted item is become the initial focus and scroll anchor. */ + g_queue_sort (items, compare_data_func, self); + array = g_malloc_n (g_queue_get_length (items), sizeof (NautilusViewItem *)); -- GitLab From 6c1c43723e76cf130f4fa2270f597d3396d97937 Mon Sep 17 00:00:00 2001 From: Corey Berla Date: Tue, 27 Sep 2022 15:20:49 -0700 Subject: [PATCH 04/10] vfs-file: Handle setting metadata with a NULL list Previous to 013edfaa7e567560baf2718ec75980b0118c93da, we had a special case for handling NULL values when calling nautilus_file_set_metadata_list, but after that commit, the NULL was passed directly to g_file_info_set_attribute_stringv() which is not allow. This broke the "Reset to Default" button in the column chooser. g_file_info_remove_attribute() does not produce the expected behavior so simply set the attribute string to an empty string. --- src/nautilus-vfs-file.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/nautilus-vfs-file.c b/src/nautilus-vfs-file.c index d9d288e43b..9e6038c527 100644 --- a/src/nautilus-vfs-file.c +++ b/src/nautilus-vfs-file.c @@ -203,7 +203,14 @@ vfs_file_set_metadata_as_list (NautilusFile *file, info = g_file_info_new (); gio_key = g_strconcat ("metadata::", key, NULL); - g_file_info_set_attribute_stringv (info, gio_key, value); + if (value == NULL) + { + g_file_info_set_attribute (info, gio_key, G_FILE_ATTRIBUTE_TYPE_INVALID, NULL); + } + else + { + g_file_info_set_attribute_stringv (info, gio_key, value); + } g_free (gio_key); location = nautilus_file_get_location (file); -- GitLab From c7bc615bad116032f8d7ae78e5d8d7dfcd28fbd6 Mon Sep 17 00:00:00 2001 From: Corey Berla Date: Mon, 10 Oct 2022 12:58:59 -0700 Subject: [PATCH 05/10] column-chooser: Don't allow "name" column to be hidden During the population of the TreeView for the column-chooser, the "sensitive" property disallows toggling the toggle for the "Name" column. The ::row-activated signal on the TreeView, however, allows the "Name" column to be toggled because the ::row-activated signal is on the TreeView itself (rather than the cell). Either we could 1) remove ::row-activated altogether (double clicking the display name) or 2) check to make sure we aren't toggling "Name". I chose #2. --- src/nautilus-column-chooser.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/nautilus-column-chooser.c b/src/nautilus-column-chooser.c index 23aa5af5c5..72e18bcc39 100644 --- a/src/nautilus-column-chooser.c +++ b/src/nautilus-column-chooser.c @@ -155,11 +155,20 @@ toggle_path (NautilusColumnChooser *chooser, { GtkTreeIter iter; gboolean visible; + g_autofree gchar *name = NULL; gtk_tree_model_get_iter (GTK_TREE_MODEL (chooser->store), &iter, path); - gtk_tree_model_get (GTK_TREE_MODEL (chooser->store), - &iter, COLUMN_VISIBLE, &visible, -1); + gtk_tree_model_get (GTK_TREE_MODEL (chooser->store), &iter, + COLUMN_VISIBLE, &visible, + COLUMN_NAME, &name, -1); + + if (g_strcmp0 (name, "name") == 0) + { + /* Don't allow name column to be disabled. */ + return; + } + gtk_list_store_set (chooser->store, &iter, COLUMN_VISIBLE, !visible, -1); list_changed (chooser); -- GitLab From 6d67376cd99064b737f29d83dc41c11857bcc1f0 Mon Sep 17 00:00:00 2001 From: Corey Berla Date: Thu, 13 Oct 2022 13:28:11 -0700 Subject: [PATCH 06/10] list-base: Make sort constants match attribute names In columnview we are setting them using the attribute value and they should match the toolbar dropdown. --- src/nautilus-list-base.c | 8 ++++---- src/resources/ui/nautilus-toolbar-view-menu.ui | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/nautilus-list-base.c b/src/nautilus-list-base.c index 04a02cb2ae..21c49707f2 100644 --- a/src/nautilus-list-base.c +++ b/src/nautilus-list-base.c @@ -82,19 +82,19 @@ static const SortConstants sorts_constants[] = }, { NAUTILUS_FILE_SORT_BY_MTIME, - "modification date", + "date_modified", }, { NAUTILUS_FILE_SORT_BY_ATIME, - "access date", + "date_accessed", }, { NAUTILUS_FILE_SORT_BY_BTIME, - "creation date", + "date_created", }, { NAUTILUS_FILE_SORT_BY_TRASHED_TIME, - "trashed", + "trashed_on", }, { NAUTILUS_FILE_SORT_BY_SEARCH_RELEVANCE, diff --git a/src/resources/ui/nautilus-toolbar-view-menu.ui b/src/resources/ui/nautilus-toolbar-view-menu.ui index 03c6e8e8d2..ffdd3ac077 100644 --- a/src/resources/ui/nautilus-toolbar-view-menu.ui +++ b/src/resources/ui/nautilus-toolbar-view-menu.ui @@ -16,13 +16,13 @@ view.sort - ('modification date',true) + ('date_modified',true) Last _Modified action-disabled view.sort - ('modification date',false) + ('date_modified',false) _First Modified action-disabled @@ -40,7 +40,7 @@ view.sort - ('trashed',true) + ('trashed_on',true) Last _Trashed action-disabled last_trashed -- GitLab From 3dfac319ec6d3c2871aafb57b46717ed11c84fc6 Mon Sep 17 00:00:00 2001 From: Corey Berla Date: Thu, 13 Oct 2022 13:29:55 -0700 Subject: [PATCH 07/10] list-view: Block using the correct sorter In action_sort_order_changed() we are blocking emission of the ::changed signal, so that sorter_changed() isn't called when we re-sort the column, but inadvertantly it's being blocked on the wrong sorter. --- src/nautilus-list-view.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nautilus-list-view.c b/src/nautilus-list-view.c index 2715e7e0e7..a525cf4387 100644 --- a/src/nautilus-list-view.c +++ b/src/nautilus-list-view.c @@ -606,6 +606,7 @@ action_sort_order_changed (GSimpleAction *action, NautilusFileSortType sort_type; NautilusListView *self; GListModel *view_columns; + NautilusViewModel *model; g_autoptr (GtkColumnViewColumn) sort_column = NULL; GtkSorter *sorter; @@ -668,7 +669,8 @@ action_sort_order_changed (GSimpleAction *action, } } - sorter = gtk_column_view_get_sorter (self->view_ui); + model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self)); + sorter = nautilus_view_model_get_sorter (model); g_signal_handlers_block_by_func (sorter, on_sorter_changed, self); /* FIXME: Set NULL to stop drawing the arrow on previous sort column -- GitLab From c72e6def48e47454d4b620f58c93753a8a1b6a3f Mon Sep 17 00:00:00 2001 From: Corey Berla Date: Thu, 13 Oct 2022 13:39:50 -0700 Subject: [PATCH 08/10] list-view: Keep track of the most recently sorted column In GtkColumnView, we don't know what column is being sorted on from clicking on the column headers. This means that we can only save the sort order when the sort is intiated from the toolbar menu rather than the column headers. As a workaround, keep track of the most recently used attribute used in the sorter. Then when ::changed is emmited on the sorter, update the metadata for what was used while sorting. This is a pretty bad hack, but the current state is worse because 1) The sort order is never saved in column view 2) If the sort order was changed (i.e. in GridView), the user can't figure out why the order keeps reverting back to another state. --- src/nautilus-list-view.c | 112 ++++++++++++++++++++++++++++++++++----- 1 file changed, 98 insertions(+), 14 deletions(-) diff --git a/src/nautilus-list-view.c b/src/nautilus-list-view.c index a525cf4387..4c5ab3eafb 100644 --- a/src/nautilus-list-view.c +++ b/src/nautilus-list-view.c @@ -44,6 +44,10 @@ struct _NautilusListView GtkColumnViewColumn *star_column; GtkWidget *column_editor; GHashTable *factory_to_column_map; + + /* Column sort hack state */ + gboolean column_header_was_clicked; + GQuark clicked_column_attribute_q; }; G_DEFINE_TYPE (NautilusListView, nautilus_list_view, NAUTILUS_TYPE_LIST_BASE) @@ -246,15 +250,34 @@ real_scroll_to_item (NautilusListBase *list_base_view, } } +typedef struct +{ + GQuark attribute; + NautilusListView *view; +} SortData; + static gint nautilus_list_view_sort (gconstpointer a, gconstpointer b, gpointer user_data) { - GQuark attribute_q = GPOINTER_TO_UINT (user_data); + SortData *data = user_data; + NautilusListView *self = data->view; + GQuark attribute_q = data->attribute; NautilusFile *file_a = nautilus_view_item_get_file (NAUTILUS_VIEW_ITEM ((gpointer) a)); NautilusFile *file_b = nautilus_view_item_get_file (NAUTILUS_VIEW_ITEM ((gpointer) b)); + /* Hack: We don't know what column is being sorted on when the column + * headers are clicked. So let's just look at what attribute was most + * recently used for sorting. + * https://gitlab.gnome.org/GNOME/gtk/-/issues/4833 */ + if (self->clicked_column_attribute_q == 0 && self->column_header_was_clicked) + { + self->clicked_column_attribute_q = attribute_q; + } + + g_return_val_if_fail (file_a != NULL && file_b != NULL, GTK_ORDERING_EQUAL); + /* The reversed argument is FALSE because the columnview sorter handles that * itself and if we don't want to reverse the reverse. The directories_first * argument is also FALSE for the same reason: we don't want the columnview @@ -672,12 +695,18 @@ action_sort_order_changed (GSimpleAction *action, model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self)); sorter = nautilus_view_model_get_sorter (model); - g_signal_handlers_block_by_func (sorter, on_sorter_changed, self); - /* FIXME: Set NULL to stop drawing the arrow on previous sort column - * to workaround https://gitlab.gnome.org/GNOME/gtk/-/issues/4696 */ - gtk_column_view_sort_by_column (self->view_ui, NULL, FALSE); - gtk_column_view_sort_by_column (self->view_ui, sort_column, reversed); - g_signal_handlers_unblock_by_func (sorter, on_sorter_changed, self); + /* Ask the column view to sort by column if it hasn't just done so already. */ + if (!self->column_header_was_clicked) + { + g_signal_handlers_block_by_func (sorter, on_sorter_changed, self); + /* FIXME: Set NULL to stop drawing the arrow on previous sort column + * to workaround https://gitlab.gnome.org/GNOME/gtk/-/issues/4696 */ + gtk_column_view_sort_by_column (self->view_ui, NULL, FALSE); + gtk_column_view_sort_by_column (self->view_ui, sort_column, reversed); + g_signal_handlers_unblock_by_func (sorter, on_sorter_changed, self); + } + + self->column_header_was_clicked = FALSE; set_directory_sort_metadata (nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self)), target_name, @@ -744,6 +773,7 @@ real_begin_loading (NautilusFilesView *files_view) NAUTILUS_FILES_VIEW_CLASS (nautilus_list_view_parent_class)->begin_loading (files_view); update_columns_settings_from_metadata_and_preferences (self); + self->clicked_column_attribute_q = 0; self->path_attribute_q = 0; g_clear_object (&self->file_path_base_location); @@ -852,12 +882,60 @@ on_sorter_changed (GtkSorter *sorter, gpointer user_data) { NautilusListView *self = NAUTILUS_LIST_VIEW (user_data); + NautilusViewModel *model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self)); - /* When user clicks a header to change sort order, we don't know what the - * new sort order is. Make sure the sort menu doesn't indicate a outdated - * action state. */ - g_action_group_change_action_state (nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self)), - "sort", g_variant_new ("(sb)", "unknown", FALSE)); + /* Set the conditions to capture the sort attribute the first time that + * nautilus_list_view_sort() is called. */ + self->column_header_was_clicked = TRUE; + self->clicked_column_attribute_q = 0; + + /* If there is only one file, enforce a comparison against a dummy item, to + * ensure nautilus_list_view_sort() gets called at least once. */ + if (g_list_model_get_n_items (G_LIST_MODEL (model)) == 1) + { + NautilusViewItem *item = g_list_model_get_item (G_LIST_MODEL (model), 0); + g_autoptr (NautilusViewItem) dummy_item = NULL; + + dummy_item = nautilus_view_item_new (nautilus_view_item_get_file (item), + NAUTILUS_LIST_ICON_SIZE_SMALL); + + gtk_sorter_compare (sorter, item, dummy_item); + } +} + +static void +on_after_sorter_changed (GtkSorter *sorter, + GtkSorterChange change, + gpointer user_data) +{ + NautilusListView *self = NAUTILUS_LIST_VIEW (user_data); + GActionGroup *action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self)); + g_autoptr (GVariant) state = NULL; + const gchar *new_sort_text; + gboolean reversed; + g_autofree gchar *current_sort_text = NULL; + + if (!self->column_header_was_clicked || self->clicked_column_attribute_q == 0) + { + return; + } + + state = g_action_group_get_action_state (action_group, "sort"); + g_variant_get (state, "(&sb)", ¤t_sort_text, &reversed); + + new_sort_text = g_quark_to_string (self->clicked_column_attribute_q); + + if (g_strcmp0 (new_sort_text, current_sort_text) == 0) + { + reversed = !reversed; + } + else + { + reversed = FALSE; + } + + g_action_group_change_action_state (action_group, "sort", + g_variant_new ("(sb)", new_sort_text, reversed)); } static guint @@ -1025,6 +1103,7 @@ setup_view_columns (NautilusListView *self) for (GList *l = nautilus_columns; l != NULL; l = l->next) { NautilusColumn *nautilus_column = NAUTILUS_COLUMN (l->data); + SortData *data; g_autofree gchar *name = NULL; g_autofree gchar *label = NULL; GQuark attribute_q = 0; @@ -1039,9 +1118,13 @@ setup_view_columns (NautilusListView *self) "default-sort-order", &sort_order, NULL); + data = g_new0 (SortData, 1); + data->attribute = attribute_q; + data->view = self; + sorter = gtk_custom_sorter_new (nautilus_list_view_sort, - GUINT_TO_POINTER (attribute_q), - NULL); + data, + g_free); factory = gtk_signal_list_item_factory_new (); view_column = gtk_column_view_column_new (NULL, factory); @@ -1115,6 +1198,7 @@ nautilus_list_view_init (NautilusListView *self) gtk_multi_sorter_append (sorter, g_object_ref (GTK_SORTER (directories_sorter))); gtk_multi_sorter_append (sorter, g_object_ref (gtk_column_view_get_sorter (self->view_ui))); g_signal_connect_object (sorter, "changed", G_CALLBACK (on_sorter_changed), self, 0); + g_signal_connect_object (sorter, "changed", G_CALLBACK (on_after_sorter_changed), self, G_CONNECT_AFTER); model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self)); nautilus_view_model_set_sorter (model, GTK_SORTER (sorter)); -- GitLab From edc2d4ba20252c22dc3645b8e0173dbcd58ac9e9 Mon Sep 17 00:00:00 2001 From: Corey Berla Date: Sun, 16 Oct 2022 09:43:47 -0700 Subject: [PATCH 09/10] list-view: Only insert columns when they should be visible In a nautilus-list-view, we create factories for the various cells within each GtkListItem. This is designed with performance in mind... we don't want to create hundreds of thousands of objects in a folder where we may only look at a small number of files. We are proactively creating every possibly LabelCell without checking which one is visible by appending all of the columns in setup_view_columns. This means that even though the GtkListView may only request a couple hundred ListItems, we end up generating thousands of LabelCells. There are currently 13 available columns of which 3 are enabled by default. Insert the columns into the columnview only when they are selected to be visible. Fixes: https://gitlab.gnome.org/GNOME/nautilus/-/issues/2498 --- src/nautilus-list-view.c | 43 ++++++++++++++-------------------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/src/nautilus-list-view.c b/src/nautilus-list-view.c index 4c5ab3eafb..58a75f857b 100644 --- a/src/nautilus-list-view.c +++ b/src/nautilus-list-view.c @@ -45,6 +45,8 @@ struct _NautilusListView GtkWidget *column_editor; GHashTable *factory_to_column_map; + GHashTable *all_view_columns_hash; + /* Column sort hack state */ gboolean column_header_was_clicked; GQuark clicked_column_attribute_q; @@ -121,7 +123,6 @@ apply_columns_settings (NautilusListView *self, g_autoptr (GList) view_columns = NULL; GListModel *old_view_columns; g_autoptr (GHashTable) visible_columns_hash = NULL; - g_autoptr (GHashTable) old_view_columns_hash = NULL; int column_i = 0; file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self)); @@ -166,25 +167,7 @@ apply_columns_settings (NautilusListView *self, } } - old_view_columns_hash = g_hash_table_new_full (g_str_hash, - g_str_equal, - (GDestroyNotify) g_free, - NULL); old_view_columns = gtk_column_view_get_columns (self->view_ui); - for (guint i = 0; i < g_list_model_get_n_items (old_view_columns); i++) - { - g_autoptr (GtkColumnViewColumn) view_column = NULL; - GtkListItemFactory *factory; - NautilusColumn *nautilus_column; - gchar *name; - - view_column = g_list_model_get_item (old_view_columns, i); - factory = gtk_column_view_column_get_factory (view_column); - nautilus_column = g_hash_table_lookup (self->factory_to_column_map, factory); - g_object_get (nautilus_column, "name", &name, NULL); - g_hash_table_insert (old_view_columns_hash, name, view_column); - } - for (GList *l = all_columns; l != NULL; l = l->next) { g_autofree char *name = NULL; @@ -197,7 +180,7 @@ apply_columns_settings (NautilusListView *self, { GtkColumnViewColumn *view_column; - view_column = g_hash_table_lookup (old_view_columns_hash, name); + view_column = g_hash_table_lookup (self->all_view_columns_hash, name); if (view_column != NULL) { view_columns = g_list_prepend (view_columns, view_column); @@ -215,11 +198,7 @@ apply_columns_settings (NautilusListView *self, view_column = g_list_model_get_item (old_view_columns, i); if (g_list_find (view_columns, view_column) == NULL) { - gtk_column_view_column_set_visible (view_column, FALSE); - } - else - { - gtk_column_view_column_set_visible (view_column, TRUE); + gtk_column_view_remove_column (self->view_ui, view_column); } } @@ -770,9 +749,11 @@ real_begin_loading (NautilusFilesView *files_view) NautilusListView *self = NAUTILUS_LIST_VIEW (files_view); NautilusFile *file; + /* We need to setup the columns before chaining up */ + update_columns_settings_from_metadata_and_preferences (self); + NAUTILUS_FILES_VIEW_CLASS (nautilus_list_view_parent_class)->begin_loading (files_view); - update_columns_settings_from_metadata_and_preferences (self); self->clicked_column_attribute_q = 0; self->path_attribute_q = 0; @@ -1099,6 +1080,10 @@ setup_view_columns (NautilusListView *self) g_direct_equal, NULL, g_object_unref); + self->all_view_columns_hash = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + g_object_unref); for (GList *l = nautilus_columns; l != NULL; l = l->next) { @@ -1155,11 +1140,12 @@ setup_view_columns (NautilusListView *self) g_signal_connect (factory, "setup", G_CALLBACK (setup_label_cell), self); } - gtk_column_view_append_column (self->view_ui, view_column); - g_hash_table_insert (self->factory_to_column_map, factory, g_object_ref (nautilus_column)); + g_hash_table_insert (self->all_view_columns_hash, + g_steal_pointer (&name), + g_steal_pointer (&view_column)); } } @@ -1224,6 +1210,7 @@ nautilus_list_view_dispose (GObject *object) g_clear_object (&self->file_path_base_location); g_clear_pointer (&self->factory_to_column_map, g_hash_table_destroy); + g_clear_pointer (&self->all_view_columns_hash, g_hash_table_destroy); G_OBJECT_CLASS (nautilus_list_view_parent_class)->dispose (object); } -- GitLab From a81aeac7fe22edbe37f1bc87afe8272b5e71980e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Fernandes?= Date: Fri, 21 Oct 2022 00:22:27 +0100 Subject: [PATCH 10/10] list-view: Set model sorter to NULL on dispose This avoids future emissions of a "changed" signal during destruction. Also fix setter not to connect to signals when the sorter is NULL. --- src/nautilus-list-view.c | 4 ++++ src/nautilus-view-model.c | 9 ++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/nautilus-list-view.c b/src/nautilus-list-view.c index 58a75f857b..7960d6ad89 100644 --- a/src/nautilus-list-view.c +++ b/src/nautilus-list-view.c @@ -1207,6 +1207,10 @@ static void nautilus_list_view_dispose (GObject *object) { NautilusListView *self = NAUTILUS_LIST_VIEW (object); + NautilusViewModel *model; + + model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self)); + nautilus_view_model_set_sorter (model, NULL); g_clear_object (&self->file_path_base_location); g_clear_pointer (&self->factory_to_column_map, g_hash_table_destroy); diff --git a/src/nautilus-view-model.c b/src/nautilus-view-model.c index ba59329f2a..2d472ef6ca 100644 --- a/src/nautilus-view-model.c +++ b/src/nautilus-view-model.c @@ -301,9 +301,12 @@ nautilus_view_model_set_sorter (NautilusViewModel *self, g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SORTER]); } - self->sorter_changed_id = g_signal_connect (self->sorter, "changed", - G_CALLBACK (on_sorter_changed), self); - g_list_store_sort (self->internal_model, compare_data_func, self); + if (self->sorter != NULL) + { + self->sorter_changed_id = g_signal_connect (self->sorter, "changed", + G_CALLBACK (on_sorter_changed), self); + g_list_store_sort (self->internal_model, compare_data_func, self); + } } GQueue * -- GitLab