gridview: Add concept of inertness
authorBenjamin Otte <otte@redhat.com>
Mon, 27 Mar 2023 05:03:38 +0000 (07:03 +0200)
committerBenjamin Otte <otte@redhat.com>
Mon, 27 Mar 2023 05:08:44 +0000 (07:08 +0200)
An inert gridview is a gridview that does not use the factory. This
allows faster updates because no calls into user code need to happen.

A gridview is inert when either:
 - It is not rooted.
 - It is not visible.
 - No factory is set (that one is obvious)

The gridview does not need to be inert without a model, as that case is
handled by the item manager.

This should allow Nautilus to keep both the gridview and the columnview
around, and just gtk_widget_hide() the unused widget.

The code for now does not disable the item manager, as some
functionality of the item manager is required to allow setting scroll
positions and such.
But that is a place where more gains could be found if profiling showed
that was useful to do.

gtk/gtkgridview.c

index b87792c7d9f73fd7c15bc257e4047f78766fbc37..9983d4153a2375579731e19208d7cea5a5439745 100644 (file)
@@ -257,13 +257,58 @@ gtk_grid_view_split (GtkListBase *base,
   return split;
 }
 
+/* We define the listview as **inert** when the factory isn't used. */
+static gboolean
+gtk_grid_view_is_inert (GtkGridView *self)
+{
+  GtkWidget *widget = GTK_WIDGET (self);
+
+  return !gtk_widget_get_visible (widget) ||
+         gtk_widget_get_root (widget) == NULL || 
+         self->factory == NULL;
+}
+
+static void
+gtk_grid_view_update_factories_with (GtkGridView        *self,
+                                     GtkListItemFactory *factory)
+{
+  GtkListTile *tile;
+
+  for (tile = gtk_list_item_manager_get_first (self->item_manager);
+       tile != NULL;
+       tile = gtk_rb_tree_node_get_next (tile))
+    {
+      if (tile->widget)
+        gtk_list_factory_widget_set_factory (GTK_LIST_FACTORY_WIDGET (tile->widget), factory);
+    }
+}
+
+static void
+gtk_grid_view_update_factories (GtkGridView *self)
+{
+  gtk_grid_view_update_factories_with (self,
+                                       gtk_grid_view_is_inert (self) ? NULL : self->factory);
+}
+
+static void
+gtk_grid_view_clear_factories (GtkGridView *self)
+{
+  gtk_grid_view_update_factories_with (self, NULL);
+}
+
 static GtkListItemBase *
 gtk_grid_view_create_list_widget (GtkListBase *base)
 {
   GtkGridView *self = GTK_GRID_VIEW (base);
+  GtkListItemFactory *factory;
   GtkWidget *result;
 
-  result = gtk_list_item_widget_new (self->factory,
+  if (gtk_grid_view_is_inert (self))
+    factory = NULL;
+  else
+    factory = self->factory;
+
+  result = gtk_list_item_widget_new (factory,
                                      "child",
                                      GTK_ACCESSIBLE_ROLE_GRID_CELL);
 
@@ -856,6 +901,50 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
   gtk_list_base_allocate (GTK_LIST_BASE (self));
 }
 
+static void
+gtk_grid_view_root (GtkWidget *widget)
+{
+  GtkGridView *self = GTK_GRID_VIEW (widget);
+
+  GTK_WIDGET_CLASS (gtk_grid_view_parent_class)->root (widget);
+
+  if (!gtk_grid_view_is_inert (self))
+    gtk_grid_view_update_factories (self);
+}
+
+static void
+gtk_grid_view_unroot (GtkWidget *widget)
+{
+  GtkGridView *self = GTK_GRID_VIEW (widget);
+
+  if (!gtk_grid_view_is_inert (self))
+    gtk_grid_view_clear_factories (self);
+
+  GTK_WIDGET_CLASS (gtk_grid_view_parent_class)->unroot (widget);
+}
+
+static void
+gtk_grid_view_show (GtkWidget *widget)
+{
+  GtkGridView *self = GTK_GRID_VIEW (widget);
+
+  GTK_WIDGET_CLASS (gtk_grid_view_parent_class)->show (widget);
+
+  if (!gtk_grid_view_is_inert (self))
+    gtk_grid_view_update_factories (self);
+}
+
+static void
+gtk_grid_view_hide (GtkWidget *widget)
+{
+  GtkGridView *self = GTK_GRID_VIEW (widget);
+
+  if (!gtk_grid_view_is_inert (self))
+    gtk_grid_view_clear_factories (self);
+
+  GTK_WIDGET_CLASS (gtk_grid_view_parent_class)->hide (widget);
+}
+
 static void
 gtk_grid_view_dispose (GObject *object)
 {
@@ -989,6 +1078,10 @@ gtk_grid_view_class_init (GtkGridViewClass *klass)
 
   widget_class->measure = gtk_grid_view_measure;
   widget_class->size_allocate = gtk_grid_view_size_allocate;
+  widget_class->root = gtk_grid_view_root;
+  widget_class->unroot = gtk_grid_view_unroot;
+  widget_class->show = gtk_grid_view_show;
+  widget_class->hide = gtk_grid_view_hide;
 
   gobject_class->dispose = gtk_grid_view_dispose;
   gobject_class->get_property = gtk_grid_view_get_property;
@@ -1237,22 +1330,18 @@ void
 gtk_grid_view_set_factory (GtkGridView        *self,
                            GtkListItemFactory *factory)
 {
-  GtkListTile *tile;
+  gboolean was_inert;
 
   g_return_if_fail (GTK_IS_GRID_VIEW (self));
   g_return_if_fail (factory == NULL || GTK_IS_LIST_ITEM_FACTORY (factory));
 
+  was_inert = gtk_grid_view_is_inert (self);
+
   if (!g_set_object (&self->factory, factory))
     return;
 
-  for (tile = gtk_list_item_manager_get_first (self->item_manager);
-       tile != NULL;
-       tile = gtk_rb_tree_node_get_next (tile))
-    {
-      if (tile->widget)
-        gtk_list_factory_widget_set_factory (GTK_LIST_FACTORY_WIDGET (tile->widget), factory);
-    }
-
+  if (!was_inert || !gtk_grid_view_is_inert (self))
+    gtk_grid_view_update_factories (self);
 
   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
 }