[mutter] clutter: Add private API to support resource scale affecting layout



commit 280429bac862b4fdbc3c094c0c034ce58642864e
Author: Jonas Dreßler <verdre v0yd nl>
Date:   Fri Apr 10 14:54:11 2020 +0200

    clutter: Add private API to support resource scale affecting layout
    
    For ClutterText, the resource scale the text is drawn with affects the
    size of the allocation: ClutterText will choose a font scale based on
    the resource scale, and that font scale can lead to a slight difference
    in size compared to the unscaled font.
    
    We currently handle that by queuing a relayout inside the
    "resource-scale-changed" signal handler. This solution is a bit
    problematic though since it will take one more allocation cycle until
    the allocation is actually updated after a scale-change, so the actor is
    painted using the wrong allocation for one frame.
    
    Also the current solution can lead to relayout loops in a few cases, for
    example if a ClutterText is located near the edge on a 1x scaled monitor
    and is moved to intersect a 2x scaled monitor: Now the resource scale
    will change to 2 and a new allocation box is calculated; if this
    allocation box is slightly smaller than the old one because of the new
    font scale, the allocation won't intersect the 2x scaled monitor again
    and the resource scale switches back to 1. Now the allocation gets
    larger again and intersects the 2x scaled monitor again.
    
    This commit introduces a way to properly support those actors: In case
    an actors resource scale might affect its allocation, it should call the
    private function clutter_actor_queue_immediate_relayout(). This will
    make sure the actor gets a relayout before the upcoming paint happens
    afte every resource scale change. Also potential relayout loops can
    be handled by the actors themselves using a "phase" argument that's
    passed to implementations of the calculate_resource_scale() vfunc.
    
    The new API is private because resource scales are not meant to be used
    in a way where the scale affects the allocation. With ClutterText and
    the current behavior of Pango, that can't be avoid though, so we need it
    anyway.
    
    https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1276

 clutter/clutter/clutter-actor-private.h |  5 ++-
 clutter/clutter/clutter-actor.c         | 57 +++++++++++++++++++++++++--------
 clutter/clutter/clutter-actor.h         |  2 ++
 clutter/clutter/clutter-stage-private.h |  2 ++
 clutter/clutter/clutter-stage.c         | 39 ++++++++++++++++++++--
 5 files changed, 87 insertions(+), 18 deletions(-)
---
diff --git a/clutter/clutter/clutter-actor-private.h b/clutter/clutter/clutter-actor-private.h
index d1adcd38f0..16ab012b6f 100644
--- a/clutter/clutter/clutter-actor-private.h
+++ b/clutter/clutter/clutter-actor-private.h
@@ -288,7 +288,10 @@ float                           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);
+void clutter_actor_update_stage_views (ClutterActor *self,
+                                       int           phase);
+
+void clutter_actor_queue_immediate_relayout (ClutterActor *self);
 
 G_END_DECLS
 
diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c
index e6a9631399..86ffe2f1f0 100644
--- a/clutter/clutter/clutter-actor.c
+++ b/clutter/clutter/clutter-actor.c
@@ -5903,6 +5903,25 @@ clutter_actor_real_has_overlaps (ClutterActor *self)
   return TRUE;
 }
 
+static float
+clutter_actor_real_calculate_resource_scale (ClutterActor *self,
+                                             int           phase)
+{
+  ClutterActorPrivate *priv = self->priv;
+  GList *l;
+  float new_resource_scale = -1.f;
+
+  for (l = priv->stage_views; l; l = l->next)
+    {
+      ClutterStageView *view = l->data;
+
+      new_resource_scale = MAX (clutter_stage_view_get_scale (view),
+                                new_resource_scale);
+    }
+
+  return new_resource_scale;
+}
+
 static void
 clutter_actor_real_destroy (ClutterActor *actor)
 {
@@ -5988,6 +6007,7 @@ clutter_actor_class_init (ClutterActorClass *klass)
   klass->get_accessible = clutter_actor_real_get_accessible;
   klass->get_paint_volume = clutter_actor_real_get_paint_volume;
   klass->has_overlaps = clutter_actor_real_has_overlaps;
+  klass->calculate_resource_scale = clutter_actor_real_calculate_resource_scale;
   klass->paint = clutter_actor_real_paint;
   klass->destroy = clutter_actor_real_destroy;
 
@@ -16131,20 +16151,14 @@ out:
 }
 
 static void
-update_resource_scale (ClutterActor *self)
+update_resource_scale (ClutterActor *self,
+                       int           phase)
 {
   ClutterActorPrivate *priv = self->priv;
-  GList *l;
-  float new_resource_scale = -1.f;
-  float old_resource_scale;
-
-  for (l = priv->stage_views; l; l = l->next)
-    {
-      ClutterStageView *view = l->data;
+  float new_resource_scale, old_resource_scale;
 
-      new_resource_scale = MAX (clutter_stage_view_get_scale (view),
-                                new_resource_scale);
-    }
+  new_resource_scale =
+    CLUTTER_ACTOR_GET_CLASS (self)->calculate_resource_scale (self, phase);
 
   if (priv->resource_scale == new_resource_scale)
     return;
@@ -16168,7 +16182,8 @@ update_resource_scale (ClutterActor *self)
 }
 
 void
