forked from AuroraMiddleware/gtk
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.
This commit is contained in:
parent
54042029d3
commit
ec8684e87d
@ -35,6 +35,11 @@ struct _GtkListItemManagerClass
|
|||||||
GObjectClass parent_class;
|
GObjectClass parent_class;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct _GtkListItemManagerChange
|
||||||
|
{
|
||||||
|
GHashTable *items;
|
||||||
|
};
|
||||||
|
|
||||||
G_DEFINE_TYPE (GtkListItemManager, gtk_list_item_manager, G_TYPE_OBJECT)
|
G_DEFINE_TYPE (GtkListItemManager, gtk_list_item_manager, G_TYPE_OBJECT)
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -122,30 +127,81 @@ gtk_list_item_manager_get_model (GtkListItemManager *self)
|
|||||||
return self->model;
|
return self->model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
/*
|
/*
|
||||||
* gtk_list_item_manager_model_changed:
|
* gtk_list_item_manager_get_size:
|
||||||
* @self: a #GtkListItemManager
|
* @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
|
* Queries the number of widgets currently handled by @self.
|
||||||
* 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
|
* This includes both widgets that have been acquired and
|
||||||
* #GListItems it intends to delete in response to the @removed rows
|
* those currently waiting to be used again.
|
||||||
* but before it starts creating new ones for the @added rows.
|
*
|
||||||
|
* 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
|
void
|
||||||
gtk_list_item_manager_model_changed (GtkListItemManager *self,
|
gtk_list_item_manager_end_change (GtkListItemManager *self,
|
||||||
guint position,
|
GtkListItemManagerChange *change)
|
||||||
guint removed,
|
|
||||||
guint added)
|
|
||||||
{
|
{
|
||||||
|
GHashTableIter iter;
|
||||||
|
gpointer list_item;
|
||||||
|
|
||||||
g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
|
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);
|
g_return_val_if_fail (next_sibling == NULL || GTK_IS_WIDGET (next_sibling), NULL);
|
||||||
|
|
||||||
result = gtk_list_item_factory_create (self->factory);
|
result = gtk_list_item_factory_create (self->factory);
|
||||||
|
|
||||||
item = g_list_model_get_item (self->model, position);
|
item = g_list_model_get_item (self->model, position);
|
||||||
gtk_list_item_factory_bind (self->factory, result, item);
|
gtk_list_item_factory_bind (self->factory, result, item);
|
||||||
g_object_unref (item);
|
g_object_unref (item);
|
||||||
@ -186,9 +243,55 @@ gtk_list_item_manager_acquire_list_item (GtkListItemManager *self,
|
|||||||
return GTK_WIDGET (result);
|
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:
|
* gtk_list_item_manager_release_list_item:
|
||||||
* @self: a #GtkListItemManager
|
* @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
|
* @item: an item previously acquired with
|
||||||
* gtk_list_item_manager_acquire_list_item()
|
* 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.
|
* gtk_list_item_manager_acquire_list_item() and is no longer in use.
|
||||||
**/
|
**/
|
||||||
void
|
void
|
||||||
gtk_list_item_manager_release_list_item (GtkListItemManager *self,
|
gtk_list_item_manager_release_list_item (GtkListItemManager *self,
|
||||||
GtkWidget *item)
|
GtkListItemManagerChange *change,
|
||||||
|
GtkWidget *item)
|
||||||
{
|
{
|
||||||
g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
|
g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
|
||||||
g_return_if_fail (GTK_IS_LIST_ITEM (item));
|
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);
|
gtk_widget_unparent (item);
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ G_BEGIN_DECLS
|
|||||||
|
|
||||||
typedef struct _GtkListItemManager GtkListItemManager;
|
typedef struct _GtkListItemManager GtkListItemManager;
|
||||||
typedef struct _GtkListItemManagerClass GtkListItemManagerClass;
|
typedef struct _GtkListItemManagerClass GtkListItemManagerClass;
|
||||||
|
typedef struct _GtkListItemManagerChange GtkListItemManagerChange;
|
||||||
|
|
||||||
GType gtk_list_item_manager_get_type (void) G_GNUC_CONST;
|
GType gtk_list_item_manager_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
@ -48,15 +49,22 @@ void gtk_list_item_manager_set_model (GtkListItemMana
|
|||||||
GListModel *model);
|
GListModel *model);
|
||||||
GListModel * gtk_list_item_manager_get_model (GtkListItemManager *self);
|
GListModel * gtk_list_item_manager_get_model (GtkListItemManager *self);
|
||||||
|
|
||||||
|
guint gtk_list_item_manager_get_size (GtkListItemManager *self);
|
||||||
|
|
||||||
void gtk_list_item_manager_model_changed (GtkListItemManager *self,
|
GtkListItemManagerChange *
|
||||||
guint position,
|
gtk_list_item_manager_begin_change (GtkListItemManager *self);
|
||||||
guint removed,
|
void gtk_list_item_manager_end_change (GtkListItemManager *self,
|
||||||
guint added);
|
GtkListItemManagerChange *change);
|
||||||
GtkWidget * gtk_list_item_manager_acquire_list_item (GtkListItemManager *self,
|
GtkWidget * gtk_list_item_manager_acquire_list_item (GtkListItemManager *self,
|
||||||
guint position,
|
guint position,
|
||||||
GtkWidget *next_sibling);
|
GtkWidget *next_sibling);
|
||||||
|
GtkWidget * gtk_list_item_manager_try_reacquire_list_item
|
||||||
|
(GtkListItemManager *self,
|
||||||
|
GtkListItemManagerChange *change,
|
||||||
|
guint position,
|
||||||
|
GtkWidget *next_sibling);
|
||||||
void gtk_list_item_manager_release_list_item (GtkListItemManager *self,
|
void gtk_list_item_manager_release_list_item (GtkListItemManager *self,
|
||||||
|
GtkListItemManagerChange *change,
|
||||||
GtkWidget *widget);
|
GtkWidget *widget);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
@ -426,9 +426,10 @@ gtk_list_view_size_allocate (GtkWidget *widget,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gtk_list_view_remove_rows (GtkListView *self,
|
gtk_list_view_remove_rows (GtkListView *self,
|
||||||
guint position,
|
GtkListItemManagerChange *change,
|
||||||
guint n_rows)
|
guint position,
|
||||||
|
guint n_rows)
|
||||||
{
|
{
|
||||||
ListRow *row;
|
ListRow *row;
|
||||||
guint i, n_remaining;
|
guint i, n_remaining;
|
||||||
@ -454,7 +455,7 @@ gtk_list_view_remove_rows (GtkListView *self,
|
|||||||
for (i = 0; i < n_rows; i++)
|
for (i = 0; i < n_rows; i++)
|
||||||
{
|
{
|
||||||
ListRow *next = gtk_rb_tree_node_get_next (row);
|
ListRow *next = gtk_rb_tree_node_get_next (row);
|
||||||
gtk_list_item_manager_release_list_item (self->item_manager, row->widget);
|
gtk_list_item_manager_release_list_item (self->item_manager, change, row->widget);
|
||||||
row->widget = NULL;
|
row->widget = NULL;
|
||||||
gtk_rb_tree_remove (self->rows, row);
|
gtk_rb_tree_remove (self->rows, row);
|
||||||
row = next;
|
row = next;
|
||||||
@ -464,10 +465,11 @@ gtk_list_view_remove_rows (GtkListView *self,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gtk_list_view_add_rows (GtkListView *self,
|
gtk_list_view_add_rows (GtkListView *self,
|
||||||
guint position,
|
GtkListItemManagerChange *change,
|
||||||
guint n_rows)
|
guint position,
|
||||||
{
|
guint n_rows)
|
||||||
|
{
|
||||||
ListRow *row;
|
ListRow *row;
|
||||||
guint i, n_total;
|
guint i, n_total;
|
||||||
|
|
||||||
@ -489,9 +491,19 @@ gtk_list_view_add_rows (GtkListView *self,
|
|||||||
|
|
||||||
new_row = gtk_rb_tree_insert_before (self->rows, row);
|
new_row = gtk_rb_tree_insert_before (self->rows, row);
|
||||||
new_row->n_rows = 1;
|
new_row->n_rows = 1;
|
||||||
new_row->widget = gtk_list_item_manager_acquire_list_item (self->item_manager,
|
if (change)
|
||||||
position + i,
|
{
|
||||||
row ? row->widget : NULL);
|
new_row->widget = gtk_list_item_manager_try_reacquire_list_item (self->item_manager,
|
||||||
|
change,
|
||||||
|
position + i,
|
||||||
|
row ? row->widget : NULL);
|
||||||
|
}
|
||||||
|
if (new_row->widget == NULL)
|
||||||
|
{
|
||||||
|
new_row->widget = gtk_list_item_manager_acquire_list_item (self->item_manager,
|
||||||
|
position + i,
|
||||||
|
row ? row->widget : NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_widget_queue_resize (GTK_WIDGET (self));
|
gtk_widget_queue_resize (GTK_WIDGET (self));
|
||||||
@ -504,9 +516,14 @@ gtk_list_view_model_items_changed_cb (GListModel *model,
|
|||||||
guint added,
|
guint added,
|
||||||
GtkListView *self)
|
GtkListView *self)
|
||||||
{
|
{
|
||||||
gtk_list_view_remove_rows (self, position, removed);
|
GtkListItemManagerChange *change;
|
||||||
gtk_list_item_manager_model_changed (self->item_manager, position, removed, added);
|
|
||||||
gtk_list_view_add_rows (self, position, added);
|
change = gtk_list_item_manager_begin_change (self->item_manager);
|
||||||
|
|
||||||
|
gtk_list_view_remove_rows (self, change, position, removed);
|
||||||
|
gtk_list_view_add_rows (self, change, position, added);
|
||||||
|
|
||||||
|
gtk_list_item_manager_end_change (self->item_manager, change);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -515,7 +532,7 @@ gtk_list_view_clear_model (GtkListView *self)
|
|||||||
if (self->model == NULL)
|
if (self->model == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
gtk_list_view_remove_rows (self, 0, g_list_model_get_n_items (self->model));
|
gtk_list_view_remove_rows (self, NULL, 0, g_list_model_get_n_items (self->model));
|
||||||
|
|
||||||
g_signal_handlers_disconnect_by_func (self->model,
|
g_signal_handlers_disconnect_by_func (self->model,
|
||||||
gtk_list_view_model_items_changed_cb,
|
gtk_list_view_model_items_changed_cb,
|
||||||
@ -830,7 +847,7 @@ gtk_list_view_set_model (GtkListView *self,
|
|||||||
G_CALLBACK (gtk_list_view_model_items_changed_cb),
|
G_CALLBACK (gtk_list_view_model_items_changed_cb),
|
||||||
self);
|
self);
|
||||||
|
|
||||||
gtk_list_view_add_rows (self, 0, g_list_model_get_n_items (model));
|
gtk_list_view_add_rows (self, NULL, 0, g_list_model_get_n_items (model));
|
||||||
}
|
}
|
||||||
|
|
||||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
|
||||||
@ -852,12 +869,12 @@ gtk_list_view_set_functions (GtkListView *self,
|
|||||||
g_return_if_fail (user_data != NULL || user_destroy == NULL);
|
g_return_if_fail (user_data != NULL || user_destroy == NULL);
|
||||||
|
|
||||||
n_items = self->model ? g_list_model_get_n_items (self->model) : 0;
|
n_items = self->model ? g_list_model_get_n_items (self->model) : 0;
|
||||||
gtk_list_view_remove_rows (self, 0, n_items);
|
gtk_list_view_remove_rows (self, NULL, 0, n_items);
|
||||||
|
|
||||||
factory = gtk_list_item_factory_new (create_func, bind_func, user_data, user_destroy);
|
factory = gtk_list_item_factory_new (create_func, bind_func, user_data, user_destroy);
|
||||||
gtk_list_item_manager_set_factory (self->item_manager, factory);
|
gtk_list_item_manager_set_factory (self->item_manager, factory);
|
||||||
g_object_unref (factory);
|
g_object_unref (factory);
|
||||||
|
|
||||||
gtk_list_view_add_rows (self, 0, n_items);
|
gtk_list_view_add_rows (self, NULL, 0, n_items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user