mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-25 18:27:02 -06:00
Fix #886: Smart Guides keep working on a Shift-constrained anchor drag
Dragging anchors with Direct Selection snapped to points, paths and Smart Guides, but with Shift held the move kept its 45° angle and snapped to nothing. The grabbed anchor now slides along the constrained line onto the anchors, paths and alignment lines that cross it, as the drawing tools' Shift does (counted from General › Constrain Angle). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d8759492df
commit
e47d542680
+16
-10
@@ -32,7 +32,7 @@ use vectorcraft_geom::{Anchor, PathData, Point, Rect};
|
||||
|
||||
use crate::bbox::move_delta;
|
||||
use crate::corners::{self, CornerDrag, over_widget};
|
||||
use crate::guides::{HandleSnap, PointSnap, Targets};
|
||||
use crate::guides::{HandleSnap, Leave, PointSnap, Targets};
|
||||
use crate::meshedit::MeshEdit;
|
||||
use crate::pathtype::{self, BracketDrag, over_bracket};
|
||||
use crate::rulerguide::GuideEdit;
|
||||
@@ -428,12 +428,12 @@ impl Tool for DirectSelectionTool {
|
||||
self.anchor_snap = Some(PointSnap::new(cx, || Targets::for_anchor_drag(cx.doc, cx.selection)));
|
||||
}
|
||||
let mut d = move_delta(start, p, ev.mods.shift);
|
||||
// The grabbed anchor snaps and the others follow it; Shift keeps the angle instead.
|
||||
// The grabbed anchor snaps and the others follow it. Shift keeps the move at its
|
||||
// angle, and the anchor slides along it onto what Smart Guides find there (#886).
|
||||
self.guides.clear();
|
||||
if !ev.mods.shift
|
||||
&& let Some(snap) = &self.anchor_snap
|
||||
{
|
||||
let (q, guides) = snap.snap(cx, grab + d);
|
||||
if let Some(snap) = &self.anchor_snap {
|
||||
let shift = ev.mods.shift.then(|| Leave::segment(cx, grab, true));
|
||||
let (q, guides) = snap.snap_from(cx, grab + (p - start), shift.as_ref());
|
||||
(d, self.guides) = (q - grab, guides);
|
||||
}
|
||||
out.push(Action::Preview("path.moveAnchors".into(), json!({"dx": d.x, "dy": d.y})));
|
||||
@@ -1022,10 +1022,16 @@ mod tests {
|
||||
// In line with B's own bottom-left corner, which stays put.
|
||||
let (v, labels) = drag(&mut t, &c, (300.0, 300.0), (303.0, 330.0), none);
|
||||
assert_eq!((v, labels), (json!({"dx": 0.0, "dy": 30.0}), vec!["align".to_string()]));
|
||||
// Shift keeps the move at 45° steps, unsnapped.
|
||||
let (v, labels) = drag(&mut t, &c, (300.0, 300.0), (203.0, 199.0), Mods { shift: true, ..none });
|
||||
let s = move_delta(Point::new(300.0, 300.0), Point::new(203.0, 199.0), true);
|
||||
assert_eq!((v, labels), (json!({"dx": s.x, "dy": s.y}), vec![]));
|
||||
// Shift keeps the move at 45° steps and still snaps along that line (#886): on the
|
||||
// diagonal from B's corner, onto A's bottom-right corner…
|
||||
let shift = Mods { shift: true, ..none };
|
||||
let (v, labels) = drag(&mut t, &c, (300.0, 300.0), (203.0, 199.0), shift);
|
||||
let near = |v: &serde_json::Value, (x, y): (f64, f64)| (v["dx"].as_f64().unwrap() - x).hypot(v["dy"].as_f64().unwrap() - y) < 1e-9;
|
||||
assert!(near(&v, (-100.0, -100.0)) && labels == ["anchor"], "{v} {labels:?}");
|
||||
// …and with nothing in reach, at the pointer's distance along its angle.
|
||||
let (v, labels) = drag(&mut t, &c, (300.0, 300.0), (380.0, 302.0), shift);
|
||||
let s = move_delta(Point::new(300.0, 300.0), Point::new(380.0, 302.0), true);
|
||||
assert!(near(&v, (s.x, s.y)) && labels.is_empty(), "{v} {labels:?}");
|
||||
// Two anchors: the one pressed on (B's top-right) lands on A's corner.
|
||||
let two = anchors_of(b, &[(0, 0), (0, 1)]);
|
||||
let c = cx(&d, &two, &p);
|
||||
|
||||
Reference in New Issue
Block a user