use bitflags::bitflags;
bitflags! {
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct KeyModifiers: u8 {
const SHIFT = 0b0000_0001;
const ALT = 0b0000_0010;
const CTRL = 0b0000_0100;
const SUPER = 0b0000_1000;
const HYPER = 0b0001_0000;
const META = 0b0010_0000;
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum KeyCode {
Backspace,
Enter,
Left,
Right,
Up,
Down,
Home,
End,
PageUp,
PageDown,
Tab,
BackTab,
Delete,
Insert,
F(u8),
Char(char),
Esc,
CapsLock,
ScrollLock,
NumLock,
PrintScreen,
Pause,
Menu,
KeypadBegin,
Null,
}
impl KeyCode {
#[must_use]
pub fn is_function_key(&self) -> bool {
matches!(self, Self::F(_))
}
#[must_use]
pub fn is_char(&self) -> bool {
matches!(self, Self::Char(_))
}
#[must_use]
pub fn is_navigation(&self) -> bool {
matches!(
self,
Self::Left
| Self::Right
| Self::Up
| Self::Down
| Self::Home
| Self::End
| Self::PageUp
| Self::PageDown
)
}
#[must_use]
pub fn char(&self) -> Option<char> {
match self {
Self::Char(c) => Some(*c),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct KeyEvent {
pub code: KeyCode,
pub modifiers: KeyModifiers,
}
impl KeyEvent {
#[must_use]
pub fn new(code: KeyCode, modifiers: KeyModifiers) -> Self {
Self { code, modifiers }
}
#[must_use]
pub fn key(code: KeyCode) -> Self {
Self::new(code, KeyModifiers::empty())
}
#[must_use]
pub fn char(c: char) -> Self {
Self::key(KeyCode::Char(c))
}
#[must_use]
pub fn with_ctrl(code: KeyCode) -> Self {
Self::new(code, KeyModifiers::CTRL)
}
#[must_use]
pub fn with_alt(code: KeyCode) -> Self {
Self::new(code, KeyModifiers::ALT)
}
#[must_use]
pub fn shift(&self) -> bool {
self.modifiers.contains(KeyModifiers::SHIFT)
}
#[must_use]
pub fn ctrl(&self) -> bool {
self.modifiers.contains(KeyModifiers::CTRL)
}
#[must_use]
pub fn alt(&self) -> bool {
self.modifiers.contains(KeyModifiers::ALT)
}
#[must_use]
pub fn matches(&self, code: KeyCode, modifiers: KeyModifiers) -> bool {
self.code == code && self.modifiers == modifiers
}
#[must_use]
pub fn is_ctrl_c(&self) -> bool {
self.matches(KeyCode::Char('c'), KeyModifiers::CTRL)
}
#[must_use]
pub fn is_ctrl_d(&self) -> bool {
self.matches(KeyCode::Char('d'), KeyModifiers::CTRL)
}
#[must_use]
pub fn is_esc(&self) -> bool {
self.code == KeyCode::Esc
}
#[must_use]
pub fn is_enter(&self) -> bool {
self.code == KeyCode::Enter
}
}
impl From<char> for KeyEvent {
fn from(c: char) -> Self {
Self::char(c)
}
}
impl From<KeyCode> for KeyEvent {
fn from(code: KeyCode) -> Self {
Self::key(code)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_key_event_creation() {
let event = KeyEvent::char('a');
assert_eq!(event.code, KeyCode::Char('a'));
assert!(event.modifiers.is_empty());
}
#[test]
fn test_key_event_modifiers() {
let event = KeyEvent::with_ctrl(KeyCode::Char('c'));
assert!(event.ctrl());
assert!(!event.shift());
assert!(!event.alt());
assert!(event.is_ctrl_c());
}
#[test]
fn test_key_code_checks() {
assert!(KeyCode::F(1).is_function_key());
assert!(KeyCode::Char('x').is_char());
assert!(KeyCode::Up.is_navigation());
assert!(!KeyCode::Enter.is_navigation());
}
#[test]
fn test_key_event_from_char() {
let event: KeyEvent = 'z'.into();
assert_eq!(event.code, KeyCode::Char('z'));
}
}