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.
This commit is contained in:
Benjamin Otte 2018-09-26 02:18:13 +02:00 committed by Matthias Clasen
parent 7389e704dc
commit d8eec549f0

View File

@ -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);
@ -138,72 +139,116 @@ 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)
{
GFileInfo *info;
GIcon *icon;
guint depth;
row_data_unbind (data);
if (item == NULL)
return;
data->current_item = g_object_ref (item);
depth = gtk_tree_list_row_get_depth (item);
gtk_widget_set_size_request (data->depth_box, 16 * depth, 0);
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)
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));
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
setup_widget (GtkListItem *list_item,
gpointer unused)
{
GtkWidget *box, *child;
GFileInfo *info;
GFile *file;
guint depth;
GIcon *icon;
gpointer item;
char *s;
RowData *data;
item = gtk_list_item_get_item (list_item);
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_list_item_set_child (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_box_append (GTK_BOX (box), child);
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_box_append (GTK_BOX (box), child);
}
data->depth_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_append (GTK_BOX (box), data->depth_box);
if (gtk_tree_list_row_is_expandable (item))
{
GtkWidget *arrow;
data->expander = g_object_new (GTK_TYPE_TOGGLE_BUTTON, "css-name", "expander-widget", NULL);
gtk_button_set_has_frame (GTK_BUTTON (data->expander), FALSE);
gtk_box_append (GTK_BOX (box), data->expander);
child = g_object_new (GTK_TYPE_SPINNER, "css-name", "expander", NULL);
g_object_bind_property (data->expander, "active", child, "spinning", G_BINDING_SYNC_CREATE);
gtk_button_set_child (GTK_BUTTON (data->expander), child);
child = g_object_new (GTK_TYPE_TOGGLE_BUTTON, "css-name", "expander-widget", NULL);
gtk_button_set_has_frame (GTK_BUTTON (child), FALSE);
g_object_bind_property (item, "expanded", child, "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
g_object_set_data_full (G_OBJECT (child), "make-sure-its-not-unreffed", g_object_ref (item), g_object_unref);
data->icon = gtk_image_new ();
gtk_box_append (GTK_BOX (box), data->icon);
arrow = g_object_new (GTK_TYPE_SPINNER, "css-name", "expander", NULL);
g_object_bind_property (item, "expanded", arrow, "spinning", G_BINDING_SYNC_CREATE);
gtk_button_set_child (GTK_BUTTON (child), arrow);
}
else
{
child = gtk_image_new (); /* empty whatever */
}
gtk_box_append (GTK_BOX (box), child);
info = gtk_tree_list_row_get_item (item);
icon = g_file_info_get_icon (info);
if (icon)
{
child = gtk_image_new_from_gicon (icon);
gtk_box_append (GTK_BOX (box), child);
}
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);
gtk_box_append (GTK_BOX (box), child);
data->name = gtk_label_new (NULL);
gtk_box_append (GTK_BOX (box), data->name);
}
static GListModel *
@ -305,8 +350,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_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), listview);