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;
|
gulong in_cloned_branch;
|
||||||
|
|
||||||
|
GListModel *child_model;
|
||||||
|
ClutterActorCreateChildFunc create_child_func;
|
||||||
|
gpointer create_child_data;
|
||||||
|
GDestroyNotify create_child_notify;
|
||||||
|
|
||||||
/* bitfields: KEEP AT THE END */
|
/* bitfields: KEEP AT THE END */
|
||||||
|
|
||||||
/* fixed position and sizes */
|
/* fixed position and sizes */
|
||||||
@ -5928,6 +5933,18 @@ clutter_actor_dispose (GObject *object)
|
|||||||
g_clear_object (&priv->effects);
|
g_clear_object (&priv->effects);
|
||||||
g_clear_object (&priv->flatten_effect);
|
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)
|
if (priv->layout_manager != NULL)
|
||||||
{
|
{
|
||||||
clutter_layout_manager_set_container (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);
|
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 */
|
/* clutter-actor.h */
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
#include <pango/pango.h>
|
#include <pango/pango.h>
|
||||||
#include <atk/atk.h>
|
#include <atk/atk.h>
|
||||||
|
|
||||||
@ -851,6 +852,32 @@ CLUTTER_AVAILABLE_IN_1_22
|
|||||||
gint clutter_actor_get_opacity_override (ClutterActor *self);
|
gint clutter_actor_get_opacity_override (ClutterActor *self);
|
||||||
#endif
|
#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
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __CLUTTER_ACTOR_H__ */
|
#endif /* __CLUTTER_ACTOR_H__ */
|
||||||
|
@ -465,6 +465,8 @@ clutter_actor_iter_next
|
|||||||
clutter_actor_iter_prev
|
clutter_actor_iter_prev
|
||||||
clutter_actor_iter_remove
|
clutter_actor_iter_remove
|
||||||
clutter_actor_iter_destroy
|
clutter_actor_iter_destroy
|
||||||
|
ClutterActorCreateChildFunc
|
||||||
|
clutter_actor_bind_model
|
||||||
|
|
||||||
<SUBSECTION>
|
<SUBSECTION>
|
||||||
clutter_actor_save_easing_state
|
clutter_actor_save_easing_state
|
||||||
|
Loading…
Reference in New Issue
Block a user