cranpose_foundation/nodes/input/
types.rs1use cranpose_ui_graphics::Point;
2use std::cell::Cell;
3use std::rc::Rc;
4
5pub type PointerId = u64;
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8pub enum PointerPhase {
9 Start,
10 Move,
11 End,
12 Cancel,
13}
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub enum PointerEventKind {
17 Down,
18 Move,
19 Up,
20 Cancel,
21 Scroll,
22 Enter,
23 Exit,
24}
25
26#[repr(u8)]
27#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
28pub enum PointerButton {
29 Primary = 0,
30 Secondary = 1,
31 Middle = 2,
32 Back = 3,
33 Forward = 4,
34}
35
36#[derive(Clone, Copy, Debug, PartialEq, Eq)]
37pub struct PointerButtons(u8);
38
39impl PointerButtons {
40 pub const NONE: Self = Self(0);
41
42 pub fn new() -> Self {
43 Self::NONE
44 }
45
46 pub fn with(mut self, button: PointerButton) -> Self {
47 self.insert(button);
48 self
49 }
50
51 pub fn insert(&mut self, button: PointerButton) {
52 self.0 |= 1 << (button as u8);
53 }
54
55 pub fn remove(&mut self, button: PointerButton) {
56 self.0 &= !(1 << (button as u8));
57 }
58
59 pub fn contains(&self, button: PointerButton) -> bool {
60 (self.0 & (1 << (button as u8))) != 0
61 }
62}
63
64impl Default for PointerButtons {
65 fn default() -> Self {
66 Self::NONE
67 }
68}
69
70#[derive(Clone, Debug)]
76pub struct PointerEvent {
77 pub id: PointerId,
78 pub kind: PointerEventKind,
79 pub phase: PointerPhase,
80 pub position: Point,
81 pub global_position: Point,
82 pub scroll_delta: Point,
87 pub buttons: PointerButtons,
88 consumed: Rc<Cell<bool>>,
91}
92
93impl PointerEvent {
94 pub fn new(kind: PointerEventKind, position: Point, global_position: Point) -> Self {
95 Self {
96 id: 0,
97 kind,
98 phase: match kind {
99 PointerEventKind::Down => PointerPhase::Start,
100 PointerEventKind::Move | PointerEventKind::Enter | PointerEventKind::Exit => {
101 PointerPhase::Move
102 }
103 PointerEventKind::Up => PointerPhase::End,
104 PointerEventKind::Cancel => PointerPhase::Cancel,
105 PointerEventKind::Scroll => PointerPhase::Move,
106 },
107 position,
108 global_position,
109 scroll_delta: Point { x: 0.0, y: 0.0 },
110 buttons: PointerButtons::NONE,
111 consumed: Rc::new(Cell::new(false)),
112 }
113 }
114
115 pub fn with_scroll_delta(mut self, scroll_delta: Point) -> Self {
117 self.scroll_delta = scroll_delta;
118 self
119 }
120
121 pub fn with_buttons(mut self, buttons: PointerButtons) -> Self {
123 self.buttons = buttons;
124 self
125 }
126
127 pub fn consume(&self) {
132 self.consumed.set(true);
133 }
134
135 pub fn is_consumed(&self) -> bool {
140 self.consumed.get()
141 }
142
143 pub fn copy_with_local_position(&self, position: Point) -> Self {
145 Self {
146 id: self.id,
147 kind: self.kind,
148 phase: self.phase,
149 position,
150 global_position: self.global_position,
151 scroll_delta: self.scroll_delta,
152 buttons: self.buttons,
153 consumed: self.consumed.clone(),
154 }
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 fn point(x: f32, y: f32) -> Point {
163 Point { x, y }
164 }
165
166 #[test]
167 fn pointer_event_clones_share_consumed_state() {
168 let event = PointerEvent::new(PointerEventKind::Move, point(1.0, 2.0), point(3.0, 4.0));
169 let cloned = event.clone();
170 assert!(!event.is_consumed());
171 assert!(!cloned.is_consumed());
172
173 cloned.consume();
174
175 assert!(event.is_consumed());
176 assert!(cloned.is_consumed());
177 }
178
179 #[test]
180 fn pointer_event_copy_with_local_position_preserves_consumption_state() {
181 let event = PointerEvent::new(PointerEventKind::Down, point(4.0, 5.0), point(4.0, 5.0));
182 let local = event.copy_with_local_position(point(1.0, 1.0));
183
184 assert_eq!(local.position, point(1.0, 1.0));
185 assert_eq!(local.global_position, event.global_position);
186 assert!(!local.is_consumed());
187
188 event.consume();
189
190 assert!(local.is_consumed());
191 }
192}