summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmmanuele Bassi <ebassi@gnome.org>2015-07-10 11:26:34 +0100
committerEmmanuele Bassi <ebassi@gnome.org>2015-07-10 11:26:34 +0100
commitb0e785c6c269f5223fe2beaa1794f850e270d9cd (patch)
treefce17d2871ae6d3f764d44034d273f487b749470
parenteda436f0c40955908b9eaf588078680f2d27354e (diff)
downloadclutter-b0e785c6c269f5223fe2beaa1794f850e270d9cd.tar.gz
actor: Add bind_model_with_properties()
When binding models to actors to map items to children we don't often need the full control of a function; in many cases we just need to specify the type of the child we want to construct and the properties on both the item and the child that we want to bind. We should provide a simple convenience function that does all this for us.
-rw-r--r--clutter/clutter-actor.c135
-rw-r--r--clutter/clutter-actor.h6
-rw-r--r--doc/reference/clutter-sections.txt1
-rw-r--r--examples/actor-model.c33
4 files changed, 151 insertions, 24 deletions
diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c
index d6f7c7d9e..1a0431293 100644
--- a/clutter/clutter-actor.c
+++ b/clutter/clutter-actor.c
@@ -20895,3 +20895,138 @@ clutter_actor_bind_model (ClutterActor *self,
g_list_model_get_n_items (priv->child_model),
self);
}
+
+typedef struct {
+ GType child_type;
+ GArray *props;
+} BindClosure;
+
+typedef struct {
+ const char *model_property;
+ const char *child_property;
+ GBindingFlags flags;
+} BindProperty;
+
+static void
+bind_closure_free (gpointer data_)
+{
+ BindClosure *data = data_;
+
+ if (data == NULL)
+ return;
+
+ g_array_unref (data->props);
+ g_slice_free (BindClosure, data);
+}
+
+static ClutterActor *
+bind_child_with_properties (gpointer item,
+ gpointer data_)
+{
+ BindClosure *data = data_;
+ ClutterActor *res;
+ guint i;
+
+ res = g_object_new (data->child_type, NULL);
+
+ for (i = 0; i < data->props->len; i++)
+ {
+ const BindProperty *prop = &g_array_index (data->props, BindProperty, i);
+
+ g_object_bind_property (item, prop->model_property,
+ res, prop->child_property,
+ prop->flags);
+ }
+
+ return res;
+}
+
+/**
+ * clutter_actor_bind_model_with_properties:
+ * @self: a #ClutterActor
+ * @model: a #GListModel
+ * @child_type: the type of #ClutterActor to use when creating
+ * children mapping to items inside the @model
+ * @first_model_property: the first property of @model to bind
+ * @...: tuples of property names on the @model, on the child, and the
+ * #GBindingFlags used to bind them, terminated by %NULL
+ *
+ * Binds a #GListModel to a #ClutterActor.
+ *
+ * Unlike clutter_actor_bind_model(), this function automatically creates
+ * a child #ClutterActor of type @child_type, and binds properties on the
+ * items inside the @model to the corresponding properties on the child,
+ * for instance:
+ *
+ * |[<!-- language="C" -->
+ * clutter_actor_bind_model_with_properties (actor, model,
+ * MY_TYPE_CHILD_VIEW,
+ * "label", "text", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE,
+ * "icon", "image", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE,
+ * "selected", "selected", G_BINDING_BIDIRECTIONAL,
+ * "active", "active", G_BINDING_BIDIRECTIONAL,
+ * NULL);
+ * ]|
+ *
+ * is the equivalent of calling clutter_actor_bind_model() with a
+ * #ClutterActorCreateChildFunc of:
+ *
+ * |[<!-- language="C" -->
+ * ClutterActor *res = g_object_new (MY_TYPE_CHILD_VIEW, NULL);
+ *
+ * g_object_bind_property (item, "label", res, "text", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
+ * g_object_bind_property (item, "icon", res, "image", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
+ * g_object_bind_property (item, "selected", res, "selected", G_BINDING_BIDIRECTIONAL);
+ * g_object_bind_property (item, "active", res, "active", G_BINDING_BIDIRECTIONAL);
+ *
+ * return res;
+ * ]|
+ *
+ * If the #ClutterActor was already bound to a #GListModel, the previous
+ * binding is destroyed.
+ *
+ * When a #ClutterActor is bound to a model, adding and removing children
+ * directly is undefined behaviour.
+ *
+ * See also: clutter_actor_bind_model()
+ *
+ * Since: 1.24
+ */
+void
+clutter_actor_bind_model_with_properties (ClutterActor *self,
+ GListModel *model,
+ GType child_type,
+ const char *first_model_property,
+ ...)
+{
+ va_list args;
+ BindClosure *clos;
+ const char *model_property;
+
+ g_return_if_fail (CLUTTER_IS_ACTOR (self));
+ g_return_if_fail (G_IS_LIST_MODEL (model));
+ g_return_if_fail (g_type_is_a (child_type, CLUTTER_TYPE_ACTOR));
+
+ clos = g_slice_new0 (BindClosure);
+ clos->child_type = child_type;
+ clos->props = g_array_new (FALSE, FALSE, sizeof (BindProperty));
+
+ va_start (args, first_model_property);
+ model_property = first_model_property;
+ while (model_property != NULL)
+ {
+ const char *child_property = va_arg (args, char *);
+ GBindingFlags binding_flags = va_arg (args, guint);
+ BindProperty bind;
+
+ bind.model_property = g_intern_string (model_property);
+ bind.child_property = g_intern_string (child_property);
+ bind.flags = binding_flags;
+
+ g_array_append_val (clos->props, bind);
+
+ model_property = va_arg (args, char *);
+ }
+
+ clutter_actor_bind_model (self, model, bind_child_with_properties, clos, bind_closure_free);
+}
diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h
index a821a61c5..6949d9743 100644
--- a/clutter/clutter-actor.h
+++ b/clutter/clutter-actor.h
@@ -877,6 +877,12 @@ void clutter_actor_bind_model
ClutterActorCreateChildFunc create_child_func,
gpointer user_data,
GDestroyNotify notify);
+CLUTTER_AVAILABLE_IN_1_24
+void clutter_actor_bind_model_with_properties (ClutterActor *self,
+ GListModel *model,
+ GType child_type,
+ const char *first_model_property,
+ ...);
G_END_DECLS
diff --git a/doc/reference/clutter-sections.txt b/doc/reference/clutter-sections.txt
index 0e727fba1..3880923c8 100644
--- a/doc/reference/clutter-sections.txt
+++ b/doc/reference/clutter-sections.txt
@@ -467,6 +467,7 @@ clutter_actor_iter_remove
clutter_actor_iter_destroy
ClutterActorCreateChildFunc
clutter_actor_bind_model
+clutter_actor_bind_model_with_properties
<SUBSECTION>
clutter_actor_save_easing_state
diff --git a/examples/actor-model.c b/examples/actor-model.c
index 18547f1b3..4a6ec778d 100644
--- a/examples/actor-model.c
+++ b/examples/actor-model.c
@@ -430,27 +430,6 @@ on_model_item_selection (GObject *model_item,
}
static ClutterActor *
-create_menu_item (gpointer item,
- gpointer data G_GNUC_UNUSED)
-{
- ClutterActor *res = g_object_new (EXAMPLE_TYPE_MENU_ITEM_VIEW, NULL);
-
- /* The label goes from the model to the view */
- g_object_bind_property (item, "label",
- res, "text",
- G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
-
- /* The selected state goes in either direction */
- g_object_bind_property (item, "selected",
- res, "selected",
- G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
-
- g_signal_connect (item, "notify::selected", G_CALLBACK (on_model_item_selection), NULL);
-
- return res;
-}
-
-static ClutterActor *
create_menu_actor (void)
{
/* Our store of menu item models */
@@ -469,6 +448,10 @@ create_menu_actor (void)
g_list_store_append (model, item);
+ g_signal_connect (item, "notify::selected",
+ G_CALLBACK (on_model_item_selection),
+ NULL);
+
g_object_unref (item);
g_free (label);
}
@@ -477,9 +460,11 @@ create_menu_actor (void)
* create ClutterActor views of each item in the model, and add them
* to the menu actor
*/
- clutter_actor_bind_model (menu, G_LIST_MODEL (model),
- create_menu_item,
- NULL, NULL);
+ clutter_actor_bind_model_with_properties (menu, G_LIST_MODEL (model),
+ EXAMPLE_TYPE_MENU_ITEM_VIEW,
+ "label", "text", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE,
+ "selected", "selected", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE,
+ NULL);
/* We don't need a pointer to the model any more, so we transfer ownership
* to the menu actor; this means that the model will go away when the menu