Merge pull request #4447 from dotCooCoo/fix/blend-tool-replaces-stale-first-pick

Blend: a click picks again when the Blend tool's first pick is gone
This commit is contained in:
BFlat
2026-10-29 20:18:53 +07:00
committed by GitHub
2 changed files with 49 additions and 0 deletions
+17
View File
@@ -168,6 +168,23 @@ fn choosing_the_blend_tool_again_finishes_the_blend() {
assert_eq!(anchors, 3, "the Pen keeps drawing the same path");
}
#[test]
fn the_blend_tool_picks_again_when_its_first_pick_is_deleted() {
let mut s = session();
let (a, b, c) = (rect(&mut s, 0.0, 0.0, 40.0), rect(&mut s, 200.0, 0.0, 40.0), rect(&mut s, 400.0, 0.0, 40.0));
s.select_tool("blend", ViewInfo::default()).unwrap();
let click = |s: &mut Session, x: f64, y: f64| s.pointer(&PointerEvent::new(PointerKind::Down, x, y), ViewInfo::default()).unwrap();
click(&mut s, 20.0, 20.0);
s.execute("edit.clear", &json!({"ids": [a.0]})).unwrap();
let (layers, depth) = (s.doc().unwrap().doc.layers.clone(), s.doc().unwrap().history.undo.len());
click(&mut s, 220.0, 20.0);
assert_eq!(s.doc().unwrap().selection.objects, vec![b], "b is picked first");
assert_eq!((&s.doc().unwrap().doc.layers, s.doc().unwrap().history.undo.len()), (&layers, depth), "the click changes no art");
click(&mut s, 420.0, 20.0);
let g = s.doc().unwrap().selection.objects[0];
assert_eq!(spec(&s, g).0, vec![b, c]);
}
#[test]
fn the_blend_tool_blends_from_clicked_anchors_and_keeps_adding() {
let mut s = session();
+32
View File
@@ -88,6 +88,11 @@ impl Tool for BlendTool {
(t.first, t.chained) = (None, false);
vec![Action::Dialog(BLEND_OPTIONS.into(), json!({}))]
};
// A first pick whose object is no longer in the document (deleted, or undone away) is
// dropped, so the click picks the object it hits as the new first pick.
if self.first.is_some_and(|(a, _)| cx.doc.node(a).is_none()) {
(self.first, self.chained) = (None, false);
}
match ev.kind {
PointerKind::DoubleClick => options(self),
PointerKind::Down if ev.mods.alt => options(self),
@@ -341,6 +346,33 @@ mod tests {
assert!(create("mesh").unwrap().chosen_again(&c).is_empty());
}
#[test]
fn blend_tool_picks_again_when_its_first_pick_is_gone() {
let (mut d, a) = doc_with_rect();
let l = d.layers[0].id;
let (b, e) = (d.alloc_id(), d.alloc_id());
for (id, y) in [(b, 100.0), (e, 300.0)] {
let r = vectorcraft_geom::shapes::rectangle(vectorcraft_geom::Rect::new(300.0, y, 350.0, y + 50.0));
d.insert(Some(l), 1, vectorcraft_doc::Node::path(id, r, vectorcraft_doc::Appearance::default_art())).unwrap();
}
let (s, p) = (Selection::default(), paint());
let mut t = BlendTool::default();
t.pointer(&cx(&d, &s, &p), &PointerEvent::new(PointerKind::Down, 150.0, 150.0));
// The first pick is deleted (or undone away) while the tool waits for the second click.
d.remove(a).unwrap();
let c = cx(&d, &s, &p);
let acts = t.pointer(&c, &PointerEvent::new(PointerKind::Down, 320.0, 120.0));
assert_eq!(acts, vec![Action::Exec("select.set".into(), json!({"ids": [b.0]}))], "the click picks b first");
let acts = t.pointer(&c, &PointerEvent::new(PointerKind::Down, 320.0, 320.0));
assert_eq!(acts, vec![Action::Exec("object.blend.make".into(), json!({"ids": [b.0, e.0]}))]);
// After a blend is made, a click while the selected blend is gone picks the object it hits.
let mut s = Selection::default();
s.set([NodeId(9999)]);
let mut t = BlendTool { first: None, chained: true };
let acts = t.pointer(&cx(&d, &s, &p), &PointerEvent::new(PointerKind::Down, 320.0, 120.0));
assert_eq!(acts, vec![Action::Exec("select.set".into(), json!({"ids": [b.0]}))]);
}
#[test]
fn blend_tool_anchor_clicks_and_options() {
let (mut d, a) = doc_with_rect();