From fd1ddfc41e164374b1e3bb18cadc5fcdd47079c3 Mon Sep 17 00:00:00 2001 From: 0HyperCube <78500760+0HyperCube@users.noreply.github.com> Date: Wed, 29 Dec 2021 04:32:44 +0000 Subject: [PATCH] Populate layer entry cache (#437) * Populate layer entry cache * Serialize the DocumentMessageHandler * Fix restoring of collapsed/expanded state, add iter impl for Layer, and clean up layer_data() functions * Fixed bug with CreateEmptyLayer revealed by test Co-authored-by: Keavon Chambers Co-authored-by: otdavies --- Cargo.lock | 14 +- editor/Cargo.toml | 1 + editor/src/document/document_file.rs | 127 ++++++++++-------- .../src/document/document_message_handler.rs | 15 ++- editor/src/document/layer_panel.rs | 4 - editor/src/document/mod.rs | 1 + editor/src/document/vectorize_layerdata.rs | 25 ++++ graphene/Cargo.toml | 1 - graphene/src/document.rs | 10 -- graphene/src/layers/mod.rs | 41 ++++++ 10 files changed, 156 insertions(+), 83 deletions(-) create mode 100644 editor/src/document/vectorize_layerdata.rs diff --git a/Cargo.lock b/Cargo.lock index c1934e35..1c97f843 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,6 +101,7 @@ dependencies = [ "log", "rand_chacha", "serde", + "serde_json", "spin", "thiserror", ] @@ -113,7 +114,6 @@ dependencies = [ "kurbo", "log", "serde", - "serde_json", ] [[package]] @@ -157,9 +157,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "itoa" -version = "0.4.8" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "js-sys" @@ -274,9 +274,9 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" [[package]] name = "scoped-tls" @@ -324,9 +324,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.68" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" +checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5" dependencies = [ "itoa", "ryu", diff --git a/editor/Cargo.toml b/editor/Cargo.toml index 53992b89..7f63f393 100644 --- a/editor/Cargo.toml +++ b/editor/Cargo.toml @@ -15,6 +15,7 @@ log = "0.4" bitflags = "1.2.1" thiserror = "1.0.24" serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0" } graphite-proc-macros = { path = "../proc-macros" } glam = { version="0.17", features = ["serde"] } rand_chacha = "0.3.1" diff --git a/editor/src/document/document_file.rs b/editor/src/document/document_file.rs index 87a34302..3753d32d 100644 --- a/editor/src/document/document_file.rs +++ b/editor/src/document/document_file.rs @@ -4,6 +4,7 @@ use std::collections::VecDeque; pub use super::layer_panel::*; use super::movement_handler::{MovementMessage, MovementMessageHandler}; use super::transform_layer_handler::{TransformLayerMessage, TransformLayerMessageHandler}; +use super::vectorize_layerdata; use crate::consts::DEFAULT_DOCUMENT_NAME; use crate::consts::{ASYMPTOTIC_EFFECT, FILE_EXPORT_SUFFIX, FILE_SAVE_SUFFIX, SCALE_EFFECT, SCROLLBAR_SPACING}; @@ -58,16 +59,21 @@ pub struct VectorManipulatorShape { pub transform: DAffine2, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct DocumentMessageHandler { pub graphene_document: GrapheneDocument, + #[serde(skip)] pub document_undo_history: Vec, + #[serde(skip)] pub document_redo_history: Vec, pub saved_document_identifier: u64, pub name: String, + #[serde(with = "vectorize_layerdata")] pub layer_data: HashMap, LayerData>, layer_range_selection_reference: Vec, + #[serde(skip)] movement_handler: MovementMessageHandler, + #[serde(skip)] transform_layer_handler: TransformLayerMessageHandler, pub snapping_enabled: bool, pub view_mode: ViewMode, @@ -115,6 +121,7 @@ pub enum DocumentMessage { ToggleLayerVisibility(Vec), FlipSelectedLayers(FlipAxis), ToggleLayerExpansion(Vec), + SetLayerExpansion(Vec, bool), FolderChanged(Vec), LayerChanged(Vec), DocumentStructureChanged, @@ -157,30 +164,27 @@ impl From for Message { } impl DocumentMessageHandler { + pub fn serialize_document(&self) -> String { + let val = serde_json::to_string(self); + // We fully expect the serialization to succeed + val.unwrap() + } + + pub fn deserialize_document(serialized_content: &str) -> Result { + log::info!("Deserialising: {:?}", serialized_content); + serde_json::from_str(serialized_content).map_err(|e| DocumentError::InvalidFile(e.to_string())) + } + pub fn with_name(name: String, ipp: &InputPreprocessor) -> Self { - let mut document = Self { - graphene_document: GrapheneDocument::default(), - document_undo_history: Vec::new(), - document_redo_history: Vec::new(), - saved_document_identifier: 0, - name, - layer_data: vec![(vec![], LayerData::new(true))].into_iter().collect(), - layer_range_selection_reference: Vec::new(), - movement_handler: MovementMessageHandler::default(), - transform_layer_handler: TransformLayerMessageHandler::default(), - snapping_enabled: true, - view_mode: ViewMode::default(), - }; - document.graphene_document.root.transform = document.layerdata(&[]).calculate_offset_transform(ipp.viewport_bounds.size() / 2., 0.); + let mut document = Self { name, ..Self::default() }; + document.graphene_document.root.transform = document.layer_data(&[]).calculate_offset_transform(ipp.viewport_bounds.size() / 2., 0.); document } - pub fn with_name_and_content(name: String, serialized_content: String, ipp: &InputPreprocessor) -> Result { - let mut document = Self::with_name(name, ipp); - let internal_document = GrapheneDocument::with_content(&serialized_content); - match internal_document { - Ok(handle) => { - document.graphene_document = handle; + pub fn with_name_and_content(name: String, serialized_content: String) -> Result { + match Self::deserialize_document(&serialized_content) { + Ok(mut document) => { + document.name = name; Ok(document) } Err(DocumentError::InvalidFile(msg)) => Err(EditorError::Document(msg)), @@ -199,7 +203,7 @@ impl DocumentMessageHandler { if self.graphene_document.layer(path).ok()?.overlay { return None; } - self.layer_data(path).selected = true; + self.layer_data_mut(path).selected = true; let data = self.layer_panel_entry(path.to_vec()).ok()?; (!path.is_empty()).then(|| FrontendMessage::UpdateLayer { data }.into()) } @@ -244,12 +248,8 @@ impl DocumentMessageHandler { shapes.collect::>() } - pub fn layerdata(&self, path: &[LayerId]) -> &LayerData { - self.layer_data.get(path).expect("Layerdata does not exist") - } - - pub fn layerdata_mut(&mut self, path: &[LayerId]) -> &mut LayerData { - self.layer_data.entry(path.to_vec()).or_insert_with(|| LayerData::new(true)) + pub fn create_layer_data(&mut self, path: &[LayerId]) { + self.layer_data.insert(path.to_vec(), LayerData::new(true)); } pub fn selected_layers(&self) -> impl Iterator { @@ -265,7 +265,7 @@ impl DocumentMessageHandler { LayerDataType::Shape(_) => (), LayerDataType::Folder(ref folder) => { path.push(*id); - if self.layerdata(path).expanded { + if self.layer_data(path).expanded { structure.push(space); self.serialize_structure(folder, structure, data, path); space = 0; @@ -352,8 +352,18 @@ impl DocumentMessageHandler { self.layers_sorted(Some(false)) } - pub fn layer_data(&mut self, path: &[LayerId]) -> &mut LayerData { - layer_data(&mut self.layer_data, path) + pub fn layer_data(&self, path: &[LayerId]) -> &LayerData { + self.layer_data.get(path).expect("Layerdata does not exist") + } + + pub fn layer_data_mut(&mut self, path: &[LayerId]) -> &mut LayerData { + Self::layer_data_mut_no_borrow_self(&mut self.layer_data, path) + } + + pub fn layer_data_mut_no_borrow_self<'a>(layer_data: &'a mut HashMap, LayerData>, path: &[LayerId]) -> &'a mut LayerData { + layer_data + .get_mut(path) + .unwrap_or_else(|| panic!("Layer data cannot be found because the path {:?} does not exist", path)) } pub fn backup(&mut self, responses: &mut VecDeque) { @@ -431,7 +441,7 @@ impl DocumentMessageHandler { } pub fn layer_panel_entry(&mut self, path: Vec) -> Result { - let data: LayerData = *layer_data(&mut self.layer_data, &path); + let data: LayerData = *self.layer_data_mut(&path); let layer = self.graphene_document.layer(&path)?; let entry = layer_panel_entry(&data, self.graphene_document.multiply_transforms(&path)?, layer, path); Ok(entry) @@ -442,27 +452,23 @@ impl DocumentMessageHandler { pub fn layer_panel(&mut self, path: &[LayerId]) -> Result, EditorError> { let folder = self.graphene_document.folder(path)?; let paths: Vec> = folder.layer_ids.iter().map(|id| [path, &[*id]].concat()).collect(); - let data: Vec = paths.iter().map(|path| *layer_data(&mut self.layer_data, path)).collect(); - let folder = self.graphene_document.folder(path)?; - let entries = folder - .layers() - .iter() - .zip(paths.iter().zip(data)) - .rev() - .filter(|(layer, _)| !layer.overlay) - .map(|(layer, (path, data))| { - layer_panel_entry( - &data, - self.graphene_document - .generate_transform_across_scope(path, Some(self.graphene_document.root.transform.inverse())) - .unwrap(), - layer, - path.to_vec(), - ) - }) - .collect(); + let entries = paths.iter().rev().filter_map(|path| self.layer_panel_entry_from_path(path)).collect(); Ok(entries) } + + pub fn layer_panel_entry_from_path(&self, path: &[LayerId]) -> Option { + let layer_data = self.layer_data(path); + let transform = self + .graphene_document + .generate_transform_across_scope(path, Some(self.graphene_document.root.transform.inverse())) + .ok()?; + let layer = self.graphene_document.layer(path).ok()?; + + match layer.overlay { + true => None, + false => Some(layer_panel_entry(layer_data, transform, layer, path.to_vec())), + } + } } impl MessageHandler for DocumentMessageHandler { @@ -471,7 +477,7 @@ impl MessageHandler for DocumentMessageHand match message { Movement(message) => self .movement_handler - .process_action(message, (layer_data(&mut self.layer_data, &[]), &self.graphene_document, ipp), responses), + .process_action(message, (Self::layer_data_mut_no_borrow_self(&mut self.layer_data, &[]), &self.graphene_document, ipp), responses), TransformLayers(message) => self .transform_layer_handler .process_action(message, (&mut self.layer_data, &mut self.graphene_document, ipp), responses), @@ -521,7 +527,7 @@ impl MessageHandler for DocumentMessageHand }; responses.push_back( FrontendMessage::SaveDocument { - document: self.graphene_document.serialize_document(), + document: self.serialize_document(), name, } .into(), @@ -530,8 +536,8 @@ impl MessageHandler for DocumentMessageHand CreateEmptyFolder(mut path) => { let id = generate_uuid(); path.push(id); - self.layerdata_mut(&path).expanded = true; - responses.push_back(DocumentOperation::CreateFolder { path }.into()) + responses.push_back(DocumentOperation::CreateFolder { path: path.clone() }.into()); + responses.push_back(DocumentMessage::SetLayerExpansion(path, true).into()); } GroupSelectedLayers => { let selected_layers = self.selected_layers(); @@ -574,7 +580,12 @@ impl MessageHandler for DocumentMessageHand responses.push_back(DocumentOperation::ToggleLayerVisibility { path }.into()); } ToggleLayerExpansion(path) => { - self.layer_data(&path).expanded ^= true; + self.layer_data_mut(&path).expanded ^= true; + responses.push_back(DocumentStructureChanged.into()); + responses.push_back(LayerChanged(path).into()) + } + SetLayerExpansion(path, is_expanded) => { + self.layer_data_mut(&path).expanded = is_expanded; responses.push_back(DocumentStructureChanged.into()); responses.push_back(LayerChanged(path).into()) } @@ -621,7 +632,7 @@ impl MessageHandler for DocumentMessageHand } else { if ctrl { // Toggle selection when holding ctrl - let layer = self.layerdata_mut(&selected); + let layer = self.layer_data_mut(&selected); layer.selected = !layer.selected; responses.push_back(LayerChanged(selected.clone()).into()); } else { @@ -734,7 +745,7 @@ impl MessageHandler for DocumentMessageHand } .into(), ); - let root_layerdata = self.layerdata(&[]); + let root_layerdata = self.layer_data(&[]); let scale = 0.5 + ASYMPTOTIC_EFFECT + root_layerdata.scale * SCALE_EFFECT; let viewport_size = ipp.viewport_bounds.size(); diff --git a/editor/src/document/document_message_handler.rs b/editor/src/document/document_message_handler.rs index aa3a26c0..e1b64de6 100644 --- a/editor/src/document/document_message_handler.rs +++ b/editor/src/document/document_message_handler.rs @@ -95,7 +95,7 @@ impl DocumentsMessageHandler { } // TODO Fix how this doesn't preserve tab order upon loading new document from file>load - fn load_document(&mut self, new_document: DocumentMessageHandler, document_id: u64, replace_first_empty: bool, responses: &mut VecDeque) { + fn load_document(&mut self, mut new_document: DocumentMessageHandler, document_id: u64, replace_first_empty: bool, responses: &mut VecDeque) { // Special case when loading a document on an empty page if replace_first_empty && self.active_document().is_unmodified_default() { responses.push_back(DocumentsMessage::CloseDocument(self.active_document_id).into()); @@ -110,6 +110,15 @@ impl DocumentsMessageHandler { self.document_ids.push(document_id); } + responses.extend( + new_document + .layer_data + .keys() + .filter_map(|path| new_document.layer_panel_entry_from_path(path)) + .map(|entry| FrontendMessage::UpdateLayer { data: entry }.into()) + .collect::>(), + ); + self.documents.insert(document_id, new_document); // Send the new list of document tab names @@ -273,7 +282,7 @@ impl MessageHandler for DocumentsMessageHa document, document_is_saved, } => { - let document = DocumentMessageHandler::with_name_and_content(document_name, document, ipp); + let document = DocumentMessageHandler::with_name_and_content(document_name, document); match document { Ok(mut document) => { document.set_save_state(document_is_saved); @@ -307,7 +316,7 @@ impl MessageHandler for DocumentsMessageHa let document = self.documents.get(&id).unwrap(); responses.push_back( FrontendMessage::AutoSaveDocument { - document: document.graphene_document.serialize_document(), + document: document.serialize_document(), details: FrontendDocumentDetails { is_saved: document.is_saved(), id, diff --git a/editor/src/document/layer_panel.rs b/editor/src/document/layer_panel.rs index d92a38a6..eb967ac7 100644 --- a/editor/src/document/layer_panel.rs +++ b/editor/src/document/layer_panel.rs @@ -35,10 +35,6 @@ impl LayerData { } } -pub fn layer_data<'a>(layer_data: &'a mut HashMap, LayerData>, path: &[LayerId]) -> &'a mut LayerData { - layer_data.get_mut(path).expect(&format!("Layer data cannot be found because the path {:?} does not exist", path)) -} - pub fn layer_panel_entry(layer_data: &LayerData, transform: DAffine2, layer: &Layer, path: Vec) -> LayerPanelEntry { let layer_type: LayerType = (&layer.data).into(); let name = layer.name.clone().unwrap_or_else(|| format!("Unnamed {}", layer_type)); diff --git a/editor/src/document/mod.rs b/editor/src/document/mod.rs index 843830cc..be4cc50d 100644 --- a/editor/src/document/mod.rs +++ b/editor/src/document/mod.rs @@ -3,6 +3,7 @@ mod document_message_handler; pub mod layer_panel; mod movement_handler; mod transform_layer_handler; +mod vectorize_layerdata; #[doc(inline)] pub use document_file::LayerData; diff --git a/editor/src/document/vectorize_layerdata.rs b/editor/src/document/vectorize_layerdata.rs new file mode 100644 index 00000000..57a36135 --- /dev/null +++ b/editor/src/document/vectorize_layerdata.rs @@ -0,0 +1,25 @@ +/// Necessary because serde can't serialize hashmaps when the keys don't implement display. +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::iter::FromIterator; + +pub fn serialize<'a, T, K, V, S>(target: T, ser: S) -> Result +where + S: Serializer, + T: IntoIterator, + K: Serialize + 'a, + V: Serialize + 'a, +{ + let container: Vec<_> = target.into_iter().collect(); + serde::Serialize::serialize(&container, ser) +} + +pub fn deserialize<'de, T, K, V, D>(des: D) -> Result +where + D: Deserializer<'de>, + T: FromIterator<(K, V)>, + K: Deserialize<'de>, + V: Deserialize<'de>, +{ + let container: Vec<_> = serde::Deserialize::deserialize(des)?; + Ok(T::from_iter(container.into_iter())) +} diff --git a/graphene/Cargo.toml b/graphene/Cargo.toml index 65a389b6..a076d4b4 100644 --- a/graphene/Cargo.toml +++ b/graphene/Cargo.toml @@ -17,5 +17,4 @@ kurbo = { git = "https://github.com/linebender/kurbo.git", features = [ "serde", ] } serde = { version = "1.0", features = ["derive"] } -serde_json = { version = "1.0" } glam = { version = "0.17", features = ["serde"] } diff --git a/graphene/src/document.rs b/graphene/src/document.rs index bc210bbe..f9eff5f1 100644 --- a/graphene/src/document.rs +++ b/graphene/src/document.rs @@ -34,10 +34,6 @@ impl Default for Document { } impl Document { - pub fn with_content(serialized_content: &str) -> Result { - serde_json::from_str(serialized_content).map_err(|e| DocumentError::InvalidFile(e.to_string())) - } - /// Wrapper around render, that returns the whole document as a Response. pub fn render_root(&mut self, mode: ViewMode) -> String { self.root.render(&mut vec![], mode); @@ -48,12 +44,6 @@ impl Document { self.state_identifier.finish() } - pub fn serialize_document(&self) -> String { - let val = serde_json::to_string(self); - // We fully expect the serialization to succeed - val.unwrap() - } - /// Checks whether each layer under `path` intersects with the provided `quad` and adds all intersection layers as paths to `intersections`. pub fn intersects_quad(&self, quad: Quad, path: &mut Vec, intersections: &mut Vec>) { self.layer(path).unwrap().intersects_quad(quad, path, intersections); diff --git a/graphene/src/layers/mod.rs b/graphene/src/layers/mod.rs index 83845d69..773c1139 100644 --- a/graphene/src/layers/mod.rs +++ b/graphene/src/layers/mod.rs @@ -105,6 +105,10 @@ impl Layer { } } + pub fn iter(&self) -> LayerIter<'_> { + LayerIter { stack: vec![self] } + } + pub fn render(&mut self, transforms: &mut Vec, view_mode: ViewMode) -> &str { if !self.visible { return ""; @@ -179,3 +183,40 @@ impl Clone for Layer { } } } + +impl<'a> IntoIterator for &'a Layer { + type Item = &'a Layer; + type IntoIter = LayerIter<'a>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +#[derive(Debug)] +pub struct LayerIter<'a> { + pub stack: Vec<&'a Layer>, +} + +impl Default for LayerIter<'_> { + fn default() -> Self { + Self { stack: vec![] } + } +} + +impl<'a> Iterator for LayerIter<'a> { + type Item = &'a Layer; + + fn next(&mut self) -> Option { + match self.stack.pop() { + Some(layer) => { + if let LayerDataType::Folder(folder) = &layer.data { + let layers = folder.layers(); + self.stack.extend(layers); + }; + Some(layer) + } + None => None, + } + } +}