actor: Provide add/remove child and get children methods

Let's try and move away from the reverse implicit scene graph build API,
which we mutuated from GTK+, towards a more traditional node/child API.

The set_parent()/unparent() API is confusing, unless you know the
history; having a add_child()/remove_child() methods pair makes it more
explicit.

We can easily implement the old set_parent()/unparent() pair in terms of
the newly add_child()/remove_child() one.
This commit is contained in:
Emmanuele Bassi 2011-11-17 15:03:32 +00:00 committed by Emmanuele Bassi
parent 0396d3e7e6
commit 53aa64aeb9
2 changed files with 117 additions and 70 deletions

View File

@ -8217,102 +8217,132 @@ clutter_actor_get_clip (ClutterActor *self,
} }
/** /**
* clutter_actor_set_parent: * clutter_actor_get_children:
* @self: A #ClutterActor * @self: a #ClutterActor
* @parent: A new #ClutterActor parent
* *
* Sets the parent of @self to @parent. The opposite function is * Retrieves the list of children of @self.
* clutter_actor_unparent().
* *
* This function should not be used by applications, but by custom * Return value: (transfer container) (element-type ClutterActor): A newly
* container actor subclasses. * allocated #GList of #ClutterActor<!-- -->s. Use g_list_free() when
* done.
*
* Since: 1.10
*/
GList *
clutter_actor_get_children (ClutterActor *self)
{
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL);
return g_list_copy (self->priv->children);
}
/**
* clutter_actor_add_child:
* @self: a #ClutterActor
* @child: a #ClutterActor
*
* Adds @child to the children of @self.
*
* This function will acquire a reference on @child.
*
* Since: 1.10
*/ */
void void
clutter_actor_set_parent (ClutterActor *self, clutter_actor_add_child (ClutterActor *self,
ClutterActor *parent) ClutterActor *child)
{ {
ClutterActorPrivate *priv;
ClutterActorPrivate *parent_priv;
ClutterTextDirection text_dir; ClutterTextDirection text_dir;
g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTOR (self));
g_return_if_fail (CLUTTER_IS_ACTOR (parent)); g_return_if_fail (CLUTTER_IS_ACTOR (child));
g_return_if_fail (self != parent); g_return_if_fail (self != child);
priv = self->priv; if (child->priv->parent_actor != NULL)
if (priv->parent_actor != NULL)
{ {
g_warning ("Cannot set a parent on an actor which has a parent.\n" g_warning ("Cannot set a parent on an actor which has a parent.\n"
"You must use clutter_actor_unparent() first.\n"); "You must use clutter_actor_unparent() first.\n");
return; return;
} }
if (CLUTTER_ACTOR_IS_TOPLEVEL (self)) if (CLUTTER_ACTOR_IS_TOPLEVEL (child))
{ {
g_warning ("Cannot set a parent on a toplevel actor\n"); g_warning ("Cannot set a parent on a toplevel actor\n");
return; return;
} }
if (CLUTTER_ACTOR_IN_DESTRUCTION (self)) if (CLUTTER_ACTOR_IN_DESTRUCTION (child))
{ {
g_warning ("Cannot set a parent currently being destroyed"); g_warning ("Cannot set a parent currently being destroyed");
return; return;
} }
g_object_ref_sink (self); g_object_ref_sink (child);
priv->parent_actor = parent; child->priv->parent_actor = self;
/* Maintain an explicit list of children for every actor... */ /* Maintain an explicit list of children for every actor... */
parent_priv = parent->priv; self->priv->children = g_list_prepend (self->priv->children, child);
parent_priv->children = self->priv->n_children++;
g_list_prepend (parent_priv->children, self);
parent_priv->n_children++;
/* if push_internal() has been called then we automatically set /* if push_internal() has been called then we automatically set
* the flag on the actor * the flag on the actor
*/ */
if (parent->priv->internal_child) if (self->priv->internal_child)
CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_INTERNAL_CHILD); CLUTTER_SET_PRIVATE_FLAGS (child, CLUTTER_INTERNAL_CHILD);
/* clutter_actor_reparent() will emit ::parent-set for us */ /* clutter_actor_reparent() will emit ::parent-set for us */
if (!CLUTTER_ACTOR_IN_REPARENT (self)) if (!CLUTTER_ACTOR_IN_REPARENT (child))
g_signal_emit (self, actor_signals[PARENT_SET], 0, NULL); g_signal_emit (child, actor_signals[PARENT_SET], 0, NULL);
/* If parent is mapped or realized, we need to also be mapped or /* If parent is mapped or realized, we need to also be mapped or
* realized once we're inside the parent. * realized once we're inside the parent.
*/ */
clutter_actor_update_map_state (self, MAP_STATE_CHECK); clutter_actor_update_map_state (child, MAP_STATE_CHECK);
/* propagate the parent's text direction to the child */ /* propagate the parent's text direction to the child */
text_dir = clutter_actor_get_text_direction (parent); text_dir = clutter_actor_get_text_direction (self);
clutter_actor_set_text_direction (self, text_dir); clutter_actor_set_text_direction (child, text_dir);
if (priv->show_on_set_parent) if (child->priv->show_on_set_parent)
clutter_actor_show (self); clutter_actor_show (child);
if (CLUTTER_ACTOR_IS_MAPPED (self)) if (CLUTTER_ACTOR_IS_MAPPED (child))
clutter_actor_queue_redraw (self); clutter_actor_queue_redraw (child);
/* maintain the invariant that if an actor needs layout, /* maintain the invariant that if an actor needs layout,
* its parents do as well * its parents do as well
*/ */
if (priv->needs_width_request || if (child->priv->needs_width_request ||
priv->needs_height_request || child->priv->needs_height_request ||
priv->needs_allocation) child->priv->needs_allocation)
{ {
/* we work around the short-circuiting we do /* we work around the short-circuiting we do
* in clutter_actor_queue_relayout() since we * in clutter_actor_queue_relayout() since we
* want to force a relayout * want to force a relayout
*/ */
priv->needs_width_request = TRUE; child->priv->needs_width_request = TRUE;
priv->needs_height_request = TRUE; child->priv->needs_height_request = TRUE;
priv->needs_allocation = TRUE; child->priv->needs_allocation = TRUE;
clutter_actor_queue_relayout (priv->parent_actor); clutter_actor_queue_relayout (child->priv->parent_actor);
} }
} }
/**
* clutter_actor_set_parent:
* @self: A #ClutterActor
* @parent: A new #ClutterActor parent
*
* Sets the parent of @self to @parent.
*
* This function just calls clutter_actor_add_child().
*/
void
clutter_actor_set_parent (ClutterActor *self,
ClutterActor *parent)
{
clutter_actor_add_child (parent, self);
}
/** /**
* clutter_actor_get_parent: * clutter_actor_get_parent:
* @self: A #ClutterActor * @self: A #ClutterActor
@ -8368,35 +8398,33 @@ invalidate_queue_redraw_entry (ClutterActor *self,
} }
/** /**
* clutter_actor_unparent: * clutter_actor_remove_child:
* @self: a #ClutterActor * @self: a #ClutterActor
* @child: a #ClutterActor
* *
* Removes the parent of @self. * Removes @child from the children of @self.
* *
* This function should not be used in applications. * This function will release the reference added by
* clutter_actor_add_child().
* *
* This function should only be called by implementations of the * Since: 1.10
* #ClutterContainer interface, or by composite actors that do
* not implicitly create their children.
*
* Since: 0.1.1
*/ */
void void
clutter_actor_unparent (ClutterActor *self) clutter_actor_remove_child (ClutterActor *self,
ClutterActor *child)
{ {
ClutterActorPrivate *priv;
ClutterActor *old_parent;
ClutterActorPrivate *old_parent_priv;
gboolean was_mapped; gboolean was_mapped;
g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTOR (self));
g_return_if_fail (CLUTTER_IS_ACTOR (child));
priv = self->priv; if (child->priv->parent_actor == NULL)
if (priv->parent_actor == NULL)
return; return;
was_mapped = CLUTTER_ACTOR_IS_MAPPED (self); if (child->priv->parent_actor != self)
return;
was_mapped = CLUTTER_ACTOR_IS_MAPPED (child);
/* we need to unrealize *before* we set parent_actor to NULL, /* we need to unrealize *before* we set parent_actor to NULL,
* because in an unrealize method actors are dissociating from the * because in an unrealize method actors are dissociating from the
@ -8404,7 +8432,7 @@ clutter_actor_unparent (ClutterActor *self)
* clutter_actor_get_stage(). This should unmap and unrealize, * clutter_actor_get_stage(). This should unmap and unrealize,
* unless we're reparenting. * unless we're reparenting.
*/ */
clutter_actor_update_map_state (self, MAP_STATE_MAKE_UNREALIZED); clutter_actor_update_map_state (child, MAP_STATE_MAKE_UNREALIZED);
/* We take this opportunity to invalidate any queue redraw entry /* We take this opportunity to invalidate any queue redraw entry
* associated with the actor and descendants since we won't be able to * associated with the actor and descendants since we won't be able to
@ -8419,31 +8447,45 @@ clutter_actor_unparent (ClutterActor *self)
* http://bugzilla.clutter-project.org/show_bug.cgi?id=2621 * http://bugzilla.clutter-project.org/show_bug.cgi?id=2621
* https://bugzilla.gnome.org/show_bug.cgi?id=652036 * https://bugzilla.gnome.org/show_bug.cgi?id=652036
*/ */
_clutter_actor_traverse (self, _clutter_actor_traverse (child,
0, 0,
invalidate_queue_redraw_entry, invalidate_queue_redraw_entry,
NULL, NULL,
NULL); NULL);
old_parent = priv->parent_actor; child->priv->parent_actor = NULL;
priv->parent_actor = NULL;
/* clutter_actor_reparent() will emit ::parent-set for us */ /* clutter_actor_reparent() will emit ::parent-set for us */
if (!CLUTTER_ACTOR_IN_REPARENT (self)) if (!CLUTTER_ACTOR_IN_REPARENT (child))
g_signal_emit (self, actor_signals[PARENT_SET], 0, old_parent); g_signal_emit (child, actor_signals[PARENT_SET], 0, self);
old_parent_priv = old_parent->priv; self->priv->children = g_list_remove (self->priv->children, child);
old_parent_priv->children = g_list_remove (old_parent_priv->children, self); self->priv->n_children--;
old_parent_priv->n_children--;
/* Queue a redraw on old_parent only if we were painted in the first /* Queue a redraw on old_parent only if we were painted in the first
* place. Will be no-op if old parent is not shown. * place. Will be no-op if old parent is not shown.
*/ */
if (was_mapped && !CLUTTER_ACTOR_IS_MAPPED (self)) if (was_mapped && !CLUTTER_ACTOR_IS_MAPPED (child))
clutter_actor_queue_redraw (old_parent); clutter_actor_queue_redraw (self);
/* remove the reference we acquired in clutter_actor_set_parent() */ /* remove the reference we acquired in clutter_actor_set_parent() */
g_object_unref (self); g_object_unref (child);
}
/**
* clutter_actor_unparent:
* @self: a #ClutterActor
*
* Removes the parent of @self.
*
* This function just calls clutter_actor_remove_child().
*
* Since: 0.1.1
*/
void
clutter_actor_unparent (ClutterActor *self)
{
clutter_actor_remove_child (self->priv->parent_actor, self);
} }
/** /**

View File

@ -411,6 +411,11 @@ void clutter_actor_set_clip_to_allocation (ClutterActor
gboolean clip_set); gboolean clip_set);
gboolean clutter_actor_get_clip_to_allocation (ClutterActor *self); gboolean clutter_actor_get_clip_to_allocation (ClutterActor *self);
void clutter_actor_add_child (ClutterActor *self,
ClutterActor *child);
void clutter_actor_remove_child (ClutterActor *self,
ClutterActor *child);
GList * clutter_actor_get_children (ClutterActor *self);
void clutter_actor_set_parent (ClutterActor *self, void clutter_actor_set_parent (ClutterActor *self,
ClutterActor *parent); ClutterActor *parent);
ClutterActor * clutter_actor_get_parent (ClutterActor *self); ClutterActor * clutter_actor_get_parent (ClutterActor *self);