mirror of
https://github.com/storytold/filmcraft.git
synced 2026-10-24 10:10:05 -06:00
shortcuts: recording a chord skips the modifier key presses (#164)
egui 0.36 reports pressing Shift, Ctrl, Alt or the Windows / Cmd key as a
key event of its own, before the key they modify. The Keyboard Shortcuts
recorder took that first press and refused it ("AltLeft can't be used as a
shortcut"), so no chord with a modifier could be recorded. Modifier keys on
their own are now skipped; the chord is recorded with the key they modify.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
8fcad73c92
commit
98c022e0b0
@@ -105,13 +105,21 @@ fn assign(app: &mut FilmcraftApp, command: &str, keys: &str, add: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Take the next key press while recording (Esc cancels).
|
||||
/// A modifier key on its own. egui reports pressing Shift, Ctrl, Alt or the Windows / Cmd key as a
|
||||
/// key event of its own, before the key it modifies arrives (#164).
|
||||
fn is_modifier_key(key: egui::Key) -> bool {
|
||||
use egui::Key::*;
|
||||
matches!(key, ShiftLeft | ShiftRight | ControlLeft | ControlRight | AltLeft | AltRight | SuperLeft | SuperRight)
|
||||
}
|
||||
|
||||
/// Take the next key press while recording (Esc cancels). Modifier keys on their own are skipped:
|
||||
/// the chord is recorded when the key they modify is pressed, with them as its modifiers.
|
||||
fn record(app: &mut FilmcraftApp, ctx: &egui::Context) {
|
||||
let Some((cmd, add)) = app.shortcut_editor.recording.clone() else { return };
|
||||
let got = ctx.input_mut(|i| {
|
||||
let mut got = None;
|
||||
i.events.retain(|e| match e {
|
||||
egui::Event::Key { key, pressed: true, modifiers, .. } if got.is_none() => {
|
||||
egui::Event::Key { key, pressed: true, modifiers, .. } if got.is_none() && !is_modifier_key(*key) => {
|
||||
got = Some((*key, *modifiers));
|
||||
false
|
||||
}
|
||||
|
||||
@@ -512,3 +512,33 @@ fn waveform_peaks_do_not_survive_opening_another_project() {
|
||||
let now = d.harness.state().tl.peaks.lock().unwrap().get(&item).cloned();
|
||||
assert!(now.is_none_or(|p| !std::sync::Arc::ptr_eq(&p, &old)), "peaks from the previous project were reused");
|
||||
}
|
||||
|
||||
/// #164: on a real keyboard, pressing Alt+Shift+J sends each modifier key on its own first (egui
|
||||
/// reports modifier keys as key events). The shortcut recorder took the Alt press and refused it
|
||||
/// ("AltLeft can't be used as a shortcut"), so no chord with Shift, Ctrl or Alt could be recorded.
|
||||
#[test]
|
||||
fn recording_a_shortcut_skips_the_modifier_key_presses() {
|
||||
let mut d = Driver::demo();
|
||||
d.ok("ui.menu.invoke", json!({"id": "app.keyboardShortcuts"}));
|
||||
d.frames(2);
|
||||
d.ok("ui.click", json!({"id": "shortcuts.search"}));
|
||||
d.ok("ui.type", json!({"text": "add edit"}));
|
||||
d.frames(2);
|
||||
d.ok("ui.click", json!({"id": "shortcuts.add.sequence.addEdit"}));
|
||||
d.frames(1);
|
||||
// what a real keyboard sends for Alt+Shift+J: Alt, then Shift, then J
|
||||
let alt = egui::Modifiers { alt: true, ..Default::default() };
|
||||
let alt_shift = egui::Modifiers { alt: true, shift: true, ..Default::default() };
|
||||
let key = |key, modifiers| egui::Event::Key { key, physical_key: Some(key), pressed: true, repeat: false, modifiers };
|
||||
let events = &mut d.harness.input_mut().events;
|
||||
events.push(key(egui::Key::AltLeft, alt));
|
||||
events.push(key(egui::Key::ShiftLeft, alt_shift));
|
||||
events.push(key(egui::Key::J, alt_shift));
|
||||
d.frames(2);
|
||||
d.ok("ui.click", json!({"id": "shortcuts.ok"}));
|
||||
d.frames(2);
|
||||
// added next to Cmd+K (`shortcuts.get` lists every chord of the command)
|
||||
let got = d.ok("engine.execute", json!({"command": "shortcuts.get", "params": {"command": "sequence.addEdit"}})).to_string();
|
||||
assert!(got.contains("Alt+Shift+J"), "{got}");
|
||||
assert!(got.contains("Cmd+K"), "{got}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user