diff --git a/crates/engine/src/tests/blendopts.rs b/crates/engine/src/tests/blendopts.rs index b749962d5..e4d19be3b 100644 --- a/crates/engine/src/tests/blendopts.rs +++ b/crates/engine/src/tests/blendopts.rs @@ -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(); diff --git a/crates/tools/src/meshblend.rs b/crates/tools/src/meshblend.rs index de750d586..0ac424267 100644 --- a/crates/tools/src/meshblend.rs +++ b/crates/tools/src/meshblend.rs @@ -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();