summaryrefslogtreecommitdiff
path: root/gtk/gtklistitemmanager.c
diff options
context:
space:
mode:
authorBenjamin Otte <otte@redhat.com>2018-09-22 22:11:27 +0200
committerMatthias Clasen <mclasen@redhat.com>2020-05-30 19:26:45 -0400
commitec8684e87ddf2dbedbf935678e402a99661bbe61 (patch)
treed0f7c35f4d3b49701873659b4aa098c631eaa111 /gtk/gtklistitemmanager.c
parent54042029d3e0cd2fbd880cdfbe923c1866db769e (diff)
downloadgtk+-ec8684e87ddf2dbedbf935678e402a99661bbe61.tar.gz
listview: Change change management
Add a GtkListItemManagerChange object that tracks all removed list rows during an item-changed signal so they can be added back later.
Diffstat (limited to 'gtk/gtklistitemmanager.c')
-rw-r--r--gtk/gtklistitemmanager.c153
1 files changed, 133 insertions, 20 deletions
diff --git a/gtk/gtklistitemmanager.c b/gtk/gtklistitemmanager.c
index 2f2a1d2db9..a88502efbb 100644
--- a/gtk/gtklistitemmanager.c
+++ b/gtk/gtklistitemmanager.c
@@ -35,6 +35,11 @@ struct _GtkListItemManagerClass
GObjectClass parent_class;
};
+struct _GtkListItemManagerChange
+{
+ GHashTable *items;
+};
+
G_DEFINE_TYPE (GtkListItemManager, gtk_list_item_manager, G_TYPE_OBJECT)
static void
@@ -122,30 +127,81 @@ gtk_list_item_manager_get_model (GtkListItemManager *self)
return self->model;
}
+#if 0
/*
- * gtk_list_item_manager_model_changed:
+ * gtk_list_item_manager_get_size:
* @self: a #GtkListItemManager
- * @position: the position at which the model changed
- * @removed: the number of items removed
- * @added: the number of items added
- *
- * This function must be called by the owning @widget at the
- * appropriate time.
- * The manager does not connect to GListModel::items-changed itself
- * but relies on its widget calling this function.
- *
- * This function should be called after @widget has released all
- * #GListItems it intends to delete in response to the @removed rows
- * but before it starts creating new ones for the @added rows.
+ *
+ * Queries the number of widgets currently handled by @self.
+ *
+ * This includes both widgets that have been acquired and
+ * those currently waiting to be used again.
+ *
+ * Returns: Number of widgets handled by @self
+ **/
+guint
+gtk_list_item_manager_get_size (GtkListItemManager *self)
+{
+ return g_hash_table_size (self->pool);
+}
+#endif
+
+/*
+ * gtk_list_item_manager_begin_change:
+ * @self: a #GtkListItemManager
+ *
+ * Begins a change operation in response to a model's items-changed
+ * signal.
+ * During an ongoing change operation, list items will not be discarded
+ * when released but will be kept around in anticipation of them being
+ * added back in a different posiion later.
+ *
+ * Once it is known that no more list items will be reused,
+ * gtk_list_item_manager_end_change() should be called. This should happen
+ * as early as possible, so the list items held for the change can be
+ * reqcquired.
+ *
+ * Returns: The object to use for this change
+ **/
+GtkListItemManagerChange *
+gtk_list_item_manager_begin_change (GtkListItemManager *self)
+{
+ GtkListItemManagerChange *change;
+
+ g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL);
+
+ change = g_slice_new (GtkListItemManagerChange);
+ change->items = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ return change;
+}
+
+/*
+ * gtk_list_item_manager_end_change:
+ * @self: a #GtkListItemManager
+ * @change: a change
+ *
+ * Ends a change operation begun with gtk_list_item_manager_begin_change()
+ * and releases all list items still cached.
**/
void
-gtk_list_item_manager_model_changed (GtkListItemManager *self,
- guint position,
- guint removed,
- guint added)
+gtk_list_item_manager_end_change (GtkListItemManager *self,
+ GtkListItemManagerChange *change)
{
+ GHashTableIter iter;
+ gpointer list_item;
+
g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
- g_return_if_fail (self->model != NULL);
+ g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
+
+ g_hash_table_iter_init (&iter, change->items);
+ while (g_hash_table_iter_next (&iter, NULL, &list_item))
+ {
+ gtk_list_item_manager_release_list_item (self, NULL, list_item);
+ }
+
+ g_hash_table_unref (change->items);
+ g_slice_free (GtkListItemManagerChange, change);
}
/*
@@ -178,6 +234,7 @@ gtk_list_item_manager_acquire_list_item (GtkListItemManager *self,
g_return_val_if_fail (next_sibling == NULL || GTK_IS_WIDGET (next_sibling), NULL);
result = gtk_list_item_factory_create (self->factory);
+
item = g_list_model_get_item (self->model, position);
gtk_list_item_factory_bind (self->factory, result, item);
g_object_unref (item);
@@ -186,9 +243,55 @@ gtk_list_item_manager_acquire_list_item (GtkListItemManager *self,
return GTK_WIDGET (result);
}
+/**
+ * gtk_list_item_manager_try_acquire_list_item_from_change:
+ * @self: a #GtkListItemManager
+ * @position: the row in the model to create a list item for
+ * @next_sibling: the widget this widget should be inserted before or %NULL
+ * if none
+ *
+ * Like gtk_list_item_manager_acquire_list_item(), but only tries to acquire list
+ * items from those previously released as part of @change.
+ * If no matching list item is found, %NULL is returned and the caller should use
+ * gtk_list_item_manager_acquire_list_item().
+ *
+ * Returns: (nullable): a properly setup widget to use in @position or %NULL if
+ * no item for reuse existed
+ **/
+GtkWidget *
+gtk_list_item_manager_try_reacquire_list_item (GtkListItemManager *self,
+ GtkListItemManagerChange *change,
+ guint position,
+ GtkWidget *next_sibling)
+{
+ GtkListItem *result;
+ gpointer item;
+
+ g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL);
+ g_return_val_if_fail (next_sibling == NULL || GTK_IS_WIDGET (next_sibling), NULL);
+
+ /* XXX: can we avoid temporarily allocating items on failure? */
+ item = g_list_model_get_item (self->model, position);
+ if (g_hash_table_steal_extended (change->items, item, NULL, (gpointer *) &result))
+ {
+ gtk_widget_insert_before (GTK_WIDGET (result), self->widget, next_sibling);
+ /* XXX: Should we let the listview do this? */
+ gtk_widget_queue_resize (GTK_WIDGET (result));
+ }
+ else
+ {
+ result = NULL;
+ }
+ g_object_unref (item);
+
+ return GTK_WIDGET (result);
+}
+
/*
* gtk_list_item_manager_release_list_item:
* @self: a #GtkListItemManager
+ * @change: (allow-none): The change associated with this release or
+ * %NULL if this is a final removal
* @item: an item previously acquired with
* gtk_list_item_manager_acquire_list_item()
*
@@ -196,11 +299,21 @@ gtk_list_item_manager_acquire_list_item (GtkListItemManager *self,
* gtk_list_item_manager_acquire_list_item() and is no longer in use.
**/
void
-gtk_list_item_manager_release_list_item (GtkListItemManager *self,
- GtkWidget *item)
+gtk_list_item_manager_release_list_item (GtkListItemManager *self,
+ GtkListItemManagerChange *change,
+ GtkWidget *item)
{
g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
g_return_if_fail (GTK_IS_LIST_ITEM (item));
+ if (change != NULL)
+ {
+ if (g_hash_table_insert (change->items, gtk_list_item_get_item (GTK_LIST_ITEM (item)), item))
+ return;
+
+ g_warning ("FIXME: Handle the same item multiple times in the list.\nLars says this totally should not happen, but here we are.");
+ }
+
+ gtk_list_item_factory_unbind (self->factory, GTK_LIST_ITEM (item));
gtk_widget_unparent (item);
}