Skip to content

Commit cab9d5a

Browse files
authored
feat!(menu): support physical key matches for non-Latin keyboard layouts
1 parent 2aa9133 commit cab9d5a

1 file changed

Lines changed: 78 additions & 8 deletions

File tree

src/widget/menu/key_bind.rs

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use iced_core::keyboard::key::{Code, Physical};
12
use iced_core::keyboard::{Key, Modifiers};
23
use std::fmt;
34

@@ -27,28 +28,97 @@ pub struct KeyBind {
2728
}
2829

2930
impl KeyBind {
30-
/// Checks if the given key and modifiers match the `KeyBind`.
31+
/// Checks if the given key and modifiers match the `KeyBind`, with an
32+
/// optional fallback to the physical key position for non-Latin keyboard
33+
/// layouts.
3134
///
3235
/// # Arguments
3336
///
3437
/// * `modifiers` - A `Modifiers` instance representing the current active modifiers.
3538
/// * `key` - A reference to the `Key` that is being checked.
39+
/// * `physical_key` - An optional reference to the physical key position,
40+
/// used as a fallback when the logical `key` does not match (e.g. on
41+
/// Cyrillic or other non-Latin layouts). Can be `None` for keys where
42+
/// the physical position is not relevant (e.g. `Key::Named`).
3643
///
3744
/// # Returns
3845
///
3946
/// * `bool` - `true` if the key and modifiers match the `KeyBind`, `false` otherwise.
40-
pub fn matches(&self, modifiers: Modifiers, key: &Key) -> bool {
41-
let key_eq = match (key, &self.key) {
42-
// CapsLock and Shift change the case of Key::Character, so we compare these in a case insensitive way
43-
(Key::Character(a), Key::Character(b)) => a.eq_ignore_ascii_case(b),
44-
(a, b) => a.eq(b),
45-
};
47+
pub fn matches(&self, modifiers: Modifiers, key: &Key, physical_key: Option<&Physical>) -> bool {
48+
let key_eq = self.key_eq(key)
49+
|| physical_key
50+
.and_then(physical_key_to_latin)
51+
.is_some_and(|latin| self.key_eq(&latin));
4652
key_eq
4753
&& modifiers.logo() == self.modifiers.contains(&Modifier::Super)
4854
&& modifiers.control() == self.modifiers.contains(&Modifier::Ctrl)
4955
&& modifiers.alt() == self.modifiers.contains(&Modifier::Alt)
5056
&& modifiers.shift() == self.modifiers.contains(&Modifier::Shift)
5157
}
58+
59+
fn key_eq(&self, key: &Key) -> bool {
60+
match (key, &self.key) {
61+
// CapsLock and Shift change the case of Key::Character, so we compare these in a case insensitive way
62+
(Key::Character(a), Key::Character(b)) => a.eq_ignore_ascii_case(b),
63+
(a, b) => a.eq(b),
64+
}
65+
}
66+
}
67+
68+
/// Converts a physical key code to the corresponding US-layout Latin `Key`.
69+
///
70+
/// This mapping is intentionally limited to keys that may produce different
71+
/// characters on non-Latin keyboard layouts (letters and punctuation). Keys
72+
/// like digits are not included because they remain the same across layouts.
73+
///
74+
/// Only used as a fallback when the primary key comparison in
75+
/// [`KeyBind::matches`] does not match.
76+
fn physical_key_to_latin(physical_key: &Physical) -> Option<Key> {
77+
let code = match physical_key {
78+
Physical::Code(code) => code,
79+
Physical::Unidentified(_) => return None,
80+
};
81+
let ch = match code {
82+
Code::KeyA => "a",
83+
Code::KeyB => "b",
84+
Code::KeyC => "c",
85+
Code::KeyD => "d",
86+
Code::KeyE => "e",
87+
Code::KeyF => "f",
88+
Code::KeyG => "g",
89+
Code::KeyH => "h",
90+
Code::KeyI => "i",
91+
Code::KeyJ => "j",
92+
Code::KeyK => "k",
93+
Code::KeyL => "l",
94+
Code::KeyM => "m",
95+
Code::KeyN => "n",
96+
Code::KeyO => "o",
97+
Code::KeyP => "p",
98+
Code::KeyQ => "q",
99+
Code::KeyR => "r",
100+
Code::KeyS => "s",
101+
Code::KeyT => "t",
102+
Code::KeyU => "u",
103+
Code::KeyV => "v",
104+
Code::KeyW => "w",
105+
Code::KeyX => "x",
106+
Code::KeyY => "y",
107+
Code::KeyZ => "z",
108+
Code::Minus => "-",
109+
Code::Equal => "=",
110+
Code::BracketLeft => "[",
111+
Code::BracketRight => "]",
112+
Code::Backslash => "\\",
113+
Code::Semicolon => ";",
114+
Code::Quote => "'",
115+
Code::Backquote => "`",
116+
Code::Comma => ",",
117+
Code::Period => ".",
118+
Code::Slash => "/",
119+
_ => return None,
120+
};
121+
Some(Key::Character(ch.into()))
52122
}
53123

54124
impl fmt::Display for KeyBind {
@@ -62,4 +132,4 @@ impl fmt::Display for KeyBind {
62132
other => write!(f, "{:?}", other),
63133
}
64134
}
65-
}
135+
}

0 commit comments

Comments
 (0)