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.
This commit is contained in:
parent
7cde4486aa
commit
bf9a71ae23
@ -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);
|
||||
}
|
||||
|
@ -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__ */
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user