diff options
author | António Fernandes <antoniof@gnome.org> | 2022-04-06 00:40:00 +0100 |
---|---|---|
committer | António Fernandes <antoniof@gnome.org> | 2023-01-10 01:11:33 +0000 |
commit | dd4dbde1bcebe033b60859cdf1d351620788f0a5 (patch) | |
tree | d73c8b01f2615403476bce5023ef9979d051e6dc /src | |
parent | d5ce96240ac0fc55ec64d3fcf756c84502f4e441 (diff) | |
download | nautilus-dd4dbde1bcebe033b60859cdf1d351620788f0a5.tar.gz |
list-view: Support expanding as a tree
Starred, Recent and Search still not supported due to bugs
Diffstat (limited to 'src')
-rw-r--r-- | src/nautilus-files-view.c | 10 | ||||
-rw-r--r-- | src/nautilus-files-view.h | 2 | ||||
-rw-r--r-- | src/nautilus-list-view.c | 162 | ||||
-rw-r--r-- | src/nautilus-name-cell.c | 8 | ||||
-rw-r--r-- | src/nautilus-name-cell.h | 1 | ||||
-rw-r--r-- | src/resources/ui/nautilus-name-cell.ui | 155 | ||||
-rw-r--r-- | src/resources/ui/nautilus-preferences-window.ui | 1 |
7 files changed, 262 insertions, 77 deletions
diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c index 047e71aa1..5fbee2bf7 100644 --- a/src/nautilus-files-view.c +++ b/src/nautilus-files-view.c @@ -4796,6 +4796,16 @@ load_error_callback (NautilusDirectory *directory, nautilus_files_view_get_containing_window (view)); } +gboolean +nautilus_files_view_has_subdirectory (NautilusFilesView *view, + NautilusDirectory *directory) +{ + NautilusFilesViewPrivate *priv; + priv = nautilus_files_view_get_instance_private (view); + + return g_list_find (priv->subdirectory_list, directory) != NULL; +} + void nautilus_files_view_add_subdirectory (NautilusFilesView *view, NautilusDirectory *directory) diff --git a/src/nautilus-files-view.h b/src/nautilus-files-view.h index b0695f226..536c613f6 100644 --- a/src/nautilus-files-view.h +++ b/src/nautilus-files-view.h @@ -258,6 +258,8 @@ gboolean nautilus_files_view_should_show_file (Nautil gboolean nautilus_files_view_should_sort_directories_first (NautilusFilesView *view); void nautilus_files_view_ignore_hidden_file_preferences (NautilusFilesView *view); +gboolean nautilus_files_view_has_subdirectory (NautilusFilesView *view, + NautilusDirectory *directory); void nautilus_files_view_add_subdirectory (NautilusFilesView *view, NautilusDirectory *directory); void nautilus_files_view_remove_subdirectory (NautilusFilesView *view, diff --git a/src/nautilus-list-view.c b/src/nautilus-list-view.c index 4a7f331f7..3e5c9dbe3 100644 --- a/src/nautilus-list-view.c +++ b/src/nautilus-list-view.c @@ -27,6 +27,9 @@ #include "nautilus-star-cell.h" #include "nautilus-tag-manager.h" +/* We wait two seconds after row is collapsed to unload the subdirectory */ +#define COLLAPSE_TO_UNLOAD_DELAY 2 + struct _NautilusListView { NautilusListBase parent_instance; @@ -37,6 +40,7 @@ struct _NautilusListView gint zoom_level; gboolean directories_first; + gboolean expand_as_a_tree; GQuark path_attribute_q; GFile *file_path_base_location; @@ -727,12 +731,19 @@ static void real_begin_loading (NautilusFilesView *files_view) { NautilusListView *self = NAUTILUS_LIST_VIEW (files_view); + NautilusViewModel *model; NautilusFile *file; NAUTILUS_FILES_VIEW_CLASS (nautilus_list_view_parent_class)->begin_loading (files_view); update_columns_settings_from_metadata_and_preferences (self); + /* TODO Reload the view if this setting changes. We can't easily switch + * tree mode on/off on an already loaded view and the preference is not + * expected to be changed frequently. */ + self->expand_as_a_tree = g_settings_get_boolean (nautilus_list_view_preferences, + NAUTILUS_PREFERENCES_LIST_VIEW_USE_TREE); + self->path_attribute_q = 0; g_clear_object (&self->file_path_base_location); file = nautilus_files_view_get_directory_as_file (files_view); @@ -747,7 +758,21 @@ real_begin_loading (NautilusFilesView *files_view) { self->path_attribute_q = g_quark_from_string ("where"); self->file_path_base_location = get_base_location (self); + + /* Forcefully disabling tree in these special locations because this + * view and its model currently don't expect the same file appearing + * more than once. + * + * NautilusFilesView still has support for the same file being present + * in multiple directories (struct FileAndDirectory), so, if someone + * cares enough about expanding folders in these special locations: + * TODO: Making the model items aware of their current model instead of + * relying on `nautilus_file_get_parent()`. */ + self->expand_as_a_tree = FALSE; } + + model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self)); + nautilus_view_model_expand_as_a_tree (model, self->expand_as_a_tree); } static void @@ -825,7 +850,6 @@ static void real_sort_directories_first_changed (NautilusFilesView *files_view) { NautilusListView *self = NAUTILUS_LIST_VIEW (files_view); - NautilusViewModel *model; self->directories_first = nautilus_files_view_should_sort_directories_first (NAUTILUS_FILES_VIEW (self)); @@ -838,6 +862,114 @@ real_get_view_id (NautilusFilesView *files_view) return NAUTILUS_VIEW_LIST_ID; } +typedef struct +{ + NautilusListView *self; + NautilusViewItem *item; + NautilusDirectory *directory; +} UnloadDelayData; + +static void +unload_delay_data_free (UnloadDelayData *unload_data) +{ + g_clear_weak_pointer (&unload_data->self); + g_clear_object (&unload_data->item); + g_clear_object (&unload_data->directory); + + g_free (unload_data); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (UnloadDelayData, unload_delay_data_free) + +static UnloadDelayData * +unload_delay_data_new (NautilusListView *self, + NautilusViewItem *item, + NautilusDirectory *directory) +{ + UnloadDelayData *unload_data; + + unload_data = g_new0 (UnloadDelayData, 1); + g_set_weak_pointer (&unload_data->self, self); + g_set_object (&unload_data->item, item); + g_set_object (&unload_data->directory, directory); + + return unload_data; +} + +static gboolean +unload_file_timeout (gpointer data) +{ + g_autoptr (UnloadDelayData) unload_data = data; + NautilusListView *self = unload_data->self; + NautilusViewModel *model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self)); + if (unload_data->self == NULL) + { + return G_SOURCE_REMOVE; + } + + for (guint i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (model)); i++) + { + g_autoptr (GtkTreeListRow) row = g_list_model_get_item (G_LIST_MODEL (model), i); + g_autoptr (NautilusViewItem) item = gtk_tree_list_row_get_item (row); + if (item != NULL && item == unload_data->item) + { + if (gtk_tree_list_row_get_expanded (row)) + { + /* It has been expanded again before the timeout. Do nothing. */ + return G_SOURCE_REMOVE; + } + break; + } + } + + if (nautilus_files_view_has_subdirectory (NAUTILUS_FILES_VIEW (self), + unload_data->directory)) + { + nautilus_files_view_remove_subdirectory (NAUTILUS_FILES_VIEW (self), + unload_data->directory); + } + + /* The model holds a GListStore for every subdirectory. Empty it. */ + nautilus_view_model_clear_subdirectory (model, unload_data->item); + + return G_SOURCE_REMOVE; +} + +static void +on_row_expanded_changed (GObject *gobject, + GParamSpec *pspec, + gpointer user_data) +{ + GtkTreeListRow *row = GTK_TREE_LIST_ROW (gobject); + NautilusListView *self = NAUTILUS_LIST_VIEW (user_data); + NautilusViewItem *item; + g_autoptr (NautilusDirectory) directory = NULL; + gboolean expanded; + + item = NAUTILUS_VIEW_ITEM (gtk_tree_list_row_get_item (row)); + if (item == NULL) + { + /* Row has been destroyed. */ + return; + } + + directory = nautilus_directory_get_for_file (nautilus_view_item_get_file (item)); + expanded = gtk_tree_list_row_get_expanded (row); + if (expanded) + { + if (!nautilus_files_view_has_subdirectory (NAUTILUS_FILES_VIEW (self), directory)) + { + nautilus_files_view_add_subdirectory (NAUTILUS_FILES_VIEW (self), directory); + } + } + else + { + g_timeout_add_seconds (COLLAPSE_TO_UNLOAD_DELAY, + unload_file_timeout, + unload_delay_data_new (self, item, directory)); + } +} + static void on_item_click_released_workaround (GtkGestureClick *gesture, gint n_press, @@ -910,6 +1042,17 @@ setup_name_cell (GtkSignalListItemFactory *factory, } setup_selection_click_workaround (cell); + + if (self->expand_as_a_tree) + { + GtkTreeExpander *expander; + + expander = nautilus_name_cell_get_expander (NAUTILUS_NAME_CELL (cell)); + gtk_tree_expander_set_indent_for_icon (expander, TRUE); + g_object_bind_property (listitem, "item", + expander, "list-row", + G_BINDING_SYNC_CREATE); + } } static void @@ -918,6 +1061,7 @@ bind_name_cell (GtkSignalListItemFactory *factory, gpointer user_data) { GtkWidget *cell; + NautilusListView *self = user_data; NautilusViewItem *item; cell = gtk_list_item_get_child (listitem); @@ -938,6 +1082,14 @@ bind_name_cell (GtkSignalListItemFactory *factory, GTK_ACCESSIBLE_RELATION_LABELLED_BY, cell, NULL, -1); } + + if (self->expand_as_a_tree) + { + g_signal_connect_object (GTK_TREE_LIST_ROW (gtk_list_item_get_item (listitem)), + "notify::expanded", + G_CALLBACK (on_row_expanded_changed), + self, 0); + } } static void @@ -945,12 +1097,20 @@ unbind_name_cell (GtkSignalListItemFactory *factory, GtkListItem *listitem, gpointer user_data) { + NautilusListView *self = user_data; NautilusViewItem *item; item = listitem_get_view_item (listitem); g_return_if_fail (NAUTILUS_IS_VIEW_ITEM (item)); nautilus_view_item_set_item_ui (item, NULL); + + if (self->expand_as_a_tree) + { + g_signal_handlers_disconnect_by_func (gtk_list_item_get_item (listitem), + on_row_expanded_changed, + self); + } } static void diff --git a/src/nautilus-name-cell.c b/src/nautilus-name-cell.c index 7c525869c..567838dc4 100644 --- a/src/nautilus-name-cell.c +++ b/src/nautilus-name-cell.c @@ -16,6 +16,7 @@ struct _NautilusNameCell GQuark path_attribute_q; GFile *file_path_base_location; + GtkWidget *expander; GtkWidget *fixed_height_box; GtkWidget *icon; GtkWidget *label; @@ -305,6 +306,7 @@ nautilus_name_cell_class_init (NautilusNameCellClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/nautilus/ui/nautilus-name-cell.ui"); + gtk_widget_class_bind_template_child (widget_class, NautilusNameCell, expander); gtk_widget_class_bind_template_child (widget_class, NautilusNameCell, fixed_height_box); gtk_widget_class_bind_template_child (widget_class, NautilusNameCell, icon); gtk_widget_class_bind_template_child (widget_class, NautilusNameCell, label); @@ -336,3 +338,9 @@ nautilus_name_cell_show_snippet (NautilusNameCell *self) { self->show_snippet = TRUE; } + +GtkTreeExpander * +nautilus_name_cell_get_expander (NautilusNameCell *self) +{ + return GTK_TREE_EXPANDER (self->expander); +} diff --git a/src/nautilus-name-cell.h b/src/nautilus-name-cell.h index 62862cdf5..11b84931e 100644 --- a/src/nautilus-name-cell.h +++ b/src/nautilus-name-cell.h @@ -19,5 +19,6 @@ void nautilus_name_cell_set_path (NautilusNameCell *self, GQuark path_attribute_q, GFile *base_location); void nautilus_name_cell_show_snippet (NautilusNameCell *self); +GtkTreeExpander * nautilus_name_cell_get_expander (NautilusNameCell *self); G_END_DECLS diff --git a/src/resources/ui/nautilus-name-cell.ui b/src/resources/ui/nautilus-name-cell.ui index 246aa3ab2..bc755918d 100644 --- a/src/resources/ui/nautilus-name-cell.ui +++ b/src/resources/ui/nautilus-name-cell.ui @@ -6,106 +6,111 @@ <relation name="labelled-by">label</relation> </accessibility> <child> - <object class="GtkBox"> - <property name="spacing">6</property> - <property name="orientation">horizontal</property> - <property name="halign">fill</property> - <property name="valign">center</property> - <child> - <object class="GtkBox" id="fixed_height_box"> - <property name="orientation">vertical</property> - <property name="halign">center</property> - <property name="height-request">16</property> + <object class="GtkTreeExpander" id="expander"> + <property name="indent-for-icon">False</property> + <property name="child"> + <object class="GtkBox"> + <property name="spacing">6</property> + <property name="orientation">horizontal</property> + <property name="halign">fill</property> <property name="valign">center</property> <child> - <object class="GtkPicture" id="icon"> + <object class="GtkBox" id="fixed_height_box"> + <property name="orientation">vertical</property> <property name="halign">center</property> + <property name="height-request">16</property> <property name="valign">center</property> - <property name="can-shrink">False</property> + <child> + <object class="GtkPicture" id="icon"> + <property name="halign">center</property> + <property name="valign">center</property> + <property name="can-shrink">False</property> + </object> + </child> </object> </child> - </object> - </child> - <child> - <object class="GtkBox"> - <property name="orientation">vertical</property> - <property name="halign">fill</property> - <property name="hexpand">True</property> - <property name="valign">center</property> - <style> - <class name="column-name-labels-box"/> - </style> <child> <object class="GtkBox"> - <property name="orientation">horizontal</property> + <property name="orientation">vertical</property> <property name="halign">fill</property> <property name="hexpand">True</property> - <property name="spacing">6</property> + <property name="valign">center</property> + <style> + <class name="column-name-labels-box"/> + </style> <child> - <object class="GtkLabel" id="label"> - <property name="ellipsize">middle</property> - <property name="lines">1</property> - <property name="max-width-chars">-1</property> - <property name="wrap">False</property> - <property name="wrap-mode">word-char</property> - <property name="halign">start</property> - <attributes> - <attribute name="insert-hyphens" value="false"></attribute> - </attributes> + <object class="GtkBox"> + <property name="orientation">horizontal</property> + <property name="halign">fill</property> + <property name="hexpand">True</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel" id="label"> + <property name="ellipsize">middle</property> + <property name="lines">1</property> + <property name="max-width-chars">-1</property> + <property name="wrap">False</property> + <property name="wrap-mode">word-char</property> + <property name="halign">start</property> + <attributes> + <attribute name="insert-hyphens" value="false"></attribute> + </attributes> + </object> + </child> + <child> + <object class="GtkBox" id="emblems_box"> + <property name="orientation">horizontal</property> + <property name="halign">start</property> + <property name="spacing">6</property> + </object> + </child> </object> </child> <child> - <object class="GtkBox" id="emblems_box"> - <property name="orientation">horizontal</property> - <property name="halign">start</property> - <property name="spacing">6</property> + <object class="GtkLabel" id="path"> + <property name="visible">False</property> + <property name="ellipsize">start</property> + <property name="justify">left</property> + <property name="halign">fill</property> + <property name="xalign">0.0</property> + <attributes> + <attribute name="insert-hyphens" value="false"></attribute> + </attributes> + <style> + <class name="caption"/> + <class name="dim-label"/> + </style> </object> </child> </object> </child> <child> - <object class="GtkLabel" id="path"> + <object class="GtkMenuButton" id="snippet_button"> + <property name="tooltip-text" translatable="yes">Full text match</property> <property name="visible">False</property> - <property name="ellipsize">start</property> - <property name="justify">left</property> - <property name="halign">fill</property> - <property name="xalign">0.0</property> - <attributes> - <attribute name="insert-hyphens" value="false"></attribute> - </attributes> + <property name="icon-name">quotation-symbolic</property> + <property name="valign">center</property> <style> - <class name="caption"/> - <class name="dim-label"/> + <class name="fts-snippet"/> </style> - </object> - </child> - </object> - </child> - <child> - <object class="GtkMenuButton" id="snippet_button"> - <property name="tooltip-text" translatable="yes">Full text match</property> - <property name="visible">False</property> - <property name="icon-name">quotation-symbolic</property> - <property name="valign">center</property> - <style> - <class name="fts-snippet"/> - </style> - <property name="popover"> - <object class="GtkPopover"> - <child> - <object class="GtkLabel" id="snippet"> - <property name="ellipsize">none</property> - <property name="justify">left</property> - <property name="max-width-chars">65</property> - <property name="lines">10</property> - <property name="wrap">True</property> - <property name="wrap-mode">word</property> + <property name="popover"> + <object class="GtkPopover"> + <child> + <object class="GtkLabel" id="snippet"> + <property name="ellipsize">none</property> + <property name="justify">left</property> + <property name="max-width-chars">65</property> + <property name="lines">10</property> + <property name="wrap">True</property> + <property name="wrap-mode">word</property> + </object> + </child> </object> - </child> + </property> </object> - </property> + </child> </object> - </child> + </property> </object> </child> </template> diff --git a/src/resources/ui/nautilus-preferences-window.ui b/src/resources/ui/nautilus-preferences-window.ui index abee4f2af..9b2cd2edc 100644 --- a/src/resources/ui/nautilus-preferences-window.ui +++ b/src/resources/ui/nautilus-preferences-window.ui @@ -34,7 +34,6 @@ <property name="title" translatable="yes">_Expandable Folders in List View</property> <property name="title_lines">0</property> <property name="use_underline">True</property> - <property name="visible">False</property> <child> <object class="GtkSwitch" id="use_tree_view_switch"> <property name="valign">center</property> |