From da3e89695f44a594954545bc77ada4dbcd6b51e8 Mon Sep 17 00:00:00 2001 From: fchinu Date: Fri, 23 Oct 2026 09:34:39 +0200 Subject: [PATCH] Format Painter: paint a drag release only if the drag changed the selection A Shift-press at the caret inside the edited text starts a text drag without changing the selection; releasing it no longer repaints the existing range. Co-Authored-By: Claude Sonnet 5.5 --- crates/engine/src/tests_keys.rs | 20 ++++++++++++++++++++ crates/engine/src/tools.rs | 13 +++++++------ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/crates/engine/src/tests_keys.rs b/crates/engine/src/tests_keys.rs index 27845045..b8cf7379 100644 --- a/crates/engine/src/tests_keys.rs +++ b/crates/engine/src/tests_keys.rs @@ -561,3 +561,23 @@ fn format_painter_ignores_a_double_click_that_selects_nothing() { assert!(s.painter.is_some(), "a click that left the selection alone keeps the painter"); assert!(bulleted(&mut s, 0)); } + +#[test] +fn format_painter_ignores_a_shift_press_that_leaves_the_selection() { + use crate::tools::PointerKind::{Down, Up}; + let (mut s, _) = painter_armed(false); + // Find the caret position under the point, then select from the line start to it. + pointer(&mut s, Down, 110.0, 90.0); + pointer(&mut s, Up, 110.0, 90.0); + let k = caret(&s); + assert!(k.1 > 0); + s.execute("text.select", &json!({"anchor": [k.0, 0], "caret": [k.0, k.1]})).unwrap(); + // Shift-pressing at the caret keeps the range, so there is nothing new to paint. + let shift = Mods { shift: true, ..NONE }; + for kind in [Down, Up] { + s.pointer(crate::tools::PointerEvent { kind, x: 110.0, y: 90.0, mods: shift, tol: 4.0, ..Default::default() }).unwrap(); + } + assert_eq!(sel(&s), ((k.0, 0), k)); + assert!(s.painter.is_some(), "the painter stays armed"); + assert!(!bold_at(&mut s, (1, 0))); +} diff --git a/crates/engine/src/tools.rs b/crates/engine/src/tools.rs index 0857ff9c..3f707655 100644 --- a/crates/engine/src/tools.rs +++ b/crates/engine/src/tools.rs @@ -194,6 +194,8 @@ enum Drag { }, TextSelect { shape: ShapeId, + /// The text selection before the press, to tell whether the drag changed it. + before: Option, }, /// Dragging a table border: the right edge of column `index`, or the bottom edge of row /// `index`. `start` is the table-local press point, `size` the column's width or the row's @@ -582,8 +584,9 @@ impl Session { ToolKind::Select => {} } // Clicking inside the text being edited moves the caret / starts a text selection. + let before = self.text_selection(); if let Some((shape, pos)) = self.place_caret(p, tol, mods.shift)? { - self.tool.drag = Some(Drag::TextSelect { shape }); + self.tool.drag = Some(Drag::TextSelect { shape, before }); return Ok(json!({"caret": [pos.0, pos.1]})); } let hit = self.hit_test(p, tol); @@ -939,7 +942,7 @@ impl Session { }; self.tool.preview = Some(x); } - Drag::TextSelect { shape } => { + Drag::TextSelect { shape, .. } => { // The selection stays in the shape or table cell where it started. let st = self.doc()?; if let Some(t) = st.selection.text.as_ref().filter(|t| t.shape == shape) { @@ -1134,10 +1137,8 @@ impl Session { let _ = cmd::text::fit_text_box(self, id); self.refit_after_resize(id)?; } - Some(Drag::TextSelect { .. }) => { - if let Some(applied) = self.paint_text_selection()? { - out = applied; - } + Some(Drag::TextSelect { before, .. }) => { + out = self.paint_if_selection_changed(before, out)?; self.commit(); } Some(_) => self.commit(),