mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-30 19:18:13 +08:00
Add caching to svg rendering (#94)
* Add documentation for functions in the document library * Restructure backchannel
This commit is contained in:
committed by
Keavon Chambers
parent
8d683a4162
commit
457c465342
@@ -34,6 +34,9 @@ fn split_path(path: &[LayerId]) -> Result<(&[LayerId], LayerId), DocumentError>
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Document {
|
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<LayerId>) -> String {
|
pub fn render(&self, path: &mut Vec<LayerId>) -> String {
|
||||||
if !self.work_mount_path.as_slice().starts_with(path) {
|
if !self.work_mount_path.as_slice().starts_with(path) {
|
||||||
match &self.layer(path).unwrap().data {
|
match &self.layer(path).unwrap().data {
|
||||||
@@ -58,7 +61,7 @@ impl Document {
|
|||||||
out
|
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 {
|
pub fn render_root(&self) -> DocumentResponse {
|
||||||
DocumentResponse::UpdateCanvas { document: self.render(&mut vec![]) }
|
DocumentResponse::UpdateCanvas { document: self.render(&mut vec![]) }
|
||||||
}
|
}
|
||||||
@@ -67,6 +70,10 @@ impl Document {
|
|||||||
path.starts_with(mount_path) && self.work_mounted
|
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> {
|
pub fn folder(&self, mut path: &[LayerId]) -> Result<&Folder, DocumentError> {
|
||||||
let mut root = &self.root;
|
let mut root = &self.root;
|
||||||
if self.is_mounted(self.work_mount_path.as_slice(), path) {
|
if self.is_mounted(self.work_mount_path.as_slice(), path) {
|
||||||
@@ -79,6 +86,10 @@ impl Document {
|
|||||||
Ok(root)
|
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> {
|
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) {
|
let mut root = if self.is_mounted(self.work_mount_path.as_slice(), path) {
|
||||||
path = &path[self.work_mount_path.len()..];
|
path = &path[self.work_mount_path.len()..];
|
||||||
@@ -92,6 +103,10 @@ impl Document {
|
|||||||
Ok(root)
|
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> {
|
pub fn document_folder(&self, path: &[LayerId]) -> Result<&Folder, DocumentError> {
|
||||||
let mut root = &self.root;
|
let mut root = &self.root;
|
||||||
for id in path {
|
for id in path {
|
||||||
@@ -100,6 +115,10 @@ impl Document {
|
|||||||
Ok(root)
|
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> {
|
pub fn document_folder_mut(&mut self, path: &[LayerId]) -> Result<&mut Folder, DocumentError> {
|
||||||
let mut root = &mut self.root;
|
let mut root = &mut self.root;
|
||||||
for id in path {
|
for id in path {
|
||||||
@@ -108,16 +127,19 @@ impl Document {
|
|||||||
Ok(root)
|
Ok(root)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the layer struct at the specified `path`.
|
||||||
pub fn layer(&self, path: &[LayerId]) -> Result<&Layer, DocumentError> {
|
pub fn layer(&self, path: &[LayerId]) -> Result<&Layer, DocumentError> {
|
||||||
let (path, id) = split_path(path)?;
|
let (path, id) = split_path(path)?;
|
||||||
self.folder(path)?.layer(id).ok_or(DocumentError::LayerNotFound)
|
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> {
|
pub fn layer_mut(&mut self, path: &[LayerId]) -> Result<&mut Layer, DocumentError> {
|
||||||
let (path, id) = split_path(path)?;
|
let (path, id) = split_path(path)?;
|
||||||
self.folder_mut(path)?.layer_mut(id).ok_or(DocumentError::LayerNotFound)
|
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> {
|
pub fn set_layer(&mut self, path: &[LayerId], layer: Layer) -> Result<(), DocumentError> {
|
||||||
let mut folder = &mut self.root;
|
let mut folder = &mut self.root;
|
||||||
if let Ok((path, id)) = split_path(path) {
|
if let Ok((path, id)) = split_path(path) {
|
||||||
@@ -131,19 +153,23 @@ impl Document {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Passing a negative `insert_index` indexes relative to the end
|
/// Adds a new layer to the folder specified by `path`.
|
||||||
/// -1 is equivalent to adding the layer to the top
|
/// 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<LayerId, DocumentError> {
|
pub fn add_layer(&mut self, path: &[LayerId], layer: Layer, insert_index: isize) -> Result<LayerId, DocumentError> {
|
||||||
let folder = self.folder_mut(path)?;
|
let folder = self.folder_mut(path)?;
|
||||||
folder.add_layer(layer, insert_index).ok_or(DocumentError::IndexOutOfBounds)
|
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> {
|
pub fn delete(&mut self, path: &[LayerId]) -> Result<(), DocumentError> {
|
||||||
let (path, id) = split_path(path)?;
|
let (path, id) = split_path(path)?;
|
||||||
self.document_folder_mut(path)?.remove_layer(id)?;
|
self.document_folder_mut(path)?.remove_layer(id)?;
|
||||||
Ok(())
|
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<Vec<LayerPanelEntry>, DocumentError> {
|
pub fn layer_panel(&self, path: &[LayerId]) -> Result<Vec<LayerPanelEntry>, DocumentError> {
|
||||||
let folder = self.document_folder(path)?;
|
let folder = self.document_folder(path)?;
|
||||||
let l_type = |layer: &LayerDataTypes| match layer {
|
let l_type = |layer: &LayerDataTypes| match layer {
|
||||||
@@ -159,13 +185,15 @@ impl Document {
|
|||||||
Ok(entries)
|
Ok(entries)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_operation<F: Fn(Vec<DocumentResponse>)>(&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<Option<Vec<DocumentResponse>>, DocumentError> {
|
||||||
self.work_operations.push(operation.clone());
|
self.work_operations.push(operation.clone());
|
||||||
match operation {
|
let responses = match operation {
|
||||||
Operation::AddCircle { path, insert_index, cx, cy, r, style } => {
|
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)?;
|
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 {
|
Operation::AddRect {
|
||||||
path,
|
path,
|
||||||
@@ -182,7 +210,7 @@ impl Document {
|
|||||||
insert_index,
|
insert_index,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
send_response(vec![self.render_root()]);
|
Some(vec![self.render_root()])
|
||||||
}
|
}
|
||||||
Operation::AddLine {
|
Operation::AddLine {
|
||||||
path,
|
path,
|
||||||
@@ -199,13 +227,13 @@ impl Document {
|
|||||||
insert_index,
|
insert_index,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
send_response(vec![self.render_root()]);
|
Some(vec![self.render_root()])
|
||||||
}
|
}
|
||||||
Operation::AddPen { path, insert_index, points, style } => {
|
Operation::AddPen { path, insert_index, points, style } => {
|
||||||
let points: Vec<kurbo::Point> = points.into_iter().map(|it| it.into()).collect();
|
let points: Vec<kurbo::Point> = points.into_iter().map(|it| it.into()).collect();
|
||||||
let polyline = PolyLine::new(points, style);
|
let polyline = PolyLine::new(points, style);
|
||||||
self.add_layer(&path, Layer::new(LayerDataTypes::PolyLine(polyline)), insert_index)?;
|
self.add_layer(&path, Layer::new(LayerDataTypes::PolyLine(polyline)), insert_index)?;
|
||||||
send_response(vec![self.render_root()]);
|
Some(vec![self.render_root()])
|
||||||
}
|
}
|
||||||
Operation::AddShape {
|
Operation::AddShape {
|
||||||
path,
|
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);
|
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)?;
|
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 } => {
|
Operation::DeleteLayer { path } => {
|
||||||
self.delete(&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 } => {
|
Operation::MountWorkingFolder { path } => {
|
||||||
self.work_operations.clear();
|
self.work_operations.clear();
|
||||||
self.work_mount_path = path;
|
self.work_mount_path = path;
|
||||||
self.work = Folder::default();
|
self.work = Folder::default();
|
||||||
self.work_mounted = true;
|
self.work_mounted = true;
|
||||||
|
None
|
||||||
}
|
}
|
||||||
Operation::DiscardWorkingFolder => {
|
Operation::DiscardWorkingFolder => {
|
||||||
self.work_operations.clear();
|
self.work_operations.clear();
|
||||||
self.work_mount_path = vec![];
|
self.work_mount_path = vec![];
|
||||||
self.work = Folder::default();
|
self.work = Folder::default();
|
||||||
self.work_mounted = false;
|
self.work_mounted = false;
|
||||||
|
None
|
||||||
}
|
}
|
||||||
Operation::ClearWorkingFolder => {
|
Operation::ClearWorkingFolder => {
|
||||||
self.work_operations.clear();
|
self.work_operations.clear();
|
||||||
self.work = Folder::default();
|
self.work = Folder::default();
|
||||||
|
None
|
||||||
}
|
}
|
||||||
Operation::CommitTransaction => {
|
Operation::CommitTransaction => {
|
||||||
let mut ops = Vec::new();
|
let mut ops = Vec::new();
|
||||||
@@ -253,14 +288,21 @@ impl Document {
|
|||||||
self.work_mounted = false;
|
self.work_mounted = false;
|
||||||
self.work_mount_path = vec![];
|
self.work_mount_path = vec![];
|
||||||
self.work = Folder::default();
|
self.work = Folder::default();
|
||||||
|
let mut responses = vec![];
|
||||||
for operation in ops.into_iter().take(len) {
|
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())?;
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
pub mod events;
|
pub mod events;
|
||||||
use crate::{tools::ToolType, Color, Document, EditorError, EditorState};
|
use crate::{tools::ToolType, Color, Document, EditorError, EditorState};
|
||||||
use document_core::Operation;
|
use document_core::Operation;
|
||||||
use events::{Event, Key, Response, ToolResponse};
|
use events::{DocumentResponse, Event, Key, Response, ToolResponse};
|
||||||
|
|
||||||
pub type Callback = Box<dyn Fn(Response)>;
|
pub type Callback = Box<dyn Fn(Response)>;
|
||||||
pub struct Dispatcher {
|
pub struct Dispatcher {
|
||||||
@@ -107,29 +107,35 @@ impl Dispatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (responses, operations) = editor_state
|
let (tool_responses, operations) = editor_state
|
||||||
.tool_state
|
.tool_state
|
||||||
.tool_data
|
.tool_data
|
||||||
.active_tool()?
|
.active_tool()?
|
||||||
.handle_input(event, &editor_state.document, &editor_state.tool_state.document_tool_data);
|
.handle_input(event, &editor_state.document, &editor_state.tool_state.document_tool_data);
|
||||||
|
|
||||||
self.dispatch_operations(&mut editor_state.document, operations);
|
let document_responses = self.dispatch_operations(&mut editor_state.document, operations);
|
||||||
self.dispatch_responses(responses);
|
self.dispatch_responses(tool_responses);
|
||||||
|
self.dispatch_responses(document_responses);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch_operations<I: IntoIterator<Item = Operation>>(&self, document: &mut Document, operations: I) {
|
fn dispatch_operations<I: IntoIterator<Item = Operation>>(&self, document: &mut Document, operations: I) -> Vec<DocumentResponse> {
|
||||||
|
let mut responses = vec![];
|
||||||
for operation in operations {
|
for operation in operations {
|
||||||
if let Err(error) = self.dispatch_operation(document, operation) {
|
match self.dispatch_operation(document, operation) {
|
||||||
log::error!("{}", error);
|
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> {
|
fn dispatch_operation(&self, document: &mut Document, operation: Operation) -> Result<Option<Vec<DocumentResponse>>, EditorError> {
|
||||||
document.handle_operation(operation, &|responses| self.dispatch_responses(responses))?;
|
Ok(document.handle_operation(operation)?)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatch_responses<T: Into<Response>, I: IntoIterator<Item = T>>(&self, responses: I) {
|
pub fn dispatch_responses<T: Into<Response>, I: IntoIterator<Item = T>>(&self, responses: I) {
|
||||||
|
|||||||
Reference in New Issue
Block a user