use crate::grid::Pos;
use alloc::string::String;
use alloc::vec::Vec;
use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not};
pub type PhysicalPos = ixy::Pos<u32>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct KeyModifiers(u8);
impl KeyModifiers {
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);
#[must_use]
pub const fn contains(self, other: Self) -> bool {
(self.0 & other.0) == other.0
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.0 == 0
}
}
impl BitOr for KeyModifiers {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl BitOrAssign for KeyModifiers {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl BitAnd for KeyModifiers {
type Output = Self;
fn bitand(self, rhs: Self) -> Self {
Self(self.0 & rhs.0)
}
}
impl BitAndAssign for KeyModifiers {
fn bitand_assign(&mut self, rhs: Self) {
self.0 &= rhs.0;
}
}
impl Not for KeyModifiers {
type Output = Self;
fn not(self) -> Self {
Self(!self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ModifierKey {
Shift,
Control,
Alt,
Super,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum KeyCode {
Char(char),
F(u8),
Backspace,
Enter,
Left,
Right,
Up,
Down,
Home,
End,
PageUp,
PageDown,
Tab,
BackTab,
Delete,
Insert,
Escape,
Modifier(ModifierKey),
CapsLock,
ScrollLock,
NumLock,
PrintScreen,
Pause,
Menu,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum KeyEventKind {
#[default]
Press,
Repeat,
Release,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum KeyLocation {
#[default]
Standard,
Left,
Right,
Numpad,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct KeyEvent {
pub code: KeyCode,
pub modifiers: KeyModifiers,
pub kind: KeyEventKind,
pub location: KeyLocation,
}
impl KeyEvent {
#[must_use]
pub const fn new(code: KeyCode, modifiers: KeyModifiers) -> Self {
Self {
code,
modifiers,
kind: KeyEventKind::Press,
location: KeyLocation::Standard,
}
}
#[must_use]
pub const fn with_kind(code: KeyCode, modifiers: KeyModifiers, kind: KeyEventKind) -> Self {
Self {
code,
modifiers,
kind,
location: KeyLocation::Standard,
}
}
#[must_use]
pub const fn with_location(
code: KeyCode,
modifiers: KeyModifiers,
kind: KeyEventKind,
location: KeyLocation,
) -> Self {
Self {
code,
modifiers,
kind,
location,
}
}
#[must_use]
pub const fn is_down(self) -> bool {
matches!(self.kind, KeyEventKind::Press | KeyEventKind::Repeat)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum MouseButton {
Left,
Right,
Middle,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum MouseEventKind {
Down(MouseButton),
Up(MouseButton),
Drag(MouseButton),
Moved,
Scroll {
dx: f32,
dy: f32,
},
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct MouseEvent {
pub kind: MouseEventKind,
pub position: Pos,
pub pixel_position: Option<PhysicalPos>,
pub modifiers: KeyModifiers,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum SystemTheme {
Light,
Dark,
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum Event {
Key(KeyEvent),
Mouse(MouseEvent),
Resize(u16, u16),
Close,
ThemeChanged(SystemTheme),
Paste(String),
FocusGained,
FocusLost,
Custom(u64),
}
#[derive(Debug, Clone, Default)]
pub struct KeyState {
held: Vec<(KeyCode, KeyLocation)>,
}
impl KeyState {
#[must_use]
pub const fn new() -> Self {
Self { held: Vec::new() }
}
pub fn apply(&mut self, event: KeyEvent) {
let entry = (event.code, event.location);
match event.kind {
KeyEventKind::Press | KeyEventKind::Repeat => {
if !self.held.contains(&entry) {
self.held.push(entry);
}
}
KeyEventKind::Release => {
self.held.retain(|&e| e != entry);
}
}
}
pub fn apply_event(&mut self, event: &Event) {
if let Event::Key(key) = event {
self.apply(*key);
}
}
#[must_use]
pub fn is_held(&self, code: KeyCode, location: KeyLocation) -> bool {
self.held.contains(&(code, location))
}
pub fn held(&self) -> impl Iterator<Item = (KeyCode, KeyLocation)> + '_ {
self.held.iter().copied()
}
pub fn clear(&mut self) {
self.held.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_key_modifiers() {
let mods = KeyModifiers::SHIFT | KeyModifiers::CONTROL;
assert!(mods.contains(KeyModifiers::SHIFT));
assert!(mods.contains(KeyModifiers::CONTROL));
assert!(!mods.contains(KeyModifiers::ALT));
assert!(!mods.is_empty());
let inverse = !mods;
assert!(inverse.contains(KeyModifiers::ALT));
assert!(inverse.contains(KeyModifiers::SUPER));
assert!(!inverse.contains(KeyModifiers::SHIFT));
assert!(!inverse.contains(KeyModifiers::CONTROL));
}
#[test]
fn test_key_modifiers_super() {
let mods = KeyModifiers::SUPER;
assert!(mods.contains(KeyModifiers::SUPER));
assert!(!mods.contains(KeyModifiers::SHIFT));
assert!(!mods.contains(KeyModifiers::CONTROL));
assert!(!mods.contains(KeyModifiers::ALT));
let all =
KeyModifiers::SHIFT | KeyModifiers::CONTROL | KeyModifiers::ALT | KeyModifiers::SUPER;
assert!(all.contains(KeyModifiers::SUPER));
assert!(all.contains(KeyModifiers::SHIFT));
assert!(all.contains(KeyModifiers::CONTROL));
assert!(all.contains(KeyModifiers::ALT));
}
#[test]
fn test_event_construction() {
let key_event = KeyEvent::new(KeyCode::Char('a'), KeyModifiers::SHIFT);
let event = Event::Key(key_event);
if let Event::Key(ke) = event {
assert_eq!(ke.code, KeyCode::Char('a'));
assert!(ke.modifiers.contains(KeyModifiers::SHIFT));
assert_eq!(ke.kind, KeyEventKind::Press);
} else {
panic!("Expected Event::Key");
}
}
#[test]
fn test_key_event_kind_helpers() {
let press = KeyEvent::new(KeyCode::Char('x'), KeyModifiers::NONE);
assert_eq!(press.kind, KeyEventKind::Press);
assert!(press.is_down());
let repeat =
KeyEvent::with_kind(KeyCode::Char('x'), KeyModifiers::NONE, KeyEventKind::Repeat);
assert!(repeat.is_down());
let release = KeyEvent::with_kind(
KeyCode::Char('x'),
KeyModifiers::NONE,
KeyEventKind::Release,
);
assert!(!release.is_down());
}
#[test]
fn test_key_state_tracks_held_keys() {
let mut state = KeyState::new();
assert!(!state.is_held(KeyCode::Left, KeyLocation::Standard));
state.apply(KeyEvent::new(KeyCode::Left, KeyModifiers::NONE));
assert!(state.is_held(KeyCode::Left, KeyLocation::Standard));
state.apply(KeyEvent::with_kind(
KeyCode::Left,
KeyModifiers::NONE,
KeyEventKind::Repeat,
));
assert!(state.is_held(KeyCode::Left, KeyLocation::Standard));
state.apply(KeyEvent::with_kind(
KeyCode::Left,
KeyModifiers::NONE,
KeyEventKind::Release,
));
assert!(!state.is_held(KeyCode::Left, KeyLocation::Standard));
}
#[test]
fn test_key_state_distinguishes_numpad_from_standard() {
let mut state = KeyState::new();
state.apply(KeyEvent::with_location(
KeyCode::Char('8'),
KeyModifiers::NONE,
KeyEventKind::Press,
KeyLocation::Numpad,
));
assert!(state.is_held(KeyCode::Char('8'), KeyLocation::Numpad));
assert!(!state.is_held(KeyCode::Char('8'), KeyLocation::Standard));
state.apply(KeyEvent::new(KeyCode::Char('8'), KeyModifiers::NONE));
assert!(state.is_held(KeyCode::Char('8'), KeyLocation::Standard));
assert!(state.is_held(KeyCode::Char('8'), KeyLocation::Numpad));
state.apply(KeyEvent::with_kind(
KeyCode::Char('8'),
KeyModifiers::NONE,
KeyEventKind::Release,
));
assert!(!state.is_held(KeyCode::Char('8'), KeyLocation::Standard));
assert!(state.is_held(KeyCode::Char('8'), KeyLocation::Numpad));
}
#[test]
fn test_key_state_apply_event_ignores_non_key() {
let mut state = KeyState::new();
state.apply_event(&Event::Resize(1, 1));
assert!(state.held().next().is_none());
state.apply_event(&Event::Key(KeyEvent::new(KeyCode::Up, KeyModifiers::NONE)));
assert!(state.is_held(KeyCode::Up, KeyLocation::Standard));
}
#[test]
fn test_paste_event_carries_text() {
let event = Event::Paste("hello".to_string());
let Event::Paste(text) = event else {
panic!("Expected Event::Paste");
};
assert_eq!(text, "hello");
}
#[test]
fn test_custom_event_carries_opaque_id() {
let event = Event::Custom(42);
let Event::Custom(id) = event else {
panic!("Expected Event::Custom");
};
assert_eq!(id, 42);
assert_ne!(Event::Custom(1), Event::Custom(2));
}
#[test]
fn test_focus_gained_and_lost_are_distinct() {
assert!(matches!(Event::FocusGained, Event::FocusGained));
assert!(matches!(Event::FocusLost, Event::FocusLost));
assert_ne!(Event::FocusGained, Event::FocusLost);
}
#[test]
fn test_mouse_event_no_pixel_position() {
let mouse_event = MouseEvent {
kind: MouseEventKind::Down(MouseButton::Left),
position: Pos { x: 10, y: 5 },
pixel_position: None,
modifiers: KeyModifiers::NONE,
};
assert!(mouse_event.pixel_position.is_none());
assert!(matches!(Event::Mouse(mouse_event), Event::Mouse(_)));
}
#[test]
fn test_mouse_event_with_pixel_position() {
let mouse_event = MouseEvent {
kind: MouseEventKind::Moved,
position: Pos { x: 3, y: 2 },
pixel_position: Some(PhysicalPos { x: 55, y: 38 }),
modifiers: KeyModifiers::NONE,
};
let px = mouse_event.pixel_position.unwrap();
assert_eq!(px.x, 55);
assert_eq!(px.y, 38);
assert_ne!(px.x, u32::from(mouse_event.position.x));
}
#[test]
fn test_physical_pos_is_copy() {
let p = PhysicalPos { x: 10, y: 20 };
let q = p; assert_eq!(p, q);
}
}