improved modifier matching

This commit is contained in:
Robin Appelman 2020-04-10 22:47:10 +02:00
commit f7c7953464
4 changed files with 106 additions and 42 deletions

19
Cargo.lock generated
View file

@ -358,6 +358,7 @@ dependencies = [
"main_error", "main_error",
"num_enum", "num_enum",
"parse-display", "parse-display",
"test-case",
] ]
[[package]] [[package]]
@ -371,6 +372,18 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "test-case"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "199464148b42bcf3da8b2a56f6ee87ca68f47402496d1268849291ec9fb463c8"
dependencies = [
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]] [[package]]
name = "thread_local" name = "thread_local"
version = "1.0.1" version = "1.0.1"
@ -395,6 +408,12 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
[[package]]
name = "version_check"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
[[package]] [[package]]
name = "void" name = "void"
version = "1.0.2" version = "1.0.2"

View file

@ -10,3 +10,6 @@ main_error = "0.1.0"
num_enum = "0.4.3" num_enum = "0.4.3"
dbus = "0.8.2" dbus = "0.8.2"
parse-display = "0.1.1" parse-display = "0.1.1"
[dev-dependencies]
test-case = "1.0.0"

View file

@ -46,7 +46,7 @@ pub enum Key {
KeySemicolon = 39, KeySemicolon = 39,
KeyApostrophe = 40, KeyApostrophe = 40,
KeyGrave = 41, KeyGrave = 41,
KeyLeftshift = 42, KeyLeftShift = 42,
KeyBackslash = 43, KeyBackslash = 43,
KeyZ = 44, KeyZ = 44,
KeyX = 45, KeyX = 45,
@ -58,7 +58,7 @@ pub enum Key {
KeyComma = 51, KeyComma = 51,
KeyDot = 52, KeyDot = 52,
KeySlash = 53, KeySlash = 53,
KeyRightshift = 54, KeyRightShift = 54,
KeyKpAsterisk = 55, KeyKpAsterisk = 55,
KeyLeftAlt = 56, KeyLeftAlt = 56,
KeySpace = 57, KeySpace = 57,

View file

@ -27,20 +27,34 @@ pub enum Modifier {
} }
impl Modifier { impl Modifier {
pub fn is_modifier(&self, key: Key) -> bool { pub fn as_mask(&self) -> u8 {
match self { match self {
Modifier::Alt => key == Key::KeyLeftAlt || key == Key::KeyRightAlt, Modifier::Alt => 0b00000011,
Modifier::LeftAlt => key == Key::KeyLeftAlt, Modifier::LeftAlt => 0b00000001,
Modifier::RightAlt => key == Key::KeyRightAlt, Modifier::RightAlt => 0b00000010,
Modifier::Ctrl => key == Key::KeyLeftCtrl || key == Key::KeyRightCtrl, Modifier::Ctrl => 0b00001100,
Modifier::LeftCtrl => key == Key::KeyLeftCtrl, Modifier::LeftCtrl => 0b00000100,
Modifier::RightCtrl => key == Key::KeyRightCtrl, Modifier::RightCtrl => 0b00001000,
Modifier::Meta => key == Key::KeyLeftMeta || key == Key::KeyRightMeta, Modifier::Meta => 0b00110000,
Modifier::LeftMeta => key == Key::KeyLeftMeta, Modifier::LeftMeta => 0b00010000,
Modifier::RightMeta => key == Key::KeyRightMeta, Modifier::RightMeta => 0b00100000,
Modifier::Shift => key == Key::KeyLeftshift || key == Key::KeyRightshift, Modifier::Shift => 0b11000000,
Modifier::LeftShift => key == Key::KeyLeftshift, Modifier::LeftShift => 0b01000000,
Modifier::RightShift => key == Key::KeyRightshift, Modifier::RightShift => 0b10000000,
}
}
pub fn mask_from_key(key: Key) -> u8 {
match key {
Key::KeyLeftAlt => 0b00000001,
Key::KeyRightAlt => 0b00000010,
Key::KeyLeftCtrl => 0b00000100,
Key::KeyRightCtrl => 0b00001000,
Key::KeyLeftMeta => 0b00010000,
Key::KeyRightMeta => 0b00100000,
Key::KeyLeftShift => 0b01000000,
Key::KeyRightShift => 0b10000000,
_ => 0,
} }
} }
} }
@ -48,6 +62,14 @@ impl Modifier {
#[derive(Clone, Debug, Hash, PartialEq, Eq)] #[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct ModifierList(Vec<Modifier>); pub struct ModifierList(Vec<Modifier>);
impl ModifierList {
pub fn as_mask(&self) -> u8 {
self.0
.iter()
.fold(0, |mask, modifier| mask | modifier.as_mask())
}
}
impl Display for ModifierList { impl Display for ModifierList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for modifier in self.0.iter() { for modifier in self.0.iter() {
@ -83,25 +105,18 @@ pub struct Shortcut {
key: Key, key: Key,
} }
#[test] #[cfg(test)]
fn test_shortcut_format() { mod tests {
assert_eq!( use crate::keyboard::{Key, Modifier, Shortcut};
"<Ctrl>-KeyP", use test_case::test_case;
format!("{}", Shortcut::new(vec![Modifier::Ctrl], Key::KeyP))
);
assert_eq!( #[test_case("<Ctrl>-KeyP", Shortcut::new(vec ! [Modifier::Ctrl], Key::KeyP))]
"<LeftCtrl><LeftAlt>-KeyLeft", #[test_case("<LeftCtrl><LeftAlt>-KeyLeft", Shortcut::new(vec ! [Modifier::LeftCtrl, Modifier::LeftAlt], Key::KeyLeft))]
format!( fn shortcut_parse_display_test(s: &str, shortcut: Shortcut) {
"{}", assert_eq!(s, format!("{}", shortcut));
Shortcut::new(vec![Modifier::LeftCtrl, Modifier::LeftAlt], Key::KeyLeft)
)
);
assert_eq!( assert_eq!(shortcut, s.parse().unwrap());
Shortcut::from_str("<LeftCtrl><LeftAlt>-KeyLeft").unwrap(), }
Shortcut::new(vec![Modifier::LeftCtrl, Modifier::LeftAlt], Key::KeyLeft)
);
} }
impl Shortcut { impl Shortcut {
@ -115,15 +130,42 @@ impl Shortcut {
impl Shortcut { impl Shortcut {
pub fn is_triggered(&self, active_keys: &HashSet<Key>) -> bool { pub fn is_triggered(&self, active_keys: &HashSet<Key>) -> bool {
for modifier in &self.modifiers.0 { let desired_mask = self.modifiers.as_mask();
if active_keys.iter().any(|key| modifier.is_modifier(*key)) { let pressed_mask = active_keys
break; .iter()
.fold(0, |mask, key| mask | Modifier::mask_from_key(*key));
let desired_presses = desired_mask & pressed_mask;
let modifiers_match = (desired_presses == pressed_mask)
&& (desired_presses.count_ones() == self.modifiers.0.len() as u32);
modifiers_match && active_keys.contains(&self.key)
}
} }
return false; #[cfg(test)]
} mod triggered_tests {
use crate::keyboard::{Key, Modifier, Shortcut};
use std::collections::HashSet;
use test_case::test_case;
active_keys.contains(&self.key) #[test_case("<Ctrl>-KeyP", & [] => false)]
#[test_case("<Ctrl>-KeyP", & [Key::KeyLeftCtrl, Key::KeyP] => true)]
#[test_case("<Ctrl>-KeyP", & [Key::KeyRightCtrl, Key::KeyP] => true)]
#[test_case("<LeftCtrl>-KeyP", & [Key::KeyLeftCtrl, Key::KeyP] => true)]
#[test_case("<LeftCtrl>-KeyP", & [Key::KeyRightCtrl, Key::KeyP] => false)]
#[test_case("<Ctrl>-KeyP", & [Key::KeyLeftCtrl, Key::KeyLeftAlt, Key::KeyP] => false)]
#[test_case("<LeftCtrl><LeftAlt>-KeyLeft", & [] => false)]
#[test_case("<LeftCtrl><LeftAlt>-KeyLeft", & [Key::KeyLeft] => false)]
#[test_case("<LeftCtrl><LeftAlt>-KeyLeft", & [Key::KeyLeftCtrl, Key::KeyLeft] => false)]
#[test_case("<LeftCtrl><LeftAlt>-KeyLeft", & [Key::KeyLeftCtrl, Key::KeyLeftAlt] => false)]
#[test_case("<LeftCtrl><LeftAlt>-KeyLeft", & [Key::KeyLeftCtrl, Key::KeyLeftAlt, Key::KeyRight] => false)]
#[test_case("<LeftCtrl><LeftAlt>-KeyLeft", & [Key::KeyLeftCtrl, Key::KeyLeftAlt, Key::KeyLeft] => true)]
#[test_case("<LeftCtrl><LeftAlt>-KeyLeft", & [Key::KeyLeftCtrl, Key::KeyRightAlt, Key::KeyLeft] => false)]
#[test_case("<Ctrl><Alt>-KeyLeft", & [Key::KeyLeftCtrl, Key::KeyRightAlt, Key::KeyLeft] => true)]
fn shortcut_triggered(s: &str, keys: &[Key]) -> bool {
let shortcut: Shortcut = s.parse().unwrap();
shortcut.is_triggered(&keys.into_iter().copied().collect())
} }
} }