diff --git a/crates/ui-egui/src/shortcuts.rs b/crates/ui-egui/src/shortcuts.rs index 086b32bd5..39fbc2a2c 100644 --- a/crates/ui-egui/src/shortcuts.rs +++ b/crates/ui-egui/src/shortcuts.rs @@ -848,9 +848,24 @@ 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. +/// (eframe's web build with no text field focused). 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_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. + let ends_with_press = |mut j: usize| loop { + if !is_press(j + 1) { + break false; + } + if !is_text(j + 2) { + break true; + } + j += 2; + }; let mut taken = None; events .iter() @@ -860,7 +875,7 @@ fn typing_presses(events: &[egui::Event]) -> Vec> { return None; } let before = j.checked_sub(1).filter(|&k| is_press(k) && taken != Some(k)); - let by = before.or_else(|| Some(j + 1).filter(|&k| is_press(k))); + let by = before.or_else(|| ends_with_press(j).then_some(j + 1)); taken = by.or(taken); by }) diff --git a/crates/ui-egui/src/shortcuts/tests.rs b/crates/ui-egui/src/shortcuts/tests.rs index 49c41536e..e07c31a74 100644 --- a/crates/ui-egui/src/shortcuts/tests.rs +++ b/crates/ui-egui/src/shortcuts/tests.rs @@ -1486,6 +1486,11 @@ fn one_key_shortcuts_use_the_key_of_the_same_keystroke_in_either_order() { app.select_tool("pen"); frame(&mut app, vec![press(Key::P), text("з"), press(Key::V), text("м")]); assert_eq!(app.session.tool_id(), "selection", "key before its text"); + // A text with no key press of its own right before a desktop keystroke leaves that press to + // the keystroke's text. + 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"); let names = |events: &[egui::Event]| -> Vec { shortcut_presses(events, Modifiers::NONE).into_iter().filter_map(|(n, _)| n.into_iter().next()).collect() };