From 62e9d1e470b0819bdcb4d2a3ba05e083306fc0da Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Mon, 27 Mar 2023 06:54:10 +0200 Subject: [PATCH] listview: Add concept of inertness An inert listview is a listview that does not use the factory. This allows faster updates because no calls into user code need to happen. A listview is inert when either: - It is not rooted. - It is not visible. - No factory is set (that one is obvious) The listview 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/gtklistview.c | 108 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 99 insertions(+), 9 deletions(-) diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c index c7977119a8..74ace1d2d7 100644 --- a/gtk/gtklistview.c +++ b/gtk/gtklistview.c @@ -215,13 +215,58 @@ gtk_list_view_split (GtkListBase *base, return new_tile; } +/* We define the listview as **inert** when the factory isn't used. */ +static gboolean +gtk_list_view_is_inert (GtkListView *self) +{ + GtkWidget *widget = GTK_WIDGET (self); + + return !gtk_widget_get_visible (widget) || + gtk_widget_get_root (widget) == NULL || + self->factory == NULL; +} + +static void +gtk_list_view_update_factories_with (GtkListView *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_list_view_update_factories (GtkListView *self) +{ + gtk_list_view_update_factories_with (self, + gtk_list_view_is_inert (self) ? NULL : self->factory); +} + +static void +gtk_list_view_clear_factories (GtkListView *self) +{ + gtk_list_view_update_factories_with (self, NULL); +} + static GtkListItemBase * gtk_list_view_create_list_widget (GtkListBase *base) { GtkListView *self = GTK_LIST_VIEW (base); + GtkListItemFactory *factory; GtkWidget *result; - result = gtk_list_item_widget_new (self->factory, + if (gtk_list_view_is_inert (self)) + factory = NULL; + else + factory = self->factory; + + result = gtk_list_item_widget_new (factory, "row", GTK_ACCESSIBLE_ROLE_LIST_ITEM); @@ -606,6 +651,50 @@ gtk_list_view_size_allocate (GtkWidget *widget, gtk_list_base_allocate (GTK_LIST_BASE (self)); } +static void +gtk_list_view_root (GtkWidget *widget) +{ + GtkListView *self = GTK_LIST_VIEW (widget); + + GTK_WIDGET_CLASS (gtk_list_view_parent_class)->root (widget); + + if (!gtk_list_view_is_inert (self)) + gtk_list_view_update_factories (self); +} + +static void +gtk_list_view_unroot (GtkWidget *widget) +{ + GtkListView *self = GTK_LIST_VIEW (widget); + + if (!gtk_list_view_is_inert (self)) + gtk_list_view_clear_factories (self); + + GTK_WIDGET_CLASS (gtk_list_view_parent_class)->unroot (widget); +} + +static void +gtk_list_view_show (GtkWidget *widget) +{ + GtkListView *self = GTK_LIST_VIEW (widget); + + GTK_WIDGET_CLASS (gtk_list_view_parent_class)->show (widget); + + if (!gtk_list_view_is_inert (self)) + gtk_list_view_update_factories (self); +} + +static void +gtk_list_view_hide (GtkWidget *widget) +{ + GtkListView *self = GTK_LIST_VIEW (widget); + + if (!gtk_list_view_is_inert (self)) + gtk_list_view_clear_factories (self); + + GTK_WIDGET_CLASS (gtk_list_view_parent_class)->hide (widget); +} + static void gtk_list_view_dispose (GObject *object) { @@ -731,6 +820,10 @@ gtk_list_view_class_init (GtkListViewClass *klass) widget_class->measure = gtk_list_view_measure; widget_class->size_allocate = gtk_list_view_size_allocate; + widget_class->root = gtk_list_view_root; + widget_class->unroot = gtk_list_view_unroot; + widget_class->show = gtk_list_view_show; + widget_class->hide = gtk_list_view_hide; gobject_class->dispose = gtk_list_view_dispose; gobject_class->get_property = gtk_list_view_get_property; @@ -961,21 +1054,18 @@ void gtk_list_view_set_factory (GtkListView *self, GtkListItemFactory *factory) { - GtkListTile *tile; + gboolean was_inert; g_return_if_fail (GTK_IS_LIST_VIEW (self)); g_return_if_fail (factory == NULL || GTK_IS_LIST_ITEM_FACTORY (factory)); + was_inert = gtk_list_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_list_view_is_inert (self)) + gtk_list_view_update_factories (self); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]); } -- 2.30.2