fix(panels): layer context-menu Delete/Duplicate act on the whole selection

Right-press menu-open + Delete/Duplicate dispatch kept the multi-selection instead of collapsing to the right-clicked row (web + native). Adds a multi-delete core test.
This commit is contained in:
Kayshen-X 2026-06-02 22:14:01 +08:00
parent 3d3ba232c8
commit fecdd395d0
3 changed files with 48 additions and 5 deletions

View file

@ -172,6 +172,25 @@ fn delete_selected_returns_false_when_unselected() {
assert!(!s.delete_selected());
}
#[test]
fn delete_selected_removes_every_node_in_the_set() {
let mut s = state_with(vec![
rect("n1", "A", 0.0, 0.0, 10.0, 10.0),
rect("n2", "B", 20.0, 0.0, 10.0, 10.0),
rect("n3", "C", 40.0, 0.0, 10.0, 10.0),
]);
s.clear_selection();
s.toggle_selection(NodeId::new("n1"));
s.toggle_selection(NodeId::new("n2"));
assert_eq!(s.selection_count(), 2);
assert!(s.delete_selected());
assert!(find_node(s.active_children(), &NodeId::new("n1")).is_none());
assert!(find_node(s.active_children(), &NodeId::new("n2")).is_none());
assert!(find_node(s.active_children(), &NodeId::new("n3")).is_some());
assert_eq!(s.active_children().len(), 1);
assert!(s.selection.is_empty());
}
#[test]
fn delete_selected_protects_ancestor_of_locked_descendant() {
let mut child = rect("n61", "child", 0.0, 0.0, 10.0, 10.0);

View file

@ -28,14 +28,22 @@ impl WidgetHostNative {
use op_editor_ui::widgets::layer_context_menu::LayerContextAction as A;
match (action, target) {
(A::Duplicate, T::Layer(id)) => {
self.editor_state.set_single_selection(id);
// Act on the whole multi-selection when the right-clicked
// row is part of it; otherwise retarget to just this row.
if !self.editor_state.is_selected(&id) {
self.editor_state.set_single_selection(id);
}
self.editor_state.commit_history();
let _ = self
.editor_state
.duplicate_selected(&mut self.next_node_id, 10.0);
}
(A::Delete, T::Layer(id)) => {
self.editor_state.set_single_selection(id);
// Keep the multi-selection so Delete removes every selected
// layer, not just the right-clicked one.
if !self.editor_state.is_selected(&id) {
self.editor_state.set_single_selection(id);
}
self.editor_state.commit_history();
let _ = self.editor_state.delete_selected();
}

View file

@ -63,7 +63,15 @@ impl WidgetHost {
match panel.hit_test(layer_rect, Point2D::new(x, y)) {
Some(LayerPanelHit::Layer(id)) => {
let ec_id = id.clone();
self.editor_state.set_single_selection(ec_id.clone());
// Right-clicking a row that's part of a multi-selection
// keeps the whole selection (so context-menu Delete /
// Duplicate act on every selected layer); right-clicking
// outside the selection retargets to just that row.
if !(self.editor_state.is_selected(&ec_id)
&& self.editor_state.selection_count() > 1)
{
self.editor_state.set_single_selection(ec_id.clone());
}
self.editor_state.editor_ui.layer_context_menu = Some(LayerContextMenuState {
target: LayerContextTarget::Layer(ec_id),
anchor_x: x,
@ -107,14 +115,22 @@ impl WidgetHost {
use op_editor_ui::widgets::layer_context_menu::LayerContextAction as A;
match (action, target) {
(A::Duplicate, T::Layer(id)) => {
self.editor_state.set_single_selection(id);
// Act on the whole multi-selection when the right-clicked
// row is part of it; otherwise retarget to just this row.
if !self.editor_state.is_selected(&id) {
self.editor_state.set_single_selection(id);
}
self.editor_state.commit_history();
let _ = self
.editor_state
.duplicate_selected(&mut self.next_node_id, 10.0);
}
(A::Delete, T::Layer(id)) => {
self.editor_state.set_single_selection(id);
// Keep the multi-selection so Delete removes every selected
// layer, not just the right-clicked one.
if !self.editor_state.is_selected(&id) {
self.editor_state.set_single_selection(id);
}
self.editor_state.commit_history();
let _ = self.editor_state.delete_selected();
}