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.
This commit is contained in:
Benjamin Otte 2018-09-28 03:33:16 +02:00 committed by Matthias Clasen
parent 7831980c1d
commit 5b69fd535d
3 changed files with 58 additions and 9 deletions

View File

@ -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

View File

@ -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);

View File

@ -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)
@ -443,12 +445,23 @@ gtk_list_view_ensure_rows (GtkListView *self,
insert_after);
}
if (new_row->widget == NULL)
{
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
{
if (update_start <= i)
@ -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