blitz_traits/
events.rs

1//! Types to represent UI and DOM events
2
3use std::str::FromStr;
4
5use bitflags::bitflags;
6use keyboard_types::{Code, Key, Location, Modifiers};
7use smol_str::SmolStr;
8
9#[derive(Default)]
10pub struct EventState {
11    cancelled: bool,
12    propagation_stopped: bool,
13    redraw_requested: bool,
14}
15impl EventState {
16    #[inline(always)]
17    pub fn prevent_default(&mut self) {
18        self.cancelled = true;
19    }
20
21    #[inline(always)]
22    pub fn stop_propagation(&mut self) {
23        self.propagation_stopped = true;
24    }
25
26    #[inline(always)]
27    pub fn request_redraw(&mut self) {
28        self.redraw_requested = true;
29    }
30
31    #[inline(always)]
32    pub fn is_cancelled(&self) -> bool {
33        self.cancelled
34    }
35
36    #[inline(always)]
37    pub fn propagation_is_stopped(&self) -> bool {
38        self.propagation_stopped
39    }
40
41    #[inline(always)]
42    pub fn redraw_is_requested(&self) -> bool {
43        self.redraw_requested
44    }
45}
46
47#[derive(Debug, Clone)]
48#[repr(u8)]
49pub enum UiEvent {
50    MouseMove(BlitzMouseButtonEvent),
51    MouseUp(BlitzMouseButtonEvent),
52    MouseDown(BlitzMouseButtonEvent),
53    KeyUp(BlitzKeyEvent),
54    KeyDown(BlitzKeyEvent),
55    Ime(BlitzImeEvent),
56}
57impl UiEvent {
58    pub fn discriminant(&self) -> u8 {
59        // SAFETY: Because `Self` is marked `repr(u8)`, its layout is a `repr(C)` `union`
60        // between `repr(C)` structs, each of which has the `u8` discriminant as its first
61        // field, so we can read the discriminant without offsetting the pointer.
62        // See: https://doc.rust-lang.org/stable/std/mem/fn.discriminant.html#accessing-the-numeric-value-of-the-discriminant
63        unsafe { *<*const _>::from(self).cast::<u8>() }
64    }
65}
66
67#[derive(Debug, Clone)]
68pub struct DomEvent {
69    pub target: usize,
70    /// Which is true if the event bubbles up through the DOM tree.
71    pub bubbles: bool,
72    /// which is true if the event can be canceled.
73    pub cancelable: bool,
74
75    pub data: DomEventData,
76    pub request_redraw: bool,
77}
78
79impl DomEvent {
80    pub fn new(target: usize, data: DomEventData) -> Self {
81        Self {
82            target,
83            bubbles: data.bubbles(),
84            cancelable: data.cancelable(),
85            data,
86            request_redraw: false,
87        }
88    }
89
90    /// Returns the name of the event ("click", "mouseover", "keypress", etc)
91    pub fn name(&self) -> &'static str {
92        self.data.name()
93    }
94}
95
96#[derive(Copy, Clone, Debug, PartialEq)]
97#[repr(u8)]
98pub enum DomEventKind {
99    MouseMove,
100    MouseDown,
101    MouseUp,
102    Click,
103    KeyPress,
104    KeyDown,
105    KeyUp,
106    Input,
107    Ime,
108}
109impl DomEventKind {
110    pub fn discriminant(self) -> u8 {
111        self as u8
112    }
113}
114impl FromStr for DomEventKind {
115    type Err = ();
116    fn from_str(s: &str) -> Result<Self, ()> {
117        match s.trim_start_matches("on") {
118            "mousemove" => Ok(Self::MouseMove),
119            "mousedown" => Ok(Self::MouseDown),
120            "mouseup" => Ok(Self::MouseUp),
121            "click" => Ok(Self::Click),
122            "keypress" => Ok(Self::KeyPress),
123            "keydown" => Ok(Self::KeyDown),
124            "keyup" => Ok(Self::KeyUp),
125            "input" => Ok(Self::Input),
126            "composition" => Ok(Self::Ime),
127            _ => Err(()),
128        }
129    }
130}
131
132#[derive(Debug, Clone)]
133#[repr(u8)]
134pub enum DomEventData {
135    MouseMove(BlitzMouseButtonEvent),
136    MouseDown(BlitzMouseButtonEvent),
137    MouseUp(BlitzMouseButtonEvent),
138    Click(BlitzMouseButtonEvent),
139    KeyPress(BlitzKeyEvent),
140    KeyDown(BlitzKeyEvent),
141    KeyUp(BlitzKeyEvent),
142    Input(BlitzInputEvent),
143    Ime(BlitzImeEvent),
144}
145impl DomEventData {
146    pub fn discriminant(&self) -> u8 {
147        // SAFETY: Because `Self` is marked `repr(u8)`, its layout is a `repr(C)` `union`
148        // between `repr(C)` structs, each of which has the `u8` discriminant as its first
149        // field, so we can read the discriminant without offsetting the pointer.
150        // See: https://doc.rust-lang.org/stable/std/mem/fn.discriminant.html#accessing-the-numeric-value-of-the-discriminant
151        unsafe { *<*const _>::from(self).cast::<u8>() }
152    }
153}
154
155impl DomEventData {
156    pub fn name(&self) -> &'static str {
157        match self {
158            Self::MouseMove { .. } => "mousemove",
159            Self::MouseDown { .. } => "mousedown",
160            Self::MouseUp { .. } => "mouseup",
161            Self::Click { .. } => "click",
162            Self::KeyPress { .. } => "keypress",
163            Self::KeyDown { .. } => "keydown",
164            Self::KeyUp { .. } => "keyup",
165            Self::Input { .. } => "input",
166            Self::Ime { .. } => "composition",
167        }
168    }
169
170    pub fn kind(&self) -> DomEventKind {
171        match self {
172            Self::MouseMove { .. } => DomEventKind::MouseMove,
173            Self::MouseDown { .. } => DomEventKind::MouseDown,
174            Self::MouseUp { .. } => DomEventKind::MouseUp,
175            Self::Click { .. } => DomEventKind::Click,
176            Self::KeyPress { .. } => DomEventKind::KeyPress,
177            Self::KeyDown { .. } => DomEventKind::KeyDown,
178            Self::KeyUp { .. } => DomEventKind::KeyUp,
179            Self::Input { .. } => DomEventKind::Input,
180            Self::Ime { .. } => DomEventKind::Ime,
181        }
182    }
183
184    pub fn cancelable(&self) -> bool {
185        match self {
186            Self::MouseMove { .. } => true,
187            Self::MouseDown { .. } => true,
188            Self::MouseUp { .. } => true,
189            Self::Click { .. } => true,
190            Self::KeyDown { .. } => true,
191            Self::KeyUp { .. } => true,
192            Self::KeyPress { .. } => true,
193            Self::Ime { .. } => true,
194            Self::Input { .. } => false,
195        }
196    }
197
198    pub fn bubbles(&self) -> bool {
199        match self {
200            Self::MouseMove { .. } => true,
201            Self::MouseDown { .. } => true,
202            Self::MouseUp { .. } => true,
203            Self::Click { .. } => true,
204            Self::KeyDown { .. } => true,
205            Self::KeyUp { .. } => true,
206            Self::KeyPress { .. } => true,
207            Self::Ime { .. } => true,
208            Self::Input { .. } => true,
209        }
210    }
211}
212
213#[derive(Debug, Clone, Copy)]
214pub struct HitResult {
215    /// The node_id of the node identified as the hit target
216    pub node_id: usize,
217    /// The x coordinate of the hit within the hit target's border-box
218    pub x: f32,
219    /// The y coordinate of the hit within the hit target's border-box
220    pub y: f32,
221}
222
223#[derive(Clone, Debug)]
224pub struct BlitzMouseButtonEvent {
225    pub x: f32,
226    pub y: f32,
227    pub button: MouseEventButton,
228    pub buttons: MouseEventButtons,
229    pub mods: Modifiers,
230}
231
232bitflags! {
233    /// The buttons property indicates which buttons are pressed on the mouse
234    /// (or other input device) when a mouse event is triggered.
235    ///
236    /// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons)
237    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
238    pub struct MouseEventButtons: u8 {
239        /// 0: No button or un-initialized
240        const None = 0b0000_0000;
241        /// 1: Primary button (usually the left button)
242        const Primary = 0b0000_0001;
243        /// 2: Secondary button (usually the right button)
244        const Secondary = 0b0000_0010;
245        /// 4: Auxiliary button (usually the mouse wheel button or middle button)
246        const Auxiliary = 0b0000_0100;
247        /// 8: 4th button (typically the "Browser Back" button)
248        const Fourth = 0b0000_1000;
249        /// 16: 5th button (typically the "Browser Forward" button)
250        const Fifth = 0b0001_0000;
251    }
252}
253
254impl Default for MouseEventButtons {
255    fn default() -> Self {
256        Self::None
257    }
258}
259
260impl From<MouseEventButton> for MouseEventButtons {
261    fn from(value: MouseEventButton) -> Self {
262        match value {
263            MouseEventButton::Main => Self::Primary,
264            MouseEventButton::Auxiliary => Self::Auxiliary,
265            MouseEventButton::Secondary => Self::Secondary,
266            MouseEventButton::Fourth => Self::Fourth,
267            MouseEventButton::Fifth => Self::Fifth,
268        }
269    }
270}
271
272/// The button property indicates which button was pressed
273/// on the mouse to trigger the event.
274///
275/// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button)
276#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
277pub enum MouseEventButton {
278    /// Main button pressed, usually the left button or the un-initialized state
279    #[default]
280    Main = 0,
281    /// Auxiliary button pressed, usually the wheel button or the middle button (if present)
282    Auxiliary = 1,
283    /// Secondary button pressed, usually the right button
284    Secondary = 2,
285    /// Fourth button, typically the Browser Back button
286    Fourth = 3,
287    /// Fifth button, typically the Browser Forward button
288    Fifth = 4,
289}
290
291#[derive(Copy, Clone, Debug, PartialEq, Eq)]
292pub enum KeyState {
293    Pressed,
294    Released,
295}
296
297impl KeyState {
298    pub fn is_pressed(self) -> bool {
299        matches!(self, Self::Pressed)
300    }
301}
302
303#[derive(Clone, Debug)]
304pub struct BlitzKeyEvent {
305    pub key: Key,
306    pub code: Code,
307    pub modifiers: Modifiers,
308    pub location: Location,
309    pub is_auto_repeating: bool,
310    pub is_composing: bool,
311    pub state: KeyState,
312    pub text: Option<SmolStr>,
313}
314
315#[derive(Clone, Debug)]
316pub struct BlitzInputEvent {
317    pub value: String,
318}
319
320/// Copy of Winit IME event to avoid lower-level Blitz crates depending on winit
321#[derive(Debug, Clone, PartialEq, Eq, Hash)]
322pub enum BlitzImeEvent {
323    /// Notifies when the IME was enabled.
324    ///
325    /// After getting this event you could receive [`Preedit`][Self::Preedit] and
326    /// [`Commit`][Self::Commit] events.
327    Enabled,
328
329    /// Notifies when a new composing text should be set at the cursor position.
330    ///
331    /// The value represents a pair of the preedit string and the cursor begin position and end
332    /// position. When it's `None`, the cursor should be hidden. When `String` is an empty string
333    /// this indicates that preedit was cleared.
334    ///
335    /// The cursor position is byte-wise indexed.
336    Preedit(String, Option<(usize, usize)>),
337
338    /// Notifies when text should be inserted into the editor widget.
339    ///
340    /// Right before this event winit will send empty [`Self::Preedit`] event.
341    Commit(String),
342
343    /// Notifies when the IME was disabled.
344    ///
345    /// After receiving this event you won't get any more [`Preedit`][Self::Preedit] or
346    /// [`Commit`][Self::Commit] events until the next [`Enabled`][Self::Enabled] event.
347    Disabled,
348}