Skip to main content

blitz_traits/
events.rs

1//! Types to represent UI and DOM events
2
3use std::str::FromStr;
4use std::sync::Arc;
5
6use atomic_refcell::AtomicRefCell;
7use bitflags::bitflags;
8use keyboard_types::{Code, Key, Location, Modifiers};
9use smol_str::SmolStr;
10
11use crate::NodeId;
12
13#[derive(Default)]
14pub struct EventState {
15    cancelled: bool,
16    propagation_stopped: bool,
17    redraw_requested: bool,
18}
19impl EventState {
20    #[inline(always)]
21    pub fn prevent_default(&mut self) {
22        self.cancelled = true;
23    }
24
25    #[inline(always)]
26    pub fn stop_propagation(&mut self) {
27        self.propagation_stopped = true;
28    }
29
30    #[inline(always)]
31    pub fn request_redraw(&mut self) {
32        self.redraw_requested = true;
33    }
34
35    #[inline(always)]
36    pub fn is_cancelled(&self) -> bool {
37        self.cancelled
38    }
39
40    #[inline(always)]
41    pub fn propagation_is_stopped(&self) -> bool {
42        self.propagation_stopped
43    }
44
45    #[inline(always)]
46    pub fn redraw_is_requested(&self) -> bool {
47        self.redraw_requested
48    }
49
50    #[inline]
51    pub fn merge(&self, other: &EventState) -> EventState {
52        EventState {
53            cancelled: self.cancelled | other.cancelled,
54            propagation_stopped: self.propagation_stopped | other.propagation_stopped,
55            redraw_requested: self.redraw_requested | other.redraw_requested,
56        }
57    }
58}
59
60#[derive(Debug, Clone)]
61#[repr(u8)]
62pub enum UiEvent {
63    PointerMove(BlitzPointerEvent),
64    PointerUp(BlitzPointerEvent),
65    PointerDown(BlitzPointerEvent),
66    PointerCancel(BlitzPointerEvent),
67    Wheel(BlitzWheelEvent),
68    KeyUp(BlitzKeyEvent),
69    KeyDown(BlitzKeyEvent),
70    Ime(BlitzImeEvent),
71    AppleStandardKeybinding(SmolStr),
72}
73impl UiEvent {
74    pub fn discriminant(&self) -> u8 {
75        // SAFETY: Because `Self` is marked `repr(u8)`, its layout is a `repr(C)` `union`
76        // between `repr(C)` structs, each of which has the `u8` discriminant as its first
77        // field, so we can read the discriminant without offsetting the pointer.
78        // See: https://doc.rust-lang.org/stable/std/mem/fn.discriminant.html#accessing-the-numeric-value-of-the-discriminant
79        unsafe { *<*const _>::from(self).cast::<u8>() }
80    }
81}
82
83#[derive(Debug, Clone)]
84pub struct DomEvent {
85    pub target: NodeId,
86    /// Which is true if the event bubbles up through the DOM tree.
87    pub bubbles: bool,
88    /// which is true if the event can be canceled.
89    pub cancelable: bool,
90
91    pub data: DomEventData,
92    pub request_redraw: bool,
93}
94
95impl DomEvent {
96    pub fn new(target: NodeId, data: DomEventData) -> Self {
97        Self {
98            target,
99            bubbles: data.bubbles(),
100            cancelable: data.cancelable(),
101            data,
102            request_redraw: false,
103        }
104    }
105
106    /// Returns the name of the event ("click", "mouseover", "keypress", etc)
107    pub fn name(&self) -> &'static str {
108        self.data.name()
109    }
110}
111
112#[derive(Copy, Clone, Debug, PartialEq)]
113#[repr(u8)]
114pub enum DomEventKind {
115    PointerMove,
116    PointerDown,
117    PointerUp,
118    PointerCancel,
119    PointerEnter,
120    PointerLeave,
121    PointerOver,
122    PointerOut,
123
124    MouseMove,
125    MouseDown,
126    MouseUp,
127    MouseEnter,
128    MouseLeave,
129    MouseOver,
130    MouseOut,
131
132    TouchStart,
133    TouchMove,
134    TouchEnd,
135    TouchCancel,
136
137    Scroll,
138    Wheel,
139
140    Click,
141    ContextMenu,
142    DoubleClick,
143
144    KeyPress,
145    KeyDown,
146    KeyUp,
147    Input,
148    Ime,
149
150    Focus,
151    Blur,
152    FocusIn,
153    FocusOut,
154
155    Submit,
156
157    AppleStandardKeybinding,
158}
159impl DomEventKind {
160    pub fn discriminant(self) -> u8 {
161        self as u8
162    }
163}
164impl FromStr for DomEventKind {
165    type Err = ();
166    fn from_str(s: &str) -> Result<Self, ()> {
167        match s.trim_start_matches("on") {
168            "pointermove" => Ok(Self::PointerMove),
169            "pointerdown" => Ok(Self::PointerDown),
170            "pointerup" => Ok(Self::PointerUp),
171            "pointercancel" => Ok(Self::PointerCancel),
172            "pointerenter" => Ok(Self::PointerEnter),
173            "pointerleave" => Ok(Self::PointerLeave),
174            "pointerover" => Ok(Self::PointerOver),
175            "pointerout" => Ok(Self::PointerOut),
176
177            "mousemove" => Ok(Self::MouseMove),
178            "mousedown" => Ok(Self::MouseDown),
179            "mouseup" => Ok(Self::MouseUp),
180            "mouseenter" => Ok(Self::MouseEnter),
181            "mouseleave" => Ok(Self::MouseLeave),
182            "mouseover" => Ok(Self::MouseOver),
183            "mouseout" => Ok(Self::MouseOut),
184
185            "touchstart" => Ok(Self::TouchStart),
186            "touchmove" => Ok(Self::TouchMove),
187            "touchend" => Ok(Self::TouchEnd),
188            "touchcancel" => Ok(Self::TouchCancel),
189
190            "scroll" => Ok(Self::Scroll),
191            "wheel" => Ok(Self::Wheel),
192
193            "click" => Ok(Self::Click),
194            "contextmenu" => Ok(Self::ContextMenu),
195            "dblclick" => Ok(Self::DoubleClick),
196
197            "keypress" => Ok(Self::KeyPress),
198            "keydown" => Ok(Self::KeyDown),
199            "keyup" => Ok(Self::KeyUp),
200            "input" => Ok(Self::Input),
201            "composition" => Ok(Self::Ime),
202
203            "focus" => Ok(Self::Focus),
204            "blur" => Ok(Self::Blur),
205            "focusin" => Ok(Self::FocusIn),
206            "focusout" => Ok(Self::FocusOut),
207            _ => Err(()),
208        }
209    }
210}
211
212#[derive(Debug, Clone)]
213#[repr(u8)]
214pub enum DomEventData {
215    PointerMove(BlitzPointerEvent),
216    PointerDown(BlitzPointerEvent),
217    PointerUp(BlitzPointerEvent),
218    PointerCancel(BlitzPointerEvent),
219    PointerEnter(BlitzPointerEvent),
220    PointerLeave(BlitzPointerEvent),
221    PointerOver(BlitzPointerEvent),
222    PointerOut(BlitzPointerEvent),
223
224    MouseMove(BlitzPointerEvent),
225    MouseDown(BlitzPointerEvent),
226    MouseUp(BlitzPointerEvent),
227    MouseEnter(BlitzPointerEvent),
228    MouseLeave(BlitzPointerEvent),
229    MouseOver(BlitzPointerEvent),
230    MouseOut(BlitzPointerEvent),
231
232    TouchStart(BlitzPointerEvent),
233    TouchMove(BlitzPointerEvent),
234    TouchEnd(BlitzPointerEvent),
235    TouchCancel(BlitzPointerEvent),
236
237    Scroll(BlitzScrollEvent),
238    Wheel(BlitzWheelEvent),
239
240    Click(BlitzPointerEvent),
241    ContextMenu(BlitzPointerEvent),
242    DoubleClick(BlitzPointerEvent),
243
244    KeyPress(BlitzKeyEvent),
245    KeyDown(BlitzKeyEvent),
246    KeyUp(BlitzKeyEvent),
247    Input(BlitzInputEvent),
248    Ime(BlitzImeEvent),
249
250    Focus(BlitzFocusEvent),
251    Blur(BlitzFocusEvent),
252    FocusIn(BlitzFocusEvent),
253    FocusOut(BlitzFocusEvent),
254
255    /// A form is about to be submitted.
256    ///
257    /// Dispatched before the submission itself, and cancelable, which is the
258    /// whole point: a single-page application handles its own forms and calls
259    /// `preventDefault`. Without this event the submission was a default
260    /// action nothing could see or stop, so every framework form in every
261    /// application navigated the page instead of running its handler.
262    Submit(BlitzSubmitEvent),
263
264    AppleStandardKeybinding(SmolStr),
265}
266impl DomEventData {
267    pub fn discriminant(&self) -> u8 {
268        // SAFETY: Because `Self` is marked `repr(u8)`, its layout is a `repr(C)` `union`
269        // between `repr(C)` structs, each of which has the `u8` discriminant as its first
270        // field, so we can read the discriminant without offsetting the pointer.
271        // See: https://doc.rust-lang.org/stable/std/mem/fn.discriminant.html#accessing-the-numeric-value-of-the-discriminant
272        unsafe { *<*const _>::from(self).cast::<u8>() }
273    }
274}
275
276impl DomEventData {
277    pub fn name(&self) -> &'static str {
278        match self {
279            Self::PointerMove { .. } => "pointermove",
280            Self::PointerDown { .. } => "pointerdown",
281            Self::PointerUp { .. } => "pointerup",
282            Self::PointerCancel { .. } => "pointercancel",
283            Self::PointerEnter { .. } => "pointerenter",
284            Self::PointerLeave { .. } => "pointerleave",
285            Self::PointerOver { .. } => "pointerover",
286            Self::PointerOut { .. } => "pointerout",
287
288            Self::MouseMove { .. } => "mousemove",
289            Self::MouseDown { .. } => "mousedown",
290            Self::MouseUp { .. } => "mouseup",
291            Self::MouseEnter { .. } => "mouseenter",
292            Self::MouseLeave { .. } => "mouseleave",
293            Self::MouseOver { .. } => "mouseover",
294            Self::MouseOut { .. } => "mouseout",
295
296            Self::TouchStart { .. } => "touchstart",
297            Self::TouchMove { .. } => "touchmove",
298            Self::TouchEnd { .. } => "touchend",
299            Self::TouchCancel { .. } => "touchcancel",
300
301            Self::Scroll { .. } => "scroll",
302            Self::Wheel { .. } => "wheel",
303
304            Self::Click { .. } => "click",
305            Self::ContextMenu { .. } => "contextmenu",
306            Self::DoubleClick { .. } => "dblclick",
307
308            Self::KeyPress { .. } => "keypress",
309            Self::KeyDown { .. } => "keydown",
310            Self::KeyUp { .. } => "keyup",
311            Self::Input { .. } => "input",
312            Self::Ime { .. } => "composition",
313
314            Self::Focus { .. } => "focus",
315            Self::Blur { .. } => "blur",
316            Self::FocusIn { .. } => "focusin",
317            Self::FocusOut { .. } => "focusout",
318
319            Self::Submit { .. } => "submit",
320
321            Self::AppleStandardKeybinding { .. } => "applekeybinding",
322        }
323    }
324
325    pub fn kind(&self) -> DomEventKind {
326        match self {
327            Self::PointerMove { .. } => DomEventKind::PointerMove,
328            Self::PointerDown { .. } => DomEventKind::PointerDown,
329            Self::PointerUp { .. } => DomEventKind::PointerUp,
330            Self::PointerCancel { .. } => DomEventKind::PointerCancel,
331            Self::PointerEnter { .. } => DomEventKind::PointerEnter,
332            Self::PointerLeave { .. } => DomEventKind::PointerLeave,
333            Self::PointerOver { .. } => DomEventKind::PointerOver,
334            Self::PointerOut { .. } => DomEventKind::PointerOut,
335
336            Self::MouseMove { .. } => DomEventKind::MouseMove,
337            Self::MouseDown { .. } => DomEventKind::MouseDown,
338            Self::MouseUp { .. } => DomEventKind::MouseUp,
339            Self::MouseEnter { .. } => DomEventKind::MouseEnter,
340            Self::MouseLeave { .. } => DomEventKind::MouseLeave,
341            Self::MouseOver { .. } => DomEventKind::MouseOver,
342            Self::MouseOut { .. } => DomEventKind::MouseOut,
343
344            Self::TouchStart { .. } => DomEventKind::TouchStart,
345            Self::TouchMove { .. } => DomEventKind::TouchMove,
346            Self::TouchEnd { .. } => DomEventKind::TouchEnd,
347            Self::TouchCancel { .. } => DomEventKind::TouchCancel,
348
349            Self::Scroll { .. } => DomEventKind::Scroll,
350            Self::Wheel { .. } => DomEventKind::Wheel,
351
352            Self::Click { .. } => DomEventKind::Click,
353            Self::ContextMenu { .. } => DomEventKind::ContextMenu,
354            Self::DoubleClick { .. } => DomEventKind::DoubleClick,
355
356            Self::KeyPress { .. } => DomEventKind::KeyPress,
357            Self::KeyDown { .. } => DomEventKind::KeyDown,
358            Self::KeyUp { .. } => DomEventKind::KeyUp,
359            Self::Input { .. } => DomEventKind::Input,
360            Self::Ime { .. } => DomEventKind::Ime,
361
362            Self::Focus { .. } => DomEventKind::Focus,
363            Self::Blur { .. } => DomEventKind::Blur,
364            Self::FocusIn { .. } => DomEventKind::FocusIn,
365            Self::FocusOut { .. } => DomEventKind::FocusOut,
366            Self::Submit { .. } => DomEventKind::Submit,
367
368            Self::AppleStandardKeybinding { .. } => DomEventKind::AppleStandardKeybinding,
369        }
370    }
371
372    pub fn cancelable(&self) -> bool {
373        match self {
374            Self::PointerMove { .. } => true,
375            Self::PointerDown { .. } => true,
376            Self::PointerUp { .. } => true,
377            Self::PointerCancel { .. } => false,
378            Self::PointerEnter { .. } => false,
379            Self::PointerLeave { .. } => false,
380            Self::PointerOver { .. } => true,
381            Self::PointerOut { .. } => true,
382
383            Self::MouseMove { .. } => true,
384            Self::MouseDown { .. } => true,
385            Self::MouseUp { .. } => true,
386            Self::MouseEnter { .. } => false,
387            Self::MouseLeave { .. } => false,
388            Self::MouseOver { .. } => true,
389            Self::MouseOut { .. } => true,
390
391            Self::TouchStart { .. } => true,
392            Self::TouchMove { .. } => true,
393            Self::TouchEnd { .. } => true,
394            Self::TouchCancel { .. } => false,
395
396            Self::Scroll { .. } => false,
397            Self::Wheel { .. } => true,
398
399            Self::Click { .. } => true,
400            Self::ContextMenu { .. } => true,
401            Self::DoubleClick { .. } => true,
402
403            Self::KeyDown { .. } => true,
404            Self::KeyUp { .. } => true,
405            Self::KeyPress { .. } => true,
406            Self::Ime { .. } => true,
407            Self::Input { .. } => false,
408
409            Self::Focus { .. } => false,
410            Self::Blur { .. } => false,
411            Self::FocusIn { .. } => false,
412            Self::FocusOut { .. } => false,
413
414            // Bubbles, the way a browser dispatches it: a framework attaches
415            // its handler to the form, and a delegating one attaches to the
416            // document.
417            Self::Submit { .. } => true,
418
419            Self::AppleStandardKeybinding { .. } => true,
420        }
421    }
422
423    pub fn bubbles(&self) -> bool {
424        match self {
425            Self::PointerMove { .. } => true,
426            Self::PointerDown { .. } => true,
427            Self::PointerUp { .. } => true,
428            Self::PointerCancel { .. } => true,
429            Self::PointerEnter { .. } => false,
430            Self::PointerLeave { .. } => false,
431            Self::PointerOver { .. } => true,
432            Self::PointerOut { .. } => true,
433
434            Self::MouseMove { .. } => true,
435            Self::MouseDown { .. } => true,
436            Self::MouseUp { .. } => true,
437            Self::MouseEnter { .. } => false,
438            Self::MouseLeave { .. } => false,
439            Self::MouseOver { .. } => true,
440            Self::MouseOut { .. } => true,
441
442            Self::TouchStart { .. } => true,
443            Self::TouchMove { .. } => true,
444            Self::TouchEnd { .. } => true,
445            Self::TouchCancel { .. } => true,
446
447            Self::Scroll { .. } => false,
448            Self::Wheel { .. } => true,
449
450            Self::Click { .. } => true,
451            Self::ContextMenu { .. } => true,
452            Self::DoubleClick { .. } => true,
453
454            Self::KeyDown { .. } => true,
455            Self::KeyUp { .. } => true,
456            Self::KeyPress { .. } => true,
457            Self::Ime { .. } => true,
458            Self::Input { .. } => true,
459
460            Self::Focus { .. } => false,
461            Self::Blur { .. } => false,
462            Self::FocusIn { .. } => true,
463            Self::FocusOut { .. } => true,
464
465            // Cancelable, which is the entire reason the event exists.
466            Self::Submit { .. } => true,
467
468            Self::AppleStandardKeybinding { .. } => false,
469        }
470    }
471}
472
473#[derive(Debug, Clone, Copy)]
474pub struct HitResult {
475    /// The node_id of the node identified as the hit target
476    pub node_id: NodeId,
477    /// Whether the hit content is text
478    pub is_text: bool,
479    /// The x coordinate of the hit within the hit target's border-box
480    pub x: f32,
481    /// The y coordinate of the hit within the hit target's border-box
482    pub y: f32,
483}
484
485#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
486pub enum BlitzPointerId {
487    Mouse,
488    Pen,
489    Finger(u64),
490}
491
492#[derive(Copy, Clone, Debug)]
493pub struct PointerCoords {
494    pub page_x: f32,
495    pub page_y: f32,
496    pub screen_x: f32,
497    pub screen_y: f32,
498    pub client_x: f32,
499    pub client_y: f32,
500}
501
502#[derive(Copy, Clone, Debug, Default)]
503pub struct PointerDetails {
504    pub pressure: f64, // default 0.5 if buttons pressed else 0.0
505    pub tangential_pressure: f32,
506    pub tilt_x: i8,
507    pub tilt_y: i8,
508    pub twist: u16,
509    pub altitude: f64,
510    pub azimuth: f64,
511}
512
513#[derive(Copy, Clone, Debug, Default)]
514pub struct Point<T> {
515    pub x: T,
516    pub y: T,
517}
518
519#[derive(Clone, Debug)]
520pub struct BlitzPointerEvent {
521    pub id: BlitzPointerId,
522    pub is_primary: bool,
523    pub coords: PointerCoords,
524    pub button: MouseEventButton,
525    pub buttons: MouseEventButtons,
526    pub mods: Modifiers,
527    pub details: PointerDetails,
528    pub element: Point<f32>,
529    /// All pointers that are currently active (pressed) on the surface, including
530    /// the one that triggered this event.
531    ///
532    /// This is shared via [`Arc`] so that it can be cheaply cloned into each
533    /// dispatched event and cheaply mutated as pointers are pressed, moved and
534    /// released. It is primarily used to populate the `touches` list of touch
535    /// events (multi-touch support).
536    ///
537    /// The events stored in this list always have an empty `active_pointers`
538    /// list themselves, to avoid a reference cycle.
539    pub active_pointers: Arc<AtomicRefCell<Vec<BlitzPointerEvent>>>,
540}
541
542impl BlitzPointerEvent {
543    /// A synthetic primary-mouse event at a viewport coordinate.
544    ///
545    /// For callers driving a document programmatically (a test, a debug
546    /// control surface, an agent) rather than translating a real device event.
547    /// The twelve-field literal this replaces is currently hand-rolled in
548    /// `blitz-shell`, `blitz-script` and `dioxus-native-dom`, each with its own
549    /// idea of the defaults.
550    ///
551    /// `screen`, `page` and `client` all take the same value: a synthetic event
552    /// has no window offset or scroll origin to distinguish them. `element` is
553    /// left at the origin because the offset within the target is only known
554    /// after hit-testing, which the document does when the event is dispatched.
555    pub fn at(x: f32, y: f32, buttons: MouseEventButtons) -> Self {
556        Self {
557            id: BlitzPointerId::Mouse,
558            is_primary: true,
559            coords: PointerCoords {
560                page_x: x,
561                page_y: y,
562                screen_x: x,
563                screen_y: y,
564                client_x: x,
565                client_y: y,
566            },
567            button: MouseEventButton::Main,
568            buttons,
569            mods: Modifiers::empty(),
570            details: PointerDetails::default(),
571            element: Point::default(),
572            active_pointers: Default::default(),
573        }
574    }
575
576    #[inline(always)]
577    pub fn is_mouse(&self) -> bool {
578        matches!(self.id, BlitzPointerId::Mouse)
579    }
580    #[inline(always)]
581    pub fn is_pen(&self) -> bool {
582        matches!(self.id, BlitzPointerId::Pen)
583    }
584    #[inline(always)]
585    pub fn is_finger(&self) -> bool {
586        matches!(self.id, BlitzPointerId::Finger(_))
587    }
588
589    #[inline(always)]
590    pub fn page_x(&self) -> f32 {
591        self.coords.page_x
592    }
593    #[inline(always)]
594    pub fn page_y(&self) -> f32 {
595        self.coords.page_y
596    }
597    #[inline(always)]
598    pub fn client_x(&self) -> f32 {
599        self.coords.client_x
600    }
601    #[inline(always)]
602    pub fn client_y(&self) -> f32 {
603        self.coords.client_y
604    }
605    #[inline(always)]
606    pub fn screen_x(&self) -> f32 {
607        self.coords.screen_x
608    }
609    #[inline(always)]
610    pub fn screen_y(&self) -> f32 {
611        self.coords.screen_y
612    }
613
614    #[inline(always)]
615    pub fn element_x(&self) -> f32 {
616        self.element.x
617    }
618    #[inline(always)]
619    pub fn element_y(&self) -> f32 {
620        self.element.y
621    }
622}
623
624#[derive(Clone, Debug)]
625pub enum BlitzWheelDelta {
626    Lines(f64, f64),
627    Pixels(f64, f64),
628}
629
630/// Gesture phase supplied by a wheel input source.
631#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
632pub enum BlitzWheelPhase {
633    Started,
634    /// Movement, including discrete wheel input with no gesture boundaries.
635    #[default]
636    Moved,
637    Ended,
638    Cancelled,
639    /// Inertial movement, when the input source distinguishes it from contact.
640    Momentum,
641}
642
643#[derive(Clone, Debug)]
644pub struct BlitzWheelEvent {
645    pub delta: BlitzWheelDelta,
646    /// Preserved through UI and DOM dispatch, independently of delta units.
647    pub phase: BlitzWheelPhase,
648    pub coords: PointerCoords,
649    pub buttons: MouseEventButtons,
650    pub mods: Modifiers,
651    pub element: Point<f32>,
652}
653
654impl BlitzWheelEvent {
655    #[inline(always)]
656    pub fn page_x(&self) -> f32 {
657        self.coords.page_x
658    }
659    #[inline(always)]
660    pub fn page_y(&self) -> f32 {
661        self.coords.page_y
662    }
663    #[inline(always)]
664    pub fn client_x(&self) -> f32 {
665        self.coords.client_x
666    }
667    #[inline(always)]
668    pub fn client_y(&self) -> f32 {
669        self.coords.client_y
670    }
671    #[inline(always)]
672    pub fn screen_x(&self) -> f32 {
673        self.coords.screen_x
674    }
675    #[inline(always)]
676    pub fn screen_y(&self) -> f32 {
677        self.coords.screen_y
678    }
679    #[inline(always)]
680    pub fn element_x(&self) -> f32 {
681        self.element.x
682    }
683    #[inline(always)]
684    pub fn element_y(&self) -> f32 {
685        self.element.y
686    }
687}
688
689#[derive(Clone, Debug)]
690pub struct BlitzScrollEvent {
691    pub scroll_top: f64,
692    pub scroll_left: f64,
693    pub scroll_width: i32,
694    pub scroll_height: i32,
695    pub client_width: i32,
696    pub client_height: i32,
697}
698
699// struct PointerInputState {
700//     id: BlitzPointerId,
701//     pointer_down_x: f32,
702//     pointer_down_y: f32,
703//     pointer_down_time: Option<Instant>,
704//     click_count: u16,
705// }
706
707// struct PointersInputState {
708//     initial_finger_active: bool,
709//     mouse: Option<PointerInputState>,
710//     fingers: Vec<PointerInputState>,
711// }
712
713bitflags! {
714    /// The buttons property indicates which buttons are pressed on the mouse
715    /// (or other input device) when a mouse event is triggered.
716    ///
717    /// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons)
718    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
719    pub struct MouseEventButtons: u8 {
720        /// 0: No button or un-initialized
721        const None = 0b0000_0000;
722        /// 1: Primary button (usually the left button)
723        const Primary = 0b0000_0001;
724        /// 2: Secondary button (usually the right button)
725        const Secondary = 0b0000_0010;
726        /// 4: Auxiliary button (usually the mouse wheel button or middle button)
727        const Auxiliary = 0b0000_0100;
728        /// 8: 4th button (typically the "Browser Back" button)
729        const Fourth = 0b0000_1000;
730        /// 16: 5th button (typically the "Browser Forward" button)
731        const Fifth = 0b0001_0000;
732    }
733}
734
735impl Default for MouseEventButtons {
736    fn default() -> Self {
737        Self::None
738    }
739}
740
741impl From<MouseEventButton> for MouseEventButtons {
742    fn from(value: MouseEventButton) -> Self {
743        match value {
744            MouseEventButton::Main => Self::Primary,
745            MouseEventButton::Auxiliary => Self::Auxiliary,
746            MouseEventButton::Secondary => Self::Secondary,
747            MouseEventButton::Fourth => Self::Fourth,
748            MouseEventButton::Fifth => Self::Fifth,
749        }
750    }
751}
752
753/// The button property indicates which button was pressed
754/// on the mouse to trigger the event.
755///
756/// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button)
757#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
758pub enum MouseEventButton {
759    /// Main button pressed, usually the left button or the un-initialized state
760    #[default]
761    Main = 0,
762    /// Auxiliary button pressed, usually the wheel button or the middle button (if present)
763    Auxiliary = 1,
764    /// Secondary button pressed, usually the right button
765    Secondary = 2,
766    /// Fourth button, typically the Browser Back button
767    Fourth = 3,
768    /// Fifth button, typically the Browser Forward button
769    Fifth = 4,
770}
771
772#[derive(Copy, Clone, Debug, PartialEq, Eq)]
773pub enum KeyState {
774    Pressed,
775    Released,
776}
777
778impl KeyState {
779    pub fn is_pressed(self) -> bool {
780        matches!(self, Self::Pressed)
781    }
782}
783
784#[derive(Clone, Debug)]
785pub struct BlitzKeyEvent {
786    pub key: Key,
787    pub code: Code,
788    pub modifiers: Modifiers,
789    pub location: Location,
790    pub is_auto_repeating: bool,
791    pub is_composing: bool,
792    pub state: KeyState,
793    pub text: Option<SmolStr>,
794}
795
796#[derive(Clone, Debug)]
797pub struct BlitzInputEvent {
798    pub value: String,
799}
800
801#[derive(Clone, Debug)]
802pub struct BlitzFocusEvent {
803    pub related_target: Option<NodeId>,
804}
805
806// Preserve the former unit-value spelling for callers with no related target.
807#[allow(non_upper_case_globals)]
808pub const BlitzFocusEvent: BlitzFocusEvent = BlitzFocusEvent {
809    related_target: None,
810};
811
812/// A form submission, before it happens.
813///
814/// Carries the control that triggered it, because that is what a handler
815/// reads to tell one submit button from another, and what the submission
816/// itself needs in order to include the submitter's own name and value.
817#[derive(Clone, Debug)]
818pub struct BlitzSubmitEvent {
819    /// The form being submitted.
820    pub form: u64,
821    /// The control that triggered it. Equal to the form when the submission
822    /// came from a key press rather than from a button.
823    pub submitter: u64,
824}
825
826/// Copy of Winit IME event to avoid lower-level Blitz crates depending on winit
827#[derive(Debug, Clone, PartialEq, Eq, Hash)]
828pub enum BlitzImeEvent {
829    /// Notifies when the IME was enabled.
830    ///
831    /// After getting this event you could receive [`Preedit`][Self::Preedit] and
832    /// [`Commit`][Self::Commit] events. You should also start performing IME related requests
833    /// like `Window::set_ime_cursor_area`.
834    Enabled,
835
836    /// Notifies when a new composing text should be set at the cursor position.
837    ///
838    /// The value represents a pair of the preedit string and the cursor begin position and end
839    /// position. When it's `None`, the cursor should be hidden. When `String` is an empty string
840    /// this indicates that preedit was cleared.
841    ///
842    /// The cursor position is byte-wise indexed, assuming UTF-8.
843    Preedit(String, Option<(usize, usize)>),
844
845    /// Notifies when text should be inserted into the editor widget.
846    ///
847    /// Right before this event winit will send empty [`Self::Preedit`] event.
848    Commit(String),
849
850    /// Delete text surrounding the cursor or selection.
851    ///
852    /// This event does not affect either the pre-edit string.
853    /// This means that the application must first remove the pre-edit,
854    /// then execute the deletion, then insert the removed text back.
855    ///
856    /// This event assumes text is stored in UTF-8.
857    DeleteSurrounding {
858        /// Bytes to remove before the selection
859        before_bytes: usize,
860        /// Bytes to remove after the selection
861        after_bytes: usize,
862    },
863
864    /// Notifies when the IME was disabled.
865    ///
866    /// After receiving this event you won't get any more [`Preedit`][Self::Preedit] or
867    /// [`Commit`][Self::Commit] events until the next [`Enabled`][Self::Enabled] event. You should
868    /// also stop issuing IME related requests like `Window::set_ime_cursor_area` and clear
869    /// pending preedit text.
870    Disabled,
871}