From a09aa95bf1f4b0e5f46c3e823b02f8e5f83c7621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Fernandes?= Date: Sun, 31 Jul 2022 16:29:17 +0100 Subject: [PATCH 1/7] files-view: Rebuild context menus every time In GTK3, we would reuse the same GtkMenu, but update the model. With GtkPopoverMenu, this is creating duplicate stack page each time our submenus are updated. And it turns out we update templates and scripts menu a lot of times, on directory monitor callbacks! Besides warnings, this causes increasing memory consumptions. Additionally, reusing the same popover while updating the model causes the old model do be temporarily displayed when the popover is opened, which sometimes even causes the popover to resize and jump around. This is obviously bad. Avoid both problems by creating a new popover menu every time we open the context menu. The old one is destroyed (by unparenting) right before this. (Not on GtkPopover::closed, because this would be too early and actions would fail to activate!) --- src/nautilus-files-view.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c index 1ba862c7a1..6df8e7a19f 100644 --- a/src/nautilus-files-view.c +++ b/src/nautilus-files-view.c @@ -8283,14 +8283,14 @@ nautilus_files_view_pop_up_selection_context_menu (NautilusFilesView *view, */ update_context_menus_if_pending (view); - if (NULL == priv->selection_menu) - { - priv->selection_menu = gtk_popover_menu_new_from_model (NULL); - gtk_widget_set_parent (priv->selection_menu, GTK_WIDGET (view)); - gtk_popover_set_has_arrow (GTK_POPOVER (priv->selection_menu), FALSE); - gtk_widget_set_halign (priv->selection_menu, GTK_ALIGN_START); - g_signal_connect (priv->selection_menu, "destroy", G_CALLBACK (gtk_widget_unparent), NULL); - } + /* Destroy old popover and create a new one, to avoid duplicate submenu bugs + * and showing old model temporarily. We don't do this when popover is + * closed because it wouldn't activate the actions then. */ + g_clear_pointer (&priv->selection_menu, gtk_widget_unparent); + priv->selection_menu = gtk_popover_menu_new_from_model (NULL); + gtk_widget_set_parent (priv->selection_menu, GTK_WIDGET (view)); + gtk_popover_set_has_arrow (GTK_POPOVER (priv->selection_menu), FALSE); + gtk_widget_set_halign (priv->selection_menu, GTK_ALIGN_START); gtk_popover_menu_set_menu_model (GTK_POPOVER_MENU (priv->selection_menu), G_MENU_MODEL (priv->selection_menu_model)); @@ -8335,15 +8335,15 @@ nautilus_files_view_pop_up_background_context_menu (NautilusFilesView *view, */ update_context_menus_if_pending (view); + /* Destroy old popover and create a new one, to avoid duplicate submenu bugs + * and showing old model temporarily. We don't do this when popover is + * closed because it wouldn't activate the actions then. */ + g_clear_pointer (&priv->background_menu, gtk_widget_unparent); + priv->background_menu = gtk_popover_menu_new_from_model (NULL); + gtk_widget_set_parent (priv->background_menu, GTK_WIDGET (view)); + gtk_popover_set_has_arrow (GTK_POPOVER (priv->background_menu), FALSE); + gtk_widget_set_halign (priv->background_menu, GTK_ALIGN_START); - if (NULL == priv->background_menu) - { - priv->background_menu = gtk_popover_menu_new_from_model (NULL); - gtk_widget_set_parent (priv->background_menu, GTK_WIDGET (view)); - gtk_popover_set_has_arrow (GTK_POPOVER (priv->background_menu), FALSE); - gtk_widget_set_halign (priv->background_menu, GTK_ALIGN_START); - g_signal_connect (priv->background_menu, "destroy", G_CALLBACK (gtk_widget_unparent), NULL); - } gtk_popover_menu_set_menu_model (GTK_POPOVER_MENU (priv->background_menu), G_MENU_MODEL (priv->background_menu_model)); -- GitLab From e88b7d5882c503910b1c3fa8b89d1bf49ddbcff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Fernandes?= Date: Sun, 31 Jul 2022 19:33:57 +0100 Subject: [PATCH 2/7] files-view: Stop setting actions on submenus We show Templates and Scripts submenus conditionally, using the hidden-when=action-disabled attribute, by assigning a dummy action which we can set as active or inactive as fit. However, this causes criticals when the popover menu is destroyed: (org.gnome.Nautilus:21502): GLib-CRITICAL **: 16:15:32.870: g_hash_table_iter_next: assertion 'ri->version == ri->hash_table->version' failed Indeed, submenus are not supposed to have actions. There is a "submenu-action" but it's for a different purpose. So, instead of controlling the visibility, and taking advantage of the fact that our models are rebuilt each time they need an update, just remove the submenu if not desirable. --- src/nautilus-files-view.c | 43 ++++++++++++------- .../ui/nautilus-files-view-context-menus.ui | 6 +-- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c index 6df8e7a19f..7be5722f7f 100644 --- a/src/nautilus-files-view.c +++ b/src/nautilus-files-view.c @@ -7043,9 +7043,7 @@ const GActionEntry view_entries[] = { "paste_accel", action_paste_files_accel }, { "create-link", action_create_links }, { "create-link-shortcut", action_create_links }, - { "new-document" }, /* Selection menu */ - { "scripts" }, { "new-folder-with-selection", action_new_folder_with_selection }, { "open-scripts-folder", action_open_scripts_folder }, { "open-item-location", action_open_item_location }, @@ -7748,11 +7746,6 @@ real_update_actions_state (NautilusFilesView *view) g_simple_action_set_enabled (G_SIMPLE_ACTION (action), show_detect_media); - action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group), - "scripts"); - g_simple_action_set_enabled (G_SIMPLE_ACTION (action), - priv->scripts_menu != NULL); - /* Background menu actions */ action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group), "open-current-directory-with-other-application"); @@ -7783,13 +7776,6 @@ real_update_actions_state (NautilusFilesView *view) "current-directory-properties"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !selection_contains_search); - action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group), - "new-document"); - g_simple_action_set_enabled (G_SIMPLE_ACTION (action), - can_create_files && - !selection_contains_recent && - !selection_contains_starred && - priv->templates_menu != NULL); /* Actions that are related to the clipboard need request, request the data * and update them once we have the data */ @@ -8128,8 +8114,19 @@ update_selection_menu (NautilusFilesView *view, update_scripts_menu (view, builder); priv->scripts_menu_updated = TRUE; } - object = gtk_builder_get_object (builder, "scripts-submenu-section"); - nautilus_gmenu_set_from_model (G_MENU (object), priv->scripts_menu); + + if (priv->scripts_menu != NULL) + { + object = gtk_builder_get_object (builder, "scripts-submenu-section"); + nautilus_gmenu_set_from_model (G_MENU (object), priv->scripts_menu); + } + else + { + gint i = nautilus_g_menu_model_find_by_string (G_MENU_MODEL (priv->selection_menu_model), + "nautilus-menu-item", + "scripts-submenu"); + g_menu_remove (priv->selection_menu_model, i); + } } static void @@ -8138,6 +8135,7 @@ update_background_menu (NautilusFilesView *view, { NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (view); GObject *object; + gboolean remove_submenu = TRUE; if (nautilus_files_view_supports_creating_files (view) && !showing_recent_directory (view) && @@ -8151,6 +8149,19 @@ update_background_menu (NautilusFilesView *view, object = gtk_builder_get_object (builder, "templates-submenu"); nautilus_gmenu_set_from_model (G_MENU (object), priv->templates_menu); + + if (priv->templates_menu != NULL) + { + remove_submenu = FALSE; + } + } + + if (remove_submenu) + { + gint i = nautilus_g_menu_model_find_by_string (G_MENU_MODEL (priv->background_menu_model), + "nautilus-menu-item", + "templates-submenu"); + g_menu_remove (priv->background_menu_model, i); } } diff --git a/src/resources/ui/nautilus-files-view-context-menus.ui b/src/resources/ui/nautilus-files-view-context-menus.ui index a88e5c5a0d..ebbe9f5d89 100644 --- a/src/resources/ui/nautilus-files-view-context-menus.ui +++ b/src/resources/ui/nautilus-files-view-context-menus.ui @@ -8,8 +8,7 @@ New _Document - view.new-document - action-disabled + templates-submenu Add to _Bookmarks @@ -72,8 +71,7 @@ _Scripts - view.scripts - action-disabled + scripts-submenu
-- GitLab From ce055de037da1edc643ca3e47c40c21ae08c6152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Fernandes?= Date: Sun, 31 Jul 2022 21:52:07 +0100 Subject: [PATCH 3/7] toolbar: Move "Visible Columns" menu item to folder menu It would be nice to use gtk_column_view_column_set_header_menu(), but unfortunately our GtkGestureClick for background context menu overrules it. Also, it would be bad for accessibility because there is no way to get keyboard focus in the column headers. The folder menu is a fitting new home because: 1. The column changes are saved only for this folder; 2. If the user right clicks the columns, expecting an option to change columns, the option will be right there. This is part of an effort to drop the view menu. --- src/resources/ui/nautilus-files-view-context-menus.ui | 5 +++++ src/resources/ui/nautilus-toolbar.ui | 7 ------- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/resources/ui/nautilus-files-view-context-menus.ui b/src/resources/ui/nautilus-files-view-context-menus.ui index ebbe9f5d89..44c58b5ca8 100644 --- a/src/resources/ui/nautilus-files-view-context-menus.ui +++ b/src/resources/ui/nautilus-files-view-context-menus.ui @@ -24,6 +24,11 @@ win.stop action-disabled + + _Visible Columns… + view.visible-columns + action-missing +
_Open With Other Application diff --git a/src/resources/ui/nautilus-toolbar.ui b/src/resources/ui/nautilus-toolbar.ui index 682fff3161..e42bb2c467 100644 --- a/src/resources/ui/nautilus-toolbar.ui +++ b/src/resources/ui/nautilus-toolbar.ui @@ -13,13 +13,6 @@ Its contents is provided by the view. -->
-
- - _Visible Columns… - view.visible-columns - action-missing - -
-- GitLab From dfac6f4fa32c8970f6dc7e63e8f29d4c0c55cd32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Fernandes?= Date: Sun, 31 Jul 2022 21:09:27 +0100 Subject: [PATCH 4/7] files-view: Add sort menu for grid view In list view mode we have a clear indication of sort order, thanks to the list column headers, which also allow to change sort order. Implement a sort header for grid view mode, which matches the visuals of the list one and provides indication of the current sort order. --- po/POTFILES.in | 1 + src/nautilus-files-view.c | 29 +++++++++++- src/nautilus-files-view.h | 6 ++- src/nautilus-grid-view.c | 62 +++++++++++++++++++++++++ src/resources/css/Adwaita.css | 19 +++++++- src/resources/ui/nautilus-files-view.ui | 15 +++++- 6 files changed, 127 insertions(+), 5 deletions(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index eb0f8f29dc..ab1b37ba23 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -28,6 +28,7 @@ src/nautilus-compress-dialog-controller.c src/nautilus-dbus-launcher.c src/nautilus-directory.c src/nautilus-dnd.c +src/nautilus-grid-view.c src/nautilus-error-reporting.c src/nautilus-file.c src/nautilus-file-conflict-dialog.c diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c index 7be5722f7f..92acb4aa47 100644 --- a/src/nautilus-files-view.c +++ b/src/nautilus-files-view.c @@ -246,6 +246,7 @@ typedef struct GtkWidget *stack; + GtkWidget *sort_menu_button; GtkWidget *scrolled_window; /* Empty states */ @@ -348,7 +349,7 @@ static gboolean nautilus_files_view_is_read_only (NautilusFilesView *view); G_DEFINE_TYPE_WITH_CODE (NautilusFilesView, nautilus_files_view, - ADW_TYPE_BIN, + GTK_TYPE_BOX, G_IMPLEMENT_INTERFACE (NAUTILUS_TYPE_VIEW, nautilus_files_view_iface_init) G_ADD_PRIVATE (NautilusFilesView)); @@ -4842,6 +4843,15 @@ nautilus_files_view_get_content_widget (NautilusFilesView *view) return priv->scrolled_window; } +void +nautilus_files_view_set_sort_label (NautilusFilesView *view, + const gchar *label) +{ + NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (view); + + gtk_menu_button_set_label (GTK_MENU_BUTTON (priv->sort_menu_button), label); +} + /* home_dir_in_selection() * * Return TRUE if the home directory is in the selection. @@ -9348,6 +9358,17 @@ nautilus_files_view_is_loading (NautilusView *view) return priv->loading; } +static void +nautilus_files_view_constructed (GObject *object) +{ + NautilusFilesView *self = NAUTILUS_FILES_VIEW (object); + NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (self); + + G_OBJECT_CLASS (nautilus_files_view_parent_class)->constructed (object); + + gtk_widget_set_visible (priv->sort_menu_button, NAUTILUS_IS_GRID_VIEW (self)); +} + static void nautilus_files_view_iface_init (NautilusViewInterface *iface) { @@ -9376,6 +9397,7 @@ nautilus_files_view_class_init (NautilusFilesViewClass *klass) widget_class = GTK_WIDGET_CLASS (klass); oclass = G_OBJECT_CLASS (klass); + oclass->constructed = nautilus_files_view_constructed; oclass->dispose = nautilus_files_view_dispose; oclass->finalize = nautilus_files_view_finalize; oclass->get_property = nautilus_files_view_get_property; @@ -9493,6 +9515,7 @@ nautilus_files_view_class_init (NautilusFilesViewClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/nautilus/ui/nautilus-files-view.ui"); + gtk_widget_class_bind_template_child_private (widget_class, NautilusFilesView, sort_menu_button); gtk_widget_class_bind_template_child_private (widget_class, NautilusFilesView, overlay); gtk_widget_class_bind_template_child_private (widget_class, NautilusFilesView, stack); gtk_widget_class_bind_template_child_private (widget_class, NautilusFilesView, empty_view_page); @@ -9603,6 +9626,9 @@ nautilus_files_view_init (NautilusFilesView *view) g_type_ensure (NAUTILUS_TYPE_FLOATING_BAR); gtk_widget_init_template (GTK_WIDGET (view)); + gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (priv->sort_menu_button), + priv->toolbar_menu_sections->sort_section); + controller = gtk_event_controller_scroll_new (GTK_EVENT_CONTROLLER_SCROLL_VERTICAL | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE); gtk_widget_add_controller (priv->scrolled_window, controller); @@ -9686,6 +9712,7 @@ nautilus_files_view_init (NautilusFilesView *view) gtk_widget_insert_action_group (GTK_WIDGET (view), "view", G_ACTION_GROUP (priv->view_action_group)); + app = g_application_get_default (); /* NOTE: Please do not add any key here that could interfere with diff --git a/src/nautilus-files-view.h b/src/nautilus-files-view.h index e1882b7ba0..dc2609a750 100644 --- a/src/nautilus-files-view.h +++ b/src/nautilus-files-view.h @@ -38,10 +38,10 @@ G_BEGIN_DECLS #define NAUTILUS_TYPE_FILES_VIEW nautilus_files_view_get_type() -G_DECLARE_DERIVABLE_TYPE (NautilusFilesView, nautilus_files_view, NAUTILUS, FILES_VIEW, AdwBin) +G_DECLARE_DERIVABLE_TYPE (NautilusFilesView, nautilus_files_view, NAUTILUS, FILES_VIEW, GtkBox) struct _NautilusFilesViewClass { - AdwBinClass parent_class; + GtkBoxClass parent_class; /* The 'clear' signal is emitted to empty the view of its contents. * It must be replaced by each subclass. @@ -321,5 +321,7 @@ void nautilus_files_view_action_show_hidden_files (NautilusFilesV GActionGroup * nautilus_files_view_get_action_group (NautilusFilesView *view); GtkWidget* nautilus_files_view_get_content_widget (NautilusFilesView *view); +void nautilus_files_view_set_sort_label (NautilusFilesView *view, + const gchar *label); G_END_DECLS diff --git a/src/nautilus-grid-view.c b/src/nautilus-grid-view.c index 1e8e54569b..a23bbd7d67 100644 --- a/src/nautilus-grid-view.c +++ b/src/nautilus-grid-view.c @@ -7,6 +7,8 @@ #include "nautilus-list-base-private.h" #include "nautilus-grid-view.h" +#include + #include "nautilus-grid-cell.h" #include "nautilus-global-preferences.h" @@ -261,6 +263,64 @@ real_sort_directories_first_changed (NautilusFilesView *files_view) nautilus_view_model_set_sorter (model, GTK_SORTER (sorter)); } +static void +update_sort_menu_label (NautilusGridView *self) +{ + const gchar *label; + + switch (self->sort_type) + { + case NAUTILUS_FILE_SORT_BY_DISPLAY_NAME: + { + label = self->reversed ? _("Z-A") : _("A-Z"); + } + break; + + case NAUTILUS_FILE_SORT_BY_MTIME: + { + label = self->reversed ? _("Last Modified") : _("First Modified"); + } + break; + + case NAUTILUS_FILE_SORT_BY_SIZE: + { + label = self->reversed ? _("Size") : _("Size (ascending)"); + } + break; + + case NAUTILUS_FILE_SORT_BY_TYPE: + { + label = self->reversed ? _("Type (reversed)") : _("Type"); + } + break; + + case NAUTILUS_FILE_SORT_BY_TRASHED_TIME: + { + label = self->reversed ? _("Last Trashed") : _("First Trashed"); + } + break; + + case NAUTILUS_FILE_SORT_BY_SEARCH_RELEVANCE: + { + label = self->reversed ? _("Relevance") : _("Least Relevant"); + } + break; + + case NAUTILUS_FILE_SORT_BY_RECENCY: + { + label = self->reversed ? _("Recency") : _("Recency (reversed)"); + } + break; + + default: + { + label = _("Sort"); + } + } + + nautilus_files_view_set_sort_label (NAUTILUS_FILES_VIEW (self), label); +} + static void action_sort_order_changed (GSimpleAction *action, GVariant *value, @@ -287,6 +347,8 @@ action_sort_order_changed (GSimpleAction *action, target_name, self->reversed); + update_sort_menu_label (self); + g_simple_action_set_state (action, value); } diff --git a/src/resources/css/Adwaita.css b/src/resources/css/Adwaita.css index f2628f3a98..a6a6466a18 100644 --- a/src/resources/css/Adwaita.css +++ b/src/resources/css/Adwaita.css @@ -144,8 +144,25 @@ } /* Grid view */ +.nautilus-grid-view .nautilus-sort-button button { + min-height: 20px; + margin-left: 24px; + margin-right: 24px; + background: none; +} +.nautilus-grid-view .nautilus-sort-button button:not(:hover):not(:checked) { + opacity: 0.4; +} + +.nautilus-grid-view scrolledwindow undershoot.top { + border-top: 1px solid @borders; +} + .nautilus-grid-view gridview { - padding: 15px; + padding-top: 12px; + padding-bottom: 21px; + padding-left: 21px; + padding-right: 21px; } .nautilus-grid-view gridview > child { diff --git a/src/resources/ui/nautilus-files-view.ui b/src/resources/ui/nautilus-files-view.ui index eab5bddbe6..db5e687796 100644 --- a/src/resources/ui/nautilus-files-view.ui +++ b/src/resources/ui/nautilus-files-view.ui @@ -1,11 +1,24 @@ -