From b2c62c398ed3a188153aa9feccca931a273ec317 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sun, 1 Nov 2026 17:38:53 -0800 Subject: [PATCH] Actions: the panel menu reads the selected action only in the current set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The panel menu read the stored (set, action) pair without checking that its set is still the current one, as the panel and Delete Selection do. After `ui.actionSet.delete` removed a set above it, the pair pointed at another set's action, and Action Options…, Batch… and Play Current Selection acted on that action. They are now off until an action of the current set is selected. --- crates/ui-egui/src/panels/actions.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/panels/actions.rs b/crates/ui-egui/src/panels/actions.rs index 109d4d824..fa8ed65bc 100644 --- a/crates/ui-egui/src/panels/actions.rs +++ b/crates/ui-egui/src/panels/actions.rs @@ -275,7 +275,9 @@ pub fn duplicate(app: &mut VectorcraftApp, p: &Value) -> Result { /// The panel's (≡) menu. pub fn menu(app: &mut VectorcraftApp, ui: &mut egui::Ui) { - let selected: Option<(usize, usize)> = pstate(ui.ctx(), "actions-selected"); + // A selected action counts only while its set is the current one, as the panel shows it: a set + // deleted since leaves the stored pair pointing at another set's action. + let selected = pstate::>(ui.ctx(), "actions-selected").filter(|&(s, _)| app.ui.action_set == Some(s)); let has = selected.and_then(|(s, a)| app.ui.action_sets.get(s).and_then(|set| set.actions.get(a))).is_some(); if widgets::menu_item(ui, tl!("New Set…"), true, false) { crate::dialogs::action_set_options::open_new(app); @@ -633,6 +635,26 @@ mod tests { } } + /// After `ui.actionSet.delete` removes a set above the selected action's set, the panel menu's + /// Action Options… is off: it does not open for the action that moved to the stored position. + #[test] + fn action_options_is_off_after_a_set_above_the_selection_is_deleted() { + use super::super::tests_appearance::{click, frame_events}; + let mut app = app_with_sets(); + let action = |name: &str| Action { name: name.into(), steps: vec![] }; + app.ui.action_sets[2].actions.push(action("Spin")); + app.ui.action_sets.push(ActionSet::new("Last", vec![action("Wrong")])); + let ctx = egui::Context::default(); + app.ui.action_set = Some(2); + set_pstate::>(&ctx, "actions-selected", Some((2, 0))); + app.run(DELETE_SETS, json!({"sets": [1]})).unwrap(); + assert_eq!(app.ui.action_set, Some(1), "Other is still current"); + let texts = frame_events(&ctx, &mut app, vec![], menu); + let item = texts.iter().find(|(s, _)| s.trim() == "Action Options…").map(|(_, r)| *r).expect("Action Options… in the menu"); + click(&ctx, &mut app, item.center(), menu); + assert!(app.ui.dialog.is_none(), "Action Options… does not open for Last's action"); + } + /// An app with the default set and the sets Mine (two actions) and Other. fn app_with_sets() -> VectorcraftApp { let mut app = VectorcraftApp::new(Session::new(), Default::default());