diff --git a/apps/soundcraft/src/native_menu.rs b/apps/soundcraft/src/native_menu.rs index 9cc72de..82cd25a 100644 --- a/apps/soundcraft/src/native_menu.rs +++ b/apps/soundcraft/src/native_menu.rs @@ -105,6 +105,11 @@ impl NativeMenu { self.last_sync = Some(now); for entry in &mut self.entries { let (enabled, checked) = menus::item_state(app, &entry.path, entry.command.as_deref()); + if let Some(label) = menus::live_label(app, entry.command.as_deref()) + && entry.item.text() != label + { + entry.item.set_text(label); + } if enabled != entry.enabled { entry.item.set_enabled(enabled); entry.enabled = enabled; diff --git a/crates/ui-egui/src/menus.rs b/crates/ui-egui/src/menus.rs index 0e540e0..5b8f079 100644 --- a/crates/ui-egui/src/menus.rs +++ b/crates/ui-egui/src/menus.rs @@ -259,7 +259,8 @@ fn menu_node(app: &mut SoundApp, ui: &mut egui::Ui, n: &MenuNode, extra: &[(&str } let id = command_for_path(&n.path, extra); let (enabled, checked) = item_state(app, &n.path, id.as_deref()); - let label = if checked { format!("✔ {}", n.label) } else { n.label.clone() }; + let label = live_label(app, id.as_deref()).unwrap_or_else(|| n.label.clone()); + let label = if checked { format!("✔ {label}") } else { label }; let shortcut = id.as_deref().and_then(soundcraft_engine::find_command).and_then(|c| c.shortcut).unwrap_or(""); let btn = egui::Button::new(label).shortcut_text(crate::shortcuts::shortcut_label(shortcut, ui.ctx().os().is_mac())); if ui.add_enabled(enabled, btn).clicked() @@ -387,6 +388,16 @@ const MENU_WINDOWS: &[(&str, &str)] = &[ ("Event > MIDI Real-Time Properties", "window.rtp"), ]; +/// Undo and Redo name the step they would undo or redo; other items keep their catalog label. +pub fn live_label(app: &SoundApp, id: Option<&str>) -> Option { + let e = &app.engine; + match id? { + "edit.undo" => Some(e.undo_label().map_or_else(|| "Can't Undo".into(), |l| format!("Undo {l}"))), + "edit.redo" => Some(e.redo_label().map_or_else(|| "Can't Redo".into(), |l| format!("Redo {l}"))), + _ => None, + } +} + /// Menu-style invocation: may open a dialog. pub fn invoke_menu(app: &mut SoundApp, id: &str, path: &str) { if let Some((_, w)) = MENU_WINDOWS.iter().find(|(p, _)| *p == path) diff --git a/crates/ui-egui/tests/edit_menu.rs b/crates/ui-egui/tests/edit_menu.rs new file mode 100644 index 0000000..8fdbb52 --- /dev/null +++ b/crates/ui-egui/tests/edit_menu.rs @@ -0,0 +1,15 @@ +use serde_json::json; +use soundcraft_ui_egui::{Services, SoundApp, menus}; + +#[test] +fn undo_and_redo_name_the_step_they_would_take() { + let mut app = SoundApp::new(soundcraft_engine::demo::demo_engine(), None, Services::default()); + let label = |app: &SoundApp, id| menus::live_label(app, Some(id)); + assert_eq!(label(&app, "edit.undo").as_deref(), Some("Can't Undo")); + app.run("mix.mute", json!({"track": "Kick"})).unwrap(); + assert_eq!(label(&app, "edit.undo").as_deref(), Some("Undo Mute")); + assert_eq!(label(&app, "edit.redo").as_deref(), Some("Can't Redo")); + app.run("edit.undo", json!({})).unwrap(); + assert_eq!(label(&app, "edit.redo").as_deref(), Some("Redo Mute")); + assert_eq!(label(&app, "edit.copy"), None); +}