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',