devela/ui/event/pointer/button.rs
1// devela::ui::event::pointer::button
2//
3//! Defines [`EventButton`], [`EventButtons`], [`EventButtonState`].
4//
5
6use crate::{_impl_init, impl_trait, is, set};
7
8/* definitions */
9
10#[doc = crate::_tags!(event interaction member)]
11/// A normalized pressable pointer/mouse button.
12#[doc = crate::_doc_meta!{
13 location("ui/event"),
14 test_size_of(EventButton = 2|16; niche Option),
15}]
16/// `Left`, `Middle`, and `Right` represent the three primary conventional buttons.
17/// `X1..X5` represent additional auxiliary button slots when a backend can report them.
18///
19/// These variants name normalized button slots, not guaranteed user actions.
20/// For example, `X1` is often used as “back”, `X2` as “forward”, and on
21/// Pointer Events a pen eraser may be reported through an auxiliary slot.
22#[repr(u8)]
23#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
24pub enum EventButton {
25 /// Left mouse button.
26 #[default]
27 Left,
28 /// Right mouse button.
29 Right,
30 /// Middle mouse button (usually the scroll wheel button).
31 Middle,
32 /// The first auxiliary mouse button, commonly "back".
33 X1,
34 /// The second auxiliary mouse button, commonly "forward".
35 X2,
36 /// The third auxiliary mouse button.
37 X3,
38 /// The fourth auxiliary mouse button.
39 X4,
40 /// The fifth auxiliary mouse button.
41 X5,
42 /// Additional buttons (e.g., side buttons on advanced mice).
43 Other(u8),
44}
45_impl_init! { Self::Left => EventButton }
46impl EventButton {
47 /// Returns some button as long as it's not 0.
48 pub const fn new(number: u8) -> Option<Self> {
49 match number {
50 1 => Some(Self::Left),
51 2 => Some(Self::Middle),
52 3 => Some(Self::Right),
53 _ => Some(Self::Other(number)),
54 }
55 }
56
57 /// Returns the button represented by the mask, if exactly one button is set.
58 #[inline(always)]
59 pub const fn from_one_bit_mask(mask: EventButtons) -> Option<EventButton> {
60 match mask.bits() {
61 1 => Some(EventButton::Left),
62 2 => Some(EventButton::Right),
63 4 => Some(EventButton::Middle),
64 8 => Some(EventButton::X1),
65 16 => Some(EventButton::X2),
66 32 => Some(EventButton::X3),
67 64 => Some(EventButton::X4),
68 128 => Some(EventButton::X5),
69 _ => None,
70 }
71 }
72 /// Returns this button as a held-button mask, if it has a normalized role.
73 #[inline(always)]
74 pub const fn to_mask(self) -> EventButtons {
75 match self {
76 Self::Left => EventButtons::new().with(EventButtons::LEFT),
77 Self::Right => EventButtons::new().with(EventButtons::RIGHT),
78 Self::Middle => EventButtons::new().with(EventButtons::MIDDLE),
79 Self::X1 => EventButtons::new().with(EventButtons::X1),
80 Self::X2 => EventButtons::new().with(EventButtons::X2),
81 Self::X3 => EventButtons::new().with(EventButtons::X3),
82 Self::X4 => EventButtons::new().with(EventButtons::X4),
83 Self::X5 => EventButtons::new().with(EventButtons::X5),
84 Self::Other(_) => EventButtons::new(),
85 }
86 }
87}
88
89#[doc = crate::_tags!(event interaction)]
90/// Represents the state of a button.
91#[doc = crate::_doc_meta!{location("ui/event")}]
92#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
93pub enum EventButtonState {
94 /// The button was pressed.
95 ///
96 /// This is the default.
97 #[default]
98 Pressed,
99 /// The button was released.
100 Released,
101 /// The pointer moved without a button press/release.
102 Moved,
103}
104_impl_init! { Self::Pressed => EventButtonState }
105
106set! {
107 #[doc = crate::_tags!(event interaction set)]
108 /// A semantic bitmask of currently held pressable buttons.
109 #[doc = crate::_doc_meta!{
110 location("ui/event"),
111 test_size_of(EventButtons = 1|8), // option = 2|16
112 }]
113 ///
114 /// The bits represent normalized button roles, not raw backend button numbers.
115 ///
116 /// This means:
117 /// - `LEFT`, `RIGHT`, and `MIDDLE` are the three primary buttons.
118 /// - `X1..X5` are additional auxiliary buttons when a backend can report them.
119 /// - wheel motion is **not** represented here; it belongs in
120 /// [`EventWheel`][crate::EventWheel].
121 ///
122 /// Unsupported buttons are left cleared.
123 ///
124 /// # Backend notes
125 /// - **Web** can naturally carry `LEFT`, `RIGHT`, `MIDDLE`, `X1`, and `X2`.
126 /// - **X11 core** currently sets only `LEFT`, `RIGHT`, and `MIDDLE`;
127 /// buttons 4 and 5 are treated as wheel input in the current backend.
128 /// - **Terminal SGR** reports primary buttons, wheel motion, and some
129 /// extended button encodings. This library normalizes the first supported
130 /// non-primary button encodings into `X1..X5` when unambiguous.
131 /// - Other backends may populate only the subset they can observe.
132 ///
133 /// This type is semantic and cross-platform.
134 /// Backend-specific numbering should be translated at the backend edge.
135 //
136 // Firefox may consume browser Back/Forward mouse buttons before dispatching
137 // DOM mouse events, so button=3/4 and buttons=8/16 may never reach web code.
138 // WAIT: [firefox-back-forward-buttons](https://bugzilla.mozilla.org/show_bug.cgi?id=1933746)
139 pub struct EventButtons(u8) {
140 /// The primary left button.
141 LEFT = 0;
142 /// The primary right button.
143 RIGHT = 1;
144 /// The primary middle button.
145 MIDDLE = 2;
146 /// The first auxiliary button, often “back”.
147 X1 = 3;
148 /// The second auxiliary button, often “forward”.
149 X2 = 4;
150 /// The third auxiliary button, when available.
151 X3 = 5;
152 /// The fourth auxiliary button, when available.
153 X4 = 6;
154 /// The fifth auxiliary button, when available.
155 X5 = 7;
156 }
157 traits(!Debug);
158}
159impl_trait! { fmt::Debug for EventButtons |self, f| {
160 let l = is![self.has_left(), "L", "-"];
161 let r = is![self.has_right(), "R", "-"];
162 let m = is![self.has_middle(), "M", "-"];
163 let x1 = is![self.has_x1(), "1", "-"];
164 let x2 = is![self.has_x2(), "2", "-"];
165 let x3 = is![self.has_x3(), "3", "-"];
166 let x4 = is![self.has_x4(), "4", "-"];
167 let x5 = is![self.has_x5(), "5", "-"];
168 write![f, "{l}{r}{m}{x1}{x2}{x3}{x4}{x5}"]
169} }