From 774f5af0134f8420c1c9b718f193795e539354e3 Mon Sep 17 00:00:00 2001 From: Elena Torro Date: Wed, 2 Sep 2026 16:54:03 +0200 Subject: [PATCH] :bug: Fix stale text layout cache reuse --- render-wasm/src/render/text.rs | 9 +++++++++ render-wasm/src/shapes.rs | 4 +--- render-wasm/src/shapes/text.rs | 27 +++++++++++++++------------ 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/render-wasm/src/render/text.rs b/render-wasm/src/render/text.rs index d3970869ba..770007026a 100644 --- a/render-wasm/src/render/text.rs +++ b/render-wasm/src/render/text.rs @@ -341,6 +341,15 @@ pub fn try_paint_from_layout_cache( return Ok(false); } + if text_content + .layout + .paragraphs + .iter() + .any(|group| group.is_empty()) + { + return Ok(false); + } + if let Some(render_state) = render_state { let target_surface = surface_id.unwrap_or(SurfaceId::Fills); let canvas = render_state.surfaces.canvas_and_mark_dirty(target_surface); diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index 2db654e799..366e2fdeea 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -135,9 +135,7 @@ impl Type { layout.scale_content(value); } } - Type::Text(TextContent { paragraphs, .. }) => { - paragraphs.iter_mut().for_each(|p| p.scale_content(value)); - } + Type::Text(content) => content.scale_content(value), _ => {} } } diff --git a/render-wasm/src/shapes/text.rs b/render-wasm/src/shapes/text.rs index 221c08c42d..35d89f85a0 100644 --- a/render-wasm/src/shapes/text.rs +++ b/render-wasm/src/shapes/text.rs @@ -491,8 +491,8 @@ impl TextContent { /// clones paragraphs into a rebound copy with an empty layout cache. pub fn paint_content_for_selrect<'a>(&'a self, selrect: Rect) -> Cow<'a, Self> { let stored_bounds = self.bounds(); - if (stored_bounds.width() - selrect.width()).abs() < 0.01 - && (stored_bounds.height() - selrect.height()).abs() < 0.01 + if crate::math::is_close_to(stored_bounds.width(), selrect.width()) + && crate::math::is_close_to(stored_bounds.height(), selrect.height()) { Cow::Borrowed(self) } else { @@ -500,6 +500,13 @@ impl TextContent { } } + pub fn scale_content(&mut self, value: f32) { + self.paragraphs_mut() + .iter_mut() + .for_each(|p| p.scale_content(value)); + self.layout.clear(); + } + pub fn set_xywh(&mut self, x: f32, y: f32, w: f32, h: f32) { self.bounds = Rect::from_xywh(x, y, w, h); } @@ -631,7 +638,7 @@ impl TextContent { return self.content_rect(selrect, valign); } - let tight = if !self.layout.paragraphs.is_empty() { + let tight = if self.has_usable_paint_layout(shape) { self.rect_from_paragraphs(selrect, valign) } else { let mut text_content = self.clone(); @@ -1004,10 +1011,7 @@ impl TextContent { /// True when cached Skia paragraphs can be painted as-is (no rebuild/layout). pub fn has_usable_paint_layout(&self, shape: &Shape) -> bool { - if self.layout.needs_update() || self.layout_version != self.content_version { - return false; - } - self.layout_matches_paint_container(shape) + self.layout_cache_versions_match() && self.layout_matches_paint_container(shape) } pub(crate) fn layout_cache_versions_match(&self) -> bool { @@ -1021,8 +1025,7 @@ impl TextContent { let Some(layout_w) = self.layout_width else { return false; }; - let container_w = self.get_width(shape.selrect().width()); - (layout_w - container_w).abs() < f32::EPSILON + crate::math::is_close_to(layout_w, self.get_width(shape.selrect().width())) } /// True when any span requests underline/overline/line-through (custom draw path). @@ -1062,7 +1065,7 @@ impl TextContent { let layout_matches_container = self.grow_type() == GrowType::AutoWidth || self .layout_width - .is_some_and(|w| (w - selrect.width()).abs() < f32::EPSILON); + .is_some_and(|w| crate::math::is_close_to(w, selrect.width())); if !self.layout.needs_update() && self.layout_version == self.content_version @@ -1076,7 +1079,7 @@ impl TextContent { match self.grow_type() { GrowType::AutoHeight => { let result = self.text_layout_auto_height(); - self.layout_width = Some(result.2.width); + self.layout_width = Some(selrect.width()); self.set_layout_from_result(result, selrect.width(), selrect.height()); } GrowType::AutoWidth => { @@ -1086,7 +1089,7 @@ impl TextContent { } GrowType::Fixed => { let result = self.text_layout_fixed(); - self.layout_width = Some(result.2.width); + self.layout_width = Some(selrect.width()); self.set_layout_from_result(result, selrect.width(), selrect.height()); } }