[gtk/wip/otte/listview: 128/169] testlistview: Create widgets only once



commit d227a713edc74df2f86803a91eb043751be67a1f
Author: Benjamin Otte <otte redhat com>
Date:   Wed Sep 26 02:18:13 2018 +0200

    testlistview: Create widgets only once
    
    Previously, we were recreating all widgets every time the list item was
    rebound, which caused a lot of extra work every time we scrolled.
    
    Now we keep the widgets around and only set their properties again when
    the item changes.

 tests/testlistview.c | 161 ++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 107 insertions(+), 54 deletions(-)
---
diff --git a/tests/testlistview.c b/tests/testlistview.c
index 5de3dcc97a..0fb4927f63 100644
--- a/tests/testlistview.c
+++ b/tests/testlistview.c
@@ -20,7 +20,8 @@ start_enumerate (GListStore *store)
   enumerate = g_file_enumerate_children (file,
                                          G_FILE_ATTRIBUTE_STANDARD_TYPE
                                          "," G_FILE_ATTRIBUTE_STANDARD_ICON
-                                         "," G_FILE_ATTRIBUTE_STANDARD_NAME,
+                                         "," G_FILE_ATTRIBUTE_STANDARD_NAME
+                                         "," G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
                                          0,
                                          NULL,
                                          &error);
@@ -161,77 +162,129 @@ create_list_model_for_directory (gpointer file)
   return G_LIST_MODEL (sort);
 }
 
+typedef struct _RowData RowData;
+struct _RowData
+{
+  GtkWidget *depth_box;
+  GtkWidget *expander;
+  GtkWidget *icon;
+  GtkWidget *name;
+
+  GtkTreeListRow *current_item;
+  GBinding *expander_binding;
+};
+
+static void row_data_notify_item (GtkListItem *item,
+                                  GParamSpec  *pspec,
+                                  RowData     *data);
 static void
