use std::{any::Any, rc::Rc};
use crate::events::*;
use dioxus_core::ElementId;
use serde::{Deserialize, Serialize};
#[cfg(feature = "serialize")]
#[derive(Serialize, Debug, PartialEq)]
pub struct HtmlEvent {
pub element: ElementId,
pub name: String,
pub bubbles: bool,
pub data: EventData,
}
#[cfg(feature = "serialize")]
impl<'de> Deserialize<'de> for HtmlEvent {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize, Debug, Clone)]
struct Inner {
element: ElementId,
name: String,
bubbles: bool,
data: serde_json::Value,
}
let Inner {
element,
name,
bubbles,
data,
} = Inner::deserialize(deserializer)?;
let data = deserialize_raw(&name, &data).map_err(|e| {
serde::de::Error::custom(format!(
"Failed to deserialize event data for event {}: {:#?}\n'{:#?}'",
name, e, data,
))
})?;
Ok(HtmlEvent {
data,
element,
bubbles,
name,
})
}
}
#[cfg(feature = "serialize")]
fn deserialize_raw(name: &str, data: &serde_json::Value) -> Result<EventData, serde_json::Error> {
use EventData::*;
#[inline]
fn de<'de, F>(f: &'de serde_json::Value) -> Result<F, serde_json::Error>
where
F: Deserialize<'de>,
{
F::deserialize(f)
}
let data = match name {
"click" | "contextmenu" | "dblclick" | "doubleclick" | "mousedown" | "mouseenter"
| "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => Mouse(de(data)?),
"copy" | "cut" | "paste" => Clipboard(de(data)?),
"compositionend" | "compositionstart" | "compositionupdate" => Composition(de(data)?),
"keydown" | "keypress" | "keyup" => Keyboard(de(data)?),
"blur" | "focus" | "focusin" | "focusout" => Focus(de(data)?),
"change" | "input" | "invalid" | "reset" | "submit" => Form(de(data)?),
"drag" | "dragend" | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart"
| "drop" => Drag(de(data)?),
"pointerlockchange" | "pointerlockerror" | "pointerdown" | "pointermove" | "pointerup"
| "pointerover" | "pointerout" | "pointerenter" | "pointerleave" | "gotpointercapture"
| "lostpointercapture" => Pointer(de(data)?),
"selectstart" | "selectionchange" | "select" => Selection(de(data)?),
"touchcancel" | "touchend" | "touchmove" | "touchstart" => Touch(de(data)?),
"resize" => Resize(de(data)?),
"scroll" => Scroll(de(data)?),
"visible" => Visible(de(data)?),
"wheel" => Wheel(de(data)?),
"abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
| "ended" | "interruptbegin" | "interruptend" | "loadeddata" | "loadedmetadata"
| "loadstart" | "pause" | "play" | "playing" | "progress" | "ratechange" | "seeked"
| "seeking" | "stalled" | "suspend" | "timeupdate" | "volumechange" | "waiting"
| "loadend" | "timeout" => Media(de(data)?),
"animationstart" | "animationend" | "animationiteration" => Animation(de(data)?),
"transitionend" => Transition(de(data)?),
"toggle" => Toggle(de(data)?),
"load" | "error" => Image(de(data)?),
"mounted" => Mounted,
other => {
return Err(serde::de::Error::custom(format!(
"Unknown event type: {other}"
)))
}
};
Ok(data)
}
#[cfg(feature = "serialize")]
impl HtmlEvent {
pub fn bubbles(&self) -> bool {
self.bubbles
}
}
#[derive(Deserialize, Serialize, Debug, PartialEq)]
#[serde(untagged)]
#[non_exhaustive]
pub enum EventData {
Mouse(SerializedMouseData),
Clipboard(SerializedClipboardData),
Composition(SerializedCompositionData),
Keyboard(SerializedKeyboardData),
Focus(SerializedFocusData),
Form(SerializedFormData),
Drag(SerializedDragData),
Pointer(SerializedPointerData),
Selection(SerializedSelectionData),
Touch(SerializedTouchData),
Resize(SerializedResizeData),
Scroll(SerializedScrollData),
Visible(SerializedVisibleData),
Wheel(SerializedWheelData),
Media(SerializedMediaData),
Animation(SerializedAnimationData),
Transition(SerializedTransitionData),
Toggle(SerializedToggleData),
Image(SerializedImageData),
Mounted,
}
impl EventData {
pub fn into_any(self) -> Rc<dyn Any> {
match self {
EventData::Mouse(data) => {
Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
}
EventData::Clipboard(data) => {
Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
}
EventData::Composition(data) => {
Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
}
EventData::Keyboard(data) => {
Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
}
EventData::Focus(data) => {
Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
}
EventData::Form(data) => Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>,
EventData::Drag(data) => Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>,
EventData::Pointer(data) => {
Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
}
EventData::Selection(data) => {
Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
}
EventData::Touch(data) => {
Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
}
EventData::Resize(data) => {
Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
}
EventData::Scroll(data) => {
Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
}
EventData::Visible(data) => {
Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
}
EventData::Wheel(data) => {
Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
}
EventData::Media(data) => {
Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
}
EventData::Animation(data) => {
Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
}
EventData::Transition(data) => {
Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
}
EventData::Toggle(data) => {
Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
}
EventData::Image(data) => {
Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
}
EventData::Mounted => {
Rc::new(PlatformEventData::new(Box::new(MountedData::new(())))) as Rc<dyn Any>
}
}
}
}
#[test]
fn test_back_and_forth() {
let data = HtmlEvent {
element: ElementId(0),
data: EventData::Mouse(SerializedMouseData::default()),
name: "click".to_string(),
bubbles: true,
};
println!("{}", serde_json::to_string_pretty(&data).unwrap());
let o = r#"
{
"element": 0,
"name": "click",
"bubbles": true,
"data": {
"alt_key": false,
"button": 0,
"buttons": 0,
"client_x": 0,
"client_y": 0,
"ctrl_key": false,
"meta_key": false,
"offset_x": 0,
"offset_y": 0,
"page_x": 0,
"page_y": 0,
"screen_x": 0,
"screen_y": 0,
"shift_key": false
}
}
"#;
let p: HtmlEvent = serde_json::from_str(o).unwrap();
assert_eq!(data, p);
}
pub struct SerializedHtmlEventConverter;
impl HtmlEventConverter for SerializedHtmlEventConverter {
fn convert_animation_data(&self, event: &PlatformEventData) -> AnimationData {
event
.downcast::<SerializedAnimationData>()
.cloned()
.unwrap()
.into()
}
fn convert_clipboard_data(&self, event: &PlatformEventData) -> ClipboardData {
event
.downcast::<SerializedClipboardData>()
.cloned()
.unwrap()
.into()
}
fn convert_composition_data(&self, event: &PlatformEventData) -> CompositionData {
event
.downcast::<SerializedCompositionData>()
.cloned()
.unwrap()
.into()
}
fn convert_drag_data(&self, event: &PlatformEventData) -> DragData {
event
.downcast::<SerializedDragData>()
.cloned()
.unwrap()
.into()
}
fn convert_focus_data(&self, event: &PlatformEventData) -> FocusData {
event
.downcast::<SerializedFocusData>()
.cloned()
.unwrap()
.into()
}
fn convert_form_data(&self, event: &PlatformEventData) -> FormData {
event
.downcast::<SerializedFormData>()
.cloned()
.unwrap()
.into()
}
fn convert_image_data(&self, event: &PlatformEventData) -> ImageData {
event
.downcast::<SerializedImageData>()
.cloned()
.unwrap()
.into()
}
fn convert_keyboard_data(&self, event: &PlatformEventData) -> KeyboardData {
event
.downcast::<SerializedKeyboardData>()
.cloned()
.unwrap()
.into()
}
fn convert_media_data(&self, event: &PlatformEventData) -> MediaData {
event
.downcast::<SerializedMediaData>()
.cloned()
.unwrap()
.into()
}
fn convert_mounted_data(&self, _: &PlatformEventData) -> MountedData {
MountedData::from(())
}
fn convert_mouse_data(&self, event: &PlatformEventData) -> MouseData {
event
.downcast::<SerializedMouseData>()
.cloned()
.unwrap()
.into()
}
fn convert_pointer_data(&self, event: &PlatformEventData) -> PointerData {
event
.downcast::<SerializedPointerData>()
.cloned()
.unwrap()
.into()
}
fn convert_resize_data(&self, event: &PlatformEventData) -> ResizeData {
event
.downcast::<SerializedResizeData>()
.cloned()
.unwrap()
.into()
}
fn convert_scroll_data(&self, event: &PlatformEventData) -> ScrollData {
event
.downcast::<SerializedScrollData>()
.cloned()
.unwrap()
.into()
}
fn convert_selection_data(&self, event: &PlatformEventData) -> SelectionData {
event
.downcast::<SerializedSelectionData>()
.cloned()
.unwrap()
.into()
}
fn convert_toggle_data(&self, event: &PlatformEventData) -> ToggleData {
event
.downcast::<SerializedToggleData>()
.cloned()
.unwrap()
.into()
}
fn convert_touch_data(&self, event: &PlatformEventData) -> TouchData {
event
.downcast::<SerializedTouchData>()
.cloned()
.unwrap()
.into()
}
fn convert_transition_data(&self, event: &PlatformEventData) -> TransitionData {
event
.downcast::<SerializedTransitionData>()
.cloned()
.unwrap()
.into()
}
fn convert_visible_data(&self, event: &PlatformEventData) -> VisibleData {
event
.downcast::<SerializedVisibleData>()
.cloned()
.unwrap()
.into()
}
fn convert_wheel_data(&self, event: &PlatformEventData) -> WheelData {
event
.downcast::<SerializedWheelData>()
.cloned()
.unwrap()
.into()
}
}