From 99cb5ab77117478d373d79a2ec6474b2d1294916 Mon Sep 17 00:00:00 2001 From: Elena Torro Date: Wed, 6 May 2026 15:59:39 +0200 Subject: [PATCH] :bug: Fix text drag --- render-wasm/src/render.rs | 11 +++++++++++ render-wasm/src/shapes.rs | 22 ++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index a6822c991e..942600f46b 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -386,6 +386,7 @@ pub(crate) struct RenderState { /// GPU crops from `Backbuffer` keyed by shape id. Filled on full-frame completion; during /// drag, entries for the moved top-level selection are ensured here pub backbuffer_crop_cache: HashMap, + pub last_full_render_drawn: HashSet, } pub struct InteractiveDragCrop { @@ -542,6 +543,7 @@ impl RenderState { current_tile_had_shapes: false, interactive_target_seeded: false, backbuffer_crop_cache: HashMap::default(), + last_full_render_drawn: HashSet::default(), }) } @@ -1668,6 +1670,10 @@ impl RenderState { continue; } + if !shape.all_text_descendants_drawn(tree, &self.last_full_render_drawn) { + continue; + } + candidates.push((shape.id, doc_bounds, selrect)); } @@ -1953,6 +1959,7 @@ impl RenderState { } else { self.reset_canvas(); self.interactive_target_seeded = false; + self.last_full_render_drawn.clear(); } let surface_ids = SurfaceId::Strokes as u32 @@ -3154,6 +3161,10 @@ impl RenderState { target_surface, )?; + if !self.options.is_interactive_transform() && !export { + self.last_full_render_drawn.insert(node_id); + } + self.surfaces .canvas(SurfaceId::DropShadows) .clear(skia::Color::TRANSPARENT); diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index 22e9e1e9d8..cf6ae67312 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -1386,6 +1386,28 @@ impl Shape { } } + pub fn all_text_descendants_drawn( + &self, + shapes_pool: ShapesPoolRef, + drawn: &HashSet, + ) -> bool { + for child_id in self.children_ids_iter(false) { + let Some(child) = shapes_pool.get(child_id) else { + continue; + }; + if child.hidden { + continue; + } + if matches!(child.shape_type, Type::Text(_)) && !drawn.contains(child_id) { + return false; + } + if !child.all_text_descendants_drawn(shapes_pool, drawn) { + return false; + } + } + true + } + /// Whether this shape may use the backbuffer crop fast path during interactive drag. /// /// Conservative: only effects and fills that match what we snapshot and clip in