Skip to content

Commit 6a2ae55

Browse files
otdaviesTrueDoctor
andauthored
Fix group creation parenting (#455)
* WIP fix of folder crash / indent * Fixed most crashes * Known cases of crash / incorrect behavior resolved * Removed todo & readability tweak * Removed left over test * Resolved clippy issue resulting from merging master * Replace recursive tree traversal with prefix matching * Reverted changes for #453 and added undo functionality to Paste * Make uuid generator thread local for tests * Maintain layer order, test still failing 50% of the time * Reverting back to known working with the knowledge we can optimize later. This reverts commit 1aaf5c0. * Revert "Make uuid generator thread local for tests" This reverts commit d68e3b9. * Revert "Reverted changes for #453 and added undo functionality to Paste" This reverts commit d66992a. * Revert "Replace recursive tree traversal with prefix matching" This reverts commit 6bcbb9f. * Reverted to working state and added comment back to optimized commit hash * Re-removed the changes involving #453 Co-authored-by: Dennis <[email protected]>
1 parent 7da03d7 commit 6a2ae55

File tree

5 files changed

+77
-30
lines changed

5 files changed

+77
-30
lines changed

editor/src/document/document_file.rs

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,37 @@ impl DocumentMessageHandler {
273273
self.layer_metadata.iter().filter_map(|(path, data)| data.selected.then(|| path.as_slice()))
274274
}
275275

276+
pub fn selected_layers_without_children(&self) -> Vec<Vec<LayerId>> {
277+
// TODO optimize this after MVP. Look at commit 1aaf5c0d7dbe67cd9f4ba8b536a4771a2cef6439, optimization was started
278+
// Traversing the layer tree recursively was chosen for readability, but should be replaced with an optimized approach later
279+
fn recurse_layer_tree(ctx: &DocumentMessageHandler, mut path: Vec<u64>, without_children: &mut Vec<Vec<LayerId>>, selected: bool) {
280+
if let Ok(folder) = ctx.graphene_document.folder(&path) {
281+
for child in folder.list_layers() {
282+
path.push(*child);
283+
let selected_or_parent_selected = selected || ctx.selected_layers_contains(&path);
284+
let selected_without_any_parent_selected = !selected && ctx.selected_layers_contains(&path);
285+
if ctx.graphene_document.is_folder(&path) {
286+
if selected_without_any_parent_selected {
287+
without_children.push(path.clone());
288+
}
289+
recurse_layer_tree(ctx, path.clone(), without_children, selected_or_parent_selected);
290+
} else if selected_without_any_parent_selected {
291+
without_children.push(path.clone());
292+
}
293+
path.pop();
294+
}
295+
}
296+
}
297+
298+
let mut without_children: Vec<Vec<LayerId>> = vec![];
299+
recurse_layer_tree(self, vec![], &mut without_children, false);
300+
without_children
301+
}
302+
303+
pub fn selected_layers_contains(&self, path: &[LayerId]) -> bool {
304+
self.layer_metadata.get(path).map(|layer| layer.selected).unwrap_or(false)
305+
}
306+
276307
pub fn selected_visible_layers(&self) -> impl Iterator<Item = &[LayerId]> {
277308
self.selected_layers().filter(|path| match self.graphene_document.layer(path) {
278309
Ok(layer) => layer.visible,
@@ -562,12 +593,13 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
562593
responses.push_back(DocumentMessage::SetLayerExpansion(path, true).into());
563594
}
564595
GroupSelectedLayers => {
565-
let selected_layers = self.selected_layers();
596+
let mut new_folder_path: Vec<u64> = self.graphene_document.shallowest_common_folder(self.selected_layers()).unwrap_or(&[]).to_vec();
566597

567-
let common_prefix = self.graphene_document.common_layer_path_prefix(selected_layers);
568-
let (_id, common_prefix) = common_prefix.split_last().unwrap_or((&0, &[]));
598+
// Required for grouping parent folders with their own children
599+
if !new_folder_path.is_empty() && self.selected_layers_contains(&new_folder_path) {
600+
new_folder_path.remove(new_folder_path.len() - 1);
601+
}
569602

570-
let mut new_folder_path = common_prefix.to_vec();
571603
new_folder_path.push(generate_uuid());
572604

573605
responses.push_back(DocumentsMessage::Copy(Clipboard::System).into());
@@ -618,10 +650,12 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
618650
}
619651
DeleteSelectedLayers => {
620652
self.backup(responses);
621-
responses.push_front(ToolMessage::DocumentIsDirty.into());
622-
for path in self.selected_layers().map(|path| path.to_vec()) {
653+
654+
for path in self.selected_layers_without_children() {
623655
responses.push_front(DocumentOperation::DeleteLayer { path }.into());
624656
}
657+
658+
responses.push_front(ToolMessage::DocumentIsDirty.into());
625659
}
626660
SetViewMode(mode) => {
627661
self.view_mode = mode;
@@ -652,6 +686,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
652686
let layer = self.layer_metadata_mut(&selected);
653687
layer.selected = !layer.selected;
654688
responses.push_back(LayerChanged(selected.clone()).into());
689+
responses.push_back(ToolMessage::DocumentIsDirty.into());
655690
} else {
656691
paths.push(selected.clone());
657692
}

editor/src/document/document_message_handler.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -352,15 +352,18 @@ impl MessageHandler<DocumentsMessage, &InputPreprocessor> for DocumentsMessageHa
352352
responses.push_back(DocumentsMessage::SelectDocument(prev_id).into());
353353
}
354354
Copy(clipboard) => {
355-
let paths = self.active_document().selected_layers_sorted();
356-
self.copy_buffer[clipboard as usize].clear();
357-
for path in paths {
358-
let document = self.active_document();
359-
match (document.graphene_document.layer(&path).map(|t| t.clone()), *document.layer_metadata(&path)) {
355+
// We can't use `self.active_document()` because it counts as an immutable borrow of the entirety of `self`
356+
let active_document = self.documents.get(&self.active_document_id).unwrap();
357+
358+
let copy_buffer = &mut self.copy_buffer;
359+
copy_buffer[clipboard as usize].clear();
360+
361+
for layer_path in active_document.selected_layers_without_children() {
362+
match (active_document.graphene_document.layer(&layer_path).map(|t| t.clone()), *active_document.layer_metadata(&layer_path)) {
360363
(Ok(layer), layer_metadata) => {
361-
self.copy_buffer[clipboard as usize].push(CopyBufferEntry { layer, layer_metadata });
364+
copy_buffer[clipboard as usize].push(CopyBufferEntry { layer, layer_metadata });
362365
}
363-
(Err(e), _) => warn!("Could not access selected layer {:?}: {:?}", path, e),
366+
(Err(e), _) => warn!("Could not access selected layer {:?}: {:?}", layer_path, e),
364367
}
365368
}
366369
}
@@ -372,9 +375,9 @@ impl MessageHandler<DocumentsMessage, &InputPreprocessor> for DocumentsMessageHa
372375
let document = self.active_document();
373376
let shallowest_common_folder = document
374377
.graphene_document
375-
.deepest_common_folder(document.selected_layers())
378+
.shallowest_common_folder(document.selected_layers())
376379
.expect("While pasting, the selected layers did not exist while attempting to find the appropriate folder path for insertion");
377-
380+
responses.push_back(StartTransaction.into());
378381
responses.push_back(
379382
PasteIntoFolder {
380383
clipboard,
@@ -383,6 +386,7 @@ impl MessageHandler<DocumentsMessage, &InputPreprocessor> for DocumentsMessageHa
383386
}
384387
.into(),
385388
);
389+
responses.push_back(CommitTransaction.into());
386390
}
387391
PasteIntoFolder { clipboard, path, insert_index } => {
388392
let paste = |entry: &CopyBufferEntry, responses: &mut VecDeque<_>| {

graphene/src/document.rs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ impl Document {
6161
pub fn folder(&self, path: &[LayerId]) -> Result<&Folder, DocumentError> {
6262
let mut root = &self.root;
6363
for id in path {
64-
root = root.as_folder()?.layer(*id).ok_or(DocumentError::LayerNotFound)?;
64+
root = root.as_folder()?.layer(*id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?;
6565
}
6666
root.as_folder()
6767
}
@@ -72,7 +72,7 @@ impl Document {
7272
fn folder_mut(&mut self, path: &[LayerId]) -> Result<&mut Folder, DocumentError> {
7373
let mut root = &mut self.root;
7474
for id in path {
75-
root = root.as_folder_mut()?.layer_mut(*id).ok_or(DocumentError::LayerNotFound)?;
75+
root = root.as_folder_mut()?.layer_mut(*id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?;
7676
}
7777
root.as_folder_mut()
7878
}
@@ -83,7 +83,7 @@ impl Document {
8383
return Ok(&self.root);
8484
}
8585
let (path, id) = split_path(path)?;
86-
self.folder(path)?.layer(id).ok_or(DocumentError::LayerNotFound)
86+
self.folder(path)?.layer(id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))
8787
}
8888

8989
/// Returns a mutable reference to the layer or folder at the path.
@@ -92,10 +92,10 @@ impl Document {
9292
return Ok(&mut self.root);
9393
}
9494
let (path, id) = split_path(path)?;
95-
self.folder_mut(path)?.layer_mut(id).ok_or(DocumentError::LayerNotFound)
95+
self.folder_mut(path)?.layer_mut(id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))
9696
}
9797

98-
pub fn deepest_common_folder<'a>(&self, layers: impl Iterator<Item = &'a [LayerId]>) -> Result<&'a [LayerId], DocumentError> {
98+
pub fn shallowest_common_folder<'a>(&self, layers: impl Iterator<Item = &'a [LayerId]>) -> Result<&'a [LayerId], DocumentError> {
9999
let common_prefix_of_path = self.common_layer_path_prefix(layers);
100100

101101
Ok(match self.layer(common_prefix_of_path)?.data {
@@ -113,6 +113,10 @@ impl Document {
113113
.unwrap_or_default()
114114
}
115115

116+
pub fn is_folder(&self, path: &[LayerId]) -> bool {
117+
return self.folder(path).is_ok();
118+
}
119+
116120
// Determines which layer is closer to the root, if path_a return true, if path_b return false
117121
// Answers the question: Is A closer to the root than B?
118122
pub fn layer_closer_to_root(&self, path_a: &[u64], path_b: &[u64]) -> bool {
@@ -136,9 +140,9 @@ impl Document {
136140
false
137141
}
138142

139-
// Is the target layer between a <-> b layers, inclusive
143+
// Is the target layer between a <-> b layers, inclusive
140144
pub fn layer_is_between(&self, target: &[u64], path_a: &[u64], path_b: &[u64]) -> bool {
141-
// If the target is a nonsense path, it isn't between
145+
// If the target is the root, it isn't between
142146
if target.is_empty() {
143147
return false;
144148
}
@@ -165,12 +169,12 @@ impl Document {
165169

166170
// TODO: appears to be n^2? should we maintain a lookup table?
167171
for id in path {
168-
let pos = root.layer_ids.iter().position(|x| *x == *id).ok_or(DocumentError::LayerNotFound)?;
172+
let pos = root.layer_ids.iter().position(|x| *x == *id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?;
169173
indices.push(pos);
170-
root = root.folder(*id).ok_or(DocumentError::LayerNotFound)?;
174+
root = root.folder(*id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?;
171175
}
172176

173-
indices.push(root.layer_ids.iter().position(|x| *x == layer_id).ok_or(DocumentError::LayerNotFound)?);
177+
indices.push(root.layer_ids.iter().position(|x| *x == layer_id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?);
174178

175179
Ok(indices)
176180
}
@@ -264,7 +268,7 @@ impl Document {
264268
let mut root = &mut self.root;
265269
root.cache_dirty = true;
266270
for id in path {
267-
root = root.as_folder_mut()?.layer_mut(*id).ok_or(DocumentError::LayerNotFound)?;
271+
root = root.as_folder_mut()?.layer_mut(*id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?;
268272
root.cache_dirty = true;
269273
}
270274
Ok(())
@@ -297,7 +301,7 @@ impl Document {
297301
let mut transforms = vec![self.root.transform];
298302
for id in path {
299303
if let Ok(folder) = root.as_folder() {
300-
root = folder.layer(*id).ok_or(DocumentError::LayerNotFound)?;
304+
root = folder.layer(*id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?;
301305
}
302306
transforms.push(root.transform);
303307
}
@@ -309,7 +313,7 @@ impl Document {
309313
let mut trans = self.root.transform;
310314
for id in path {
311315
if let Ok(folder) = root.as_folder() {
312-
root = folder.layer(*id).ok_or(DocumentError::LayerNotFound)?;
316+
root = folder.layer(*id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))?;
313317
}
314318
trans = trans * root.transform;
315319
}

graphene/src/layers/folder.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,12 @@ impl Folder {
101101
Some(&mut self.layers[pos])
102102
}
103103

104+
pub fn folder_contains(&self, id: LayerId) -> bool {
105+
self.layer_ids.contains(&id)
106+
}
107+
104108
pub fn position_of_layer(&self, layer_id: LayerId) -> Result<usize, DocumentError> {
105-
self.layer_ids.iter().position(|x| *x == layer_id).ok_or(DocumentError::LayerNotFound)
109+
self.layer_ids.iter().position(|x| *x == layer_id).ok_or_else(|| DocumentError::LayerNotFound([layer_id].into()))
106110
}
107111

108112
pub fn folder(&self, id: LayerId) -> Option<&Folder> {

graphene/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub type LayerId = u64;
1414

1515
#[derive(Debug, Clone, PartialEq)]
1616
pub enum DocumentError {
17-
LayerNotFound,
17+
LayerNotFound(Vec<LayerId>),
1818
InvalidPath,
1919
IndexOutOfBounds,
2020
NotAFolder,

0 commit comments

Comments
 (0)