[gtk+] gtklistbox: add bind_model()



commit d825249b42a490175dd051af2d73e579b58a3a64
Author: Lars Uebernickel <lars uebernickel canonical com>
Date:   Mon Feb 9 11:49:31 2015 +0100

    gtklistbox: add bind_model()
    
    gtk_list_box_bind_model() binds a GListModel to a GtkListBox.
    
    This is a first step towards having GListModel support in Gtk. It's not
    useful for large models, because GtkListBox always creates all widgets
    for all rows.

 docs/reference/gtk/gtk3-sections.txt |    2 +
 gtk/gtklistbox.c                     |  112 ++++++++++++++++++++++++++++++++++
 gtk/gtklistbox.h                     |   20 ++++++
 3 files changed, 134 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/gtk/gtk3-sections.txt b/docs/reference/gtk/gtk3-sections.txt
index 55c9f54..88f20c3 100644
--- a/docs/reference/gtk/gtk3-sections.txt
+++ b/docs/reference/gtk/gtk3-sections.txt
@@ -550,6 +550,8 @@ gtk_list_box_set_header_func
 gtk_list_box_set_sort_func
 gtk_list_box_drag_highlight_row
 gtk_list_box_drag_unhighlight_row
+GtkListBoxCreateWidgetFunc
+gtk_list_box_bind_model
 
 gtk_list_box_row_new
 gtk_list_box_row_changed
diff --git a/gtk/gtklistbox.c b/gtk/gtklistbox.c
index a800f84..e358415 100644
--- a/gtk/gtklistbox.c
+++ b/gtk/gtklistbox.c
@@ -99,6 +99,11 @@ typedef struct
 
   int n_visible_rows;
   gboolean in_widget;
+
+  GListModel *bound_model;
+  GtkListBoxCreateWidgetFunc create_widget_func;
+  gpointer create_widget_func_data;
+  GDestroyNotify create_widget_func_data_destroy;
 } GtkListBoxPrivate;
 
 typedef struct
@@ -260,6 +265,12 @@ static void gtk_list_box_update_row_styles (GtkListBox    *box);
 static void gtk_list_box_update_row_style  (GtkListBox    *box,
                                             GtkListBoxRow *row);
 
+static void                 gtk_list_box_bound_model_changed            (GListModel          *list,
+                                                                         guint                position,
+                                                                         guint                removed,
+                                                                         guint                added,
+                                                                         gpointer             user_data);
+
 static GParamSpec *properties[LAST_PROPERTY] = { NULL, };
 static guint signals[LAST_SIGNAL] = { 0 };
 static GParamSpec *row_properties[LAST_ROW_PROPERTY] = { NULL, };
@@ -374,6 +385,15 @@ gtk_list_box_finalize (GObject *obj)
   g_sequence_free (priv->children);
   g_hash_table_unref (priv->header_hash);
 
+  if (priv->bound_model)
+    {
+      if (priv->create_widget_func_data_destroy)
+        priv->create_widget_func_data_destroy (priv->create_widget_func_data);
+
+      g_signal_handlers_disconnect_by_func (priv->bound_model, gtk_list_box_bound_model_changed, obj);
+      g_clear_object (&priv->bound_model);
+    }
+
   G_OBJECT_CLASS (gtk_list_box_parent_class)->finalize (obj);
 }
 
@@ -3561,3 +3581,95 @@ gtk_list_box_buildable_interface_init (GtkBuildableIface *iface)
 {
   iface->add_child = gtk_list_box_buildable_add_child;
 }
