[nautilus/wip/antoniof/new-list-view-continuation: 19/20] list-view-item-ui: Show spinner while loading subfolders




commit 6e4a42808a743a8976909ee975e067e1020548b1
Author: António Fernandes <antoniof gnome org>
Date:   Thu Apr 7 00:17:45 2022 +0100

    list-view-item-ui: Show spinner while loading subfolders
    
    The old list view used dummy rows, but that's too troublesome.

 src/nautilus-list-view-item-ui.c               | 55 ++++++++++++++++++++++++++
 src/nautilus-list-view.c                       |  2 +
 src/nautilus-view-item-model.c                 | 29 ++++++++++++++
 src/nautilus-view-item-model.h                 |  2 +
 src/nautilus-view-model.c                      | 28 ++++++++++++-
 src/resources/ui/nautilus-list-view-item-ui.ui |  8 ++++
 6 files changed, 123 insertions(+), 1 deletion(-)
---
diff --git a/src/nautilus-list-view-item-ui.c b/src/nautilus-list-view-item-ui.c
index 007d975ff..e1c89ddaf 100644
--- a/src/nautilus-list-view-item-ui.c
+++ b/src/nautilus-list-view-item-ui.c
@@ -4,6 +4,8 @@
 #include "nautilus-file-utilities.h"
 #include "nautilus-thumbnails.h"
 
+#define LOADING_TIMEOUT_SECONDS 2
+
 struct _NautilusListViewItemUi
 {
     GtkBox parent_instance;
@@ -13,6 +15,7 @@ struct _NautilusListViewItemUi
     GFile *file_path_base_location;
 
     GtkWidget *fixed_height_box;
+    GtkWidget *spinner;
     GtkWidget *icon;
     GtkWidget *label;
     GtkWidget *snippet;
@@ -20,6 +23,7 @@ struct _NautilusListViewItemUi
 
     gboolean show_snippet;
     gboolean called_once;
+    guint loading_timeout_id;
 };
 
 G_DEFINE_TYPE (NautilusListViewItemUi, nautilus_list_view_item_ui, GTK_TYPE_BOX)
@@ -197,6 +201,15 @@ static void
 set_model (NautilusListViewItemUi *self,
            NautilusViewItemModel  *model);
 
+static void
+dispose (GObject *object)
+{
+    NautilusListViewItemUi *self = (NautilusListViewItemUi *) object;
+
+    g_clear_handle_id (&self->loading_timeout_id, g_source_remove);
+    G_OBJECT_CLASS (nautilus_list_view_item_ui_parent_class)->dispose (object);
+}
+
 static void
 finalize (GObject *object)
 {
@@ -246,6 +259,44 @@ on_view_item_is_cut_changed (GObject    *object,
     }
 }
 
+static gboolean
+on_loading_timeout (gpointer user_data)
+{
+    NautilusListViewItemUi *self = NAUTILUS_LIST_VIEW_ITEM_UI (user_data);
+
+    gtk_widget_hide (self->spinner);
+    gtk_spinner_stop (GTK_SPINNER (self->spinner));
+    nautilus_view_item_model_set_loading (self->model, FALSE);
+
+    return G_SOURCE_REMOVE;
+}
+
+static void
+on_view_item_is_loading_changed (GObject    *object,
+                                 GParamSpec *pspec,
+                                 gpointer    user_data)
+{
+    NautilusListViewItemUi *self = NAUTILUS_LIST_VIEW_ITEM_UI (user_data);
+    gboolean is_loading;
+
+    g_clear_handle_id (&self->loading_timeout_id, g_source_remove);
+
+    g_object_get (object, "is-loading", &is_loading, NULL);
+    if (is_loading)
+    {
+        gtk_widget_show (self->spinner);
+        gtk_spinner_start (GTK_SPINNER (self->spinner));
+        self->loading_timeout_id = g_timeout_add_seconds (LOADING_TIMEOUT_SECONDS,
+                                                          G_SOURCE_FUNC (on_loading_timeout),
+                                                          self);
+    }
+    else
+    {
+        gtk_widget_hide (self->spinner);
+        gtk_spinner_stop (GTK_SPINNER (self->spinner));
+    }
+}
+
 static void
 set_model (NautilusListViewItemUi *self,
            NautilusViewItemModel  *model)
