[clutter] actor: Allow binding an actor to a GListModel



commit bf9a71ae2381e3d0fdc9846f61777489befe1960
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Mon Jun 29 20:22:56 2015 +0100

    actor: Allow binding an actor to a GListModel
    
    It can be useful to bind the children list to set of objects inside a
    GListModel implementation; the GListModel stores the objects, and every
    time the model changes, a function is called that maps each object in
    the model to a newly created ClutterActor, which is then added as a
    child. This API, along with the property binding one inside GObject,
    allows automatic creation of views based on object models that update
    themselves without manual intervention.

 clutter/clutter-actor.c            |  127 ++++++++++++++++++++++++++++++++++++
 clutter/clutter-actor.h            |   27 ++++++++
 doc/reference/clutter-sections.txt |    2 +
 3 files changed, 156 insertions(+), 0 deletions(-)
---
diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c
index 0305b74..fc2995b 100644
--- a/clutter/clutter-actor.c
+++ b/clutter/clutter-actor.c
@@ -798,6 +798,11 @@ struct _ClutterActorPrivate
    */
   gulong in_cloned_branch;
 
+  GListModel *child_model;
+  ClutterActorCreateChildFunc create_child_func;
+  gpointer create_child_data;
+  GDestroyNotify create_child_notify;
+
   /* bitfields: KEEP AT THE END */
 
   /* fixed position and sizes */
@@ -5928,6 +5933,18 @@ clutter_actor_dispose (GObject *object)
   g_clear_object (&priv->effects);
   g_clear_object (&priv->flatten_effect);
 
+  if (priv->child_model != NULL)
+    {
+      if (priv->create_child_notify != NULL)
+        priv->create_child_notify (priv->create_child_data);
+
+      priv->create_child_func = NULL;
+      priv->create_child_data = NULL;
+      priv->create_child_notify = NULL;
+
+      g_clear_object (&priv->child_model);
+    }
+
   if (priv->layout_manager != NULL)
     {
       clutter_layout_manager_set_container (priv->layout_manager, NULL);
@@ -20776,3 +20793,113 @@ _clutter_actor_get_active_framebuffer (ClutterActor *self)
 
   return _clutter_stage_get_active_framebuffer (stage);
 }
