From 9fbc1f8c14f50a812b40dff650009a7e02743ca4 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Fri, 28 Sep 2018 03:33:16 +0200 Subject: [PATCH] listview: Try to keep the list items in order when scrolling Instead of just destroying all items and then recreating them (or even hide()ing and then show()ing them again (or even even repositioning them in the widget tree)), just try to reust them in the order they are. This works surprisingly well when scrolling and most/all widgets just moved. --- gtk/gtklistitemmanager.c | 29 ++++++++++++++++++++++++++++ gtk/gtklistitemmanagerprivate.h | 4 ++++ gtk/gtklistview.c | 34 ++++++++++++++++++++++++--------- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/gtk/gtklistitemmanager.c b/gtk/gtklistitemmanager.c index 32687440b2..62888f366a 100644 --- a/gtk/gtklistitemmanager.c +++ b/gtk/gtklistitemmanager.c @@ -21,6 +21,8 @@ #include "gtklistitemmanagerprivate.h" +#include "gtkwidgetprivate.h" + struct _GtkListItemManager { GObject parent_instance; @@ -312,6 +314,33 @@ gtk_list_item_manager_try_reacquire_list_item (GtkListItemManager *self, return GTK_WIDGET (result); } +/** + * gtk_list_item_manager_move_list_item: + * @self: a #GtkListItemManager + * @list_item: an acquired #GtkListItem that should be moved to represent + * a different row + * @position: the new position of that list item + * @prev_sibling: the new previous sibling + * + * Moves the widget to represent a new position in the listmodel without + * releasing the item. + * + * This is most useful when scrolling. + **/ +void +gtk_list_item_manager_move_list_item (GtkListItemManager *self, + GtkWidget *list_item, + guint position, + GtkWidget *prev_sibling) +{ + gpointer item; + + item = g_list_model_get_item (self->model, position); + gtk_list_item_factory_bind (self->factory, GTK_LIST_ITEM (list_item), position, item); + gtk_widget_insert_after (list_item, _gtk_widget_get_parent (list_item), prev_sibling); + g_object_unref (item); +} + /** * gtk_list_item_manager_update_list_item: * @self: a #GtkListItemManager diff --git a/gtk/gtklistitemmanagerprivate.h b/gtk/gtklistitemmanagerprivate.h index 0f1d1bddd1..d34e346a21 100644 --- a/gtk/gtklistitemmanagerprivate.h +++ b/gtk/gtklistitemmanagerprivate.h @@ -69,6 +69,10 @@ GtkWidget * gtk_list_item_manager_try_reacquire_list_item void gtk_list_item_manager_update_list_item (GtkListItemManager *self, GtkWidget *item, guint position); +void gtk_list_item_manager_move_list_item (GtkListItemManager *self, + GtkWidget *list_item, + guint position, + GtkWidget *prev_sibling); void gtk_list_item_manager_release_list_item (GtkListItemManager *self, GtkListItemManagerChange *change, GtkWidget *widget); diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c index c9cfc7c840..35cf3b6da3 100644 --- a/gtk/gtklistview.c +++ b/gtk/gtklistview.c @@ -336,7 +336,8 @@ gtk_list_view_merge_list_rows (GtkListView *self, } static void -gtk_list_view_release_rows (GtkListView *self) +gtk_list_view_release_rows (GtkListView *self, + GQueue *released) { ListRow *row, *prev, *next; guint i; @@ -347,7 +348,7 @@ gtk_list_view_release_rows (GtkListView *self) { if (row->widget) { - gtk_list_item_manager_release_list_item (self->item_manager, NULL, row->widget); + g_queue_push_tail (released, row->widget); row->widget = NULL; i++; prev = gtk_rb_tree_node_get_previous (row); @@ -379,7 +380,7 @@ gtk_list_view_release_rows (GtkListView *self) if (row->widget) { - gtk_list_item_manager_release_list_item (self->item_manager, NULL, row->widget); + g_queue_push_tail (released, row->widget); row->widget = NULL; prev = gtk_rb_tree_node_get_previous (row); if (prev && gtk_list_view_merge_list_rows (self, prev, row)) @@ -390,7 +391,7 @@ gtk_list_view_release_rows (GtkListView *self) { if (next->widget) { - gtk_list_item_manager_release_list_item (self->item_manager, NULL, next->widget); + g_queue_push_tail (released, next->widget); next->widget = NULL; } gtk_list_view_merge_list_rows (self, row, next); @@ -404,9 +405,10 @@ gtk_list_view_ensure_rows (GtkListView *self, { ListRow *row, *new_row; guint i, offset; - GtkWidget *insert_after; + GtkWidget *widget, *insert_after; + GQueue released = G_QUEUE_INIT; - gtk_list_view_release_rows (self); + gtk_list_view_release_rows (self, &released); row = gtk_list_view_get_row (self, self->anchor_start, &offset); if (offset > 0) @@ -444,9 +446,20 @@ gtk_list_view_ensure_rows (GtkListView *self, } if (new_row->widget == NULL) { - new_row->widget = gtk_list_item_manager_acquire_list_item (self->item_manager, - i, - insert_after); + new_row->widget = g_queue_pop_head (&released); + if (new_row->widget) + { + gtk_list_item_manager_move_list_item (self->item_manager, + new_row->widget, + i, + insert_after); + } + else + { + new_row->widget = gtk_list_item_manager_acquire_list_item (self->item_manager, + i, + insert_after); + } } } else @@ -456,6 +469,9 @@ gtk_list_view_ensure_rows (GtkListView *self, } insert_after = new_row->widget; } + + while ((widget = g_queue_pop_head (&released))) + gtk_list_item_manager_release_list_item (self->item_manager, NULL, widget); } static void