mirror of
https://github.com/brl/mutter.git
synced 2024-12-29 06:12:14 +00:00
actor: Add ClutterActorIter
Iterating over children and ancestors of an actor is a relatively common operation. Currently, you only have one option: start a for() loop, get the first child of the actor, and advance to the next sibling for the list of children; or start a for() loop and advance to the parent of the actor. These operations can be easily done through the ClutterActor API, but they all require going through the public API, and performing multiple type checks on the arguments. Along with the DOM API, it would be nice to have an ancillary, utility API that uses an iterator structure to hold the state, and can be advanced in a loop. https://bugzilla.gnome.org/show_bug.cgi?id=668669
This commit is contained in:
parent
fa856e3f5e
commit
58ffcfb10e
@ -395,6 +395,14 @@ struct _ClutterActorPrivate
|
||||
|
||||
gint n_children;
|
||||
|
||||
/* tracks whenever the children of an actor are changed; the
|
||||
* age is incremented by 1 whenever an actor is added or
|
||||
* removed. the age is not incremented when the first or the
|
||||
* last child pointers are changed, or when grandchildren of
|
||||
* an actor are changed.
|
||||
*/
|
||||
gint age;
|
||||
|
||||
gchar *name; /* a non-unique name, used for debugging */
|
||||
guint32 id; /* unique id, used for backward compatibility */
|
||||
|
||||
@ -3500,6 +3508,8 @@ clutter_actor_remove_child_internal (ClutterActor *self,
|
||||
|
||||
self->priv->n_children -= 1;
|
||||
|
||||
self->priv->age += 1;
|
||||
|
||||
/* clutter_actor_reparent() will emit ::parent-set for us */
|
||||
if (emit_parent_set && !CLUTTER_ACTOR_IN_REPARENT (child))
|
||||
g_signal_emit (child, actor_signals[PARENT_SET], 0, self);
|
||||
@ -10040,6 +10050,8 @@ clutter_actor_add_child_internal (ClutterActor *self,
|
||||
|
||||
self->priv->n_children += 1;
|
||||
|
||||
self->priv->age += 1;
|
||||
|
||||
/* if push_internal() has been called then we automatically set
|
||||
* the flag on the actor
|
||||
*/
|
||||
@ -15770,3 +15782,177 @@ clutter_actor_get_last_child (ClutterActor *self)
|
||||
|
||||
return self->priv->last_child;
|
||||
}
|
||||
|
||||
/* easy way to have properly named fields instead of the dummy ones
|
||||
* we use in the public structure
|
||||
*/
|
||||
typedef struct _RealActorIter
|
||||
{
|
||||
ClutterActor *root; /* dummy1 */
|
||||
ClutterActor *current; /* dummy2 */
|
||||
gpointer padding_1; /* dummy3 */
|
||||
gint age; /* dummy4 */
|
||||
gpointer padding_2; /* dummy5 */
|
||||
} RealActorIter;
|
||||
|
||||
/**
|
||||
* clutter_actor_iter_init:
|
||||
* @iter: a #ClutterActorIter
|
||||
* @root: a #ClutterActor
|
||||
*
|
||||
* Initializes a #ClutterActorIter, which can then be used to iterate
|
||||
* efficiently over a section of the scene graph, and associates it
|
||||
* with @root.
|
||||
*
|
||||
* Modifying the scene graph section that contains @root will invalidate
|
||||
* the iterator.
|
||||
*
|
||||
* |[
|
||||
* ClutterActorIter iter;
|
||||
* ClutterActor *child;
|
||||
*
|
||||
* clutter_actor_iter_init (&iter, container);
|
||||
* while (clutter_actor_iter_next (&iter, &child))
|
||||
* {
|
||||
* /* do something with child */
|
||||
* }
|
||||
* ]|
|
||||
*
|
||||
* Since: 1.10
|
||||
*/
|
||||
void
|
||||
clutter_actor_iter_init (ClutterActorIter *iter,
|
||||
ClutterActor *root)
|
||||
{
|
||||
RealActorIter *ri = (RealActorIter *) iter;
|
||||
|
||||
g_return_if_fail (iter != NULL);
|
||||
g_return_if_fail (CLUTTER_IS_ACTOR (root));
|
||||
|
||||
ri->root = root;
|
||||
ri->current = NULL;
|
||||
ri->age = root->priv->age;
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_actor_iter_next:
|
||||
* @iter: a #ClutterActorIter
|
||||
* @child: (out): return location for a #ClutterActor
|
||||
*
|
||||
* Advances the @iter and retrieves the next child of the root #ClutterActor
|
||||
* that was used to initialize the #ClutterActorIterator.
|
||||
*
|
||||
* If the iterator can advance, this function returns %TRUE and sets the
|
||||
* @child argument.
|
||||
*
|
||||
* If the iterator cannot advance, this function returns %FALSE, and
|
||||
* the contents of @child are undefined.
|
||||
*
|
||||
* Return value: %TRUE if the iterator could advance, and %FALSE otherwise.
|
||||
*
|
||||
* Since: 1.10
|
||||
*/
|
||||
gboolean
|
||||
clutter_actor_iter_next (ClutterActorIter *iter,
|
||||
ClutterActor **child)
|
||||
{
|
||||
RealActorIter *ri = (RealActorIter *) iter;
|
||||
|
||||
g_return_val_if_fail (iter != NULL, FALSE);
|
||||
g_return_val_if_fail (ri->root != NULL, FALSE);
|
||||
#ifndef G_DISABLE_ASSERT
|
||||
g_return_val_if_fail (ri->age == ri->root->priv->age, FALSE);
|
||||
#endif
|
||||
|
||||
if (ri->current == NULL)
|
||||
ri->current = ri->root->priv->first_child;
|
||||
else
|
||||
ri->current = ri->current->priv->next_sibling;
|
||||
|
||||
if (child != NULL)
|
||||
*child = ri->current;
|
||||
|
||||
return ri->current != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_actor_iter_next:
|
||||
* @iter: a #ClutterActorIter
|
||||
* @child: (out): return location for a #ClutterActor
|
||||
*
|
||||
* Advances the @iter and retrieves the previous child of the root
|
||||
* #ClutterActor that was used to initialize the #ClutterActorIterator.
|
||||
*
|
||||
* If the iterator can advance, this function returns %TRUE and sets the
|
||||
* @child argument.
|
||||
*
|
||||
* If the iterator cannot advance, this function returns %FALSE, and
|
||||
* the contents of @child are undefined.
|
||||
*
|
||||
* Return value: %TRUE if the iterator could advance, and %FALSE otherwise.
|
||||
*
|
||||
* Since: 1.10
|
||||
*/
|
||||
gboolean
|
||||
clutter_actor_iter_prev (ClutterActorIter *iter,
|
||||
ClutterActor **child)
|
||||
{
|
||||
RealActorIter *ri = (RealActorIter *) iter;
|
||||
|
||||
g_return_val_if_fail (iter != NULL, FALSE);
|
||||
g_return_val_if_fail (ri->root != NULL, FALSE);
|
||||
#ifndef G_DISABLE_ASSERT
|
||||
g_return_val_if_fail (ri->age == ri->root->priv->age, FALSE);
|
||||
#endif
|
||||
|
||||
if (ri->current == NULL)
|
||||
ri->current = ri->root->priv->last_child;
|
||||
else
|
||||
ri->current = ri->current->priv->prev_sibling;
|
||||
|
||||
if (child != NULL)
|
||||
*child = ri->current;
|
||||
|
||||
return ri->current != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_actor_iter_remove:
|
||||
* @iter: a #ClutterActorIter
|
||||
*
|
||||
* Safely removes the #ClutterActor currently pointer to by the iterator
|
||||
* from its parent.
|
||||
*
|
||||
* This function can only be called after clutter_actor_iter_next() or
|
||||
* clutter_actor_iter_prev() returned %TRUE, and cannot be called more
|
||||
* than once for the same actor.
|
||||
*
|
||||
* This function will call clutter_actor_remove_child() internally.
|
||||
*
|
||||
* Since: 1.10
|
||||
*/
|
||||
void
|
||||
clutter_actor_iter_remove (ClutterActorIter *iter)
|
||||
{
|
||||
RealActorIter *ri = (RealActorIter *) iter;
|
||||
ClutterActor *cur;
|
||||
|
||||
g_return_if_fail (iter != NULL);
|
||||
g_return_if_fail (ri->root != NULL);
|
||||
#ifndef G_DISABLE_ASSERT
|
||||
g_return_if_fail (ri->age == ri->root->priv->age);
|
||||
#endif
|
||||
g_return_if_fail (ri->current != NULL);
|
||||
|
||||
cur = ri->current;
|
||||
|
||||
if (cur != NULL)
|
||||
{
|
||||
ri->current = cur->priv->prev_sibling;
|
||||
|
||||
clutter_actor_remove_child_internal (ri->root, cur,
|
||||
REMOVE_CHILD_DEFAULT_FLAGS);
|
||||
|
||||
ri->age += 1;
|
||||
}
|
||||
}
|
||||
|
@ -263,6 +263,27 @@ struct _ClutterActorClass
|
||||
gpointer _padding_dummy[28];
|
||||
};
|
||||
|
||||
/**
|
||||
* ClutterActorIter:
|
||||
*
|
||||
* An iterator structure that allows to efficiently iterate over a
|
||||
* section of the scene graph.
|
||||
*
|
||||
* The contents of the <structname>ClutterActorIter</structname> structure
|
||||
* are private and should only be accessed using the provided API.
|
||||
*
|
||||
* Since: 1.10
|
||||
*/
|
||||
struct _ClutterActorIter
|
||||
{
|
||||
/*< private >*/
|
||||
gpointer CLUTTER_PRIVATE_FIELD (dummy1);
|
||||
gpointer CLUTTER_PRIVATE_FIELD (dummy2);
|
||||
gpointer CLUTTER_PRIVATE_FIELD (dummy3);
|
||||
gint CLUTTER_PRIVATE_FIELD (dummy4);
|
||||
gpointer CLUTTER_PRIVATE_FIELD (dummy5);
|
||||
};
|
||||
|
||||
GType clutter_actor_get_type (void) G_GNUC_CONST;
|
||||
|
||||
ClutterActor * clutter_actor_new (void);
|
||||
@ -486,6 +507,14 @@ void clutter_actor_set_child_at_index (ClutterActor
|
||||
ClutterActor *child,
|
||||
gint index_);
|
||||
|
||||
void clutter_actor_iter_init (ClutterActorIter *iter,
|
||||
ClutterActor *root);
|
||||
gboolean clutter_actor_iter_next (ClutterActorIter *iter,
|
||||
ClutterActor **child);
|
||||
gboolean clutter_actor_iter_prev (ClutterActorIter *iter,
|
||||
ClutterActor **child);
|
||||
void clutter_actor_iter_remove (ClutterActorIter *iter);
|
||||
|
||||
/* Transformations */
|
||||
gboolean clutter_actor_is_rotated (ClutterActor *self);
|
||||
gboolean clutter_actor_is_scaled (ClutterActor *self);
|
||||
|
@ -48,6 +48,18 @@ G_BEGIN_DECLS
|
||||
#define CLUTTER_DEPRECATED_FOR(f) G_DEPRECATED_FOR(f)
|
||||
#endif
|
||||
|
||||
/* some structures are meant to be opaque and still be allocated on the stack;
|
||||
* in order to avoid people poking at their internals, we use this macro to
|
||||
* ensure that users don't accidentally access a struct private members.
|
||||
*
|
||||
* we use the CLUTTER_COMPILATION define to allow us easier access, though.
|
||||
*/
|
||||
#ifdef CLUTTER_COMPILATION
|
||||
#define CLUTTER_PRIVATE_FIELD(x) x
|
||||
#else
|
||||
#define CLUTTER_PRIVATE_FIELD(x) clutter_private_ ## x
|
||||
#endif
|
||||
|
||||
#define CLUTTER_TYPE_ACTOR_BOX (clutter_actor_box_get_type ())
|
||||
#define CLUTTER_TYPE_FOG (clutter_fog_get_type ())
|
||||
#define CLUTTER_TYPE_GEOMETRY (clutter_geometry_get_type ())
|
||||
@ -65,6 +77,7 @@ typedef struct _ClutterChildMeta ClutterChildMeta;
|
||||
typedef struct _ClutterLayoutMeta ClutterLayoutMeta;
|
||||
typedef struct _ClutterActorMeta ClutterActorMeta;
|
||||
typedef struct _ClutterLayoutManager ClutterLayoutManager;
|
||||
typedef struct _ClutterActorIter ClutterActorIter;
|
||||
|
||||
typedef struct _ClutterAlpha ClutterAlpha;
|
||||
typedef struct _ClutterAnimatable ClutterAnimatable; /* dummy */
|
||||
|
@ -157,6 +157,10 @@ clutter_actor_insert_child_below
|
||||
clutter_actor_is_in_clone_paint
|
||||
clutter_actor_is_rotated
|
||||
clutter_actor_is_scaled
|
||||
clutter_actor_iter_init
|
||||
clutter_actor_iter_next
|
||||
clutter_actor_iter_prev
|
||||
clutter_actor_iter_remove
|
||||
clutter_actor_lower
|
||||
clutter_actor_lower_bottom
|
||||
clutter_actor_map
|
||||
|
@ -403,6 +403,13 @@ clutter_actor_set_child_at_index
|
||||
clutter_actor_set_child_below_sibling
|
||||
clutter_actor_contains
|
||||
clutter_actor_get_stage
|
||||
ClutterActorIter
|
||||
clutter_actor_iter_init
|
||||
clutter_actor_iter_next
|
||||
clutter_actor_iter_prev
|
||||
clutter_actor_iter_remove
|
||||
|
||||
<SUBSECTION>
|
||||
clutter_actor_push_internal
|
||||
clutter_actor_pop_internal
|
||||
clutter_actor_set_parent
|
||||
|
@ -62,6 +62,7 @@ units_sources += \
|
||||
test-actor-graph.c \
|
||||
test-actor-destroy.c \
|
||||
test-actor-invariants.c \
|
||||
test-actor-iter.c \
|
||||
test-actor-layout.c \
|
||||
test-actor-size.c \
|
||||
test-anchors.c \
|
||||
|
129
tests/conform/test-actor-iter.c
Normal file
129
tests/conform/test-actor-iter.c
Normal file
@ -0,0 +1,129 @@
|
||||
#include <glib.h>
|
||||
#include <clutter/clutter.h>
|
||||
#include "test-conform-common.h"
|
||||
|
||||
void
|
||||
actor_iter_traverse_children (TestConformSimpleFixture *fixture G_GNUC_UNUSED,
|
||||
gconstpointer dummy G_GNUC_UNUSED)
|
||||
{
|
||||
ClutterActorIter iter;
|
||||
ClutterActor *actor;
|
||||
ClutterActor *child;
|
||||
int i, n_actors;
|
||||
|
||||
actor = clutter_actor_new ();
|
||||
clutter_actor_set_name (actor, "root");
|
||||
g_object_ref_sink (actor);
|
||||
|
||||
n_actors = g_random_int_range (10, 50);
|
||||
for (i = 0; i < n_actors; i++)
|
||||
{
|
||||
char *name;
|
||||
|
||||
name = g_strdup_printf ("actor%d", i);
|
||||
child = clutter_actor_new ();
|
||||
clutter_actor_set_name (child, name);
|
||||
|
||||
clutter_actor_add_child (actor, child);
|
||||
|
||||
g_free (name);
|
||||
}
|
||||
|
||||
g_assert_cmpint (clutter_actor_get_n_children (actor), ==, n_actors);
|
||||
|
||||
i = 0;
|
||||
clutter_actor_iter_init (&iter, actor);
|
||||
while (clutter_actor_iter_next (&iter, &child))
|
||||
{
|
||||
g_assert (CLUTTER_IS_ACTOR (child));
|
||||
g_assert (clutter_actor_get_parent (child) == actor);
|
||||
|
||||
if (g_test_verbose ())
|
||||
g_print ("actor %d = '%s'\n", i, clutter_actor_get_name (child));
|
||||
|
||||
if (i == 0)
|
||||
g_assert (child == clutter_actor_get_first_child (actor));
|
||||
|
||||
if (i == (n_actors - 1))
|
||||
g_assert (child == clutter_actor_get_last_child (actor));
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
g_assert_cmpint (i, ==, n_actors);
|
||||
|
||||
i = 0;
|
||||
clutter_actor_iter_init (&iter, actor);
|
||||
while (clutter_actor_iter_prev (&iter, &child))
|
||||
{
|
||||
g_assert (CLUTTER_IS_ACTOR (child));
|
||||
g_assert (clutter_actor_get_parent (child) == actor);
|
||||
|
||||
if (g_test_verbose ())
|
||||
g_print ("actor %d = '%s'\n", i, clutter_actor_get_name (child));
|
||||
|
||||
if (i == 0)
|
||||
g_assert (child == clutter_actor_get_last_child (actor));
|
||||
|
||||
if (i == (n_actors - 1))
|
||||
g_assert (child == clutter_actor_get_first_child (actor));
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
g_object_unref (actor);
|
||||
}
|
||||
|
||||
void
|
||||
actor_iter_traverse_remove (TestConformSimpleFixture *fixture G_GNUC_UNUSED,
|
||||
gconstpointer dummy G_GNUC_UNUSED)
|
||||
{
|
||||
ClutterActorIter iter;
|
||||
ClutterActor *actor;
|
||||
ClutterActor *child;
|
||||
int i, n_actors;
|
||||
|
||||
actor = clutter_actor_new ();
|
||||
clutter_actor_set_name (actor, "root");
|
||||
g_object_ref_sink (actor);
|
||||
|
||||
n_actors = g_random_int_range (10, 50);
|
||||
for (i = 0; i < n_actors; i++)
|
||||
{
|
||||
char *name;
|
||||
|
||||
name = g_strdup_printf ("actor%d", i);
|
||||
child = clutter_actor_new ();
|
||||
clutter_actor_set_name (child, name);
|
||||
|
||||
clutter_actor_add_child (actor, child);
|
||||
|
||||
g_free (name);
|
||||
}
|
||||
|
||||
g_assert_cmpint (clutter_actor_get_n_children (actor), ==, n_actors);
|
||||
|
||||
i = 0;
|
||||
clutter_actor_iter_init (&iter, actor);
|
||||
while (clutter_actor_iter_next (&iter, &child))
|
||||
{
|
||||
g_assert (CLUTTER_IS_ACTOR (child));
|
||||
g_assert (clutter_actor_get_parent (child) == actor);
|
||||
|
||||
if (g_test_verbose ())
|
||||
g_print ("actor %d = '%s'\n", i, clutter_actor_get_name (child));
|
||||
|
||||
if (i == 0)
|
||||
g_assert (child == clutter_actor_get_first_child (actor));
|
||||
|
||||
if (i == (n_actors - 1))
|
||||
g_assert (child == clutter_actor_get_last_child (actor));
|
||||
|
||||
clutter_actor_iter_remove (&iter);
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
g_assert_cmpint (i, ==, n_actors);
|
||||
g_assert_cmpint (0, ==, clutter_actor_get_n_children (actor));
|
||||
}
|
@ -145,20 +145,23 @@ main (int argc, char **argv)
|
||||
TEST_CONFORM_SIMPLE ("/actor", test_offscreen_redirect);
|
||||
TEST_CONFORM_SIMPLE ("/actor", test_shader_effect);
|
||||
|
||||
TEST_CONFORM_SIMPLE ("/invariants", test_initial_state);
|
||||
TEST_CONFORM_SIMPLE ("/invariants", test_shown_not_parented);
|
||||
TEST_CONFORM_SIMPLE ("/invariants", test_realized);
|
||||
TEST_CONFORM_SIMPLE ("/invariants", test_realize_not_recursive);
|
||||
TEST_CONFORM_SIMPLE ("/invariants", test_map_recursive);
|
||||
TEST_CONFORM_SIMPLE ("/invariants", test_mapped);
|
||||
TEST_CONFORM_SIMPLE ("/invariants", test_show_on_set_parent);
|
||||
TEST_CONFORM_SIMPLE ("/invariants", test_clone_no_map);
|
||||
TEST_CONFORM_SIMPLE ("/invariants", test_contains);
|
||||
TEST_CONFORM_SIMPLE ("/invariants", default_stage);
|
||||
TEST_CONFORM_SIMPLE ("/actor/iter", actor_iter_traverse_children);
|
||||
TEST_CONFORM_SIMPLE ("/actor/iter", actor_iter_traverse_remove);
|
||||
|
||||
TEST_CONFORM_SIMPLE ("/opacity", test_label_opacity);
|
||||
TEST_CONFORM_SIMPLE ("/opacity", test_rectangle_opacity);
|
||||
TEST_CONFORM_SIMPLE ("/opacity", test_paint_opacity);
|
||||
TEST_CONFORM_SIMPLE ("/actor/invariants", test_initial_state);
|
||||
TEST_CONFORM_SIMPLE ("/actor/invariants", test_shown_not_parented);
|
||||
TEST_CONFORM_SIMPLE ("/actor/invariants", test_realized);
|
||||
TEST_CONFORM_SIMPLE ("/actor/invariants", test_realize_not_recursive);
|
||||
TEST_CONFORM_SIMPLE ("/actor/invariants", test_map_recursive);
|
||||
TEST_CONFORM_SIMPLE ("/actor/invariants", test_mapped);
|
||||
TEST_CONFORM_SIMPLE ("/actor/invariants", test_show_on_set_parent);
|
||||
TEST_CONFORM_SIMPLE ("/actor/invariants", test_clone_no_map);
|
||||
TEST_CONFORM_SIMPLE ("/actor/invariants", test_contains);
|
||||
TEST_CONFORM_SIMPLE ("/actor/invariants", default_stage);
|
||||
|
||||
TEST_CONFORM_SIMPLE ("/actor/opacity", test_label_opacity);
|
||||
TEST_CONFORM_SIMPLE ("/actor/opacity", test_rectangle_opacity);
|
||||
TEST_CONFORM_SIMPLE ("/actor/opacity", test_paint_opacity);
|
||||
|
||||
TEST_CONFORM_SIMPLE ("/text", text_utf8_validation);
|
||||
TEST_CONFORM_SIMPLE ("/text", text_set_empty);
|
||||
|
Loading…
Reference in New Issue
Block a user