From 5c13279d91ff4c00856bd588c4dcadf881ea8ce6 Mon Sep 17 00:00:00 2001 From: T0mstone <39707032+T0mstone@users.noreply.github.com> Date: Sun, 28 Mar 2021 23:39:33 +0200 Subject: [PATCH] Add plumbing for event system (#52) * Add plumbing for event system * Apply review suggestions * Add swap and reset color functions --- Cargo.lock | 33 ++++ client/web/package-lock.json | 178 +++++++++--------- .../src/components/panels/ViewportPanel.vue | 29 ++- client/web/wasm/src/viewport.rs | 77 +++++--- core/editor/Cargo.toml | 5 + core/editor/src/dispatcher/events.rs | 138 +++++++------- core/editor/src/dispatcher/mod.rs | 62 +++++- core/editor/src/lib.rs | 4 +- core/editor/src/tools/mod.rs | 36 +++- 9 files changed, 352 insertions(+), 210 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2aef4a0a..dc2858c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,17 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + [[package]] name = "bumpalo" version = "3.6.1" @@ -35,11 +47,17 @@ version = "0.1.0" [[package]] name = "graphite-document-core" version = "0.1.0" +dependencies = [ + "kurbo", + "svg_fmt", +] [[package]] name = "graphite-editor-core" version = "0.1.0" dependencies = [ + "bitflags", + "graphite-document-core", "log", ] @@ -66,6 +84,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kurbo" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb348d766edbac91ba1eb83020d96f4f8867924d194393083c15a51f185e6a82" +dependencies = [ + "arrayvec", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -105,6 +132,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" +[[package]] +name = "svg_fmt" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2" + [[package]] name = "syn" version = "1.0.64" diff --git a/client/web/package-lock.json b/client/web/package-lock.json index ad676ceb..81870b7a 100644 --- a/client/web/package-lock.json +++ b/client/web/package-lock.json @@ -605,6 +605,95 @@ "tslint": "^5.20.1", "webpack": "^4.0.0", "yorkie": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "optional": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "optional": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "optional": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "optional": true + }, + "fork-ts-checker-webpack-plugin-v5": { + "version": "npm:fork-ts-checker-webpack-plugin@5.2.1", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.1.tgz", + "integrity": "sha512-SVi+ZAQOGbtAsUWrZvGzz38ga2YqjWvca1pXQFUArIVXqli0lLoDQ8uS0wg0kSpcwpZmaW5jVCZXQebkyUQSsw==", + "dev": true, + "optional": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "optional": true + }, + "schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "dev": true, + "optional": true, + "requires": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "optional": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "@vue/cli-plugin-vuex": { @@ -5386,95 +5475,6 @@ } } }, - "fork-ts-checker-webpack-plugin-v5": { - "version": "npm:fork-ts-checker-webpack-plugin@5.2.1", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.2.1.tgz", - "integrity": "sha512-SVi+ZAQOGbtAsUWrZvGzz38ga2YqjWvca1pXQFUArIVXqli0lLoDQ8uS0wg0kSpcwpZmaW5jVCZXQebkyUQSsw==", - "dev": true, - "optional": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@types/json-schema": "^7.0.5", - "chalk": "^4.1.0", - "cosmiconfig": "^6.0.0", - "deepmerge": "^4.2.2", - "fs-extra": "^9.0.0", - "memfs": "^3.1.2", - "minimatch": "^3.0.4", - "schema-utils": "2.7.0", - "semver": "^7.3.2", - "tapable": "^1.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "optional": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "optional": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "optional": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "optional": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "optional": true - }, - "schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "dev": true, - "optional": true, - "requires": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "optional": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", diff --git a/client/web/src/components/panels/ViewportPanel.vue b/client/web/src/components/panels/ViewportPanel.vue index 34358d60..13907a4b 100644 --- a/client/web/src/components/panels/ViewportPanel.vue +++ b/client/web/src/components/panels/ViewportPanel.vue @@ -12,7 +12,12 @@ - + @@ -57,7 +62,7 @@ flex: 0 0 32px; } - .viewport-container { + .canvas { background: #111; flex: 1 1 100%; } @@ -78,11 +83,21 @@ export default defineComponent({ LayoutCol, }, methods: { - async canvasClick(e: MouseEvent) { - console.log(e); - const { on_mouse_click } = await wasm; - on_mouse_click(e.offsetX, e.offsetY); - } + async canvasMouseDown(e: MouseEvent) { + console.log(e); + const { on_mouse_down } = await wasm; + on_mouse_down(e.offsetX, e.offsetY, e.buttons); + }, + async canvasMouseUp(e: MouseEvent) { + console.log(e); + const { on_mouse_up } = await wasm; + on_mouse_up(e.offsetX, e.offsetY, e.buttons); + }, + async canvasMouseMove(e: MouseEvent) { + console.log(e); + const { on_mouse_move } = await wasm; + on_mouse_move(e.offsetX, e.offsetY); + }, }, }); diff --git a/client/web/wasm/src/viewport.rs b/client/web/wasm/src/viewport.rs index ef0e509f..6dcb50e4 100644 --- a/client/web/wasm/src/viewport.rs +++ b/client/web/wasm/src/viewport.rs @@ -8,51 +8,68 @@ use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn select_tool(tool: String) -> Result<(), JsValue> { EDITOR_STATE.with(|editor| match translate_tool(&tool) { - Some(tool) => { - editor.borrow_mut().tools.active_tool = tool; - Ok(()) - } + Some(tool) => editor.borrow_mut().handle_event(events::Event::SelectTool(tool)).map_err(|err| Error::new(&err.to_string()).into()), None => Err(Error::new(&format!("Couldn't select {} because it was not recognized as a valid tool", tool)).into()), }) } /// Mouse movement with the bounds of the canvas #[wasm_bindgen] -pub fn on_mouse_move(x: u32, y: u32) { - EDITOR_STATE.with(|editor| { - let mut editor = editor.borrow_mut(); - if editor.tools.mouse_is_clicked { - editor.tools.trace.append_point(x, y) - } - }) +pub fn on_mouse_move(x: u32, y: u32) -> Result<(), JsValue> { + let ev = events::Event::MouseMovement(events::CanvasPosition { x, y }); + EDITOR_STATE.with(|editor| editor.borrow_mut().handle_event(ev)).map_err(|err| Error::new(&err.to_string()).into()) } /// Mouse click within the bounds of the canvas #[wasm_bindgen] -pub fn on_mouse_click(x: u32, y: u32) -> Result<(), JsValue> { - let ev = events::Event::Click(events::MouseState::from_pos(x, y)); - EDITOR_STATE - .with(|editor| { - let mut editor = editor.borrow_mut(); - editor.tools.mouse_is_clicked = true; - editor.tools.trace.clear(); - editor.handle_event(ev) - }) - .map_err(|err| Error::new(&err.to_string()).into()) +pub fn on_mouse_down(x: u32, y: u32, mouse_keys: u8) -> Result<(), JsValue> { + let mouse_keys = events::MouseKeys::from_bits(mouse_keys).expect("invalid modifier keys"); + let ev = events::Event::MouseDown(events::MouseState { + position: events::CanvasPosition { x, y }, + mouse_keys, + }); + EDITOR_STATE.with(|editor| editor.borrow_mut().handle_event(ev)).map_err(|err| Error::new(&err.to_string()).into()) } /// Mouse released #[wasm_bindgen] -pub fn on_mouse_release() { - EDITOR_STATE.with(|editor| editor.borrow_mut().tools.mouse_is_clicked = false) +pub fn on_mouse_up(x: u32, y: u32, mouse_keys: u8) -> Result<(), JsValue> { + let mouse_keys = events::MouseKeys::from_bits(mouse_keys).expect("invalid modifier keys"); + let ev = events::Event::MouseDown(events::MouseState { + position: events::CanvasPosition { x, y }, + mouse_keys, + }); + EDITOR_STATE.with(|editor| editor.borrow_mut().handle_event(ev)).map_err(|err| Error::new(&err.to_string()).into()) } -/// Update working colors +/// Update primary color #[wasm_bindgen] -pub fn update_colors(primary_color: Color, secondary_color: Color) { - EDITOR_STATE.with(|editor| { - let mut editor = editor.borrow_mut(); - editor.tools.primary_color = primary_color.inner(); - editor.tools.secondary_color = secondary_color.inner(); - }) +pub fn update_primary_color(primary_color: Color) -> Result<(), JsValue> { + EDITOR_STATE + .with(|editor| editor.borrow_mut().handle_event(events::Event::SelectPrimaryColor(primary_color.inner()))) + .map_err(|err: editor_core::EditorError| Error::new(&err.to_string()).into()) +} + +/// Update secondary color +#[wasm_bindgen] +pub fn update_secondary_color(secondary_color: Color) -> Result<(), JsValue> { + EDITOR_STATE + .with(|editor| editor.borrow_mut().handle_event(events::Event::SelectSecondaryColor(secondary_color.inner()))) + .map_err(|err: editor_core::EditorError| Error::new(&err.to_string()).into()) +} + +/// Swap primary and secondary color +#[wasm_bindgen] +pub fn swap_colors() -> Result<(), JsValue> { + EDITOR_STATE + .with(|editor| editor.borrow_mut().handle_event(events::Event::SwapColors)) + .map_err(|err: editor_core::EditorError| Error::new(&err.to_string()).into()) +} + +/// Reset primary and secondary colors to their defaults +#[wasm_bindgen] +pub fn reset_colors() -> Result<(), JsValue> { + EDITOR_STATE + .with(|editor| editor.borrow_mut().handle_event(events::Event::ResetColors)) + .map_err(|err: editor_core::EditorError| Error::new(&err.to_string()).into()) } diff --git a/core/editor/Cargo.toml b/core/editor/Cargo.toml index 25e8a4b9..2d741290 100644 --- a/core/editor/Cargo.toml +++ b/core/editor/Cargo.toml @@ -10,3 +10,8 @@ license = "Apache-2.0" [dependencies] log = "0.4" +bitflags = "1.2.1" + +[dependencies.document-core] +path = "../document" +package = "graphite-document-core" diff --git a/core/editor/src/dispatcher/events.rs b/core/editor/src/dispatcher/events.rs index ef152146..08459cc1 100644 --- a/core/editor/src/dispatcher/events.rs +++ b/core/editor/src/dispatcher/events.rs @@ -1,12 +1,21 @@ use crate::tools::ToolType; +use crate::Color; +use bitflags::bitflags; +use std::ops::{Deref, DerefMut}; + #[derive(Debug, Clone)] #[repr(C)] pub enum Event { SelectTool(ToolType), - ModifierKeyDown(ModKey), - ModifierKeyUp(ModKey), - MouseMovement(Trace), - Click(MouseState), + SelectPrimaryColor(Color), + SelectSecondaryColor(Color), + SwapColors, + ResetColors, + MouseDown(MouseState), + MouseUp(MouseState), + MouseMovement(CanvasPosition), + ModifierKeyDown(ModKeys), + ModifierKeyUp(ModKeys), KeyPress(Key), } @@ -17,92 +26,81 @@ pub enum Response { } #[derive(Debug, Clone, Default)] -pub struct Trace(Vec); +pub struct Trace(Vec); + +impl Deref for Trace { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Trace { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} impl Trace { pub fn new() -> Self { Self::default() } - pub fn first_point(&self) -> Option<&MouseState> { - self.0.first() - } - pub fn last_point(&self) -> Option<&MouseState> { - self.0.last() - } - pub fn append_point(&mut self, x: u32, y: u32) { - self.0.push(MouseState::from_pos(x, y)) - } - pub fn clear(&mut self) { - self.0.clear() - } } -#[derive(Debug, Clone, Default)] + +// origin is top left +#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)] +pub struct CanvasPosition { + pub x: u32, + pub y: u32, +} + +#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)] +pub struct TracePoint { + pub mouse_state: MouseState, + pub mod_keys: ModKeys, +} + +#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)] pub struct MouseState { - x: u32, - y: u32, - mod_keys: ModKeysStorage, - mouse_keys: MouseKeysStorage, + pub position: CanvasPosition, + pub mouse_keys: MouseKeys, } impl MouseState { - pub const fn new() -> MouseState { + pub fn new() -> MouseState { + Self::default() + } + + pub fn from_pos(x: u32, y: u32) -> MouseState { MouseState { - x: 0, - y: 0, - mod_keys: 0, - mouse_keys: 0, + position: CanvasPosition { x, y }, + mouse_keys: MouseKeys::default(), } } - pub const fn from_pos(x: u32, y: u32) -> MouseState { - MouseState { x, y, mod_keys: 0, mouse_keys: 0 } - } } -#[derive(Debug, Clone)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Key { - None, + UnknownKey, } -pub type ModKeysStorage = u8; -pub type MouseKeysStorage = u8; -#[derive(Debug, Clone, Copy)] -#[repr(transparent)] -pub struct ModKeys(ModKeysStorage); - -impl ModKeys { - pub fn get_key(&self, key: ModKey) -> bool { - key as ModKeysStorage & self.0 > 0 - } - pub fn set_key(&mut self, key: ModKey) { - self.0 |= key as ModKeysStorage +bitflags! { + #[derive(Default)] + #[repr(transparent)] + pub struct ModKeys: u8 { + const CONTROL = 0b0000_0001; + const SHIFT = 0b0000_0010; + const ALT = 0b0000_0100; } } -#[derive(Debug, Clone, Copy)] -#[repr(transparent)] -struct MouseKeys(u8); - -impl MouseKeys { - pub fn get_key(&self, key: MouseKey) -> bool { - key as ModKeysStorage & self.0 > 0 - } - pub fn set_key(&mut self, key: MouseKey) { - self.0 |= key as MouseKeysStorage +bitflags! { + #[derive(Default)] + #[repr(transparent)] + pub struct MouseKeys: u8 { + const LEFT = 0b0000_0001; + const RIGHT = 0b0000_0010; + const MIDDLE = 0b0000_0100; } } - -#[repr(u8)] -#[derive(Debug, Clone)] -pub enum ModKey { - Control = 1, - Shift = 2, - Alt = 4, -} - -#[repr(u8)] -#[derive(Debug, Clone)] -pub enum MouseKey { - LeftMouse = 1, - RightMouse = 2, - MiddleMouse = 4, -} diff --git a/core/editor/src/dispatcher/mod.rs b/core/editor/src/dispatcher/mod.rs index 42208860..1c787953 100644 --- a/core/editor/src/dispatcher/mod.rs +++ b/core/editor/src/dispatcher/mod.rs @@ -1,5 +1,6 @@ pub mod events; -use crate::EditorError; +use crate::tools::ToolState; +use crate::{Color, EditorError}; use events::{Event, Response}; pub type Callback = Box; @@ -8,10 +9,63 @@ pub struct Dispatcher { } impl Dispatcher { - pub fn handle_event(&self, event: Event) -> Result<(), EditorError> { + pub fn handle_event(&self, tool_state: &mut ToolState, event: Event) -> Result<(), EditorError> { match event { - Event::Click(_) => Ok(self.emit_response(Response::UpdateCanvas)), - _ => todo!(), + Event::SelectTool(tool_type) => { + tool_state.active_tool = tool_type; + Ok(()) + } + Event::SelectPrimaryColor(color) => { + tool_state.primary_color = color; + Ok(()) + } + Event::SelectSecondaryColor(color) => { + tool_state.secondary_color = color; + Ok(()) + } + Event::SwapColors => { + std::mem::swap(&mut tool_state.primary_color, &mut tool_state.secondary_color); + Ok(()) + } + Event::ResetColors => { + tool_state.primary_color = Color::BLACK; + tool_state.secondary_color = Color::WHITE; + Ok(()) + } + Event::MouseDown(mouse_state) => { + tool_state.mouse_state = mouse_state; + // the state has changed so we add a trace point + tool_state.record_trace_point(); + + self.emit_response(Response::UpdateCanvas); + Ok(()) + } + Event::MouseUp(mouse_state) => { + tool_state.mouse_state = mouse_state; + // the state has changed so we add a trace point + tool_state.record_trace_point(); + + self.emit_response(Response::UpdateCanvas); + Ok(()) + } + Event::MouseMovement(pos) => { + tool_state.mouse_state.position = pos; + tool_state.record_trace_point(); + Ok(()) + } + Event::ModifierKeyDown(mod_keys) => { + tool_state.mod_keys = mod_keys; + // the state has changed so we add a trace point + tool_state.record_trace_point(); + Ok(()) + } + Event::ModifierKeyUp(mod_keys) => { + tool_state.mod_keys = mod_keys; + // the state has changed so we add a trace point + tool_state.record_trace_point(); + Ok(()) + } + Event::KeyPress(key) => todo!(), } } pub fn emit_response(&self, response: Response) { diff --git a/core/editor/src/lib.rs b/core/editor/src/lib.rs index 9d37f08c..00f4daad 100644 --- a/core/editor/src/lib.rs +++ b/core/editor/src/lib.rs @@ -22,7 +22,7 @@ use workspace::Workspace; // TODO: serialize with serde to save the current editor state pub struct Editor { - pub tools: ToolState, + tools: ToolState, workspace: Workspace, dispatcher: Dispatcher, } @@ -36,6 +36,6 @@ impl Editor { } } pub fn handle_event(&mut self, event: events::Event) -> Result<(), EditorError> { - self.dispatcher.handle_event(event) + self.dispatcher.handle_event(&mut self.tools, event) } } diff --git a/core/editor/src/tools/mod.rs b/core/editor/src/tools/mod.rs index d9650e4b..3257a433 100644 --- a/core/editor/src/tools/mod.rs +++ b/core/editor/src/tools/mod.rs @@ -1,8 +1,10 @@ +use crate::events::{ModKeys, MouseState, TracePoint}; use crate::{events::Trace, Color}; use std::collections::HashMap; pub struct ToolState { - pub mouse_is_clicked: bool, + pub mouse_state: MouseState, + pub mod_keys: ModKeys, pub trace: Trace, pub primary_color: Color, pub secondary_color: Color, @@ -10,10 +12,11 @@ pub struct ToolState { tool_settings: HashMap, } -impl ToolState { - pub fn new() -> Self { +impl Default for ToolState { + fn default() -> Self { ToolState { - mouse_is_clicked: false, + mouse_state: MouseState::default(), + mod_keys: ModKeys::default(), trace: Trace::new(), primary_color: Color::BLACK, secondary_color: Color::WHITE, @@ -23,14 +26,29 @@ impl ToolState { } } +impl ToolState { + pub fn new() -> Self { + Self::default() + } + + pub fn record_trace_point(&mut self) { + self.trace.push(TracePoint { + mouse_state: self.mouse_state, + mod_keys: self.mod_keys, + }) + } +} + fn default_tool_settings() -> HashMap { let tool_init = |tool: &ToolType| (*tool, tool.default_settings()); + // TODO: when 1.51 is more common, change this to use array::IntoIter [ tool_init(&ToolType::Select), + tool_init(&ToolType::Ellipse), tool_init(&ToolType::Shape), // TODO: Add more tool defaults ] .iter() - .cloned() + .copied() .collect() } @@ -54,6 +72,7 @@ impl ToolType { fn default_settings(&self) -> ToolSettings { match self { ToolType::Select => ToolSettings::Select { append_mode: SelectAppendMode::New }, + ToolType::Ellipse => ToolSettings::Ellipse, ToolType::Shape => ToolSettings::Shape { shape: Shape::Polygon { vertices: 3 }, }, @@ -62,13 +81,14 @@ impl ToolType { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum ToolSettings { Select { append_mode: SelectAppendMode }, + Ellipse, Shape { shape: Shape }, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum SelectAppendMode { New, Add, @@ -76,7 +96,7 @@ pub enum SelectAppendMode { Intersect, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum Shape { Star { vertices: u32 }, Polygon { vertices: u32 },