From 46c9ef02ca5bfdf32278424821b76d9c83756e42 Mon Sep 17 00:00:00 2001 From: 0HyperCube <78500760+0HyperCube@users.noreply.github.com> Date: Wed, 21 Apr 2021 22:25:06 +0100 Subject: [PATCH] Add colors in Rust (#78) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🎨 Add colors in Rust * 🌿 Use an option for the properties and #[repr(C)] * ❌ Remove WASM dependency on document. * 😎 Wrap Fill and stroke in a style struct. * 📦 Use crate::Color * Merge Add transactions for temporary modifications to the document * Run cargo fmt * Color without a 'U' --- client/web/src/components/panels/Document.vue | 12 +- client/web/wasm/src/wrappers.rs | 4 +- core/{editor => document}/src/color.rs | 19 +- core/document/src/document.rs | 234 ++++++++++++ core/document/src/layers/circle.rs | 29 ++ core/document/src/layers/folder.rs | 87 +++++ core/document/src/layers/line.rs | 30 ++ core/document/src/layers/mod.rs | 54 +++ core/document/src/layers/rect.rs | 30 ++ core/document/src/layers/shape.rs | 25 ++ core/document/src/layers/style/mod.rs | 56 +++ core/document/src/lib.rs | 334 +----------------- core/document/src/operation.rs | 6 +- core/editor/src/dispatcher/mod.rs | 32 +- core/editor/src/lib.rs | 5 +- core/editor/src/tools/crop.rs | 4 +- core/editor/src/tools/ellipse.rs | 11 +- core/editor/src/tools/line.rs | 10 +- core/editor/src/tools/mod.rs | 69 ++-- core/editor/src/tools/navigate.rs | 4 +- core/editor/src/tools/path.rs | 4 +- core/editor/src/tools/pen.rs | 4 +- core/editor/src/tools/rectangle.rs | 10 +- core/editor/src/tools/sample.rs | 4 +- core/editor/src/tools/select.rs | 8 +- core/editor/src/tools/shape.rs | 11 +- 26 files changed, 689 insertions(+), 407 deletions(-) rename core/{editor => document}/src/color.rs (81%) create mode 100644 core/document/src/document.rs create mode 100644 core/document/src/layers/circle.rs create mode 100644 core/document/src/layers/folder.rs create mode 100644 core/document/src/layers/line.rs create mode 100644 core/document/src/layers/mod.rs create mode 100644 core/document/src/layers/rect.rs create mode 100644 core/document/src/layers/shape.rs create mode 100644 core/document/src/layers/style/mod.rs diff --git a/client/web/src/components/panels/Document.vue b/client/web/src/components/panels/Document.vue index 75f8f0ac..e4c21a57 100644 --- a/client/web/src/components/panels/Document.vue +++ b/client/web/src/components/panels/Document.vue @@ -322,13 +322,14 @@ export default defineComponent({ } todo(toolIndex); }, + async updatePrimaryColor(c: { r: number; g: number; b: number; a: number }) { + const { update_primary_color, Color } = await wasm; + update_primary_color(new Color(c.r, c.g, c.b, c.a)); + }, }, mounted() { registerResponseHandler(ResponseType.UpdateCanvas, (responseData) => { - this.viewportSvg = responseData - .split("\n") - .map((shape, i) => shape.replace("#fff", `#${Math.floor(Math.abs(Math.sin(i + 1)) * 16777215).toString(16)}`)) - .join("\n"); + this.viewportSvg = responseData; }); registerResponseHandler(ResponseType.SetActiveTool, (responseData) => { this.activeTool = responseData; @@ -336,6 +337,9 @@ export default defineComponent({ window.addEventListener("keyup", (e: KeyboardEvent) => this.keyUp(e)); window.addEventListener("keydown", (e: KeyboardEvent) => this.keyDown(e)); + + // TODO: Implement actuall UI for chosing colors (this is completly temporary) + this.updatePrimaryColor({ r: 0.29, g: 0.52, b: 0.29, a: 0.6 }); }, data() { return { diff --git a/client/web/wasm/src/wrappers.rs b/client/web/wasm/src/wrappers.rs index 3fc4008a..b6fb8401 100644 --- a/client/web/wasm/src/wrappers.rs +++ b/client/web/wasm/src/wrappers.rs @@ -12,8 +12,8 @@ impl Color { #[wasm_bindgen(constructor)] pub fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Result { match InnerColor::from_rgbaf32(red, green, blue, alpha) { - Ok(v) => Ok(Self(v)), - Err(e) => Err(Error::new(&e.to_string()).into()), + Some(v) => Ok(Self(v)), + None => Err(Error::new("invalid color").into()), } } } diff --git a/core/editor/src/color.rs b/core/document/src/color.rs similarity index 81% rename from core/editor/src/color.rs rename to core/document/src/color.rs index 76f24db8..797d6dc2 100644 --- a/core/editor/src/color.rs +++ b/core/document/src/color.rs @@ -1,7 +1,5 @@ -use crate::EditorError; - #[repr(C)] -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Default)] pub struct Color { red: f32, green: f32, @@ -16,12 +14,12 @@ impl Color { pub const GREEN: Color = Color::from_unsafe(0., 1., 0.); pub const BLUE: Color = Color::from_unsafe(0., 0., 1.); - pub fn from_rgbaf32(red: f32, green: f32, blue: f32, alpha: f32) -> Result { + pub fn from_rgbaf32(red: f32, green: f32, blue: f32, alpha: f32) -> Option { let color = Color { red, green, blue, alpha }; if [red, green, blue, alpha].iter().any(|c| c.is_sign_negative() || !c.is_finite()) { - Err(color)? + return None; } - Ok(color) + Some(color) } const fn from_unsafe(red: f32, green: f32, blue: f32) -> Color { @@ -55,4 +53,13 @@ impl Color { pub fn components(&self) -> (f32, f32, f32, f32) { (self.red, self.green, self.blue, self.alpha) } + pub fn as_hex(&self) -> String { + format!( + "{:02X?}{:02X?}{:02X?}{:02X?}", + (self.r() * 255.) as u8, + (self.g() * 255.) as u8, + (self.b() * 255.) as u8, + (self.a() * 255.) as u8, + ) + } } diff --git a/core/document/src/document.rs b/core/document/src/document.rs new file mode 100644 index 00000000..98e29d13 --- /dev/null +++ b/core/document/src/document.rs @@ -0,0 +1,234 @@ +use crate::{ + layers::{self, Folder, Layer, LayerData, LayerDataTypes, Line, Rect, Shape}, + DocumentError, LayerId, Operation, +}; + +#[derive(Debug, Clone, PartialEq)] +pub struct Document { + pub root: layers::Folder, + pub work: Folder, + pub work_mount_path: Vec, + pub work_operations: Vec, + pub work_mounted: bool, +} + +impl Default for Document { + fn default() -> Self { + Self { + root: Folder::default(), + work: Folder::default(), + work_mount_path: Vec::new(), + work_operations: Vec::new(), + work_mounted: false, + } + } +} + +fn split_path(path: &[LayerId]) -> Result<(&[LayerId], LayerId), DocumentError> { + let id = path.last().ok_or(DocumentError::InvalidPath)?; + let folder_path = &path[0..path.len() - 1]; + Ok((folder_path, *id)) +} + +impl Document { + pub fn render(&self, path: &mut Vec) -> String { + if !self.work_mount_path.as_slice().starts_with(path) { + match &self.layer(path).unwrap().data { + LayerDataTypes::Folder(_) => (), + element => { + path.pop(); + return element.render(); + } + } + } + if path.as_slice() == self.work_mount_path { + let mut out = self.document_folder(path).unwrap().render(); + out += self.work.render().as_str(); + path.pop(); + return out; + } + let mut out = String::with_capacity(30); + for element in self.folder(path).unwrap().layer_ids.iter() { + path.push(*element); + out += self.render(path).as_str(); + } + out + } + + fn is_mounted(&self, mount_path: &[LayerId], path: &[LayerId]) -> bool { + path.starts_with(mount_path) && self.work_mounted + } + + pub fn folder(&self, mut path: &[LayerId]) -> Result<&Folder, DocumentError> { + let mut root = &self.root; + if self.is_mounted(self.work_mount_path.as_slice(), path) { + path = &path[self.work_mount_path.len()..]; + root = &self.work; + } + for id in path { + root = root.folder(*id).ok_or(DocumentError::LayerNotFound)?; + } + Ok(root) + } + + pub fn folder_mut(&mut self, mut path: &[LayerId]) -> Result<&mut Folder, DocumentError> { + let mut root = if self.is_mounted(self.work_mount_path.as_slice(), path) { + path = &path[self.work_mount_path.len()..]; + &mut self.work + } else { + &mut self.root + }; + for id in path { + root = root.folder_mut(*id).ok_or(DocumentError::LayerNotFound)?; + } + Ok(root) + } + + pub fn document_folder(&self, path: &[LayerId]) -> Result<&Folder, DocumentError> { + let mut root = &self.root; + for id in path { + root = root.folder(*id).ok_or(DocumentError::LayerNotFound)?; + } + Ok(root) + } + + pub fn document_folder_mut(&mut self, path: &[LayerId]) -> Result<&mut Folder, DocumentError> { + let mut root = &mut self.root; + for id in path { + root = root.folder_mut(*id).ok_or(DocumentError::LayerNotFound)?; + } + Ok(root) + } + + pub fn layer(&self, path: &[LayerId]) -> Result<&Layer, DocumentError> { + let (path, id) = split_path(path)?; + self.folder(path)?.layer(id).ok_or(DocumentError::LayerNotFound) + } + + pub fn layer_mut(&mut self, path: &[LayerId]) -> Result<&mut Layer, DocumentError> { + let (path, id) = split_path(path)?; + self.folder_mut(path)?.layer_mut(id).ok_or(DocumentError::LayerNotFound) + } + + pub fn set_layer(&mut self, path: &[LayerId], layer: Layer) -> Result<(), DocumentError> { + let mut folder = &mut self.root; + if let Ok((path, id)) = split_path(path) { + folder = self.folder_mut(path)?; + if let Some(folder_layer) = folder.layer_mut(id) { + *folder_layer = layer; + return Ok(()); + } + } + folder.add_layer(layer, -1).ok_or(DocumentError::IndexOutOfBounds)?; + Ok(()) + } + + /// Passing a negative `insert_index` indexes relative to the end + /// -1 is equivalent to adding the layer to the top + pub fn add_layer(&mut self, path: &[LayerId], layer: Layer, insert_index: isize) -> Result { + let folder = self.folder_mut(path)?; + folder.add_layer(layer, insert_index).ok_or(DocumentError::IndexOutOfBounds) + } + + pub fn delete(&mut self, path: &[LayerId]) -> Result<(), DocumentError> { + let (path, id) = split_path(path)?; + self.document_folder_mut(path)?.remove_layer(id)?; + Ok(()) + } + + pub fn handle_operation(&mut self, operation: Operation, update_frontend: &F) -> Result<(), DocumentError> { + self.work_operations.push(operation.clone()); + match operation { + Operation::AddCircle { path, insert_index, cx, cy, r, style } => { + self.add_layer(&path, Layer::new(LayerDataTypes::Circle(layers::Circle::new(kurbo::Point::new(cx, cy), r, style))), insert_index)?; + + update_frontend(self.render(&mut vec![])); + } + Operation::AddRect { + path, + insert_index, + x0, + y0, + x1, + y1, + style, + } => { + self.add_layer( + &path, + Layer::new(LayerDataTypes::Rect(Rect::new(kurbo::Point::new(x0, y0), kurbo::Point::new(x1, y1), style))), + insert_index, + )?; + + update_frontend(self.render(&mut vec![])); + } + Operation::AddLine { + path, + insert_index, + x0, + y0, + x1, + y1, + style, + } => { + self.add_layer( + &path, + Layer::new(LayerDataTypes::Line(Line::new(kurbo::Point::new(x0, y0), kurbo::Point::new(x1, y1), style))), + insert_index, + )?; + + update_frontend(self.render(&mut vec![])); + } + Operation::AddShape { + path, + insert_index, + x0, + y0, + x1, + y1, + sides, + style, + } => { + let s = Shape::new(kurbo::Point::new(x0, y0), kurbo::Vec2 { x: x0 - x1, y: y0 - y1 }, sides, style); + self.add_layer(&path, Layer::new(LayerDataTypes::Shape(s)), insert_index)?; + + update_frontend(self.render(&mut vec![])); + } + Operation::DeleteLayer { path } => { + self.delete(&path)?; + + update_frontend(self.render(&mut vec![])); + } + Operation::AddFolder { path } => self.set_layer(&path, Layer::new(LayerDataTypes::Folder(Folder::default())))?, + Operation::MountWorkingFolder { path } => { + self.work_operations.clear(); + self.work_mount_path = path; + self.work = Folder::default(); + self.work_mounted = true; + } + Operation::DiscardWorkingFolder => { + self.work_operations.clear(); + self.work_mount_path = vec![]; + self.work = Folder::default(); + self.work_mounted = false; + } + Operation::ClearWorkingFolder => { + self.work_operations.clear(); + self.work = Folder::default(); + } + Operation::CommitTransaction => { + let mut ops = Vec::new(); + std::mem::swap(&mut ops, &mut self.work_operations); + let len = ops.len() - 1; + self.work_mounted = false; + self.work_mount_path = vec![]; + self.work = Folder::default(); + for operation in ops.into_iter().take(len) { + self.handle_operation(operation, update_frontend)? + } + + update_frontend(self.render(&mut vec![])); + } + } + Ok(()) + } +} diff --git a/core/document/src/layers/circle.rs b/core/document/src/layers/circle.rs new file mode 100644 index 00000000..f1b6e647 --- /dev/null +++ b/core/document/src/layers/circle.rs @@ -0,0 +1,29 @@ +use super::style; +use super::LayerData; + +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub struct Circle { + shape: kurbo::Circle, + style: style::PathStyle, +} + +impl Circle { + pub fn new(center: impl Into, radius: f64, style: style::PathStyle) -> Circle { + Circle { + shape: kurbo::Circle::new(center, radius), + style, + } + } +} + +impl LayerData for Circle { + fn render(&self) -> String { + format!( + r#""#, + self.shape.center.x, + self.shape.center.y, + self.shape.radius, + self.style.render(), + ) + } +} diff --git a/core/document/src/layers/folder.rs b/core/document/src/layers/folder.rs new file mode 100644 index 00000000..34557d89 --- /dev/null +++ b/core/document/src/layers/folder.rs @@ -0,0 +1,87 @@ +use crate::{DocumentError, LayerId}; + +use super::{Layer, LayerData, LayerDataTypes}; + +#[derive(Debug, Clone, PartialEq)] +pub struct Folder { + next_assignment_id: LayerId, + pub layer_ids: Vec, + layers: Vec, +} + +impl LayerData for Folder { + fn render(&self) -> String { + self.layers + .iter() + .filter(|layer| layer.visible) + .map(|layer| layer.data.render()) + .fold(String::with_capacity(self.layers.len() * 30), |s, n| s + "\n" + &n) + } +} +impl Folder { + pub fn add_layer(&mut self, layer: Layer, insert_index: isize) -> Option { + let mut insert_index = insert_index as i128; + if insert_index < 0 { + insert_index = self.layers.len() as i128 + insert_index as i128 + 1; + } + + if insert_index <= self.layers.len() as i128 && insert_index >= 0 { + self.layers.insert(insert_index as usize, layer); + self.layer_ids.insert(insert_index as usize, self.next_assignment_id); + self.next_assignment_id += 1; + Some(self.next_assignment_id - 1) + } else { + None + } + } + + pub fn remove_layer(&mut self, id: LayerId) -> Result<(), DocumentError> { + let pos = self.layer_ids.iter().position(|x| *x == id).ok_or(DocumentError::LayerNotFound)?; + self.layers.remove(pos); + self.layer_ids.remove(pos); + Ok(()) + } + + /// Returns a list of layers in the folder + pub fn list_layers(&self) -> &[LayerId] { + self.layer_ids.as_slice() + } + + pub fn layer(&self, id: LayerId) -> Option<&Layer> { + let pos = self.layer_ids.iter().position(|x| *x == id)?; + Some(&self.layers[pos]) + } + + pub fn layer_mut(&mut self, id: LayerId) -> Option<&mut Layer> { + let pos = self.layer_ids.iter().position(|x| *x == id)?; + Some(&mut self.layers[pos]) + } + + pub fn folder(&self, id: LayerId) -> Option<&Folder> { + match self.layer(id) { + Some(Layer { + data: LayerDataTypes::Folder(folder), .. + }) => Some(&folder), + _ => None, + } + } + + pub fn folder_mut(&mut self, id: LayerId) -> Option<&mut Folder> { + match self.layer_mut(id) { + Some(Layer { + data: LayerDataTypes::Folder(folder), .. + }) => Some(folder), + _ => None, + } + } +} + +impl Default for Folder { + fn default() -> Self { + Self { + layer_ids: vec![], + layers: vec![], + next_assignment_id: 0, + } + } +} diff --git a/core/document/src/layers/line.rs b/core/document/src/layers/line.rs new file mode 100644 index 00000000..ab9e991d --- /dev/null +++ b/core/document/src/layers/line.rs @@ -0,0 +1,30 @@ +use super::style; +use super::LayerData; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Line { + shape: kurbo::Line, + style: style::PathStyle, +} + +impl Line { + pub fn new(p0: impl Into, p1: impl Into, style: style::PathStyle) -> Line { + Line { + shape: kurbo::Line::new(p0, p1), + style, + } + } +} + +impl LayerData for Line { + fn render(&self) -> String { + format!( + r#""#, + self.shape.p0.x, + self.shape.p0.y, + self.shape.p1.x, + self.shape.p1.y, + self.style.render(), + ) + } +} diff --git a/core/document/src/layers/mod.rs b/core/document/src/layers/mod.rs new file mode 100644 index 00000000..3700c639 --- /dev/null +++ b/core/document/src/layers/mod.rs @@ -0,0 +1,54 @@ +pub mod style; + +pub mod circle; +pub use circle::Circle; + +pub mod line; +pub use line::Line; + +pub mod rect; +pub use rect::Rect; + +pub mod shape; +pub use shape::Shape; + +pub mod folder; +pub use folder::Folder; + +pub trait LayerData { + fn render(&self) -> String; +} + +#[derive(Debug, Clone, PartialEq)] +pub enum LayerDataTypes { + Folder(Folder), + Circle(Circle), + Rect(Rect), + Line(Line), + Shape(Shape), +} + +impl LayerDataTypes { + pub fn render(&self) -> String { + match self { + Self::Folder(f) => f.render(), + Self::Circle(c) => c.render(), + Self::Rect(r) => r.render(), + Self::Line(l) => l.render(), + Self::Shape(s) => s.render(), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Layer { + pub visible: bool, + pub name: Option, + pub data: LayerDataTypes, +} + +impl Layer { + pub fn new(data: LayerDataTypes) -> Self { + Self { visible: true, name: None, data } + } +} diff --git a/core/document/src/layers/rect.rs b/core/document/src/layers/rect.rs new file mode 100644 index 00000000..469ba7c2 --- /dev/null +++ b/core/document/src/layers/rect.rs @@ -0,0 +1,30 @@ +use super::style; +use super::LayerData; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Rect { + shape: kurbo::Rect, + style: style::PathStyle, +} + +impl Rect { + pub fn new(p0: impl Into, p1: impl Into, style: style::PathStyle) -> Rect { + Rect { + shape: kurbo::Rect::from_points(p0, p1), + style, + } + } +} + +impl LayerData for Rect { + fn render(&self) -> String { + format!( + r#""#, + self.shape.min_x(), + self.shape.min_y(), + self.shape.width(), + self.shape.height(), + self.style.render(), + ) + } +} diff --git a/core/document/src/layers/shape.rs b/core/document/src/layers/shape.rs new file mode 100644 index 00000000..427ebdf8 --- /dev/null +++ b/core/document/src/layers/shape.rs @@ -0,0 +1,25 @@ +use crate::shape_points; + +use super::style; +use super::LayerData; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Shape { + shape: shape_points::ShapePoints, + style: style::PathStyle, +} + +impl Shape { + pub fn new(center: impl Into, extent: impl Into, sides: u8, style: style::PathStyle) -> Shape { + Shape { + shape: shape_points::ShapePoints::new(center, extent, sides), + style, + } + } +} + +impl LayerData for Shape { + fn render(&self) -> String { + format!(r#""#, self.shape, self.style.render(),) + } +} diff --git a/core/document/src/layers/style/mod.rs b/core/document/src/layers/style/mod.rs new file mode 100644 index 00000000..7def803e --- /dev/null +++ b/core/document/src/layers/style/mod.rs @@ -0,0 +1,56 @@ +use crate::color::Color; + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub struct Fill { + color: Color, +} +impl Fill { + pub fn new(color: Color) -> Self { + Self { color } + } + pub fn render(&self) -> String { + format!("fill: #{};", self.color.as_hex()) + } +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub struct Stroke { + color: Color, + width: f32, +} + +impl Stroke { + pub fn new(color: Color, width: f32) -> Self { + Self { color, width } + } + pub fn render(&self) -> String { + format!("stroke: #{};stroke-width:{};", self.color.as_hex(), self.width) + } +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub struct PathStyle { + stroke: Option, + fill: Option, +} +impl PathStyle { + pub fn new(stroke: Option, fill: Option) -> Self { + Self { stroke, fill } + } + pub fn render(&self) -> String { + format!( + "style=\"{}{}\"", + match self.fill { + Some(fill) => fill.render(), + None => String::new(), + }, + match self.stroke { + Some(stroke) => stroke.render(), + None => String::new(), + }, + ) + } +} diff --git a/core/document/src/lib.rs b/core/document/src/lib.rs index 3a26e73d..c42707c8 100644 --- a/core/document/src/lib.rs +++ b/core/document/src/lib.rs @@ -1,37 +1,12 @@ +pub mod color; +pub mod document; +pub mod layers; pub mod operation; - mod shape_points; -pub use kurbo::{Circle, Line, Point, Rect, Vec2}; + pub use operation::Operation; -#[derive(Debug, Clone, PartialEq)] -pub enum LayerType { - Folder(Folder), - Circle(Circle), - Rect(Rect), - Line(Line), - Shape(shape_points::ShapePoints), -} - -impl LayerType { - pub fn render(&self) -> String { - match self { - Self::Folder(f) => f.render(), - Self::Circle(c) => { - format!(r#""#, c.center.x, c.center.y, c.radius) - } - 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) - } - Self::Shape(s) => { - format!(r#""#, s) - } - } - } -} +type LayerId = u64; #[derive(Debug, Clone, Copy, PartialEq)] pub enum DocumentError { @@ -39,302 +14,3 @@ pub enum DocumentError { InvalidPath, IndexOutOfBounds, } - -type LayerId = u64; - -#[derive(Debug, Clone, PartialEq)] -pub struct Layer { - visible: bool, - name: Option, - data: LayerType, -} - -impl Layer { - pub fn new(data: LayerType) -> Self { - Self { visible: true, name: None, data } - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct Folder { - next_assignment_id: LayerId, - layer_ids: Vec, - layers: Vec, -} - -impl Folder { - pub fn render(&self) -> String { - self.layers - .iter() - .filter(|layer| layer.visible) - .map(|layer| layer.data.render()) - .fold(String::with_capacity(self.layers.len() * 30), |s, n| s + "\n" + &n) - } - - fn add_layer(&mut self, layer: Layer, insert_index: isize) -> Option { - let mut insert_index = insert_index as i128; - if insert_index < 0 { - insert_index = self.layers.len() as i128 + insert_index as i128 + 1; - } - - if insert_index <= self.layers.len() as i128 && insert_index >= 0 { - self.layers.insert(insert_index as usize, layer); - self.layer_ids.insert(insert_index as usize, self.next_assignment_id); - self.next_assignment_id += 1; - Some(self.next_assignment_id - 1) - } else { - None - } - } - - fn remove_layer(&mut self, id: LayerId) -> Result<(), DocumentError> { - let pos = self.layer_ids.iter().position(|x| *x == id).ok_or(DocumentError::LayerNotFound)?; - self.layers.remove(pos); - self.layer_ids.remove(pos); - Ok(()) - } - - /// Returns a list of layers in the folder - pub fn list_layers(&self) -> &[LayerId] { - self.layer_ids.as_slice() - } - - fn layer(&self, id: LayerId) -> Option<&Layer> { - let pos = self.layer_ids.iter().position(|x| *x == id)?; - Some(&self.layers[pos]) - } - - fn layer_mut(&mut self, id: LayerId) -> Option<&mut Layer> { - let pos = self.layer_ids.iter().position(|x| *x == id)?; - Some(&mut self.layers[pos]) - } - - fn folder(&self, id: LayerId) -> Option<&Folder> { - match self.layer(id) { - Some(Layer { data: LayerType::Folder(folder), .. }) => Some(&folder), - _ => None, - } - } - - fn folder_mut(&mut self, id: LayerId) -> Option<&mut Folder> { - match self.layer_mut(id) { - Some(Layer { data: LayerType::Folder(folder), .. }) => Some(folder), - _ => None, - } - } -} - -impl Default for Folder { - fn default() -> Self { - Self { - layer_ids: vec![], - layers: vec![], - next_assignment_id: 0, - } - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct Document { - pub root: Folder, - pub work: Folder, - pub work_mount_path: Vec, - pub work_operations: Vec, - pub work_mounted: bool, -} - -impl Default for Document { - fn default() -> Self { - Self { - root: Folder::default(), - work: Folder::default(), - work_mount_path: Vec::new(), - work_operations: Vec::new(), - work_mounted: false, - } - } -} - -fn split_path(path: &[LayerId]) -> Result<(&[LayerId], LayerId), DocumentError> { - let id = path.last().ok_or(DocumentError::InvalidPath)?; - let folder_path = &path[0..path.len() - 1]; - Ok((folder_path, *id)) -} - -impl Document { - pub fn render(&self, path: &mut Vec) -> String { - if !self.work_mount_path.as_slice().starts_with(path) { - match &self.layer(path).unwrap().data { - LayerType::Folder(_) => (), - element => { - path.pop(); - return element.render(); - } - } - } - if path.as_slice() == self.work_mount_path { - let mut out = self.document_folder(path).unwrap().render(); - out += self.work.render().as_str(); - path.pop(); - return out; - } - let mut out = String::with_capacity(30); - for element in self.folder(path).unwrap().layer_ids.iter() { - path.push(*element); - out += self.render(path).as_str(); - } - out - } - - fn is_mounted(&self, mount_path: &[LayerId], path: &[LayerId]) -> bool { - path.starts_with(mount_path) && self.work_mounted - } - - pub fn folder(&self, mut path: &[LayerId]) -> Result<&Folder, DocumentError> { - let mut root = &self.root; - if self.is_mounted(self.work_mount_path.as_slice(), path) { - path = &path[self.work_mount_path.len()..]; - root = &self.work; - } - for id in path { - root = root.folder(*id).ok_or(DocumentError::LayerNotFound)?; - } - Ok(root) - } - - pub fn folder_mut(&mut self, mut path: &[LayerId]) -> Result<&mut Folder, DocumentError> { - let mut root = if self.is_mounted(self.work_mount_path.as_slice(), path) { - path = &path[self.work_mount_path.len()..]; - &mut self.work - } else { - &mut self.root - }; - for id in path { - root = root.folder_mut(*id).ok_or(DocumentError::LayerNotFound)?; - } - Ok(root) - } - - pub fn document_folder(&self, path: &[LayerId]) -> Result<&Folder, DocumentError> { - let mut root = &self.root; - for id in path { - root = root.folder(*id).ok_or(DocumentError::LayerNotFound)?; - } - Ok(root) - } - - pub fn document_folder_mut(&mut self, path: &[LayerId]) -> Result<&mut Folder, DocumentError> { - let mut root = &mut self.root; - for id in path { - root = root.folder_mut(*id).ok_or(DocumentError::LayerNotFound)?; - } - Ok(root) - } - - pub fn layer(&self, path: &[LayerId]) -> Result<&Layer, DocumentError> { - let (path, id) = split_path(path)?; - self.folder(path)?.layer(id).ok_or(DocumentError::LayerNotFound) - } - - pub fn layer_mut(&mut self, path: &[LayerId]) -> Result<&mut Layer, DocumentError> { - let (path, id) = split_path(path)?; - self.folder_mut(path)?.layer_mut(id).ok_or(DocumentError::LayerNotFound) - } - - pub fn set_layer(&mut self, path: &[LayerId], layer: Layer) -> Result<(), DocumentError> { - let mut folder = &mut self.root; - if let Ok((path, id)) = split_path(path) { - folder = self.folder_mut(path)?; - if let Some(folder_layer) = folder.layer_mut(id) { - *folder_layer = layer; - return Ok(()); - } - } - folder.add_layer(layer, -1).ok_or(DocumentError::IndexOutOfBounds)?; - Ok(()) - } - - /// Passing a negative `insert_index` indexes relative to the end - /// -1 is equivalent to adding the layer to the top - pub fn add_layer(&mut self, path: &[LayerId], layer: Layer, insert_index: isize) -> Result { - let folder = self.folder_mut(path)?; - folder.add_layer(layer, insert_index).ok_or(DocumentError::IndexOutOfBounds) - } - - pub fn delete(&mut self, path: &[LayerId]) -> Result<(), DocumentError> { - let (path, id) = split_path(path)?; - self.folder_mut(path)?.remove_layer(id)?; - Ok(()) - } - - pub fn handle_operation(&mut self, operation: Operation, update_frontend: &F) -> Result<(), DocumentError> { - self.work_operations.push(operation.clone()); - match operation { - Operation::AddCircle { path, insert_index, cx, cy, r } => { - self.add_layer(&path, Layer::new(LayerType::Circle(Circle::new(Point::new(cx, cy), r))), insert_index)?; - - update_frontend(self.render(&mut vec![])); - } - Operation::AddRect { path, insert_index, x0, y0, x1, y1 } => { - self.add_layer(&path, Layer::new(LayerType::Rect(Rect::from_points(Point::new(x0, y0), Point::new(x1, y1)))), insert_index)?; - - update_frontend(self.render(&mut vec![])); - } - 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(&mut vec![])); - } - Operation::AddShape { - path, - insert_index, - x0, - y0, - x1, - y1, - sides, - } => { - let s = shape_points::ShapePoints::new(Point::new(x0, y0), Vec2 { x: x0 - x1, y: y0 - y1 }, sides); - self.add_layer(&path, Layer::new(LayerType::Shape(s)), insert_index)?; - - update_frontend(self.render(&mut vec![])); - } - Operation::DeleteLayer { path } => { - self.delete(&path)?; - - update_frontend(self.render(&mut vec![])); - } - Operation::AddFolder { path } => self.set_layer(&path, Layer::new(LayerType::Folder(Folder::default())))?, - Operation::MountWorkingFolder { path } => { - self.work_operations.clear(); - self.work_mount_path = path; - self.work = Folder::default(); - self.work_mounted = true; - } - Operation::DiscardWorkingFolder => { - self.work_operations.clear(); - self.work_mount_path = vec![]; - self.work = Folder::default(); - self.work_mounted = false; - } - Operation::ClearWorkingFolder => { - self.work_operations.clear(); - self.work = Folder::default(); - } - Operation::CommitTransaction => { - let mut ops = Vec::new(); - std::mem::swap(&mut ops, &mut self.work_operations); - let len = ops.len() - 1; - self.work_mounted = false; - self.work_mount_path = vec![]; - self.work = Folder::default(); - for operation in ops.into_iter().take(len) { - self.handle_operation(operation, update_frontend)? - } - - update_frontend(self.render(&mut vec![])); - } - } - Ok(()) - } -} diff --git a/core/document/src/operation.rs b/core/document/src/operation.rs index e19ec99c..fcfe2b69 100644 --- a/core/document/src/operation.rs +++ b/core/document/src/operation.rs @@ -1,4 +1,4 @@ -use crate::LayerId; +use crate::{layers::style, LayerId}; #[derive(Debug, Clone, PartialEq)] pub enum Operation { @@ -8,6 +8,7 @@ pub enum Operation { cx: f64, cy: f64, r: f64, + style: style::PathStyle, }, AddRect { path: Vec, @@ -16,6 +17,7 @@ pub enum Operation { y0: f64, x1: f64, y1: f64, + style: style::PathStyle, }, AddLine { path: Vec, @@ -24,6 +26,7 @@ pub enum Operation { y0: f64, x1: f64, y1: f64, + style: style::PathStyle, }, AddShape { path: Vec, @@ -33,6 +36,7 @@ pub enum Operation { x1: f64, y1: f64, sides: u8, + style: style::PathStyle, }, DeleteLayer { path: Vec, diff --git a/core/editor/src/dispatcher/mod.rs b/core/editor/src/dispatcher/mod.rs index b51b72b6..d90096d5 100644 --- a/core/editor/src/dispatcher/mod.rs +++ b/core/editor/src/dispatcher/mod.rs @@ -14,30 +14,30 @@ impl Dispatcher { match event { Event::SelectTool(tool_name) => { - editor_state.tool_state.active_tool_type = *tool_name; + editor_state.tool_state.tool_data.active_tool_type = *tool_name; self.dispatch_response(Response::SetActiveTool { tool_name: tool_name.to_string() }); } Event::SelectPrimaryColor(color) => { - editor_state.tool_state.primary_color = *color; + editor_state.tool_state.document_tool_data.primary_color = *color; } Event::SelectSecondaryColor(color) => { - editor_state.tool_state.secondary_color = *color; + editor_state.tool_state.document_tool_data.secondary_color = *color; } Event::SwapColors => { editor_state.tool_state.swap_colors(); } Event::ResetColors => { - editor_state.tool_state.primary_color = Color::BLACK; - editor_state.tool_state.secondary_color = Color::WHITE; + editor_state.tool_state.document_tool_data.primary_color = Color::BLACK; + editor_state.tool_state.document_tool_data.secondary_color = Color::WHITE; } Event::MouseDown(mouse_state) => { - editor_state.tool_state.mouse_state = *mouse_state; + editor_state.tool_state.document_tool_data.mouse_state = *mouse_state; } Event::MouseUp(mouse_state) => { - editor_state.tool_state.mouse_state = *mouse_state; + editor_state.tool_state.document_tool_data.mouse_state = *mouse_state; } Event::MouseMove(pos) => { - editor_state.tool_state.mouse_state.position = *pos; + editor_state.tool_state.document_tool_data.mouse_state.position = *pos; } Event::KeyUp(key) => (), Event::KeyDown(key) => { @@ -58,31 +58,31 @@ impl Dispatcher { log::debug!("set log verbosity to trace"); } Key::KeyV => { - editor_state.tool_state.active_tool_type = ToolType::Select; + editor_state.tool_state.tool_data.active_tool_type = ToolType::Select; self.dispatch_response(Response::SetActiveTool { tool_name: ToolType::Select.to_string(), }); } Key::KeyL => { - editor_state.tool_state.active_tool_type = ToolType::Line; + editor_state.tool_state.tool_data.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; + editor_state.tool_state.tool_data.active_tool_type = ToolType::Rectangle; self.dispatch_response(Response::SetActiveTool { tool_name: ToolType::Rectangle.to_string(), }); } Key::KeyY => { - editor_state.tool_state.active_tool_type = ToolType::Shape; + editor_state.tool_state.tool_data.active_tool_type = ToolType::Shape; self.dispatch_response(Response::SetActiveTool { tool_name: ToolType::Shape.to_string(), }); } Key::KeyE => { - editor_state.tool_state.active_tool_type = ToolType::Ellipse; + editor_state.tool_state.tool_data.active_tool_type = ToolType::Ellipse; self.dispatch_response(Response::SetActiveTool { tool_name: ToolType::Ellipse.to_string(), }); @@ -95,7 +95,11 @@ impl Dispatcher { } } - let (responses, operations) = editor_state.tool_state.active_tool()?.handle_input(event, &editor_state.document); + let (responses, operations) = editor_state + .tool_state + .tool_data + .active_tool()? + .handle_input(event, &editor_state.document, &editor_state.tool_state.document_tool_data); self.dispatch_operations(&mut editor_state.document, operations); // TODO - Dispatch Responses diff --git a/core/editor/src/lib.rs b/core/editor/src/lib.rs index 9bf330bd..7e0c403a 100644 --- a/core/editor/src/lib.rs +++ b/core/editor/src/lib.rs @@ -1,7 +1,6 @@ #[macro_use] mod macros; -mod color; mod dispatcher; mod error; pub mod hint; @@ -12,7 +11,7 @@ pub mod workspace; pub use error::EditorError; #[doc(inline)] -pub use color::Color; +pub use document_core::color::Color; #[doc(inline)] pub use dispatcher::events; @@ -21,7 +20,7 @@ pub use dispatcher::events; pub use dispatcher::Callback; use dispatcher::Dispatcher; -use document_core::Document; +use document_core::document::Document; use tools::ToolFsmState; use workspace::Workspace; diff --git a/core/editor/src/tools/crop.rs b/core/editor/src/tools/crop.rs index 57a3ef80..ef3f1c9f 100644 --- a/core/editor/src/tools/crop.rs +++ b/core/editor/src/tools/crop.rs @@ -3,11 +3,13 @@ use crate::tools::Tool; use crate::Document; use document_core::Operation; +use super::DocumentToolData; + #[derive(Default)] pub struct Crop; impl Tool for Crop { - fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec, Vec) { + fn handle_input(&mut self, event: &Event, document: &Document, tool_data: &DocumentToolData) -> (Vec, Vec) { todo!(); } } diff --git a/core/editor/src/tools/ellipse.rs b/core/editor/src/tools/ellipse.rs index 84dba399..53263f85 100644 --- a/core/editor/src/tools/ellipse.rs +++ b/core/editor/src/tools/ellipse.rs @@ -2,8 +2,11 @@ use crate::events::{Event, Response}; 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 Ellipse { fsm_state: EllipseToolFsmState, @@ -11,10 +14,10 @@ pub struct Ellipse { } impl Tool for Ellipse { - fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec, Vec) { + fn handle_input(&mut self, event: &Event, document: &Document, tool_data: &DocumentToolData) -> (Vec, Vec) { 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); + self.fsm_state = self.fsm_state.transition(event, document, tool_data, &mut self.data, &mut responses, &mut operations); (responses, operations) } @@ -39,7 +42,7 @@ struct EllipseToolData { impl Fsm for EllipseToolFsmState { type ToolData = EllipseToolData; - fn transition(self, event: &Event, document: &Document, data: &mut Self::ToolData, responses: &mut Vec, operations: &mut Vec) -> Self { + fn transition(self, event: &Event, document: &Document, tool_data: &DocumentToolData, data: &mut Self::ToolData, responses: &mut Vec, operations: &mut Vec) -> Self { match (self, event) { (EllipseToolFsmState::Ready, Event::MouseDown(mouse_state)) if mouse_state.mouse_keys.contains(MouseKeys::LEFT) => { data.drag_start = mouse_state.position; @@ -62,6 +65,7 @@ impl Fsm for EllipseToolFsmState { cx: data.drag_start.x as f64, cy: data.drag_start.y as f64, r: data.drag_start.distance(&mouse_state), + style: style::PathStyle::new(None, Some(style::Fill::new(tool_data.primary_color))), }); EllipseToolFsmState::LmbDown @@ -78,6 +82,7 @@ impl Fsm for EllipseToolFsmState { cx: data.drag_start.x as f64, cy: data.drag_start.y as f64, r: data.drag_start.distance(&mouse_state.position), + style: style::PathStyle::new(None, Some(style::Fill::new(tool_data.primary_color))), }); operations.push(Operation::CommitTransaction); diff --git a/core/editor/src/tools/line.rs b/core/editor/src/tools/line.rs index fc70cf91..f815dac6 100644 --- a/core/editor/src/tools/line.rs +++ b/core/editor/src/tools/line.rs @@ -2,8 +2,11 @@ use crate::events::{Event, Response}; 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 Line { fsm_state: LineToolFsmState, @@ -11,10 +14,10 @@ pub struct Line { } impl Tool for Line { - fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec, Vec) { + fn handle_input(&mut self, event: &Event, document: &Document, tool_data: &DocumentToolData) -> (Vec, Vec) { 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); + self.fsm_state = self.fsm_state.transition(event, document, tool_data, &mut self.data, &mut responses, &mut operations); (responses, operations) } @@ -39,7 +42,7 @@ struct LineToolData { 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 { + fn transition(self, event: &Event, document: &Document, tool_data: &DocumentToolData, 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; @@ -64,6 +67,7 @@ impl Fsm for LineToolFsmState { y0: start.y as f64, x1: end.x as f64, y1: end.y as f64, + style: style::PathStyle::new(Some(style::Stroke::new(tool_data.primary_color, 5.)), None), }); LineToolFsmState::Ready diff --git a/core/editor/src/tools/mod.rs b/core/editor/src/tools/mod.rs index 8cba2777..371ebe66 100644 --- a/core/editor/src/tools/mod.rs +++ b/core/editor/src/tools/mod.rs @@ -17,47 +17,64 @@ use document_core::Operation; use std::{collections::HashMap, fmt}; pub trait Tool { - fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec, Vec); + fn handle_input(&mut self, event: &Event, document: &Document, tool_data: &DocumentToolData) -> (Vec, Vec); } pub trait Fsm { type ToolData; - fn transition(self, event: &Event, document: &Document, data: &mut Self::ToolData, responses: &mut Vec, operations: &mut Vec) -> Self; + fn transition(self, event: &Event, document: &Document, tool_data: &DocumentToolData, data: &mut Self::ToolData, responses: &mut Vec, operations: &mut Vec) -> Self; } -pub struct ToolFsmState { +pub struct DocumentToolData { pub mouse_state: MouseState, pub mod_keys: ModKeys, - pub trace: Trace, pub primary_color: Color, pub secondary_color: Color, +} +pub struct ToolData { pub active_tool_type: ToolType, pub tools: HashMap>, tool_settings: HashMap, } +impl ToolData { + pub fn active_tool(&mut self) -> Result<&mut Box, EditorError> { + self.tools.get_mut(&self.active_tool_type).ok_or(EditorError::UnknownTool) + } +} + +pub struct ToolFsmState { + pub document_tool_data: DocumentToolData, + pub tool_data: ToolData, + pub trace: Trace, +} + impl Default for ToolFsmState { fn default() -> Self { ToolFsmState { - mouse_state: MouseState::default(), - mod_keys: ModKeys::default(), trace: Trace::new(), - primary_color: Color::BLACK, - secondary_color: Color::WHITE, - 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_data: ToolData { + 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(), + }, + document_tool_data: DocumentToolData { + mouse_state: MouseState::default(), + mod_keys: ModKeys::default(), + primary_color: Color::BLACK, + secondary_color: Color::WHITE, }, - tool_settings: default_tool_settings(), } } } @@ -69,17 +86,13 @@ impl ToolFsmState { pub fn record_trace_point(&mut self) { self.trace.push(TracePoint { - mouse_state: self.mouse_state, - mod_keys: self.mod_keys, + mouse_state: self.document_tool_data.mouse_state, + mod_keys: self.document_tool_data.mod_keys, }) } - pub fn active_tool(&mut self) -> Result<&mut Box, EditorError> { - self.tools.get_mut(&self.active_tool_type).ok_or(EditorError::UnknownTool) - } - pub fn swap_colors(&mut self) { - std::mem::swap(&mut self.primary_color, &mut self.secondary_color); + std::mem::swap(&mut self.document_tool_data.primary_color, &mut self.document_tool_data.secondary_color); } } diff --git a/core/editor/src/tools/navigate.rs b/core/editor/src/tools/navigate.rs index 28b82c63..8c021a3f 100644 --- a/core/editor/src/tools/navigate.rs +++ b/core/editor/src/tools/navigate.rs @@ -3,11 +3,13 @@ use crate::tools::Tool; use crate::Document; use document_core::Operation; +use super::DocumentToolData; + #[derive(Default)] pub struct Navigate; impl Tool for Navigate { - fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec, Vec) { + fn handle_input(&mut self, event: &Event, document: &Document, tool_data: &DocumentToolData) -> (Vec, Vec) { todo!(); } } diff --git a/core/editor/src/tools/path.rs b/core/editor/src/tools/path.rs index 454ffb45..1d00e2b9 100644 --- a/core/editor/src/tools/path.rs +++ b/core/editor/src/tools/path.rs @@ -3,11 +3,13 @@ use crate::tools::Tool; use crate::Document; use document_core::Operation; +use super::DocumentToolData; + #[derive(Default)] pub struct Path; impl Tool for Path { - fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec, Vec) { + fn handle_input(&mut self, event: &Event, document: &Document, tool_data: &DocumentToolData) -> (Vec, Vec) { todo!(); } } diff --git a/core/editor/src/tools/pen.rs b/core/editor/src/tools/pen.rs index 057bc7f6..7d2f2a03 100644 --- a/core/editor/src/tools/pen.rs +++ b/core/editor/src/tools/pen.rs @@ -3,11 +3,13 @@ use crate::tools::Tool; use crate::Document; use document_core::Operation; +use super::DocumentToolData; + #[derive(Default)] pub struct Pen; impl Tool for Pen { - fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec, Vec) { + fn handle_input(&mut self, event: &Event, document: &Document, tool_data: &DocumentToolData) -> (Vec, Vec) { todo!(); } } diff --git a/core/editor/src/tools/rectangle.rs b/core/editor/src/tools/rectangle.rs index cd27e3db..84a7dddd 100644 --- a/core/editor/src/tools/rectangle.rs +++ b/core/editor/src/tools/rectangle.rs @@ -2,8 +2,11 @@ use crate::events::{Event, Response}; 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 Rectangle { fsm_state: RectangleToolFsmState, @@ -11,10 +14,10 @@ pub struct Rectangle { } impl Tool for Rectangle { - fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec, Vec) { + fn handle_input(&mut self, event: &Event, document: &Document, tool_data: &DocumentToolData) -> (Vec, Vec) { 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); + self.fsm_state = self.fsm_state.transition(event, document, tool_data, &mut self.data, &mut responses, &mut operations); (responses, operations) } @@ -39,7 +42,7 @@ struct RectangleToolData { impl Fsm for RectangleToolFsmState { type ToolData = RectangleToolData; - fn transition(self, event: &Event, document: &Document, data: &mut Self::ToolData, responses: &mut Vec, operations: &mut Vec) -> Self { + fn transition(self, event: &Event, document: &Document, tool_data: &DocumentToolData, data: &mut Self::ToolData, responses: &mut Vec, operations: &mut Vec) -> Self { match (self, event) { (RectangleToolFsmState::Ready, Event::MouseDown(mouse_state)) if mouse_state.mouse_keys.contains(MouseKeys::LEFT) => { data.drag_start = mouse_state.position; @@ -65,6 +68,7 @@ impl Fsm for RectangleToolFsmState { y0: start.y as f64, x1: end.x as f64, y1: end.y as f64, + style: style::PathStyle::new(None, Some(style::Fill::new(tool_data.primary_color))), }); RectangleToolFsmState::Ready diff --git a/core/editor/src/tools/sample.rs b/core/editor/src/tools/sample.rs index 07f667b5..8d686047 100644 --- a/core/editor/src/tools/sample.rs +++ b/core/editor/src/tools/sample.rs @@ -3,11 +3,13 @@ use crate::tools::Tool; use crate::Document; use document_core::Operation; +use super::DocumentToolData; + #[derive(Default)] pub struct Sample; impl Tool for Sample { - fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec, Vec) { + fn handle_input(&mut self, event: &Event, document: &Document, tool_data: &DocumentToolData) -> (Vec, Vec) { todo!(); } } diff --git a/core/editor/src/tools/select.rs b/core/editor/src/tools/select.rs index 06ed7806..a1585f76 100644 --- a/core/editor/src/tools/select.rs +++ b/core/editor/src/tools/select.rs @@ -4,6 +4,8 @@ use crate::tools::{Fsm, Tool}; use crate::Document; use document_core::Operation; +use super::DocumentToolData; + #[derive(Default)] pub struct Select { fsm_state: SelectToolFsmState, @@ -11,10 +13,10 @@ pub struct Select { } impl Tool for Select { - fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec, Vec) { + fn handle_input(&mut self, event: &Event, document: &Document, tool_data: &DocumentToolData) -> (Vec, Vec) { 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); + self.fsm_state = self.fsm_state.transition(event, document, tool_data, &mut self.data, &mut responses, &mut operations); (responses, operations) } @@ -39,7 +41,7 @@ struct SelectToolData; impl Fsm for SelectToolFsmState { type ToolData = SelectToolData; - fn transition(self, event: &Event, document: &Document, data: &mut Self::ToolData, responses: &mut Vec, operations: &mut Vec) -> Self { + fn transition(self, event: &Event, document: &Document, tool_data: &DocumentToolData, data: &mut Self::ToolData, responses: &mut Vec, operations: &mut Vec) -> Self { match (self, event) { (SelectToolFsmState::Ready, Event::MouseDown(mouse_state)) if mouse_state.mouse_keys.contains(MouseKeys::LEFT) => SelectToolFsmState::LmbDown, diff --git a/core/editor/src/tools/shape.rs b/core/editor/src/tools/shape.rs index 8f5a68fb..fb68445a 100644 --- a/core/editor/src/tools/shape.rs +++ b/core/editor/src/tools/shape.rs @@ -2,8 +2,11 @@ use crate::events::{Event, Response}; 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 Shape { fsm_state: ShapeToolFsmState, @@ -11,10 +14,10 @@ pub struct Shape { } impl Tool for Shape { - fn handle_input(&mut self, event: &Event, document: &Document) -> (Vec, Vec) { + fn handle_input(&mut self, event: &Event, document: &Document, tool_data: &DocumentToolData) -> (Vec, Vec) { 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); + self.fsm_state = self.fsm_state.transition(event, document, tool_data, &mut self.data, &mut responses, &mut operations); (responses, operations) } @@ -40,7 +43,7 @@ struct ShapeToolData { impl Fsm for ShapeToolFsmState { type ToolData = ShapeToolData; - fn transition(self, event: &Event, document: &Document, data: &mut Self::ToolData, responses: &mut Vec, operations: &mut Vec) -> Self { + fn transition(self, event: &Event, document: &Document, tool_data: &DocumentToolData, data: &mut Self::ToolData, responses: &mut Vec, operations: &mut Vec) -> Self { match (self, event) { (ShapeToolFsmState::Ready, Event::MouseDown(mouse_state)) if mouse_state.mouse_keys.contains(MouseKeys::LEFT) => { data.drag_start = mouse_state.position; @@ -60,6 +63,7 @@ impl Fsm for ShapeToolFsmState { let start = data.drag_start; let end = mouse_state.position; + // TODO: Set the sides value and use it for the operation. let sides = data.sides; operations.push(Operation::AddShape { path: vec![], @@ -69,6 +73,7 @@ impl Fsm for ShapeToolFsmState { x1: end.x as f64, y1: end.y as f64, sides: 6, + style: style::PathStyle::new(None, Some(style::Fill::new(tool_data.primary_color))), }); ShapeToolFsmState::Ready