diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index 48fa2e344..c43a5c3f6 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -1425,6 +1425,9 @@ pub struct Session { pub(crate) liquify_stroke: Option>, /// A press on the Plane Switching Widget is under way: its drag and release are the widget's. pub(crate) plane_widget_press: bool, + /// The pointer button is held: the last pointer event was a press or a drag, not a release or + /// a move ([`Session::pointer_held`]). + pub(crate) pointer_held: bool, /// A guide being dragged out of a ruler ([`Session::ruler_guide`]). pub(crate) ruler_guide: Option, /// Which selected type overflows its frame, for the markers every tool but the Type tools shows. @@ -1520,6 +1523,7 @@ impl Session { envelope_defaults: None, liquify_stroke: None, plane_widget_press: false, + pointer_held: false, ruler_guide: None, overflow_marks: Default::default(), font_search: None, diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index ed7cee619..f0c081ef6 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -825,6 +825,26 @@ fn selection_tool_drag_is_one_undo_step() { assert_eq!(s.doc().unwrap().doc.node(a).unwrap().geometric_bounds().unwrap().x0, 100.0); } +/// The pointer button is held from a press or a drag until a release or a move. The Dimension +/// tool stays busy after its first click with the button released, waiting for the second one. +#[test] +fn the_pointer_is_held_from_a_press_until_a_release_or_a_move() { + let mut s = session(); + let v = ViewInfo::default(); + s.select_tool("dimension", v).unwrap(); + let mut held = vec![]; + for (kind, x) in [(PointerKind::Down, 100.0), (PointerKind::Drag, 100.0), (PointerKind::Up, 100.0)] { + s.pointer(&PointerEvent::new(kind, x, 100.0), v).unwrap(); + held.push(s.pointer_held()); + } + assert!(s.tool_busy(), "the first point waits for the second"); + for (kind, x) in [(PointerKind::Move, 200.0), (PointerKind::Down, 300.0), (PointerKind::Drag, 310.0), (PointerKind::Move, 320.0)] { + s.pointer(&PointerEvent::new(kind, x, 100.0), v).unwrap(); + held.push(s.pointer_held()); + } + assert_eq!(held, [true, true, false, false, true, true, false]); +} + /// How many steps there are to undo and to redo, and whether the document has unsaved changes. pub(crate) fn history(s: &Session) -> (usize, usize, bool) { let st = s.doc().unwrap(); diff --git a/crates/engine/src/tooling.rs b/crates/engine/src/tooling.rs index e56934c14..c426b99a5 100644 --- a/crates/engine/src/tooling.rs +++ b/crates/engine/src/tooling.rs @@ -313,6 +313,11 @@ impl Session { /// Feed a pointer event to the active tool. Returns requests for the UI. pub fn pointer(&mut self, ev: &PointerEvent, view: ViewInfo) -> Result> { self.last_view = view; + match ev.kind { + PointerKind::Down | PointerKind::Drag => self.pointer_held = true, + PointerKind::Up | PointerKind::Move => self.pointer_held = false, + PointerKind::DoubleClick => {} + } // The Plane Switching Widget takes its clicks whatever the tool. if let Some(r) = self.plane_widget_pointer(ev, view) { return r; @@ -437,6 +442,13 @@ impl Session { self.tool.busy() } + /// Is the pointer button held: was the last pointer event a press or a drag, not a release or a + /// move? A tool that is busy while the button is not held is waiting between its clicks (the + /// Dimension tool after its first point). + pub fn pointer_held(&self) -> bool { + self.pointer_held + } + /// Is the active tool moving, scaling or rotating the selection with a drag? pub fn tool_transforming(&self) -> bool { self.tool.transforming() diff --git a/crates/ui-egui/src/opening.rs b/crates/ui-egui/src/opening.rs index 65db2c75e..37d5d85b1 100644 --- a/crates/ui-egui/src/opening.rs +++ b/crates/ui-egui/src/opening.rs @@ -82,10 +82,14 @@ pub fn start(app: &mut VectorcraftApp, path: &str, bytes: Vec) -> Result>(), ["photo.tif"]); assert_eq!(placed_in(1), [("a.png".to_string(), Some(vectorcraft_geom::Rect::new(20.0, 20.0, 60.0, 60.0)))], "the drag placed its file"); } + + /// A file read while a tool waits between its clicks (the Dimension tool after its first point), + /// in the file's document or in another one, is placed without waiting for the next click. In + /// the same document the tool keeps its point; in another one it starts afresh, as when the user + /// switches documents. + #[test] + fn a_file_read_between_a_tools_clicks_is_placed() { + use vectorcraft_tools::{PointerEvent, PointerKind}; + let path = saved("clicks", "photo.tif", &picture(Tiff, 60, 40)); + for other_document in [false, true] { + let mut app = app(true); + start(&mut app, &json!({"path": path, "at": [300, 200]}), false).unwrap(); + if other_document { + app.run("file.new", json!({"width": 400, "height": 300})).unwrap(); + } + app.select_tool("dimension"); + let view = vectorcraft_engine::ViewInfo::default(); + for kind in [PointerKind::Down, PointerKind::Up] { + crate::canvas::dispatch(&mut app, &PointerEvent::new(kind, 20.0, 20.0), view); + } + assert!(app.session.tool_busy() && !app.session.in_interaction(), "other document: {other_document}"); + placed(&mut app); + assert_eq!(names(&app), ["photo.tif"], "other document: {other_document}"); + assert_eq!(app.session.tool_busy(), !other_document, "other document: {other_document}"); + } + } }