dioxus_html/
transit.rs

1use std::{any::Any, rc::Rc};
2
3use crate::events::*;
4use dioxus_core::ElementId;
5use serde::{Deserialize, Serialize};
6
7#[cfg(feature = "serialize")]
8#[derive(Serialize, Debug, PartialEq)]
9pub struct HtmlEvent {
10    pub element: ElementId,
11    pub name: String,
12    pub bubbles: bool,
13    pub data: EventData,
14}
15
16#[cfg(feature = "serialize")]
17impl<'de> Deserialize<'de> for HtmlEvent {
18    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
19    where
20        D: serde::Deserializer<'de>,
21    {
22        #[derive(Deserialize, Debug, Clone)]
23        struct Inner {
24            element: ElementId,
25            name: String,
26            bubbles: bool,
27            data: serde_json::Value,
28        }
29
30        let Inner {
31            element,
32            name,
33            bubbles,
34            data,
35        } = Inner::deserialize(deserializer)?;
36
37        // in debug mode let's try and be helpful as to why the deserialization failed
38        let data = deserialize_raw(&name, &data).map_err(|e| {
39            serde::de::Error::custom(format!(
40                "Failed to deserialize event data for event {}:  {:#?}\n'{:#?}'",
41                name, e, data,
42            ))
43        })?;
44
45        Ok(HtmlEvent {
46            data,
47            element,
48            bubbles,
49            name,
50        })
51    }
52}
53
54#[cfg(feature = "serialize")]
55fn deserialize_raw(name: &str, data: &serde_json::Value) -> Result<EventData, serde_json::Error> {
56    use EventData::*;
57
58    // a little macro-esque thing to make the code below more readable
59    #[inline]
60    fn de<'de, F>(f: &'de serde_json::Value) -> Result<F, serde_json::Error>
61    where
62        F: Deserialize<'de>,
63    {
64        F::deserialize(f)
65    }
66
67    let data = match name {
68        // Mouse
69        "click" | "contextmenu" | "dblclick" | "doubleclick" | "mousedown" | "mouseenter"
70        | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => Mouse(de(data)?),
71
72        // Clipboard
73        "copy" | "cut" | "paste" => Clipboard(de(data)?),
74
75        // Composition
76        "compositionend" | "compositionstart" | "compositionupdate" => Composition(de(data)?),
77
78        // Keyboard
79        "keydown" | "keypress" | "keyup" => Keyboard(de(data)?),
80
81        // Focus
82        "blur" | "focus" | "focusin" | "focusout" => Focus(de(data)?),
83
84        // Form
85        "change" | "input" | "invalid" | "reset" | "submit" => Form(de(data)?),
86
87        // Drag
88        "drag" | "dragend" | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart"
89        | "drop" => Drag(de(data)?),
90
91        // Pointer
92        "pointerlockchange" | "pointerlockerror" | "pointerdown" | "pointermove" | "pointerup"
93        | "pointerover" | "pointerout" | "pointerenter" | "pointerleave" | "gotpointercapture"
94        | "lostpointercapture" => Pointer(de(data)?),
95
96        // Selection
97        "selectstart" | "selectionchange" | "select" => Selection(de(data)?),
98
99        // Touch
100        "touchcancel" | "touchend" | "touchmove" | "touchstart" => Touch(de(data)?),
101
102        // Resize
103        "resize" => Resize(de(data)?),
104
105        // Scroll
106        "scroll" => Scroll(de(data)?),
107
108        // Visible
109        "visible" => Visible(de(data)?),
110
111        // Wheel
112        "wheel" => Wheel(de(data)?),
113
114        // Media
115        "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
116        | "ended" | "interruptbegin" | "interruptend" | "loadeddata" | "loadedmetadata"
117        | "loadstart" | "pause" | "play" | "playing" | "progress" | "ratechange" | "seeked"
118        | "seeking" | "stalled" | "suspend" | "timeupdate" | "volumechange" | "waiting"
119        | "loadend" | "timeout" => Media(de(data)?),
120
121        // Animation
122        "animationstart" | "animationend" | "animationiteration" => Animation(de(data)?),
123
124        // Transition
125        "transitionend" => Transition(de(data)?),
126
127        // Toggle
128        "toggle" => Toggle(de(data)?),
129
130        "load" | "error" => Image(de(data)?),
131
132        // Mounted
133        "mounted" => Mounted,
134
135        // OtherData => "abort" | "afterprint" | "beforeprint" | "beforeunload" | "hashchange" | "languagechange" | "message" | "offline" | "online" | "pagehide" | "pageshow" | "popstate" | "rejectionhandled" | "storage" | "unhandledrejection" | "unload" | "userproximity" | "vrdisplayactivate" | "vrdisplayblur" | "vrdisplayconnect" | "vrdisplaydeactivate" | "vrdisplaydisconnect" | "vrdisplayfocus" | "vrdisplaypointerrestricted" | "vrdisplaypointerunrestricted" | "vrdisplaypresentchange";
136        other => {
137            return Err(serde::de::Error::custom(format!(
138                "Unknown event type: {other}"
139            )))
140        }
141    };
142
143    Ok(data)
144}
145
146#[cfg(feature = "serialize")]
147impl HtmlEvent {
148    pub fn bubbles(&self) -> bool {
149        self.bubbles
150    }
151}
152
153#[derive(Deserialize, Serialize, Debug, PartialEq)]
154#[serde(untagged)]
155#[non_exhaustive]
156pub enum EventData {
157    Mouse(SerializedMouseData),
158    Clipboard(SerializedClipboardData),
159    Composition(SerializedCompositionData),
160    Keyboard(SerializedKeyboardData),
161    Focus(SerializedFocusData),
162    Form(SerializedFormData),
163    Drag(SerializedDragData),
164    Pointer(SerializedPointerData),
165    Selection(SerializedSelectionData),
166    Touch(SerializedTouchData),
167    Resize(SerializedResizeData),
168    Scroll(SerializedScrollData),
169    Visible(SerializedVisibleData),
170    Wheel(SerializedWheelData),
171    Media(SerializedMediaData),
172    Animation(SerializedAnimationData),
173    Transition(SerializedTransitionData),
174    Toggle(SerializedToggleData),
175    Image(SerializedImageData),
176    Mounted,
177}
178
179impl EventData {
180    pub fn into_any(self) -> Rc<dyn Any> {
181        match self {
182            EventData::Mouse(data) => {
183                Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
184            }
185            EventData::Clipboard(data) => {
186                Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
187            }
188            EventData::Composition(data) => {
189                Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
190            }
191            EventData::Keyboard(data) => {
192                Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
193            }
194            EventData::Focus(data) => {
195                Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
196            }
197            EventData::Form(data) => Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>,
198            EventData::Drag(data) => Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>,
199            EventData::Pointer(data) => {
200                Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
201            }
202            EventData::Selection(data) => {
203                Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
204            }
205            EventData::Touch(data) => {
206                Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
207            }
208            EventData::Resize(data) => {
209                Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
210            }
211            EventData::Scroll(data) => {
212                Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
213            }
214            EventData::Visible(data) => {
215                Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
216            }
217            EventData::Wheel(data) => {
218                Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
219            }
220            EventData::Media(data) => {
221                Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
222            }
223            EventData::Animation(data) => {
224                Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
225            }
226            EventData::Transition(data) => {
227                Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
228            }
229            EventData::Toggle(data) => {
230                Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
231            }
232            EventData::Image(data) => {
233                Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
234            }
235            EventData::Mounted => {
236                Rc::new(PlatformEventData::new(Box::new(MountedData::new(())))) as Rc<dyn Any>
237            }
238        }
239    }
240}
241
242#[test]
243fn test_back_and_forth() {
244    let data = HtmlEvent {
245        element: ElementId(0),
246        data: EventData::Mouse(SerializedMouseData::default()),
247        name: "click".to_string(),
248        bubbles: true,
249    };
250
251    println!("{}", serde_json::to_string_pretty(&data).unwrap());
252
253    let o = r#"
254{
255  "element": 0,
256  "name": "click",
257  "bubbles": true,
258  "data": {
259    "alt_key": false,
260    "button": 0,
261    "buttons": 0,
262    "client_x": 0,
263    "client_y": 0,
264    "ctrl_key": false,
265    "meta_key": false,
266    "offset_x": 0,
267    "offset_y": 0,
268    "page_x": 0,
269    "page_y": 0,
270    "screen_x": 0,
271    "screen_y": 0,
272    "shift_key": false
273  }
274}
275    "#;
276
277    let p: HtmlEvent = serde_json::from_str(o).unwrap();
278
279    assert_eq!(data, p);
280}
281
282/// A trait for converting from a serialized event to a concrete event type.
283pub struct SerializedHtmlEventConverter;
284
285impl HtmlEventConverter for SerializedHtmlEventConverter {
286    fn convert_animation_data(&self, event: &PlatformEventData) -> AnimationData {
287        event
288            .downcast::<SerializedAnimationData>()
289            .cloned()
290            .unwrap()
291            .into()
292    }
293
294    fn convert_clipboard_data(&self, event: &PlatformEventData) -> ClipboardData {
295        event
296            .downcast::<SerializedClipboardData>()
297            .cloned()
298            .unwrap()
299            .into()
300    }
301
302    fn convert_composition_data(&self, event: &PlatformEventData) -> CompositionData {
303        event
304            .downcast::<SerializedCompositionData>()
305            .cloned()
306            .unwrap()
307            .into()
308    }
309
310    fn convert_drag_data(&self, event: &PlatformEventData) -> DragData {
311        event
312            .downcast::<SerializedDragData>()
313            .cloned()
314            .unwrap()
315            .into()
316    }
317
318    fn convert_focus_data(&self, event: &PlatformEventData) -> FocusData {
319        event
320            .downcast::<SerializedFocusData>()
321            .cloned()
322            .unwrap()
323            .into()
324    }
325
326    fn convert_form_data(&self, event: &PlatformEventData) -> FormData {
327        event
328            .downcast::<SerializedFormData>()
329            .cloned()
330            .unwrap()
331            .into()
332    }
333
334    fn convert_image_data(&self, event: &PlatformEventData) -> ImageData {
335        event
336            .downcast::<SerializedImageData>()
337            .cloned()
338            .unwrap()
339            .into()
340    }
341
342    fn convert_keyboard_data(&self, event: &PlatformEventData) -> KeyboardData {
343        event
344            .downcast::<SerializedKeyboardData>()
345            .cloned()
346            .unwrap()
347            .into()
348    }
349
350    fn convert_media_data(&self, event: &PlatformEventData) -> MediaData {
351        event
352            .downcast::<SerializedMediaData>()
353            .cloned()
354            .unwrap()
355            .into()
356    }
357
358    fn convert_mounted_data(&self, _: &PlatformEventData) -> MountedData {
359        MountedData::from(())
360    }
361
362    fn convert_mouse_data(&self, event: &PlatformEventData) -> MouseData {
363        event
364            .downcast::<SerializedMouseData>()
365            .cloned()
366            .unwrap()
367            .into()
368    }
369
370    fn convert_pointer_data(&self, event: &PlatformEventData) -> PointerData {
371        event
372            .downcast::<SerializedPointerData>()
373            .cloned()
374            .unwrap()
375            .into()
376    }
377    fn convert_resize_data(&self, event: &PlatformEventData) -> ResizeData {
378        event
379            .downcast::<SerializedResizeData>()
380            .cloned()
381            .unwrap()
382            .into()
383    }
384
385    fn convert_scroll_data(&self, event: &PlatformEventData) -> ScrollData {
386        event
387            .downcast::<SerializedScrollData>()
388            .cloned()
389            .unwrap()
390            .into()
391    }
392
393    fn convert_selection_data(&self, event: &PlatformEventData) -> SelectionData {
394        event
395            .downcast::<SerializedSelectionData>()
396            .cloned()
397            .unwrap()
398            .into()
399    }
400
401    fn convert_toggle_data(&self, event: &PlatformEventData) -> ToggleData {
402        event
403            .downcast::<SerializedToggleData>()
404            .cloned()
405            .unwrap()
406            .into()
407    }
408
409    fn convert_touch_data(&self, event: &PlatformEventData) -> TouchData {
410        event
411            .downcast::<SerializedTouchData>()
412            .cloned()
413            .unwrap()
414            .into()
415    }
416
417    fn convert_transition_data(&self, event: &PlatformEventData) -> TransitionData {
418        event
419            .downcast::<SerializedTransitionData>()
420            .cloned()
421            .unwrap()
422            .into()
423    }
424
425    fn convert_visible_data(&self, event: &PlatformEventData) -> VisibleData {
426        event
427            .downcast::<SerializedVisibleData>()
428            .cloned()
429            .unwrap()
430            .into()
431    }
432
433    fn convert_wheel_data(&self, event: &PlatformEventData) -> WheelData {
434        event
435            .downcast::<SerializedWheelData>()
436            .cloned()
437            .unwrap()
438            .into()
439    }
440}