From 18c792d6f8a15bfd36edfb9751c63556190eeded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Fri, 10 Apr 2020 00:59:05 +0200 Subject: [PATCH] clutter/actor: Emit a signal when the stage-views an actor is on change Add a new signal that's emitted when the stage views an actor being painted on have changed, "stage-views-changed". For example this signal can be helpful when tracking whether an actor is painted on multiple stage views or only one. Since we must clear the stage-views list when an actor leaves the stage (actors that aren't attached to a stage don't get notified about the stage views being changed/replaced), we also emit the new signal when an actor gets detached from the stage (otherwise there would be an edge case where no signal is emitted but it really should: An actor is visible on a stage view, then detached from the stage, and then attached again and immeditely moved outside the view). Also skip the comparison of the old stage-views list and the new one if nobody is listening to the signal to save some resources. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1196 --- clutter/clutter/clutter-actor.c | 69 ++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 5 deletions(-) diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c index 1d3f98d41..dd7b1ba74 100644 --- a/clutter/clutter/clutter-actor.c +++ b/clutter/clutter/clutter-actor.c @@ -1020,6 +1020,7 @@ enum TRANSITIONS_COMPLETED, TOUCH_EVENT, TRANSITION_STOPPED, + STAGE_VIEWS_CHANGED, LAST_SIGNAL }; @@ -4421,7 +4422,7 @@ clutter_actor_remove_child_internal (ClutterActor *self, * 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) + if (clear_stage_views && !CLUTTER_ACTOR_IN_DESTRUCTION (child)) clutter_actor_clear_stage_views_recursive (child); if (emit_parent_set && !CLUTTER_ACTOR_IN_DESTRUCTION (child)) @@ -8634,6 +8635,27 @@ clutter_actor_class_init (ClutterActorClass *klass) g_signal_set_va_marshaller (actor_signals[TOUCH_EVENT], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_BOOLEAN__BOXEDv); + + /** + * ClutterActor::stage-views-changed: + * @actor: a #ClutterActor + * + * The ::stage-views-changed signal is emitted when the position or + * size an actor is being painted at have changed so that it's visible + * on different stage views. + * + * This signal is also emitted when the actor gets detached from the stage + * or when the views of the stage have been invalidated and will be + * replaced; it's not emitted when the actor gets hidden. + */ + actor_signals[STAGE_VIEWS_CHANGED] = + g_signal_new (I_("stage-views-changed"), + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); + } static void @@ -17540,10 +17562,15 @@ clear_stage_views_cb (ClutterActor *actor, int depth, gpointer user_data) { + g_autoptr (GList) old_stage_views = NULL; + actor->priv->needs_update_stage_views = TRUE; actor->priv->needs_compute_resource_scale = TRUE; - g_clear_pointer (&actor->priv->stage_views, g_list_free); + old_stage_views = g_steal_pointer (&actor->priv->stage_views); + + if (old_stage_views) + g_signal_emit (actor, actor_signals[STAGE_VIEWS_CHANGED], 0); return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE; } @@ -17645,20 +17672,44 @@ clutter_actor_get_resource_scale (ClutterActor *self, return FALSE; } +static gboolean +sorted_lists_equal (GList *list_a, + GList *list_b) +{ + GList *a, *b; + + if (!list_a && !list_b) + return TRUE; + + for (a = list_a, b = list_b; + a && b; + a = a->next, b = b->next) + { + if (a->data != b->data) + break; + + if (!a->next && !b->next) + return TRUE; + } + + return FALSE; +} + static void update_stage_views (ClutterActor *self) { ClutterActorPrivate *priv = self->priv; + g_autoptr (GList) old_stage_views = NULL; ClutterStage *stage; graphene_rect_t bounding_rect; - g_clear_pointer (&priv->stage_views, g_list_free); + old_stage_views = g_steal_pointer (&priv->stage_views); 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; + goto out; } stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); @@ -17673,10 +17724,18 @@ update_stage_views (ClutterActor *self) if (bounding_rect.size.width == 0.0 || bounding_rect.size.height == 0.0) - return; + goto out; priv->stage_views = clutter_stage_get_views_for_rect (stage, &bounding_rect); + +out: + if (g_signal_has_handler_pending (self, actor_signals[STAGE_VIEWS_CHANGED], + 0, TRUE)) + { + if (!sorted_lists_equal (old_stage_views, priv->stage_views)) + g_signal_emit (self, actor_signals[STAGE_VIEWS_CHANGED], 0); + } } void