diff --git a/client/web/src/components/panels/Document.vue b/client/web/src/components/panels/Document.vue index 14f2ca56..220d25f5 100644 --- a/client/web/src/components/panels/Document.vue +++ b/client/web/src/components/panels/Document.vue @@ -75,7 +75,7 @@ - + diff --git a/client/web/wasm/src/wrappers.rs b/client/web/wasm/src/wrappers.rs index 332fcd1a..83901665 100644 --- a/client/web/wasm/src/wrappers.rs +++ b/client/web/wasm/src/wrappers.rs @@ -79,6 +79,7 @@ pub fn translate_key(name: &str) -> events::Key { match name { "e" => K::KeyE, "v" => K::KeyV, + "l" => K::KeyL, "r" => K::KeyR, "m" => K::KeyM, "x" => K::KeyX, diff --git a/core/document/src/lib.rs b/core/document/src/lib.rs index d3093baf..7b60bed0 100644 --- a/core/document/src/lib.rs +++ b/core/document/src/lib.rs @@ -1,6 +1,6 @@ pub mod operation; -pub use kurbo::{Circle, Point, Rect}; +pub use kurbo::{Circle, Line, Point, Rect}; pub use operation::Operation; #[derive(Debug, Clone, PartialEq)] @@ -8,6 +8,7 @@ pub enum LayerType { Folder(Folder), Circle(Circle), Rect(Rect), + Line(Line), } impl LayerType { @@ -20,6 +21,9 @@ impl LayerType { Self::Rect(r) => { format!(r#""#, r.min_x(), r.min_y(), r.width(), r.height()) } + Self::Line(l) => { + format!(r#""#, l.p0.x, l.p0.y, l.p1.x, l.p1.y) + } } } } @@ -211,6 +215,11 @@ impl Document { update_frontend(self.render()); } + Operation::AddLine { path, insert_index, x0, y0, x1, y1 } => { + self.add_layer(&path, Layer::new(LayerType::Line(Line::new(Point::new(x0, y0), Point::new(x1, y1)))), insert_index)?; + + update_frontend(self.render()); + } Operation::DeleteLayer { path } => { self.delete(&path)?; diff --git a/core/document/src/operation.rs b/core/document/src/operation.rs index df1d1fce..3c35bb17 100644 --- a/core/document/src/operation.rs +++ b/core/document/src/operation.rs @@ -16,6 +16,14 @@ pub enum Operation { x1: f64, y1: f64, }, + AddLine { + path: Vec, + insert_index: isize, + x0: f64, + y0: f64, + x1: f64, + y1: f64, + }, DeleteLayer { path: Vec, }, diff --git a/core/editor/src/dispatcher/events.rs b/core/editor/src/dispatcher/events.rs index fb6501c6..1f75781a 100644 --- a/core/editor/src/dispatcher/events.rs +++ b/core/editor/src/dispatcher/events.rs @@ -111,6 +111,7 @@ pub enum Key { KeyR, KeyM, KeyE, + KeyL, KeyV, KeyX, KeyZ, diff --git a/core/editor/src/dispatcher/mod.rs b/core/editor/src/dispatcher/mod.rs index e342db17..ec089e54 100644 --- a/core/editor/src/dispatcher/mod.rs +++ b/core/editor/src/dispatcher/mod.rs @@ -63,6 +63,12 @@ impl Dispatcher { tool_name: ToolType::Select.to_string(), }); } + Key::KeyL => { + editor_state.tool_state.active_tool_type = ToolType::Line; + self.dispatch_response(Response::SetActiveTool { + tool_name: ToolType::Line.to_string(), + }); + } Key::KeyM => { editor_state.tool_state.active_tool_type = ToolType::Rectangle; self.dispatch_response(Response::SetActiveTool { diff --git a/core/editor/src/tools/line.rs b/core/editor/src/tools/line.rs index ad894950..fc70cf91 100644 --- a/core/editor/src/tools/line.rs +++ b/core/editor/src/tools/line.rs @@ -1,13 +1,75 @@ use crate::events::{Event, Response}; -use crate::tools::Tool; +use crate::events::{Key, MouseKeys, ViewportPosition}; +use crate::tools::{Fsm, Tool}; use crate::Document; use document_core::Operation; #[derive(Default)] -pub struct Line; +pub struct Line { + fsm_state: LineToolFsmState, + data: LineToolData, +} impl Tool for Line { fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec, Vec) { - todo!(); + let mut responses = Vec::new(); + let mut operations = Vec::new(); + self.fsm_state = self.fsm_state.transition(event, document, &mut self.data, &mut responses, &mut operations); + + (responses, operations) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum LineToolFsmState { + Ready, + LmbDown, +} + +impl Default for LineToolFsmState { + fn default() -> Self { + LineToolFsmState::Ready + } +} +#[derive(Clone, Debug, Default)] +struct LineToolData { + drag_start: ViewportPosition, +} + +impl Fsm for LineToolFsmState { + type ToolData = LineToolData; + + fn transition(self, event: &Event, document: &Document, data: &mut Self::ToolData, responses: &mut Vec, operations: &mut Vec) -> Self { + match (self, event) { + (LineToolFsmState::Ready, Event::MouseDown(mouse_state)) if mouse_state.mouse_keys.contains(MouseKeys::LEFT) => { + data.drag_start = mouse_state.position; + LineToolFsmState::LmbDown + } + (LineToolFsmState::Ready, Event::KeyDown(Key::KeyZ)) => { + if let Some(id) = document.root.list_layers().last() { + operations.push(Operation::DeleteLayer { path: vec![*id] }) + } + LineToolFsmState::Ready + } + // TODO - Check for left mouse button + (LineToolFsmState::LmbDown, Event::MouseUp(mouse_state)) => { + let distance = data.drag_start.distance(&mouse_state.position); + log::info!("draw Line with distance: {:.2}", distance); + let start = data.drag_start; + let end = mouse_state.position; + operations.push(Operation::AddLine { + path: vec![], + insert_index: -1, + x0: start.x as f64, + y0: start.y as f64, + x1: end.x as f64, + y1: end.y as f64, + }); + + LineToolFsmState::Ready + } + + _ => self, + } } }