From 52bebfad25d1a3b7b8558b4a3ca79b8e5f8e4041 Mon Sep 17 00:00:00 2001 From: Paul Kupper <11900073+pkupper@users.noreply.github.com> Date: Sun, 9 May 2021 10:26:47 +0200 Subject: [PATCH] Cancel in-progress drag with right click or escape key (#119) --- client/web/public/index.html | 2 +- client/web/src/main.ts | 2 + client/web/wasm/src/wrappers.rs | 1 + core/document/src/document.rs | 4 +- core/editor/src/dispatcher/events.rs | 1 + core/editor/src/tools/ellipse.rs | 13 +++--- core/editor/src/tools/line.rs | 12 +++--- core/editor/src/tools/pen.rs | 62 +++++++++++++++++----------- core/editor/src/tools/rectangle.rs | 10 +++-- core/editor/src/tools/shape.rs | 10 +++-- 10 files changed, 70 insertions(+), 47 deletions(-) diff --git a/client/web/public/index.html b/client/web/public/index.html index ab3e785f..af63f786 100644 --- a/client/web/public/index.html +++ b/client/web/public/index.html @@ -21,4 +21,4 @@
- \ No newline at end of file + diff --git a/client/web/src/main.ts b/client/web/src/main.ts index 60ab2236..d4785a3f 100644 --- a/client/web/src/main.ts +++ b/client/web/src/main.ts @@ -2,6 +2,8 @@ import { createApp } from "vue"; import App from "./App.vue"; import { attachResponseHandlerToPage } from "./response-handler"; +document.addEventListener("contextmenu", (e) => e.preventDefault()); + attachResponseHandlerToPage(); createApp(App).mount("#app"); diff --git a/client/web/wasm/src/wrappers.rs b/client/web/wasm/src/wrappers.rs index 00d5ef5f..47cd7494 100644 --- a/client/web/wasm/src/wrappers.rs +++ b/client/web/wasm/src/wrappers.rs @@ -111,6 +111,7 @@ pub fn translate_key(name: &str) -> events::Key { "Shift" => KeyShift, "Control" => KeyControl, "Alt" => KeyAlt, + "Escape" => KeyEscape, _ => UnknownKey, } } diff --git a/core/document/src/document.rs b/core/document/src/document.rs index e9ca769e..d5dcc4a2 100644 --- a/core/document/src/document.rs +++ b/core/document/src/document.rs @@ -274,12 +274,12 @@ impl Document { self.work_mount_path = vec![]; self.work = Folder::default(); self.work_mounted = false; - None + Some(vec![DocumentResponse::DocumentChanged]) } Operation::ClearWorkingFolder => { self.work_operations.clear(); self.work = Folder::default(); - None + Some(vec![DocumentResponse::DocumentChanged]) } Operation::CommitTransaction => { let mut ops = Vec::new(); diff --git a/core/editor/src/dispatcher/events.rs b/core/editor/src/dispatcher/events.rs index a29a6719..75405424 100644 --- a/core/editor/src/dispatcher/events.rs +++ b/core/editor/src/dispatcher/events.rs @@ -187,6 +187,7 @@ pub enum Key { KeyShift, KeyControl, KeyAlt, + KeyEscape, } bitflags! { diff --git a/core/editor/src/tools/ellipse.rs b/core/editor/src/tools/ellipse.rs index bb4a2ca3..d62277a1 100644 --- a/core/editor/src/tools/ellipse.rs +++ b/core/editor/src/tools/ellipse.rs @@ -53,14 +53,12 @@ impl Fsm for EllipseToolFsmState { operations.push(Operation::MountWorkingFolder { path: vec![] }); EllipseToolFsmState::LmbDown } - (EllipseToolFsmState::Ready, Event::KeyDown(Key::KeyZ)) => { if let Some(id) = document.root.list_layers().last() { operations.push(Operation::DeleteLayer { path: vec![*id] }) } EllipseToolFsmState::Ready } - (EllipseToolFsmState::LmbDown, Event::MouseMove(mouse_state)) => { data.drag_current = *mouse_state; @@ -69,11 +67,11 @@ impl Fsm for EllipseToolFsmState { EllipseToolFsmState::LmbDown } - (EllipseToolFsmState::LmbDown, Event::LmbUp(mouse_state)) => { data.drag_current = mouse_state.position; operations.push(Operation::ClearWorkingFolder); + // TODO - introduce comparison threshold when operating with canvas coordinates (https://github.com/GraphiteEditor/Graphite/issues/100) if data.drag_start != data.drag_current { operations.push(make_operation(data, tool_data)); operations.push(Operation::CommitTransaction); @@ -81,7 +79,12 @@ impl Fsm for EllipseToolFsmState { EllipseToolFsmState::Ready } + // TODO - simplify with or_patterns when rust 1.53.0 is stable (https://github.com/rust-lang/rust/issues/54883) + (EllipseToolFsmState::LmbDown, Event::KeyUp(Key::KeyEscape)) | (EllipseToolFsmState::LmbDown, Event::RmbDown(_)) => { + operations.push(Operation::DiscardWorkingFolder); + EllipseToolFsmState::Ready + } (state, Event::KeyDown(Key::KeyShift)) => { data.constrain_to_circle = true; @@ -92,7 +95,6 @@ impl Fsm for EllipseToolFsmState { self } - (state, Event::KeyUp(Key::KeyShift)) => { data.constrain_to_circle = false; @@ -103,7 +105,6 @@ impl Fsm for EllipseToolFsmState { self } - (state, Event::KeyDown(Key::KeyAlt)) => { data.center_around_cursor = true; @@ -114,7 +115,6 @@ impl Fsm for EllipseToolFsmState { self } - (state, Event::KeyUp(Key::KeyAlt)) => { data.center_around_cursor = false; @@ -125,7 +125,6 @@ impl Fsm for EllipseToolFsmState { self } - _ => self, } } diff --git a/core/editor/src/tools/line.rs b/core/editor/src/tools/line.rs index f9beca69..3c3a5241 100644 --- a/core/editor/src/tools/line.rs +++ b/core/editor/src/tools/line.rs @@ -78,6 +78,7 @@ impl Fsm for LineToolFsmState { data.drag_current = mouse_state.position; operations.push(Operation::ClearWorkingFolder); + // TODO - introduce comparison threshold when operating with canvas coordinates (https://github.com/GraphiteEditor/Graphite/issues/100) if data.drag_start != data.drag_current { operations.push(make_operation(data, tool_data)); operations.push(Operation::CommitTransaction); @@ -85,7 +86,12 @@ impl Fsm for LineToolFsmState { LineToolFsmState::Ready } + // TODO - simplify with or_patterns when rust 1.53.0 is stable (https://github.com/rust-lang/rust/issues/54883) + (LineToolFsmState::LmbDown, Event::KeyUp(Key::KeyEscape)) | (LineToolFsmState::LmbDown, Event::RmbDown(_)) => { + operations.push(Operation::DiscardWorkingFolder); + LineToolFsmState::Ready + } (state, Event::KeyDown(Key::KeyShift)) => { data.snap_angle = true; @@ -96,7 +102,6 @@ impl Fsm for LineToolFsmState { self } - (state, Event::KeyUp(Key::KeyShift)) => { data.snap_angle = false; @@ -107,7 +112,6 @@ impl Fsm for LineToolFsmState { self } - (state, Event::KeyDown(Key::KeyControl)) => { data.lock_angle = true; @@ -118,7 +122,6 @@ impl Fsm for LineToolFsmState { self } - (state, Event::KeyUp(Key::KeyControl)) => { data.lock_angle = false; @@ -129,7 +132,6 @@ impl Fsm for LineToolFsmState { self } - (state, Event::KeyDown(Key::KeyAlt)) => { data.center_around_cursor = true; @@ -140,7 +142,6 @@ impl Fsm for LineToolFsmState { self } - (state, Event::KeyUp(Key::KeyAlt)) => { data.center_around_cursor = false; @@ -151,7 +152,6 @@ impl Fsm for LineToolFsmState { self } - _ => self, } } diff --git a/core/editor/src/tools/pen.rs b/core/editor/src/tools/pen.rs index c21998ed..bcf687d1 100644 --- a/core/editor/src/tools/pen.rs +++ b/core/editor/src/tools/pen.rs @@ -38,59 +38,75 @@ impl Default for PenToolFsmState { #[derive(Clone, Debug, Default)] struct PenToolData { points: Vec, + next_point: ViewportPosition, } impl Fsm for PenToolFsmState { type ToolData = PenToolData; fn transition(self, event: &Event, document: &Document, tool_data: &DocumentToolData, data: &mut Self::ToolData, _responses: &mut Vec, operations: &mut Vec) -> Self { - let stroke = style::Stroke::new(tool_data.primary_color, 5.); - let fill = style::Fill::none(); - let style = style::PathStyle::new(Some(stroke), Some(fill)); - match (self, event) { (PenToolFsmState::Ready, Event::LmbDown(mouse_state)) => { operations.push(Operation::MountWorkingFolder { path: vec![] }); + data.points.push(mouse_state.position); + data.next_point = mouse_state.position; + PenToolFsmState::LmbDown } (PenToolFsmState::Ready, Event::KeyDown(Key::KeyZ)) => { if let Some(id) = document.root.list_layers().last() { operations.push(Operation::DeleteLayer { path: vec![*id] }) } + PenToolFsmState::Ready } (PenToolFsmState::LmbDown, Event::LmbUp(mouse_state)) => { - data.points.push(mouse_state.position); + // TODO - introduce comparison threshold when operating with canvas coordinates (https://github.com/GraphiteEditor/Graphite/issues/100) + if data.points.last() != Some(&mouse_state.position) { + data.points.push(mouse_state.position); + data.next_point = mouse_state.position; + } + PenToolFsmState::LmbDown } (PenToolFsmState::LmbDown, Event::MouseMove(mouse_state)) => { - let mut points: Vec<_> = data.points.iter().map(|p| (p.x as f64, p.y as f64)).collect(); - points.push((mouse_state.x as f64, mouse_state.y as f64)); + data.next_point = *mouse_state; operations.push(Operation::ClearWorkingFolder); - operations.push(Operation::AddPen { - path: vec![], - insert_index: -1, - points, - style, - }); + operations.push(make_operation(data, tool_data, true)); + PenToolFsmState::LmbDown } - (PenToolFsmState::LmbDown, Event::KeyDown(Key::KeyEnter)) => { - let points = data.points.drain(..).map(|p| (p.x as f64, p.y as f64)).collect(); + // TODO - simplify with or_patterns when rust 1.53.0 is stable (https://github.com/rust-lang/rust/issues/54883) + (PenToolFsmState::LmbDown, Event::KeyDown(Key::KeyEnter)) | (PenToolFsmState::LmbDown, Event::KeyDown(Key::KeyEscape)) | (PenToolFsmState::LmbDown, Event::RmbDown(_)) => { operations.push(Operation::ClearWorkingFolder); - operations.push(Operation::AddPen { - path: vec![], - insert_index: -1, - points, - style, - }); - operations.push(Operation::CommitTransaction); + + if data.points.len() >= 2 { + operations.push(make_operation(data, tool_data, false)); + operations.push(Operation::CommitTransaction); + } else { + operations.push(Operation::DiscardWorkingFolder); + } + + data.points.clear(); + PenToolFsmState::Ready } - _ => self, } } } + +fn make_operation(data: &PenToolData, tool_data: &DocumentToolData, show_preview: bool) -> Operation { + let mut points: Vec<(f64, f64)> = data.points.iter().map(|p| (p.x as f64, p.y as f64)).collect(); + if show_preview { + points.push((data.next_point.x as f64, data.next_point.y as f64)) + } + Operation::AddPen { + path: vec![], + insert_index: -1, + points, + style: style::PathStyle::new(Some(style::Stroke::new(tool_data.primary_color, 5.)), Some(style::Fill::none())), + } +} diff --git a/core/editor/src/tools/rectangle.rs b/core/editor/src/tools/rectangle.rs index 3be9b3b2..351183cb 100644 --- a/core/editor/src/tools/rectangle.rs +++ b/core/editor/src/tools/rectangle.rs @@ -71,6 +71,7 @@ impl Fsm for RectangleToolFsmState { data.drag_current = mouse_state.position; operations.push(Operation::ClearWorkingFolder); + // TODO - introduce comparison threshold when operating with canvas coordinates (https://github.com/GraphiteEditor/Graphite/issues/100) if data.drag_start != data.drag_current { operations.push(make_operation(data, tool_data)); operations.push(Operation::CommitTransaction); @@ -78,7 +79,12 @@ impl Fsm for RectangleToolFsmState { RectangleToolFsmState::Ready } + // TODO - simplify with or_patterns when rust 1.53.0 is stable (https://github.com/rust-lang/rust/issues/54883) + (RectangleToolFsmState::LmbDown, Event::KeyUp(Key::KeyEscape)) | (RectangleToolFsmState::LmbDown, Event::RmbDown(_)) => { + operations.push(Operation::DiscardWorkingFolder); + RectangleToolFsmState::Ready + } (state, Event::KeyDown(Key::KeyShift)) => { data.constrain_to_square = true; @@ -89,7 +95,6 @@ impl Fsm for RectangleToolFsmState { self } - (state, Event::KeyUp(Key::KeyShift)) => { data.constrain_to_square = false; @@ -100,7 +105,6 @@ impl Fsm for RectangleToolFsmState { self } - (state, Event::KeyDown(Key::KeyAlt)) => { data.center_around_cursor = true; @@ -111,7 +115,6 @@ impl Fsm for RectangleToolFsmState { self } - (state, Event::KeyUp(Key::KeyAlt)) => { data.center_around_cursor = false; @@ -122,7 +125,6 @@ impl Fsm for RectangleToolFsmState { self } - _ => self, } } diff --git a/core/editor/src/tools/shape.rs b/core/editor/src/tools/shape.rs index 4f6d232b..9dfda0d4 100644 --- a/core/editor/src/tools/shape.rs +++ b/core/editor/src/tools/shape.rs @@ -73,6 +73,7 @@ impl Fsm for ShapeToolFsmState { (ShapeToolFsmState::LmbDown, Event::LmbUp(mouse_state)) => { data.drag_current = mouse_state.position; operations.push(Operation::ClearWorkingFolder); + // TODO - introduce comparison threshold when operating with canvas coordinates (https://github.com/GraphiteEditor/Graphite/issues/100) if data.drag_start != data.drag_current { operations.push(make_operation(data, tool_data)); operations.push(Operation::CommitTransaction); @@ -80,7 +81,12 @@ impl Fsm for ShapeToolFsmState { ShapeToolFsmState::Ready } + // TODO - simplify with or_patterns when rust 1.53.0 is stable (https://github.com/rust-lang/rust/issues/54883) + (ShapeToolFsmState::LmbDown, Event::KeyUp(Key::KeyEscape)) | (ShapeToolFsmState::LmbDown, Event::RmbDown(_)) => { + operations.push(Operation::DiscardWorkingFolder); + ShapeToolFsmState::Ready + } (state, Event::KeyDown(Key::KeyShift)) => { data.constrain_to_square = true; @@ -91,7 +97,6 @@ impl Fsm for ShapeToolFsmState { self } - (state, Event::KeyUp(Key::KeyShift)) => { data.constrain_to_square = false; @@ -102,7 +107,6 @@ impl Fsm for ShapeToolFsmState { self } - (state, Event::KeyDown(Key::KeyAlt)) => { data.center_around_cursor = true; @@ -113,7 +117,6 @@ impl Fsm for ShapeToolFsmState { self } - (state, Event::KeyUp(Key::KeyAlt)) => { data.center_around_cursor = false; @@ -124,7 +127,6 @@ impl Fsm for ShapeToolFsmState { self } - _ => self, } }