diff --git a/crates/ui-egui/src/dialogs/command_search.rs b/crates/ui-egui/src/dialogs/command_search.rs index c03d0a0f8..e43a0cb43 100644 --- a/crates/ui-egui/src/dialogs/command_search.rs +++ b/crates/ui-egui/src/dialogs/command_search.rs @@ -20,6 +20,7 @@ pub(super) fn command_search(app: &mut SheetApp, ui: &mut egui::Ui, d: &mut Dial d.focus_initialized = true; } + let mac = ui.ctx().os() == egui::os::OperatingSystem::Mac; let q = d.search.to_ascii_lowercase(); let cmds: Vec<(String, String, String, Option<&str>)> = app .session @@ -27,14 +28,7 @@ pub(super) fn command_search(app: &mut SheetApp, ui: &mut egui::Ui, d: &mut Dial .into_iter() .filter(|c| c.enabled) .filter(|c| q.is_empty() || c.label.to_ascii_lowercase().contains(&q) || c.id.to_ascii_lowercase().contains(&q)) - .map(|c| { - ( - c.id.to_string(), - c.label.to_string(), - c.menu.join(" › "), - crate::widgets::tooltip_shortcut(c.label, cfg!(target_os = "macos")).or(c.shortcut), - ) - }) + .map(|c| (c.id.to_string(), c.label.to_string(), c.menu.join(" › "), command_search_shortcut(c.id, mac))) .take(40) .collect(); @@ -71,15 +65,6 @@ pub(super) fn command_search(app: &mut SheetApp, ui: &mut egui::Ui, d: &mut Dial egui::ScrollArea::vertical().max_height(LIST_HEIGHT).auto_shrink([true, false]).show(ui, |ui| { let scroll_area_width = ui.available_width(); for (i, (id, label, menu, shortcut)) in cmds.iter().enumerate() { - let shortcut = shortcut.map(|keys| { - if cfg!(target_os = "macos") { - keys.to_string() - } else if keys == "Ctrl+Cmd+V" { - "Ctrl+Alt+V".to_string() - } else { - keys.strip_prefix("Cmd+").map_or_else(|| keys.to_string(), |rest| format!("Ctrl+{rest}")) - } - }); let text = match shortcut { Some(keys) => format!("{label} ({keys}) {menu}"), None => format!("{label} {menu}"), @@ -107,3 +92,38 @@ pub(super) fn command_search(app: &mut SheetApp, ui: &mut egui::Ui, d: &mut Dial app.run_or_alert(&id, json!({})); } } + +/// Only advertises shortcuts verified against the grid keymap and ribbon. +/// Commands without a confirmed binding are shown without a shortcut. +fn command_search_shortcut(id: &str, mac: bool) -> Option<&'static str> { + let (mac_keys, other_keys) = match id { + "file.new" => ("⌘N", Some("Ctrl+N")), + "file.open" => ("⌘O", Some("Ctrl+O")), + "file.save" => ("⌘S", Some("Ctrl+S")), + "file.saveAs" => ("⌘⇧S", Some("Ctrl+Shift+S")), + "file.close" => ("⌘W", Some("Ctrl+W")), + "edit.copy" => ("⌘C", Some("Ctrl+C")), + "edit.cut" => ("⌘X", Some("Ctrl+X")), + "edit.paste" => ("⌘V", Some("Ctrl+V")), + "edit.undo" => ("⌘Z", Some("Ctrl+Z")), + "edit.redo" => ("⌘Y", Some("Ctrl+Y")), + "edit.selectAll" => ("⌘A", Some("Ctrl+A")), + "edit.fillDown" => ("⌘D", Some("Ctrl+D")), + "edit.fillRight" => ("⌘R", Some("Ctrl+R")), + "home.bold" => ("⌘B", Some("Ctrl+B")), + "home.italic" => ("⌘I", Some("Ctrl+I")), + "home.underline" => ("⌘U", Some("Ctrl+U")), + "formulas.autoSum" => ("⌘⇧T", Some("Alt+=")), + "insert.table" => ("⌘T", Some("Ctrl+T")), + "data.filter" => ("⌘⇧F", Some("Ctrl+Shift+L")), + "edit.flashFill" => ("Control+E", Some("Ctrl+E")), + "edit.pasteSpecial" => ("Ctrl+Cmd+V", Some("Ctrl+Alt+V")), + "home.alignLeft" => ("⌘L", None), + "home.alignCenter" => ("⌘E", None), + "view.showObjects" => ("Ctrl+6", Some("Ctrl+6")), + "formulas.showFormulas" => ("⌘`", Some("Ctrl+`")), + _ => return None, + }; + + if mac { Some(mac_keys) } else { other_keys } +} diff --git a/crates/ui-egui/tests/it/dialog_focus.rs b/crates/ui-egui/tests/it/dialog_focus.rs index 44b726958..902bb8e4c 100644 --- a/crates/ui-egui/tests/it/dialog_focus.rs +++ b/crates/ui-egui/tests/it/dialog_focus.rs @@ -93,13 +93,13 @@ fn command_search_shows_shortcuts_for_current_platform() { h.state_mut().dialog.as_mut().unwrap().search = "bold".into(); h.run_steps(3); - let bold = if cfg!(target_os = "macos") { "Bold (⌘B)" } else { "Bold (Ctrl+B)" }; + let bold = if h.ctx.os() == egui::os::OperatingSystem::Mac { "Bold (⌘B)" } else { "Bold (Ctrl+B)" }; h.get_by_label_contains(bold); h.state_mut().dialog.as_mut().unwrap().search = "filter".into(); h.run_steps(3); - let filter = if cfg!(target_os = "macos") { "Filter (⌘⇧F)" } else { "Filter (Ctrl+Shift+L)" }; + let filter = if h.ctx.os() == egui::os::OperatingSystem::Mac { "Filter (⌘⇧F)" } else { "Filter (Ctrl+Shift+L)" }; h.get_by_label_contains(filter); } @@ -117,7 +117,57 @@ fn command_search_shows_paste_special_shortcut() { h.state_mut().dialog.as_mut().unwrap().search = "pasteSpecial".into(); h.run_steps(3); - let expected = if cfg!(target_os = "macos") { "Paste Special… (Ctrl+Cmd+V)" } else { "Paste Special… (Ctrl+Alt+V)" }; + let expected = if h.ctx.os() == egui::os::OperatingSystem::Mac { "Paste Special… (Ctrl+Cmd+V)" } else { "Paste Special… (Ctrl+Alt+V)" }; h.get_by_label_contains(expected); } + +/// Checks both OS mappings on one host, including commands with conflicting keys. +#[test] +fn command_search_uses_runtime_os_and_verified_shortcuts() { + use egui::os::OperatingSystem; + + for (os, bold, filter, flash, paste, left, center) in [ + ( + OperatingSystem::Windows, + "Bold (Ctrl+B)", + "Filter (Ctrl+Shift+L)", + "Flash Fill (Ctrl+E)", + "Paste Special… (Ctrl+Alt+V)", + "Align Left Home", + "Center Home", + ), + ( + OperatingSystem::Mac, + "Bold (⌘B)", + "Filter (⌘⇧F)", + "Flash Fill (Control+E)", + "Paste Special… (Ctrl+Cmd+V)", + "Align Left (⌘L)", + "Center (⌘E)", + ), + ] { + let mut h = harness(); + h.ctx.set_os(os); + h.run_steps(2); + + h.state_mut().session.execute("cell.set", json!({"cell": "C3", "input": "copied"})).unwrap(); + h.state_mut().session.execute("edit.copy", json!({"range": "C3"})).unwrap(); + + h.state_mut().open_dialog("commandSearch", json!({})); + h.run_steps(6); + + for (query, expected) in [ + ("bold", bold), + ("filter", filter), + ("edit.flashFill", flash), + ("edit.pasteSpecial", paste), + ("home.alignLeft", left), + ("home.alignCenter", center), + ] { + h.state_mut().dialog.as_mut().unwrap().search = query.into(); + h.run_steps(3); + h.get_by_label_contains(expected); + } + } +}