@@ -281,6 +332,8 @@ set_model (NautilusListViewItemUi *self,
                       (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 (self->model, "notify::is-loading",
+                      (GCallback) on_view_item_is_loading_changed, self);
     g_signal_connect_swapped (self->model, "file-changed",
                               (GCallback) on_file_changed, self);
 }
@@ -312,6 +365,7 @@ nautilus_list_view_item_ui_class_init (NautilusListViewItemUiClass *klass)
     GObjectClass *object_class = G_OBJECT_CLASS (klass);
     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
+    object_class->dispose = dispose;
     object_class->finalize = finalize;
     object_class->get_property = get_property;
     object_class->set_property = set_property;
@@ -327,6 +381,7 @@ nautilus_list_view_item_ui_class_init (NautilusListViewItemUiClass *klass)
     gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/nautilus/ui/nautilus-list-view-item-ui.ui");
 
     gtk_widget_class_bind_template_child (widget_class, NautilusListViewItemUi, fixed_height_box);
+    gtk_widget_class_bind_template_child (widget_class, NautilusListViewItemUi, spinner);
     gtk_widget_class_bind_template_child (widget_class, NautilusListViewItemUi, icon);
     gtk_widget_class_bind_template_child (widget_class, NautilusListViewItemUi, label);
     gtk_widget_class_bind_template_child (widget_class, NautilusListViewItemUi, snippet);
diff --git a/src/nautilus-list-view.c b/src/nautilus-list-view.c
index c058f95b4..5ccecd1cd 100644
--- a/src/nautilus-list-view.c
+++ b/src/nautilus-list-view.c
@@ -1651,9 +1651,11 @@ on_row_expanded_changed (GObject    *gobject,
     if (expanded && !has_subdirectory)
     {
         nautilus_files_view_add_subdirectory (NAUTILUS_FILES_VIEW (self), directory);
+        nautilus_view_item_model_set_loading (item_model, TRUE);
     }
     else if (!expanded && has_subdirectory)
     {
+        nautilus_view_item_model_set_loading (item_model, FALSE);
         g_timeout_add_seconds (COLLAPSE_TO_UNLOAD_DELAY,
                                unload_file_timeout,
                                unload_delay_data_new (self, item_model, directory));
diff --git a/src/nautilus-view-item-model.c b/src/nautilus-view-item-model.c
index 13fecb98c..54bae18c4 100644
--- a/src/nautilus-view-item-model.c
+++ b/src/nautilus-view-item-model.c
@@ -6,6 +6,7 @@ struct _NautilusViewItemModel
     GObject parent_instance;
     guint icon_size;
     gboolean is_cut;
+    gboolean is_loading;
     NautilusFile *file;
     GtkWidget *item_ui;
 };
@@ -18,6 +19,7 @@ enum
     PROP_FILE,
     PROP_ICON_SIZE,
     PROP_IS_CUT,
+    PROP_IS_LOADING,
     PROP_ITEM_UI,
     N_PROPS
 };
@@ -78,6 +80,12 @@ nautilus_view_item_model_get_property (GObject    *object,
         }
         break;
 
+        case PROP_IS_LOADING:
+        {
+            g_value_set_boolean (value, self->is_loading);
+        }
+        break;
+
         case PROP_ITEM_UI:
         {
             g_value_set_object (value, self->item_ui);
@@ -119,6 +127,12 @@ nautilus_view_item_model_set_property (GObject      *object,
         }
         break;
 
+        case PROP_IS_LOADING:
+        {
+            self->is_loading = g_value_get_boolean (value);
+        }
+        break;
+
         case PROP_ITEM_UI:
         {
             g_set_object (&self->item_ui, g_value_get_object (value));
@@ -162,6 +176,12 @@ nautilus_view_item_model_class_init (NautilusViewItemModelClass *klass)
                                                            "", "",
                                                            FALSE,
                                                            G_PARAM_READWRITE));
+    g_object_class_install_property (object_class,
+                                     PROP_IS_LOADING,
+                                     g_param_spec_boolean ("is-loading",
+                                                           "", "",
+                                                           FALSE,
+                                                           G_PARAM_READWRITE));
     g_object_class_install_property (object_class,
                                      PROP_FILE,
                                      g_param_spec_object ("file",
@@ -223,6 +243,15 @@ nautilus_view_item_model_set_cut (NautilusViewItemModel *self,
     g_object_set (self, "is-cut", is_cut, NULL);
 }
 
+void
+nautilus_view_item_model_set_loading (NautilusViewItemModel *self,
+                                      gboolean               loading)
+{
+    g_return_if_fail (NAUTILUS_IS_VIEW_ITEM_MODEL (self));
+
+    g_object_set (self, "is-loading", loading, NULL);
+}
+
 NautilusFile *
 nautilus_view_item_model_get_file (NautilusViewItemModel *self)
 {
diff --git a/src/nautilus-view-item-model.h b/src/nautilus-view-item-model.h
index 27c4a8fd9..5e7372e15 100644
--- a/src/nautilus-view-item-model.h
+++ b/src/nautilus-view-item-model.h
@@ -20,6 +20,8 @@ void nautilus_view_item_model_set_icon_size (NautilusViewItemModel *self,
 guint nautilus_view_item_model_get_icon_size (NautilusViewItemModel *self);
 void nautilus_view_item_model_set_cut (NautilusViewItemModel *self,
                                        gboolean               is_cut);
+void nautilus_view_item_model_set_loading (NautilusViewItemModel *self,
+                                           gboolean               subdirectory_loaded);
 
 NautilusFile * nautilus_view_item_model_get_file (NautilusViewItemModel *self);
 
diff --git a/src/nautilus-view-model.c b/src/nautilus-view-model.c
index 6a80d81d0..a708a6b63 100644
--- a/src/nautilus-view-model.c
+++ b/src/nautilus-view-model.c
@@ -46,6 +46,21 @@ get_child_row (NautilusViewModel *self,
     }
 }
 
+static inline void
+subdirectory_mark_loaded (NautilusViewModel *self,
+                          NautilusFile      *parent)
+{
+    NautilusViewItemModel *parent_item;
+
+    parent_item = nautilus_view_model_get_item_from_file (self, parent);
+    if (parent_item == NULL)
+    {
+        return; /* It's the root model, not a subdirectory. */
+    }
+
+    nautilus_view_item_model_set_loading (parent_item, FALSE);
+}
+
 static GType
 nautilus_view_model_get_item_type (GListModel *list)
 {
@@ -411,11 +426,18 @@ nautilus_view_model_add_item (NautilusViewModel     *self,
 {
     NautilusFile *file;
     g_autoptr (NautilusFile) parent = NULL;
+    GListStore *parent_store;
 
     file = nautilus_view_item_model_get_file (item);
     parent = nautilus_file_get_parent (file);
+    parent_store = get_directory_store (self, parent);
+
+    if (g_list_model_get_n_items (G_LIST_MODEL (parent_store)) == 0)
+    {
+        subdirectory_mark_loaded (self, parent);
+    }
 
-    g_list_store_append (get_directory_store (self, parent), item);
+    g_list_store_append (parent_store, item);
     g_hash_table_insert (self->map_files_to_model, file, item);
 }
 
@@ -427,6 +449,10 @@ splice_items_into_common_parent (NautilusViewModel *self,
     GListStore *dir_store;
 
     dir_store = get_directory_store (self, common_parent);
+    if (g_list_model_get_n_items (G_LIST_MODEL (dir_store)) == 0)
+    {
+        subdirectory_mark_loaded (self, common_parent);
+    }
     g_list_store_splice (dir_store,
                          g_list_model_get_n_items (G_LIST_MODEL (dir_store)),
                          0, items->pdata, items->len);
diff --git a/src/resources/ui/nautilus-list-view-item-ui.ui b/src/resources/ui/nautilus-list-view-item-ui.ui
index fe2c0e30d..22b311632 100644
--- a/src/resources/ui/nautilus-list-view-item-ui.ui
+++ b/src/resources/ui/nautilus-list-view-item-ui.ui
@@ -6,6 +6,14 @@
     <property name="orientation">horizontal</property>
     <property name="halign">fill</property>
     <property name="valign">center</property>
+    <child>
+      <object class="GtkSpinner" id="spinner">
+        <property name="visible">False</property>
+        <property name="spinning">False</property>
+        <property name="halign">center</property>
+        <property name="valign">center</property>
+      </object>
+    </child>
     <child>
       <object class="GtkBox" id="fixed_height_box">
         <property name="orientation">vertical</property>


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