mod key;
mod mouse;
pub use key::{KeyCode, KeyEvent, KeyEventKind, KeyLocation, KeyModifiers, KeyState, ModifierKey};
pub use mouse::{MouseButton, MouseEvent, MouseEventKind, PhysicalPos};
use alloc::string::String;
#[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),
}
#[must_use]
pub const fn coalesces_with(new: &Event, existing: &Event) -> bool {
matches!(
(new, existing),
(
Event::Mouse(MouseEvent {
kind: MouseEventKind::Moved,
..
}),
Event::Mouse(MouseEvent {
kind: MouseEventKind::Moved,
..
}),
)
) || matches!(
(new, existing),
(
Event::Mouse(MouseEvent {
kind: MouseEventKind::Drag(new_button),
..
}),
Event::Mouse(MouseEvent {
kind: MouseEventKind::Drag(existing_button),
..
}),
) if *new_button as u8 == *existing_button as u8
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::grid::Pos;
#[test]
fn test_paste_event_carries_text() {
use alloc::string::ToString as _;
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);
}
fn moved_at(x: u16, y: u16) -> Event {
Event::Mouse(MouseEvent {
kind: MouseEventKind::Moved,
position: Pos::new(x, y),
pixel_position: None,
modifiers: KeyModifiers::NONE,
})
}
#[test]
fn coalesces_with_true_for_two_consecutive_moved_events() {
assert!(coalesces_with(&moved_at(1, 1), &moved_at(0, 0)));
}
#[test]
fn coalesces_with_false_for_non_moved_mouse_events() {
let down = Event::Mouse(MouseEvent {
kind: MouseEventKind::Down(MouseButton::Left),
position: Pos::new(0, 0),
pixel_position: None,
modifiers: KeyModifiers::NONE,
});
assert!(!coalesces_with(&moved_at(1, 1), &down));
assert!(!coalesces_with(&down, &moved_at(0, 0)));
}
#[test]
fn coalesces_with_false_for_non_mouse_events() {
let key = Event::Key(KeyEvent::new(KeyCode::Char('a'), KeyModifiers::NONE));
assert!(!coalesces_with(&moved_at(1, 1), &key));
assert!(!coalesces_with(&key, &moved_at(0, 0)));
}
fn drag_at(x: u16, y: u16, button: MouseButton) -> Event {
Event::Mouse(MouseEvent {
kind: MouseEventKind::Drag(button),
position: Pos::new(x, y),
pixel_position: None,
modifiers: KeyModifiers::NONE,
})
}
#[test]
fn coalesces_with_true_for_two_consecutive_drag_events_same_button() {
assert!(coalesces_with(
&drag_at(1, 1, MouseButton::Left),
&drag_at(0, 0, MouseButton::Left),
));
}
#[test]
fn coalesces_with_false_for_drag_events_with_different_buttons() {
assert!(!coalesces_with(
&drag_at(1, 1, MouseButton::Right),
&drag_at(0, 0, MouseButton::Left),
));
assert!(!coalesces_with(
&drag_at(1, 1, MouseButton::Left),
&drag_at(0, 0, MouseButton::Middle),
));
}
#[test]
fn coalesces_with_false_for_scroll_events() {
let scroll = |dy: f32| {
Event::Mouse(MouseEvent {
kind: MouseEventKind::Scroll { dx: 0.0, dy },
position: Pos::new(0, 0),
pixel_position: None,
modifiers: KeyModifiers::NONE,
})
};
assert!(!coalesces_with(&scroll(1.0), &scroll(1.0)));
}
#[test]
fn coalesces_with_false_for_two_non_mouse_events() {
assert!(!coalesces_with(&Event::Close, &Event::Resize(1, 1)));
}
}