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:
* @self: A #ClutterActor
* @parent: A new #ClutterActor parent
* clutter_actor_get_children:
* @self: a #ClutterActor
*
* Sets the parent of @self to @parent. The opposite function is
* clutter_actor_unparent().
* Retrieves the list of children of @self.
*
* This function should not be used by applications, but by custom
* container actor subclasses.
* Return value: (transfer container) (element-type ClutterActor): A newly
* 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
clutter_actor_set_parent (ClutterActor *self,
ClutterActor *parent)
clutter_actor_add_child (ClutterActor *self,
ClutterActor *child)
{
ClutterActorPrivate *priv;
ClutterActorPrivate *parent_priv;
ClutterTextDirection text_dir;
g_return_if_fail (CLUTTER_IS_ACTOR (self));
g_return_if_fail (CLUTTER_IS_ACTOR (parent));
g_return_if_fail (self != parent);
g_return_if_fail (CLUTTER_IS_ACTOR (child));
g_return_if_fail (self != child);
priv = self->priv;
if (priv->parent_actor != NULL)
if (child->priv->parent_actor != NULL)
{
g_warning ("Cannot set a parent on an actor which has a parent.\n"
"You must use clutter_actor_unparent() first.\n");
return;
}
if (CLUTTER_ACTOR_IS_TOPLEVEL (self))
if (CLUTTER_ACTOR_IS_TOPLEVEL (child))
{
g_warning ("Cannot set a parent on a toplevel actor\n");
return;
}
if (CLUTTER_ACTOR_IN_DESTRUCTION (self))
if (CLUTTER_ACTOR_IN_DESTRUCTION (child))
{
g_warning ("Cannot set a parent currently being destroyed");
return;
}
g_object_ref_sink (self);
priv->parent_actor = parent;
g_object_ref_sink (child);
child->priv->parent_actor = self;
/* Maintain an explicit list of children for every actor... */
parent_priv = parent->priv;
parent_priv->children =
g_list_prepend (parent_priv->children, self);
parent_priv->n_children++;
self->priv->children = g_list_prepend (self->priv->children, child);
self->priv->n_children++;
/* if push_internal() has been called then we automatically set
* the flag on the actor
*/
if (parent->priv->internal_child)
CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_INTERNAL_CHILD);
if (self->priv->internal_child)
CLUTTER_SET_PRIVATE_FLAGS (child, CLUTTER_INTERNAL_CHILD);
/* clutter_actor_reparent() will emit ::parent-set for us */
if (!CLUTTER_ACTOR_IN_REPARENT (self))
g_signal_emit (self, actor_signals[PARENT_SET], 0, NULL);
if (!CLUTTER_ACTOR_IN_REPARENT (child))
g_signal_emit (child, actor_signals[PARENT_SET], 0, NULL);
/* If parent is mapped or realized, we need to also be mapped or
* 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 */
text_dir = clutter_actor_get_text_direction (parent);
clutter_actor_set_text_direction (self, text_dir);
text_dir = clutter_actor_get_text_direction (self);
clutter_actor_set_text_direction (child, text_dir);
if (priv->show_on_set_parent)
clutter_actor_show (self);
if (child->priv->show_on_set_parent)
clutter_actor_show (child);
if (CLUTTER_ACTOR_IS_MAPPED (self))
clutter_actor_queue_redraw (self);
if (CLUTTER_ACTOR_IS_MAPPED (child))
clutter_actor_queue_redraw (child);
/* maintain the invariant that if an actor needs layout,
* its parents do as well
*/
if (priv->needs_width_request ||
priv->needs_height_request ||
priv->needs_allocation)
if (child->priv->needs_width_request ||
child->priv->needs_height_request ||
child->priv->needs_allocation)
{
/* we work around the short-circuiting we do
* in clutter_actor_queue_relayout() since we
* want to force a relayout
*/
priv->needs_width_request = TRUE;
priv->needs_height_request = TRUE;
priv->needs_allocation = TRUE;
child->priv->needs_width_request = TRUE;
child->priv->needs_height_request = 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:
* @self: A #ClutterActor
@ -8368,35 +8398,33 @@ invalidate_queue_redraw_entry (ClutterActor *self,
}
/**
* clutter_actor_unparent:
* clutter_actor_remove_child:
* @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
* #ClutterContainer interface, or by composite actors that do
* not implicitly create their children.
*
* Since: 0.1.1
* Since: 1.10
*/
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;
g_return_if_fail (CLUTTER_IS_ACTOR (self));
g_return_if_fail (CLUTTER_IS_ACTOR (child));
priv = self->priv;
if (priv->parent_actor == NULL)
if (child->priv->parent_actor == NULL)
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,
* 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,
* 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
* 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
* https://bugzilla.gnome.org/show_bug.cgi?id=652036
*/
_clutter_actor_traverse (self,
_clutter_actor_traverse (child,
0,
invalidate_queue_redraw_entry,
NULL,
NULL);
old_parent = priv->parent_actor;
priv->parent_actor = NULL;
child->priv->parent_actor = NULL;
/* clutter_actor_reparent() will emit ::parent-set for us */
if (!CLUTTER_ACTOR_IN_REPARENT (self))
g_signal_emit (self, actor_signals[PARENT_SET], 0, old_parent);
if (!CLUTTER_ACTOR_IN_REPARENT (child))
g_signal_emit (child, actor_signals[PARENT_SET], 0, self);
old_parent_priv = old_parent->priv;
old_parent_priv->children = g_list_remove (old_parent_priv->children, self);
old_parent_priv->n_children--;
self->priv->children = g_list_remove (self->priv->children, child);
self->priv->n_children--;
/* 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.
*/
if (was_mapped && !CLUTTER_ACTOR_IS_MAPPED (self))
clutter_actor_queue_redraw (old_parent);
if (was_mapped && !CLUTTER_ACTOR_IS_MAPPED (child))
clutter_actor_queue_redraw (self);
/* 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 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,
ClutterActor *parent);
ClutterActor * clutter_actor_get_parent (ClutterActor *self);