Pencil: the continuation cursor follows the snapped release; Precise Cursors tests keep the badges

This commit is contained in:
ncic
2026-11-14 09:22:18 +01:00
parent cbd1c142dc
commit 77070d29a7
3 changed files with 53 additions and 7 deletions
+7 -5
View File
@@ -723,8 +723,10 @@ fn use_precise_cursors_swaps_the_tools_own_pointers_for_a_crosshair() {
assert_eq!(cursor(&mut s, tool), Cursor::Crosshair, "{tool} off");
}
s.execute("prefs.set", &json!({"key": "usePreciseCursors", "value": true})).unwrap();
for (tool, _) in own {
assert_eq!(cursor(&mut s, tool), Cursor::Crosshair, "{tool} on");
for (tool, c) in own {
// The Pencil's star is drawn on the crosshair and keeps its badge.
let want = if tool == "pencil" { c } else { Cursor::Crosshair };
assert_eq!(cursor(&mut s, tool), want, "{tool} on");
}
for tool in always {
assert_eq!(cursor(&mut s, tool), Cursor::Crosshair, "{tool} on");
@@ -758,8 +760,8 @@ fn use_precise_cursors_keeps_the_smooth_tools_not_allowed_pointer() {
}
}
/// With Alt held while drawing, 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. Use Precise Cursors keeps
/// them: they are drawn on the crosshair, and the ring says the release closes the path.
#[test]
fn alt_shows_the_pencil_and_paintbrush_closing_cursors() {
use vectorcraft_tools::{Cursor, Mods, PointerEvent, PointerKind};
@@ -773,7 +775,7 @@ fn alt_shows_the_pencil_and_paintbrush_closing_cursors() {
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}");
assert_eq!(s.cursor(at, alt, ViewInfo::default()), c, "{tool}, precise {precise}");
s.pointer(&PointerEvent::new(PointerKind::Up, 300.0, 300.0), ViewInfo::default()).unwrap();
}
}
+12 -2
View File
@@ -98,6 +98,9 @@ pub struct GestureTool {
/// A release where the pointer last dragged to would close the stroke because it ends near
/// its start (Close Paths When Ends Are Within): the cursor shows the closing ring.
near_start: bool,
/// A release where the pointer last dragged to would connect the continued path to another
/// selected path's open end (`finish`): the cursor shows the continuation cursor.
connects: bool,
/// Pencil/Paintbrush continuing a selected open path: (path, continue from its start).
extend: Option<(NodeId, bool)>,
/// The other end of the path `extend` continues: a release near it closes the path.
@@ -819,6 +822,7 @@ impl GestureTool {
active: false,
began: false,
near_start: false,
connects: false,
extend: None,
close_to: None,
connect_to: vec![],
@@ -1303,6 +1307,7 @@ impl Tool for GestureTool {
self.active = true;
self.began = false;
self.near_start = false;
self.connects = false;
// Alt switches the Pencil when its toggle is on, the Blob Brush always, and the
// Paintbrush when the press has a selected path.
self.smoothing = ev.mods.alt
@@ -1380,10 +1385,12 @@ impl Tool for GestureTool {
let kept = self.points.last().copied().filter(|l| l.distance(p) < cx.tol(1.0) && !self.snaps());
let release_end = kept.unwrap_or(end);
self.near_start = self.release_closes_near_start(cx, release_end);
self.connects = self.connects_at(cx, release_end).is_some();
return vec![];
}
self.push(p, ev);
self.near_start = self.release_closes_near_start(cx, end);
self.connects = self.connects_at(cx, end).is_some();
if !self.draws_path() || self.smoothing {
return vec![];
}
@@ -1444,6 +1451,7 @@ impl Tool for GestureTool {
self.active = false;
self.began = false;
self.near_start = false;
self.connects = false;
self.smoothing = false;
self.straight = false;
self.unconstrained = false;
@@ -1479,6 +1487,7 @@ impl Tool for GestureTool {
self.active = false;
self.began = false;
self.near_start = false;
self.connects = false;
self.smoothing = false;
self.straight = false;
self.unconstrained = false;
@@ -1509,6 +1518,7 @@ impl Tool for GestureTool {
self.snap.clear();
self.active = false;
self.near_start = false;
self.connects = false;
self.began = false;
self.smoothing = false;
self.straight = false;
@@ -1597,8 +1607,8 @@ impl Tool for GestureTool {
return if self.find_extend(cx, p).is_some() { Cursor::PencilContinue } else { Cursor::PencilStraight };
}
// A continuing Pencil stroke that a release would connect to another selected path's open
// end (`finish`) shows that it continues on.
if self.id == "pencil" && self.active && self.points.last().is_some_and(|&end| self.connects_at(cx, end).is_some()) {
// end (`finish`) shows that it continues on; `connects` tests the point the release keeps.
if self.id == "pencil" && self.active && self.connects {
return Cursor::PencilContinue;
}
// Alt held at the release closes the path (`finish`).
+34
View File
@@ -959,6 +959,40 @@ fn a_continued_stroke_shows_where_it_connects_or_closes() {
assert_eq!((shown, v["closed"].clone()), (Cursor::Pencil, json!(false)), "at 0 px");
}
/// The continuation cursor tests where the release ends, after snapping, not the pointer: here
/// Snap to Grid moves a stroke's end from 13 pt away from another selected path's end (outside Edit
/// Selected Paths' 12 px) to 9 pt away, so the release connects, and the cursor says so.
#[test]
fn the_continuation_cursor_follows_the_snapped_release() {
let (mut d, _) = doc_with_rect();
let l = d.layers[0].id;
let add = |d: &mut vectorcraft_doc::Document, a: (f64, f64), b: (f64, f64)| {
let id = d.alloc_id();
let els = vec![vectorcraft_geom::PathEl::MoveTo(Point::new(a.0, a.1)), vectorcraft_geom::PathEl::LineTo(Point::new(b.0, b.1))];
let pd = vectorcraft_geom::PathData::from_bezpath(&vectorcraft_geom::BezPath::from_vec(els));
d.insert(Some(l), 1, vectorcraft_doc::Node::path(id, pd, vectorcraft_doc::Appearance::default_art())).unwrap();
id
};
// On the 9 pt grid.
let a = add(&mut d, (306.0, 306.0), (405.0, 405.0));
let b = add(&mut d, (504.0, 504.0), (603.0, 504.0));
let p = paint();
let mut both = Selection::default();
both.set([a, b]);
let mut cx = cx(&d, &both, &p);
cx.smart_guides = false;
cx.snap_to_point = false;
cx.snap_to_grid = true;
let mut t = GestureTool::new("pencil");
t.pointer(&cx, &PointerEvent::new(PointerKind::Down, 405.0, 405.0));
t.pointer(&cx, &PointerEvent::new(PointerKind::Drag, 460.0, 450.0));
t.pointer(&cx, &PointerEvent::new(PointerKind::Drag, 517.0, 504.0));
assert_eq!(t.cursor(&cx, Point::new(517.0, 504.0), Mods::default()), Cursor::PencilContinue);
let acts = t.pointer(&cx, &PointerEvent::new(PointerKind::Up, 517.0, 504.0));
let Some(Action::Preview(_, v)) = acts.first() else { panic!("{acts:?}") };
assert_eq!(v["connect"], json!({"id": b.0, "end": "start"}), "{v}");
}
/// #3846: Alt during a Shift line doesn't promise a close the release can't do, and with Alt
/// Toggles to Smooth, Alt pressed after the press doesn't turn the line into a smoothing drag.
#[test]