@@ -812,6 +812,8 @@ struct _ClutterActorPrivate
812812 gulong resolution_changed_id;
813813 gulong font_changed_id;
814814
815+ GList *stage_views;
816+
815817 /* bitfields: KEEP AT THE END */
816818
817819 /* fixed position and sizes */
@@ -854,6 +856,7 @@ struct _ClutterActorPrivate
854856 guint had_effects_on_last_paint_volume_update : 1;
855857 guint needs_compute_resource_scale : 1;
856858 guint absolute_origin_changed : 1;
859+ guint needs_update_stage_views : 1;
857860};
858861
859862enum
@@ -1016,6 +1019,7 @@ enum
10161019 TRANSITIONS_COMPLETED,
10171020 TOUCH_EVENT,
10181021 TRANSITION_STOPPED,
1022+ STAGE_VIEWS_CHANGED,
10191023
10201024 LAST_SIGNAL
10211025};
@@ -1636,6 +1640,22 @@ clutter_actor_update_map_state (ClutterActor *self,
16361640#endif
16371641}
16381642
1643+ static void
1644+ queue_update_stage_views (ClutterActor *actor)
1645+ {
1646+ while (actor && !actor->priv->needs_update_stage_views)
1647+ {
1648+ actor->priv->needs_update_stage_views = TRUE;
1649+
1650+ /* We don't really need to update the stage-views of the actors up the
1651+ * hierarchy, we set the flag anyway though so we can avoid traversing
1652+ * the whole scenegraph when looking for actors which need an update
1653+ * in clutter_actor_update_stage_views().
1654+ */
1655+ actor = actor->priv->parent;
1656+ }
1657+ }
1658+
16391659static void
16401660clutter_actor_real_map (ClutterActor *self)
16411661{
@@ -1650,6 +1670,18 @@ clutter_actor_real_map (ClutterActor *self)
16501670
16511671 self->priv->needs_paint_volume_update = TRUE;
16521672
1673+ /* We skip unmapped actors when updating the stage-views list, so if
1674+ * an actors list got invalidated while it was unmapped make sure to
1675+ * set priv->needs_update_stage_views to TRUE for all actors up the
1676+ * hierarchy now.
1677+ */
1678+ if (self->priv->needs_update_stage_views)
1679+ {
1680+ /* Avoid the early return in queue_update_stage_views() */
1681+ self->priv->needs_update_stage_views = FALSE;
1682+ queue_update_stage_views (self);
1683+ }
1684+
16531685 clutter_actor_ensure_resource_scale (self);
16541686
16551687 /* notify on parent mapped before potentially mapping
@@ -2587,6 +2619,7 @@ static void
25872619absolute_allocation_changed (ClutterActor *actor)
25882620{
25892621 actor->priv->needs_compute_resource_scale = TRUE;
2622+ queue_update_stage_views (actor);
25902623}
25912624
25922625static ClutterActorTraverseVisitFlags
@@ -4403,6 +4436,7 @@ typedef enum
44034436 REMOVE_CHILD_FLUSH_QUEUE = 1 << 4,
44044437 REMOVE_CHILD_NOTIFY_FIRST_LAST = 1 << 5,
44054438 REMOVE_CHILD_STOP_TRANSITIONS = 1 << 6,
4439+ REMOVE_CHILD_CLEAR_STAGE_VIEWS = 1 << 7,
44064440
44074441 /* default flags for public API */
44084442 REMOVE_CHILD_DEFAULT_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS |
@@ -4411,14 +4445,16 @@ typedef enum
44114445 REMOVE_CHILD_EMIT_ACTOR_REMOVED |
44124446 REMOVE_CHILD_CHECK_STATE |
44134447 REMOVE_CHILD_FLUSH_QUEUE |
4414- REMOVE_CHILD_NOTIFY_FIRST_LAST,
4448+ REMOVE_CHILD_NOTIFY_FIRST_LAST |
4449+ REMOVE_CHILD_CLEAR_STAGE_VIEWS,
44154450
44164451 /* flags for legacy/deprecated API */
44174452 REMOVE_CHILD_LEGACY_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS |
44184453 REMOVE_CHILD_CHECK_STATE |
44194454 REMOVE_CHILD_FLUSH_QUEUE |
44204455 REMOVE_CHILD_EMIT_PARENT_SET |
4421- REMOVE_CHILD_NOTIFY_FIRST_LAST
4456+ REMOVE_CHILD_NOTIFY_FIRST_LAST |
4457+ REMOVE_CHILD_CLEAR_STAGE_VIEWS
44224458} ClutterActorRemoveChildFlags;
44234459
44244460/*< private >
@@ -4440,6 +4476,7 @@ clutter_actor_remove_child_internal (ClutterActor *self,
44404476 gboolean notify_first_last;
44414477 gboolean was_mapped;
44424478 gboolean stop_transitions;
4479+ gboolean clear_stage_views;
44434480 GObject *obj;
44444481
44454482 if (self == child)
@@ -4456,6 +4493,7 @@ clutter_actor_remove_child_internal (ClutterActor *self,
44564493 flush_queue = (flags & REMOVE_CHILD_FLUSH_QUEUE) != 0;
44574494 notify_first_last = (flags & REMOVE_CHILD_NOTIFY_FIRST_LAST) != 0;
44584495 stop_transitions = (flags & REMOVE_CHILD_STOP_TRANSITIONS) != 0;
4496+ clear_stage_views = (flags & REMOVE_CHILD_CLEAR_STAGE_VIEWS) != 0;
44594497
44604498 obj = G_OBJECT (self);
44614499 g_object_freeze_notify (obj);
@@ -4529,6 +4567,13 @@ clutter_actor_remove_child_internal (ClutterActor *self,
45294567 clutter_actor_queue_compute_expand (self);
45304568 }
45314569
4570+ /* Only actors which are attached to a stage get notified about changes
4571+ * to the stage views, so make sure all the stage-views lists are
4572+ * cleared as the child and its children leave the actor tree.
4573+ */
4574+ if (clear_stage_views && !CLUTTER_ACTOR_IN_DESTRUCTION (child))
4575+ clutter_actor_clear_stage_views_recursive (child);
4576+
45324577 if (emit_parent_set && !CLUTTER_ACTOR_IN_REPARENT (child) &&
45334578 !CLUTTER_ACTOR_IN_DESTRUCTION (child))
45344579 {
@@ -6201,6 +6246,8 @@ clutter_actor_dispose (GObject *object)
62016246 priv->clones = NULL;
62026247 }
62036248
6249+ g_clear_pointer (&priv->stage_views, g_list_free);
6250+
62046251 G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object);
62056252}
62066253
@@ -8748,6 +8795,26 @@ clutter_actor_class_init (ClutterActorClass *klass)
87488795 g_signal_set_va_marshaller (actor_signals[TOUCH_EVENT],
87498796 G_TYPE_FROM_CLASS (object_class),
87508797 _clutter_marshal_BOOLEAN__BOXEDv);
8798+
8799+ /**
8800+ * ClutterActor::stage-views-changed:
8801+ * @actor: a #ClutterActor
8802+ *
8803+ * The ::stage-views-changed signal is emitted when the position or
8804+ * size an actor is being painted at have changed so that it's visible
8805+ * on different stage views.
8806+ *
8807+ * This signal is also emitted when the actor gets detached from the stage
8808+ * or when the views of the stage have been invalidated and will be
8809+ * replaced; it's not emitted when the actor gets hidden.
8810+ */
8811+ actor_signals[STAGE_VIEWS_CHANGED] =
8812+ g_signal_new (I_("stage-views-changed"),
8813+ G_TYPE_FROM_CLASS (object_class),
8814+ G_SIGNAL_RUN_LAST,
8815+ 0,
8816+ NULL, NULL, NULL,
8817+ G_TYPE_NONE, 0);
87518818}
87528819
87538820static void
@@ -8766,6 +8833,7 @@ clutter_actor_init (ClutterActor *self)
87668833 priv->needs_allocation = TRUE;
87678834 priv->needs_paint_volume_update = TRUE;
87688835 priv->needs_compute_resource_scale = TRUE;
8836+ priv->needs_update_stage_views = TRUE;
87698837
87708838 priv->cached_width_age = 1;
87718839 priv->cached_height_age = 1;
@@ -17578,17 +17646,27 @@ _clutter_actor_get_resource_scale_for_rect (ClutterActor *self,
1757817646 float *resource_scale)
1757917647{
1758017648 ClutterActor *stage;
17649+ g_autoptr (GList) views = NULL;
17650+ GList *l;
1758117651 float max_scale = 0;
1758217652
1758317653 stage = _clutter_actor_get_stage_internal (self);
1758417654 if (!stage)
1758517655 return FALSE;
1758617656
17587- if (!_clutter_stage_get_max_view_scale_factor_for_rect (CLUTTER_STAGE (stage),
17588- bounding_rect,
17589- &max_scale))
17657+ views = clutter_stage_get_views_for_rect (CLUTTER_STAGE (stage),
17658+ bounding_rect);
17659+
17660+ if (!views)
1759017661 return FALSE;
1759117662
17663+ for (l = views; l; l = l->next)
17664+ {
17665+ ClutterStageView *view = l->data;
17666+
17667+ max_scale = MAX (clutter_stage_view_get_scale (view), max_scale);
17668+ }
17669+
1759217670 *resource_scale = max_scale;
1759317671
1759417672 return TRUE;
@@ -17664,20 +17742,30 @@ _clutter_actor_compute_resource_scale (ClutterActor *self,
1766417742}
1766517743
1766617744static ClutterActorTraverseVisitFlags
17667- queue_update_resource_scale_cb (ClutterActor *actor,
17668- int depth,
17669- void * user_data)
17745+ clear_stage_views_cb (ClutterActor *actor,
17746+ int depth,
17747+ gpointer user_data)
1767017748{
17749+ g_autoptr (GList) old_stage_views = NULL;
17750+
17751+ actor->priv->needs_update_stage_views = TRUE;
17752+
1767117753 actor->priv->needs_compute_resource_scale = TRUE;
17754+
17755+ old_stage_views = g_steal_pointer (&actor->priv->stage_views);
17756+
17757+ if (old_stage_views)
17758+ g_signal_emit (actor, actor_signals[STAGE_VIEWS_CHANGED], 0);
17759+
1767217760 return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE;
1767317761}
1767417762
1767517763void
17676- _clutter_actor_queue_update_resource_scale_recursive (ClutterActor *self)
17764+ clutter_actor_clear_stage_views_recursive (ClutterActor *self)
1767717765{
1767817766 _clutter_actor_traverse (self,
1767917767 CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST,
17680- queue_update_resource_scale_cb ,
17768+ clear_stage_views_cb ,
1768117769 NULL,
1768217770 NULL);
1768317771}
@@ -17769,6 +17857,125 @@ clutter_actor_get_resource_scale (ClutterActor *self,
1776917857 return FALSE;
1777017858}
1777117859
17860+ static gboolean
17861+ sorted_lists_equal (GList *list_a,
17862+ GList *list_b)
17863+ {
17864+ GList *a, *b;
17865+
17866+ if (!list_a && !list_b)
17867+ return TRUE;
17868+
17869+ for (a = list_a, b = list_b;
17870+ a && b;
17871+ a = a->next, b = b->next)
17872+ {
17873+ if (a->data != b->data)
17874+ break;
17875+
17876+ if (!a->next && !b->next)
17877+ return TRUE;
17878+ }
17879+
17880+ return FALSE;
17881+ }
17882+
17883+ static void
17884+ update_stage_views (ClutterActor *self)
17885+ {
17886+ ClutterActorPrivate *priv = self->priv;
17887+ g_autoptr (GList) old_stage_views = NULL;
17888+ ClutterStage *stage;
17889+ graphene_rect_t bounding_rect;
17890+
17891+ old_stage_views = g_steal_pointer (&priv->stage_views);
17892+
17893+ if (priv->needs_allocation)
17894+ {
17895+ g_warning ("Can't update stage views actor %s is on because it needs an "
17896+ "allocation.", _clutter_actor_get_debug_name (self));
17897+ goto out;
17898+ }
17899+
17900+ stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self));
17901+ g_return_if_fail (stage);
17902+
17903+ clutter_actor_get_transformed_position (self,
17904+ &bounding_rect.origin.x,
17905+ &bounding_rect.origin.y);
17906+ clutter_actor_get_transformed_size (self,
17907+ &bounding_rect.size.width,
17908+ &bounding_rect.size.height);
17909+
17910+ if (bounding_rect.size.width == 0.0 ||
17911+ bounding_rect.size.height == 0.0)
17912+ goto out;
17913+
17914+ priv->stage_views = clutter_stage_get_views_for_rect (stage,
17915+ &bounding_rect);
17916+
17917+ out:
17918+ if (g_signal_has_handler_pending (self, actor_signals[STAGE_VIEWS_CHANGED],
17919+ 0, TRUE))
17920+ {
17921+ if (!sorted_lists_equal (old_stage_views, priv->stage_views))
17922+ g_signal_emit (self, actor_signals[STAGE_VIEWS_CHANGED], 0);
17923+ }
17924+ }
17925+
17926+ void
17927+ clutter_actor_update_stage_views (ClutterActor *self)
17928+ {
17929+ ClutterActorPrivate *priv = self->priv;
17930+ ClutterActor *child;
17931+
17932+ if (!CLUTTER_ACTOR_IS_MAPPED (self) ||
17933+ CLUTTER_ACTOR_IN_DESTRUCTION (self))
17934+ return;
17935+
17936+ if (!priv->needs_update_stage_views)
17937+ return;
17938+
17939+ update_stage_views (self);
17940+
17941+ priv->needs_update_stage_views = FALSE;
17942+
17943+ for (child = priv->first_child; child; child = child->priv->next_sibling)
17944+ clutter_actor_update_stage_views (child);
17945+ }
17946+
17947+ /**
17948+ * clutter_actor_peek_stage_views:
17949+ * @self: A #ClutterActor
17950+ *
17951+ * Retrieves the list of #ClutterStageView<!-- -->s the actor is being
17952+ * painted on.
17953+ *
17954+ * If this function is called during the paint cycle, the list is guaranteed
17955+ * to be up-to-date, if called outside the paint cycle, the list will
17956+ * contain the views the actor was painted on last.
17957+ *
17958+ * The list returned by this function is not updated when the actors
17959+ * visibility changes: If an actor gets hidden and is not being painted
17960+ * anymore, this function will return the list of views the actor was
17961+ * painted on last.
17962+ *
17963+ * If an actor is not attached to a stage (realized), this function will
17964+ * always return an empty list.
17965+ *
17966+ * Returns: (transfer none) (element-type Clutter.StageView): The list of
17967+ * #ClutterStageView<!-- -->s the actor is being painted on. The list and
17968+ * its contents are owned by the #ClutterActor and the list may not be
17969+ * freed or modified.
17970+ */
17971+ GList *
17972+ clutter_actor_peek_stage_views (ClutterActor *self)
17973+ {
17974+ g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
17975+
17976+ return self->priv->stage_views;
17977+ }
17978+
1777217979/**
1777317980 * clutter_actor_has_overlaps:
1777417981 * @self: A #ClutterActor
0 commit comments