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 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 match deserialize_raw_event(name, data)? {
57 Some(result) => Ok(result),
58 None => Err(serde::de::Error::custom(format!(
59 "Unknown event type: {name}"
60 ))),
61 }
62}
63
64#[cfg(feature = "serialize")]
65impl HtmlEvent {
66 pub fn bubbles(&self) -> bool {
67 self.bubbles
68 }
69}
70
71#[derive(Deserialize, Serialize, Debug, PartialEq)]
72#[serde(untagged)]
73#[non_exhaustive]
74pub enum EventData {
75 BeforeInput(SerializedBeforeInputData),
76 Cancel(SerializedCancelData),
77 Mouse(SerializedMouseData),
78 Clipboard(SerializedClipboardData),
79 Composition(SerializedCompositionData),
80 Keyboard(SerializedKeyboardData),
81 Focus(SerializedFocusData),
82 Form(SerializedFormData),
83 Drag(SerializedDragData),
84 Pointer(SerializedPointerData),
85 Selection(SerializedSelectionData),
86 Touch(SerializedTouchData),
87 Resize(SerializedResizeData),
88 Scroll(SerializedScrollData),
89 Visible(SerializedVisibleData),
90 Wheel(SerializedWheelData),
91 Media(SerializedMediaData),
92 Animation(SerializedAnimationData),
93 Transition(SerializedTransitionData),
94 Toggle(SerializedToggleData),
95 Image(SerializedImageData),
96 Mounted,
97}
98
99impl EventData {
100 pub fn into_any(self) -> Rc<dyn Any> {
101 match self {
102 EventData::BeforeInput(data) => {
103 Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
104 }
105 EventData::Cancel(data) => {
106 Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
107 }
108 EventData::Mouse(data) => {
109 Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
110 }
111 EventData::Clipboard(data) => {
112 Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
113 }
114 EventData::Composition(data) => {
115 Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
116 }
117 EventData::Keyboard(data) => {
118 Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
119 }
120 EventData::Focus(data) => {
121 Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
122 }
123 EventData::Form(data) => Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>,
124 EventData::Drag(data) => Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>,
125 EventData::Pointer(data) => {
126 Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
127 }
128 EventData::Selection(data) => {
129 Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
130 }
131 EventData::Touch(data) => {
132 Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
133 }
134 EventData::Resize(data) => {
135 Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
136 }
137 EventData::Scroll(data) => {
138 Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
139 }
140 EventData::Visible(data) => {
141 Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
142 }
143 EventData::Wheel(data) => {
144 Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
145 }
146 EventData::Media(data) => {
147 Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
148 }
149 EventData::Animation(data) => {
150 Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
151 }
152 EventData::Transition(data) => {
153 Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
154 }
155 EventData::Toggle(data) => {
156 Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
157 }
158 EventData::Image(data) => {
159 Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
160 }
161 EventData::Mounted => {
162 Rc::new(PlatformEventData::new(Box::new(MountedData::new(())))) as Rc<dyn Any>
163 }
164 }
165 }
166}
167
168#[test]
169fn beforeinput_event_deserializes_from_interpreter_payload() {
170 let raw = r#"
171{
172 "element": 0,
173 "name": "beforeinput",
174 "bubbles": true,
175 "data": {
176 "input_type": "insertText",
177 "data": "x",
178 "is_composing": false,
179 "value": "hello"
180 }
181}
182 "#;
183
184 let event: HtmlEvent = serde_json::from_str(raw).unwrap();
185 assert_eq!(event.name, "beforeinput");
186 assert!(event.bubbles);
187 match event.data {
188 EventData::BeforeInput(data) => {
189 assert_eq!(data.input_type, "insertText");
190 assert_eq!(data.data.as_deref(), Some("x"));
191 assert!(!data.is_composing);
192 assert_eq!(data.value, "hello");
193 }
194 other => panic!("expected EventData::BeforeInput, got {other:?}"),
195 }
196}
197
198#[test]
199fn test_back_and_forth() {
200 let data = HtmlEvent {
201 element: ElementId(0),
202 data: EventData::Mouse(SerializedMouseData::default()),
203 name: "click".to_string(),
204 bubbles: true,
205 };
206
207 println!("{}", serde_json::to_string_pretty(&data).unwrap());
208
209 let o = r#"
210{
211 "element": 0,
212 "name": "click",
213 "bubbles": true,
214 "data": {
215 "alt_key": false,
216 "button": 0,
217 "buttons": 0,
218 "client_x": 0,
219 "client_y": 0,
220 "ctrl_key": false,
221 "meta_key": false,
222 "offset_x": 0,
223 "offset_y": 0,
224 "page_x": 0,
225 "page_y": 0,
226 "screen_x": 0,
227 "screen_y": 0,
228 "shift_key": false
229 }
230}
231 "#;
232
233 let p: HtmlEvent = serde_json::from_str(o).unwrap();
234
235 assert_eq!(data, p);
236}
237
238pub struct SerializedHtmlEventConverter;
240
241impl HtmlEventConverter for SerializedHtmlEventConverter {
242 fn convert_animation_data(&self, event: &PlatformEventData) -> AnimationData {
243 event
244 .downcast::<SerializedAnimationData>()
245 .cloned()
246 .unwrap()
247 .into()
248 }
249
250 fn convert_before_input_data(&self, event: &PlatformEventData) -> BeforeInputData {
251 event
252 .downcast::<SerializedBeforeInputData>()
253 .cloned()
254 .unwrap()
255 .into()
256 }
257
258 fn convert_cancel_data(&self, event: &PlatformEventData) -> CancelData {
259 event
260 .downcast::<SerializedCancelData>()
261 .cloned()
262 .unwrap()
263 .into()
264 }
265
266 fn convert_clipboard_data(&self, event: &PlatformEventData) -> ClipboardData {
267 event
268 .downcast::<SerializedClipboardData>()
269 .cloned()
270 .unwrap()
271 .into()
272 }
273
274 fn convert_composition_data(&self, event: &PlatformEventData) -> CompositionData {
275 event
276 .downcast::<SerializedCompositionData>()
277 .cloned()
278 .unwrap()
279 .into()
280 }
281
282 fn convert_drag_data(&self, event: &PlatformEventData) -> DragData {
283 event
284 .downcast::<SerializedDragData>()
285 .cloned()
286 .unwrap()
287 .into()
288 }
289
290 fn convert_focus_data(&self, event: &PlatformEventData) -> FocusData {
291 event
292 .downcast::<SerializedFocusData>()
293 .cloned()
294 .unwrap()
295 .into()
296 }
297
298 fn convert_form_data(&self, event: &PlatformEventData) -> FormData {
299 event
300 .downcast::<SerializedFormData>()
301 .cloned()
302 .unwrap()
303 .into()
304 }
305
306 fn convert_image_data(&self, event: &PlatformEventData) -> ImageData {
307 event
308 .downcast::<SerializedImageData>()
309 .cloned()
310 .unwrap()
311 .into()
312 }
313
314 fn convert_keyboard_data(&self, event: &PlatformEventData) -> KeyboardData {
315 event
316 .downcast::<SerializedKeyboardData>()
317 .cloned()
318 .unwrap()
319 .into()
320 }
321
322 fn convert_media_data(&self, event: &PlatformEventData) -> MediaData {
323 event
324 .downcast::<SerializedMediaData>()
325 .cloned()
326 .unwrap()
327 .into()
328 }
329
330 fn convert_mounted_data(&self, _: &PlatformEventData) -> MountedData {
331 MountedData::from(())
332 }
333
334 fn convert_mouse_data(&self, event: &PlatformEventData) -> MouseData {
335 event
336 .downcast::<SerializedMouseData>()
337 .cloned()
338 .unwrap()
339 .into()
340 }
341
342 fn convert_pointer_data(&self, event: &PlatformEventData) -> PointerData {
343 event
344 .downcast::<SerializedPointerData>()
345 .cloned()
346 .unwrap()
347 .into()
348 }
349 fn convert_resize_data(&self, event: &PlatformEventData) -> ResizeData {
350 event
351 .downcast::<SerializedResizeData>()
352 .cloned()
353 .unwrap()
354 .into()
355 }
356
357 fn convert_scroll_data(&self, event: &PlatformEventData) -> ScrollData {
358 event
359 .downcast::<SerializedScrollData>()
360 .cloned()
361 .unwrap()
362 .into()
363 }
364
365 fn convert_selection_data(&self, event: &PlatformEventData) -> SelectionData {
366 event
367 .downcast::<SerializedSelectionData>()
368 .cloned()
369 .unwrap()
370 .into()
371 }
372
373 fn convert_toggle_data(&self, event: &PlatformEventData) -> ToggleData {
374 event
375 .downcast::<SerializedToggleData>()
376 .cloned()
377 .unwrap()
378 .into()
379 }
380
381 fn convert_touch_data(&self, event: &PlatformEventData) -> TouchData {
382 event
383 .downcast::<SerializedTouchData>()
384 .cloned()
385 .unwrap()
386 .into()
387 }
388
389 fn convert_transition_data(&self, event: &PlatformEventData) -> TransitionData {
390 event
391 .downcast::<SerializedTransitionData>()
392 .cloned()
393 .unwrap()
394 .into()
395 }
396
397 fn convert_visible_data(&self, event: &PlatformEventData) -> VisibleData {
398 event
399 .downcast::<SerializedVisibleData>()
400 .cloned()
401 .unwrap()
402 .into()
403 }
404
405 fn convert_wheel_data(&self, event: &PlatformEventData) -> WheelData {
406 event
407 .downcast::<SerializedWheelData>()
408 .cloned()
409 .unwrap()
410 .into()
411 }
412}