[mutter] clutter: Add private API to support resource scale affecting layout
- From: Jonas Ådahl <jadahl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [mutter] clutter: Add private API to support resource scale affecting layout
- Date: Tue, 30 Jun 2020 13:41:18 +0000 (UTC)
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]