mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-01 14:18:12 +08:00
Ungroup layers (#465)
* WIP handling corner cases, like ungrouping subfolders * Resolved hanging * Fix recursive ungrouping * Functional, corner case free Ungroup. Small Undo issue & warnings * Update layertree upon undo * Also update layerdata upon redo * Add some polish * Resolved TODOs * Oops didn't save all after rename, ha. Co-authored-by: Dennis <dennis@kobert.dev>
This commit is contained in:
committed by
Keavon Chambers
co-authored by
Dennis
parent
aa9770092f
commit
8e35424c43
@@ -339,7 +339,7 @@ mod test {
|
|||||||
init_logger();
|
init_logger();
|
||||||
let mut editor = create_editor_with_three_layers();
|
let mut editor = create_editor_with_three_layers();
|
||||||
|
|
||||||
fn map_to_vec<'a>(paths: Vec<&'a [LayerId]>) -> Vec<Vec<LayerId>> {
|
fn map_to_vec(paths: Vec<&[LayerId]>) -> Vec<Vec<LayerId>> {
|
||||||
paths.iter().map(|layer| layer.to_vec()).collect::<Vec<_>>()
|
paths.iter().map(|layer| layer.to_vec()).collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
let sorted_layers = map_to_vec(editor.dispatcher.documents_message_handler.active_document().all_layers_sorted());
|
let sorted_layers = map_to_vec(editor.dispatcher.documents_message_handler.active_document().all_layers_sorted());
|
||||||
@@ -356,15 +356,15 @@ mod test {
|
|||||||
editor.handle_message(DocumentMessage::SetSelectedLayers(sorted_layers[..2].to_vec()));
|
editor.handle_message(DocumentMessage::SetSelectedLayers(sorted_layers[..2].to_vec()));
|
||||||
|
|
||||||
editor.handle_message(DocumentMessage::ReorderSelectedLayers(1));
|
editor.handle_message(DocumentMessage::ReorderSelectedLayers(1));
|
||||||
let (all, non_selected, selected) = verify_order(&mut editor.dispatcher.documents_message_handler.active_document_mut());
|
let (all, non_selected, selected) = verify_order(editor.dispatcher.documents_message_handler.active_document_mut());
|
||||||
assert_eq!(all, non_selected.into_iter().chain(selected.into_iter()).collect::<Vec<_>>());
|
assert_eq!(all, non_selected.into_iter().chain(selected.into_iter()).collect::<Vec<_>>());
|
||||||
|
|
||||||
editor.handle_message(DocumentMessage::ReorderSelectedLayers(-1));
|
editor.handle_message(DocumentMessage::ReorderSelectedLayers(-1));
|
||||||
let (all, non_selected, selected) = verify_order(&mut editor.dispatcher.documents_message_handler.active_document_mut());
|
let (all, non_selected, selected) = verify_order(editor.dispatcher.documents_message_handler.active_document_mut());
|
||||||
assert_eq!(all, selected.into_iter().chain(non_selected.into_iter()).collect::<Vec<_>>());
|
assert_eq!(all, selected.into_iter().chain(non_selected.into_iter()).collect::<Vec<_>>());
|
||||||
|
|
||||||
editor.handle_message(DocumentMessage::ReorderSelectedLayers(i32::MAX));
|
editor.handle_message(DocumentMessage::ReorderSelectedLayers(i32::MAX));
|
||||||
let (all, non_selected, selected) = verify_order(&mut editor.dispatcher.documents_message_handler.active_document_mut());
|
let (all, non_selected, selected) = verify_order(editor.dispatcher.documents_message_handler.active_document_mut());
|
||||||
assert_eq!(all, non_selected.into_iter().chain(selected.into_iter()).collect::<Vec<_>>());
|
assert_eq!(all, non_selected.into_iter().chain(selected.into_iter()).collect::<Vec<_>>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,6 +139,8 @@ pub enum DocumentMessage {
|
|||||||
StartTransaction,
|
StartTransaction,
|
||||||
RollbackTransaction,
|
RollbackTransaction,
|
||||||
GroupSelectedLayers,
|
GroupSelectedLayers,
|
||||||
|
UngroupSelectedLayers,
|
||||||
|
UngroupLayers(Vec<LayerId>),
|
||||||
AbortTransaction,
|
AbortTransaction,
|
||||||
CommitTransaction,
|
CommitTransaction,
|
||||||
ExportDocument,
|
ExportDocument,
|
||||||
@@ -217,9 +219,14 @@ impl DocumentMessageHandler {
|
|||||||
fn select_layer(&mut self, path: &[LayerId]) -> Option<Message> {
|
fn select_layer(&mut self, path: &[LayerId]) -> Option<Message> {
|
||||||
println!("Select_layer fail: {:?}", self.all_layers_sorted());
|
println!("Select_layer fail: {:?}", self.all_layers_sorted());
|
||||||
|
|
||||||
self.layer_metadata_mut(path).selected = true;
|
if let Some(layer) = self.layer_metadata.get_mut(path) {
|
||||||
let data = self.layer_panel_entry(path.to_vec()).ok()?;
|
layer.selected = true;
|
||||||
(!path.is_empty()).then(|| FrontendMessage::UpdateLayer { data }.into())
|
let data = self.layer_panel_entry(path.to_vec()).ok()?;
|
||||||
|
(!path.is_empty()).then(|| FrontendMessage::UpdateLayer { data }.into())
|
||||||
|
} else {
|
||||||
|
log::warn!("Tried to select non existing layer {:?}", path);
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn selected_visible_layers_bounding_box(&self) -> Option<[DVec2; 2]> {
|
pub fn selected_visible_layers_bounding_box(&self) -> Option<[DVec2; 2]> {
|
||||||
@@ -274,13 +281,10 @@ impl DocumentMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn selected_layers_without_children(&self) -> Vec<&[LayerId]> {
|
pub fn selected_layers_without_children(&self) -> Vec<&[LayerId]> {
|
||||||
let mut sorted_layers = self.selected_layers().collect::<Vec<_>>();
|
let unique_layers = self.graphene_document.shallowest_unique_layers(self.selected_layers());
|
||||||
// Sorting here creates groups of similar UUID paths
|
|
||||||
sorted_layers.sort();
|
|
||||||
sorted_layers.dedup_by(|a, b| a.starts_with(b));
|
|
||||||
|
|
||||||
// We need to maintain layer ordering
|
// We need to maintain layer ordering
|
||||||
self.sort_layers(sorted_layers.iter().copied())
|
self.sort_layers(unique_layers.iter().copied())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn selected_layers_contains(&self, path: &[LayerId]) -> bool {
|
pub fn selected_layers_contains(&self, path: &[LayerId]) -> bool {
|
||||||
@@ -427,6 +431,9 @@ impl DocumentMessageHandler {
|
|||||||
let document = std::mem::replace(&mut self.graphene_document, document);
|
let document = std::mem::replace(&mut self.graphene_document, document);
|
||||||
let layer_metadata = std::mem::replace(&mut self.layer_metadata, layer_metadata);
|
let layer_metadata = std::mem::replace(&mut self.layer_metadata, layer_metadata);
|
||||||
self.document_redo_history.push((document, layer_metadata));
|
self.document_redo_history.push((document, layer_metadata));
|
||||||
|
for layer in self.layer_metadata.keys() {
|
||||||
|
responses.push_back(DocumentMessage::LayerChanged(layer.clone()).into())
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
None => Err(EditorError::NoTransactionInProgress),
|
None => Err(EditorError::NoTransactionInProgress),
|
||||||
@@ -442,6 +449,9 @@ impl DocumentMessageHandler {
|
|||||||
let document = std::mem::replace(&mut self.graphene_document, document);
|
let document = std::mem::replace(&mut self.graphene_document, document);
|
||||||
let layer_metadata = std::mem::replace(&mut self.layer_metadata, layer_metadata);
|
let layer_metadata = std::mem::replace(&mut self.layer_metadata, layer_metadata);
|
||||||
self.document_undo_history.push((document, layer_metadata));
|
self.document_undo_history.push((document, layer_metadata));
|
||||||
|
for layer in self.layer_metadata.keys() {
|
||||||
|
responses.push_back(DocumentMessage::LayerChanged(layer.clone()).into())
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
None => Err(EditorError::NoTransactionInProgress),
|
None => Err(EditorError::NoTransactionInProgress),
|
||||||
@@ -470,7 +480,10 @@ impl DocumentMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn layer_panel_entry(&mut self, path: Vec<LayerId>) -> Result<LayerPanelEntry, EditorError> {
|
pub fn layer_panel_entry(&mut self, path: Vec<LayerId>) -> Result<LayerPanelEntry, EditorError> {
|
||||||
let data: LayerMetadata = *self.layer_metadata_mut(&path);
|
let data: LayerMetadata = *self
|
||||||
|
.layer_metadata
|
||||||
|
.get_mut(&path)
|
||||||
|
.ok_or_else(|| EditorError::Document(format!("Could not get layer metadata for {:?}", path)))?;
|
||||||
let layer = self.graphene_document.layer(&path)?;
|
let layer = self.graphene_document.layer(&path)?;
|
||||||
let entry = layer_panel_entry(&data, self.graphene_document.multiply_transforms(&path)?, layer, path);
|
let entry = layer_panel_entry(&data, self.graphene_document.multiply_transforms(&path)?, layer, path);
|
||||||
Ok(entry)
|
Ok(entry)
|
||||||
@@ -505,7 +518,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
|
|||||||
TransformLayers(message) => self
|
TransformLayers(message) => self
|
||||||
.transform_layer_handler
|
.transform_layer_handler
|
||||||
.process_action(message, (&mut self.layer_metadata, &mut self.graphene_document, ipp), responses),
|
.process_action(message, (&mut self.layer_metadata, &mut self.graphene_document, ipp), responses),
|
||||||
DeleteLayer(path) => responses.push_back(DocumentOperation::DeleteLayer { path }.into()),
|
DeleteLayer(path) => responses.push_front(DocumentOperation::DeleteLayer { path }.into()),
|
||||||
StartTransaction => self.backup(responses),
|
StartTransaction => self.backup(responses),
|
||||||
RollbackTransaction => {
|
RollbackTransaction => {
|
||||||
self.rollback(responses).unwrap_or_else(|e| log::warn!("{}", e));
|
self.rollback(responses).unwrap_or_else(|e| log::warn!("{}", e));
|
||||||
@@ -595,6 +608,37 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
|
|||||||
);
|
);
|
||||||
responses.push_back(DocumentMessage::SetSelectedLayers(vec![new_folder_path]).into());
|
responses.push_back(DocumentMessage::SetSelectedLayers(vec![new_folder_path]).into());
|
||||||
}
|
}
|
||||||
|
UngroupLayers(folder_path) => {
|
||||||
|
// Select all the children of the folder
|
||||||
|
let to_select = self.graphene_document.folder_children_paths(&folder_path);
|
||||||
|
let message_buffer = [
|
||||||
|
// Copy them
|
||||||
|
DocumentMessage::SetSelectedLayers(to_select).into(),
|
||||||
|
DocumentsMessage::Copy(Clipboard::System).into(),
|
||||||
|
// Paste them into the folder above
|
||||||
|
DocumentsMessage::PasteIntoFolder {
|
||||||
|
clipboard: Clipboard::System,
|
||||||
|
path: folder_path[..folder_path.len() - 1].to_vec(),
|
||||||
|
insert_index: -1,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
// Delete parent folder
|
||||||
|
DocumentMessage::DeleteLayer(folder_path).into(),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Push these messages in reverse due to push_front
|
||||||
|
for message in message_buffer.into_iter().rev() {
|
||||||
|
responses.push_front(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UngroupSelectedLayers => {
|
||||||
|
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||||
|
let folder_paths = self.graphene_document.sorted_folders_by_depth(self.selected_layers());
|
||||||
|
for folder_path in folder_paths {
|
||||||
|
responses.push_back(DocumentMessage::UngroupLayers(folder_path.to_vec()).into());
|
||||||
|
}
|
||||||
|
responses.push_back(DocumentMessage::CommitTransaction.into());
|
||||||
|
}
|
||||||
SetBlendModeForSelectedLayers(blend_mode) => {
|
SetBlendModeForSelectedLayers(blend_mode) => {
|
||||||
self.backup(responses);
|
self.backup(responses);
|
||||||
for path in self.layer_metadata.iter().filter_map(|(path, data)| data.selected.then(|| path.clone())) {
|
for path in self.layer_metadata.iter().filter_map(|(path, data)| data.selected.then(|| path.clone())) {
|
||||||
@@ -864,7 +908,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
|
|||||||
let insert = all_layer_paths.get(insert_pos);
|
let insert = all_layer_paths.get(insert_pos);
|
||||||
if let Some(insert_path) = insert {
|
if let Some(insert_path) = insert {
|
||||||
let (id, path) = insert_path.split_last().expect("Can't move the root folder");
|
let (id, path) = insert_path.split_last().expect("Can't move the root folder");
|
||||||
if let Some(folder) = self.graphene_document.layer(path).ok().map(|layer| layer.as_folder().ok()).flatten() {
|
if let Some(folder) = self.graphene_document.layer(path).ok().and_then(|layer| layer.as_folder().ok()) {
|
||||||
let selected: Vec<_> = selected_layers
|
let selected: Vec<_> = selected_layers
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|layer| layer.starts_with(path) && layer.len() == path.len() + 1)
|
.filter(|layer| layer.starts_with(path) && layer.len() == path.len() + 1)
|
||||||
@@ -1003,6 +1047,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
|
|||||||
NudgeSelectedLayers,
|
NudgeSelectedLayers,
|
||||||
ReorderSelectedLayers,
|
ReorderSelectedLayers,
|
||||||
GroupSelectedLayers,
|
GroupSelectedLayers,
|
||||||
|
UngroupSelectedLayers,
|
||||||
);
|
);
|
||||||
common.extend(select);
|
common.extend(select);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -394,29 +394,29 @@ impl MessageHandler<DocumentsMessage, &InputPreprocessor> for DocumentsMessageHa
|
|||||||
|
|
||||||
let destination_path = [path.to_vec(), vec![generate_uuid()]].concat();
|
let destination_path = [path.to_vec(), vec![generate_uuid()]].concat();
|
||||||
|
|
||||||
responses.push_back(
|
responses.push_front(
|
||||||
DocumentOperation::InsertLayer {
|
DocumentMessage::UpdateLayerMetadata {
|
||||||
layer: entry.layer.clone(),
|
layer_path: destination_path.clone(),
|
||||||
destination_path: destination_path.clone(),
|
layer_metadata: entry.layer_metadata,
|
||||||
insert_index,
|
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
responses.push_back(
|
responses.push_front(
|
||||||
DocumentMessage::UpdateLayerMetadata {
|
DocumentOperation::InsertLayer {
|
||||||
layer_path: destination_path,
|
layer: entry.layer.clone(),
|
||||||
layer_metadata: entry.layer_metadata,
|
destination_path,
|
||||||
|
insert_index,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
if insert_index == -1 {
|
if insert_index == -1 {
|
||||||
for entry in self.copy_buffer[clipboard as usize].iter() {
|
for entry in self.copy_buffer[clipboard as usize].iter().rev() {
|
||||||
paste(entry, responses)
|
paste(entry, responses)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for entry in self.copy_buffer[clipboard as usize].iter().rev() {
|
for entry in self.copy_buffer[clipboard as usize].iter() {
|
||||||
paste(entry, responses)
|
paste(entry, responses)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -261,6 +261,7 @@ impl Default for Mapping {
|
|||||||
entry! {action=DocumentsMessage::Copy(User), key_down=KeyC, modifiers=[KeyControl]},
|
entry! {action=DocumentsMessage::Copy(User), key_down=KeyC, modifiers=[KeyControl]},
|
||||||
entry! {action=DocumentsMessage::Cut(User), key_down=KeyX, modifiers=[KeyControl]},
|
entry! {action=DocumentsMessage::Cut(User), key_down=KeyX, modifiers=[KeyControl]},
|
||||||
entry! {action=DocumentMessage::GroupSelectedLayers, key_down=KeyG, modifiers=[KeyControl]},
|
entry! {action=DocumentMessage::GroupSelectedLayers, key_down=KeyG, modifiers=[KeyControl]},
|
||||||
|
entry! {action=DocumentMessage::UngroupSelectedLayers, key_down=KeyG, modifiers=[KeyControl, KeyShift]},
|
||||||
// Nudging
|
// Nudging
|
||||||
entry! {action=DocumentMessage::NudgeSelectedLayers(-SHIFT_NUDGE_AMOUNT, -SHIFT_NUDGE_AMOUNT), key_down=KeyArrowUp, modifiers=[KeyShift, KeyArrowLeft]},
|
entry! {action=DocumentMessage::NudgeSelectedLayers(-SHIFT_NUDGE_AMOUNT, -SHIFT_NUDGE_AMOUNT), key_down=KeyArrowUp, modifiers=[KeyShift, KeyArrowLeft]},
|
||||||
entry! {action=DocumentMessage::NudgeSelectedLayers(SHIFT_NUDGE_AMOUNT, -SHIFT_NUDGE_AMOUNT), key_down=KeyArrowUp, modifiers=[KeyShift, KeyArrowRight]},
|
entry! {action=DocumentMessage::NudgeSelectedLayers(SHIFT_NUDGE_AMOUNT, -SHIFT_NUDGE_AMOUNT), key_down=KeyArrowUp, modifiers=[KeyShift, KeyArrowRight]},
|
||||||
|
|||||||
@@ -57,6 +57,7 @@
|
|||||||
:data-index="index"
|
:data-index="index"
|
||||||
draggable="true"
|
draggable="true"
|
||||||
@dragstart="dragStart($event, layer)"
|
@dragstart="dragStart($event, layer)"
|
||||||
|
:title="layer.path"
|
||||||
>
|
>
|
||||||
<div class="layer-thumbnail" v-html="layer.thumbnail"></div>
|
<div class="layer-thumbnail" v-html="layer.thumbnail"></div>
|
||||||
<div class="layer-type-icon">
|
<div class="layer-type-icon">
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ fn panic_hook(info: &panic::PanicInfo) {
|
|||||||
let panic_info = info.to_string();
|
let panic_info = info.to_string();
|
||||||
let title = "The editor crashed — sorry about that".to_string();
|
let title = "The editor crashed — sorry about that".to_string();
|
||||||
let description = "An internal error occurred. Reload the editor to continue. Please report this by filing an issue on GitHub.".to_string();
|
let description = "An internal error occurred. Reload the editor to continue. Please report this by filing an issue on GitHub.".to_string();
|
||||||
|
log::error!("{}", info);
|
||||||
EDITOR_INSTANCES.with(|instances| {
|
EDITOR_INSTANCES.with(|instances| {
|
||||||
instances.borrow_mut().values_mut().for_each(|instance| {
|
instances.borrow_mut().values_mut().for_each(|instance| {
|
||||||
instance.1.handle_response_rust_proxy(FrontendMessage::DisplayPanic {
|
instance.1.handle_response_rust_proxy(FrontendMessage::DisplayPanic {
|
||||||
|
|||||||
@@ -95,6 +95,15 @@ impl Document {
|
|||||||
self.folder_mut(path)?.layer_mut(id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))
|
self.folder_mut(path)?.layer_mut(id).ok_or_else(|| DocumentError::LayerNotFound(path.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn common_layer_path_prefix<'a>(&self, layers: impl Iterator<Item = &'a [LayerId]>) -> &'a [LayerId] {
|
||||||
|
layers.reduce(|a, b| &a[..a.iter().zip(b.iter()).take_while(|&(a, b)| a == b).count()]).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn folders<'a>(&'a self, layers: impl Iterator<Item = &'a [LayerId]>) -> impl Iterator<Item = &'a [LayerId]> {
|
||||||
|
layers.filter(|layer| self.is_folder(layer))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the shallowest folder given the selection, even if the selection doesn't contain any folders
|
||||||
pub fn shallowest_common_folder<'a>(&self, layers: impl Iterator<Item = &'a [LayerId]>) -> Result<&'a [LayerId], DocumentError> {
|
pub fn shallowest_common_folder<'a>(&self, layers: impl Iterator<Item = &'a [LayerId]>) -> Result<&'a [LayerId], DocumentError> {
|
||||||
let common_prefix_of_path = self.common_layer_path_prefix(layers);
|
let common_prefix_of_path = self.common_layer_path_prefix(layers);
|
||||||
|
|
||||||
@@ -104,13 +113,32 @@ impl Document {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn common_layer_path_prefix<'a>(&self, layers: impl Iterator<Item = &'a [LayerId]>) -> &'a [LayerId] {
|
// Return returns all folders that are not contained in any other of the given folders
|
||||||
layers
|
pub fn shallowest_folders<'a>(&'a self, layers: impl Iterator<Item = &'a [LayerId]>) -> Vec<&[LayerId]> {
|
||||||
.reduce(|a, b| {
|
self.shallowest_unique_layers(self.folders(layers))
|
||||||
let number_of_uncommon_ids_in_a = (0..a.len()).position(|i| b.starts_with(&a[..a.len() - i])).unwrap_or_default();
|
}
|
||||||
&a[..(a.len() - number_of_uncommon_ids_in_a)]
|
|
||||||
})
|
// Return returns all layers that are not contained in any other of the given folders
|
||||||
.unwrap_or_default()
|
pub fn shallowest_unique_layers<'a>(&'a self, layers: impl Iterator<Item = &'a [LayerId]>) -> Vec<&[LayerId]> {
|
||||||
|
let mut sorted_layers: Vec<_> = layers.collect();
|
||||||
|
sorted_layers.sort();
|
||||||
|
// Sorting here creates groups of similar UUID paths
|
||||||
|
sorted_layers.dedup_by(|a, b| a.starts_with(b));
|
||||||
|
sorted_layers
|
||||||
|
}
|
||||||
|
// Deepest to shallowest (longest to shortest path length)
|
||||||
|
pub fn sorted_folders_by_depth<'a>(&'a self, layers: impl Iterator<Item = &'a [LayerId]>) -> Vec<&'a [LayerId]> {
|
||||||
|
let mut folders: Vec<_> = self.folders(layers).collect();
|
||||||
|
folders.sort_by_key(|a| std::cmp::Reverse(a.len()));
|
||||||
|
folders
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn folder_children_paths(&self, path: &[LayerId]) -> Vec<Vec<LayerId>> {
|
||||||
|
if let Ok(folder) = self.folder(path) {
|
||||||
|
folder.list_layers().iter().map(|f| [path, &[*f]].concat()).collect()
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_folder(&self, path: &[LayerId]) -> bool {
|
pub fn is_folder(&self, path: &[LayerId]) -> bool {
|
||||||
@@ -457,7 +485,7 @@ impl Document {
|
|||||||
};
|
};
|
||||||
self.delete(path)?;
|
self.delete(path)?;
|
||||||
|
|
||||||
let (folder, _) = split_path(path.as_slice()).unwrap_or_else(|_| (&[], 0));
|
let (folder, _) = split_path(path.as_slice()).unwrap_or((&[], 0));
|
||||||
responses.extend([DocumentChanged, DeletedLayer { path: path.clone() }, FolderChanged { path: folder.to_vec() }]);
|
responses.extend([DocumentChanged, DeletedLayer { path: path.clone() }, FolderChanged { path: folder.to_vec() }]);
|
||||||
responses.extend(update_thumbnails_upstream(folder));
|
responses.extend(update_thumbnails_upstream(folder));
|
||||||
Some(responses)
|
Some(responses)
|
||||||
@@ -494,7 +522,7 @@ impl Document {
|
|||||||
}
|
}
|
||||||
Operation::DuplicateLayer { path } => {
|
Operation::DuplicateLayer { path } => {
|
||||||
let layer = self.layer(path)?.clone();
|
let layer = self.layer(path)?.clone();
|
||||||
let (folder_path, _) = split_path(path.as_slice()).unwrap_or_else(|_| (&[], 0));
|
let (folder_path, _) = split_path(path.as_slice()).unwrap_or((&[], 0));
|
||||||
let folder = self.folder_mut(folder_path)?;
|
let folder = self.folder_mut(folder_path)?;
|
||||||
if let Some(new_layer_id) = folder.add_layer(layer, None, -1) {
|
if let Some(new_layer_id) = folder.add_layer(layer, None, -1) {
|
||||||
let new_path = [folder_path, &[new_layer_id]].concat();
|
let new_path = [folder_path, &[new_layer_id]].concat();
|
||||||
|
|||||||
Reference in New Issue
Block a user