[clutter/master-next: 3/3] Allow mixing old and new API without falling apart
- From: Emmanuele Bassi <ebassi src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [clutter/master-next: 3/3] Allow mixing old and new API without falling apart
- Date: Mon, 23 Jan 2012 12:45:26 +0000 (UTC)
commit 0d5646350bdb466cf4e4bbfd1171213ebf22fb02
Author: Emmanuele Bassi <ebassi linux intel com>
Date: Sat Jan 21 23:06:49 2012 +0000
Allow mixing old and new API without falling apart
Now that we reinstated Group to its "former glory", we need to ensure
that applications using the deprecated containers with the new DOM API
in ClutterActor can actually work - or, at least, not break horribly.
This actually means making sure that ClutterStage and ClutterGroup can
cope with the DOM, while retaining their old implementations, as well as
their bizarre idiosyncrasies and their utter, utter brokenness.
clutter/clutter-actor-private.h | 2 +
clutter/clutter-actor.c | 531 ++++++++++++++++++++++++------------
clutter/clutter-enums.h | 49 +++-
clutter/clutter-stage.c | 14 +-
clutter/deprecated/clutter-group.c | 77 ++++--
tests/interactive/test-depth.c | 3 +
6 files changed, 464 insertions(+), 212 deletions(-)
---
diff --git a/clutter/clutter-actor-private.h b/clutter/clutter-actor-private.h
index 968dbef..10444ce 100644
--- a/clutter/clutter-actor-private.h
+++ b/clutter/clutter-actor-private.h
@@ -215,6 +215,8 @@ void _clutter_actor_traverse (ClutterActor *actor,
ClutterTraverseCallback before_children_callback,
ClutterTraverseCallback after_children_callback,
gpointer user_data);
+void _clutter_actor_sort_children (ClutterActor *actor,
+ GCompareFunc func);
ClutterActor *_clutter_actor_get_stage_internal (ClutterActor *actor);
void _clutter_actor_apply_modelview_transform (ClutterActor *self,
diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c
index 8df0b17..a3c0e94 100644
--- a/clutter/clutter-actor.c
+++ b/clutter/clutter-actor.c
@@ -1968,36 +1968,92 @@ clutter_actor_set_allocation_internal (ClutterActor *self,
return retval;
}
+static void clutter_actor_real_allocate (ClutterActor *self,
+ const ClutterActorBox *box,
+ ClutterAllocationFlags flags);
+
static inline void
-clutter_actor_maybe_layout_children (ClutterActor *self)
+clutter_actor_maybe_layout_children (ClutterActor *self,
+ const ClutterActorBox *allocation,
+ ClutterAllocationFlags flags)
{
ClutterActorPrivate *priv = self->priv;
+ /* this is going to be a bit hard to follow, so let's put an explanation
+ * here.
+ *
+ * we want ClutterActor to have a default layout manager if the actor was
+ * created using "g_object_new (CLUTTER_TYPE_ACTOR, NULL)".
+ *
+ * we also want any subclass of ClutterActor that does not override the
+ * ::allocate() virtual function to delegate to a layout manager.
+ *
+ * finally, we want to allow people subclassing ClutterActor and overriding
+ * the ::allocate() vfunc to let Clutter delegate to the layout manager.
+ *
+ * on the other hand, we want existing actor subclasses overriding the
+ * ::allocate() virtual function and chaining up to the parent's
+ * implementation to continue working without allocating their children
+ * twice, or without entering an allocation loop.
+ *
+ * for the first two points, we check if the class of the actor is
+ * overridding the ::allocate() virtual function; if it isn't, then we
+ * follow through with checking whether we have children and a layout
+ * manager, and eventually calling clutter_layout_manager_allocate().
+ *
+ * for the third point, we check the CLUTTER_DELEGATE_LAYOUT flag in the
+ * allocation flags that we got passed, and if it is present, we continue
+ * with the check above.
+ *
+ * if neither of these two checks yields a positive result, we just
+ * assume that the ::allocate() virtual function that resulted in this
+ * function being called will also allocate the children of the actor.
+ */
+
+ if (CLUTTER_ACTOR_GET_CLASS (self)->allocate == clutter_actor_real_allocate)
+ goto check_layout;
+
+ if ((flags & CLUTTER_DELEGATE_LAYOUT) != 0)
+ goto check_layout;
+
+ return;
+
+check_layout:
if (priv->n_children != 0 &&
priv->layout_manager != NULL)
{
ClutterContainer *container = CLUTTER_CONTAINER (self);
- gfloat width, height;
+ ClutterAllocationFlags children_flags;
ClutterActorBox children_box;
- clutter_actor_box_get_size (&priv->allocation, &width, &height);
+ /* normalize the box passed to the layout manager */
+ children_box.x1 = children_box.y1 = 0.f;
+ children_box.x2 = (allocation->x2 - allocation->x1);
+ children_box.y2 = (allocation->y2 - allocation->y1);
- clutter_actor_box_set_origin (&children_box, 0, 0);
- clutter_actor_box_set_size (&children_box, width, height);
+ /* remove the DELEGATE_LAYOUT flag; this won't be passed to
+ * the actor's children, since it refers only to the current
+ * actor's allocation.
+ */
+ children_flags = flags;
+ children_flags &= ~CLUTTER_DELEGATE_LAYOUT;
CLUTTER_NOTE (LAYOUT,
- "Allocating children of %s at { %.2f, %.2f - %.2f x %.2f } using %s",
+ "Allocating %d children of %s "
+ "at { %.2f, %.2f - %.2f x %.2f } "
+ "using %s",
+ priv->n_children,
_clutter_actor_get_debug_name (self),
- priv->allocation.x1,
- priv->allocation.y1,
- width,
- height,
+ allocation->x1,
+ allocation->y1,
+ (allocation->x2 - allocation->x1),
+ (allocation->y2 - allocation->y1),
G_OBJECT_TYPE_NAME (priv->layout_manager));
clutter_layout_manager_allocate (priv->layout_manager,
container,
&children_box,
- priv->allocation_flags);
+ children_flags);
}
}
@@ -2018,7 +2074,7 @@ clutter_actor_real_allocate (ClutterActor *self,
* data out of the sub-tree of the scene graph that has this actor at
* the root.
*/
- clutter_actor_maybe_layout_children (self);
+ clutter_actor_maybe_layout_children (self, box, flags);
if (changed)
g_signal_emit (self, actor_signals[ALLOCATION_CHANGED], 0,
@@ -3299,6 +3355,172 @@ clutter_actor_continue_paint (ClutterActor *self)
}
}
+static ClutterActorTraverseVisitFlags
+invalidate_queue_redraw_entry (ClutterActor *self,
+ int depth,
+ gpointer user_data)
+{
+ ClutterActorPrivate *priv = self->priv;
+
+ if (priv->queue_redraw_entry != NULL)
+ {
+ _clutter_stage_queue_redraw_entry_invalidate (priv->queue_redraw_entry);
+ priv->queue_redraw_entry = NULL;
+ }
+
+ return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
+}
+
+static inline void
+remove_child (ClutterActor *self,
+ ClutterActor *child)
+{
+ ClutterActor *prev_sibling, *next_sibling;
+
+ prev_sibling = child->priv->prev_sibling;
+ next_sibling = child->priv->next_sibling;
+
+ if (prev_sibling != NULL)
+ prev_sibling->priv->next_sibling = next_sibling;
+
+ if (next_sibling != NULL)
+ next_sibling->priv->prev_sibling = prev_sibling;
+
+ if (self->priv->first_child == child)
+ self->priv->first_child = next_sibling;
+
+ if (self->priv->last_child == child)
+ self->priv->last_child = prev_sibling;
+
+ child->priv->prev_sibling = NULL;
+ child->priv->next_sibling = NULL;
+}
+
+typedef enum {
+ REMOVE_CHILD_DESTROY_META = 1 << 0,
+ REMOVE_CHILD_EMIT_PARENT_SET = 1 << 1,
+ REMOVE_CHILD_EMIT_ACTOR_REMOVED = 1 << 2,
+ REMOVE_CHILD_CHECK_STATE = 1 << 3,
+ REMOVE_CHILD_FLUSH_QUEUE = 1 << 4,
+ REMOVE_CHILD_NOTIFY_FIRST_LAST = 1 << 5,
+
+ /* default flags for public API */
+ REMOVE_CHILD_DEFAULT_FLAGS = REMOVE_CHILD_DESTROY_META |
+ REMOVE_CHILD_EMIT_PARENT_SET |
+ REMOVE_CHILD_EMIT_ACTOR_REMOVED |
+ REMOVE_CHILD_CHECK_STATE |
+ REMOVE_CHILD_FLUSH_QUEUE |
+ REMOVE_CHILD_NOTIFY_FIRST_LAST,
+
+ /* flags for legacy/deprecated API */
+ REMOVE_CHILD_LEGACY_FLAGS = REMOVE_CHILD_CHECK_STATE |
+ REMOVE_CHILD_FLUSH_QUEUE |
+ REMOVE_CHILD_EMIT_PARENT_SET |
+ REMOVE_CHILD_NOTIFY_FIRST_LAST
+} ClutterActorRemoveChildFlags;
+
+/*< private >
+ * clutter_actor_remove_child_internal:
+ * @self: a #ClutterActor
+ * @child: the child of @self that has to be removed
+ * @flags: control the removal operations
+ *
+ * Removes @child from the list of children of @self.
+ */
+static void
+clutter_actor_remove_child_internal (ClutterActor *self,
+ ClutterActor *child,
+ ClutterActorRemoveChildFlags flags)
+{
+ ClutterActor *old_first, *old_last;
+ gboolean destroy_meta, emit_parent_set, emit_actor_removed, check_state;
+ gboolean flush_queue;
+ gboolean notify_first_last;
+ gboolean was_mapped;
+
+ destroy_meta = (flags & REMOVE_CHILD_DESTROY_META) != 0;
+ emit_parent_set = (flags & REMOVE_CHILD_EMIT_PARENT_SET) != 0;
+ emit_actor_removed = (flags & REMOVE_CHILD_EMIT_ACTOR_REMOVED) != 0;
+ check_state = (flags & REMOVE_CHILD_CHECK_STATE) != 0;
+ flush_queue = (flags & REMOVE_CHILD_FLUSH_QUEUE) != 0;
+ notify_first_last = (flags & REMOVE_CHILD_NOTIFY_FIRST_LAST) != 0;
+
+ if (destroy_meta)
+ clutter_container_destroy_child_meta (CLUTTER_CONTAINER (self), child);
+
+ if (check_state)
+ {
+ 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
+ * stage, which means they need to be able to
+ * clutter_actor_get_stage(). This should unmap and unrealize,
+ * unless we're reparenting.
+ */
+ clutter_actor_update_map_state (child, MAP_STATE_MAKE_UNREALIZED);
+ }
+ else
+ was_mapped = FALSE;
+
+ if (flush_queue)
+ {
+ /* We take this opportunity to invalidate any queue redraw entry
+ * associated with the actor and descendants since we won't be able to
+ * determine the appropriate stage after this.
+ *
+ * we do this after we updated the mapped state because actors might
+ * end up queueing redraws inside their mapped/unmapped virtual
+ * functions, and if we invalidate the redraw entry we could end up
+ * with an inconsistent state and weird memory corruption. see
+ * bugs:
+ *
+ * http://bugzilla.clutter-project.org/show_bug.cgi?id=2621
+ * https://bugzilla.gnome.org/show_bug.cgi?id=652036
+ */
+ _clutter_actor_traverse (child,
+ 0,
+ invalidate_queue_redraw_entry,
+ NULL,
+ NULL);
+ }
+
+ old_first = self->priv->first_child;
+ old_last = self->priv->last_child;
+
+ child->priv->parent = NULL;
+
+ /* 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);
+
+ remove_child (self, child);
+
+ self->priv->n_children -= 1;
+
+ /* if the child was mapped then we need to relayout ourselves to account
+ * for the removed child
+ */
+ if (was_mapped)
+ clutter_actor_queue_relayout (self);
+
+ /* we need to emit the signal before dropping the reference */
+ if (emit_actor_removed)
+ g_signal_emit_by_name (self, "actor-removed", child);
+
+ if (notify_first_last)
+ {
+ if (old_first != self->priv->first_child)
+ g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FIRST_CHILD]);
+
+ if (old_last != self->priv->last_child)
+ g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LAST_CHILD]);
+ }
+
+ /* remove the reference we acquired in clutter_actor_add_child() */
+ g_object_unref (child);
+}
+
static const ClutterTransformInfo default_transform_info = {
0.0, { 0, }, /* rotation-x */
0.0, { 0, }, /* rotation-y */
@@ -4306,7 +4528,8 @@ clutter_actor_dispose (GObject *object)
if (!CLUTTER_ACTOR_IS_INTERNAL_CHILD (self))
clutter_container_remove_actor (CLUTTER_CONTAINER (parent), self);
else
- clutter_actor_remove_child (parent, self);
+ clutter_actor_remove_child_internal (parent, self,
+ REMOVE_CHILD_LEGACY_FLAGS);
}
/* parent should be gone */
@@ -4501,6 +4724,32 @@ clutter_actor_real_destroy (ClutterActor *self)
}
}
+static GObject *
+clutter_actor_constructor (GType gtype,
+ guint n_props,
+ GObjectConstructParam *props)
+{
+ GObjectClass *gobject_class;
+ ClutterActor *self;
+ GObject *retval;
+
+ gobject_class = G_OBJECT_CLASS (clutter_actor_parent_class);
+ retval = gobject_class->constructor (gtype, n_props, props);
+ self = CLUTTER_ACTOR (retval);
+
+ if (self->priv->layout_manager == NULL)
+ {
+ ClutterLayoutManager *default_layout;
+
+ CLUTTER_NOTE (LAYOUT, "Creating default layout manager");
+
+ default_layout = clutter_fixed_layout_new ();
+ clutter_actor_set_layout_manager (self, default_layout);
+ }
+
+ return retval;
+}
+
static void
clutter_actor_class_init (ClutterActorClass *klass)
{
@@ -4510,6 +4759,7 @@ clutter_actor_class_init (ClutterActorClass *klass)
quark_actor_layout_info = g_quark_from_static_string ("-clutter-actor-layout-info");
quark_actor_transform_info = g_quark_from_static_string ("-clutter-actor-transform-info");
+ object_class->constructor = clutter_actor_constructor;
object_class->set_property = clutter_actor_set_property;
object_class->get_property = clutter_actor_get_property;
object_class->dispose = clutter_actor_dispose;
@@ -7549,7 +7799,7 @@ clutter_actor_set_allocation (ClutterActor *self,
* data out of the sub-tree of the scene graph that has this actor at
* the root.
*/
- clutter_actor_maybe_layout_children (self);
+ clutter_actor_maybe_layout_children (self, box, flags);
if (changed)
g_signal_emit (self, actor_signals[ALLOCATION_CHANGED], 0,
@@ -9133,6 +9383,11 @@ clutter_actor_set_depth (ClutterActor *self,
priv->transform_valid = FALSE;
+ /* FIXME - remove this crap; sadly, there are still containers
+ * in Clutter that depend on this utter brain damage
+ */
+ clutter_container_sort_depth_order (CLUTTER_CONTAINER (self));
+
clutter_actor_queue_redraw (self);
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_DEPTH]);
@@ -9602,7 +9857,7 @@ insert_child_at_index (ClutterActor *self,
{
ClutterActor *tmp = iter->priv->prev_sibling;
- child->priv->prev_sibling = iter->priv->prev_sibling;
+ child->priv->prev_sibling = tmp;
child->priv->next_sibling = iter;
iter->priv->prev_sibling = child;
@@ -9693,20 +9948,23 @@ typedef void (* ClutterActorAddChildFunc) (ClutterActor *parent,
gpointer data);
typedef enum {
- ADD_CHILD_CREATE_META = 1 << 0,
- ADD_CHILD_EMIT_PARENT_SET = 1 << 1,
- ADD_CHILD_EMIT_ACTOR_ADDED = 1 << 2,
- ADD_CHILD_CHECK_STATE = 1 << 3,
+ ADD_CHILD_CREATE_META = 1 << 0,
+ ADD_CHILD_EMIT_PARENT_SET = 1 << 1,
+ ADD_CHILD_EMIT_ACTOR_ADDED = 1 << 2,
+ ADD_CHILD_CHECK_STATE = 1 << 3,
+ ADD_CHILD_NOTIFY_FIRST_LAST = 1 << 4,
/* default flags for public API */
ADD_CHILD_DEFAULT_FLAGS = ADD_CHILD_CREATE_META |
ADD_CHILD_EMIT_PARENT_SET |
ADD_CHILD_EMIT_ACTOR_ADDED |
- ADD_CHILD_CHECK_STATE,
+ ADD_CHILD_CHECK_STATE |
+ ADD_CHILD_NOTIFY_FIRST_LAST,
/* flags for legacy/deprecated API */
ADD_CHILD_LEGACY_FLAGS = ADD_CHILD_EMIT_PARENT_SET |
- ADD_CHILD_CHECK_STATE
+ ADD_CHILD_CHECK_STATE |
+ ADD_CHILD_NOTIFY_FIRST_LAST
} ClutterActorAddChildFlags;
/*< private >
@@ -9733,7 +9991,10 @@ clutter_actor_add_child_internal (ClutterActor *self,
gpointer data)
{
ClutterTextDirection text_dir;
- gboolean create_meta, emit_parent_set, emit_actor_added, check_state;
+ gboolean create_meta;
+ gboolean emit_parent_set, emit_actor_added;
+ gboolean check_state;
+ gboolean notify_first_last;
ClutterActor *old_first_child, *old_last_child;
if (child->priv->parent != NULL)
@@ -9759,6 +10020,7 @@ clutter_actor_add_child_internal (ClutterActor *self,
emit_parent_set = (flags & ADD_CHILD_EMIT_PARENT_SET) != 0;
emit_actor_added = (flags & ADD_CHILD_EMIT_ACTOR_ADDED) != 0;
check_state = (flags & ADD_CHILD_CHECK_STATE) != 0;
+ notify_first_last = (flags & ADD_CHILD_NOTIFY_FIRST_LAST) != 0;
old_first_child = self->priv->first_child;
old_last_child = self->priv->last_child;
@@ -9825,11 +10087,14 @@ clutter_actor_add_child_internal (ClutterActor *self,
if (emit_actor_added)
g_signal_emit_by_name (self, "actor-added", child);
- if (old_first_child != self->priv->first_child)
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FIRST_CHILD]);
+ if (notify_first_last)
+ {
+ if (old_first_child != self->priv->first_child)
+ g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FIRST_CHILD]);
- if (old_last_child != self->priv->last_child)
- g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LAST_CHILD]);
+ if (old_last_child != self->priv->last_child)
+ g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LAST_CHILD]);
+ }
}
/**
@@ -10058,154 +10323,6 @@ clutter_actor_get_paint_visibility (ClutterActor *actor)
return CLUTTER_ACTOR_IS_MAPPED (actor);
}
-static ClutterActorTraverseVisitFlags
-invalidate_queue_redraw_entry (ClutterActor *self,
- int depth,
- gpointer user_data)
-{
- ClutterActorPrivate *priv = self->priv;
-
- if (priv->queue_redraw_entry != NULL)
- {
- _clutter_stage_queue_redraw_entry_invalidate (priv->queue_redraw_entry);
- priv->queue_redraw_entry = NULL;
- }
-
- return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
-}
-
-static inline void
-remove_child (ClutterActor *self,
- ClutterActor *child)
-{
- ClutterActor *prev_sibling, *next_sibling;
-
- prev_sibling = child->priv->prev_sibling;
- next_sibling = child->priv->next_sibling;
-
- if (prev_sibling != NULL)
- prev_sibling->priv->next_sibling = next_sibling;
-
- if (next_sibling != NULL)
- next_sibling->priv->prev_sibling = prev_sibling;
-
- if (self->priv->first_child == child)
- self->priv->first_child = next_sibling;
-
- if (self->priv->last_child == child)
- self->priv->last_child = prev_sibling;
-
- child->priv->prev_sibling = NULL;
- child->priv->next_sibling = NULL;
-}
-
-typedef enum {
- REMOVE_CHILD_DESTROY_META = 1 << 0,
- REMOVE_CHILD_EMIT_PARENT_SET = 1 << 1,
- REMOVE_CHILD_EMIT_ACTOR_REMOVED = 1 << 2,
- REMOVE_CHILD_CHECK_STATE = 1 << 3,
- REMOVE_CHILD_FLUSH_QUEUE = 1 << 4,
-
- /* default flags for public API */
- REMOVE_CHILD_DEFAULT_FLAGS = REMOVE_CHILD_DESTROY_META |
- REMOVE_CHILD_EMIT_PARENT_SET |
- REMOVE_CHILD_EMIT_ACTOR_REMOVED |
- REMOVE_CHILD_CHECK_STATE |
- REMOVE_CHILD_FLUSH_QUEUE,
-
- /* flags for legacy/deprecated API */
- REMOVE_CHILD_LEGACY_FLAGS = REMOVE_CHILD_CHECK_STATE |
- REMOVE_CHILD_FLUSH_QUEUE |
- REMOVE_CHILD_EMIT_PARENT_SET
-} ClutterActorRemoveChildFlags;
-
-/*< private >
- * clutter_actor_remove_child_internal:
- * @self: a #ClutterActor
- * @child: the child of @self that has to be removed
- * @flags: control the removal operations
- *
- * Removes @child from the list of children of @self.
- */
-static void
-clutter_actor_remove_child_internal (ClutterActor *self,
- ClutterActor *child,
- ClutterActorRemoveChildFlags flags)
-{
- gboolean destroy_meta, emit_parent_set, emit_actor_removed, check_state;
- gboolean flush_queue;
- gboolean was_mapped;
-
- destroy_meta = (flags & REMOVE_CHILD_DESTROY_META) != 0;
- emit_parent_set = (flags & REMOVE_CHILD_EMIT_PARENT_SET) != 0;
- emit_actor_removed = (flags & REMOVE_CHILD_EMIT_ACTOR_REMOVED) != 0;
- check_state = (flags & REMOVE_CHILD_CHECK_STATE) != 0;
- flush_queue = (flags & REMOVE_CHILD_FLUSH_QUEUE) != 0;
-
- if (destroy_meta)
- clutter_container_destroy_child_meta (CLUTTER_CONTAINER (self), child);
-
- if (check_state)
- {
- 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
- * stage, which means they need to be able to
- * clutter_actor_get_stage(). This should unmap and unrealize,
- * unless we're reparenting.
- */
- clutter_actor_update_map_state (child, MAP_STATE_MAKE_UNREALIZED);
- }
- else
- was_mapped = FALSE;
-
- if (flush_queue)
- {
- /* We take this opportunity to invalidate any queue redraw entry
- * associated with the actor and descendants since we won't be able to
- * determine the appropriate stage after this.
- *
- * we do this after we updated the mapped state because actors might
- * end up queueing redraws inside their mapped/unmapped virtual
- * functions, and if we invalidate the redraw entry we could end up
- * with an inconsistent state and weird memory corruption. see
- * bugs:
- *
- * http://bugzilla.clutter-project.org/show_bug.cgi?id=2621
- * https://bugzilla.gnome.org/show_bug.cgi?id=652036
- */
- _clutter_actor_traverse (child,
- 0,
- invalidate_queue_redraw_entry,
- NULL,
- NULL);
- }
-
- child->priv->parent = NULL;
-
- /* 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);
-
- remove_child (self, child);
-
- self->priv->n_children -= 1;
-
- /* if the child was mapped then we need to relayout ourselves to account
- * for the removed child
- */
- if (was_mapped)
- clutter_actor_queue_relayout (self);
-
- /* we need to emit the signal before dropping the reference */
- if (emit_actor_removed)
- g_signal_emit_by_name (self, "actor-removed", child);
-
- /* remove the reference we acquired in clutter_actor_add_child() */
- g_object_unref (child);
-}
-
/**
* clutter_actor_remove_child:
* @self: a #ClutterActor
@@ -10539,7 +10656,8 @@ clutter_actor_set_child_above_sibling (ClutterActor *self,
*/
g_object_ref (child);
clutter_actor_remove_child_internal (self, child, 0);
- clutter_actor_add_child_internal (self, child, 0,
+ clutter_actor_add_child_internal (self, child,
+ ADD_CHILD_NOTIFY_FIRST_LAST,
insert_child_above,
sibling);
@@ -10579,7 +10697,8 @@ clutter_actor_set_child_below_sibling (ClutterActor *self,
/* see the comment in set_child_above_sibling() */
g_object_ref (child);
clutter_actor_remove_child_internal (self, child, 0);
- clutter_actor_add_child_internal (self, child, 0,
+ clutter_actor_add_child_internal (self, child,
+ ADD_CHILD_NOTIFY_FIRST_LAST,
insert_child_below,
sibling);
@@ -10612,7 +10731,8 @@ clutter_actor_set_child_at_index (ClutterActor *self,
g_object_ref (child);
clutter_actor_remove_child_internal (self, child, 0);
- clutter_actor_add_child_internal (self, child, 0,
+ clutter_actor_add_child_internal (self, child,
+ ADD_CHILD_NOTIFY_FIRST_LAST,
insert_child_at_index,
GINT_TO_POINTER (index_));
@@ -15648,3 +15768,60 @@ clutter_actor_get_last_child (ClutterActor *self)
return self->priv->last_child;
}
+
+/*< private >
+ * @self: a #ClutterActor
+ * @func: a comparison function
+ *
+ * Sorts the list of children of @self using the provided comparison
+ * function.
+ */
+void
+_clutter_actor_sort_children (ClutterActor *self,
+ GCompareFunc func)
+{
+ ClutterActor *old_first, *old_last;
+ ClutterActor *iter;
+ GList *tmp, *l;
+
+ old_first = self->priv->first_child;
+ old_last = self->priv->last_child;
+
+ /* build a list from the list of children, while removing them
+ * at the same time; removal is O(1), as well as prepending it
+ * to the temporary list
+ */
+ tmp = NULL;
+ iter = self->priv->first_child;
+ while (iter != NULL)
+ {
+ ClutterActor *next = iter->priv->next_sibling;
+
+ tmp = g_list_prepend (tmp, g_object_ref (iter));
+
+ clutter_actor_remove_child_internal (self, iter, 0);
+
+ iter = next;
+ }
+
+ tmp = g_list_sort (tmp, func);
+
+ for (l = tmp; l != NULL; l = l->next)
+ {
+ clutter_actor_add_child_internal (self, l->data, 0,
+ insert_child_above,
+ NULL);
+ g_object_unref (l->data);
+ }
+
+ g_list_free (tmp);
+
+ /* we don't notify :first-child and :last-child until the end, to avoid
+ * spurious signal emissions
+ */
+ if (old_first != self->priv->first_child)
+ g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FIRST_CHILD]);
+
+ if (old_last != self->priv->last_child)
+ g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LAST_CHILD]);
+}
diff --git a/clutter/clutter-enums.h b/clutter/clutter-enums.h
index cbd450e..a75f45c 100644
--- a/clutter/clutter-enums.h
+++ b/clutter/clutter-enums.h
@@ -400,16 +400,55 @@ typedef enum { /*< prefix=CLUTTER_OFFSCREEN_REDIRECT >*/
* @CLUTTER_ALLOCATION_NONE: No flag set
* @CLUTTER_ABSOLUTE_ORIGIN_CHANGED: Whether the absolute origin of the
* actor has changed; this implies that any ancestor of the actor has
- * been moved
- *
- * Flags passed to the #ClutterActor::allocate() virtual function and
- * to the clutter_actor_allocate() function
+ * been moved.
+ * @CLUTTER_DELEGATE_LAYOUT: Whether the allocation should be delegated
+ * to the #ClutterLayoutManager instance stored inside the
+ * #ClutterActor:layout-manager property of #ClutterActor. This flag
+ * should only be used if you are subclassing #ClutterActor and
+ * overriding the #ClutterActorClass.allocate() virtual function, but
+ * you wish to use the default implementation of the virtual function
+ * inside #ClutterActor, for instance:
+ * |[
+ * static void
+ * my_actor_allocate (ClutterActor *actor,
+ * const ClutterActorBox *alloc,
+ * ClutterAllocationFlags flags)
+ * {
+ * ClutterActorBox new_alloc;
+ * ClutterAllocationFlags new_flags;
+ *
+ * /* change the allocation */
+ * new_alloc = *alloc;
+ * new_alloc.x1 += 6;
+ * new_alloc.y1 += 3;
+ * new_alloc.x2 -= 6;
+ * new_alloc.y2 -= 3;
+ *
+ * /* change the flags */
+ * new_flags = flags | CLUTTER_DELEGATE_LAYOUT;
+ *
+ * /* store the allocation and delegate the children layout
+ * * to the ClutterLayoutManager used by the actor.
+ * */
+ * clutter_actor_set_allocation (actor, &new_alloc, flags);
+ *
+ * /* alternatively, instead of using this flags, you could
+ * * retrieve the layout manager and call the
+ * * clutter_layout_manager_allocate() yourself.
+ * */
+ * }
+ * ]|
+ * the %CLUTTER_DELEGATE_LAYOUT was added in Clutter 1.10.
+ *
+ * Flags passed to the #ClutterActorClass.allocate() virtual function
+ * and to the clutter_actor_allocate() function
*
* Since: 1.0
*/
typedef enum {
CLUTTER_ALLOCATION_NONE = 0,
- CLUTTER_ABSOLUTE_ORIGIN_CHANGED = 1 << 1
+ CLUTTER_ABSOLUTE_ORIGIN_CHANGED = 1 << 1,
+ CLUTTER_DELEGATE_LAYOUT = 1 << 2
} ClutterAllocationFlags;
/**
diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c
index df7535a..0b8d299 100644
--- a/clutter/clutter-stage.c
+++ b/clutter/clutter-stage.c
@@ -55,6 +55,7 @@
#include "clutter-stage.h"
#include "deprecated/clutter-stage.h"
+#include "deprecated/clutter-container.h"
#include "clutter-actor-private.h"
#include "clutter-backend-private.h"
@@ -604,6 +605,7 @@ clutter_stage_paint (ClutterActor *self)
CoglBufferBit clear_flags;
CoglColor stage_color;
guint8 real_alpha;
+
CLUTTER_STATIC_TIMER (stage_clear_timer,
"Painting actors", /* parent */
"Stage clear",
@@ -655,7 +657,6 @@ clutter_stage_paint (ClutterActor *self)
cogl_disable_fog ();
#endif
- /* this will take care of painting every child */
CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->paint (self);
}
@@ -663,18 +664,13 @@ static void
clutter_stage_pick (ClutterActor *self,
const ClutterColor *color)
{
- ClutterActor *child;
-
/* Note: we don't chain up to our parent as we don't want any geometry
* emitted for the stage itself. The stage's pick id is effectively handled
* by the call to cogl_clear done in clutter-main.c:_clutter_do_pick_async()
*/
- for (child = clutter_actor_get_first_child (self);
- child != NULL;
- child = clutter_actor_get_next_sibling (child))
- {
- clutter_actor_paint (child);
- }
+ clutter_container_foreach (CLUTTER_CONTAINER (self),
+ CLUTTER_CALLBACK (clutter_actor_paint),
+ NULL);
}
static gboolean
diff --git a/clutter/deprecated/clutter-group.c b/clutter/deprecated/clutter-group.c
index 121e47a..52e3d19 100644
--- a/clutter/deprecated/clutter-group.c
+++ b/clutter/deprecated/clutter-group.c
@@ -54,6 +54,7 @@
#include "clutter-group.h"
#include "clutter-actor.h"
+#include "clutter-actor-private.h"
#include "clutter-container.h"
#include "clutter-fixed-layout.h"
#include "clutter-main.h"
@@ -109,21 +110,35 @@ clutter_group_real_add (ClutterContainer *container,
{
ClutterGroupPrivate *priv = CLUTTER_GROUP (container)->priv;
- g_object_ref (actor);
-
priv->children = g_list_append (priv->children, actor);
clutter_actor_set_parent (actor, CLUTTER_ACTOR (container));
- /* queue a relayout, to get the correct positioning inside
- * the ::actor-added signal handlers
- */
clutter_actor_queue_relayout (CLUTTER_ACTOR (container));
g_signal_emit_by_name (container, "actor-added", actor);
clutter_container_sort_depth_order (container);
+}
- g_object_unref (actor);
+static void
+clutter_group_real_actor_added (ClutterContainer *container,
+ ClutterActor *actor)
+{
+ ClutterGroupPrivate *priv = CLUTTER_GROUP (container)->priv;
+
+ /* XXX - children added using clutter_actor_add_child() will
+ * cause actor-added to be emitted without going through the
+ * add() virtual function.
+ *
+ * if we get an actor-added for a child that is not in our
+ * list of children already, then we go in compatibility
+ * mode.
+ */
+ if (g_list_find (priv->children, actor) != NULL)
+ return;
+
+ priv->children = g_list_append (priv->children, actor);
+ clutter_container_sort_depth_order (container);
}
static void
@@ -137,23 +152,28 @@ clutter_group_real_remove (ClutterContainer *container,
priv->children = g_list_remove (priv->children, actor);
clutter_actor_unparent (actor);
- /* queue a relayout, to get the correct positioning inside
- * the ::actor-removed signal handlers
- */
clutter_actor_queue_relayout (CLUTTER_ACTOR (container));
- /* at this point, the actor passed to the "actor-removed" signal
- * handlers is not parented anymore to the container but since we
- * are holding a reference on it, it's still valid
- */
g_signal_emit_by_name (container, "actor-removed", actor);
- clutter_actor_queue_redraw (CLUTTER_ACTOR (container));
-
g_object_unref (actor);
}
static void
+clutter_group_real_actor_removed (ClutterContainer *container,
+ ClutterActor *actor)
+{
+ ClutterGroupPrivate *priv = CLUTTER_GROUP (container)->priv;
+
+ /* XXX - same compatibility mode of the ::actor-added implementation */
+ if (g_list_find (priv->children, actor) == NULL)
+ return;
+
+ priv->children = g_list_remove (priv->children, actor);
+ clutter_container_sort_depth_order (container);
+}
+
+static void
clutter_group_real_foreach (ClutterContainer *container,
ClutterCallback callback,
gpointer user_data)
@@ -199,7 +219,9 @@ clutter_group_real_raise (ClutterContainer *container,
* as values are equal ordering shouldn't change but Z
* values will be correct.
*
- * FIXME: optimise
+ * FIXME: get rid of this crap; this is so utterly broken and wrong on
+ * so many levels it's not even funny. sadly, we get to keep this until
+ * we can break API and remove Group for good.
*/
if (sibling &&
clutter_actor_get_depth (sibling) != clutter_actor_get_depth (actor))
@@ -254,8 +276,22 @@ clutter_group_real_sort_depth_order (ClutterContainer *container)
{
ClutterGroupPrivate *priv = CLUTTER_GROUP (container)->priv;
+ /* no point in sorting an empty list, or a list with just one item */
+ if (priv->children == NULL)
+ return;
+
+ if (priv->children->next == NULL)
+ return;
+
priv->children = g_list_sort (priv->children, sort_by_depth);
+ /* XXX - this is a hack, to ensure that the list of children that is stored
+ * inside ClutterActor itself is kept in sync with the list of children held
+ * by ClutterGroup. this is needed so we can use the old deprecated API and
+ * mix it with the Actor API.
+ */
+ _clutter_actor_sort_children (CLUTTER_ACTOR (container), sort_by_depth);
+
clutter_actor_queue_redraw (CLUTTER_ACTOR (container));
}
@@ -263,7 +299,9 @@ static void
clutter_container_iface_init (ClutterContainerIface *iface)
{
iface->add = clutter_group_real_add;
+ iface->actor_added = clutter_group_real_actor_added;
iface->remove = clutter_group_real_remove;
+ iface->actor_removed = clutter_group_real_actor_removed;
iface->foreach = clutter_group_real_foreach;
iface->raise = clutter_group_real_raise;
iface->lower = clutter_group_real_lower;
@@ -337,9 +375,6 @@ clutter_group_real_allocate (ClutterActor *actor,
klass = CLUTTER_ACTOR_CLASS (clutter_group_parent_class);
klass->allocate (actor, allocation, flags);
- if (priv->children == NULL)
- return;
-
clutter_layout_manager_allocate (priv->layout,
CLUTTER_CONTAINER (actor),
allocation, flags);
@@ -444,8 +479,8 @@ clutter_group_init (ClutterGroup *self)
self->priv->layout = clutter_fixed_layout_new ();
g_object_ref_sink (self->priv->layout);
- clutter_layout_manager_set_container (self->priv->layout,
- CLUTTER_CONTAINER (self));
+ clutter_actor_set_layout_manager (CLUTTER_ACTOR (self),
+ self->priv->layout);
}
/**
diff --git a/tests/interactive/test-depth.c b/tests/interactive/test-depth.c
index 94b367a..995889e 100644
--- a/tests/interactive/test-depth.c
+++ b/tests/interactive/test-depth.c
@@ -36,6 +36,7 @@ clone_box (ClutterActor *original)
clutter_actor_get_size (original, &width, &height);
group = clutter_actor_new ();
+ clutter_actor_set_layout_manager (group, clutter_fixed_layout_new ());
clone = clutter_clone_new (original);
clutter_container_add_actor (CLUTTER_CONTAINER (group), clone);
clutter_actor_set_depth (clone, width / 2);
@@ -81,6 +82,7 @@ janus_group (const gchar *front_text,
gfloat width2, height2;
group = clutter_actor_new ();
+ clutter_actor_set_layout_manager (group, clutter_fixed_layout_new ());
rectangle = clutter_actor_new ();
clutter_actor_set_background_color (rectangle, CLUTTER_COLOR_White);
front = clutter_text_new_with_text ("Sans 50px", front_text);
@@ -131,6 +133,7 @@ test_depth_main (int argc, char *argv[])
NULL);
group = clutter_actor_new ();
+ clutter_actor_set_layout_manager (group, clutter_fixed_layout_new ());
clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
label = clutter_text_new_with_text ("Mono 26", "Clutter");
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]