From c935bf5648ddab35df8f6e1b69e2065bf1dc3c49 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Mon, 24 Sep 2018 04:01:37 +0200 Subject: [PATCH] listitem: Add gtk_list_item_get_position() Also refactor the whole list item management yet again. Now, list item APIs doesn't have bind/unbind functions anymore, but only property setters. The item factory is the only one doing the binding. As before, the item manager manages when items need to be bound. --- docs/reference/gtk/gtk4-sections.txt | 1 + gtk/gtklistitem.c | 66 ++++++++++++++++++++++------ gtk/gtklistitem.h | 2 + gtk/gtklistitemfactory.c | 26 ++++++++++- gtk/gtklistitemfactoryprivate.h | 4 ++ gtk/gtklistitemmanager.c | 23 +++++++++- gtk/gtklistitemmanagerprivate.h | 3 ++ gtk/gtklistitemprivate.h | 5 ++- gtk/gtklistview.c | 18 ++++++++ 9 files changed, 130 insertions(+), 18 deletions(-) diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index 26270c1bd2..a5d168e045 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -495,6 +495,7 @@ gtk_single_selection_get_type GtkListItem GtkListItem gtk_list_item_get_item +gtk_list_item_get_position gtk_list_item_get_child gtk_list_item_set_child diff --git a/gtk/gtklistitem.c b/gtk/gtklistitem.c index 65f52acf22..ac4fda6737 100644 --- a/gtk/gtklistitem.c +++ b/gtk/gtklistitem.c @@ -54,6 +54,7 @@ struct _GtkListItem GObject *item; GtkWidget *child; + guint position; }; struct _GtkListItemClass @@ -66,6 +67,7 @@ enum PROP_0, PROP_CHILD, PROP_ITEM, + PROP_POSITION, N_PROPS }; @@ -103,6 +105,10 @@ gtk_list_item_get_property (GObject *object, g_value_set_object (value, self->item); break; + case PROP_POSITION: + g_value_set_uint (value, self->position); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -163,6 +169,18 @@ gtk_list_item_class_init (GtkListItemClass *klass) G_TYPE_OBJECT, G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + /** + * GtkListItem:position: + * + * Position of the item + */ + properties[PROP_POSITION] = + g_param_spec_uint ("position", + P_("Position"), + P_("Position of the item"), + 0, G_MAXUINT, GTK_INVALID_LIST_POSITION, + G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (gobject_class, N_PROPS, properties); /* This gets overwritten by gtk_list_item_new() but better safe than sorry */ @@ -189,10 +207,10 @@ gtk_list_item_new (const char *css_name) * gtk_list_item_get_item: * @self: a #GtkListItem * - * Gets the item that is currently displayed or model that @self is + * Gets the item that is currently displayed in model that @self is * currently bound to or %NULL if @self is unbound. * - * Returns: (nullable) (transfer none) (type GObject): The model in use + * Returns: (nullable) (transfer none) (type GObject): The item displayed **/ gpointer gtk_list_item_get_item (GtkListItem *self) @@ -252,28 +270,50 @@ gtk_list_item_set_child (GtkListItem *self, } void -gtk_list_item_bind (GtkListItem *self, - gpointer item) +gtk_list_item_set_item (GtkListItem *self, + gpointer item) { g_return_if_fail (GTK_IS_LIST_ITEM (self)); - g_return_if_fail (G_IS_OBJECT (item)); - /* Must unbind before rebinding */ - g_return_if_fail (self->item == NULL); + g_return_if_fail (item == NULL || G_IS_OBJECT (item)); - self->item = g_object_ref (item); + if (self->item == item) + return; + + g_clear_object (&self->item); + if (item) + self->item = g_object_ref (item); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ITEM]); } +/** + * gtk_list_item_get_position: + * @self: a #GtkListItem + * + * Gets the position in the model that @self currently displays. + * If @self is unbound, 0 is returned. + * + * Returns: The position of this item + **/ +guint +gtk_list_item_get_position (GtkListItem *self) +{ + g_return_val_if_fail (GTK_IS_LIST_ITEM (self), 0); + + return self->position; +} + void -gtk_list_item_unbind (GtkListItem *self) +gtk_list_item_set_position (GtkListItem *self, + guint position) { g_return_if_fail (GTK_IS_LIST_ITEM (self)); - /* Must be bound */ - g_return_if_fail (self->item != NULL); - g_clear_object (&self->item); + if (self->position == position) + return; - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ITEM]); + self->position = position; + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_POSITION]); } diff --git a/gtk/gtklistitem.h b/gtk/gtklistitem.h index 3af77cb14e..ed7f21c45f 100644 --- a/gtk/gtklistitem.h +++ b/gtk/gtklistitem.h @@ -44,6 +44,8 @@ GType gtk_list_item_get_type (void) G_GNUC_CO GDK_AVAILABLE_IN_ALL gpointer gtk_list_item_get_item (GtkListItem *self); +GDK_AVAILABLE_IN_ALL +guint gtk_list_item_get_position (GtkListItem *self); GDK_AVAILABLE_IN_ALL void gtk_list_item_set_child (GtkListItem *self, diff --git a/gtk/gtklistitemfactory.c b/gtk/gtklistitemfactory.c index 7b2d01e584..450aac1fda 100644 --- a/gtk/gtklistitemfactory.c +++ b/gtk/gtklistitemfactory.c @@ -155,14 +155,31 @@ gtk_list_item_factory_create (GtkListItemFactory *self) void gtk_list_item_factory_bind (GtkListItemFactory *self, GtkListItem *list_item, + guint position, gpointer item) { g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self)); g_return_if_fail (GTK_IS_LIST_ITEM (list_item)); - gtk_list_item_bind (list_item, item); + g_object_freeze_notify (G_OBJECT (list_item)); + + gtk_list_item_set_item (list_item, item); + gtk_list_item_set_position (list_item, position); self->bind_func (gtk_list_item_get_child (list_item), item, self->user_data); + + g_object_thaw_notify (G_OBJECT (list_item)); +} + +void +gtk_list_item_factory_update (GtkListItemFactory *self, + GtkListItem *list_item, + guint position) +{ + g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self)); + g_return_if_fail (GTK_IS_LIST_ITEM (list_item)); + + gtk_list_item_set_position (list_item, position); } void @@ -172,5 +189,10 @@ gtk_list_item_factory_unbind (GtkListItemFactory *self, g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self)); g_return_if_fail (GTK_IS_LIST_ITEM (list_item)); - gtk_list_item_unbind (list_item); + g_object_freeze_notify (G_OBJECT (list_item)); + + gtk_list_item_set_item (list_item, NULL); + gtk_list_item_set_position (list_item, 0); + + g_object_thaw_notify (G_OBJECT (list_item)); } diff --git a/gtk/gtklistitemfactoryprivate.h b/gtk/gtklistitemfactoryprivate.h index a6bec12faf..21bc5b5586 100644 --- a/gtk/gtklistitemfactoryprivate.h +++ b/gtk/gtklistitemfactoryprivate.h @@ -47,7 +47,11 @@ GtkListItem * gtk_list_item_factory_create (GtkListItemFact void gtk_list_item_factory_bind (GtkListItemFactory *self, GtkListItem *list_item, + guint position, gpointer item); +void gtk_list_item_factory_update (GtkListItemFactory *self, + GtkListItem *list_item, + guint position); void gtk_list_item_factory_unbind (GtkListItemFactory *self, GtkListItem *list_item); diff --git a/gtk/gtklistitemmanager.c b/gtk/gtklistitemmanager.c index a88502efbb..d365137d18 100644 --- a/gtk/gtklistitemmanager.c +++ b/gtk/gtklistitemmanager.c @@ -236,7 +236,7 @@ gtk_list_item_manager_acquire_list_item (GtkListItemManager *self, 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); + gtk_list_item_factory_bind (self->factory, result, position, item); g_object_unref (item); gtk_widget_insert_before (GTK_WIDGET (result), self->widget, next_sibling); @@ -274,6 +274,7 @@ gtk_list_item_manager_try_reacquire_list_item (GtkListItemManager *self, item = g_list_model_get_item (self->model, position); if (g_hash_table_steal_extended (change->items, item, NULL, (gpointer *) &result)) { + gtk_list_item_factory_update (self->factory, result, position); 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)); @@ -287,6 +288,26 @@ gtk_list_item_manager_try_reacquire_list_item (GtkListItemManager *self, return GTK_WIDGET (result); } +/** + * gtk_list_item_manager_update_list_item: + * @self: a #GtkListItemManager + * @item: a #GtkListItem that has been acquired + * @position: the new position of that list item + * + * Updates the position of the given @item. This function must be called whenever + * the position of an item changes, like when new items are added before it. + **/ +void +gtk_list_item_manager_update_list_item (GtkListItemManager *self, + GtkWidget *item, + guint position) +{ + g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self)); + g_return_if_fail (GTK_IS_LIST_ITEM (item)); + + gtk_list_item_factory_update (self->factory, GTK_LIST_ITEM (item), position); +} + /* * gtk_list_item_manager_release_list_item: * @self: a #GtkListItemManager diff --git a/gtk/gtklistitemmanagerprivate.h b/gtk/gtklistitemmanagerprivate.h index 2af8e8fae7..d1cdfaf819 100644 --- a/gtk/gtklistitemmanagerprivate.h +++ b/gtk/gtklistitemmanagerprivate.h @@ -63,6 +63,9 @@ GtkWidget * gtk_list_item_manager_try_reacquire_list_item GtkListItemManagerChange *change, guint position, GtkWidget *next_sibling); +void gtk_list_item_manager_update_list_item (GtkListItemManager *self, + GtkWidget *item, + guint position); void gtk_list_item_manager_release_list_item (GtkListItemManager *self, GtkListItemManagerChange *change, GtkWidget *widget); diff --git a/gtk/gtklistitemprivate.h b/gtk/gtklistitemprivate.h index a0a90f7d79..f238685c76 100644 --- a/gtk/gtklistitemprivate.h +++ b/gtk/gtklistitemprivate.h @@ -26,9 +26,10 @@ G_BEGIN_DECLS GtkWidget * gtk_list_item_new (const char *css_name); -void gtk_list_item_bind (GtkListItem *self, +void gtk_list_item_set_item (GtkListItem *self, gpointer item); -void gtk_list_item_unbind (GtkListItem *self); +void gtk_list_item_set_position (GtkListItem *self, + guint position); G_END_DECLS diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c index f5c36e55fc..1539a2a530 100644 --- a/gtk/gtklistview.c +++ b/gtk/gtklistview.c @@ -509,6 +509,22 @@ gtk_list_view_add_rows (GtkListView *self, gtk_widget_queue_resize (GTK_WIDGET (self)); } +static void +gtk_list_view_update_rows (GtkListView *self, + guint position) +{ + ListRow *row; + + for (row = gtk_list_view_get_row (self, position, NULL); + row; + row = gtk_rb_tree_node_get_next (row)) + { + gtk_list_item_manager_update_list_item (self->item_manager, row->widget, position); + + position++; + } +} + static void gtk_list_view_model_items_changed_cb (GListModel *model, guint position, @@ -522,6 +538,8 @@ gtk_list_view_model_items_changed_cb (GListModel *model, gtk_list_view_remove_rows (self, change, position, removed); gtk_list_view_add_rows (self, change, position, added); + if (removed != added) + gtk_list_view_update_rows (self, position + added); gtk_list_item_manager_end_change (self->item_manager, change); }