From fa9d89efad052d9f5591ac4f8b72d18db4ca58ed Mon Sep 17 00:00:00 2001 From: "sorokin.oleg.edu" Date: Thu, 24 Sep 2026 22:38:52 -0700 Subject: [PATCH] Close expired menus and cancel noneditable sequence gestures --- crates/oa-desktop/src/app.rs | 5 +++++ crates/oa-desktop/src/behavior.rs | 6 +++--- .../src/behavior/state_multiselect_canvas_tests.rs | 5 ++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/crates/oa-desktop/src/app.rs b/crates/oa-desktop/src/app.rs index bc8853a1..55954271 100644 --- a/crates/oa-desktop/src/app.rs +++ b/crates/oa-desktop/src/app.rs @@ -1452,6 +1452,11 @@ impl App { || menu.request.canvas.is_some()) && (menu.token != *token || !menu.valid(self)) { + // Consume an expired menu's own packet without touching a newer + // menu or crossing source/selection boundaries. + if menu.token == *token { + self.model_menu = None; + } return Task::none(); } if self.scenario_group_selected() diff --git a/crates/oa-desktop/src/behavior.rs b/crates/oa-desktop/src/behavior.rs index 60050025..61804541 100644 --- a/crates/oa-desktop/src/behavior.rs +++ b/crates/oa-desktop/src/behavior.rs @@ -2071,15 +2071,15 @@ impl canvas::Program for Scene { } } state.bounds = Some(bounds); - if !editable { - state.group_press = None; - } if (!editable || !self.coordinate_input) && matches!(state.press, Some(SequencePress::Spacing { .. })) && let Some(cancelled) = state.cancel_spacing() { return Some(cancelled); } + if !editable { + state.cancel(); + } // The viewport masks input outside its visible canvas bounds. let visible = cursor.position().is_some(); if !visible diff --git a/crates/oa-desktop/src/behavior/state_multiselect_canvas_tests.rs b/crates/oa-desktop/src/behavior/state_multiselect_canvas_tests.rs index 48860f5a..fc415505 100644 --- a/crates/oa-desktop/src/behavior/state_multiselect_canvas_tests.rs +++ b/crates/oa-desktop/src/behavior/state_multiselect_canvas_tests.rs @@ -328,7 +328,10 @@ fn state_group_delete_and_escape_respect_held_gesture_and_full_set() { let mut state = State::default(); use keyboard::key::Named; assert!(matches!(group_key(&canvas, &mut state, Named::Delete, keyboard::Modifiers::empty()), Some(Event::Selection(SelectionEvent::Hide(_))))); - assert!(matches!(group_key(&canvas, &mut state, Named::Delete, keyboard::Modifiers::SHIFT), Some(Event::Selection(SelectionEvent::Refuse(_, _))))); + let Some(Event::ModelRemoval(removal)) = group_key(&canvas, &mut state, Named::Delete, keyboard::Modifiers::SHIFT) else { + panic!("Shift+Delete must capture the complete model-removal group"); + }; + assert!(matches!(*removal, crate::app::saved_removal::Event::Begin { snapshot, .. } if snapshot.targets() == selection.targets())); let at = screen(&canvas, header(&canvas, f.a)); motion(&canvas, &mut state, at, at); input(&canvas, &mut state, mouse::Event::ButtonPressed(mouse::Button::Left), at);