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 },