+
+static void
+clutter_actor_bound_model__changed (GListModel *model,
+                                    guint       position,
+                                    guint       removed,
+                                    guint       added,
+                                    gpointer    user_data)
+{
+  ClutterActor *parent = user_data;
+  ClutterActorPrivate *priv = parent->priv;
+  guint i;
+
+  while (removed--)
+    {
+      ClutterActor *child = clutter_actor_get_child_at_index (parent, position);
+      clutter_actor_destroy (child);
+    }
+
+  for (i = 0; i < added; i++)
+    {
+      GObject *item = g_list_model_get_item (model, position + i);
+      ClutterActor *child = priv->create_child_func (item, priv->create_child_data);
+
+      /* The actor returned by the function can have a floating reference,
+       * if the implementation is in pure C, or have a full reference, usually
+       * the case for language bindings. To avoid leaking references, we
+       * try to assume ownership of the instance, and release the reference
+       * at the end unconditionally, leaving the only reference to the actor
+       * itself.
+       */
+      if (g_object_is_floating (child))
+        g_object_ref_sink (child);
+
+      clutter_actor_insert_child_at_index (parent, child, position + i);
+
+      g_object_unref (child);
+      g_object_unref (item);
+    }
+}
+
+/**
+ * clutter_actor_bind_model:
+ * @self: a #ClutterActor
+ * @model: (optional): a #GListModel
+ * @create_child_func: a function that creates #ClutterActor instances
+ *   from the contents of the @model
+ * @user_data: user data passed to @create_child_func
+ * @notify: function called when unsetting the @model
+ *
+ * Binds a #GListModel to a #ClutterActor.
+ *
+ * If the #ClutterActor was already bound to a #GListModel, the previous
+ * binding is destroyed.
+ *
+ * The existing children of #ClutterActor are destroyed when setting a
+ * model, and new children are created and added, representing the contents
+ * of the @model. The #ClutterActor is updated whenever the @model changes.
+ * If @model is %NULL, the #ClutterActor is left empty.
+ *
+ * When a #ClutterActor is bound to a model, adding and removing children
+ * directly is undefined behaviour.
+ *
+ * Since: 1.24
+ */
+void
+clutter_actor_bind_model (ClutterActor                *self,
+                          GListModel                  *model,
+                          ClutterActorCreateChildFunc  create_child_func,
+                          gpointer                     user_data,
+                          GDestroyNotify               notify)
+{
+  ClutterActorPrivate *priv = clutter_actor_get_instance_private (self);
+
+  g_return_if_fail (CLUTTER_IS_ACTOR (self));
+  g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
+  g_return_if_fail (model == NULL || create_child_func != NULL);
+
+  if (priv->child_model != NULL)
+    {
+      if (priv->create_child_notify != NULL)
+        priv->create_child_notify (priv->create_child_data);
+
+      g_signal_handlers_disconnect_by_func (priv->child_model,
+                                            clutter_actor_bound_model__changed,
+                                            self);
+      g_clear_object (&priv->child_model);
+      priv->create_child_func = NULL;
+      priv->create_child_data = NULL;
+      priv->create_child_notify = NULL;
+    }
+
+  clutter_actor_destroy_all_children (self);
+
+  if (model == NULL)
+    return;
+
+  priv->child_model = g_object_ref (model);
+  priv->create_child_func = create_child_func;
+  priv->create_child_data = user_data;
+  priv->create_child_notify = notify;
+
+  g_signal_connect (priv->child_model, "items-changed",
+                    G_CALLBACK (clutter_actor_bound_model__changed),
+                    self);
+
+  clutter_actor_bound_model__changed (priv->child_model,
+                                      0,
+                                      0, g_list_model_get_n_items (priv->child_model),
+                                      self);
+}
diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h
index a71ca27..cd5e10d 100644
--- a/clutter/clutter-actor.h
+++ b/clutter/clutter-actor.h
@@ -31,6 +31,7 @@
 
 /* clutter-actor.h */
 
+#include <gio/gio.h>
 #include <pango/pango.h>
 #include <atk/atk.h>
 
@@ -851,6 +852,32 @@ CLUTTER_AVAILABLE_IN_1_22
 gint                            clutter_actor_get_opacity_override              (ClutterActor               
*self);
 #endif
 
+/**
+ * ClutterActorCreateChildFunc:
+ * @item: (type GObject): the item in the model
+ * @user_data: Data passed to clutter_actor_bind_model()
+ *
+ * Creates a #ClutterActor using the @item in the model.
+ *
+ * The usual way to implement this function is to create a #ClutterActor
+ * instance and then bind the #GObject properties to the actor properties
+ * of interest, using g_object_bind_property(). This way, when the @item
+ * in the #GListModel changes, the #ClutterActor changes as well.
+ *
+ * Returns: (transfer full): The newly created child #ClutterActor
+ *
+ * Since: 1.24
+ */
+typedef ClutterActor * (* ClutterActorCreateChildFunc) (gpointer item,
+                                                        gpointer user_data);
+
+CLUTTER_AVAILABLE_IN_1_24
+void                            clutter_actor_bind_model                        (ClutterActor               
*self,
+                                                                                 GListModel                 
*model,
+                                                                                 ClutterActorCreateChildFunc 
create_child_func,
+                                                                                 gpointer                    
user_data,
+                                                                                 GDestroyNotify              
notify);
+
 G_END_DECLS
 
 #endif /* __CLUTTER_ACTOR_H__ */
diff --git a/doc/reference/clutter-sections.txt b/doc/reference/clutter-sections.txt
index 0f807df..0e727fb 100644
--- a/doc/reference/clutter-sections.txt
+++ b/doc/reference/clutter-sections.txt
@@ -465,6 +465,8 @@ clutter_actor_iter_next
 clutter_actor_iter_prev
 clutter_actor_iter_remove
 clutter_actor_iter_destroy
+ClutterActorCreateChildFunc
+clutter_actor_bind_model
 
 <SUBSECTION>
 clutter_actor_save_easing_state


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