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 Zoom,
25 Enter,
26 Exit,
27}
28
29#[repr(u8)]
30#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
31pub enum PointerButton {
32 Primary = 0,
33 Secondary = 1,
34 Middle = 2,
35 Back = 3,
36 Forward = 4,
37}
38
39#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40pub struct PointerButtons(u8);
41
42impl PointerButtons {
43 pub const NONE: Self = Self(0);
44
45 pub fn new() -> Self {
46 Self::NONE
47 }
48
49 pub fn with(mut self, button: PointerButton) -> Self {
50 self.insert(button);
51 self
52 }
53
54 pub fn insert(&mut self, button: PointerButton) {
55 self.0 |= 1 << (button as u8);
56 }
57
58 pub fn remove(&mut self, button: PointerButton) {
59 self.0 &= !(1 << (button as u8));
60 }
61
62 pub fn contains(&self, button: PointerButton) -> bool {
63 (self.0 & (1 << (button as u8))) != 0
64 }
65}
66
67impl Default for PointerButtons {
68 fn default() -> Self {
69 Self::NONE
70 }
71}
72
73#[derive(Clone, Debug)]
79pub struct PointerEvent {
80 pub id: PointerId,
81 pub kind: PointerEventKind,
82 pub phase: PointerPhase,
83 pub position: Point,
84 pub global_position: Point,
85 pub scroll_delta: Point,
90 pub buttons: PointerButtons,
91 pub time_ms: Option<i64>,
100 pub zoom_delta: f32,
103 consumed: Rc<Cell<bool>>,
106}
107
108impl PointerEvent {
109 pub fn new(kind: PointerEventKind, position: Point, global_position: Point) -> Self {
110 Self {
111 id: 0,
112 kind,
113 phase: match kind {
114 PointerEventKind::Down => PointerPhase::Start,
115 PointerEventKind::Move | PointerEventKind::Enter | PointerEventKind::Exit => {
116 PointerPhase::Move
117 }
118 PointerEventKind::Up => PointerPhase::End,
119 PointerEventKind::Cancel => PointerPhase::Cancel,
120 PointerEventKind::Scroll | PointerEventKind::Zoom => PointerPhase::Move,
121 },
122 position,
123 global_position,
124 scroll_delta: Point { x: 0.0, y: 0.0 },
125 buttons: PointerButtons::NONE,
126 time_ms: None,
127 zoom_delta: 1.0,
128 consumed: Rc::new(Cell::new(false)),
129 }
130 }
131
132 pub fn with_id(mut self, id: PointerId) -> Self {
134 self.id = id;
135 self
136 }
137
138 pub fn with_zoom_delta(mut self, zoom_delta: f32) -> Self {
140 self.zoom_delta = zoom_delta;
141 self
142 }
143
144 pub fn with_scroll_delta(mut self, scroll_delta: Point) -> Self {
146 self.scroll_delta = scroll_delta;
147 self
148 }
149
150 pub fn with_time_ms(mut self, time_ms: Option<i64>) -> Self {
152 self.time_ms = time_ms;
153 self
154 }
155
156 pub fn with_buttons(mut self, buttons: PointerButtons) -> Self {
158 self.buttons = buttons;
159 self
160 }
161
162 pub fn consume(&self) {
167 self.consumed.set(true);
168 }
169
170 pub fn is_consumed(&self) -> bool {
175 self.consumed.get()
176 }
177
178 pub fn copy_with_local_position(&self, position: Point) -> Self {
180 Self {
181 id: self.id,
182 kind: self.kind,
183 phase: self.phase,
184 position,
185 global_position: self.global_position,
186 scroll_delta: self.scroll_delta,
187 buttons: self.buttons,
188 time_ms: self.time_ms,
189 zoom_delta: self.zoom_delta,
190 consumed: self.consumed.clone(),
191 }
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198
199 fn point(x: f32, y: f32) -> Point {
200 Point { x, y }
201 }
202
203 #[test]
204 fn pointer_event_clones_share_consumed_state() {
205 let event = PointerEvent::new(PointerEventKind::Move, point(1.0, 2.0), point(3.0, 4.0));
206 let cloned = event.clone();
207 assert!(!event.is_consumed());
208 assert!(!cloned.is_consumed());
209
210 cloned.consume();
211
212 assert!(event.is_consumed());
213 assert!(cloned.is_consumed());
214 }
215
216 #[test]
217 fn pointer_event_copy_with_local_position_preserves_consumption_state() {
218 let event = PointerEvent::new(PointerEventKind::Down, point(4.0, 5.0), point(4.0, 5.0));
219 let local = event.copy_with_local_position(point(1.0, 1.0));
220
221 assert_eq!(local.position, point(1.0, 1.0));
222 assert_eq!(local.global_position, event.global_position);
223 assert!(!local.is_consumed());
224
225 event.consume();
226
227 assert!(local.is_consumed());
228 }
229}