[clutter] actor: Allow binding an actor to a GListModel
- From: Emmanuele Bassi <ebassi src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [clutter] actor: Allow binding an actor to a GListModel
- Date: Tue, 7 Jul 2015 15:05:27 +0000 (UTC)
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]