diff --git a/crates/ui-egui/src/shortcut_editor.rs b/crates/ui-egui/src/shortcut_editor.rs index 3b4671024..50026209e 100644 --- a/crates/ui-egui/src/shortcut_editor.rs +++ b/crates/ui-egui/src/shortcut_editor.rs @@ -274,19 +274,18 @@ pub fn chord_from_event(key: Key, m: Modifiers) -> Option { ui_mirror!(fn backtick_store() -> Option); -/// Notes what the backtick key (left of 1) typed in `events`: the one-character text whose key, a -/// press of it without modifiers, comes right before it (egui-winit) or, when no press comes -/// before, right after it (eframe's web build with no text field focused). egui reports that key -/// as [`Key::Backtick`] on layouts where it types a character egui has no key for, such as `"` on -/// Turkish Q. +/// Notes what the backtick key (left of 1) typed in `events`: the last one-character text that +/// [`crate::shortcuts::typing_presses`] pairs with a press of that key without modifiers, whether +/// the press comes before its text (egui-winit) or after it (eframe's web build with no text field +/// focused). egui reports that key as [`Key::Backtick`] on layouts where it types a character egui +/// has no key for, such as `"` on Turkish Q. pub(crate) fn note_backtick_text(events: &[egui::Event]) { - let is_press = |e: Option<&egui::Event>| matches!(e, Some(egui::Event::Key { pressed: true, .. })); - let is_backtick = - |e: Option<&egui::Event>| matches!(e, Some(egui::Event::Key { key: Key::Backtick, pressed: true, modifiers, .. }) if modifiers.is_none()); - let typed = events.iter().enumerate().rev().find_map(|(j, e)| match e { - egui::Event::Text(t) if t.chars().count() == 1 && !t.chars().any(char::is_whitespace) => { - let before = j.checked_sub(1).and_then(|j| events.get(j)); - (is_backtick(before) || (!is_press(before) && is_backtick(events.get(j + 1)))).then(|| t.clone()) + let typed_by = crate::shortcuts::typing_presses(events); + let typed = events.iter().zip(typed_by).rev().find_map(|(e, by)| match (e, by.and_then(|k| events.get(k))) { + (egui::Event::Text(t), Some(egui::Event::Key { key: Key::Backtick, modifiers, .. })) + if modifiers.is_none() && t.chars().count() == 1 && !t.chars().any(char::is_whitespace) => + { + Some(t.clone()) } _ => None, }); @@ -1322,6 +1321,13 @@ mod tests { assert_eq!(backtick_text().as_deref(), Some("\"")); note_backtick_text(&[text("`"), press(Key::Backtick)]); assert_eq!(backtick_text(), None); + // Several keys typed in one frame on the web: each text goes with the press after it. + let web = [text("a"), press(Key::A), text("\""), press(Key::Backtick), text("b"), press(Key::B)]; + note_backtick_text(&web); + assert_eq!(backtick_text().as_deref(), Some("\"")); + note_backtick_text(&[text("`"), press(Key::Backtick)]); + note_backtick_text(&[press(Key::A), text("a"), press(Key::Backtick), text("\""), press(Key::B), text("b")]); + assert_eq!(backtick_text().as_deref(), Some("\"")); // Only the unshifted key types that character. assert_eq!(on_layout("Shift+~".into(), Some("\"")), "Shift+~"); assert_eq!(on_layout("Cmd+~".into(), Some("\"")), "Cmd+\""); diff --git a/crates/ui-egui/src/shortcuts.rs b/crates/ui-egui/src/shortcuts.rs index b551b758d..f8aaf2dd2 100644 --- a/crates/ui-egui/src/shortcuts.rs +++ b/crates/ui-egui/src/shortcuts.rs @@ -929,7 +929,7 @@ fn latin(text: &str, physical: Option) -> String { /// 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> { +pub(crate) fn typing_presses(events: &[egui::Event]) -> Vec> { 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