From 135981286ffb157da6ab185cbcd22dbc7e09e80d Mon Sep 17 00:00:00 2001 From: aiongg Date: Fri, 23 Oct 2026 08:03:27 +0000 Subject: [PATCH] keymap: a layout switch forgets what letter positions typed, and Layout::forget_letters does so on request --- crates/keymap/README.md | 4 +++- crates/keymap/src/layout.rs | 17 +++++++++++++++-- crates/keymap/tests/layout.rs | 23 +++++++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/crates/keymap/README.md b/crates/keymap/README.md index daa389c8..8982dce8 100644 --- a/crates/keymap/README.md +++ b/crates/keymap/README.md @@ -99,7 +99,9 @@ macOS it folds into `cmd` (`Chord::effective`). 5. With Shift held on a Latin layout, a press that egui reports as the letter at its own position, because the character typed has no key (Dvorak's ⇧, types `<`), runs nothing when that position has been seen typing something other than its letter: once a `,` has been - typed, Ctrl+Shift+, never runs Ctrl+Shift+W. + typed, Ctrl+Shift+, never runs Ctrl+Shift+W. A change in the reported Latin status clears + what the positions typed; an app that learns of a layout switch itself calls + `Layout::forget_letters`. 6. Labels show letters and digits by name, and a punctuation key as the character the layout types at its position when that character has no key of its own (`Ctrl+Ü` for Ctrl+[ on German). diff --git a/crates/keymap/src/layout.rs b/crates/keymap/src/layout.rs index 391ee5f4..ba8c9eee 100644 --- a/crates/keymap/src/layout.rs +++ b/crates/keymap/src/layout.rs @@ -22,7 +22,8 @@ //! that has no key (Dvorak's ⇧, types `<`) as the key's position, W. With Shift held on a //! Latin layout, such a press at a letter position that has been seen typing something other //! than its letter resolves to nothing, so Ctrl+Shift+, never runs Ctrl+Shift+W once a `,` -//! has been typed. +//! has been typed. A change in the reported Latin status, or [`Layout::forget_letters`], +//! clears what the positions typed. //! 6. **Labels** show letters and digits by name, and a punctuation key as the character the //! layout types at its position when that character has no key of its own: Ctrl+[ is `Ctrl+Ü` //! on German and `Ctrl+Х` on Russian. @@ -143,11 +144,23 @@ impl Layout { } /// Whether the layout is Latin, as the platform reports it; `None` when it cannot tell, so - /// that typing decides. Call again when the user switches layouts. + /// that typing decides. Call again when the user switches layouts. A report that differs + /// from the last one means another layout, so what letter positions typed is forgotten + /// ([`forget_letters`](Self::forget_letters)). pub fn set_latin(&mut self, latin: Option) { + if latin != self.reported_latin { + self.forget_letters(); + } self.reported_latin = latin; } + /// Forgets what each letter position has been seen typing, for an app that learns of a + /// layout switch the platform does not report: until typing shows the new layout, Shift + /// presses at letter positions resolve as their letters again (rule 5). + pub fn forget_letters(&mut self) { + self.letters.clear(); + } + /// Whether letters are read as typed (rule 1): what the platform reported, else what a letter /// key typed last, else true. pub fn is_latin(&self) -> bool { diff --git a/crates/keymap/tests/layout.rs b/crates/keymap/tests/layout.rs index a0376476..46ff8876 100644 --- a/crates/keymap/tests/layout.rs +++ b/crates/keymap/tests/layout.rs @@ -182,6 +182,29 @@ fn dvorak_shift_punctuation_never_runs_its_position() { assert_eq!(qwerty.resolve(&at("Cmd+Shift+E", Key::E)), Some(chord("Cmd+Shift+E"))); } +#[test] +fn a_layout_switch_forgets_what_letter_positions_typed() { + // On Dvorak the W position types `,`; after a switch to QWERTY, Ctrl+Shift+W is W again. + let ctrl_shift_w = at("Cmd+Shift+W", Key::W); + let mut layout = Layout::new().with_latin(true); + layout.observe(Key::W, Mods::NONE, ","); + assert_eq!(layout.resolve(&ctrl_shift_w), None); + layout.set_latin(Some(true)); + assert_eq!(layout.resolve(&ctrl_shift_w), None, "the same report is the same layout"); + + // Dvorak, then Russian, then QWERTY, as the platform reports them. + layout.set_latin(Some(false)); + layout.set_latin(Some(true)); + assert_eq!(layout.resolve(&ctrl_shift_w), Some(chord("Cmd+Shift+W"))); + + // An app that learns of a switch itself. + let mut unreported = Layout::new(); + unreported.observe(Key::W, Mods::NONE, ","); + assert_eq!(unreported.resolve(&ctrl_shift_w), None); + unreported.forget_letters(); + assert_eq!(unreported.resolve(&ctrl_shift_w), Some(chord("Cmd+Shift+W"))); +} + #[test] fn labels_show_the_layouts_own_punctuation() { let mut german = Layout::new().with_char(Key::OpenBracket, 'ü').with_char(Key::Slash, '-').with_char(Key::CloseBracket, '+');