Add Active Tool Router (#58)

* Add Active Tool Router

* Remove commented out import
This commit is contained in:
ProTheory8
2021-04-01 21:10:28 +05:00
committed by Keavon Chambers
parent 6050038047
commit 4992bdee0e
18 changed files with 278 additions and 30 deletions
+1 -1
View File
@@ -5,5 +5,5 @@ members = [
"client/web/wasm", "client/web/wasm",
] ]
[profile.release.package.wasm-wrapper] [profile.release.package.graphite-wasm-wrapper]
opt-level = "s" opt-level = "s"
+3
View File
@@ -1,4 +1,7 @@
pub mod operation;
pub use kurbo::{Circle, Point}; pub use kurbo::{Circle, Point};
pub use operation::Operation;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum SvgElement { pub enum SvgElement {
+3
View File
@@ -0,0 +1,3 @@
pub enum Operation {
AddCircle((f64, f64), f64),
}
+29 -22
View File
@@ -11,40 +11,42 @@ pub struct Dispatcher {
impl Dispatcher { impl Dispatcher {
pub fn handle_event(&self, state: &mut EditorState, event: Event) -> Result<(), EditorError> { pub fn handle_event(&self, state: &mut EditorState, event: Event) -> Result<(), EditorError> {
log::trace!("{:?}", event); log::trace!("{:?}", event);
match event { match event {
Event::SelectTool(tool_type) => { Event::SelectTool(tool_type) => {
state.tools.active_tool = tool_type; state.tool_state.active_tool_type = tool_type;
Ok(()) Ok(())
} }
Event::SelectPrimaryColor(color) => { Event::SelectPrimaryColor(color) => {
state.tools.primary_color = color; state.tool_state.primary_color = color;
Ok(()) Ok(())
} }
Event::SelectSecondaryColor(color) => { Event::SelectSecondaryColor(color) => {
state.tools.secondary_color = color; state.tool_state.secondary_color = color;
Ok(()) Ok(())
} }
Event::SwapColors => { 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(()) Ok(())
} }
Event::ResetColors => { Event::ResetColors => {
state.tools.primary_color = Color::BLACK; state.tool_state.primary_color = Color::BLACK;
state.tools.secondary_color = Color::WHITE; state.tool_state.secondary_color = Color::WHITE;
Ok(()) Ok(())
} }
Event::MouseDown(mouse_state) => { Event::MouseDown(mouse_state) => {
state.tools.mouse_state = mouse_state; state.tool_state.mouse_state = mouse_state;
// the state has changed so we add a trace point state.tool_state.active_tool()?.handle_input(event);
state.tools.record_trace_point();
// self.emit_response(Response::UpdateCanvas { document: state.document.render() });
Ok(()) Ok(())
} }
Event::MouseUp(mouse_state) => { Event::MouseUp(mouse_state) => {
state.tools.mouse_state = mouse_state; state.tool_state.mouse_state = mouse_state;
// the state has changed so we add a trace point
state.tools.record_trace_point();
state.document.svg.push(SvgElement::Circle(Circle { state.document.svg.push(SvgElement::Circle(Circle {
center: Point { center: Point {
@@ -53,34 +55,39 @@ impl Dispatcher {
}, },
radius: 10.0, radius: 10.0,
})); }));
self.emit_response(Response::UpdateCanvas { document: state.document.render() }); self.emit_response(Response::UpdateCanvas { document: state.document.render() });
state.tool_state.active_tool()?.handle_input(event);
Ok(()) Ok(())
} }
Event::MouseMovement(pos) => { Event::MouseMovement(pos) => {
state.tools.mouse_state.position = pos; state.tool_state.mouse_state.position = pos;
state.tools.record_trace_point(); state.tool_state.active_tool()?.handle_input(event);
Ok(()) Ok(())
} }
Event::ModifierKeyDown(mod_keys) => { Event::ModifierKeyDown(mod_keys) => {
state.tools.mod_keys = mod_keys; state.tool_state.mod_keys = mod_keys;
// the state has changed so we add a trace point state.tool_state.active_tool()?.handle_input(event);
state.tools.record_trace_point();
Ok(()) Ok(())
} }
Event::ModifierKeyUp(mod_keys) => { Event::ModifierKeyUp(mod_keys) => {
state.tools.mod_keys = mod_keys; state.tool_state.mod_keys = mod_keys;
// the state has changed so we add a trace point state.tool_state.active_tool()?.handle_input(event);
state.tools.record_trace_point();
Ok(()) Ok(())
} }
Event::KeyPress(key) => todo!(), Event::KeyPress(key) => todo!(),
} }
} }
pub fn emit_response(&self, response: Response) { pub fn emit_response(&self, response: Response) {
let func = &self.callback; let func = &self.callback;
func(response) func(response)
} }
pub fn new(callback: Callback) -> Dispatcher { pub fn new(callback: Callback) -> Dispatcher {
Dispatcher { callback } Dispatcher { callback }
} }
+3 -1
View File
@@ -3,13 +3,14 @@ use crate::Color;
use std::error::Error; use std::error::Error;
use std::fmt::{self, Display}; use std::fmt::{self, Display};
/// The error type used by the graphite editor. /// The error type used by the Graphite editor.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum EditorError { pub enum EditorError {
InvalidOperation(String), InvalidOperation(String),
InvalidEvent(String), InvalidEvent(String),
Misc(String), Misc(String),
Color(String), Color(String),
UnknownTool,
} }
impl Display for EditorError { impl Display for EditorError {
@@ -19,6 +20,7 @@ impl Display for EditorError {
EditorError::InvalidEvent(e) => write!(f, "Failed to dispatch event: {}", e), EditorError::InvalidEvent(e) => write!(f, "Failed to dispatch event: {}", e),
EditorError::Misc(e) => write!(f, "{}", e), EditorError::Misc(e) => write!(f, "{}", e),
EditorError::Color(c) => write!(f, "Tried to construct an invalid color {:?}", c), EditorError::Color(c) => write!(f, "Tried to construct an invalid color {:?}", c),
EditorError::UnknownTool => write!(f, "The requested tool does not exist"),
} }
} }
} }
+4 -2
View File
@@ -1,6 +1,8 @@
mod color; mod color;
mod dispatcher; mod dispatcher;
mod error; mod error;
#[macro_use]
mod macros;
pub mod tools; pub mod tools;
pub mod workspace; pub mod workspace;
@@ -22,7 +24,7 @@ use tools::ToolState;
use workspace::Workspace; use workspace::Workspace;
pub struct EditorState { pub struct EditorState {
tools: ToolState, tool_state: ToolState,
workspace: Workspace, workspace: Workspace,
document: Document, document: Document,
} }
@@ -37,7 +39,7 @@ impl Editor {
pub fn new(callback: Callback) -> Self { pub fn new(callback: Callback) -> Self {
Self { Self {
state: EditorState { state: EditorState {
tools: ToolState::new(), tool_state: ToolState::new(),
workspace: Workspace::new(), workspace: Workspace::new(),
document: Document::default(), document: Document::default(),
}, },
+49
View File
@@ -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<crate::tools::ToolType, Box<dyn crate::tools::Tool>> = 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<dyn $crate::tools::Tool>> = ::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
}};
}
+12
View File
@@ -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<Operation> {
todo!();
}
}
+12
View File
@@ -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<Operation> {
todo!();
}
}
+12
View File
@@ -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<Operation> {
todo!();
}
}
+38 -4
View File
@@ -1,14 +1,32 @@
use crate::events::{ModKeys, MouseState, TracePoint}; mod crop;
use crate::{events::Trace, Color}; 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; use std::collections::HashMap;
pub trait Tool {
fn handle_input(&mut self, event: Event) -> Vec<Operation>;
}
pub struct ToolState { pub struct ToolState {
pub mouse_state: MouseState, pub mouse_state: MouseState,
pub mod_keys: ModKeys, pub mod_keys: ModKeys,
pub trace: Trace, pub trace: Trace,
pub primary_color: Color, pub primary_color: Color,
pub secondary_color: Color, pub secondary_color: Color,
pub active_tool: ToolType, pub active_tool_type: ToolType,
pub tools: HashMap<ToolType, Box<dyn Tool>>,
tool_settings: HashMap<ToolType, ToolSettings>, tool_settings: HashMap<ToolType, ToolSettings>,
} }
@@ -20,7 +38,19 @@ impl Default for ToolState {
trace: Trace::new(), trace: Trace::new(),
primary_color: Color::BLACK, primary_color: Color::BLACK,
secondary_color: Color::WHITE, 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(), tool_settings: default_tool_settings(),
} }
} }
@@ -37,6 +67,10 @@ impl ToolState {
mod_keys: self.mod_keys, mod_keys: self.mod_keys,
}) })
} }
pub fn active_tool(&mut self) -> Result<&mut Box<dyn Tool>, EditorError> {
self.tools.get_mut(&self.active_tool_type).ok_or(EditorError::UnknownTool)
}
} }
fn default_tool_settings() -> HashMap<ToolType, ToolSettings> { fn default_tool_settings() -> HashMap<ToolType, ToolSettings> {
+12
View File
@@ -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<Operation> {
todo!();
}
}
+12
View File
@@ -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<Operation> {
todo!();
}
}
+12
View File
@@ -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<Operation> {
todo!();
}
}
+12
View File
@@ -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<Operation> {
todo!();
}
}
+12
View File
@@ -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<Operation> {
todo!();
}
}
+40
View File
@@ -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<Operation> {
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
}
}
+12
View File
@@ -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<Operation> {
todo!();
}
}