use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
#[allow(clippy::struct_excessive_bools)] pub struct Modifiers {
#[serde(default, skip_serializing_if = "is_false")]
pub shift: bool,
#[serde(default, skip_serializing_if = "is_false")]
pub ctrl: bool,
#[serde(default, skip_serializing_if = "is_false")]
pub alt: bool,
#[serde(default, skip_serializing_if = "is_false")]
pub super_key: bool,
#[serde(default, skip_serializing_if = "is_false")]
pub hyper: bool,
#[serde(default, skip_serializing_if = "is_false")]
pub meta: bool,
}
#[allow(clippy::trivially_copy_pass_by_ref)]
const fn is_false(b: &bool) -> bool {
!*b
}
impl Modifiers {
pub const NONE: Self = Self {
shift: false,
ctrl: false,
alt: false,
super_key: false,
hyper: false,
meta: false,
};
#[must_use]
pub const fn is_empty(&self) -> bool {
!self.shift && !self.ctrl && !self.alt && !self.super_key && !self.hyper && !self.meta
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(tag = "type", content = "value")]
pub enum KeyCode {
Char(char),
F(u8),
Up,
Down,
Left,
Right,
Home,
End,
PageUp,
PageDown,
Backspace,
Delete,
Insert,
Tab,
BackTab,
Enter,
Escape,
Null,
CapsLock,
ScrollLock,
NumLock,
PrintScreen,
Pause,
Menu,
KeypadBegin,
MediaPlay,
MediaPause,
MediaPlayPause,
MediaStop,
MediaReverse,
MediaFastForward,
MediaRewind,
MediaNext,
MediaPrevious,
MediaRecord,
MediaLowerVolume,
MediaRaiseVolume,
MediaMuteVolume,
LeftShift,
RightShift,
LeftCtrl,
RightCtrl,
LeftAlt,
RightAlt,
LeftSuper,
RightSuper,
LeftHyper,
RightHyper,
LeftMeta,
RightMeta,
IsoLevel3Shift,
IsoLevel5Shift,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum KeyEventKind {
#[default]
Press,
Repeat,
Release,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct KeyEvent {
pub code: KeyCode,
#[serde(default, skip_serializing_if = "Modifiers::is_empty")]
pub modifiers: Modifiers,
#[serde(default, skip_serializing_if = "is_press")]
pub kind: KeyEventKind,
}
#[allow(clippy::trivially_copy_pass_by_ref)]
const fn is_press(kind: &KeyEventKind) -> bool {
matches!(kind, KeyEventKind::Press)
}
impl KeyEvent {
#[must_use]
pub const fn new(code: KeyCode) -> Self {
Self {
code,
modifiers: Modifiers::NONE,
kind: KeyEventKind::Press,
}
}
#[must_use]
pub const fn with_modifiers(code: KeyCode, modifiers: Modifiers) -> Self {
Self {
code,
modifiers,
kind: KeyEventKind::Press,
}
}
#[must_use]
pub const fn is_press(&self) -> bool {
matches!(self.kind, KeyEventKind::Press)
}
#[must_use]
pub const fn is_release(&self) -> bool {
matches!(self.kind, KeyEventKind::Release)
}
#[must_use]
pub const fn is_repeat(&self) -> bool {
matches!(self.kind, KeyEventKind::Repeat)
}
}
#[cfg(test)]
#[path = "key_tests.rs"]
mod tests;