-bind_widget (GtkListItem *list_item,
-             gpointer     unused)
+row_data_unbind (RowData *data)
+{
+  if (data->current_item == NULL)
+    return;
+
+  g_binding_unbind (data->expander_binding);
+
+  g_clear_object (&data->current_item);
+}
+
+static void
+row_data_bind (RowData        *data,
+               GtkTreeListRow *item)
 {
-  GtkWidget *box, *child;
   GFileInfo *info;
-  GFile *file;
-  guint depth;
   GIcon *icon;
-  gpointer item;
-  char *s;
+  guint depth;
 
-  item = gtk_list_item_get_item (list_item);
+  row_data_unbind (data);
 
-  child = gtk_bin_get_child (GTK_BIN (list_item));
-  if (child)
-    gtk_container_remove (GTK_CONTAINER (list_item), child);
-  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
-  gtk_container_add (GTK_CONTAINER (list_item), box);
+  if (item == NULL)
+    return;
 
-  child = gtk_label_new (NULL);
-  gtk_label_set_width_chars (GTK_LABEL (child), 5);
-  gtk_label_set_xalign (GTK_LABEL (child), 1.0);
-  g_object_bind_property (list_item, "position", child, "label", G_BINDING_SYNC_CREATE);
-  gtk_container_add (GTK_CONTAINER (box), child);
+  data->current_item = g_object_ref (item);
 
   depth = gtk_tree_list_row_get_depth (item);
-  if (depth > 0)
-    {
-      child = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
-      gtk_widget_set_size_request (child, 16 * depth, 0);
-      gtk_container_add (GTK_CONTAINER (box), child);
-    }
+  gtk_widget_set_size_request (data->depth_box, 16 * depth, 0);
 
-  if (gtk_tree_list_row_is_expandable (item))
-    {
-      GtkWidget *title, *arrow;
-
-      child = g_object_new (GTK_TYPE_BOX, "css-name", "expander", NULL);
-      
-      title = g_object_new (GTK_TYPE_TOGGLE_BUTTON, "css-name", "title", NULL);
-      gtk_button_set_relief (GTK_BUTTON (title), GTK_RELIEF_NONE);
-      g_object_bind_property (item, "expanded", title, "active", G_BINDING_BIDIRECTIONAL | 
G_BINDING_SYNC_CREATE);
-      g_object_set_data_full (G_OBJECT (title), "make-sure-its-not-unreffed", g_object_ref (item), 
g_object_unref);
-      gtk_container_add (GTK_CONTAINER (child), title);
-
-      arrow = g_object_new (GTK_TYPE_SPINNER, "css-name", "arrow", NULL);
-      gtk_container_add (GTK_CONTAINER (title), arrow);
-    }
-  else
-    {
-     child = gtk_image_new (); /* empty whatever */
-    }
-  gtk_container_add (GTK_CONTAINER (box), child);
+  gtk_widget_set_sensitive (data->expander, gtk_tree_list_row_is_expandable (item));
+  data->expander_binding = g_object_bind_property (item, "expanded", data->expander, "active", 
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
 
   info = gtk_tree_list_row_get_item (item);
 
   icon = g_file_info_get_icon (info);
+  gtk_widget_set_visible (data->icon, icon != NULL);
   if (icon)
-    {
-      child = gtk_image_new_from_gicon (icon);
-      gtk_container_add (GTK_CONTAINER (box), child);
-    }
+    gtk_image_set_from_gicon (GTK_IMAGE (data->icon), icon);
+
+  gtk_label_set_label (GTK_LABEL (data->name), g_file_info_get_display_name (info));
 
-  file = g_object_get_data (G_OBJECT (info), "file");
-  s = g_file_get_basename (file);
-  child = gtk_label_new (s);
-  g_free (s);
   g_object_unref (info);
+}
 
+static void
+row_data_notify_item (GtkListItem *item,
+                      GParamSpec  *pspec,
+                      RowData     *data)
+{
+  row_data_bind (data, gtk_list_item_get_item (item));
+}
+
+static void
+row_data_free (gpointer _data)
+{
+  RowData *data = _data;
+
+  row_data_unbind (data);
+
+  g_slice_free (RowData, data);
+}
+
+static void
+expander_set_checked_cb (GtkToggleButton *button,
+                         GParamSpec      *pspec,
+                         GtkWidget       *expander)
+{
+  if (gtk_toggle_button_get_active (button))
+    gtk_widget_set_state_flags (expander, GTK_STATE_FLAG_CHECKED, FALSE);
+  else
+    gtk_widget_unset_state_flags (expander, GTK_STATE_FLAG_CHECKED);
+}
+
+static void
+setup_widget (GtkListItem *list_item,
+              gpointer     unused)
+{
+  GtkWidget *box, *child;
+  RowData *data;
+
+  data = g_slice_new0 (RowData);
+  g_signal_connect (list_item, "notify::item", G_CALLBACK (row_data_notify_item), data);
+  g_object_set_data_full (G_OBJECT (list_item), "row-data", data, row_data_free);
+
+  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+  gtk_container_add (GTK_CONTAINER (list_item), box);
+
+  child = gtk_label_new (NULL);
+  gtk_label_set_width_chars (GTK_LABEL (child), 5);
+  gtk_label_set_xalign (GTK_LABEL (child), 1.0);
+  g_object_bind_property (list_item, "position", child, "label", G_BINDING_SYNC_CREATE);
   gtk_container_add (GTK_CONTAINER (box), child);
+
+  data->depth_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+  gtk_container_add (GTK_CONTAINER (box), data->depth_box);
+  
+  data->expander = g_object_new (GTK_TYPE_TOGGLE_BUTTON, "css-name", "title", NULL);
+  gtk_button_set_relief (GTK_BUTTON (data->expander), GTK_RELIEF_NONE);
+  gtk_container_add (GTK_CONTAINER (box), data->expander);
+  child = g_object_new (GTK_TYPE_SPINNER, "css-name", "expander", NULL);
+  g_signal_connect (data->expander, "notify::active", G_CALLBACK (expander_set_checked_cb), child);
+  gtk_container_add (GTK_CONTAINER (data->expander), child);
+
+  data->icon = gtk_image_new ();
+  gtk_container_add (GTK_CONTAINER (box), data->icon);
+
+  data->name = gtk_label_new (NULL);
+  gtk_container_add (GTK_CONTAINER (box), data->name);
 }
 
 static GListModel *
@@ -325,8 +378,8 @@ main (int argc, char *argv[])
 
   listview = gtk_list_view_new ();
   gtk_list_view_set_functions (GTK_LIST_VIEW (listview),
+                               setup_widget,
                                NULL,
-                               bind_widget,
                                NULL, NULL);
   gtk_container_add (GTK_CONTAINER (sw), listview);
 


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