diff --git a/editor/src/communication/dispatcher.rs b/editor/src/communication/dispatcher.rs index cc7331f6c..622e98627 100644 --- a/editor/src/communication/dispatcher.rs +++ b/editor/src/communication/dispatcher.rs @@ -339,10 +339,19 @@ mod test { init_logger(); let mut editor = create_editor_with_three_layers(); - let sorted_layers = editor.dispatcher.documents_message_handler.active_document().all_layers_sorted(); + fn map_to_vec<'a>(paths: Vec<&'a [LayerId]>) -> Vec> { + paths.iter().map(|layer| layer.to_vec()).collect::>() + } + let sorted_layers = map_to_vec(editor.dispatcher.documents_message_handler.active_document().all_layers_sorted()); println!("Sorted layers: {:?}", sorted_layers); - let verify_order = |handler: &mut DocumentMessageHandler| (handler.all_layers_sorted(), handler.non_selected_layers_sorted(), handler.selected_layers_sorted()); + let verify_order = |handler: &mut DocumentMessageHandler| { + ( + map_to_vec(handler.all_layers_sorted()), + map_to_vec(handler.non_selected_layers_sorted()), + map_to_vec(handler.selected_layers_sorted()), + ) + }; editor.handle_message(DocumentMessage::SetSelectedLayers(sorted_layers[..2].to_vec())); diff --git a/editor/src/document/document_file.rs b/editor/src/document/document_file.rs index eb65ff494..8eefbbcd9 100644 --- a/editor/src/document/document_file.rs +++ b/editor/src/document/document_file.rs @@ -273,31 +273,14 @@ impl DocumentMessageHandler { self.layer_metadata.iter().filter_map(|(path, data)| data.selected.then(|| path.as_slice())) } - pub fn selected_layers_without_children(&self) -> Vec> { - // TODO optimize this after MVP. Look at commit 1aaf5c0d7dbe67cd9f4ba8b536a4771a2cef6439, optimization was started - // Traversing the layer tree recursively was chosen for readability, but should be replaced with an optimized approach later - fn recurse_layer_tree(ctx: &DocumentMessageHandler, mut path: Vec, without_children: &mut Vec>, selected: bool) { - if let Ok(folder) = ctx.graphene_document.folder(&path) { - for child in folder.list_layers() { - path.push(*child); - let selected_or_parent_selected = selected || ctx.selected_layers_contains(&path); - let selected_without_any_parent_selected = !selected && ctx.selected_layers_contains(&path); - if ctx.graphene_document.is_folder(&path) { - if selected_without_any_parent_selected { - without_children.push(path.clone()); - } - recurse_layer_tree(ctx, path.clone(), without_children, selected_or_parent_selected); - } else if selected_without_any_parent_selected { - without_children.push(path.clone()); - } - path.pop(); - } - } - } + pub fn selected_layers_without_children(&self) -> Vec<&[LayerId]> { + let mut sorted_layers = self.selected_layers().collect::>(); + // Sorting here creates groups of similar UUID paths + sorted_layers.sort(); + sorted_layers.dedup_by(|a, b| a.starts_with(b)); - let mut without_children: Vec> = vec![]; - recurse_layer_tree(self, vec![], &mut without_children, false); - without_children + // We need to maintain layer ordering + self.sort_layers(sorted_layers.iter().copied()) } pub fn selected_layers_contains(&self, path: &[LayerId]) -> bool { @@ -365,23 +348,19 @@ impl DocumentMessageHandler { } /// Returns an unsorted list of all layer paths including folders at all levels, except the document's top-level root folder itself - pub fn all_layers(&self) -> Vec> { - self.layer_metadata.keys().filter(|path| !path.is_empty()).cloned().collect() + pub fn all_layers(&self) -> impl Iterator { + self.layer_metadata.keys().filter_map(|path| (!path.is_empty()).then(|| path.as_slice())) } /// Returns the paths to all layers in order, optionally including only selected or non-selected layers. - fn layers_sorted(&self, selected: Option) -> Vec> { + fn sort_layers<'a>(&self, paths: impl Iterator) -> Vec<&'a [LayerId]> { // Compute the indices for each layer to be able to sort them - let mut layers_with_indices: Vec<(Vec, Vec)> = self - - .layer_metadata - .iter() + let mut layers_with_indices: Vec<(&[LayerId], Vec)> = paths // 'path.len() > 0' filters out root layer since it has no indices - .filter_map(|(path, data)| (!path.is_empty() && (data.selected == selected.unwrap_or(data.selected))).then(|| path.clone())) + .filter_map(|path| (!path.is_empty()).then(|| path)) .filter_map(|path| { - // TODO: Currently it is possible that `layer_metadata` contains layers that are don't actually exist (has been partially fixed in #281) and thus // TODO: `indices_for_path` can return an error. We currently skip these layers and log a warning. Once this problem is solved this code can be simplified. - match self.graphene_document.indices_for_path(&path) { + match self.graphene_document.indices_for_path(path) { Err(err) => { warn!("layers_sorted: Could not get indices for the layer {:?}: {:?}", path, err); None @@ -396,19 +375,19 @@ impl DocumentMessageHandler { } /// Returns the paths to all layers in order - pub fn all_layers_sorted(&self) -> Vec> { - self.layers_sorted(None) + pub fn all_layers_sorted(&self) -> Vec<&[LayerId]> { + self.sort_layers(self.all_layers()) } /// Returns the paths to all selected layers in order - pub fn selected_layers_sorted(&self) -> Vec> { - self.layers_sorted(Some(true)) + pub fn selected_layers_sorted(&self) -> Vec<&[LayerId]> { + self.sort_layers(self.selected_layers()) } /// Returns the paths to all non_selected layers in order #[allow(dead_code)] // used for test cases - pub fn non_selected_layers_sorted(&self) -> Vec> { - self.layers_sorted(Some(false)) + pub fn non_selected_layers_sorted(&self) -> Vec<&[LayerId]> { + self.sort_layers(self.all_layers().filter(|layer| !self.selected_layers().any(|path| &path == layer))) } pub fn layer_metadata(&self, path: &[LayerId]) -> &LayerMetadata { @@ -652,7 +631,7 @@ impl MessageHandler for DocumentMessageHand self.backup(responses); for path in self.selected_layers_without_children() { - responses.push_front(DocumentOperation::DeleteLayer { path }.into()); + responses.push_front(DocumentOperation::DeleteLayer { path: path.to_vec() }.into()); } responses.push_front(ToolMessage::DocumentIsDirty.into()); @@ -664,7 +643,7 @@ impl MessageHandler for DocumentMessageHand DuplicateSelectedLayers => { self.backup(responses); for path in self.selected_layers_sorted() { - responses.push_back(DocumentOperation::DuplicateLayer { path }.into()); + responses.push_back(DocumentOperation::DuplicateLayer { path: path.to_vec() }.into()); } } SelectLayer(selected, ctrl, shift) => { @@ -730,7 +709,7 @@ impl MessageHandler for DocumentMessageHand } SelectAllLayers => { let all_layer_paths = self.all_layers(); - responses.push_front(SetSelectedLayers(all_layer_paths).into()); + responses.push_front(SetSelectedLayers(all_layer_paths.map(|path| path.to_vec()).collect()).into()); } DeselectAllLayers => { responses.push_front(SetSelectedLayers(vec![]).into()); diff --git a/editor/src/document/document_message_handler.rs b/editor/src/document/document_message_handler.rs index 16843000b..a9466c528 100644 --- a/editor/src/document/document_message_handler.rs +++ b/editor/src/document/document_message_handler.rs @@ -359,7 +359,7 @@ impl MessageHandler for DocumentsMessageHa copy_buffer[clipboard as usize].clear(); for layer_path in active_document.selected_layers_without_children() { - match (active_document.graphene_document.layer(&layer_path).map(|t| t.clone()), *active_document.layer_metadata(&layer_path)) { + match (active_document.graphene_document.layer(layer_path).map(|t| t.clone()), *active_document.layer_metadata(layer_path)) { (Ok(layer), layer_metadata) => { copy_buffer[clipboard as usize].push(CopyBufferEntry { layer, layer_metadata }); } diff --git a/editor/src/document/overlay_message_handler.rs b/editor/src/document/overlay_message_handler.rs index 77f9d2d7c..27d62641b 100644 --- a/editor/src/document/overlay_message_handler.rs +++ b/editor/src/document/overlay_message_handler.rs @@ -8,7 +8,6 @@ use graphene::Operation as DocumentOperation; use graphene::document::Document as GrapheneDocument; use graphene::layers::style::ViewMode; use serde::{Deserialize, Serialize}; -use std::collections::{HashMap, VecDeque}; #[impl_message(Message, DocumentMessage, Overlay)] #[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] @@ -26,7 +25,6 @@ impl From for OverlayMessage { #[derive(Debug, Clone, Default)] pub struct OverlayMessageHandler { pub overlays_graphene_document: GrapheneDocument, - overlay_path_mapping: HashMap, Vec>, } impl MessageHandler for OverlayMessageHandler { diff --git a/editor/src/tool/snapping.rs b/editor/src/tool/snapping.rs index 1597361bc..6519183ac 100644 --- a/editor/src/tool/snapping.rs +++ b/editor/src/tool/snapping.rs @@ -5,26 +5,21 @@ use crate::consts::SNAP_TOLERANCE; use super::DocumentMessageHandler; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct SnapHandler { snap_targets: Option<(Vec, Vec)>, } -impl Default for SnapHandler { - fn default() -> Self { - Self { snap_targets: None } - } -} impl SnapHandler { /// Gets a list of snap targets for the X and Y axes in Viewport coords for the target layers (usually all layers or all non-selected layers.) /// This should be called at the start of a drag. - pub fn start_snap(&mut self, document_message_handler: &DocumentMessageHandler, target_layers: Vec>, ignore_layers: &[Vec]) { + pub fn start_snap(&mut self, document_message_handler: &DocumentMessageHandler, target_layers: Vec<&[LayerId]>, ignore_layers: &[Vec]) { if document_message_handler.snapping_enabled { // Could be made into sorted Vec or a HashSet for more performant lookups. self.snap_targets = Some( target_layers .iter() - .filter(|path| !ignore_layers.contains(path)) + .filter(|path| !ignore_layers.iter().any(|layer| layer.as_slice() == **path)) .filter_map(|path| document_message_handler.graphene_document.viewport_bounding_box(path).ok()?) .flat_map(|[bound1, bound2]| [bound1, bound2, ((bound1 + bound2) / 2.)]) .map(|vec| vec.into()) diff --git a/graphene/src/layers/mod.rs b/graphene/src/layers/mod.rs index a3cff9d60..06f8beb2e 100644 --- a/graphene/src/layers/mod.rs +++ b/graphene/src/layers/mod.rs @@ -189,17 +189,11 @@ impl<'a> IntoIterator for &'a Layer { } } -#[derive(Debug)] +#[derive(Debug, Default)] 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;