diff --git a/core/document/src/document.rs b/core/document/src/document.rs index 4a5ad2848..670f134c9 100644 --- a/core/document/src/document.rs +++ b/core/document/src/document.rs @@ -34,6 +34,9 @@ fn split_path(path: &[LayerId]) -> Result<(&[LayerId], LayerId), DocumentError> } impl Document { + /// Renders the layer graph with the root `path` as an SVG string. + /// This operation merges currently mounted folder and document_folder + /// contents together. pub fn render(&self, path: &mut Vec) -> String { if !self.work_mount_path.as_slice().starts_with(path) { match &self.layer(path).unwrap().data { @@ -58,7 +61,7 @@ impl Document { out } - /// Wrapper around render, that returns the whole document as a Response + /// Wrapper around render, that returns the whole document as a Response. pub fn render_root(&self) -> DocumentResponse { DocumentResponse::UpdateCanvas { document: self.render(&mut vec![]) } } @@ -67,6 +70,10 @@ impl Document { path.starts_with(mount_path) && self.work_mounted } + /// Returns a reference to the requested folder. Fails if the path does not exist, + /// or if the requested layer is not of type folder. + /// This function respects mounted folders and will thus not contain the layers already + /// present in the document if a temporary folder is mounted on top. 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) { @@ -79,6 +86,10 @@ impl Document { Ok(root) } + /// Returns a mutable reference to the requested folder. Fails if the path does not exist, + /// or if the requested layer is not of type folder. + /// This function respects mounted folders and will thus not contain the layers already + /// present in the document if a temporary folder is mounted on top. 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()..]; @@ -92,6 +103,10 @@ impl Document { Ok(root) } + /// Returns a reference to the requested folder. Fails if the path does not exist, + /// or if the requested layer is not of type folder. + /// This function does **not** respect mounted folders and will always return the current + /// state of the document, disregarding any temporary modifications. pub fn document_folder(&self, path: &[LayerId]) -> Result<&Folder, DocumentError> { let mut root = &self.root; for id in path { @@ -100,6 +115,10 @@ impl Document { Ok(root) } + /// Returns a mutable reference to the requested folder. Fails if the path does not exist, + /// or if the requested layer is not of type folder. + /// This function does **not** respect mounted folders and will always return the current + /// state of the document, disregarding any temporary modifications. pub fn document_folder_mut(&mut self, path: &[LayerId]) -> Result<&mut Folder, DocumentError> { let mut root = &mut self.root; for id in path { @@ -108,16 +127,19 @@ impl Document { Ok(root) } + /// Returns a reference to the layer struct at the specified `path`. pub fn layer(&self, path: &[LayerId]) -> Result<&Layer, DocumentError> { let (path, id) = split_path(path)?; self.folder(path)?.layer(id).ok_or(DocumentError::LayerNotFound) } + /// Returns a mutable reference to the layer struct at the specified `path`. 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) } + /// Replaces the layer at the specified `path` with `layer`. 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) { @@ -131,19 +153,23 @@ impl Document { Ok(()) } - /// Passing a negative `insert_index` indexes relative to the end - /// -1 is equivalent to adding the layer to the top + /// Adds a new layer to the folder specified by `path`. + /// 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) } + /// Deletes the layer specified by `path`. pub fn delete(&mut self, path: &[LayerId]) -> Result<(), DocumentError> { let (path, id) = split_path(path)?; self.document_folder_mut(path)?.remove_layer(id)?; Ok(()) } + /// Returns a list of `LayerPanelEntry`s intended for display purposes. These don't contain + /// any actual data, but rather metadata such as visibility and names of the layers. pub fn layer_panel(&self, path: &[LayerId]) -> Result, DocumentError> { let folder = self.document_folder(path)?; let l_type = |layer: &LayerDataTypes| match layer { @@ -159,13 +185,15 @@ impl Document { Ok(entries) } - pub fn handle_operation)>(&mut self, operation: Operation, send_response: &F) -> Result<(), DocumentError> { + /// Mutate the document by applying the `operation` to it. If the operation necessitates a + /// reaction from the frontend, responses may be returned. + pub fn handle_operation(&mut self, operation: Operation) -> Result>, DocumentError> { self.work_operations.push(operation.clone()); - match operation { + let responses = 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)?; - send_response(vec![self.render_root()]); + Some(vec![self.render_root()]) } Operation::AddRect { path, @@ -182,7 +210,7 @@ impl Document { insert_index, )?; - send_response(vec![self.render_root()]); + Some(vec![self.render_root()]) } Operation::AddLine { path, @@ -199,13 +227,13 @@ impl Document { insert_index, )?; - send_response(vec![self.render_root()]); + Some(vec![self.render_root()]) } 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)?; - send_response(vec![self.render_root()]); + Some(vec![self.render_root()]) } Operation::AddShape { path, @@ -220,29 +248,36 @@ impl Document { 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)?; - send_response(vec![self.render_root()]); + Some(vec![self.render_root()]) } Operation::DeleteLayer { path } => { self.delete(&path)?; - send_response(vec![self.render_root()]); + Some(vec![self.render_root()]) + } + Operation::AddFolder { path } => { + self.set_layer(&path, Layer::new(LayerDataTypes::Folder(Folder::default())))?; + + Some(vec![self.render_root()]) } - 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; + None } Operation::DiscardWorkingFolder => { self.work_operations.clear(); self.work_mount_path = vec![]; self.work = Folder::default(); self.work_mounted = false; + None } Operation::ClearWorkingFolder => { self.work_operations.clear(); self.work = Folder::default(); + None } Operation::CommitTransaction => { let mut ops = Vec::new(); @@ -253,14 +288,21 @@ impl Document { self.work_mounted = false; self.work_mount_path = vec![]; self.work = Folder::default(); + let mut responses = vec![]; for operation in ops.into_iter().take(len) { - self.handle_operation(operation, send_response)? + if let Some(op_responses) = self.handle_operation(operation)? { + for response in op_responses { + if !matches!(response, DocumentResponse::UpdateCanvas { .. }) { + responses.push(response); + } + } + } } let children = self.layer_panel(path.as_slice())?; - send_response(vec![self.render_root(), DocumentResponse::ExpandFolder { path, children }]); + Some(vec![self.render_root(), DocumentResponse::ExpandFolder { path, children }]) } - } - Ok(()) + }; + Ok(responses) } } diff --git a/core/editor/src/dispatcher/mod.rs b/core/editor/src/dispatcher/mod.rs index fb617ef2a..f8a608ea3 100644 --- a/core/editor/src/dispatcher/mod.rs +++ b/core/editor/src/dispatcher/mod.rs @@ -1,7 +1,7 @@ pub mod events; use crate::{tools::ToolType, Color, Document, EditorError, EditorState}; use document_core::Operation; -use events::{Event, Key, Response, ToolResponse}; +use events::{DocumentResponse, Event, Key, Response, ToolResponse}; pub type Callback = Box; pub struct Dispatcher { @@ -107,29 +107,35 @@ impl Dispatcher { } } - let (responses, operations) = editor_state + let (tool_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); - self.dispatch_responses(responses); + let document_responses = self.dispatch_operations(&mut editor_state.document, operations); + self.dispatch_responses(tool_responses); + self.dispatch_responses(document_responses); Ok(()) } - fn dispatch_operations>(&self, document: &mut Document, operations: I) { + fn dispatch_operations>(&self, document: &mut Document, operations: I) -> Vec { + let mut responses = vec![]; for operation in operations { - if let Err(error) = self.dispatch_operation(document, operation) { - log::error!("{}", error); + match self.dispatch_operation(document, operation) { + Ok(Some(mut res)) => { + responses.append(&mut res); + } + Ok(None) => (), + Err(error) => log::error!("{}", error), } } + responses } - fn dispatch_operation(&self, document: &mut Document, operation: Operation) -> Result<(), EditorError> { - document.handle_operation(operation, &|responses| self.dispatch_responses(responses))?; - Ok(()) + fn dispatch_operation(&self, document: &mut Document, operation: Operation) -> Result>, EditorError> { + Ok(document.handle_operation(operation)?) } pub fn dispatch_responses, I: IntoIterator>(&self, responses: I) {