From 1520ba6190165dc8902b622833efbccf23716751 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 18 Dec 2009 23:20:04 +0000 Subject: [PATCH] actor: Add internal child flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ClutterActor checks, when destroying and reparenting, if the parent actor implements the Container interface, and automatically calls the remove() method to perform a clean removal. Actors implementing Container, though, might have internal children; that is, children that are not added through the Container API. It is already possible to iterate through them using the Container API to avoid breaking invariants - but calling clutter_actor_destroy() on these children (even from the Container implementation, and thus outside of Clutter's control) will either lead to leaks or to segmentation faults. Clutter needs a way to distinguish a clutter_actor_set_parent() done on an internal child from one done on a "public" child; for this reason, a push/pop pair of functions should be available to Actor implementations to mark the section where they wish to add internal children: ➔ clutter_actor_push_internal (); ... clutter_actor_set_parent (child1, parent); clutter_actor_set_parent (child2, parent); ... ➔ clutter_actor_pop_internal (); The set_parent() call will automatically set the newly added INTERNAL_CHILD private flag on each child, and both clutter_actor_destroy() and clutter_actor_unparent() will check for the flag before deciding whether to call the Container's remove method. --- clutter/clutter-actor.c | 110 ++++++++++++++++++++- clutter/clutter-actor.h | 3 + clutter/clutter-private.h | 29 ++++-- doc/reference/clutter/clutter-sections.txt | 2 + 4 files changed, 132 insertions(+), 12 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index f676d5e3b..b291cd2b8 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -3001,8 +3001,14 @@ clutter_actor_dispose (GObject *object) { ClutterActor *parent = priv->parent_actor; - if (CLUTTER_IS_CONTAINER (parent)) - clutter_container_remove_actor (CLUTTER_CONTAINER (parent), self); + /* go through the Container implementation unless this + * is an internal child and has been marked as such + */ + if (CLUTTER_IS_CONTAINER (parent) && + !(CLUTTER_PRIVATE_FLAGS (self) & CLUTTER_ACTOR_INTERNAL_CHILD)) + { + clutter_container_remove_actor (CLUTTER_CONTAINER (parent), self); + } else priv->parent_actor = NULL; } @@ -6619,6 +6625,7 @@ clutter_actor_set_parent (ClutterActor *self, { ClutterActorPrivate *priv; ClutterTextDirection text_dir; + ClutterMainContext *ctx; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTOR (parent)); @@ -6648,6 +6655,14 @@ clutter_actor_set_parent (ClutterActor *self, g_object_ref_sink (self); priv->parent_actor = parent; + ctx = _clutter_context_get_default (); + + /* if push_internal() has been called then we automatically set + * the flag on the actor + */ + if (ctx->internal_child) + CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_ACTOR_INTERNAL_CHILD); + /* clutter_actor_reparent() will emit ::parent-set for us */ if (!(CLUTTER_PRIVATE_FLAGS (self) & CLUTTER_ACTOR_IN_REPARENT)) g_signal_emit (self, actor_signals[PARENT_SET], 0, NULL); @@ -6825,17 +6840,22 @@ clutter_actor_reparent (ClutterActor *self, g_object_ref (self); - if (CLUTTER_IS_CONTAINER (priv->parent_actor)) + /* go through the Container implementation if this is a regular + * child and not an internal one + */ + if (CLUTTER_IS_CONTAINER (priv->parent_actor) && + !(CLUTTER_PRIVATE_FLAGS (self) & CLUTTER_ACTOR_INTERNAL_CHILD)) { ClutterContainer *parent = CLUTTER_CONTAINER (priv->parent_actor); - /* Note, will call unparent() */ + + /* this will have to call unparent() */ clutter_container_remove_actor (parent, self); } else clutter_actor_unparent (self); - if (CLUTTER_IS_CONTAINER (new_parent)) /* Note, will call parent() */ + if (CLUTTER_IS_CONTAINER (new_parent)) clutter_container_add_actor (CLUTTER_CONTAINER (new_parent), self); else clutter_actor_set_parent (self, new_parent); @@ -9471,3 +9491,83 @@ clutter_actor_get_text_direction (ClutterActor *self) return priv->text_direction; } + +/** + * clutter_actor_push_internal: + * + * Should be used by actors implementing the #ClutterContainer and with + * internal children added through clutter_actor_set_parent(), for instance: + * + * |[ + * static void + * my_actor_init (MyActor *self) + * { + * self->priv = SELF_ACTOR_GET_PRIVATE (self); + * + * clutter_actor_push_internal (); + * + * /* calling clutter_actor_set_parent() now will result in + * * the internal flag being set on a child of MyActor + * */ + * + * /* internal child: a background texture */ + * self->priv->background_tex = clutter_texture_new (); + * clutter_actor_set_parent (self->priv->background_tex, + * CLUTTER_ACTOR (self)); + * + * /* internal child: a label */ + * self->priv->label = clutter_text_new (); + * clutter_actor_set_parent (self->priv->label, + * CLUTTER_ACTOR (self)); + * + * clutter_actor_pop_internal (); + * + * /* calling clutter_actor_set_parent() now will not result in + * * the internal flag being set on a child of MyActor + * */ + * } + * ]| + * + * This function will be used by Clutter to toggle an "internal child" + * flag whenever clutter_actor_set_parent() is called; internal children + * are handled differently by Clutter, specifically when destroying their + * parent. + * + * Call clutter_actor_pop_internal() when you finished adding internal + * children. + * + * Nested calls to clutter_actor_push_internal() are allowed, but each + * one must by followed by a clutter_actor_pop_internal() call. + * + * Since: 1.2 + */ +void +clutter_actor_push_internal (void) +{ + ClutterMainContext *ctx = _clutter_context_get_default (); + + ctx->internal_child += 1; +} + +/** + * clutter_actor_pop_internal: + * + * Disables the effects of clutter_actor_pop_internal() + * + * Since: 1.2 + */ +void +clutter_actor_pop_internal (void) +{ + ClutterMainContext *ctx = _clutter_context_get_default (); + + if (ctx->internal_child == 0) + { + g_warning ("Mismatched %s: you need to call " + "clutter_actor_push_composite() at least once before " + "calling this function", G_STRFUNC); + return; + } + + ctx->internal_child -= 1; +} diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index 2f190a5ed..33c3ae773 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -532,6 +532,9 @@ void clutter_actor_set_text_direction (ClutterActor *sel ClutterTextDirection text_dir); ClutterTextDirection clutter_actor_get_text_direction (ClutterActor *self); +void clutter_actor_push_internal (void); +void clutter_actor_pop_internal (void); + G_END_DECLS #endif /* __CLUTTER_ACTOR_H__ */ diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index db9a6d2d0..d272ca9a4 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -58,13 +58,26 @@ typedef enum { CLUTTER_ACTOR_IN_DESTRUCTION = 1 << 0, CLUTTER_ACTOR_IS_TOPLEVEL = 1 << 1, CLUTTER_ACTOR_IN_REPARENT = 1 << 2, - CLUTTER_ACTOR_SYNC_MATRICES = 1 << 3, /* Used by stage to indicate GL - * viewport / perspective etc - * needs (re)setting. - */ - CLUTTER_ACTOR_IN_PAINT = 1 << 4, /* Used to avoid recursion */ - CLUTTER_ACTOR_IN_RELAYOUT = 1 << 5, /* Used to avoid recursion */ - CLUTTER_STAGE_IN_RESIZE = 1 << 6 + + /* Used by the stage to indicate GL viewport / perspective etc needs + * (re)setting. + */ + CLUTTER_ACTOR_SYNC_MATRICES = 1 << 3, + + /* Used to avoid recursion */ + CLUTTER_ACTOR_IN_PAINT = 1 << 4, + + /* Used to avoid recursion */ + CLUTTER_ACTOR_IN_RELAYOUT = 1 << 5, + + /* Used by the stage if resizing is an asynchronous operation (like on + * X11) to delay queueing relayouts until we got a notification from the + * event handling + */ + CLUTTER_STAGE_IN_RESIZE = 1 << 6, + + /* a flag for internal children of Containers */ + CLUTTER_ACTOR_INTERNAL_CHILD = 1 << 7 } ClutterPrivateFlags; struct _ClutterInputDevice @@ -133,6 +146,8 @@ struct _ClutterMainContext gulong redraw_count; GList *repaint_funcs; + + gint internal_child; }; #define CLUTTER_CONTEXT() (_clutter_context_get_default ()) diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index e7dafd772..ef125b011 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -352,6 +352,8 @@ clutter_actor_lower clutter_actor_raise_top clutter_actor_lower_bottom clutter_actor_get_stage +clutter_actor_push_internal +clutter_actor_pop_internal clutter_actor_set_depth