diff --git a/client/web/src/components/panels/Document.vue b/client/web/src/components/panels/Document.vue
index e4c21a57..8ce15f84 100644
--- a/client/web/src/components/panels/Document.vue
+++ b/client/web/src/components/panels/Document.vue
@@ -72,10 +72,10 @@
-
+
-
+
diff --git a/client/web/wasm/src/wrappers.rs b/client/web/wasm/src/wrappers.rs
index b6fb8401..6a7a5c64 100644
--- a/client/web/wasm/src/wrappers.rs
+++ b/client/web/wasm/src/wrappers.rs
@@ -80,6 +80,7 @@ pub fn translate_key(name: &str) -> events::Key {
"e" => K::KeyE,
"v" => K::KeyV,
"l" => K::KeyL,
+ "p" => K::KeyP,
"r" => K::KeyR,
"m" => K::KeyM,
"x" => K::KeyX,
@@ -95,6 +96,7 @@ pub fn translate_key(name: &str) -> events::Key {
"7" => K::Key7,
"8" => K::Key8,
"9" => K::Key9,
+ "Enter" => K::KeyEnter,
_ => K::UnknownKey,
}
}
diff --git a/core/document/src/document.rs b/core/document/src/document.rs
index 98e29d13..0969e65c 100644
--- a/core/document/src/document.rs
+++ b/core/document/src/document.rs
@@ -1,3 +1,5 @@
+use layers::PolyLine;
+
use crate::{
layers::{self, Folder, Layer, LayerData, LayerDataTypes, Line, Rect, Shape},
DocumentError, LayerId, Operation,
@@ -178,6 +180,12 @@ impl Document {
update_frontend(self.render(&mut vec![]));
}
+ Operation::AddPen { path, insert_index, points, style } => {
+ let points: Vec = points.into_iter().map(|it| it.into()).collect();
+ let polyline = PolyLine::new(points, style);
+ self.add_layer(&path, Layer::new(LayerDataTypes::PolyLine(polyline)), insert_index)?;
+ update_frontend(self.render(&mut vec![]));
+ }
Operation::AddShape {
path,
insert_index,
diff --git a/core/document/src/layers/mod.rs b/core/document/src/layers/mod.rs
index 3700c639..4fd5a76b 100644
--- a/core/document/src/layers/mod.rs
+++ b/core/document/src/layers/mod.rs
@@ -9,6 +9,9 @@ pub use line::Line;
pub mod rect;
pub use rect::Rect;
+pub mod polyline;
+pub use polyline::PolyLine;
+
pub mod shape;
pub use shape::Shape;
@@ -25,6 +28,7 @@ pub enum LayerDataTypes {
Circle(Circle),
Rect(Rect),
Line(Line),
+ PolyLine(PolyLine),
Shape(Shape),
}
@@ -35,6 +39,7 @@ impl LayerDataTypes {
Self::Circle(c) => c.render(),
Self::Rect(r) => r.render(),
Self::Line(l) => l.render(),
+ Self::PolyLine(pl) => pl.render(),
Self::Shape(s) => s.render(),
}
}
diff --git a/core/document/src/layers/polyline.rs b/core/document/src/layers/polyline.rs
new file mode 100644
index 00000000..fc67af88
--- /dev/null
+++ b/core/document/src/layers/polyline.rs
@@ -0,0 +1,24 @@
+use super::style;
+use super::LayerData;
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct PolyLine {
+ points: Vec,
+ style: style::PathStyle,
+}
+
+impl PolyLine {
+ pub fn new(points: Vec>, style: style::PathStyle) -> PolyLine {
+ PolyLine {
+ points: points.into_iter().map(|it| it.into()).collect(),
+ style,
+ }
+ }
+}
+
+impl LayerData for PolyLine {
+ fn render(&self) -> String {
+ let points = self.points.iter().map(|p| format!("{},{}", p.x, p.y)).collect::>().join(" ");
+ format!(r#""#, points, self.style.render())
+ }
+}
diff --git a/core/document/src/layers/style/mod.rs b/core/document/src/layers/style/mod.rs
index 7def803e..958bc219 100644
--- a/core/document/src/layers/style/mod.rs
+++ b/core/document/src/layers/style/mod.rs
@@ -3,14 +3,20 @@ use crate::color::Color;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Fill {
- color: Color,
+ color: Option,
}
impl Fill {
pub fn new(color: Color) -> Self {
- Self { color }
+ Self { color: Some(color) }
+ }
+ pub fn none() -> Self {
+ Self { color: None }
}
pub fn render(&self) -> String {
- format!("fill: #{};", self.color.as_hex())
+ match self.color {
+ Some(c) => format!("fill: #{};", c.as_hex()),
+ None => format!("fill: none;"),
+ }
}
}
diff --git a/core/document/src/operation.rs b/core/document/src/operation.rs
index fcfe2b69..7af1f740 100644
--- a/core/document/src/operation.rs
+++ b/core/document/src/operation.rs
@@ -28,6 +28,12 @@ pub enum Operation {
y1: f64,
style: style::PathStyle,
},
+ AddPen {
+ path: Vec,
+ insert_index: isize,
+ points: Vec<(f64, f64)>,
+ style: style::PathStyle,
+ },
AddShape {
path: Vec,
insert_index: isize,
diff --git a/core/editor/src/dispatcher/events.rs b/core/editor/src/dispatcher/events.rs
index 4b4d3c5f..a2c8e60d 100644
--- a/core/editor/src/dispatcher/events.rs
+++ b/core/editor/src/dispatcher/events.rs
@@ -112,10 +112,12 @@ pub enum Key {
KeyM,
KeyE,
KeyL,
+ KeyP,
KeyV,
KeyX,
KeyZ,
KeyY,
+ KeyEnter,
Key0,
Key1,
Key2,
diff --git a/core/editor/src/tools/pen.rs b/core/editor/src/tools/pen.rs
index 812e881a..5d72a478 100644
--- a/core/editor/src/tools/pen.rs
+++ b/core/editor/src/tools/pen.rs
@@ -1,15 +1,97 @@
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::layers::style;
use document_core::Operation;
use super::DocumentToolData;
#[derive(Default)]
-pub struct Pen;
+pub struct Pen {
+ fsm_state: PenToolFsmState,
+ data: PenToolData,
+}
impl Tool for Pen {
fn handle_input(&mut self, event: &Event, document: &Document, tool_data: &DocumentToolData) -> (Vec, Vec) {
- todo!("{}::handle_input {:?} {:?} {:?}", module_path!(), event, document, tool_data)
+ let mut responses = Vec::new();
+ let mut operations = Vec::new();
+ self.fsm_state = self.fsm_state.transition(event, document, tool_data, &mut self.data, &mut responses, &mut operations);
+
+ (responses, operations)
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+enum PenToolFsmState {
+ Ready,
+ LmbDown,
+}
+
+impl Default for PenToolFsmState {
+ fn default() -> Self {
+ PenToolFsmState::Ready
+ }
+}
+#[derive(Clone, Debug, Default)]
+struct PenToolData {
+ points: Vec,
+}
+
+impl Fsm for PenToolFsmState {
+ type ToolData = PenToolData;
+
+ fn transition(self, event: &Event, document: &Document, tool_data: &DocumentToolData, data: &mut Self::ToolData, _responses: &mut Vec, operations: &mut Vec) -> Self {
+ let stroke = style::Stroke::new(tool_data.primary_color, 5.);
+ let fill = style::Fill::none();
+ let style = style::PathStyle::new(Some(stroke), Some(fill));
+
+ match (self, event) {
+ (PenToolFsmState::Ready, Event::MouseDown(mouse_state)) if mouse_state.mouse_keys.contains(MouseKeys::LEFT) => {
+ operations.push(Operation::MountWorkingFolder { path: vec![] });
+ data.points.push(mouse_state.position);
+ PenToolFsmState::LmbDown
+ }
+ (PenToolFsmState::Ready, Event::KeyDown(Key::KeyZ)) => {
+ if let Some(id) = document.root.list_layers().last() {
+ operations.push(Operation::DeleteLayer { path: vec![*id] })
+ }
+ PenToolFsmState::Ready
+ }
+ // TODO - Check for left mouse button
+ (PenToolFsmState::LmbDown, Event::MouseDown(mouse_state)) => {
+ data.points.push(mouse_state.position);
+ PenToolFsmState::LmbDown
+ }
+ (PenToolFsmState::LmbDown, Event::MouseMove(mouse_state)) => {
+ let mut points: Vec<_> = data.points.iter().map(|p| (p.x as f64, p.y as f64)).collect();
+ points.push((mouse_state.x as f64, mouse_state.y as f64));
+
+ operations.push(Operation::ClearWorkingFolder);
+ operations.push(Operation::AddPen {
+ path: vec![],
+ insert_index: -1,
+ points,
+ style,
+ });
+ PenToolFsmState::LmbDown
+ }
+ (PenToolFsmState::LmbDown, Event::KeyDown(Key::KeyEnter)) => {
+ let points = data.points.drain(..).map(|p| (p.x as f64, p.y as f64)).collect();
+ operations.push(Operation::ClearWorkingFolder);
+ operations.push(Operation::AddPen {
+ path: vec![],
+ insert_index: -1,
+ points,
+ style,
+ });
+ operations.push(Operation::CommitTransaction);
+ PenToolFsmState::Ready
+ }
+
+ _ => self,
+ }
}
}