Fix #3799: Alt held at the press draws a straight Pencil line at any angle

With Alt key toggles to Smooth Tool off, holding Alt when a Pencil drag
starts now draws one straight segment from the press point to the
pointer, at the angle the pointer takes, as Shift does at 45° steps.
Pressed on an end of a selected open path, the line continues the path
from that end. Both ends snap to Smart Guides as a stroke's ends do, and
the line stays open when Alt is still held at the release. Shift and Alt
together keep the 45° steps.

Before, a drag started and released with Alt held drew a closed freehand
path. Alt pressed after the drag has started still closes the path at
the release, and with Alt key toggles to Smooth Tool on, Alt still switches
to the Smooth tool. Before a Pencil press, Alt no longer shows the closing
cursor.

Docs: docs/target-app-parity.md, docs/ui-parity.md.
This commit is contained in:
dotCooCoo
2026-10-24 17:29:32 +07:00
committed by bflatastic
parent 23c0e8c501
commit 4c5c54ccbc
7 changed files with 176 additions and 26 deletions
+23
View File
@@ -141,6 +141,29 @@ fn pencil_with_shift_draws_a_straight_line() {
assert!((angle - 75.0).abs() < 1e-6, "{angle}");
}
/// #3799: with Alt held from the press to the release, a wavy Pencil drag draws one open straight
/// segment from the press point to the release point, at the angle the pointer took.
#[test]
fn pencil_with_alt_draws_an_open_straight_line() {
let mut s = session();
let v = view();
let alt = Mods { alt: true, ..Default::default() };
s.select_tool("pencil", v).unwrap();
s.pointer(&PointerEvent::new(PointerKind::Down, 100.0, 400.0).with_mods(alt), v).unwrap();
for (x, y) in [(120.0, 380.0), (125.0, 330.0), (160.0, 320.0), (170.0, 333.0)] {
s.pointer(&PointerEvent::new(PointerKind::Drag, x, y).with_mods(alt), v).unwrap();
}
s.pointer(&PointerEvent::new(PointerKind::Up, 170.0, 333.0).with_mods(alt), v).unwrap();
let ps = paths(&s);
assert_eq!(ps.len(), 1);
assert!(!ps[0].1.is_closed());
let sp = &ps[0].1.subpaths[0];
assert_eq!(sp.anchors.len(), 2, "{sp:?}");
assert!(sp.anchors.iter().all(|a| a.h_in == a.p && a.h_out == a.p), "no handles: {sp:?}");
assert!(sp.anchors[0].p.distance(Point::new(100.0, 400.0)) < 1e-6, "{sp:?}");
assert!(sp.anchors[1].p.distance(Point::new(170.0, 333.0)) < 1e-6, "{sp:?}");
}
#[test]
fn pencil_continues_selected_open_path() {
let mut s = session();
+6 -3
View File
@@ -463,11 +463,11 @@ fn use_precise_cursors_swaps_the_tools_own_pointers_for_a_crosshair() {
}
}
/// With Alt held, the Pencil and the Paintbrush show their closing cursors, and Use Precise Cursors turns those into
/// a crosshair too.
/// With Alt held while drawing, the Pencil and the Paintbrush show their closing cursors, and Use Precise Cursors
/// turns those into a crosshair too.
#[test]
fn alt_shows_the_pencil_and_paintbrush_closing_cursors() {
use vectorcraft_tools::{Cursor, Mods};
use vectorcraft_tools::{Cursor, Mods, PointerEvent, PointerKind};
let mut s = Session::new();
s.execute("file.new", &json!({"width": 400, "height": 400})).unwrap();
let at = vectorcraft_geom::Point::new(300.0, 300.0);
@@ -476,7 +476,10 @@ fn alt_shows_the_pencil_and_paintbrush_closing_cursors() {
s.execute("prefs.set", &json!({"key": "usePreciseCursors", "value": precise})).unwrap();
for (tool, c) in [("pencil", Cursor::PencilClose), ("paintbrush", Cursor::PaintbrushClose)] {
s.select_tool(tool, ViewInfo::default()).unwrap();
s.pointer(&PointerEvent::new(PointerKind::Down, 250.0, 250.0), ViewInfo::default()).unwrap();
s.pointer(&PointerEvent::new(PointerKind::Drag, 300.0, 300.0), ViewInfo::default()).unwrap();
assert_eq!(s.cursor(at, alt, ViewInfo::default()), if precise { Cursor::Crosshair } else { c }, "{tool}, precise {precise}");
s.pointer(&PointerEvent::new(PointerKind::Up, 300.0, 300.0), ViewInfo::default()).unwrap();
}
}
}