Add a "Reverse" command to the "Order" menu (#2273)

* addded reverse for layer ordering

* added reverse to layer odering

* fixed indentation and spacing

* added reverse for layer ordering

* Added Reverse to layer ordering

* Update document_message_handler.rs

* Update document_message_handler.rs

* Cleanup, fix compiling, fix history bug

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Tarun Prabhu 2025-02-11 15:06:56 +05:30 committed by GitHub
parent ec8c8d6485
commit bf3b05070c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 97 additions and 2 deletions

View File

@ -114,6 +114,7 @@ pub enum DocumentMessage {
SelectedLayersLowerToBack, SelectedLayersLowerToBack,
SelectedLayersRaise, SelectedLayersRaise,
SelectedLayersRaiseToFront, SelectedLayersRaiseToFront,
SelectedLayersReverse,
SelectedLayersReorder { SelectedLayersReorder {
relative_index_offset: isize, relative_index_offset: isize,
}, },

View File

@ -948,6 +948,9 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
DocumentMessage::SelectedLayersRaiseToFront => { DocumentMessage::SelectedLayersRaiseToFront => {
responses.add(DocumentMessage::SelectedLayersReorder { relative_index_offset: isize::MIN }); responses.add(DocumentMessage::SelectedLayersReorder { relative_index_offset: isize::MIN });
} }
DocumentMessage::SelectedLayersReverse => {
self.selected_layers_reverse(responses);
}
DocumentMessage::SelectedLayersReorder { relative_index_offset } => { DocumentMessage::SelectedLayersReorder { relative_index_offset } => {
self.selected_layers_reorder(relative_index_offset, responses); self.selected_layers_reorder(relative_index_offset, responses);
} }
@ -2149,6 +2152,98 @@ impl DocumentMessageHandler {
}); });
} }
pub fn selected_layers_reverse(&mut self, responses: &mut VecDeque<Message>) {
let selected_layers = self.network_interface.selected_nodes(&[]).unwrap();
let metadata = self.metadata();
let selected_layer_set = selected_layers.selected_layers(metadata).collect::<HashSet<_>>();
// Ignore those with selected ancestors
let mut top_level_layers = Vec::new();
for &layer in &selected_layer_set {
let mut is_top_level = true;
let mut current_layer = layer;
while let Some(parent) = current_layer.parent(metadata) {
if selected_layer_set.contains(&parent) {
is_top_level = false;
break;
}
current_layer = parent;
}
if is_top_level {
top_level_layers.push(layer);
}
}
// Group selected layers by their parent
let mut grouped_layers: HashMap<LayerNodeIdentifier, Vec<(usize, LayerNodeIdentifier)>> = HashMap::new();
for &layer in &top_level_layers {
if let Some(parent) = layer.parent(metadata) {
let index = parent.children(metadata).position(|child| child == layer).unwrap_or(usize::MAX);
grouped_layers.entry(parent).or_default().push((index, layer));
}
}
let mut modified = false;
// Process each group separately
for (parent, mut layers) in grouped_layers {
// Retrieve all children under the parent
let all_children = parent.children(metadata).collect::<Vec<_>>();
// Separate unselected layers with their original indices
let unselected_layers = all_children
.iter()
.enumerate()
.filter_map(|(index, &layer)| if !selected_layer_set.contains(&layer) { Some((index, layer)) } else { None })
.collect::<Vec<_>>();
layers.sort_by_key(|(index, _)| *index);
let reversed_layers = layers.iter().rev().map(|(_, layer)| *layer).collect::<Vec<_>>();
let selected_positions = layers.iter().map(|(index, _)| *index).collect::<Vec<_>>();
let selected_iter = reversed_layers.into_iter();
let mut merged_layers = vec![None; all_children.len()];
for (&original_index, new_layer) in selected_positions.iter().zip(selected_iter) {
merged_layers[original_index] = Some(new_layer);
}
// Place unselected layers at their original positions
for (index, layer) in unselected_layers {
if merged_layers[index].is_none() {
merged_layers[index] = Some(layer);
}
}
let final_layers = merged_layers.into_iter().flatten().collect::<Vec<_>>();
if final_layers.is_empty() {
continue;
}
if !modified {
responses.add(DocumentMessage::AddTransaction);
}
for (index, layer) in final_layers.iter().enumerate() {
responses.add(NodeGraphMessage::MoveLayerToStack {
layer: *layer,
parent,
insert_index: index,
});
}
modified = true;
}
if modified {
responses.add(NodeGraphMessage::RunDocumentGraph);
responses.add(NodeGraphMessage::SendGraph);
}
}
pub fn selected_layers_reorder(&mut self, relative_index_offset: isize, responses: &mut VecDeque<Message>) { pub fn selected_layers_reorder(&mut self, relative_index_offset: isize, responses: &mut VecDeque<Message>) {
let selected_nodes = self.network_interface.selected_nodes(&[]).unwrap(); let selected_nodes = self.network_interface.selected_nodes(&[]).unwrap();
let mut selected_layers = selected_nodes.selected_layers(self.metadata()); let mut selected_layers = selected_nodes.selected_layers(self.metadata());

View File

@ -346,8 +346,7 @@ impl LayoutHolder for MenuBarMessageHandler {
vec![MenuBarEntry { vec![MenuBarEntry {
label: "Reverse".into(), label: "Reverse".into(),
icon: Some("StackReverse".into()), icon: Some("StackReverse".into()),
// shortcut: action_keys!(DocumentMessageDiscriminant::SelectedLayersReverse), action: MenuBarEntry::create_action(|_| DocumentMessage::SelectedLayersReverse.into()),
action: MenuBarEntry::create_action(|_| DialogMessage::RequestComingSoonDialog { issue: Some(2271) }.into()),
disabled: no_active_document || !has_selected_layers, disabled: no_active_document || !has_selected_layers,
..MenuBarEntry::default() ..MenuBarEntry::default()
}], }],