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:
parent
eda436f0c4
commit
b0e785c6c2
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user