From 3a74f44d8621d06d0dfdec5097f374b03c116aac Mon Sep 17 00:00:00 2001 From: otdavies Date: Fri, 31 Dec 2021 02:39:59 -0800 Subject: [PATCH] WIP fix of folder crash / indent --- editor/src/document/document_file.rs | 16 +++-- graphene/src/document.rs | 100 ++++++++++++++++++++++----- graphene/src/layers/folder.rs | 2 +- graphene/src/lib.rs | 2 +- 4 files changed, 95 insertions(+), 25 deletions(-) diff --git a/editor/src/document/document_file.rs b/editor/src/document/document_file.rs index 8d3eb01e7e..a7f994bdca 100644 --- a/editor/src/document/document_file.rs +++ b/editor/src/document/document_file.rs @@ -567,11 +567,9 @@ impl MessageHandler for DocumentMessageHand GroupSelectedLayers => { let selected_layers = self.selected_layers(); - let common_prefix = self.graphene_document.common_layer_path_prefix(selected_layers); - let (_id, common_prefix) = common_prefix.split_last().unwrap_or((&0, &[])); - - let mut new_folder_path = common_prefix.to_vec(); + let mut new_folder_path: Vec = self.graphene_document.common_path_prefix(selected_layers); new_folder_path.push(generate_uuid()); + log::debug!("new_folder_path {:?}", new_folder_path); responses.push_back(DocumentsMessage::Copy(Clipboard::System).into()); responses.push_back(DocumentMessage::DeleteSelectedLayers.into()); @@ -621,10 +619,10 @@ impl MessageHandler for DocumentMessageHand } DeleteSelectedLayers => { self.backup(responses); - responses.push_front(ToolMessage::DocumentIsDirty.into()); for path in self.selected_layers().map(|path| path.to_vec()) { responses.push_front(DocumentOperation::DeleteLayer { path }.into()); } + responses.push_front(ToolMessage::DocumentIsDirty.into()); } SetViewMode(mode) => { self.view_mode = mode; @@ -645,7 +643,7 @@ impl MessageHandler for DocumentMessageHand // Fill the selection range self.layer_data .iter() - .filter(|(target, _)| self.graphene_document.layer_is_between(&target, &selected, &self.layer_range_selection_reference)) + .filter(|(target, _)| self.graphene_document.layer_is_between(target, &selected, &self.layer_range_selection_reference)) .for_each(|(layer_path, _)| { paths.push(layer_path.clone()); }); @@ -654,7 +652,9 @@ impl MessageHandler for DocumentMessageHand // Toggle selection when holding ctrl let layer = self.layer_data_mut(&selected); layer.selected = !layer.selected; + log::debug!("Ctrl Selection: {:?}", selected); responses.push_back(LayerChanged(selected.clone()).into()); + responses.push_back(ToolMessage::DocumentIsDirty.into()); } else { paths.push(selected.clone()); } @@ -664,7 +664,7 @@ impl MessageHandler for DocumentMessageHand } // Don't create messages for empty operations - if paths.len() > 0 { + if !paths.is_empty() { // Add or set our selected layers if ctrl { responses.push_front(AddSelectedLayers(paths).into()); @@ -677,6 +677,7 @@ impl MessageHandler for DocumentMessageHand self.layer_data.insert(path, layer_data_entry); } SetSelectedLayers(paths) => { + log::debug!("Set Selection: {:?}", paths); self.layer_data.iter_mut().filter(|(_, layer_data)| layer_data.selected).for_each(|(path, layer_data)| { layer_data.selected = false; responses.push_back(LayerChanged(path.clone()).into()) @@ -685,6 +686,7 @@ impl MessageHandler for DocumentMessageHand responses.push_front(AddSelectedLayers(paths).into()); } AddSelectedLayers(paths) => { + log::debug!("Add Selection: {:?}", paths); for path in paths { responses.extend(self.select_layer(&path)); } diff --git a/graphene/src/document.rs b/graphene/src/document.rs index 90c4517f55..0f01705013 100644 --- a/graphene/src/document.rs +++ b/graphene/src/document.rs @@ -61,7 +61,7 @@ impl Document { pub fn folder(&self, path: &[LayerId]) -> Result<&Folder, DocumentError> { let mut root = &self.root; for id in path { - root = root.as_folder()?.layer(*id).ok_or(DocumentError::LayerNotFound)?; + root = root.as_folder()?.layer(*id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?; } root.as_folder() } @@ -72,7 +72,7 @@ impl Document { fn folder_mut(&mut self, path: &[LayerId]) -> Result<&mut Folder, DocumentError> { let mut root = &mut self.root; for id in path { - root = root.as_folder_mut()?.layer_mut(*id).ok_or(DocumentError::LayerNotFound)?; + root = root.as_folder_mut()?.layer_mut(*id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?; } root.as_folder_mut() } @@ -83,7 +83,7 @@ impl Document { return Ok(&self.root); } let (path, id) = split_path(path)?; - self.folder(path)?.layer(id).ok_or(DocumentError::LayerNotFound) + self.folder(path)?.layer(id).ok_or_else(|| DocumentError::LayerNotFound(path.into())) } /// Returns a mutable reference to the layer or folder at the path. @@ -92,7 +92,7 @@ impl Document { return Ok(&mut self.root); } 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_else(|| DocumentError::LayerNotFound(path.into())) } pub fn deepest_common_folder<'a>(&self, layers: impl Iterator) -> Result<&'a [LayerId], DocumentError> { @@ -113,9 +113,36 @@ impl Document { .unwrap_or_default() } + pub fn common_path_prefix<'a>(&self, layers: impl Iterator) -> Vec { + let mut path_len: usize = usize::MAX; + let mut path: Vec = vec![]; + layers.for_each(|layer| { + for i in 0..std::cmp::min(layer.len(), path_len) { + let a = *layer.get(i).unwrap(); + let l = layer.len(); + + if i >= path.len() { + path.insert(i, a); + if l < path_len { + path_len = l; + } + } + + let b = *path.get(i).unwrap(); + + if a != b { + path_len = i; + path.drain(path_len..path.len()); + } + } + }); + + path + } + // Determines which layer is closer to the root, if path_a return true, if path_b return false // Answers the question: Is A closer to the root than B? - pub fn layer_closer_to_root(&self, path_a: &Vec, path_b: &Vec) -> bool { + pub fn layer_closer_to_root(&self, path_a: &[u64], path_b: &[u64]) -> bool { // Convert UUIDs to indices let indices_for_path_a = self.indices_for_path(path_a).unwrap(); let indices_for_path_b = self.indices_for_path(path_b).unwrap(); @@ -137,13 +164,13 @@ impl Document { return index_a < index_b; } - return false; + false } // Is the target layer between a <-> b layers, inclusive - pub fn layer_is_between(&self, target: &Vec, path_a: &Vec, path_b: &Vec) -> bool { - // If the target is a nonsense path, it isn't between - if target.len() < 1 { + pub fn layer_is_between(&self, target: &[u64], path_a: &[u64], path_b: &[u64]) -> bool { + // If the target is the root, it isn't between + if target.is_empty() { return false; } @@ -157,7 +184,7 @@ impl Document { let layer_vs_b = self.layer_closer_to_root(target, path_b); // To be inbetween you need to be above A and below B or vice versa - return layer_vs_a != layer_vs_b; + layer_vs_a != layer_vs_b } /// Given a path to a layer, returns a vector of the indices in the layer tree @@ -169,12 +196,12 @@ impl Document { // TODO: appears to be n^2? should we maintain a lookup table? for id in path { - let pos = root.layer_ids.iter().position(|x| *x == *id).ok_or(DocumentError::LayerNotFound)?; + let pos = root.layer_ids.iter().position(|x| *x == *id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?; indices.push(pos); - root = root.folder(*id).ok_or(DocumentError::LayerNotFound)?; + root = root.folder(*id).ok_or(DocumentError::LayerNotFound(path.into()))?; } - indices.push(root.layer_ids.iter().position(|x| *x == layer_id).ok_or(DocumentError::LayerNotFound)?); + indices.push(root.layer_ids.iter().position(|x| *x == layer_id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?); Ok(indices) } @@ -268,7 +295,7 @@ impl Document { let mut root = &mut self.root; root.cache_dirty = true; for id in path { - root = root.as_folder_mut()?.layer_mut(*id).ok_or(DocumentError::LayerNotFound)?; + root = root.as_folder_mut()?.layer_mut(*id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?; root.cache_dirty = true; } Ok(()) @@ -301,7 +328,7 @@ impl Document { let mut transforms = vec![self.root.transform]; for id in path { if let Ok(folder) = root.as_folder() { - root = folder.layer(*id).ok_or(DocumentError::LayerNotFound)?; + root = folder.layer(*id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?; } transforms.push(root.transform); } @@ -313,7 +340,7 @@ impl Document { let mut trans = self.root.transform; for id in path { if let Ok(folder) = root.as_folder() { - root = folder.layer(*id).ok_or(DocumentError::LayerNotFound)?; + root = folder.layer(*id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?; } trans = trans * root.transform; } @@ -515,6 +542,7 @@ impl Document { Some(vec![LayerChanged { path: path.clone() }]) } Operation::CreateFolder { path } => { + log::debug!("Creating a folder with path {:?}", path); self.set_layer(path, Layer::new(LayerDataType::Folder(Folder::default()), DAffine2::IDENTITY.to_cols_array()), -1)?; self.mark_as_dirty(path)?; @@ -633,3 +661,43 @@ fn update_thumbnails_upstream(path: &[LayerId]) -> Vec { } responses } + +#[test] +fn common_layer_path_prefix_test() { + let mut fake_layers: Vec> = vec![vec![]]; + fake_layers.push(vec![1, 2]); + // fake_layers.push(vec![1, 2]); + // fake_layers.push(vec![1, 2]); + // fake_layers.push(vec![1, 2, 0]); + + let layers = fake_layers.iter().map(|layer| layer.as_slice()); + let result = common_path_prefix(layers); + println!("Layers {:?}", result); +} + +pub fn common_path_prefix<'a>(layers: impl Iterator) -> Vec { + let mut path_len: usize = usize::MAX; + let mut path: Vec = vec![]; + layers.for_each(|layer| { + for i in 0..std::cmp::min(layer.len(), path_len) { + let a = *layer.get(i).unwrap(); + let l = layer.len(); + + if i >= path.len() { + path.insert(i, a); + if l < path_len { + path_len = l; + } + } + + let b = *path.get(i).unwrap(); + + if a != b { + path_len = i; + path.drain(path_len..path.len()); + } + } + }); + + path +} diff --git a/graphene/src/layers/folder.rs b/graphene/src/layers/folder.rs index 8942abe24b..e48cce5031 100644 --- a/graphene/src/layers/folder.rs +++ b/graphene/src/layers/folder.rs @@ -102,7 +102,7 @@ impl Folder { } pub fn position_of_layer(&self, layer_id: LayerId) -> Result { - self.layer_ids.iter().position(|x| *x == layer_id).ok_or(DocumentError::LayerNotFound) + self.layer_ids.iter().position(|x| *x == layer_id).ok_or_else(|| DocumentError::LayerNotFound([layer_id].into())) } pub fn folder(&self, id: LayerId) -> Option<&Folder> { diff --git a/graphene/src/lib.rs b/graphene/src/lib.rs index d302333807..c54ea46bc6 100644 --- a/graphene/src/lib.rs +++ b/graphene/src/lib.rs @@ -14,7 +14,7 @@ pub type LayerId = u64; #[derive(Debug, Clone, PartialEq)] pub enum DocumentError { - LayerNotFound, + LayerNotFound(Vec), InvalidPath, IndexOutOfBounds, NotAFolder,