use std::ops::{BitAnd, BitOr, BitOrAssign};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Key {
Char(char),
F(u8),
Backspace,
Enter,
Left,
Right,
Up,
Down,
Home,
End,
PageUp,
PageDown,
Tab,
Delete,
Insert,
Esc,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct KeyEvent {
pub code: Key,
pub modifiers: Modifiers,
pub kind: KeyEventKind,
pub raw_char: Option<char>,
}
impl KeyEvent {
pub fn new(key: Key) -> Self {
match key {
Key::Char(c) if c.is_ascii_uppercase() => Self {
code: Key::Char(c.to_ascii_lowercase()),
modifiers: Modifiers::SHIFT,
kind: KeyEventKind::Press,
raw_char: Some(c),
},
Key::Char(c) => Self {
code: Key::Char(c),
modifiers: Modifiers::NONE,
kind: KeyEventKind::Press,
raw_char: Some(c),
},
_ => Self {
code: key,
modifiers: Modifiers::NONE,
kind: KeyEventKind::Press,
raw_char: None,
},
}
}
pub fn char(c: char) -> Self {
if c.is_ascii_uppercase() {
Self {
code: Key::Char(c.to_ascii_lowercase()),
modifiers: Modifiers::SHIFT,
kind: KeyEventKind::Press,
raw_char: Some(c),
}
} else {
Self {
code: Key::Char(c),
modifiers: Modifiers::NONE,
kind: KeyEventKind::Press,
raw_char: Some(c),
}
}
}
pub fn ctrl(c: char) -> Self {
Self {
code: Key::Char(c.to_ascii_lowercase()),
modifiers: Modifiers::CONTROL,
kind: KeyEventKind::Press,
raw_char: Some(c),
}
}
pub fn is_press(&self) -> bool {
self.kind == KeyEventKind::Press
}
pub fn is_release(&self) -> bool {
self.kind == KeyEventKind::Release
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum KeyEventKind {
Press,
Release,
Repeat,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct Modifiers(u8);
impl Modifiers {
pub const NONE: Self = Self(0);
pub const SHIFT: Self = Self(1 << 0);
pub const CONTROL: Self = Self(1 << 1);
pub const ALT: Self = Self(1 << 2);
pub const SUPER: Self = Self(1 << 3);
pub fn shift(self) -> bool {
self.0 & Self::SHIFT.0 != 0
}
pub fn ctrl(self) -> bool {
self.0 & Self::CONTROL.0 != 0
}
pub fn alt(self) -> bool {
self.0 & Self::ALT.0 != 0
}
pub fn super_key(self) -> bool {
self.0 & Self::SUPER.0 != 0
}
pub fn is_none(self) -> bool {
self.0 == 0
}
}
impl BitOr for Modifiers {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl BitOrAssign for Modifiers {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl BitAnd for Modifiers {
type Output = Self;
fn bitand(self, rhs: Self) -> Self {
Self(self.0 & rhs.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_key_equality() {
assert_eq!(Key::Char('a'), Key::Char('a'));
assert_ne!(Key::Char('a'), Key::Char('b'));
assert_ne!(Key::Char('a'), Key::Enter);
assert_eq!(Key::F(5), Key::F(5));
assert_ne!(Key::F(5), Key::F(6));
}
#[test]
fn test_key_event_new_non_char() {
let event = KeyEvent::new(Key::Enter);
assert_eq!(event.code, Key::Enter);
assert!(event.modifiers.is_none());
assert_eq!(event.kind, KeyEventKind::Press);
assert!(event.raw_char.is_none());
}
#[test]
fn test_key_event_new_lowercase_char() {
let event = KeyEvent::new(Key::Char('a'));
assert_eq!(event.code, Key::Char('a'));
assert!(event.modifiers.is_none());
assert_eq!(event.raw_char, Some('a'));
}
#[test]
fn test_key_event_new_uppercase_normalizes() {
let event = KeyEvent::new(Key::Char('G'));
assert_eq!(event.code, Key::Char('g'));
assert!(event.modifiers.shift());
assert_eq!(event.raw_char, Some('G'));
}
#[test]
fn test_key_event_new_non_letter_char() {
let event = KeyEvent::new(Key::Char('!'));
assert_eq!(event.code, Key::Char('!'));
assert!(event.modifiers.is_none());
assert_eq!(event.raw_char, Some('!'));
}
#[test]
fn test_key_event_char_lowercase() {
let event = KeyEvent::char('a');
assert_eq!(event.code, Key::Char('a'));
assert!(event.modifiers.is_none());
assert_eq!(event.raw_char, Some('a'));
}
#[test]
fn test_key_event_char_uppercase_normalizes() {
let event = KeyEvent::char('A');
assert_eq!(event.code, Key::Char('a'));
assert!(event.modifiers.shift());
assert_eq!(event.raw_char, Some('A'));
}
#[test]
fn test_key_event_ctrl() {
let event = KeyEvent::ctrl('c');
assert_eq!(event.code, Key::Char('c'));
assert!(event.modifiers.ctrl());
assert!(!event.modifiers.shift());
assert_eq!(event.raw_char, Some('c'));
}
#[test]
fn test_key_event_is_press_release() {
let press = KeyEvent::new(Key::Enter);
assert!(press.is_press());
assert!(!press.is_release());
let release = KeyEvent {
kind: KeyEventKind::Release,
..KeyEvent::new(Key::Enter)
};
assert!(!release.is_press());
assert!(release.is_release());
}
#[test]
fn test_modifiers_default() {
let m = Modifiers::default();
assert!(m.is_none());
assert!(!m.shift());
assert!(!m.ctrl());
assert!(!m.alt());
assert!(!m.super_key());
}
#[test]
fn test_modifiers_individual() {
assert!(Modifiers::SHIFT.shift());
assert!(!Modifiers::SHIFT.ctrl());
assert!(Modifiers::CONTROL.ctrl());
assert!(!Modifiers::CONTROL.shift());
assert!(Modifiers::ALT.alt());
assert!(Modifiers::SUPER.super_key());
}
#[test]
fn test_modifiers_bitor() {
let mods = Modifiers::CONTROL | Modifiers::SHIFT;
assert!(mods.ctrl());
assert!(mods.shift());
assert!(!mods.alt());
}
#[test]
fn test_modifiers_bitor_assign() {
let mut mods = Modifiers::NONE;
mods |= Modifiers::ALT;
assert!(mods.alt());
assert!(!mods.ctrl());
}
#[test]
fn test_modifiers_bitand() {
let mods = Modifiers::CONTROL | Modifiers::SHIFT;
let masked = mods & Modifiers::CONTROL;
assert!(masked.ctrl());
assert!(!masked.shift());
}
#[test]
fn test_key_event_kind_equality() {
assert_eq!(KeyEventKind::Press, KeyEventKind::Press);
assert_ne!(KeyEventKind::Press, KeyEventKind::Release);
}
}