diff --git a/Cargo.toml b/Cargo.toml index f18edc5ac..2efbb6c22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,5 +5,5 @@ members = [ "client/web/wasm", ] -[profile.release.package.wasm-wrapper] +[profile.release.package.graphite-wasm-wrapper] opt-level = "s" diff --git a/core/document/src/lib.rs b/core/document/src/lib.rs index de74d511d..43b99a254 100644 --- a/core/document/src/lib.rs +++ b/core/document/src/lib.rs @@ -1,4 +1,7 @@ +pub mod operation; + pub use kurbo::{Circle, Point}; +pub use operation::Operation; #[derive(Debug, Clone, PartialEq)] pub enum SvgElement { diff --git a/core/document/src/operation.rs b/core/document/src/operation.rs new file mode 100644 index 000000000..b2f818805 --- /dev/null +++ b/core/document/src/operation.rs @@ -0,0 +1,3 @@ +pub enum Operation { + AddCircle((f64, f64), f64), +} diff --git a/core/editor/src/dispatcher/mod.rs b/core/editor/src/dispatcher/mod.rs index f4adc089d..3b50102ca 100644 --- a/core/editor/src/dispatcher/mod.rs +++ b/core/editor/src/dispatcher/mod.rs @@ -11,40 +11,42 @@ pub struct Dispatcher { impl Dispatcher { pub fn handle_event(&self, state: &mut EditorState, event: Event) -> Result<(), EditorError> { log::trace!("{:?}", event); + match event { Event::SelectTool(tool_type) => { - state.tools.active_tool = tool_type; + state.tool_state.active_tool_type = tool_type; + Ok(()) } Event::SelectPrimaryColor(color) => { - state.tools.primary_color = color; + state.tool_state.primary_color = color; + Ok(()) } Event::SelectSecondaryColor(color) => { - state.tools.secondary_color = color; + state.tool_state.secondary_color = color; + Ok(()) } Event::SwapColors => { - std::mem::swap(&mut state.tools.primary_color, &mut state.tools.secondary_color); + std::mem::swap(&mut state.tool_state.primary_color, &mut state.tool_state.secondary_color); + Ok(()) } Event::ResetColors => { - state.tools.primary_color = Color::BLACK; - state.tools.secondary_color = Color::WHITE; + state.tool_state.primary_color = Color::BLACK; + state.tool_state.secondary_color = Color::WHITE; + Ok(()) } Event::MouseDown(mouse_state) => { - state.tools.mouse_state = mouse_state; - // the state has changed so we add a trace point - state.tools.record_trace_point(); + state.tool_state.mouse_state = mouse_state; + state.tool_state.active_tool()?.handle_input(event); - // self.emit_response(Response::UpdateCanvas { document: state.document.render() }); Ok(()) } Event::MouseUp(mouse_state) => { - state.tools.mouse_state = mouse_state; - // the state has changed so we add a trace point - state.tools.record_trace_point(); + state.tool_state.mouse_state = mouse_state; state.document.svg.push(SvgElement::Circle(Circle { center: Point { @@ -53,34 +55,39 @@ impl Dispatcher { }, radius: 10.0, })); - self.emit_response(Response::UpdateCanvas { document: state.document.render() }); + + state.tool_state.active_tool()?.handle_input(event); + Ok(()) } Event::MouseMovement(pos) => { - state.tools.mouse_state.position = pos; - state.tools.record_trace_point(); + state.tool_state.mouse_state.position = pos; + state.tool_state.active_tool()?.handle_input(event); + Ok(()) } Event::ModifierKeyDown(mod_keys) => { - state.tools.mod_keys = mod_keys; - // the state has changed so we add a trace point - state.tools.record_trace_point(); + state.tool_state.mod_keys = mod_keys; + state.tool_state.active_tool()?.handle_input(event); + Ok(()) } Event::ModifierKeyUp(mod_keys) => { - state.tools.mod_keys = mod_keys; - // the state has changed so we add a trace point - state.tools.record_trace_point(); + state.tool_state.mod_keys = mod_keys; + state.tool_state.active_tool()?.handle_input(event); + Ok(()) } Event::KeyPress(key) => todo!(), } } + pub fn emit_response(&self, response: Response) { let func = &self.callback; func(response) } + pub fn new(callback: Callback) -> Dispatcher { Dispatcher { callback } } diff --git a/core/editor/src/error.rs b/core/editor/src/error.rs index b238bf893..84982e3fa 100644 --- a/core/editor/src/error.rs +++ b/core/editor/src/error.rs @@ -3,13 +3,14 @@ use crate::Color; use std::error::Error; use std::fmt::{self, Display}; -/// The error type used by the graphite editor. +/// The error type used by the Graphite editor. #[derive(Clone, Debug)] pub enum EditorError { InvalidOperation(String), InvalidEvent(String), Misc(String), Color(String), + UnknownTool, } impl Display for EditorError { @@ -19,6 +20,7 @@ impl Display for EditorError { EditorError::InvalidEvent(e) => write!(f, "Failed to dispatch event: {}", e), EditorError::Misc(e) => write!(f, "{}", e), EditorError::Color(c) => write!(f, "Tried to construct an invalid color {:?}", c), + EditorError::UnknownTool => write!(f, "The requested tool does not exist"), } } } diff --git a/core/editor/src/lib.rs b/core/editor/src/lib.rs index da17d39ab..4435c55b5 100644 --- a/core/editor/src/lib.rs +++ b/core/editor/src/lib.rs @@ -1,6 +1,8 @@ mod color; mod dispatcher; mod error; +#[macro_use] +mod macros; pub mod tools; pub mod workspace; @@ -22,7 +24,7 @@ use tools::ToolState; use workspace::Workspace; pub struct EditorState { - tools: ToolState, + tool_state: ToolState, workspace: Workspace, document: Document, } @@ -37,7 +39,7 @@ impl Editor { pub fn new(callback: Callback) -> Self { Self { state: EditorState { - tools: ToolState::new(), + tool_state: ToolState::new(), workspace: Workspace::new(), document: Document::default(), }, diff --git a/core/editor/src/macros.rs b/core/editor/src/macros.rs new file mode 100644 index 000000000..54e62a88d --- /dev/null +++ b/core/editor/src/macros.rs @@ -0,0 +1,49 @@ +/// Counts args in the macro invocation by adding `+ 1` for every arg. +/// +/// # Example +/// +/// ```ignore +/// let x = count_args!(("example1"), (10), (25)); +/// assert_eq!(x, 3); +/// ``` +/// expands to +/// ```ignore +/// let x = 0 + 1 + 1 + 1; +/// assert_eq!(x, 3); +/// ``` +macro_rules! count_args { + (@one $($t:tt)*) => { 1 }; + ($(($($x:tt)*)),*$(,)?) => { + 0 $(+ count_args!(@one $($x)*))* + }; +} + +/// Generates a [`std::collections::HashMap`] for `ToolState`'s `tools` variable. +/// +/// # Example +/// +/// ```ignore +/// let tools = gen_tools_hash_map! { +/// Select => select::Select, +/// Crop => crop::Crop, +/// }; +/// ``` +/// expands to +/// ```ignore +/// let tools = { +/// let mut hash_map: std::collections::HashMap> = std::collections::HashMap::with_capacity(count_args!(/* Macro args */)); +/// +/// hash_map.insert(crate::tools::ToolType::Select, Box::new(select::Select::default())); +/// hash_map.insert(crate::tools::ToolType::Crop, Box::new(crop::Crop::default())); +/// +/// hash_map +/// }; +/// ``` +macro_rules! gen_tools_hash_map { + ($($enum_variant:ident => $struct_path:ty),* $(,)?) => {{ + let mut hash_map: ::std::collections::HashMap<$crate::tools::ToolType, ::std::boxed::Box> = ::std::collections::HashMap::with_capacity(count_args!($(($enum_variant)),*)); + $(hash_map.insert($crate::tools::ToolType::$enum_variant, ::std::boxed::Box::new(<$struct_path>::default()));)* + + hash_map + }}; +} diff --git a/core/editor/src/tools/crop.rs b/core/editor/src/tools/crop.rs new file mode 100644 index 000000000..b1c331eb8 --- /dev/null +++ b/core/editor/src/tools/crop.rs @@ -0,0 +1,12 @@ +use crate::events::Event; +use crate::tools::Tool; +use document_core::Operation; + +#[derive(Default)] +pub struct Crop; + +impl Tool for Crop { + fn handle_input(&mut self, event: Event) -> Vec { + todo!(); + } +} diff --git a/core/editor/src/tools/ellipse.rs b/core/editor/src/tools/ellipse.rs new file mode 100644 index 000000000..acd322679 --- /dev/null +++ b/core/editor/src/tools/ellipse.rs @@ -0,0 +1,12 @@ +use crate::events::Event; +use crate::tools::Tool; +use document_core::Operation; + +#[derive(Default)] +pub struct Ellipse; + +impl Tool for Ellipse { + fn handle_input(&mut self, event: Event) -> Vec { + todo!(); + } +} diff --git a/core/editor/src/tools/line.rs b/core/editor/src/tools/line.rs new file mode 100644 index 000000000..fab95415f --- /dev/null +++ b/core/editor/src/tools/line.rs @@ -0,0 +1,12 @@ +use crate::events::Event; +use crate::tools::Tool; +use document_core::Operation; + +#[derive(Default)] +pub struct Line; + +impl Tool for Line { + fn handle_input(&mut self, event: Event) -> Vec { + todo!(); + } +} diff --git a/core/editor/src/tools/mod.rs b/core/editor/src/tools/mod.rs index 3257a4335..4f11c2569 100644 --- a/core/editor/src/tools/mod.rs +++ b/core/editor/src/tools/mod.rs @@ -1,14 +1,32 @@ -use crate::events::{ModKeys, MouseState, TracePoint}; -use crate::{events::Trace, Color}; +mod crop; +mod ellipse; +mod line; +mod navigate; +mod path; +mod pen; +mod rectangle; +mod sample; +mod select; +mod shape; + +use crate::events::{Event, ModKeys, MouseState, Trace, TracePoint}; +use crate::Color; +use crate::EditorError; +use document_core::Operation; use std::collections::HashMap; +pub trait Tool { + fn handle_input(&mut self, event: Event) -> Vec; +} + pub struct ToolState { pub mouse_state: MouseState, pub mod_keys: ModKeys, pub trace: Trace, pub primary_color: Color, pub secondary_color: Color, - pub active_tool: ToolType, + pub active_tool_type: ToolType, + pub tools: HashMap>, tool_settings: HashMap, } @@ -20,7 +38,19 @@ impl Default for ToolState { trace: Trace::new(), primary_color: Color::BLACK, secondary_color: Color::WHITE, - active_tool: ToolType::Select, + active_tool_type: ToolType::Select, + tools: gen_tools_hash_map! { + Select => select::Select, + Crop => crop::Crop, + Navigate => navigate::Navigate, + Sample => sample::Sample, + Path => path::Path, + Pen => pen::Pen, + Line => line::Line, + Rectangle => rectangle::Rectangle, + Ellipse => ellipse::Ellipse, + Shape => shape::Shape, + }, tool_settings: default_tool_settings(), } } @@ -37,6 +67,10 @@ impl ToolState { mod_keys: self.mod_keys, }) } + + pub fn active_tool(&mut self) -> Result<&mut Box, EditorError> { + self.tools.get_mut(&self.active_tool_type).ok_or(EditorError::UnknownTool) + } } fn default_tool_settings() -> HashMap { diff --git a/core/editor/src/tools/navigate.rs b/core/editor/src/tools/navigate.rs new file mode 100644 index 000000000..5bbb759fe --- /dev/null +++ b/core/editor/src/tools/navigate.rs @@ -0,0 +1,12 @@ +use crate::events::Event; +use crate::tools::Tool; +use document_core::Operation; + +#[derive(Default)] +pub struct Navigate; + +impl Tool for Navigate { + fn handle_input(&mut self, event: Event) -> Vec { + todo!(); + } +} diff --git a/core/editor/src/tools/path.rs b/core/editor/src/tools/path.rs new file mode 100644 index 000000000..0a70dfc6e --- /dev/null +++ b/core/editor/src/tools/path.rs @@ -0,0 +1,12 @@ +use crate::events::Event; +use crate::tools::Tool; +use document_core::Operation; + +#[derive(Default)] +pub struct Path; + +impl Tool for Path { + fn handle_input(&mut self, event: Event) -> Vec { + todo!(); + } +} diff --git a/core/editor/src/tools/pen.rs b/core/editor/src/tools/pen.rs new file mode 100644 index 000000000..f8f19c6cf --- /dev/null +++ b/core/editor/src/tools/pen.rs @@ -0,0 +1,12 @@ +use crate::events::Event; +use crate::tools::Tool; +use document_core::Operation; + +#[derive(Default)] +pub struct Pen; + +impl Tool for Pen { + fn handle_input(&mut self, event: Event) -> Vec { + todo!(); + } +} diff --git a/core/editor/src/tools/rectangle.rs b/core/editor/src/tools/rectangle.rs new file mode 100644 index 000000000..bd5e70406 --- /dev/null +++ b/core/editor/src/tools/rectangle.rs @@ -0,0 +1,12 @@ +use crate::events::Event; +use crate::tools::Tool; +use document_core::Operation; + +#[derive(Default)] +pub struct Rectangle; + +impl Tool for Rectangle { + fn handle_input(&mut self, event: Event) -> Vec { + todo!(); + } +} diff --git a/core/editor/src/tools/sample.rs b/core/editor/src/tools/sample.rs new file mode 100644 index 000000000..aac338bf5 --- /dev/null +++ b/core/editor/src/tools/sample.rs @@ -0,0 +1,12 @@ +use crate::events::Event; +use crate::tools::Tool; +use document_core::Operation; + +#[derive(Default)] +pub struct Sample; + +impl Tool for Sample { + fn handle_input(&mut self, event: Event) -> Vec { + todo!(); + } +} diff --git a/core/editor/src/tools/select.rs b/core/editor/src/tools/select.rs new file mode 100644 index 000000000..07ffac8a6 --- /dev/null +++ b/core/editor/src/tools/select.rs @@ -0,0 +1,40 @@ +use crate::events::Event; +use crate::events::MouseKeys; +use crate::tools::Tool; +use document_core::Operation; + +#[derive(Default)] +pub struct Select(Fsm); + +impl Tool for Select { + fn handle_input(&mut self, event: Event) -> Vec { + match event { + Event::MouseDown(state) => { + if state.mouse_keys.contains(MouseKeys::LEFT) { + self.0 = Fsm::LmbDown; + } + } + Event::MouseUp(state) => { + if self.0 == Fsm::LmbDown && state.mouse_keys.contains(MouseKeys::LEFT) { + self.0 = Fsm::SelectedObject; + } + } + _ => {} + } + + Vec::new() + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum Fsm { + Ready, + LmbDown, + SelectedObject, +} + +impl Default for Fsm { + fn default() -> Self { + Fsm::Ready + } +} diff --git a/core/editor/src/tools/shape.rs b/core/editor/src/tools/shape.rs new file mode 100644 index 000000000..3c54686d0 --- /dev/null +++ b/core/editor/src/tools/shape.rs @@ -0,0 +1,12 @@ +use crate::events::Event; +use crate::tools::Tool; +use document_core::Operation; + +#[derive(Default)] +pub struct Shape; + +impl Tool for Shape { + fn handle_input(&mut self, event: Event) -> Vec { + todo!(); + } +}