diff --git a/clutter/clutter-actor-private.h b/clutter/clutter-actor-private.h index 716234682..27a4b1342 100644 --- a/clutter/clutter-actor-private.h +++ b/clutter/clutter-actor-private.h @@ -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); diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 4bf713bca..39a157b11 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -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); }