mirror of
https://github.com/brl/mutter.git
synced 2024-12-23 19:42:05 +00:00
actor: make _clutter_actor_traverse more flexible
This makes it possible to choose the traversal order; either depth first or breadth first and when visiting actors in a depth first order there is now a callback called before children are traversed and one called after. Some tasks such as unrealizing actors need to explicitly control the traversal order to maintain the invariable that all children of an actor are unrealized before we actually mark the parent as unrealized. The callbacks are now passed the relative depth in the graph of the actor being visited and instead of only being able to return a boolean to bail out of further traversal it can now do one of: continue, skip_children or break. To implement something like unrealize it's desirable to skip children that you find have already been unrealized.
This commit is contained in:
parent
9df6f0c524
commit
4bda674732
@ -44,15 +44,55 @@ typedef enum
|
||||
} ClutterRedrawFlags;
|
||||
|
||||
/* ClutterActorTraverseFlags:
|
||||
* CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST: Traverse the graph in
|
||||
* a depth first order.
|
||||
* CLUTTER_ACTOR_TRAVERSE_BREADTH_FIRST: Traverse the graph in a
|
||||
* breadth first order.
|
||||
*
|
||||
* Controls some options for how clutter_actor_traverse() iterates
|
||||
* through the graph.
|
||||
*/
|
||||
typedef enum _ClutterActorTraverseFlags
|
||||
{
|
||||
CLUTTER_ACTOR_TRAVERSE_PLACE_HOLDER = 1L<<0
|
||||
CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST = 1L<<0,
|
||||
CLUTTER_ACTOR_TRAVERSE_BREADTH_FIRST = 1L<<1
|
||||
} ClutterActorTraverseFlags;
|
||||
|
||||
/**
|
||||
* ClutterActorTraverseVisitFlags:
|
||||
* CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE: Continue traversing as
|
||||
* normal
|
||||
* CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN: Don't traverse the
|
||||
* children of the last visited actor. (Not applicable when using
|
||||
* CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST_POST_ORDER since the children
|
||||
* are visited before having an opportunity to bail out)
|
||||
* CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK: Immediately bail out without
|
||||
* visiting any more actors.
|
||||
*
|
||||
* Each time an actor is visited during a scenegraph traversal the
|
||||
* ClutterTraverseCallback can return a set of flags that may affect
|
||||
* the continuing traversal. It may stop traversal completely, just
|
||||
* skip over children for the current actor or continue as normal.
|
||||
*/
|
||||
typedef enum _ClutterActorTraverseVisitFlags
|
||||
{
|
||||
CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE = 1L<<0,
|
||||
CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN = 1L<<1,
|
||||
CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK = 1L<<2
|
||||
} ClutterActorTraverseVisitFlags;
|
||||
|
||||
/**
|
||||
* ClutterTraverseCallback:
|
||||
*
|
||||
* The callback prototype used with clutter_actor_traverse. The
|
||||
* returned flags can be used to affect the continuing traversal
|
||||
* either by continuing as normal, skipping over children of an
|
||||
* actor or bailing out completely.
|
||||
*/
|
||||
typedef ClutterActorTraverseVisitFlags (*ClutterTraverseCallback) (ClutterActor *actor,
|
||||
int depth,
|
||||
void *user_data);
|
||||
|
||||
/* ClutterForeachCallback:
|
||||
* @actor: The actor being iterated
|
||||
* @user_data: The private data specified when starting the iteration
|
||||
@ -72,9 +112,10 @@ gint _clutter_actor_get_n_children (ClutterActor *self);
|
||||
gboolean _clutter_actor_foreach_child (ClutterActor *self,
|
||||
ClutterForeachCallback callback,
|
||||
void *user_data);
|
||||
gboolean _clutter_actor_traverse (ClutterActor *actor,
|
||||
void _clutter_actor_traverse (ClutterActor *actor,
|
||||
ClutterActorTraverseFlags flags,
|
||||
ClutterForeachCallback callback,
|
||||
ClutterTraverseCallback before_children_callback,
|
||||
ClutterTraverseCallback after_children_callback,
|
||||
void *user_data);
|
||||
ClutterActor *_clutter_actor_get_stage_internal (ClutterActor *actor);
|
||||
|
||||
|
@ -7664,8 +7664,9 @@ clutter_actor_get_paint_visibility (ClutterActor *actor)
|
||||
return CLUTTER_ACTOR_IS_MAPPED (actor);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
static ClutterActorTraverseVisitFlags
|
||||
invalidate_queue_redraw_entry (ClutterActor *self,
|
||||
int depth,
|
||||
gpointer user_data)
|
||||
{
|
||||
ClutterActorPrivate *priv = self->priv;
|
||||
@ -7676,7 +7677,7 @@ invalidate_queue_redraw_entry (ClutterActor *self,
|
||||
priv->queue_redraw_entry = NULL;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -7712,6 +7713,7 @@ clutter_actor_unparent (ClutterActor *self)
|
||||
_clutter_actor_traverse (self,
|
||||
0,
|
||||
invalidate_queue_redraw_entry,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
was_mapped = CLUTTER_ACTOR_IS_MAPPED (self);
|
||||
@ -11840,42 +11842,132 @@ _clutter_actor_foreach_child (ClutterActor *self,
|
||||
return cont;
|
||||
}
|
||||
|
||||
/* For debugging purposes this gives us a simple way to print out
|
||||
* the scenegraph e.g in gdb using:
|
||||
* [|
|
||||
* _clutter_actor_traverse (clutter_stage_get_default (),
|
||||
* 0,
|
||||
* _clutter_debug_print_actor_cb,
|
||||
* NULL,
|
||||
* NULL);
|
||||
* |]
|
||||
*/
|
||||
ClutterActorTraverseVisitFlags
|
||||
_clutter_debug_print_actor_cb (ClutterActor *actor,
|
||||
int depth,
|
||||
void *user_data)
|
||||
{
|
||||
g_print ("%*s%s:%p\n", depth * 2, "", G_OBJECT_TYPE_NAME (actor), actor);
|
||||
return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_clutter_actor_traverse_breadth (ClutterActor *actor,
|
||||
ClutterTraverseCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GQueue *queue = g_queue_new ();
|
||||
ClutterActor dummy;
|
||||
int current_depth = 0;
|
||||
|
||||
g_queue_push_tail (queue, actor);
|
||||
g_queue_push_tail (queue, &dummy); /* use to delimit depth changes */
|
||||
|
||||
while ((actor = g_queue_pop_head (queue)))
|
||||
{
|
||||
ClutterActorTraverseVisitFlags flags;
|
||||
|
||||
if (actor == &dummy)
|
||||
{
|
||||
current_depth++;
|
||||
g_queue_push_tail (queue, &dummy);
|
||||
continue;
|
||||
}
|
||||
|
||||
flags = callback (actor, current_depth, user_data);
|
||||
if (flags & CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK)
|
||||
break;
|
||||
else if (!(flags & CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN))
|
||||
{
|
||||
GList *l;
|
||||
for (l = actor->priv->children; l; l = l->next)
|
||||
g_queue_push_tail (queue, l->data);
|
||||
}
|
||||
}
|
||||
|
||||
g_queue_free (queue);
|
||||
}
|
||||
|
||||
static ClutterActorTraverseVisitFlags
|
||||
_clutter_actor_traverse_depth (ClutterActor *actor,
|
||||
ClutterTraverseCallback before_children_callback,
|
||||
ClutterTraverseCallback after_children_callback,
|
||||
int current_depth,
|
||||
gpointer user_data)
|
||||
{
|
||||
ClutterActorTraverseVisitFlags flags;
|
||||
|
||||
flags = before_children_callback (actor, current_depth, user_data);
|
||||
if (flags & CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK)
|
||||
return CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK;
|
||||
|
||||
if (!(flags & CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN))
|
||||
{
|
||||
GList *l;
|
||||
for (l = actor->priv->children; l; l = l->next)
|
||||
{
|
||||
flags = _clutter_actor_traverse_depth (l->data,
|
||||
before_children_callback,
|
||||
after_children_callback,
|
||||
current_depth + 1,
|
||||
user_data);
|
||||
if (flags & CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK)
|
||||
return CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK;
|
||||
}
|
||||
}
|
||||
|
||||
if (after_children_callback)
|
||||
return after_children_callback (actor, current_depth, user_data);
|
||||
else
|
||||
return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
|
||||
}
|
||||
|
||||
/* _clutter_actor_traverse:
|
||||
* @actor: The actor to start traversing the graph from
|
||||
* @flags: These flags may affect how the traversal is done
|
||||
* @callback: The function to call for each actor traversed
|
||||
* @user_data: The private data to pass to the @callback
|
||||
* @before_children_callback: A function to call before visiting the
|
||||
* children of the current actor.
|
||||
* @after_children_callback: A function to call after visiting the
|
||||
* children of the current actor. (Ignored if
|
||||
* %CLUTTER_ACTOR_TRAVERSE_BREADTH_FIRST is passed to @flags.)
|
||||
* @user_data: The private data to pass to the callbacks
|
||||
*
|
||||
* Traverses the scenegraph starting at the specified @actor and
|
||||
* descending through all its children and its children's children.
|
||||
* For each actor traversed @callback is called with the specified
|
||||
* @user_data.
|
||||
* For each actor traversed @before_children_callback and
|
||||
* @after_children_callback are called with the specified
|
||||
* @user_data, before and after visiting that actor's children.
|
||||
*
|
||||
* If @callback ever returns %FALSE then no more actors will be
|
||||
* traversed.
|
||||
*
|
||||
* Return value: %TRUE if @actor and all its descendants were
|
||||
* traversed or %FALSE if the @callback returned %FALSE to stop
|
||||
* traversal early.
|
||||
* The callbacks can return flags that affect the ongoing traversal
|
||||
* such as by skipping over an actors children or bailing out of
|
||||
* any further traversing.
|
||||
*/
|
||||
gboolean
|
||||
void
|
||||
_clutter_actor_traverse (ClutterActor *actor,
|
||||
ClutterActorTraverseFlags flags,
|
||||
ClutterForeachCallback callback,
|
||||
ClutterTraverseCallback before_children_callback,
|
||||
ClutterTraverseCallback after_children_callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
ClutterActorPrivate *priv;
|
||||
GList *l;
|
||||
gboolean cont;
|
||||
|
||||
if (!callback (actor, user_data))
|
||||
return FALSE;
|
||||
|
||||
priv = actor->priv;
|
||||
|
||||
for (cont = TRUE, l = priv->children; cont && l; l = l->next)
|
||||
cont = _clutter_actor_traverse (l->data, flags, callback, user_data);
|
||||
|
||||
return cont;
|
||||
if (flags & CLUTTER_ACTOR_TRAVERSE_BREADTH_FIRST)
|
||||
_clutter_actor_traverse_breadth (actor,
|
||||
before_children_callback,
|
||||
user_data);
|
||||
else /* DEPTH_FIRST */
|
||||
_clutter_actor_traverse_depth (actor,
|
||||
before_children_callback,
|
||||
after_children_callback,
|
||||
0, /* start depth */
|
||||
user_data);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user