tuxtui_core/
event.rs

1//! Event handling types and utilities.
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6/// Mouse button types.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
9pub enum MouseButton {
10    /// Left mouse button
11    Left,
12    /// Right mouse button
13    Right,
14    /// Middle mouse button
15    Middle,
16}
17
18/// Mouse event types.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
20#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21pub enum MouseEventKind {
22    /// Mouse button pressed
23    Down(MouseButton),
24    /// Mouse button released
25    Up(MouseButton),
26    /// Mouse dragged with button held
27    Drag(MouseButton),
28    /// Mouse moved without button
29    Moved,
30    /// Mouse wheel scrolled
31    ScrollDown,
32    /// Mouse wheel scrolled up
33    ScrollUp,
34    /// Mouse wheel scrolled left
35    ScrollLeft,
36    /// Mouse wheel scrolled right
37    ScrollRight,
38}
39
40/// A mouse event with position.
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
42#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
43pub struct MouseEvent {
44    /// The kind of mouse event
45    pub kind: MouseEventKind,
46    /// Column position (0-indexed)
47    pub column: u16,
48    /// Row position (0-indexed)
49    pub row: u16,
50}
51
52impl MouseEvent {
53    /// Create a new mouse event.
54    #[must_use]
55    pub const fn new(kind: MouseEventKind, column: u16, row: u16) -> Self {
56        Self { kind, column, row }
57    }
58
59    /// Check if this is a click event at the given position.
60    #[must_use]
61    pub const fn is_click_at(&self, column: u16, row: u16) -> bool {
62        matches!(self.kind, MouseEventKind::Down(_)) && self.column == column && self.row == row
63    }
64
65    /// Check if this is a click event within the given area.
66    #[must_use]
67    pub fn is_click_in(&self, area: crate::geometry::Rect) -> bool {
68        matches!(self.kind, MouseEventKind::Down(_))
69            && self.column >= area.left()
70            && self.column < area.right()
71            && self.row >= area.top()
72            && self.row < area.bottom()
73    }
74}
75
76/// Keyboard modifiers.
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
78#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
79pub struct KeyModifiers {
80    /// Shift key
81    pub shift: bool,
82    /// Control key
83    pub ctrl: bool,
84    /// Alt key
85    pub alt: bool,
86    /// Meta/Super/Cmd key
87    pub meta: bool,
88}
89
90impl KeyModifiers {
91    /// No modifiers pressed.
92    pub const NONE: Self = Self {
93        shift: false,
94        ctrl: false,
95        alt: false,
96        meta: false,
97    };
98
99    /// Only shift pressed.
100    pub const SHIFT: Self = Self {
101        shift: true,
102        ctrl: false,
103        alt: false,
104        meta: false,
105    };
106
107    /// Only control pressed.
108    pub const CTRL: Self = Self {
109        shift: false,
110        ctrl: true,
111        alt: false,
112        meta: false,
113    };
114
115    /// Only alt pressed.
116    pub const ALT: Self = Self {
117        shift: false,
118        ctrl: false,
119        alt: true,
120        meta: false,
121    };
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127    use crate::geometry::Rect;
128
129    #[test]
130    fn test_mouse_event_click_detection() {
131        let event = MouseEvent::new(MouseEventKind::Down(MouseButton::Left), 5, 10);
132        assert!(event.is_click_at(5, 10));
133        assert!(!event.is_click_at(6, 10));
134    }
135
136    #[test]
137    fn test_mouse_event_area_detection() {
138        let event = MouseEvent::new(MouseEventKind::Down(MouseButton::Left), 5, 10);
139        let area = Rect::new(0, 0, 10, 20);
140        assert!(event.is_click_in(area));
141
142        let outside_area = Rect::new(20, 20, 10, 10);
143        assert!(!event.is_click_in(outside_area));
144    }
145}