From d3ea958f8f8240fb42f5d3030809d7328f9b4a6c Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sat, 31 Oct 2026 02:47:09 -0700 Subject: [PATCH] Shortcuts: a modifier key press is never the key that typed a text In the web build, pressing Shift and then a letter in one frame sends the Shift press, the letter's text and then the letter's key press. The text paired with the Shift press as the press before it, so a key-position shortcut looked up Shift instead of the letter, and a held Shift's repeated presses dropped the text as a repeat. Modifier key presses now never pair with a text. --- crates/ui-egui/src/shortcuts.rs | 5 +++-- crates/ui-egui/src/shortcuts/tests.rs | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/src/shortcuts.rs b/crates/ui-egui/src/shortcuts.rs index 39fbc2a2c..435758b85 100644 --- a/crates/ui-egui/src/shortcuts.rs +++ b/crates/ui-egui/src/shortcuts.rs @@ -848,12 +848,13 @@ fn latin(text: &str, physical: Option) -> String { /// For each event, the index of the key press that typed it when it is text: the press right /// before it (egui-winit) unless an earlier text took that press, or else the press right after it -/// (eframe's web build with no text field focused). Each press types at most one text. A text +/// (eframe's web build with no text field focused). Modifier key presses type no text. Each press +/// types at most one text. A text /// takes the press after it only when every later text in the same run of alternating texts and /// presses has a press after it too; otherwise the text after that press takes it as the press /// before it, and this text has none. fn typing_presses(events: &[egui::Event]) -> Vec> { - let is_press = |k: usize| matches!(events.get(k), Some(egui::Event::Key { pressed: true, .. })); + let is_press = |k: usize| matches!(events.get(k), Some(egui::Event::Key { key, pressed: true, .. }) if !is_modifier(*key)); let is_text = |k: usize| matches!(events.get(k), Some(egui::Event::Text(_))); // Whether the run of alternating texts and presses that starts with the text at `j` ends // with a press. diff --git a/crates/ui-egui/src/shortcuts/tests.rs b/crates/ui-egui/src/shortcuts/tests.rs index e07c31a74..6bce0cc6f 100644 --- a/crates/ui-egui/src/shortcuts/tests.rs +++ b/crates/ui-egui/src/shortcuts/tests.rs @@ -1491,6 +1491,8 @@ fn one_key_shortcuts_use_the_key_of_the_same_keystroke_in_either_order() { assert_eq!(typing_presses(&[text("p"), press(Key::V), text("м")]), [None, None, Some(1)]); frame(&mut app, vec![text("p"), press(Key::V), text("м")]); assert_eq!(app.session.tool_id(), "selection", "the text after the key uses it"); + // A modifier key press does not type text, so the web text after Shift uses the key after it. + assert_eq!(typing_presses(&[press(Key::ShiftLeft), text("З"), press(Key::P)]), [None, Some(2), None]); let names = |events: &[egui::Event]| -> Vec { shortcut_presses(events, Modifiers::NONE).into_iter().filter_map(|(n, _)| n.into_iter().next()).collect() };