mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-19 02:48: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
parent
aa9770092f
commit
8e35424c43
@@ -139,6 +139,8 @@ pub enum DocumentMessage {
|
||||
StartTransaction,
|
||||
RollbackTransaction,
|
||||
GroupSelectedLayers,
|
||||
UngroupSelectedLayers,
|
||||
UngroupLayers(Vec<LayerId>),
|
||||
AbortTransaction,
|
||||
CommitTransaction,
|
||||
ExportDocument,
|
||||
@@ -217,9 +219,14 @@ impl DocumentMessageHandler {
|
||||
fn select_layer(&mut self, path: &[LayerId]) -> Option<Message> {
|
||||
println!("Select_layer fail: {:?}", self.all_layers_sorted());
|
||||
|
||||
self.layer_metadata_mut(path).selected = true;
|
||||
let data = self.layer_panel_entry(path.to_vec()).ok()?;
|
||||
(!path.is_empty()).then(|| FrontendMessage::UpdateLayer { data }.into())
|
||||
if let Some(layer) = self.layer_metadata.get_mut(path) {
|
||||
layer.selected = true;
|
||||
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]> {
|
||||
@@ -274,13 +281,10 @@ impl DocumentMessageHandler {
|
||||
}
|
||||
|
||||
pub fn selected_layers_without_children(&self) -> Vec<&[LayerId]> {
|
||||
let mut sorted_layers = self.selected_layers().collect::<Vec<_>>();
|
||||
// Sorting here creates groups of similar UUID paths
|
||||
sorted_layers.sort();
|
||||
sorted_layers.dedup_by(|a, b| a.starts_with(b));
|
||||
let unique_layers = self.graphene_document.shallowest_unique_layers(self.selected_layers());
|
||||
|
||||
// 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 {
|
||||
@@ -427,6 +431,9 @@ impl DocumentMessageHandler {
|
||||
let document = std::mem::replace(&mut self.graphene_document, document);
|
||||
let layer_metadata = std::mem::replace(&mut self.layer_metadata, 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(())
|
||||
}
|
||||
None => Err(EditorError::NoTransactionInProgress),
|
||||
@@ -442,6 +449,9 @@ impl DocumentMessageHandler {
|
||||
let document = std::mem::replace(&mut self.graphene_document, document);
|
||||
let layer_metadata = std::mem::replace(&mut self.layer_metadata, 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(())
|
||||
}
|
||||
None => Err(EditorError::NoTransactionInProgress),
|
||||
@@ -470,7 +480,10 @@ impl DocumentMessageHandler {
|
||||
}
|
||||
|
||||
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 entry = layer_panel_entry(&data, self.graphene_document.multiply_transforms(&path)?, layer, path);
|
||||
Ok(entry)
|
||||
@@ -505,7 +518,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
|
||||
TransformLayers(message) => self
|
||||
.transform_layer_handler
|
||||
.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),
|
||||
RollbackTransaction => {
|
||||
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());
|
||||
}
|
||||
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) => {
|
||||
self.backup(responses);
|
||||
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);
|
||||
if let Some(insert_path) = insert {
|
||||
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
|
||||
.iter()
|
||||
.filter(|layer| layer.starts_with(path) && layer.len() == path.len() + 1)
|
||||
@@ -1003,6 +1047,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
|
||||
NudgeSelectedLayers,
|
||||
ReorderSelectedLayers,
|
||||
GroupSelectedLayers,
|
||||
UngroupSelectedLayers,
|
||||
);
|
||||
common.extend(select);
|
||||
}
|
||||
|
||||
@@ -394,29 +394,29 @@ impl MessageHandler<DocumentsMessage, &InputPreprocessor> for DocumentsMessageHa
|
||||
|
||||
let destination_path = [path.to_vec(), vec![generate_uuid()]].concat();
|
||||
|
||||
responses.push_back(
|
||||
DocumentOperation::InsertLayer {
|
||||
layer: entry.layer.clone(),
|
||||
destination_path: destination_path.clone(),
|
||||
insert_index,
|
||||
responses.push_front(
|
||||
DocumentMessage::UpdateLayerMetadata {
|
||||
layer_path: destination_path.clone(),
|
||||
layer_metadata: entry.layer_metadata,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
responses.push_back(
|
||||
DocumentMessage::UpdateLayerMetadata {
|
||||
layer_path: destination_path,
|
||||
layer_metadata: entry.layer_metadata,
|
||||
responses.push_front(
|
||||
DocumentOperation::InsertLayer {
|
||||
layer: entry.layer.clone(),
|
||||
destination_path,
|
||||
insert_index,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
};
|
||||
|
||||
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)
|
||||
}
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user