#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum MouseButton {
Left,
Right,
Middle,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum MouseKind {
Down(MouseButton),
Up(MouseButton),
Drag(MouseButton),
Moved,
ScrollUp,
ScrollDown,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct MouseEvent {
pub x: u16,
pub y: u16,
pub kind: MouseKind,
pub mods: KeyMods,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct MouseMoveEvent {
pub x: u16,
pub y: u16,
pub local_x: u16,
pub local_y: u16,
pub target_w: u16,
pub target_h: u16,
pub mods: KeyMods,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct MouseDragEvent {
pub from_x: u16,
pub from_y: u16,
pub from_local_x: u16,
pub from_local_y: u16,
pub x: u16,
pub y: u16,
pub local_x: u16,
pub local_y: u16,
pub delta_x: i16,
pub delta_y: i16,
pub target_w: u16,
pub target_h: u16,
pub mods: KeyMods,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct KeyMods {
pub ctrl: bool,
pub alt: bool,
pub shift: bool,
pub super_key: bool,
}
impl KeyMods {
pub const NONE: Self = Self {
ctrl: false,
alt: false,
shift: false,
super_key: false,
};
pub const SHIFT: Self = Self {
ctrl: false,
alt: false,
shift: true,
super_key: false,
};
pub const CTRL: Self = Self {
ctrl: true,
alt: false,
shift: false,
super_key: false,
};
pub const ALT: Self = Self {
ctrl: false,
alt: true,
shift: false,
super_key: false,
};
pub fn is_empty(&self) -> bool {
!self.ctrl && !self.alt && !self.shift && !self.super_key
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum KeyCode {
Char(char),
Insert,
Enter,
Esc,
Tab,
BackTab,
Backspace,
Delete,
Home,
End,
PageUp,
PageDown,
Up,
Down,
Left,
Right,
F(u8),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct KeyEvent {
pub code: KeyCode,
pub mods: KeyMods,
}
impl KeyEvent {
pub fn is(&self, code: KeyCode) -> bool {
self.code == code && self.mods.is_empty()
}
pub fn is_with(&self, code: KeyCode, mods: KeyMods) -> bool {
self.code == code && self.mods == mods
}
pub fn to_formatted_string(&self, lowercase: bool) -> String {
let mut parts = Vec::new();
if self.mods.ctrl {
parts.push("Ctrl");
}
if self.mods.alt {
parts.push("Alt");
}
if self.mods.super_key {
parts.push("Super");
}
if self.mods.shift {
parts.push("Shift");
}
let code_str = match self.code {
KeyCode::Char(c) => {
if c == ' ' {
"Space".to_string()
} else {
c.to_uppercase().to_string()
}
}
KeyCode::Insert => "Insert".to_string(),
KeyCode::Enter => "Enter".to_string(),
KeyCode::Esc => "Esc".to_string(),
KeyCode::Tab => "Tab".to_string(),
KeyCode::BackTab => "BackTab".to_string(),
KeyCode::Backspace => "Backspace".to_string(),
KeyCode::Delete => "Delete".to_string(),
KeyCode::Home => "Home".to_string(),
KeyCode::End => "End".to_string(),
KeyCode::PageUp => "PageUp".to_string(),
KeyCode::PageDown => "PageDown".to_string(),
KeyCode::Up => "Up".to_string(),
KeyCode::Down => "Down".to_string(),
KeyCode::Left => "Left".to_string(),
KeyCode::Right => "Right".to_string(),
KeyCode::F(n) => format!("F{n}"),
};
parts.push(&code_str);
let result = parts.join("+");
if lowercase {
result.to_lowercase()
} else {
result
}
}
}
impl std::fmt::Display for KeyEvent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_formatted_string(false))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_key_event_formatting() {
let key1 = KeyEvent {
code: KeyCode::Char('e'),
mods: KeyMods {
ctrl: true,
alt: false,
shift: false,
super_key: false,
},
};
assert_eq!(key1.to_formatted_string(false), "Ctrl+E");
assert_eq!(key1.to_formatted_string(true), "ctrl+e");
assert_eq!(key1.to_string(), "Ctrl+E");
let key2 = KeyEvent {
code: KeyCode::Enter,
mods: KeyMods {
ctrl: true,
alt: true,
shift: true,
super_key: false,
},
};
assert_eq!(key2.to_formatted_string(false), "Ctrl+Alt+Shift+Enter");
assert_eq!(key2.to_formatted_string(true), "ctrl+alt+shift+enter");
assert_eq!(key2.to_string(), "Ctrl+Alt+Shift+Enter");
let key3 = KeyEvent {
code: KeyCode::Char(' '),
mods: KeyMods {
ctrl: false,
alt: false,
shift: false,
super_key: false,
},
};
assert_eq!(key3.to_formatted_string(false), "Space");
assert_eq!(key3.to_string(), "Space");
let key4 = KeyEvent {
code: KeyCode::F(12),
mods: KeyMods {
ctrl: false,
alt: false,
shift: false,
super_key: false,
},
};
assert_eq!(key4.to_formatted_string(false), "F12");
assert_eq!(key4.to_formatted_string(true), "f12");
assert_eq!(key4.to_string(), "F12");
}
#[test]
fn test_key_event_matching_helpers_and_mod_constants() {
let plain_enter = KeyEvent {
code: KeyCode::Enter,
mods: KeyMods::NONE,
};
assert!(plain_enter.is(KeyCode::Enter));
assert!(plain_enter.is_with(KeyCode::Enter, KeyMods::NONE));
let shift_enter = KeyEvent {
code: KeyCode::Enter,
mods: KeyMods::SHIFT,
};
assert!(!shift_enter.is(KeyCode::Enter));
assert!(shift_enter.is_with(KeyCode::Enter, KeyMods::SHIFT));
assert!(!shift_enter.is_with(KeyCode::Enter, KeyMods::NONE));
assert!(KeyMods::NONE.is_empty());
assert!(!KeyMods::SHIFT.is_empty());
}
}