-clutter_actor_update_stage_views (ClutterActor *self)
+clutter_actor_update_stage_views (ClutterActor *self,
+                                  gboolean      use_max_scale)
 {
   ClutterActorPrivate *priv = self->priv;
   ClutterActor *child;
@@ -16181,12 +16196,12 @@ clutter_actor_update_stage_views (ClutterActor *self)
     return;
 
   update_stage_views (self);
-  update_resource_scale (self);
+  update_resource_scale (self, use_max_scale);
 
   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_update_stage_views (child, use_max_scale);
 }
 
 /**
@@ -19710,3 +19725,17 @@ clutter_actor_has_accessible (ClutterActor *actor)
 
   return TRUE;
 }
+
+void
+clutter_actor_queue_immediate_relayout (ClutterActor *self)
+{
+  ClutterStage *stage;
+
+  g_return_if_fail (CLUTTER_IS_ACTOR (self));
+
+  clutter_actor_queue_relayout (self);
+
+  stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self));
+  if (stage)
+    clutter_stage_set_actor_needs_immediate_relayout (stage);
+}
diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h
index 9f795f6d3f..67d327b5b4 100644
--- a/clutter/clutter/clutter-actor.h
+++ b/clutter/clutter/clutter-actor.h
@@ -297,6 +297,8 @@ struct _ClutterActorClass
                                      ClutterTouchEvent    *event);
   gboolean (* has_accessible)       (ClutterActor         *self);
   void     (* resource_scale_changed) (ClutterActor *self);
+  float    (* calculate_resource_scale) (ClutterActor *self,
+                                         int           phase);
 
   /*< private >*/
   /* padding for future expansion */
diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h
index 5d785b644a..2ce0cd05b5 100644
--- a/clutter/clutter/clutter-stage-private.h
+++ b/clutter/clutter/clutter-stage-private.h
@@ -136,6 +136,8 @@ void            clutter_stage_queue_actor_relayout      (ClutterStage *stage,
 GList * clutter_stage_get_views_for_rect (ClutterStage          *stage,
                                           const graphene_rect_t *rect);
 
+void clutter_stage_set_actor_needs_immediate_relayout (ClutterStage *stage);
+
 G_END_DECLS
 
 #endif /* __CLUTTER_STAGE_PRIVATE_H__ */
diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c
index 99d2184412..8d3fc8b800 100644
--- a/clutter/clutter/clutter-stage.c
+++ b/clutter/clutter/clutter-stage.c
@@ -146,6 +146,7 @@ struct _ClutterStagePrivate
   guint min_size_changed       : 1;
   guint motion_events_enabled  : 1;
   guint stage_was_relayout     : 1;
+  guint actor_needs_immediate_relayout : 1;
 };
 
 enum
@@ -1354,8 +1355,34 @@ static void
 update_actor_stage_views (ClutterStage *stage)
 {
   ClutterActor *actor = CLUTTER_ACTOR (stage);
+  ClutterStagePrivate *priv = stage->priv;
+  int phase;
+
+  COGL_TRACE_BEGIN_SCOPED (ClutterStageUpdateActorStageViews,
+                           "Actor stage-views");
+
+  /* If an actor needs an immediate relayout because its resource scale
+   * changed, we give it another chance to allocate correctly before
+   * the paint.
+   *
+   * We're doing the whole thing twice and pass the phase to
+   * clutter_actor_update_stage_views() to allow actors to detect loops:
+   * If the resource scale changes again after the relayout, the new
+   * allocation of an actor probably moved the actor onto another stage
+   * view, so if an actor sees phase == 1, it can choose a "final" scale.
+   */
+  for (phase = 0; phase < 2; phase++)
+    {
+      clutter_actor_update_stage_views (actor, phase);
 
-  clutter_actor_update_stage_views (actor);
+      if (!priv->actor_needs_immediate_relayout)
+        break;
+
+      priv->actor_needs_immediate_relayout = FALSE;
+      _clutter_stage_maybe_relayout (actor);
+    }
+
+  g_warn_if_fail (!priv->actor_needs_immediate_relayout);
 }
 
 /**
@@ -1405,9 +1432,7 @@ _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");
 
@@ -4115,3 +4140,11 @@ clutter_stage_get_views_for_rect (ClutterStage          *stage,
 
   return views_for_rect;
 }
+
+void
+clutter_stage_set_actor_needs_immediate_relayout (ClutterStage *stage)
+{
+  ClutterStagePrivate *priv = stage->priv;
+
+  priv->actor_needs_immediate_relayout = TRUE;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]