diff --git a/clutter/clutter/clutter-actor-private.h b/clutter/clutter/clutter-actor-private.h index 3b6a4153b..ed398bf21 100644 --- a/clutter/clutter/clutter-actor-private.h +++ b/clutter/clutter/clutter-actor-private.h @@ -321,6 +321,8 @@ gboolean _clutter_actor_get_real_resource_scale ClutterPaintNode * clutter_actor_create_texture_paint_node (ClutterActor *self, CoglTexture *texture); +void clutter_actor_update_stage_views (ClutterActor *self); + G_END_DECLS #endif /* __CLUTTER_ACTOR_PRIVATE_H__ */ diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c index 5ea4cc1c9..1d3f98d41 100644 --- a/clutter/clutter/clutter-actor.c +++ b/clutter/clutter/clutter-actor.c @@ -813,6 +813,8 @@ struct _ClutterActorPrivate gulong font_changed_id; gulong layout_changed_id; + GList *stage_views; + /* bitfields: KEEP AT THE END */ /* fixed position and sizes */ @@ -855,6 +857,7 @@ struct _ClutterActorPrivate guint had_effects_on_last_paint_volume_update : 1; guint needs_compute_resource_scale : 1; guint absolute_origin_changed : 1; + guint needs_update_stage_views : 1; }; enum @@ -2565,6 +2568,7 @@ static void absolute_allocation_changed (ClutterActor *actor) { actor->priv->needs_compute_resource_scale = TRUE; + actor->priv->needs_update_stage_views = TRUE; } static ClutterActorTraverseVisitFlags @@ -4282,6 +4286,7 @@ typedef enum REMOVE_CHILD_FLUSH_QUEUE = 1 << 4, REMOVE_CHILD_NOTIFY_FIRST_LAST = 1 << 5, REMOVE_CHILD_STOP_TRANSITIONS = 1 << 6, + REMOVE_CHILD_CLEAR_STAGE_VIEWS = 1 << 7, /* default flags for public API */ REMOVE_CHILD_DEFAULT_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS | @@ -4290,14 +4295,16 @@ typedef enum REMOVE_CHILD_EMIT_ACTOR_REMOVED | REMOVE_CHILD_CHECK_STATE | REMOVE_CHILD_FLUSH_QUEUE | - REMOVE_CHILD_NOTIFY_FIRST_LAST, + REMOVE_CHILD_NOTIFY_FIRST_LAST | + REMOVE_CHILD_CLEAR_STAGE_VIEWS, /* flags for legacy/deprecated API */ REMOVE_CHILD_LEGACY_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS | REMOVE_CHILD_CHECK_STATE | REMOVE_CHILD_FLUSH_QUEUE | REMOVE_CHILD_EMIT_PARENT_SET | - REMOVE_CHILD_NOTIFY_FIRST_LAST + REMOVE_CHILD_NOTIFY_FIRST_LAST | + REMOVE_CHILD_CLEAR_STAGE_VIEWS } ClutterActorRemoveChildFlags; /*< private > @@ -4319,6 +4326,7 @@ clutter_actor_remove_child_internal (ClutterActor *self, gboolean notify_first_last; gboolean was_mapped; gboolean stop_transitions; + gboolean clear_stage_views; GObject *obj; if (self == child) @@ -4335,6 +4343,7 @@ clutter_actor_remove_child_internal (ClutterActor *self, flush_queue = (flags & REMOVE_CHILD_FLUSH_QUEUE) != 0; notify_first_last = (flags & REMOVE_CHILD_NOTIFY_FIRST_LAST) != 0; stop_transitions = (flags & REMOVE_CHILD_STOP_TRANSITIONS) != 0; + clear_stage_views = (flags & REMOVE_CHILD_CLEAR_STAGE_VIEWS) != 0; obj = G_OBJECT (self); g_object_freeze_notify (obj); @@ -4408,6 +4417,13 @@ clutter_actor_remove_child_internal (ClutterActor *self, clutter_actor_queue_compute_expand (self); } + /* Only actors which are attached to a stage get notified about changes + * to the stage views, so make sure all the stage-views lists are + * cleared as the child and its children leave the actor tree. + */ + if (clear_stage_views) + clutter_actor_clear_stage_views_recursive (child); + if (emit_parent_set && !CLUTTER_ACTOR_IN_DESTRUCTION (child)) { child->priv->needs_compute_resource_scale = TRUE; @@ -6080,6 +6096,8 @@ clutter_actor_dispose (GObject *object) priv->clones = NULL; } + g_clear_pointer (&priv->stage_views, g_list_free); + G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object); } @@ -8634,6 +8652,7 @@ clutter_actor_init (ClutterActor *self) priv->needs_allocation = TRUE; priv->needs_paint_volume_update = TRUE; priv->needs_compute_resource_scale = TRUE; + priv->needs_update_stage_views = TRUE; priv->cached_width_age = 1; priv->cached_height_age = 1; @@ -17521,7 +17540,11 @@ clear_stage_views_cb (ClutterActor *actor, int depth, gpointer user_data) { + actor->priv->needs_update_stage_views = TRUE; actor->priv->needs_compute_resource_scale = TRUE; + + g_clear_pointer (&actor->priv->stage_views, g_list_free); + return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE; } @@ -17622,6 +17645,93 @@ clutter_actor_get_resource_scale (ClutterActor *self, return FALSE; } +static void +update_stage_views (ClutterActor *self) +{ + ClutterActorPrivate *priv = self->priv; + ClutterStage *stage; + graphene_rect_t bounding_rect; + + g_clear_pointer (&priv->stage_views, g_list_free); + + if (priv->needs_allocation) + { + g_warning ("Can't update stage views actor %s is on because it needs an " + "allocation.", _clutter_actor_get_debug_name (self)); + return; + } + + stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); + g_return_if_fail (stage); + + clutter_actor_get_transformed_position (self, + &bounding_rect.origin.x, + &bounding_rect.origin.y); + clutter_actor_get_transformed_size (self, + &bounding_rect.size.width, + &bounding_rect.size.height); + + if (bounding_rect.size.width == 0.0 || + bounding_rect.size.height == 0.0) + return; + + priv->stage_views = clutter_stage_get_views_for_rect (stage, + &bounding_rect); +} + +void +clutter_actor_update_stage_views (ClutterActor *self) +{ + ClutterActorPrivate *priv = self->priv; + ClutterActor *child; + + if (!CLUTTER_ACTOR_IS_MAPPED (self) || + CLUTTER_ACTOR_IN_DESTRUCTION (self)) + return; + + if (priv->needs_update_stage_views) + { + update_stage_views (self); + + priv->needs_update_stage_views = FALSE; + } + + for (child = priv->first_child; child; child = child->priv->next_sibling) + clutter_actor_update_stage_views (child); +} + +/** + * clutter_actor_peek_stage_views: + * @self: A #ClutterActor + * + * Retrieves the list of #ClutterStageViews the actor is being + * painted on. + * + * If this function is called during the paint cycle, the list is guaranteed + * to be up-to-date, if called outside the paint cycle, the list will + * contain the views the actor was painted on last. + * + * The list returned by this function is not updated when the actors + * visibility changes: If an actor gets hidden and is not being painted + * anymore, this function will return the list of views the actor was + * painted on last. + * + * If an actor is not attached to a stage (realized), this function will + * always return an empty list. + * + * Returns: (transfer none) (element-type Clutter.StageView): The list of + * #ClutterStageViews the actor is being painted on. The list and + * its contents are owned by the #ClutterActor and the list may not be + * freed or modified. + */ +GList * +clutter_actor_peek_stage_views (ClutterActor *self) +{ + g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); + + return self->priv->stage_views; +} + /** * clutter_actor_has_overlaps: * @self: A #ClutterActor diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h index b5d4b7845..616e80169 100644 --- a/clutter/clutter/clutter-actor.h +++ b/clutter/clutter/clutter-actor.h @@ -919,6 +919,9 @@ void clutter_actor_pick_box (ClutterActor *self, ClutterPickContext *pick_context, const ClutterActorBox *box); +CLUTTER_EXPORT +GList * clutter_actor_peek_stage_views (ClutterActor *self); + G_END_DECLS #endif /* __CLUTTER_ACTOR_H__ */ diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index 5326b1cc2..ff079c1ad 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -1469,6 +1469,14 @@ _clutter_stage_check_updated_pointers (ClutterStage *stage) return updating; } +static void +update_actor_stage_views (ClutterStage *stage) +{ + ClutterActor *actor = CLUTTER_ACTOR (stage); + + clutter_actor_update_stage_views (actor); +} + /** * _clutter_stage_do_update: * @stage: A #ClutterStage @@ -1516,6 +1524,10 @@ _clutter_stage_do_update (ClutterStage *stage) if (stage_was_relayout) pointers = _clutter_stage_check_updated_pointers (stage); + COGL_TRACE_BEGIN (ClutterStageUpdateActorStageViews, "Actor stage-views"); + update_actor_stage_views (stage); + COGL_TRACE_END (ClutterStageUpdateActorStageViews); + COGL_TRACE_BEGIN (ClutterStagePaint, "Paint"); clutter_stage_maybe_finish_queue_redraws (stage);