mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-30 21:47:00 -06:00
feat(ui): support Alt+1..9 shortcuts for Quick Access Toolbar items
This commit is contained in:
@@ -72,6 +72,24 @@ pub fn ui_shortcut(app: &mut SlideApp, key: egui::Key, m: Mods) -> bool {
|
||||
}
|
||||
(F, true, false, false) => "app.find",
|
||||
(H, true, false, false) => "app.replace",
|
||||
(Num1 | Num2 | Num3 | Num4 | Num5 | Num6 | Num7 | Num8 | Num9, false, false, true) => {
|
||||
if app.session.mac_keys || app.backstage.is_some() || app.session.active().is_some_and(|d| d.selection.text.is_some()) {
|
||||
return false;
|
||||
}
|
||||
let idx = match key {
|
||||
Num1 => 0,
|
||||
Num2 => 1,
|
||||
Num3 => 2,
|
||||
Num4 => 3,
|
||||
Num5 => 4,
|
||||
Num6 => 5,
|
||||
Num7 => 6,
|
||||
Num8 => 7,
|
||||
Num9 => 8,
|
||||
_ => return false,
|
||||
};
|
||||
return crate::qat::run_index(app, idx);
|
||||
}
|
||||
_ => return false,
|
||||
};
|
||||
let _ = app.run(id, json!({}));
|
||||
|
||||
@@ -101,26 +101,45 @@ fn run(app: &mut SlideApp, q: &QatItem) {
|
||||
}
|
||||
}
|
||||
|
||||
fn tip(q: &QatItem) -> String {
|
||||
/// Whether a Quick Access Toolbar item can be run right now.
|
||||
pub fn is_enabled(app: &SlideApp, id: &str) -> bool {
|
||||
match id {
|
||||
"app.home" => true,
|
||||
"file.save" | "show.fromStart" | "review.spelling" | "app.find" => app.session.active().is_some(),
|
||||
id => crate::ribbon::enabled(app, id),
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs the Quick Access Toolbar item at `index` (0-based) if it exists and is enabled. Returns `true` if run.
|
||||
pub fn run_index(app: &mut SlideApp, index: usize) -> bool {
|
||||
let Some(q) = app.ui.qat.get(index).cloned() else { return false };
|
||||
if !is_enabled(app, &q.id) {
|
||||
return false;
|
||||
}
|
||||
run(app, &q);
|
||||
true
|
||||
}
|
||||
|
||||
fn tip(q: &QatItem, index: usize, mac: bool) -> String {
|
||||
let sc =
|
||||
deckcraft_engine::find_command(&q.id).and_then(|c| c.shortcut).or_else(|| crate::UI_COMMANDS.iter().find(|c| c.0 == q.id).and_then(|c| c.2));
|
||||
match sc {
|
||||
Some(s) => format!("{} ({})", q.label, crate::ribbon::pretty_shortcut(s)),
|
||||
None => q.label.clone(),
|
||||
let qat_sc = if !mac && index < 9 { Some(format!("Alt+{}", index + 1)) } else { None };
|
||||
match (sc, qat_sc) {
|
||||
(Some(s), Some(qsc)) => format!("{} ({}, {})", q.label, crate::ribbon::pretty_shortcut(s), qsc),
|
||||
(Some(s), None) => format!("{} ({})", q.label, crate::ribbon::pretty_shortcut(s)),
|
||||
(None, Some(qsc)) => format!("{} ({})", q.label, qsc),
|
||||
(None, None) => q.label.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw the toolbar and its Customize menu into a left-to-right row of the title bar.
|
||||
pub fn show(app: &mut SlideApp, ui: &mut Ui) {
|
||||
let items = app.ui.qat.clone();
|
||||
for q in &items {
|
||||
let mac = app.session.mac_keys;
|
||||
for (i, q) in items.iter().enumerate() {
|
||||
let on = q.id == "app.home" && app.backstage_open();
|
||||
let en = match q.id.as_str() {
|
||||
"app.home" => true,
|
||||
"file.save" | "show.fromStart" | "review.spelling" | "app.find" => app.session.active().is_some(),
|
||||
id => crate::ribbon::enabled(app, id),
|
||||
};
|
||||
let r = crate::widgets::icon_toggle(ui, q.icon(), &tip(q), en, on);
|
||||
let en = is_enabled(app, &q.id);
|
||||
let r = crate::widgets::icon_toggle(ui, q.icon(), &tip(q, i, mac), en, on);
|
||||
if r.clicked() {
|
||||
run(app, q);
|
||||
}
|
||||
|
||||
@@ -123,6 +123,27 @@ fn quick_access_toolbar_adds_removes_resets_and_persists() {
|
||||
assert_eq!(app.ui.qat.len(), 6);
|
||||
app.run("app.qat", json!({"reset": true})).unwrap();
|
||||
assert_eq!(ids(app), ["app.home", "file.save", "app.exportDialog", "edit.undo", "edit.redo", "show.fromStart"]);
|
||||
// Alt+1 runs position 0 (app.home) when on Windows/Linux, opening Backstage.
|
||||
app.session.mac_keys = false;
|
||||
assert!(!app.backstage_open());
|
||||
assert!(crate::menus::ui_shortcut(app, egui::Key::Num1, deckcraft_engine::Mods { shift: false, alt: true, cmd: false }));
|
||||
assert!(app.backstage_open());
|
||||
// Disabled while Backstage is open.
|
||||
assert!(!crate::menus::ui_shortcut(app, egui::Key::Num1, deckcraft_engine::Mods { shift: false, alt: true, cmd: false }));
|
||||
app.close_backstage();
|
||||
// Alt+9 is unassigned when there are only 6 items.
|
||||
assert!(!crate::menus::ui_shortcut(app, egui::Key::Num9, deckcraft_engine::Mods { shift: false, alt: true, cmd: false }));
|
||||
// Disabled on macOS so Option+digit types characters.
|
||||
app.session.mac_keys = true;
|
||||
assert!(!crate::menus::ui_shortcut(app, egui::Key::Num1, deckcraft_engine::Mods { shift: false, alt: true, cmd: false }));
|
||||
app.session.mac_keys = false;
|
||||
// Disabled when text is being edited.
|
||||
let shape_id = deckcraft_model::ShapeId(app.session.active().unwrap().selection.shapes.first().map(|s| s.0).unwrap_or(0));
|
||||
app.session.active_mut().unwrap().selection.text = Some(deckcraft_engine::TextSel { shape: shape_id, ..Default::default() });
|
||||
assert!(!crate::menus::ui_shortcut(app, egui::Key::Num1, deckcraft_engine::Mods { shift: false, alt: true, cmd: false }));
|
||||
app.session.active_mut().unwrap().selection.text = None;
|
||||
// Disabled QAT items (e.g. edit.undo when stack is empty) cannot run.
|
||||
assert!(!crate::qat::run_index(app, 3));
|
||||
// An older settings file without the toolbar gets the defaults.
|
||||
let old: UiState = serde_json::from_value(json!({"tab": "Home"})).unwrap();
|
||||
assert_eq!(old.qat.len(), 6);
|
||||
|
||||
Reference in New Issue
Block a user