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.
This commit is contained in:
Emmanuele Bassi 2015-07-10 11:26:34 +01:00
parent eda436f0c4
commit b0e785c6c2
4 changed files with 151 additions and 24 deletions

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -429,27 +429,6 @@ on_model_item_selection (GObject *model_item,
g_free (label);
}
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)
{
@ -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