Fix #4980: Pen with Cmd/Ctrl drags the path, not hidden bounding-box handles

Cmd/Ctrl held with the Pen lends the last-used selection tool (Direct
Selection until one is chosen), which already moved anchors, straight
segments and curves of the path being drawn, one undo step each, and
handed the path back to the Pen.

With the Selection tool used last, the lent tool hit-tested the
bounding box even though the canvas hides it while anchors are selected
on their own, as the end anchor of a path being drawn is. Over the
path it showed a resize pointer, and the drag scaled the path by a
handle nobody could see: a flat path did nothing at all.

The Selection tool's handles now take the pointer only while the box is
shown (View > Show Bounding Box, no anchors selected on their own), so
the lent Selection tool moves the path being drawn.

Tests: select::tests::hidden_box_handles_take_no_press_while_anchors_are_selected,
tests::draw2::pen_cmd_drag_edits_anchors_and_segments_and_goes_on_drawing.
This commit is contained in:
bflatastic
2026-11-01 06:11:09 +07:00
parent ffc9b71a2b
commit 8704c15c6d
4 changed files with 89 additions and 9 deletions
+54
View File
@@ -2579,6 +2579,60 @@ fn pen_with_cmd_borrows_direct_selection_and_goes_on_drawing() {
assert_eq!(s.tool_id(), "rectangle");
}
/// #4980: Cmd/Ctrl held with the Pen drags as Direct Selection: an anchor of the path being drawn
/// moves on its own and the Pen goes on drawing that path from it; another path's straight segment
/// moves both its ends and a curved one bends; each drag is one undo step and the pointer is the
/// Pen's again once Cmd is let go. With the Selection tool used last, a Cmd drag on the path being
/// drawn moves it: the bounding box isn't shown, so its handles don't scale it to nothing.
#[test]
fn pen_cmd_drag_edits_anchors_and_segments_and_goes_on_drawing() {
let (none, cmd) = (Mods::default(), Mods { cmd: true, ..Mods::default() });
let v = view();
let mut s = session();
let straight = line(&mut s, 100.0, 500.0, 300.0, 500.0);
let anchors = json!([{"x": 400, "y": 500, "out": [450, 400]}, {"x": 600, "y": 500, "in": [550, 400]}]);
let curve = NodeId(s.execute("path.create", &json!({"anchors": anchors})).unwrap()["id"].as_u64().unwrap());
s.execute("select.none", &json!({})).unwrap();
s.select_tool("pen", v).unwrap();
for x in [100.0, 200.0, 300.0] {
gesture(&mut s, &[(x, 100.0)], none);
}
let id = s.doc().unwrap().selection.objects[0];
let points = |s: &Session, id| path(s, id).subpaths[0].anchors.iter().map(|a| (a.p.x, a.p.y)).collect::<Vec<_>>();
assert_eq!(gesture(&mut s, &[(200.0, 100.0), (205.0, 120.0), (210.0, 140.0)], cmd), 1);
assert_eq!(points(&s, id), [(100.0, 100.0), (210.0, 140.0), (300.0, 100.0)]);
assert_eq!(s.tool_id(), "pen");
let pen = s.cursor(Point::new(700.0, 300.0), none, v);
assert!(matches!(pen, vectorcraft_tools::Cursor::Pen | vectorcraft_tools::Cursor::PenDrawing), "{pen:?}");
// The next click goes on drawing the same path from its end.
gesture(&mut s, &[(400.0, 200.0)], none);
assert_eq!(paths(&s).len(), 3, "no new path");
assert_eq!(path(&s, id).subpaths[0].anchors.len(), 4);
assert_eq!(gesture(&mut s, &[(200.0, 500.0), (200.0, 510.0), (200.0, 530.0)], cmd), 1);
assert_eq!(points(&s, straight), [(100.0, 530.0), (300.0, 530.0)]);
let before = path(&s, curve).subpaths[0].anchors.clone();
assert_eq!(gesture(&mut s, &[(500.0, 425.0), (500.0, 410.0), (500.0, 380.0)], cmd), 1);
let after = &path(&s, curve).subpaths[0].anchors;
assert_eq!((after[0].p, after[1].p), (before[0].p, before[1].p), "the ends stay");
assert!(after[0].h_out.y < before[0].h_out.y && after[1].h_in.y < before[1].h_in.y, "{after:?}");
// A Cmd-click away from the art deselects: the path is done, the next click starts another.
gesture(&mut s, &[(700.0, 300.0)], cmd);
assert!(s.doc().unwrap().selection.is_empty());
gesture(&mut s, &[(700.0, 50.0)], none);
assert_eq!(paths(&s).len(), 4);
// Selection used last, a flat path being drawn: a Cmd drag where its box's top handle would be moves it.
s.select_tool("selection", v).unwrap();
s.execute("select.none", &json!({})).unwrap();
s.select_tool("pen", v).unwrap();
gesture(&mut s, &[(100.0, 300.0)], none);
gesture(&mut s, &[(300.0, 300.0)], none);
let flat = s.doc().unwrap().selection.objects[0];
assert_ne!(s.cursor(Point::new(200.0, 300.0), cmd, v), vectorcraft_tools::Cursor::ResizeV);
assert_eq!(gesture(&mut s, &[(200.0, 300.0), (200.0, 320.0), (200.0, 340.0)], cmd), 1);
assert_eq!(points(&s, flat), [(100.0, 340.0), (300.0, 340.0)]);
assert_eq!(s.tool_id(), "pen");
}
#[test]
fn cmd_alt_lends_group_selection_while_direct_selection_is_active() {
let (cmd, cmd_alt) = (Mods { cmd: true, ..Mods::default() }, Mods { cmd: true, alt: true, ..Mods::default() });