mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-30 04:37:29 -06:00
Blend: Escape drops the Blend tool's first pick
After a click picked the first object with the Blend tool, Escape did nothing: the pick stayed, and the next click on another object blended the two. After a blend was made, Escape did not stop the next click adding to it either. Escape now drops the object picked first and stops the next click adding to the blend just made, as a click on empty canvas does, so the next click picks a new first object. The document and the selection stay as they are, and no undo step is added. Docs: docs/mcp.md, docs/target-app-parity.md.
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
//! - Blend: click object A, then object B → `object.blend.make {ids: [A, B]}`; clicking an anchor
|
||||
//! point of a path blends from that point (`starts`). Each further click adds the object as one
|
||||
//! more key of the new blend (as does clicking a blend first). Double-click or Alt-click opens
|
||||
//! Blend Options (dialog `blendOptions`). Clicking empty canvas starts over.
|
||||
//! Blend Options (dialog `blendOptions`). Clicking empty canvas or pressing Escape starts over.
|
||||
//! - Mesh: click inside a filled path → `object.mesh.create {ids, at}` (1×1 mesh plus lines through
|
||||
//! the click); click inside a mesh or a mesh envelope → `object.mesh.addLine` (a gradient mesh's
|
||||
//! new point takes the current fill color in its own model, unless Shift); drag a mesh point,
|
||||
@@ -136,6 +136,14 @@ impl Tool for BlendTool {
|
||||
fn busy(&self) -> bool {
|
||||
self.first.is_some()
|
||||
}
|
||||
/// Escape drops the object picked first and stops adding to the blend just made, so the next
|
||||
/// click picks a new first object. The document and the selection stay as they are.
|
||||
fn key(&mut self, _cx: &ToolContext, key: crate::ToolKey, _mods: Mods) -> Vec<Action> {
|
||||
if key == crate::ToolKey::Escape {
|
||||
(self.first, self.chained) = (None, false);
|
||||
}
|
||||
vec![]
|
||||
}
|
||||
fn deactivate(&mut self, _cx: &ToolContext) -> Vec<Action> {
|
||||
(self.first, self.chained) = (None, false);
|
||||
vec![]
|
||||
@@ -340,6 +348,49 @@ mod tests {
|
||||
assert!(t.first(&c).is_none());
|
||||
}
|
||||
|
||||
/// Escape drops the object picked first, and stops adding to the blend just made: the next
|
||||
/// click is a new first pick, and the click after it blends with that one.
|
||||
#[test]
|
||||
fn escape_drops_the_blend_tools_first_pick() {
|
||||
let (mut d, a) = doc_with_rect();
|
||||
let l = d.layers[0].id;
|
||||
let (b, g) = (d.alloc_id(), d.alloc_id());
|
||||
let r = vectorcraft_geom::shapes::rectangle(vectorcraft_geom::Rect::new(300.0, 100.0, 350.0, 150.0));
|
||||
d.insert(Some(l), 1, vectorcraft_doc::Node::path(b, r, vectorcraft_doc::Appearance::default_art())).unwrap();
|
||||
let (k1, k2) = (d.alloc_id(), d.alloc_id());
|
||||
let key = |id: NodeId, x: f64| {
|
||||
let r = vectorcraft_geom::shapes::rectangle(vectorcraft_geom::Rect::new(x, 300.0, x + 20.0, 320.0));
|
||||
std::sync::Arc::new(vectorcraft_doc::Node::path(id, r, vectorcraft_doc::Appearance::default_art()))
|
||||
};
|
||||
let blend = vectorcraft_doc::Node::new(g, NodeKind::Blend { children: vec![key(k1, 0.0), key(k2, 100.0)], spec: Default::default() });
|
||||
d.insert(Some(l), 2, blend).unwrap();
|
||||
let (s, p) = (Selection::default(), paint());
|
||||
let c = cx(&d, &s, &p);
|
||||
let picks = |id: NodeId| vec![Action::Exec("select.set".into(), json!({"ids": [id.0]}))];
|
||||
let down = |x: f64, y: f64| PointerEvent::new(PointerKind::Down, x, y);
|
||||
let esc = |t: &mut BlendTool| t.key(&c, crate::ToolKey::Escape, Mods::default());
|
||||
let mut t = BlendTool::default();
|
||||
assert_eq!(t.pointer(&c, &down(150.0, 150.0)), picks(a));
|
||||
assert!(t.busy() && !t.overlays(&c).is_empty());
|
||||
assert!(t.key(&c, crate::ToolKey::Enter, Mods::default()).is_empty());
|
||||
assert!(t.busy(), "Enter keeps the pick");
|
||||
assert!(esc(&mut t).is_empty(), "Escape changes nothing in the document");
|
||||
assert!(!t.busy() && t.first(&c).is_none() && t.overlays(&c).is_empty(), "Escape drops the pick");
|
||||
// The next click picks b first instead of blending a with it.
|
||||
assert_eq!(t.pointer(&c, &down(320.0, 120.0)), picks(b));
|
||||
assert_eq!(t.pointer(&c, &down(150.0, 150.0)), vec![Action::Exec("object.blend.make".into(), json!({"ids": [b.0, a.0]}))]);
|
||||
// After a blend is made, Escape stops the next click adding to it.
|
||||
let mut s = Selection::default();
|
||||
s.set([g]);
|
||||
let c = cx(&d, &s, &p);
|
||||
let mut t = BlendTool { first: None, chained: true };
|
||||
assert!(t.first(&c).is_some());
|
||||
assert!(t.key(&c, crate::ToolKey::Escape, Mods::default()).is_empty());
|
||||
assert!(t.first(&c).is_none(), "Escape ends adding to the blend just made");
|
||||
assert_eq!(t.pointer(&c, &down(150.0, 150.0)), picks(a));
|
||||
assert!(esc(&mut BlendTool::default()).is_empty(), "nothing to drop");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mesh_tool_click_path_creates_mesh() {
|
||||
let (d, a) = doc_with_rect();
|
||||
|
||||
Reference in New Issue
Block a user