mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-02 03:08:12 +08:00
Add Active Tool Router (#58)
* Add Active Tool Router * Remove commented out import
This commit is contained in:
committed by
Keavon Chambers
parent
6050038047
commit
4992bdee0e
+1
-1
@@ -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"
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
pub enum Operation {
|
||||||
|
AddCircle((f64, f64), f64),
|
||||||
|
}
|
||||||
@@ -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,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"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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
|
||||||
|
}};
|
||||||
|
}
|
||||||
@@ -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!();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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!();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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!();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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> {
|
||||||
|
|||||||
@@ -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!();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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!();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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!();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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!();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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!();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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!();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user