From b89ddeccd0a121442030ce9642ee52e5d81eba15 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Thu, 12 Nov 2026 19:59:19 -0800 Subject: [PATCH] Smart Guides: a moved selection reaches ruler guides within the Snapping Tolerance With Smart Guides on, a selection moved with the Selection tool, Direct Selection or a shape tool reached a ruler guide only within the Snap to Point distance (2 px), while drawn points and bounding-box handles reach it within the Snapping Tolerance (4 px). The move now uses the Snapping Tolerance for the grabbed point and the edges, with and without Shift. With Smart Guides off, Snap to Point keeps its own distance. --- crates/tools/src/select.rs | 9 ++++---- crates/tools/src/select/tests.rs | 35 +++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/crates/tools/src/select.rs b/crates/tools/src/select.rs index 17bf7eeaef..d0d624bbeb 100644 --- a/crates/tools/src/select.rs +++ b/crates/tools/src/select.rs @@ -239,7 +239,8 @@ pub(crate) struct MoveSnap { copy_targets: Option, copy_points: Option, /// With Smart Guides on, the ruler guides View › Snap to Point still pulls the grabbed point - /// onto (#1534), and lines the bounds up with (#5746); `points` holds them with Smart Guides off. + /// onto (#1534), and lines the bounds up with (#5746), within the Snapping Tolerance as drawing + /// does; `points` holds them with Smart Guides off. rulers: Option, } @@ -303,7 +304,7 @@ impl MoveSnap { // axis without one that Smart Guides left free, the bounds line up with the ruler guides // (#5746), alongside the Smart Guides. if let Some(t) = &self.rulers { - let (adj, ov, held) = t.snap_moved_to_rulers(start + d, self.bounds.map(|b| b + d), cx.tol(cx.snap_tolerance), taken); + let (adj, ov, held) = t.snap_moved_to_rulers(start + d, self.bounds.map(|b| b + d), cx.snap_tol(), taken); if adj != Vec2::ZERO { d += adj; if held { @@ -373,7 +374,7 @@ impl MoveSnap { } } if let Some(t) = &self.rulers { - let (q, ov) = t.snap_point_along(start + d, u, cx.tol(cx.snap_tolerance)); + let (q, ov) = t.snap_point_along(start + d, u, cx.snap_tol()); if q != start + d { d = q - start; guides = ov; @@ -382,7 +383,7 @@ impl MoveSnap { { // Nothing else lined up: the bounds slide along the line onto a ruler guide, as // they do without Shift (#4312). - let (adj, ov) = t.snap_moved_along_to_rulers(start + d, b + d, u, cx.tol(cx.snap_tolerance)); + let (adj, ov) = t.snap_moved_along_to_rulers(start + d, b + d, u, cx.snap_tol()); d += adj; guides = ov; } diff --git a/crates/tools/src/select/tests.rs b/crates/tools/src/select/tests.rs index 659e50a6a5..c41056b7aa 100644 --- a/crates/tools/src/select/tests.rs +++ b/crates/tools/src/select/tests.rs @@ -751,7 +751,7 @@ fn escape_cancels_a_move_or_an_alt_drag_copy() { } /// #1534: with Smart Guides on (the default), the point a selection is dragged by still snaps -/// to a ruler guide within the Snap to Point distance; not with Snap to Point off. +/// to a ruler guide within the Snapping Tolerance; not with Snap to Point off. #[test] fn a_move_snaps_the_grabbed_point_to_a_ruler_guide_with_smart_guides_on() { let (mut d, id) = doc_with_rect(); @@ -797,6 +797,39 @@ fn a_shift_move_lands_the_edges_on_a_ruler_guide() { assert!(moved_by(m, (231.0, 0.0)), "{m:?}"); } +/// With Smart Guides on, a moved selection reaches a ruler guide within the Snapping Tolerance +/// (4 px), as drawing and handle drags do; with Smart Guides off, Snap to Point reaches it within +/// its own distance (2 px). +#[test] +fn a_move_reaches_ruler_guides_within_the_snapping_tolerance() { + let (mut d, id) = doc_with_rect(); + d.add_ruler_guide(&vectorcraft_doc::Guide::new(true, 340.0)); + let mut s = Selection::default(); + s.add(id); + let p = paint(); + let dx = |c: &ToolContext, to: f64| { + let mut t = SelectionTool::default(); + t.pointer(c, &ev(PointerKind::Down, 150.0, 150.0)); + let a = t.pointer(c, &ev(PointerKind::Drag, to, 150.0)); + let Some(Action::Preview(_, v)) = a.last() else { panic!("{a:?}") }; + v["matrix"][4].as_f64().unwrap() + }; + let c = cx(&d, &s, &p); + assert!(c.smart_guides && c.snap_to_point && c.guides); + // The right edge, 3 px short of the guide, lands on it, with or without Shift. + assert_eq!(dx(&c, 150.0 + 137.0), 140.0); + let (m, _) = shift_moved(&c, (150.0 + 137.0, 150.0)); + assert!(moved_by(m, (140.0, 0.0)), "{m:?}"); + // The grabbed point, 3 px from the guide, lands on it. + assert_eq!(dx(&c, 337.0), 190.0); + // 5 px away is out of reach. + assert_eq!(dx(&c, 150.0 + 135.0), 135.0); + // Smart Guides off: the grabbed point lands on the guide from 1.5 px, not from 3 px. + let off = ToolContext { smart_guides: false, ..cx(&d, &s, &p) }; + assert_eq!(dx(&off, 338.5), 190.0); + assert_eq!(dx(&off, 337.0), 187.0); +} + /// Alt pressed during a move makes a copy, which snaps to the original it leaves behind (as /// gathered before the previews moved it); released, the move doesn't. #[test]