Skip to main content

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} }