#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Key(pub u16);
impl Key {
pub const BACKSPACE: Key = Key(0x08);
pub const TAB: Key = Key(0x09);
pub const ENTER: Key = Key(0x0D);
pub const ESCAPE: Key = Key(0x1B);
pub const SPACE: Key = Key(0x20);
pub const PAGE_UP: Key = Key(0x21);
pub const PAGE_DOWN: Key = Key(0x22);
pub const END: Key = Key(0x23);
pub const HOME: Key = Key(0x24);
pub const LEFT: Key = Key(0x25);
pub const UP: Key = Key(0x26);
pub const RIGHT: Key = Key(0x27);
pub const DOWN: Key = Key(0x28);
pub const INSERT: Key = Key(0x2D);
pub const DELETE: Key = Key(0x2E);
pub const fn char(c: char) -> Key {
match c {
'a'..='z' => Key(c as u16 - 0x20),
'A'..='Z' | '0'..='9' => Key(c as u16),
_ => panic!("Key::char takes an ASCII letter or digit"),
}
}
pub const fn function(n: u8) -> Key {
assert!(n >= 1 && n <= 24, "Key::function takes 1 to 24");
Key(0x6F + n as u16)
}
pub(super) fn modifier(self) -> bool {
matches!(self.0, 0x10..=0x12 | 0x5B | 0x5C | 0xA0..=0xA5)
}
pub(super) fn types_no_text(self) -> bool {
self == Key::ESCAPE
|| self == Key::PAGE_UP
|| self == Key::PAGE_DOWN
|| (0x70..=0x87).contains(&self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct KeyEvent {
pub key: Key,
pub ctrl: bool,
pub shift: bool,
pub alt: bool,
pub repeat: bool,
}
impl KeyEvent {
pub fn is_ctrl(&self, key: Key) -> bool {
self.key == key && self.ctrl && !self.alt && !self.shift
}
pub fn is_plain(&self, key: Key) -> bool {
self.key == key && !self.ctrl && !self.alt && !self.shift
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn letters_and_digits_are_their_codes() {
assert_eq!(Key::char('s'), Key(0x53));
assert_eq!(Key::char('S'), Key(0x53));
assert_eq!(Key::char('7'), Key(0x37));
assert_eq!(Key::function(1), Key(0x70));
assert_eq!(Key::function(24), Key(0x87));
}
#[test]
fn shortcuts_match_their_modifiers() {
let save = KeyEvent {
key: Key::char('s'),
ctrl: true,
shift: false,
alt: false,
repeat: false,
};
assert!(save.is_ctrl(Key::char('S')));
assert!(!save.is_plain(Key::char('S')));
assert!(Key(0x10).modifier());
assert!(Key::function(5).types_no_text());
assert!(!Key::char('a').types_no_text());
}
}