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#[derive(Clone, Debug)]
631pub struct BlitzWheelEvent {
632    pub delta: BlitzWheelDelta,
633    pub coords: PointerCoords,
634    pub buttons: MouseEventButtons,
635    pub mods: Modifiers,
636    pub element: Point<f32>,
637}
638
639impl BlitzWheelEvent {
640    #[inline(always)]
641    pub fn page_x(&self) -> f32 {
642        self.coords.page_x
643    }
644    #[inline(always)]
645    pub fn page_y(&self) -> f32 {
646        self.coords.page_y
647    }
648    #[inline(always)]
649    pub fn client_x(&self) -> f32 {
650        self.coords.client_x
651    }
652    #[inline(always)]
653    pub fn client_y(&self) -> f32 {
654        self.coords.client_y
655    }
656    #[inline(always)]
657    pub fn screen_x(&self) -> f32 {
658        self.coords.screen_x
659    }
660    #[inline(always)]
661    pub fn screen_y(&self) -> f32 {
662        self.coords.screen_y
663    }
664    #[inline(always)]
665    pub fn element_x(&self) -> f32 {
666        self.element.x
667    }
668    #[inline(always)]
669    pub fn element_y(&self) -> f32 {
670        self.element.y
671    }
672}
673
674#[derive(Clone, Debug)]
675pub struct BlitzScrollEvent {
676    pub scroll_top: f64,
677    pub scroll_left: f64,
678    pub scroll_width: i32,
679    pub scroll_height: i32,
680    pub client_width: i32,
681    pub client_height: i32,
682}
683
684// struct PointerInputState {
685//     id: BlitzPointerId,
686//     pointer_down_x: f32,
687//     pointer_down_y: f32,
688//     pointer_down_time: Option<Instant>,
689//     click_count: u16,
690// }
691
692// struct PointersInputState {
693//     initial_finger_active: bool,
694//     mouse: Option<PointerInputState>,
695//     fingers: Vec<PointerInputState>,
696// }
697
698bitflags! {
699    /// The buttons property indicates which buttons are pressed on the mouse
700    /// (or other input device) when a mouse event is triggered.
701    ///
702    /// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons)
703    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
704    pub struct MouseEventButtons: u8 {
705        /// 0: No button or un-initialized
706        const None = 0b0000_0000;
707        /// 1: Primary button (usually the left button)
708        const Primary = 0b0000_0001;
709        /// 2: Secondary button (usually the right button)
710        const Secondary = 0b0000_0010;
711        /// 4: Auxiliary button (usually the mouse wheel button or middle button)
712        const Auxiliary = 0b0000_0100;
713        /// 8: 4th button (typically the "Browser Back" button)
714        const Fourth = 0b0000_1000;
715        /// 16: 5th button (typically the "Browser Forward" button)
716        const Fifth = 0b0001_0000;
717    }
718}
719
720impl Default for MouseEventButtons {
721    fn default() -> Self {
722        Self::None
723    }
724}
725
726impl From<MouseEventButton> for MouseEventButtons {
727    fn from(value: MouseEventButton) -> Self {
728        match value {
729            MouseEventButton::Main => Self::Primary,
730            MouseEventButton::Auxiliary => Self::Auxiliary,
731            MouseEventButton::Secondary => Self::Secondary,
732            MouseEventButton::Fourth => Self::Fourth,
733            MouseEventButton::Fifth => Self::Fifth,
734        }
735    }
736}
737
738/// The button property indicates which button was pressed
739/// on the mouse to trigger the event.
740///
741/// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button)
742#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
743pub enum MouseEventButton {
744    /// Main button pressed, usually the left button or the un-initialized state
745    #[default]
746    Main = 0,
747    /// Auxiliary button pressed, usually the wheel button or the middle button (if present)
748    Auxiliary = 1,
749    /// Secondary button pressed, usually the right button
750    Secondary = 2,
751    /// Fourth button, typically the Browser Back button
752    Fourth = 3,
753    /// Fifth button, typically the Browser Forward button
754    Fifth = 4,
755}
756
757#[derive(Copy, Clone, Debug, PartialEq, Eq)]
758pub enum KeyState {
759    Pressed,
760    Released,
761}
762
763impl KeyState {
764    pub fn is_pressed(self) -> bool {
765        matches!(self, Self::Pressed)
766    }
767}
768
769#[derive(Clone, Debug)]
770pub struct BlitzKeyEvent {
771    pub key: Key,
772    pub code: Code,
773    pub modifiers: Modifiers,
774    pub location: Location,
775    pub is_auto_repeating: bool,
776    pub is_composing: bool,
777    pub state: KeyState,
778    pub text: Option<SmolStr>,
779}
780
781#[derive(Clone, Debug)]
782pub struct BlitzInputEvent {
783    pub value: String,
784}
785
786#[derive(Clone, Debug)]
787pub struct BlitzFocusEvent;
788
789/// A form submission, before it happens.
790///
791/// Carries the control that triggered it, because that is what a handler
792/// reads to tell one submit button from another, and what the submission
793/// itself needs in order to include the submitter's own name and value.
794#[derive(Clone, Debug)]
795pub struct BlitzSubmitEvent {
796    /// The form being submitted.
797    pub form: u64,
798    /// The control that triggered it. Equal to the form when the submission
799    /// came from a key press rather than from a button.
800    pub submitter: u64,
801}
802
803/// Copy of Winit IME event to avoid lower-level Blitz crates depending on winit
804#[derive(Debug, Clone, PartialEq, Eq, Hash)]
805pub enum BlitzImeEvent {
806    /// Notifies when the IME was enabled.
807    ///
808    /// After getting this event you could receive [`Preedit`][Self::Preedit] and
809    /// [`Commit`][Self::Commit] events. You should also start performing IME related requests
810    /// like `Window::set_ime_cursor_area`.
811    Enabled,
812
813    /// Notifies when a new composing text should be set at the cursor position.
814    ///
815    /// The value represents a pair of the preedit string and the cursor begin position and end
816    /// position. When it's `None`, the cursor should be hidden. When `String` is an empty string
817    /// this indicates that preedit was cleared.
818    ///
819    /// The cursor position is byte-wise indexed, assuming UTF-8.
820    Preedit(String, Option<(usize, usize)>),
821
822    /// Notifies when text should be inserted into the editor widget.
823    ///
824    /// Right before this event winit will send empty [`Self::Preedit`] event.
825    Commit(String),
826
827    /// Delete text surrounding the cursor or selection.
828    ///
829    /// This event does not affect either the pre-edit string.
830    /// This means that the application must first remove the pre-edit,
831    /// then execute the deletion, then insert the removed text back.
832    ///
833    /// This event assumes text is stored in UTF-8.
834    DeleteSurrounding {
835        /// Bytes to remove before the selection
836        before_bytes: usize,
837        /// Bytes to remove after the selection
838        after_bytes: usize,
839    },
840
841    /// Notifies when the IME was disabled.
842    ///
843    /// After receiving this event you won't get any more [`Preedit`][Self::Preedit] or
844    /// [`Commit`][Self::Commit] events until the next [`Enabled`][Self::Enabled] event. You should
845    /// also stop issuing IME related requests like `Window::set_ime_cursor_area` and clear
846    /// pending preedit text.
847    Disabled,
848}