Skip to main content

cranpose_foundation/nodes/input/
types.rs

1use 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}
22
23#[repr(u8)]
24#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
25pub enum PointerButton {
26    Primary = 0,
27    Secondary = 1,
28    Middle = 2,
29    Back = 3,
30    Forward = 4,
31}
32
33#[derive(Clone, Copy, Debug, PartialEq, Eq)]
34pub struct PointerButtons(u8);
35
36impl PointerButtons {
37    pub const NONE: Self = Self(0);
38
39    pub fn new() -> Self {
40        Self::NONE
41    }
42
43    pub fn with(mut self, button: PointerButton) -> Self {
44        self.insert(button);
45        self
46    }
47
48    pub fn insert(&mut self, button: PointerButton) {
49        self.0 |= 1 << (button as u8);
50    }
51
52    pub fn remove(&mut self, button: PointerButton) {
53        self.0 &= !(1 << (button as u8));
54    }
55
56    pub fn contains(&self, button: PointerButton) -> bool {
57        (self.0 & (1 << (button as u8))) != 0
58    }
59}
60
61impl Default for PointerButtons {
62    fn default() -> Self {
63        Self::NONE
64    }
65}
66
67/// Pointer event with consumption tracking for gesture disambiguation.
68///
69/// Events can be consumed by handlers (e.g., scroll) to prevent other handlers
70/// (e.g., clicks) from receiving them. This enables proper gesture disambiguation
71/// matching Jetpack Compose's event consumption pattern.
72#[derive(Clone, Debug)]
73pub struct PointerEvent {
74    pub id: PointerId,
75    pub kind: PointerEventKind,
76    pub phase: PointerPhase,
77    pub position: Point,
78    pub global_position: Point,
79    pub buttons: PointerButtons,
80    /// Tracks whether this event has been consumed by a handler.
81    /// Shared via Rc<Cell> so consumption can be tracked across copies.
82    consumed: Rc<Cell<bool>>,
83}
84
85impl PointerEvent {
86    pub fn new(kind: PointerEventKind, position: Point, global_position: Point) -> Self {
87        Self {
88            id: 0,
89            kind,
90            phase: match kind {
91                PointerEventKind::Down => PointerPhase::Start,
92                PointerEventKind::Move => PointerPhase::Move,
93                PointerEventKind::Up => PointerPhase::End,
94                PointerEventKind::Cancel => PointerPhase::Cancel,
95            },
96            position,
97            global_position,
98            buttons: PointerButtons::NONE,
99            consumed: Rc::new(Cell::new(false)),
100        }
101    }
102
103    /// Set the buttons state for this event
104    pub fn with_buttons(mut self, buttons: PointerButtons) -> Self {
105        self.buttons = buttons;
106        self
107    }
108
109    /// Mark this event as consumed, preventing other handlers from processing it.
110    ///
111    /// Example: Scroll gestures consume events once dragging starts to prevent
112    /// child buttons from firing clicks.
113    pub fn consume(&self) {
114        self.consumed.set(true);
115    }
116
117    /// Check if this event has been consumed by another handler.
118    ///
119    /// Handlers should check this before processing events. For example,
120    /// clickable should not fire if the event was consumed by a scroll gesture.
121    pub fn is_consumed(&self) -> bool {
122        self.consumed.get()
123    }
124
125    /// Creates a copy of this event with a new local position, sharing the consumption state.
126    pub fn copy_with_local_position(&self, position: Point) -> Self {
127        Self {
128            id: self.id,
129            kind: self.kind,
130            phase: self.phase,
131            position,
132            global_position: self.global_position,
133            buttons: self.buttons,
134            consumed: self.consumed.clone(),
135        }
136    }
137}