diff --git a/gtk/gtklistitemmanager.c b/gtk/gtklistitemmanager.c new file mode 100644 index 0000000000..28630bda99 --- /dev/null +++ b/gtk/gtklistitemmanager.c @@ -0,0 +1,181 @@ +/* + * Copyright © 2018 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Benjamin Otte + */ + +#include "config.h" + +#include "gtklistitemmanagerprivate.h" + +struct _GtkListItemManager +{ + GObject parent_instance; + + GtkWidget *widget; + GListModel *model; + GtkListItemFactory *factory; +}; + +struct _GtkListItemManagerClass +{ + GObjectClass parent_class; +}; + +G_DEFINE_TYPE (GtkListItemManager, gtk_list_item_manager, G_TYPE_OBJECT) + +static void +gtk_list_item_manager_dispose (GObject *object) +{ + GtkListItemManager *self = GTK_LIST_ITEM_MANAGER (object); + + g_clear_object (&self->model); + g_clear_object (&self->factory); + + G_OBJECT_CLASS (gtk_list_item_manager_parent_class)->dispose (object); +} + +static void +gtk_list_item_manager_class_init (GtkListItemManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gtk_list_item_manager_dispose; +} + +static void +gtk_list_item_manager_init (GtkListItemManager *self) +{ +} + +GtkListItemManager * +gtk_list_item_manager_new (GtkWidget *widget) +{ + GtkListItemManager *self; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + + self = g_object_new (GTK_TYPE_LIST_ITEM_MANAGER, NULL); + + self->widget = widget; + + return self; +} + +void +gtk_list_item_manager_set_factory (GtkListItemManager *self, + GtkListItemFactory *factory) +{ + g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self)); + g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (factory)); + + if (self->factory == factory) + return; + + g_clear_object (&self->factory); + + self->factory = g_object_ref (factory); +} + +GtkListItemFactory * +gtk_list_item_manager_get_factory (GtkListItemManager *self) +{ + g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL); + + return self->factory; +} + +void +gtk_list_item_manager_set_model (GtkListItemManager *self, + GListModel *model) +{ + g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self)); + g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model)); + + if (self->model == model) + return; + + g_clear_object (&self->model); + + if (model) + self->model = g_object_ref (model); +} + +GListModel * +gtk_list_item_manager_get_model (GtkListItemManager *self) +{ + g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL); + + return self->model; +} + +/* + * gtk_list_item_manager_model_changed: + * @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. + **/ +void +gtk_list_item_manager_model_changed (GtkListItemManager *self, + guint position, + guint removed, + guint added) +{ + g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self)); + g_return_if_fail (self->model != NULL); +} + +/* + * gtk_list_item_manager_create_list_item: + * @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 + * + * Creates a new list item widget to use for @position. No widget may + * yet exist that is used for @position. + * + * Returns: a properly setup widget to use in @position + **/ +GtkWidget * +gtk_list_item_manager_create_list_item (GtkListItemManager *self, + guint position, + GtkWidget *next_sibling) +{ + GtkWidget *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); + + 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); + gtk_widget_insert_before (result, self->widget, next_sibling); + + return result; +} diff --git a/gtk/gtklistitemmanagerprivate.h b/gtk/gtklistitemmanagerprivate.h new file mode 100644 index 0000000000..34b57da97e --- /dev/null +++ b/gtk/gtklistitemmanagerprivate.h @@ -0,0 +1,62 @@ +/* + * Copyright © 2018 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Benjamin Otte + */ + + +#ifndef __GTK_LIST_ITEM_MANAGER_H__ +#define __GTK_LIST_ITEM_MANAGER_H__ + +#include "gtk/gtktypes.h" + +#include "gtk/gtklistitemfactoryprivate.h" + +G_BEGIN_DECLS + +#define GTK_TYPE_LIST_ITEM_MANAGER (gtk_list_item_manager_get_type ()) +#define GTK_LIST_ITEM_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_ITEM_MANAGER, GtkListItemManager)) +#define GTK_LIST_ITEM_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_ITEM_MANAGER, GtkListItemManagerClass)) +#define GTK_IS_LIST_ITEM_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_ITEM_MANAGER)) +#define GTK_IS_LIST_ITEM_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_ITEM_MANAGER)) +#define GTK_LIST_ITEM_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_ITEM_MANAGER, GtkListItemManagerClass)) + +typedef struct _GtkListItemManager GtkListItemManager; +typedef struct _GtkListItemManagerClass GtkListItemManagerClass; + +GType gtk_list_item_manager_get_type (void) G_GNUC_CONST; + +GtkListItemManager * gtk_list_item_manager_new (GtkWidget *widget); + +void gtk_list_item_manager_set_factory (GtkListItemManager *self, + GtkListItemFactory *factory); +GtkListItemFactory * gtk_list_item_manager_get_factory (GtkListItemManager *self); +void gtk_list_item_manager_set_model (GtkListItemManager *self, + GListModel *model); +GListModel * gtk_list_item_manager_get_model (GtkListItemManager *self); + + +void gtk_list_item_manager_model_changed (GtkListItemManager *self, + guint position, + guint removed, + guint added); +GtkWidget * gtk_list_item_manager_create_list_item (GtkListItemManager *self, + guint position, + GtkWidget *next_sibling); + +G_END_DECLS + +#endif /* __GTK_LIST_ITEM_MANAGER_H__ */ diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c index abaf526419..dec238c00c 100644 --- a/gtk/gtklistview.c +++ b/gtk/gtklistview.c @@ -25,6 +25,7 @@ #include "gtkintl.h" #include "gtkrbtreeprivate.h" #include "gtklistitemfactoryprivate.h" +#include "gtklistitemmanagerprivate.h" #include "gtkscrollable.h" #include "gtkwidgetprivate.h" @@ -45,7 +46,7 @@ struct _GtkListView GtkWidget parent_instance; GListModel *model; - GtkListItemFactory *item_factory; + GtkListItemManager *item_manager; GtkAdjustment *adjustment[2]; GtkScrollablePolicy scroll_policy[2]; @@ -483,14 +484,12 @@ gtk_list_view_add_rows (GtkListView *self, for (i = 0; i < n_rows; i++) { ListRow *new_row; - gpointer item; - + new_row = gtk_rb_tree_insert_before (self->rows, row); new_row->n_rows = 1; - new_row->widget = gtk_list_item_factory_create (self->item_factory); - gtk_widget_insert_before (new_row->widget, GTK_WIDGET (self), row ? row->widget : NULL); - item = g_list_model_get_item (self->model, position + i); - gtk_list_item_factory_bind (self->item_factory, new_row->widget, item); + new_row->widget = gtk_list_item_manager_create_list_item (self->item_manager, + position + i, + row ? row->widget : NULL); } gtk_widget_queue_resize (GTK_WIDGET (self)); @@ -504,6 +503,7 @@ gtk_list_view_model_items_changed_cb (GListModel *model, GtkListView *self) { gtk_list_view_remove_rows (self, position, removed); + gtk_list_item_manager_model_changed (self->item_manager, position, removed, added); gtk_list_view_add_rows (self, position, added); } @@ -573,7 +573,7 @@ gtk_list_view_dispose (GObject *object) gtk_list_view_clear_adjustment (self, GTK_ORIENTATION_HORIZONTAL); gtk_list_view_clear_adjustment (self, GTK_ORIENTATION_VERTICAL); - g_clear_object (&self->item_factory); + g_clear_object (&self->item_manager); G_OBJECT_CLASS (gtk_list_view_parent_class)->dispose (object); } @@ -584,6 +584,7 @@ gtk_list_view_finalize (GObject *object) GtkListView *self = GTK_LIST_VIEW (object); gtk_rb_tree_unref (self->rows); + g_clear_object (&self->item_manager); G_OBJECT_CLASS (gtk_list_view_parent_class)->finalize (object); } @@ -751,6 +752,8 @@ gtk_list_view_class_init (GtkListViewClass *klass) static void gtk_list_view_init (GtkListView *self) { + self->item_manager = gtk_list_item_manager_new (GTK_WIDGET (self)); + self->rows = gtk_rb_tree_new (ListRow, ListRowAugment, list_row_augment, @@ -814,6 +817,8 @@ gtk_list_view_set_model (GtkListView *self, gtk_list_view_clear_model (self); + gtk_list_item_manager_set_model (self->item_manager, model); + if (model) { self->model = g_object_ref (model); @@ -836,6 +841,7 @@ gtk_list_view_set_functions (GtkListView *self, gpointer user_data, GDestroyNotify user_destroy) { + GtkListItemFactory *factory; guint n_items; g_return_if_fail (GTK_IS_LIST_VIEW (self)); @@ -846,8 +852,9 @@ gtk_list_view_set_functions (GtkListView *self, n_items = self->model ? g_list_model_get_n_items (self->model) : 0; gtk_list_view_remove_rows (self, 0, n_items); - g_clear_object (&self->item_factory); - self->item_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); + g_object_unref (factory); gtk_list_view_add_rows (self, 0, n_items); } diff --git a/gtk/meson.build b/gtk/meson.build index 3b70089525..68f2b8de94 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -281,6 +281,7 @@ gtk_public_sources = files([ 'gtklinkbutton.c', 'gtklistbox.c', 'gtklistitemfactory.c', + 'gtklistitemmanager.c', 'gtklistlistmodel.c', 'gtkliststore.c', 'gtklistview.c',