Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions editor/src/communication/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ mod test {
init_logger();
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<_>>()
}
let sorted_layers = map_to_vec(editor.dispatcher.documents_message_handler.active_document().all_layers_sorted());
Expand All @@ -356,15 +356,15 @@ mod test {
editor.handle_message(DocumentMessage::SetSelectedLayers(sorted_layers[..2].to_vec()));

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<_>>());

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<_>>());

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<_>>());
}
}
67 changes: 56 additions & 11 deletions editor/src/document/document_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ pub enum DocumentMessage {
StartTransaction,
RollbackTransaction,
GroupSelectedLayers,
UngroupSelectedLayers,
UngroupLayers(Vec<LayerId>),
AbortTransaction,
CommitTransaction,
ExportDocument,
Expand Down Expand Up @@ -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]> {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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),
Expand All @@ -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),
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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())) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -1003,6 +1047,7 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
NudgeSelectedLayers,
ReorderSelectedLayers,
GroupSelectedLayers,
UngroupSelectedLayers,
);
common.extend(select);
}
Expand Down
22 changes: 11 additions & 11 deletions editor/src/document/document_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
1 change: 1 addition & 0 deletions editor/src/input/input_mapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ impl Default for Mapping {
entry! {action=DocumentsMessage::Copy(User), key_down=KeyC, modifiers=[KeyControl]},
entry! {action=DocumentsMessage::Cut(User), key_down=KeyX, modifiers=[KeyControl]},
entry! {action=DocumentMessage::GroupSelectedLayers, key_down=KeyG, modifiers=[KeyControl]},
entry! {action=DocumentMessage::UngroupSelectedLayers, key_down=KeyG, modifiers=[KeyControl, KeyShift]},
// 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, KeyArrowRight]},
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/panels/LayerTree.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
:data-index="index"
draggable="true"
@dragstart="dragStart($event, layer)"
:title="layer.path"
>
<div class="layer-thumbnail" v-html="layer.thumbnail"></div>
<div class="layer-type-icon">
Expand Down
1 change: 1 addition & 0 deletions frontend/wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ fn panic_hook(info: &panic::PanicInfo) {
let panic_info = info.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();
log::error!("{}", info);
EDITOR_INSTANCES.with(|instances| {
instances.borrow_mut().values_mut().for_each(|instance| {
instance.1.handle_response_rust_proxy(FrontendMessage::DisplayPanic {
Expand Down
46 changes: 37 additions & 9 deletions graphene/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@ impl Document {
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> {
let common_prefix_of_path = self.common_layer_path_prefix(layers);

Expand All @@ -104,13 +113,32 @@ impl Document {
})
}

pub fn common_layer_path_prefix<'a>(&self, layers: impl Iterator<Item = &'a [LayerId]>) -> &'a [LayerId] {
layers
.reduce(|a, b| {
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)]
})
.unwrap_or_default()
// Return returns all folders that are not contained in any other of the given folders
pub fn shallowest_folders<'a>(&'a self, layers: impl Iterator<Item = &'a [LayerId]>) -> Vec<&[LayerId]> {
self.shallowest_unique_layers(self.folders(layers))
}

// Return returns all layers that are not contained in any other of the given folders
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 {
Expand Down Expand Up @@ -457,7 +485,7 @@ impl Document {
};
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(update_thumbnails_upstream(folder));
Some(responses)
Expand Down Expand Up @@ -494,7 +522,7 @@ impl Document {
}
Operation::DuplicateLayer { path } => {
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)?;
if let Some(new_layer_id) = folder.add_layer(layer, None, -1) {
let new_path = [folder_path, &[new_layer_id]].concat();
Expand Down