mirror of
https://github.com/storytold/gridcraft.git
synced 2026-11-03 00:41:56 -06:00
fix(ui): preserve portable shortcuts and macOS overrides
This commit is contained in:
+10
-42
@@ -41,7 +41,6 @@ pub struct CommandSpec {
|
||||
pub menu: &'static [&'static str],
|
||||
pub shortcut: Option<&'static str>,
|
||||
pub shortcut_mac: Option<&'static str>,
|
||||
pub shortcut_other: Option<&'static str>,
|
||||
pub params: &'static str,
|
||||
pub enabled: Enabled,
|
||||
pub run: Run,
|
||||
@@ -56,7 +55,6 @@ pub struct CommandInfo {
|
||||
pub menu: Vec<&'static str>,
|
||||
pub shortcut: Option<&'static str>,
|
||||
pub shortcut_mac: Option<&'static str>,
|
||||
pub shortcut_other: Option<&'static str>,
|
||||
pub params: &'static str,
|
||||
pub enabled: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@@ -64,9 +62,13 @@ pub struct CommandInfo {
|
||||
}
|
||||
|
||||
impl CommandInfo {
|
||||
/// Shortcut declared by the engine for the active platform.
|
||||
pub fn shortcut_for_platform(&self, mac: bool) -> Option<&'static str> {
|
||||
if mac { self.shortcut_mac } else { self.shortcut_other }
|
||||
/// Format the portable shortcut notation for the runtime operating system.
|
||||
pub fn shortcut_for_platform(&self, mac: bool) -> Option<String> {
|
||||
let keys = if mac { self.shortcut_mac.or(self.shortcut) } else { self.shortcut }?;
|
||||
if mac {
|
||||
return Some(keys.to_owned());
|
||||
}
|
||||
Some(keys.split('+').map(|part| if part == "Cmd" { "Ctrl" } else { part }).collect::<Vec<_>>().join("+"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +81,6 @@ impl CommandSpec {
|
||||
menu: self.menu.to_vec(),
|
||||
shortcut: self.shortcut,
|
||||
shortcut_mac: self.shortcut_mac,
|
||||
shortcut_other: self.shortcut_other,
|
||||
params: self.params,
|
||||
enabled: e.is_ok(),
|
||||
disabled_reason: e.err(),
|
||||
@@ -129,45 +130,14 @@ pub fn has_clipboard(s: &Session) -> std::result::Result<(), String> {
|
||||
}
|
||||
|
||||
macro_rules! cmd {
|
||||
(platform $id:literal, $label:literal, [$($m:literal),*], $mac:expr, $other:expr, $params:literal, $en:expr, $run:expr) => {
|
||||
$crate::cmd::CommandSpec {
|
||||
id: $id,
|
||||
label: $label,
|
||||
menu: &[$($m),*],
|
||||
shortcut: $mac,
|
||||
shortcut_mac: $mac,
|
||||
shortcut_other: $other,
|
||||
params: $params,
|
||||
enabled: $en,
|
||||
run: $run,
|
||||
journal: true,
|
||||
undoable: true,
|
||||
}
|
||||
};
|
||||
($id:literal, $label:literal, [$($m:literal),*], $sc:expr, $params:literal, $en:expr, $run:expr) => {
|
||||
$crate::cmd::CommandSpec { id: $id, label: $label, menu: &[$($m),*], shortcut: $sc, shortcut_mac: $sc, shortcut_other: $sc, params: $params, enabled: $en, run: $run, journal: true, undoable: true }
|
||||
$crate::cmd::CommandSpec { id: $id, label: $label, menu: &[$($m),*], shortcut: $sc, shortcut_mac: None, params: $params, enabled: $en, run: $run, journal: true, undoable: true }
|
||||
};
|
||||
(query $id:literal, $label:literal, [$($m:literal),*], $sc:expr, $params:literal, $en:expr, $run:expr) => {
|
||||
$crate::cmd::CommandSpec { id: $id, label: $label, menu: &[$($m),*], shortcut: $sc, shortcut_mac: $sc, shortcut_other: $sc, params: $params, enabled: $en, run: $run, journal: false, undoable: false }
|
||||
$crate::cmd::CommandSpec { id: $id, label: $label, menu: &[$($m),*], shortcut: $sc, shortcut_mac: None, params: $params, enabled: $en, run: $run, journal: false, undoable: false }
|
||||
};
|
||||
(noundo $id:literal, $label:literal, [$($m:literal),*], $sc:expr, $params:literal, $en:expr, $run:expr) => {
|
||||
$crate::cmd::CommandSpec { id: $id, label: $label, menu: &[$($m),*], shortcut: $sc, shortcut_mac: $sc, shortcut_other: $sc, params: $params, enabled: $en, run: $run, journal: true, undoable: false }
|
||||
};
|
||||
(platform_noundo $id:literal, $label:literal, [$($m:literal),*],
|
||||
$mac:expr, $other:expr, $params:literal, $en:expr, $run:expr) => {
|
||||
$crate::cmd::CommandSpec {
|
||||
id: $id,
|
||||
label: $label,
|
||||
menu: &[$($m),*],
|
||||
shortcut: $mac,
|
||||
shortcut_mac: $mac,
|
||||
shortcut_other: $other,
|
||||
params: $params,
|
||||
enabled: $en,
|
||||
run: $run,
|
||||
journal: true,
|
||||
undoable: false,
|
||||
}
|
||||
$crate::cmd::CommandSpec { id: $id, label: $label, menu: &[$($m),*], shortcut: $sc, shortcut_mac: None, params: $params, enabled: $en, run: $run, journal: true, undoable: false }
|
||||
};
|
||||
}
|
||||
pub(crate) use cmd;
|
||||
@@ -223,7 +193,6 @@ pub fn register_test_command(id: &'static str, run: Run) {
|
||||
menu: &[],
|
||||
shortcut: None,
|
||||
shortcut_mac: None,
|
||||
shortcut_other: None,
|
||||
params: "",
|
||||
enabled: |_| Ok(()),
|
||||
run,
|
||||
@@ -242,7 +211,6 @@ pub(crate) fn register_undoable_test_command(id: &'static str, run: Run) {
|
||||
menu: &[],
|
||||
shortcut: None,
|
||||
shortcut_mac: None,
|
||||
shortcut_other: None,
|
||||
params: "",
|
||||
enabled: |_| Ok(()),
|
||||
run,
|
||||
|
||||
@@ -93,18 +93,30 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
),
|
||||
cmd!(query "data.validate", "Check Value Against Validation", [], None, "{cell?, input} → {ok, message?}", has_doc, validate_cmd),
|
||||
cmd!(noundo "data.circleInvalid", "Circle Invalid Data", ["Data", "Data Tools", "Data Validation"], None, "{} → invalid cells", has_doc, circle_invalid),
|
||||
cmd!(platform
|
||||
"data.group",
|
||||
"Group",
|
||||
["Data", "Outline"],
|
||||
Some("Cmd+Shift+K"), Some("Alt+Shift+Right"),
|
||||
"{rows?: \"2:5\" | cols?: \"B:C\" | by?: rows|cols (the selection's rows or columns; a block of cells groups rows by default), sheet?}",
|
||||
has_doc,
|
||||
|s, p| group(s, p, true)
|
||||
),
|
||||
cmd!(platform "data.ungroup", "Ungroup", ["Data", "Outline"], Some("Cmd+Shift+J"), Some("Alt+Shift+Left"), "{rows? | cols? | by?: rows|cols, sheet?}", has_doc, |s, p| {
|
||||
group(s, p, false)
|
||||
}),
|
||||
CommandSpec {
|
||||
shortcut_mac: Some("Cmd+Shift+K"),
|
||||
..cmd!(
|
||||
"data.group",
|
||||
"Group",
|
||||
["Data", "Outline"],
|
||||
Some("Alt+Shift+Right"),
|
||||
"{rows?: \"2:5\" | cols?: \"B:C\" | by?: rows|cols (the selection's rows or columns; a block of cells groups rows by default), sheet?}",
|
||||
has_doc,
|
||||
|s, p| group(s, p, true)
|
||||
)
|
||||
},
|
||||
CommandSpec {
|
||||
shortcut_mac: Some("Cmd+Shift+J"),
|
||||
..cmd!(
|
||||
"data.ungroup",
|
||||
"Ungroup",
|
||||
["Data", "Outline"],
|
||||
Some("Alt+Shift+Left"),
|
||||
"{rows? | cols? | by?: rows|cols, sheet?}",
|
||||
has_doc,
|
||||
|s, p| { group(s, p, false) }
|
||||
)
|
||||
},
|
||||
cmd!(
|
||||
"data.hideDetail",
|
||||
"Hide Detail",
|
||||
|
||||
@@ -59,33 +59,37 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
cmd!(noundo "selection.set", "Select", [], None, "{range: \"A1:B2,D4\" | cell, active?: \"A1\", sheet?}", has_doc, selection_set),
|
||||
cmd!(noundo "selection.move", "Move Selection", [], None, "{dr, dc, extend?: bool, jump?: bool (Ctrl+arrow), page?: bool}", has_doc, selection_move),
|
||||
cmd!(noundo "selection.next", "Next Cell in Selection", [], None, "{forward?: true, byRow?: false} (Enter/Tab inside a selection)", has_doc, selection_next),
|
||||
cmd!(platform_noundo "edit.selectAll", "Select All", ["Edit"], Some("Cmd+A"), Some("Ctrl+A"), "{} (current region first, then the whole sheet)", has_doc, select_all),
|
||||
cmd!(noundo "edit.selectAll", "Select All", ["Edit"], Some("Cmd+A"), "{} (current region first, then the whole sheet)", has_doc, select_all),
|
||||
cmd!(noundo "selection.currentRegion", "Select Current Region", [], Some("Ctrl+Shift+8"), "{}", has_doc, select_region),
|
||||
cmd!(noundo "selection.row", "Select Entire Row", [], Some("Shift+Space"), "{}", has_doc, select_row),
|
||||
cmd!(noundo "selection.column", "Select Entire Column", [], Some("Ctrl+Space"), "{}", has_doc, select_col),
|
||||
cmd!(noundo "edit.goTo", "Go To…", ["Home", "Editing", "Find & Select"], Some("Ctrl+G"), "{reference: \"B5\" | \"Sheet2!A1:C3\" | name | \"Sheet2!Name\" | \"B14:C22,F19:G30\" | \"Price, Type\", record?: bool} (record: keep a reference without names among the recent ones) or {list: true} → {names, recent}", has_doc, go_to),
|
||||
cmd!(noundo "edit.goToSpecial", "Go To Special…", ["Home", "Editing", "Find & Select"], None, "{kind: blanks|constants|formulas|comments|lastCell|currentRegion|visible|conditionalFormats|dataValidation|errors|numbers|text}", has_doc, go_to_special),
|
||||
cmd!(platform_noundo "edit.undo", "Undo", ["Edit"], Some("Cmd+Z"), Some("Ctrl+Z"), "{steps?: 1}", can_undo, undo),
|
||||
cmd!(platform_noundo "edit.redo", "Redo", ["Edit"], Some("Cmd+Y"), Some("Ctrl+Y"), "{steps?: 1}", can_redo, redo),
|
||||
cmd!(platform_noundo "edit.copy", "Copy", ["Home", "Clipboard"], Some("Cmd+C"), Some("Ctrl+C"), "{range?, html?: bool} → {range, text, html?} (html: a styled HTML table, only when requested and small enough)", has_doc, copy),
|
||||
cmd!(platform_noundo "edit.cut", "Cut", ["Home", "Clipboard"], Some("Cmd+X"), Some("Ctrl+X"), "{range?, html?: bool} → {range, text, html?} (html: a styled HTML table, only when requested and small enough)", has_doc, cut),
|
||||
cmd!(platform "edit.paste",
|
||||
cmd!(noundo "edit.undo", "Undo", ["Edit"], Some("Cmd+Z"), "{steps?: 1}", can_undo, undo),
|
||||
cmd!(noundo "edit.redo", "Redo", ["Edit"], Some("Cmd+Y"), "{steps?: 1}", can_redo, redo),
|
||||
cmd!(noundo "edit.copy", "Copy", ["Home", "Clipboard"], Some("Cmd+C"), "{range?, html?: bool} → {range, text, html?} (html: a styled HTML table, only when requested and small enough)", has_doc, copy),
|
||||
cmd!(noundo "edit.cut", "Cut", ["Home", "Clipboard"], Some("Cmd+X"), "{range?, html?: bool} → {range, text, html?} (html: a styled HTML table, only when requested and small enough)", has_doc, cut),
|
||||
cmd!(
|
||||
"edit.paste",
|
||||
"Paste",
|
||||
["Home", "Clipboard"],
|
||||
Some("Cmd+V"), Some("Ctrl+V"),
|
||||
Some("Cmd+V"),
|
||||
"{at?: \"C3\", text?: \"tab-separated text from the system clipboard\", html?: \"the clipboard's HTML flavour; cells the source recorded as numbers (x:num, sdval) stay numbers\"}",
|
||||
has_doc,
|
||||
paste
|
||||
),
|
||||
cmd!(platform
|
||||
"edit.pasteSpecial",
|
||||
"Paste Special…",
|
||||
["Home", "Clipboard"],
|
||||
Some("Ctrl+Cmd+V"), Some("Ctrl+Alt+V"),
|
||||
"{what: all|formulas|values|formats|comments|validation|allExceptBorders|columnWidths|formulasAndNumberFormats|valuesAndNumberFormats, operation?: none|add|subtract|multiply|divide, skipBlanks?, transpose?, link?}",
|
||||
has_clipboard,
|
||||
paste_special
|
||||
),
|
||||
CommandSpec {
|
||||
shortcut_mac: Some("Ctrl+Cmd+V"),
|
||||
..cmd!(
|
||||
"edit.pasteSpecial",
|
||||
"Paste Special…",
|
||||
["Home", "Clipboard"],
|
||||
Some("Ctrl+Alt+V"),
|
||||
"{what: all|formulas|values|formats|comments|validation|allExceptBorders|columnWidths|formulasAndNumberFormats|valuesAndNumberFormats, operation?: none|add|subtract|multiply|divide, skipBlanks?, transpose?, link?}",
|
||||
has_clipboard,
|
||||
paste_special
|
||||
)
|
||||
},
|
||||
cmd!(noundo "edit.clearClipboard", "Cancel Copy", [], Some("Escape"), "{}", has_doc, clear_clipboard),
|
||||
cmd!("edit.clearAll", "Clear All", ["Home", "Editing", "Clear"], None, "{range?}", has_doc, |s, p| clear(s, p, "all")),
|
||||
cmd!("edit.clearFormats", "Clear Formats", ["Home", "Editing", "Clear"], None, "{range?}", has_doc, |s, p| clear(s, p, "formats")),
|
||||
@@ -96,8 +100,8 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
s, p, "comments"
|
||||
)),
|
||||
cmd!("edit.clearHyperlinks", "Clear Hyperlinks", ["Home", "Editing", "Clear"], None, "{range?}", has_doc, |s, p| clear(s, p, "hyperlinks")),
|
||||
cmd!(platform "edit.fillDown", "Fill Down", ["Home", "Editing", "Fill"], Some("Cmd+D"), Some("Ctrl+D"), "{range?}", has_doc, |s, p| fill_dir(s, p, 1, 0)),
|
||||
cmd!(platform "edit.fillRight", "Fill Right", ["Home", "Editing", "Fill"], Some("Cmd+R"), Some("Ctrl+R"), "{range?}", has_doc, |s, p| fill_dir(s, p, 0, 1)),
|
||||
cmd!("edit.fillDown", "Fill Down", ["Home", "Editing", "Fill"], Some("Cmd+D"), "{range?}", has_doc, |s, p| fill_dir(s, p, 1, 0)),
|
||||
cmd!("edit.fillRight", "Fill Right", ["Home", "Editing", "Fill"], Some("Cmd+R"), "{range?}", has_doc, |s, p| fill_dir(s, p, 0, 1)),
|
||||
cmd!("edit.fillUp", "Fill Up", ["Home", "Editing", "Fill"], None, "{range?}", has_doc, |s, p| fill_dir(s, p, -1, 0)),
|
||||
cmd!("edit.fillLeft", "Fill Left", ["Home", "Editing", "Fill"], None, "{range?}", has_doc, |s, p| fill_dir(s, p, 0, -1)),
|
||||
cmd!(
|
||||
|
||||
@@ -9,22 +9,23 @@ use super::*;
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
vec![
|
||||
// Font
|
||||
cmd!(platform "home.bold", "Bold", ["Home", "Font"], Some("Cmd+B"), Some("Ctrl+B"), "{range?, on?: bool}", has_doc, |s, p| toggle(
|
||||
cmd!("home.bold", "Bold", ["Home", "Font"], Some("Cmd+B"), "{range?, on?: bool}", has_doc, |s, p| toggle(
|
||||
s,
|
||||
p,
|
||||
|st| st.font.bold,
|
||||
|st, v| st.font.bold = v
|
||||
)),
|
||||
cmd!(platform "home.italic", "Italic", ["Home", "Font"], Some("Cmd+I"), Some("Ctrl+I"), "{range?, on?}", has_doc, |s, p| toggle(
|
||||
cmd!("home.italic", "Italic", ["Home", "Font"], Some("Cmd+I"), "{range?, on?}", has_doc, |s, p| toggle(
|
||||
s,
|
||||
p,
|
||||
|st| st.font.italic,
|
||||
|st, v| st.font.italic = v
|
||||
)),
|
||||
cmd!(platform "home.underline",
|
||||
cmd!(
|
||||
"home.underline",
|
||||
"Underline",
|
||||
["Home", "Font"],
|
||||
Some("Cmd+U"), Some("Ctrl+U"),
|
||||
Some("Cmd+U"),
|
||||
"{range?, on?, style?: single|double|singleAccounting|doubleAccounting}",
|
||||
has_doc,
|
||||
underline
|
||||
@@ -86,8 +87,14 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
format_cells
|
||||
),
|
||||
// Alignment
|
||||
cmd!(platform "home.alignLeft", "Align Left", ["Home", "Alignment"], Some("Cmd+L"), None, "{range?}", has_doc, |s, p| halign(s, p, HAlign::Left)),
|
||||
cmd!(platform "home.alignCenter", "Center", ["Home", "Alignment"], Some("Cmd+E"), None, "{range?}", has_doc, |s, p| halign(s, p, HAlign::Center)),
|
||||
CommandSpec {
|
||||
shortcut_mac: Some("Cmd+L"),
|
||||
..cmd!("home.alignLeft", "Align Left", ["Home", "Alignment"], None, "{range?}", has_doc, |s, p| halign(s, p, HAlign::Left))
|
||||
},
|
||||
CommandSpec {
|
||||
shortcut_mac: Some("Cmd+E"),
|
||||
..cmd!("home.alignCenter", "Center", ["Home", "Alignment"], None, "{range?}", has_doc, |s, p| halign(s, p, HAlign::Center))
|
||||
},
|
||||
cmd!("home.alignRight", "Align Right", ["Home", "Alignment"], None, "{range?}", has_doc, |s, p| halign(s, p, HAlign::Right)),
|
||||
cmd!("home.justify", "Justify", ["Home", "Alignment"], None, "{range?}", has_doc, |s, p| halign(s, p, HAlign::Justify)),
|
||||
cmd!("home.alignTop", "Top Align", ["Home", "Alignment"], None, "{range?}", has_doc, |s, p| valign(s, p, VAlign::Top)),
|
||||
|
||||
@@ -36,7 +36,6 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
menu: &["Review", "Protect"],
|
||||
shortcut: None,
|
||||
shortcut_mac: None,
|
||||
shortcut_other: None,
|
||||
params: "{action?: list (default) | add | modify | delete | unlock, title?, range?, password?, newTitle?, sheet?} → ranges",
|
||||
enabled: has_doc,
|
||||
run: allow_edit_ranges,
|
||||
|
||||
@@ -28,7 +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(" › "), c.shortcut_for_platform(mac).map(str::to_string)))
|
||||
.map(|c| (c.id.to_string(), c.label.to_string(), c.menu.join(" › "), c.shortcut_for_platform(mac)))
|
||||
.take(40)
|
||||
.collect();
|
||||
|
||||
|
||||
@@ -151,6 +151,7 @@ fn command_palette_shortcuts_come_from_engine_metadata() {
|
||||
|
||||
for (id, mac, other) in [
|
||||
("home.bold", Some("Cmd+B"), Some("Ctrl+B")),
|
||||
("edit.find", Some("Cmd+F"), Some("Ctrl+F")),
|
||||
("edit.selectAll", Some("Cmd+A"), Some("Ctrl+A")),
|
||||
("edit.undo", Some("Cmd+Z"), Some("Ctrl+Z")),
|
||||
("edit.redo", Some("Cmd+Y"), Some("Ctrl+Y")),
|
||||
@@ -172,8 +173,13 @@ fn command_palette_shortcuts_come_from_engine_metadata() {
|
||||
("selection.column", Some("Ctrl+Space"), Some("Ctrl+Space")),
|
||||
] {
|
||||
let command = commands.iter().find(|c| c.id == id).unwrap();
|
||||
assert_eq!(command.shortcut_for_platform(true), mac, "{id}: macOS");
|
||||
assert_eq!(command.shortcut_for_platform(false), other, "{id}: Windows/Linux");
|
||||
assert_eq!(command.shortcut_for_platform(true), mac.map(str::to_string), "{id}: macOS");
|
||||
assert_eq!(command.shortcut_for_platform(false), other.map(str::to_string), "{id}: Windows/Linux");
|
||||
}
|
||||
|
||||
for (id, legacy) in [("data.group", "Alt+Shift+Right"), ("data.ungroup", "Alt+Shift+Left")] {
|
||||
let command = commands.iter().find(|c| c.id == id).unwrap();
|
||||
assert_eq!(command.shortcut, Some(legacy), "{id}: legacy CLI shortcut");
|
||||
}
|
||||
|
||||
let command = commands.iter().find(|c| c.id == "home.alignRight").unwrap();
|
||||
|
||||
Reference in New Issue
Block a user