+
+static void
+gtk_list_box_bound_model_changed (GListModel *list,
+                                  guint       position,
+                                  guint       removed,
+                                  guint       added,
+                                  gpointer    user_data)
+{
+  GtkListBox *box = user_data;
+  GtkListBoxPrivate *priv = BOX_PRIV (user_data);
+  gint i;
+
+  while (removed--)
+    {
+      GtkListBoxRow *row;
+
+      row = gtk_list_box_get_row_at_index (box, position);
+      gtk_widget_destroy (GTK_WIDGET (row));
+    }
+
+  for (i = 0; i < added; i++)
+    {
+      GObject *item;
+      GtkWidget *widget;
+
+      item = g_list_model_get_item (list, position + i);
+      widget = priv->create_widget_func (item, priv->create_widget_func_data);
+      gtk_widget_show_all (widget);
+      gtk_list_box_insert (box, widget, position + i);
+
+      g_object_unref (item);
+    }
+}
+
+/**
+ * gtk_list_box_bind_model:
+ * @box: a #GtkListBox
+ * @model: (allow-none): the #GListModel to be bound to @box
+ * @create_widget_func: a function that creates widgets for items
+ * @user_data: user data passed to @create_widget_func
+ * @user_data_free_func: function for freeing @user_data
+ *
+ * Binds @model to @box.
+ *
+ * If @box was already bound to a model, that previous binding is
+ * destroyed.
+ *
+ * The contents of @box are cleared and then filled with widgets that
+ * represent items from @model. @box is updated whenever @model changes.
+ * If @model is %NULL, @box is left empty.
+ *
+ * It is undefined to add or remove widgets directly (for example, with
+ * gtk_list_box_insert() or gtk_container_add()) while @box is bound to a
+ * model.
+ *
+ * Since: 3.16
+ */
+void
+gtk_list_box_bind_model (GtkListBox                   *box,
+                         GListModel                   *model,
+                         GtkListBoxCreateWidgetFunc    create_widget_func,
+                         gpointer                      user_data,
+                         GDestroyNotify                user_data_free_func)
+{
+  GtkListBoxPrivate *priv = BOX_PRIV (box);
+
+  g_return_if_fail (GTK_IS_LIST_BOX (box));
+  g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
+  g_return_if_fail (model == NULL || create_widget_func != NULL);
+
+  if (priv->bound_model)
+    {
+      if (priv->create_widget_func_data_destroy)
+        priv->create_widget_func_data_destroy (priv->create_widget_func_data);
+
+      g_signal_handlers_disconnect_by_func (priv->bound_model, gtk_list_box_bound_model_changed, box);
+      g_clear_object (&priv->bound_model);
+    }
+
+  gtk_list_box_forall_internal (GTK_CONTAINER (box), FALSE, (GtkCallback) gtk_widget_destroy, NULL);
+
+  if (model == NULL)
+    return;
+
+  priv->bound_model = g_object_ref (model);
+  priv->create_widget_func = create_widget_func;
+  priv->create_widget_func_data = user_data;
+  priv->create_widget_func_data_destroy = user_data_free_func;
+
+  g_signal_connect (priv->bound_model, "items-changed", G_CALLBACK (gtk_list_box_bound_model_changed), box);
+  gtk_list_box_bound_model_changed (model, 0, 0, g_list_model_get_n_items (model), box);
+}
diff --git a/gtk/gtklistbox.h b/gtk/gtklistbox.h
index a4ee2ff..ad6d851 100644
--- a/gtk/gtklistbox.h
+++ b/gtk/gtklistbox.h
@@ -169,6 +169,20 @@ typedef void (*GtkListBoxUpdateHeaderFunc) (GtkListBoxRow *row,
                                             GtkListBoxRow *before,
                                             gpointer       user_data);
 
+/**
+ * GtkListBoxCreateWidgetFunc:
+ * @item: the item from the model for which to create a widget for
+ *
+ * Called for list boxes that are bound to a #GListModel with
+ * gtk_list_box_bind_model() for each item that gets added to the model.
+ *
+ * Returns: a #GtkWidget that represents @item
+ *
+ * Since: 3.16
+ */
+typedef GtkWidget * (*GtkListBoxCreateWidgetFunc) (gpointer item,
+                                                   gpointer user_data);
+
 GDK_AVAILABLE_IN_3_10
 GType      gtk_list_box_row_get_type      (void) G_GNUC_CONST;
 GDK_AVAILABLE_IN_3_10
@@ -286,6 +300,12 @@ GDK_AVAILABLE_IN_3_10
 GtkWidget*     gtk_list_box_new                          (void);
 
 
+GDK_AVAILABLE_IN_3_16
+void           gtk_list_box_bind_model                   (GtkListBox                   *box,
+                                                          GListModel                   *model,
+                                                          GtkListBoxCreateWidgetFunc    create_widget_func,
+                                                          gpointer                      user_data,
+                                                          GDestroyNotify                user_data_free_func);
 
 G_END_DECLS
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]