mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-03 01:00:48 +00:00
keymap: Shift presses egui reports as their position run nothing on moved layouts
Dvorak's ⇧, types <, which egui has no key for, so egui reports the press as W and Ctrl+Shift+, ran Ctrl+Shift+W. Once typing shows the layout's letters are not on QWERTY positions, such a press resolves to nothing unless the position is known to type its own letter.
This commit is contained in:
1 parent
86248331bb
commit
d0acf36588
3 files changed
+62
-3
No files matched your search
@@ -93,7 +93,10 @@ macOS it folds into `cmd` (`Chord::effective`).
|
||||
3. Punctuation: the character typed, without Shift when the layout needs Shift for it: German
|
||||
`/` is ⇧7, so ⇧⌘7 runs ⌘/.
|
||||
4. Whether a layout is Latin is decided for the layout, not per key.
|
||||
5. Labels show letters and digits by name, and a punctuation key as the character the layout
|
||||
5. With Shift held on a Latin layout whose letters are known to be moved (Dvorak), a press that
|
||||
egui reports as the letter at its own position, because the character typed has no key
|
||||
(Dvorak's ⇧, types `<`), runs nothing: Ctrl+Shift+, never runs Ctrl+Shift+W.
|
||||
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).
|
||||
|
||||
|
||||
@@ -16,7 +16,12 @@
|
||||
//! each other so zoom keys work with and without Shift.
|
||||
//! 4. **Whether a layout is Latin** is decided for the whole layout ([`Layout::is_latin`]), never
|
||||
//! per key: Dvorak's `'` key on the Q position stays `'`.
|
||||
//! 5. **Labels** show letters and digits by name, and a punctuation key as the character the
|
||||
//! 5. **No position fallback for unnamed characters on moved layouts:** egui reports a character
|
||||
//! that has no key (Dvorak's ⇧, types `<`) as the key's position, W. With Shift held on a
|
||||
//! Latin layout whose letters are known to sit elsewhere than on QWERTY, such a press at a
|
||||
//! letter position resolves to nothing unless that position is known to type its own letter,
|
||||
//! so Ctrl+Shift+, never runs Ctrl+Shift+W.
|
||||
//! 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.
|
||||
//!
|
||||
@@ -92,6 +97,8 @@ pub struct Layout {
|
||||
seen_latin: Option<bool>,
|
||||
/// The character typed without modifiers at each punctuation position.
|
||||
chars: BTreeMap<Key, char>,
|
||||
/// The character, lower case, typed without Cmd, Ctrl or Alt at each letter position.
|
||||
letters: BTreeMap<Key, char>,
|
||||
}
|
||||
|
||||
/// US base keys and the key egui reports for them with Shift held.
|
||||
@@ -175,6 +182,9 @@ impl Layout {
|
||||
if c.is_alphabetic() {
|
||||
self.seen_latin = Some(is_latin_letter(c));
|
||||
}
|
||||
if !c.is_control() {
|
||||
self.letters.insert(physical, c.to_lowercase().next().unwrap_or(c));
|
||||
}
|
||||
} else if !mods.shift && !c.is_control() {
|
||||
self.set_char(physical, c);
|
||||
}
|
||||
@@ -192,11 +202,29 @@ impl Layout {
|
||||
{
|
||||
return Some(Chord::new(held, position));
|
||||
}
|
||||
if held.shift
|
||||
&& let Some(position) = input.physical.filter(|k| k.is_letter())
|
||||
&& input.typed.is_none_or(|typed| typed == position)
|
||||
&& !self.types_own_letter(position)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let key = input.typed.or(input.physical)?;
|
||||
let drop_shift = held.shift && key.is_symbol() && input.needs_shift(key);
|
||||
Some(Chord::new(if drop_shift { without_shift(held) } else { held }, key))
|
||||
}
|
||||
|
||||
/// Rule 5: whether a press reported as the letter at its own position can be that letter.
|
||||
/// Yes when the position is known to type it, or nothing is known to sit elsewhere than on
|
||||
/// QWERTY; otherwise the press likely typed a character egui has no key for.
|
||||
fn types_own_letter(&self, position: Key) -> bool {
|
||||
let own = |key: Key, c: char| Key::from_char(c) == Some(key);
|
||||
match self.letters.get(&position) {
|
||||
Some(&c) => own(position, c),
|
||||
None => self.letters.iter().all(|(&key, &c)| own(key, c)),
|
||||
}
|
||||
}
|
||||
|
||||
/// The chords a press can run, most specific first: [`resolve`](Self::resolve)'s chord, the
|
||||
/// same press written as Shift with a US base key (`Shift+;` for `:`), `=` for `+` and `+`
|
||||
/// for Shift+`=`, and on the digit row the position's digit.
|
||||
@@ -235,7 +263,7 @@ impl Layout {
|
||||
self.candidates(input).iter().any(|c| c.effective(platform) == want)
|
||||
}
|
||||
|
||||
/// Rule 5: the layout's character for the punctuation key `key`, when pressing that position
|
||||
/// Rule 6: the layout's character for the punctuation key `key`, when pressing that position
|
||||
/// means `key` and the layout types `key`'s own character nowhere else that is known.
|
||||
pub(crate) fn key_label(&self, key: Key) -> Option<String> {
|
||||
let typed = self.char_at(key)?;
|
||||
|
||||
@@ -136,6 +136,34 @@ fn typing_decides_whether_the_layout_is_latin() {
|
||||
assert!(reported.is_latin());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dvorak_shift_punctuation_never_runs_its_position() {
|
||||
// Dvorak's , key sits on W and types < with Shift, which egui has no key for, so egui
|
||||
// reports the press as W.
|
||||
let ctrl_shift_lt = at("Cmd+Shift+W", Key::W);
|
||||
let mut dvorak = Layout::new();
|
||||
assert_eq!(dvorak.resolve(&ctrl_shift_lt), Some(chord("Cmd+Shift+W")), "nothing known yet");
|
||||
|
||||
dvorak.observe(Key::S, Mods::NONE, "o");
|
||||
assert_eq!(dvorak.resolve(&ctrl_shift_lt), None, "letters are known to be moved");
|
||||
assert!(!dvorak.matches(&chord("Ctrl+Shift+W"), &ctrl_shift_lt, Platform::Windows));
|
||||
dvorak.observe(Key::W, Mods::NONE, ",");
|
||||
assert_eq!(dvorak.resolve(&ctrl_shift_lt), None);
|
||||
// Without Shift, and for letters typed where they sit (Dvorak's A) or elsewhere, presses
|
||||
// resolve as before.
|
||||
assert_eq!(dvorak.resolve(&at("Cmd+W", Key::W)), Some(chord("Cmd+W")));
|
||||
dvorak.observe(Key::A, Mods::NONE, "a");
|
||||
assert_eq!(dvorak.resolve(&at("Cmd+Shift+A", Key::A)), Some(chord("Cmd+Shift+A")));
|
||||
assert_eq!(dvorak.resolve(&at("Cmd+Shift+O", Key::S)), Some(chord("Cmd+Shift+O")));
|
||||
|
||||
// QWERTY: a position typing its own letter keeps the position fallback.
|
||||
let mut qwerty = Layout::new();
|
||||
qwerty.observe(Key::S, Mods::NONE, "s");
|
||||
qwerty.observe(Key::W, Mods::SHIFT, "W");
|
||||
assert_eq!(qwerty.resolve(&ctrl_shift_lt), Some(chord("Cmd+Shift+W")));
|
||||
assert_eq!(qwerty.resolve(&at("Cmd+Shift+E", Key::E)), Some(chord("Cmd+Shift+E")));
|
||||
}
|
||||
|
||||
#[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, '+');
|
||||
|
||||
Reference in new issue
Block a user