Pārlūkot izejas kodu

trait based event system

Evan Almloff 1 gadu atpakaļ
vecāks
revīzija
299b123446

+ 2 - 2
packages/desktop/src/element.rs

@@ -17,8 +17,8 @@ impl DesktopElement {
 }
 
 impl RenderedElementBacking for DesktopElement {
-    fn get_raw_element(&self) -> dioxus_html::MountedResult<&dyn std::any::Any> {
-        Ok(self)
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
     }
 
     fn get_client_rect(

+ 18 - 6
packages/desktop/src/lib.rs

@@ -27,8 +27,8 @@ pub use desktop_context::{
 };
 use desktop_context::{EventData, UserWindowEvent, WebviewQueue, WindowEventHandlers};
 use dioxus_core::*;
-use dioxus_html::MountedData;
-use dioxus_html::{native_bind::NativeFileEngine, FormData, HtmlEvent};
+use dioxus_html::{native_bind::NativeFileEngine, HtmlEvent};
+use dioxus_html::{FileEngine, HasFormData, MountedData};
 use element::DesktopElement;
 use eval::init_eval;
 use futures_util::{pin_mut, FutureExt};
@@ -340,14 +340,26 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config)
                     if let Ok(file_diolog) =
                         serde_json::from_value::<file_upload::FileDialogRequest>(msg.params())
                     {
+                        struct DesktopFileUploadForm {
+                            files: Arc<NativeFileEngine>,
+                        }
+
+                        impl HasFormData for DesktopFileUploadForm {
+                            fn files(&self) -> Option<Arc<dyn FileEngine>> {
+                                Some(self.files.clone())
+                            }
+
+                            fn as_any(&self) -> &dyn std::any::Any {
+                                self
+                            }
+                        }
+
                         let id = ElementId(file_diolog.target);
                         let event_name = &file_diolog.event;
                         let event_bubbles = file_diolog.bubbles;
                         let files = file_upload::get_file_event(&file_diolog);
-                        let data = Rc::new(FormData {
-                            value: Default::default(),
-                            values: Default::default(),
-                            files: Some(Arc::new(NativeFileEngine::new(files))),
+                        let data = Rc::new(DesktopFileUploadForm {
+                            files: Arc::new(NativeFileEngine::new(files)),
                         });
 
                         let view = webviews.get_mut(&event.1).unwrap();

+ 2 - 1
packages/html/Cargo.toml

@@ -41,7 +41,6 @@ features = [
      "PointerEvent",
      "FocusEvent",
      "CompositionEvent",
-     "ClipboardEvent",
 ]
 
 [dev-dependencies]
@@ -51,11 +50,13 @@ serde_json = "1"
 default = ["serialize", "mounted"]
 serialize = [
     "serde",
+    "serde/rc",
     "serde_repr",
     "serde_json",
     "euclid/serde",
     "keyboard-types/serde",
     "dioxus-core/serialize",
+    "enumset/serde",
 ]
 mounted = [
     "web-sys/Element",

+ 1 - 3
packages/html/src/events.rs

@@ -151,8 +151,6 @@ pub fn event_bubbles(evt: &str) -> bool {
     }
 }
 
-use std::future::Future;
-
 #[doc(hidden)]
 pub trait EventReturn<P>: Sized {
     fn spawn(self, _cx: &dioxus_core::ScopeState) {}
@@ -164,7 +162,7 @@ pub struct AsyncMarker;
 
 impl<T> EventReturn<AsyncMarker> for T
 where
-    T: Future<Output = ()> + 'static,
+    T: std::future::Future<Output = ()> + 'static,
 {
     #[inline]
     fn spawn(self, cx: &dioxus_core::ScopeState) {

+ 113 - 5
packages/html/src/events/animation.rs

@@ -2,12 +2,120 @@ use dioxus_core::Event;
 
 pub type AnimationEvent = Event<AnimationData>;
 
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Debug, Clone, PartialEq)]
 pub struct AnimationData {
-    pub animation_name: String,
-    pub pseudo_element: String,
-    pub elapsed_time: f32,
+    inner: Box<dyn HasAnimationData>,
+}
+
+impl AnimationData {
+    pub fn animation_name(&self) -> String {
+        self.inner.animation_name()
+    }
+
+    pub fn pseudo_element(&self) -> String {
+        self.inner.pseudo_element()
+    }
+
+    pub fn elapsed_time(&self) -> f32 {
+        self.inner.elapsed_time()
+    }
+
+    pub fn downcast<T: 'static>(&self) -> Option<&T> {
+        self.inner.as_ref().as_any().downcast_ref::<T>()
+    }
+}
+
+impl<E: HasAnimationData> From<E> for AnimationData {
+    fn from(e: E) -> Self {
+        Self { inner: Box::new(e) }
+    }
+}
+
+impl std::fmt::Debug for AnimationData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("AnimationData")
+            .field("animation_name", &self.animation_name())
+            .field("pseudo_element", &self.pseudo_element())
+            .field("elapsed_time", &self.elapsed_time())
+            .finish()
+    }
+}
+
+impl PartialEq for AnimationData {
+    fn eq(&self, other: &Self) -> bool {
+        self.animation_name() == other.animation_name()
+            && self.pseudo_element() == other.pseudo_element()
+            && self.elapsed_time() == other.elapsed_time()
+    }
+}
+
+#[cfg(feature = "serialize")]
+#[derive(serde::Serialize, serde::Deserialize)]
+struct SerializedAnimationData {
+    animation_name: String,
+    pseudo_element: String,
+    elapsed_time: f32,
+}
+
+#[cfg(feature = "serialize")]
+impl From<&AnimationData> for SerializedAnimationData {
+    fn from(data: &AnimationData) -> Self {
+        Self {
+            animation_name: data.animation_name(),
+            pseudo_element: data.pseudo_element(),
+            elapsed_time: data.elapsed_time(),
+        }
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl HasAnimationData for SerializedAnimationData {
+    fn animation_name(&self) -> String {
+        self.animation_name.clone()
+    }
+
+    fn pseudo_element(&self) -> String {
+        self.pseudo_element.clone()
+    }
+
+    fn elapsed_time(&self) -> f32 {
+        self.elapsed_time
+    }
+
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl serde::Serialize for AnimationData {
+    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        SerializedAnimationData::from(self).serialize(serializer)
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl<'de> serde::Deserialize<'de> for AnimationData {
+    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        let data = SerializedAnimationData::deserialize(deserializer)?;
+        Ok(Self {
+            inner: Box::new(data),
+        })
+    }
+}
+
+/// A trait for any object that has the data for an animation event
+pub trait HasAnimationData: std::any::Any {
+    /// The name of the animation
+    fn animation_name(&self) -> String;
+
+    /// The name of the pseudo-element the animation runs on
+    fn pseudo_element(&self) -> String;
+
+    /// The amount of time the animation has been running
+    fn elapsed_time(&self) -> f32;
+
+    /// return self as Any
+    fn as_any(&self) -> &dyn std::any::Any;
 }
 
 impl_event! [

+ 72 - 3
packages/html/src/events/clipboard.rs

@@ -1,10 +1,79 @@
 use dioxus_core::Event;
 
 pub type ClipboardEvent = Event<ClipboardData>;
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Debug, Clone, PartialEq, Eq)]
+
 pub struct ClipboardData {
-    // DOMDataTransfer clipboardData
+    inner: Box<dyn HasClipboardData>,
+}
+
+impl<E: HasClipboardData> From<E> for ClipboardData {
+    fn from(e: E) -> Self {
+        Self { inner: Box::new(e) }
+    }
+}
+
+impl std::fmt::Debug for ClipboardData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("ClipboardData").finish()
+    }
+}
+
+impl PartialEq for ClipboardData {
+    fn eq(&self, _other: &Self) -> bool {
+        true
+    }
+}
+
+impl ClipboardData {
+    pub fn new(inner: impl HasClipboardData) -> Self {
+        Self {
+            inner: Box::new(inner),
+        }
+    }
+
+    pub fn downcast<T: 'static>(&self) -> Option<&T> {
+        self.inner.as_ref().as_any().downcast_ref::<T>()
+    }
+}
+
+#[cfg(feature = "serialize")]
+#[derive(serde::Serialize, serde::Deserialize)]
+struct SerializedClipboardData {}
+
+#[cfg(feature = "serialize")]
+impl From<&ClipboardData> for SerializedClipboardData {
+    fn from(_: &ClipboardData) -> Self {
+        Self {}
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl HasClipboardData for SerializedClipboardData {
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl serde::Serialize for ClipboardData {
+    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        SerializedClipboardData::from(self).serialize(serializer)
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl<'de> serde::Deserialize<'de> for ClipboardData {
+    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        let data = SerializedClipboardData::deserialize(deserializer)?;
+        Ok(Self {
+            inner: Box::new(data),
+        })
+    }
+}
+
+pub trait HasClipboardData: std::any::Any {
+    /// return self as Any
+    fn as_any(&self) -> &dyn std::any::Any;
 }
 
 impl_event![

+ 82 - 3
packages/html/src/events/composition.rs

@@ -1,10 +1,89 @@
 use dioxus_core::Event;
 
 pub type CompositionEvent = Event<CompositionData>;
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Debug, Clone, PartialEq, Eq)]
+
 pub struct CompositionData {
-    pub data: String,
+    inner: Box<dyn HasCompositionData>,
+}
+
+impl std::fmt::Debug for CompositionData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("CompositionData")
+            .field("data", &self.data())
+            .finish()
+    }
+}
+
+impl<E: HasCompositionData> From<E> for CompositionData {
+    fn from(e: E) -> Self {
+        Self { inner: Box::new(e) }
+    }
+}
+
+impl PartialEq for CompositionData {
+    fn eq(&self, other: &Self) -> bool {
+        self.data() == other.data()
+    }
+}
+
+impl CompositionData {
+    pub fn data(&self) -> String {
+        self.inner.data()
+    }
+
+    pub fn downcast<T: 'static>(&self) -> Option<&T> {
+        self.inner.as_any().downcast_ref()
+    }
+}
+
+#[cfg(feature = "serialize")]
+#[derive(serde::Serialize, serde::Deserialize)]
+struct SerializedCompositionData {
+    data: String,
+}
+
+#[cfg(feature = "serialize")]
+impl From<&CompositionData> for SerializedCompositionData {
+    fn from(data: &CompositionData) -> Self {
+        Self { data: data.data() }
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl HasCompositionData for SerializedCompositionData {
+    fn data(&self) -> String {
+        self.data.clone()
+    }
+
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl serde::Serialize for CompositionData {
+    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        SerializedCompositionData::from(self).serialize(serializer)
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl<'de> serde::Deserialize<'de> for CompositionData {
+    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        let data = SerializedCompositionData::deserialize(deserializer)?;
+        Ok(Self {
+            inner: Box::new(data),
+        })
+    }
+}
+
+/// A trait for any object that has the data for a composition event
+pub trait HasCompositionData: std::any::Any {
+    /// The characters generated by the input method that raised the event
+    fn data(&self) -> String;
+
+    /// return self as Any
+    fn as_any(&self) -> &dyn std::any::Any;
 }
 
 impl_event! [

+ 175 - 5
packages/html/src/events/drag.rs

@@ -1,6 +1,10 @@
+use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint};
+use crate::input_data::{MouseButton, MouseButtonSet};
+use crate::prelude::PointInteraction;
 use dioxus_core::Event;
+use keyboard_types::Modifiers;
 
-use crate::MouseData;
+use crate::HasMouseData;
 
 pub type DragEvent = Event<DragData>;
 
@@ -8,13 +12,179 @@ pub type DragEvent = Event<DragData>;
 /// placing a pointer device (such as a mouse) on the touch surface and then dragging the pointer to a new location
 /// (such as another DOM element). Applications are free to interpret a drag and drop interaction in an
 /// application-specific way.
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Debug, Clone, PartialEq, Eq)]
 pub struct DragData {
-    /// Inherit mouse data
-    pub mouse: MouseData,
+    inner: Box<dyn HasDragData>,
 }
 
+impl<E: HasDragData> From<E> for DragData {
+    fn from(e: E) -> Self {
+        Self { inner: Box::new(e) }
+    }
+}
+
+impl std::fmt::Debug for DragData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("DragData")
+            .field("coordinates", &self.coordinates())
+            .field("modifiers", &self.modifiers())
+            .field("held_buttons", &self.held_buttons())
+            .field("trigger_button", &self.trigger_button())
+            .finish()
+    }
+}
+
+impl PartialEq for DragData {
+    fn eq(&self, other: &Self) -> bool {
+        self.coordinates() == other.coordinates()
+            && self.modifiers() == other.modifiers()
+            && self.held_buttons() == other.held_buttons()
+            && self.trigger_button() == other.trigger_button()
+    }
+}
+
+impl DragData {
+    /// The event's coordinates relative to the application's viewport (as opposed to the coordinate within the page).
+    ///
+    /// For example, clicking in the top left corner of the viewport will always result in a mouse event with client coordinates (0., 0.), regardless of whether the page is scrolled horizontally.
+    pub fn client_coordinates(&self) -> ClientPoint {
+        self.inner.client_coordinates()
+    }
+
+    /// The event's coordinates relative to the padding edge of the target element
+    ///
+    /// For example, clicking in the top left corner of an element will result in element coordinates (0., 0.)
+    pub fn element_coordinates(&self) -> ElementPoint {
+        self.inner.element_coordinates()
+    }
+
+    /// The event's coordinates relative to the entire document. This includes any portion of the document not currently visible.
+    ///
+    /// For example, if the page is scrolled 200 pixels to the right and 300 pixels down, clicking in the top left corner of the viewport would result in page coordinates (200., 300.)
+    pub fn page_coordinates(&self) -> PagePoint {
+        self.inner.page_coordinates()
+    }
+
+    /// The event's coordinates relative to the entire screen. This takes into account the window's offset.
+    pub fn screen_coordinates(&self) -> ScreenPoint {
+        self.inner.screen_coordinates()
+    }
+
+    pub fn coordinates(&self) -> Coordinates {
+        self.inner.coordinates()
+    }
+
+    /// The set of modifier keys which were pressed when the event occurred
+    pub fn modifiers(&self) -> Modifiers {
+        self.inner.modifiers()
+    }
+
+    /// The set of mouse buttons which were held when the event occurred.
+    pub fn held_buttons(&self) -> MouseButtonSet {
+        self.inner.held_buttons()
+    }
+
+    /// The mouse button that triggered the event
+    ///
+    // todo the following is kind of bad; should we just return None when the trigger_button is unreliable (and frankly irrelevant)? i guess we would need the event_type here
+    /// This is only guaranteed to indicate which button was pressed during events caused by pressing or releasing a button. As such, it is not reliable for events such as mouseenter, mouseleave, mouseover, mouseout, or mousemove. For example, a value of MouseButton::Primary may also indicate that no button was pressed.
+    pub fn trigger_button(&self) -> Option<MouseButton> {
+        self.inner.trigger_button()
+    }
+
+    /// Downcast this event data to a specific type
+    pub fn downcast<T: 'static>(&self) -> Option<&T> {
+        self.inner.as_any().downcast_ref::<T>()
+    }
+}
+
+#[cfg(feature = "serialize")]
+#[derive(serde::Serialize, serde::Deserialize)]
+struct SerializedDragData {
+    mouse: crate::point_interaction::SerializedPointInteraction,
+}
+
+#[cfg(feature = "serialize")]
+impl From<&DragData> for SerializedDragData {
+    fn from(data: &DragData) -> Self {
+        Self {
+            mouse: crate::point_interaction::SerializedPointInteraction {
+                client_coordinates: data.client_coordinates(),
+                element_coordinates: data.element_coordinates(),
+                page_coordinates: data.page_coordinates(),
+                screen_coordinates: data.screen_coordinates(),
+                modifiers: data.modifiers(),
+                held_buttons: data.held_buttons(),
+                trigger_button: data.trigger_button(),
+            },
+        }
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl HasDragData for SerializedDragData {}
+
+#[cfg(feature = "serialize")]
+impl HasMouseData for SerializedDragData {
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl PointInteraction for SerializedDragData {
+    fn client_coordinates(&self) -> ClientPoint {
+        self.mouse.client_coordinates()
+    }
+
+    fn element_coordinates(&self) -> ElementPoint {
+        self.mouse.element_coordinates()
+    }
+
+    fn page_coordinates(&self) -> PagePoint {
+        self.mouse.page_coordinates()
+    }
+
+    fn screen_coordinates(&self) -> ScreenPoint {
+        self.mouse.screen_coordinates()
+    }
+
+    fn coordinates(&self) -> Coordinates {
+        self.mouse.coordinates()
+    }
+
+    fn modifiers(&self) -> Modifiers {
+        self.mouse.modifiers()
+    }
+
+    fn held_buttons(&self) -> MouseButtonSet {
+        self.mouse.held_buttons()
+    }
+
+    fn trigger_button(&self) -> Option<MouseButton> {
+        self.mouse.trigger_button()
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl serde::Serialize for DragData {
+    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        SerializedDragData::from(self).serialize(serializer)
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl<'de> serde::Deserialize<'de> for DragData {
+    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        let data = SerializedDragData::deserialize(deserializer)?;
+        Ok(Self {
+            inner: Box::new(data),
+        })
+    }
+}
+
+/// A trait for any object that has the data for a drag event
+pub trait HasDragData: HasMouseData {}
+
 impl_event! {
     DragData;
 

+ 68 - 3
packages/html/src/events/focus.rs

@@ -2,9 +2,74 @@ use dioxus_core::Event;
 
 pub type FocusEvent = Event<FocusData>;
 
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct FocusData {/* DOMEventInner:  Send + SyncTarget relatedTarget */}
+pub struct FocusData {
+    inner: Box<dyn HasFocusData>,
+}
+
+impl<E: HasFocusData> From<E> for FocusData {
+    fn from(e: E) -> Self {
+        Self { inner: Box::new(e) }
+    }
+}
+
+impl std::fmt::Debug for FocusData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("FocusData").finish()
+    }
+}
+
+impl PartialEq for FocusData {
+    fn eq(&self, _other: &Self) -> bool {
+        true
+    }
+}
+
+impl FocusData {
+    /// Downcast this event data to a specific type
+    pub fn downcast<T: 'static>(&self) -> Option<&T> {
+        self.inner.as_any().downcast_ref::<T>()
+    }
+}
+
+#[cfg(feature = "serialize")]
+#[derive(serde::Serialize, serde::Deserialize)]
+struct SerializedFocusData {}
+
+#[cfg(feature = "serialize")]
+impl From<&FocusData> for SerializedFocusData {
+    fn from(_: &FocusData) -> Self {
+        Self {}
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl HasFocusData for SerializedFocusData {
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl serde::Serialize for FocusData {
+    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        SerializedFocusData::from(self).serialize(serializer)
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl<'de> serde::Deserialize<'de> for FocusData {
+    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        let data = SerializedFocusData::deserialize(deserializer)?;
+        Ok(Self {
+            inner: Box::new(data),
+        })
+    }
+}
+
+pub trait HasFocusData: std::any::Any {
+    /// return self as Any
+    fn as_any(&self) -> &dyn std::any::Any;
+}
 
 impl_event! [
     FocusData;

+ 165 - 49
packages/html/src/events/form.rs

@@ -4,28 +4,176 @@ use dioxus_core::Event;
 
 pub type FormEvent = Event<FormData>;
 
-/* DOMEvent:  Send + SyncTarget relatedTarget */
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Clone)]
 pub struct FormData {
-    pub value: String,
-
-    pub values: HashMap<String, Vec<String>>,
-
-    #[cfg_attr(
-        feature = "serialize",
-        serde(
-            default,
-            skip_serializing,
-            deserialize_with = "deserialize_file_engine"
-        )
-    )]
-    pub files: Option<std::sync::Arc<dyn FileEngine>>,
+    inner: Box<dyn HasFormData>,
+}
+
+impl<E: HasFormData> From<E> for FormData {
+    fn from(e: E) -> Self {
+        Self { inner: Box::new(e) }
+    }
+}
+
+impl PartialEq for FormData {
+    fn eq(&self, other: &Self) -> bool {
+        self.value() == other.value() && self.values() == other.values()
+    }
+}
+
+impl Debug for FormData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("FormEvent")
+            .field("value", &self.value())
+            .field("values", &self.values())
+            .finish()
+    }
+}
+
+impl FormData {
+    /// Create a new form event
+    pub fn new(event: impl HasFormData + 'static) -> Self {
+        Self {
+            inner: Box::new(event),
+        }
+    }
+
+    /// Get the value of the form event
+    pub fn value(&self) -> String {
+        self.inner.value()
+    }
+
+    /// Get the values of the form event
+    pub fn values(&self) -> HashMap<String, Vec<String>> {
+        self.inner.values()
+    }
+
+    /// Get the files of the form event
+    pub fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
+        self.inner.files()
+    }
+
+    /// Downcast this event to a concrete event type
+    pub fn downcast<T: 'static>(&self) -> Option<&T> {
+        self.inner.as_any().downcast_ref::<T>()
+    }
+}
+
+/// An object that has all the data for a form event
+pub trait HasFormData: std::any::Any {
+    fn value(&self) -> String {
+        Default::default()
+    }
+
+    fn values(&self) -> HashMap<String, Vec<String>> {
+        Default::default()
+    }
+
+    fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
+        None
+    }
+
+    /// return self as Any
+    fn as_any(&self) -> &dyn std::any::Any;
 }
 
 #[cfg(feature = "serialize")]
+/// A serialized form data object
 #[derive(serde::Serialize, serde::Deserialize)]
-struct SerializedFileEngine {
+pub struct SerializedFormData {
+    value: String,
+    values: HashMap<String, Vec<String>>,
+    files: Option<std::sync::Arc<SerializedFileEngine>>,
+}
+
+#[cfg(feature = "serialize")]
+impl SerializedFormData {
+    /// Create a new serialized form data object
+    pub fn new(
+        value: String,
+        values: HashMap<String, Vec<String>>,
+        files: Option<std::sync::Arc<SerializedFileEngine>>,
+    ) -> Self {
+        Self {
+            value,
+            values,
+            files,
+        }
+    }
+
+    /// Create a new serialized form data object from a traditional form data object
+    pub async fn async_from(data: &FormData) -> Self {
+        Self {
+            value: data.value(),
+            values: data.values(),
+            files: match data.files() {
+                Some(files) => {
+                    let mut resolved_files = HashMap::new();
+
+                    for file in files.files() {
+                        let bytes = files.read_file(&file).await;
+                        resolved_files.insert(file, bytes.unwrap_or_default());
+                    }
+
+                    Some(std::sync::Arc::new(SerializedFileEngine {
+                        files: resolved_files,
+                    }))
+                }
+                None => None,
+            },
+        }
+    }
+
+    fn from_lossy(data: &FormData) -> Self {
+        Self {
+            value: data.value(),
+            values: data.values(),
+            files: None,
+        }
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl HasFormData for SerializedFormData {
+    fn value(&self) -> String {
+        self.value.clone()
+    }
+
+    fn values(&self) -> HashMap<String, Vec<String>> {
+        self.values.clone()
+    }
+
+    fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
+        self.files
+            .as_ref()
+            .map(|files| std::sync::Arc::clone(files) as std::sync::Arc<dyn FileEngine>)
+    }
+
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl serde::Serialize for FormData {
+    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        SerializedFormData::from_lossy(self).serialize(serializer)
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl<'de> serde::Deserialize<'de> for FormData {
+    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        let data = SerializedFormData::deserialize(deserializer)?;
+        Ok(Self {
+            inner: Box::new(data),
+        })
+    }
+}
+
+#[cfg(feature = "serialize")]
+/// A file engine that serializes files to bytes
+#[derive(serde::Serialize, serde::Deserialize)]
+pub struct SerializedFileEngine {
     files: HashMap<String, Vec<u8>>,
 }
 
@@ -53,38 +201,6 @@ impl FileEngine for SerializedFileEngine {
     }
 }
 
-#[cfg(feature = "serialize")]
-fn deserialize_file_engine<'de, D>(
-    deserializer: D,
-) -> Result<Option<std::sync::Arc<dyn FileEngine>>, D::Error>
-where
-    D: serde::Deserializer<'de>,
-{
-    use serde::Deserialize;
-
-    let Ok(file_engine) = SerializedFileEngine::deserialize(deserializer) else {
-        return Ok(None);
-    };
-
-    let file_engine = std::sync::Arc::new(file_engine);
-    Ok(Some(file_engine))
-}
-
-impl PartialEq for FormData {
-    fn eq(&self, other: &Self) -> bool {
-        self.value == other.value && self.values == other.values
-    }
-}
-
-impl Debug for FormData {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("FormEvent")
-            .field("value", &self.value)
-            .field("values", &self.values)
-            .finish()
-    }
-}
-
 #[async_trait::async_trait(?Send)]
 pub trait FileEngine {
     // get a list of file names

+ 84 - 4
packages/html/src/events/image.rs

@@ -1,11 +1,91 @@
 use dioxus_core::Event;
 
 pub type ImageEvent = Event<ImageData>;
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Debug, Clone, PartialEq, Eq)]
 pub struct ImageData {
-    #[cfg_attr(feature = "serialize", serde(default))]
-    pub load_error: bool,
+    inner: Box<dyn HasImageData>,
+}
+
+impl<E: HasImageData> From<E> for ImageData {
+    fn from(e: E) -> Self {
+        Self { inner: Box::new(e) }
+    }
+}
+
+impl std::fmt::Debug for ImageData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("ImageData")
+            .field("load_error", &self.load_error())
+            .finish()
+    }
+}
+
+impl PartialEq for ImageData {
+    fn eq(&self, other: &Self) -> bool {
+        self.load_error() == other.load_error()
+    }
+}
+
+impl ImageData {
+    /// If the renderer encountered an error while loading the image
+    pub fn load_error(&self) -> bool {
+        self.inner.load_error()
+    }
+
+    pub fn downcast<T: 'static>(&self) -> Option<&T> {
+        self.inner.as_any().downcast_ref::<T>()
+    }
+}
+
+#[cfg(feature = "serialize")]
+#[derive(serde::Serialize, serde::Deserialize)]
+struct SerializedImageData {
+    load_error: bool,
+}
+
+#[cfg(feature = "serialize")]
+impl From<&ImageData> for SerializedImageData {
+    fn from(data: &ImageData) -> Self {
+        Self {
+            load_error: data.load_error(),
+        }
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl HasImageData for SerializedImageData {
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+
+    fn load_error(&self) -> bool {
+        self.load_error
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl serde::Serialize for ImageData {
+    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        SerializedImageData::from(self).serialize(serializer)
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl<'de> serde::Deserialize<'de> for ImageData {
+    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        let data = SerializedImageData::deserialize(deserializer)?;
+        Ok(Self {
+            inner: Box::new(data),
+        })
+    }
+}
+
+/// A trait for any object that has the data for an image event
+pub trait HasImageData: std::any::Any {
+    /// If the renderer encountered an error while loading the image
+    fn load_error(&self) -> bool;
+
+    /// return self as Any
+    fn as_any(&self) -> &dyn std::any::Any;
 }
 
 impl_event! [

+ 130 - 123
packages/html/src/events/keyboard.rs

@@ -1,9 +1,7 @@
-use crate::input_data::{decode_key_location, encode_key_location};
 use dioxus_core::Event;
 use keyboard_types::{Code, Key, Location, Modifiers};
 use std::convert::TryInto;
-use std::fmt::{Debug, Formatter};
-use std::str::FromStr;
+use std::fmt::Debug;
 
 #[cfg(feature = "serialize")]
 fn resilient_deserialize_code<'de, D>(deserializer: D) -> Result<Code, D::Error>
@@ -16,57 +14,136 @@ where
 }
 
 pub type KeyboardEvent = Event<KeyboardData>;
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Clone, PartialEq, Eq)]
 pub struct KeyboardData {
-    #[deprecated(
-        since = "0.3.0",
-        note = "This may not work in all environments. Use key() instead."
-    )]
-    pub char_code: u32,
-
-    /// Identify which "key" was entered.
-    #[deprecated(since = "0.3.0", note = "use key() instead")]
-    pub key: String,
-
-    /// Get the key code as an enum Variant.
-    #[deprecated(
-        since = "0.3.0",
-        note = "This may not work in all environments. Use code() instead."
-    )]
-    pub key_code: KeyCode,
-
-    /// the physical key on the keyboard
-    #[cfg_attr(
-        feature = "serialize",
-        serde(deserialize_with = "resilient_deserialize_code")
-    )]
+    inner: Box<dyn HasKeyboardData>,
+}
+
+impl<E: HasKeyboardData> From<E> for KeyboardData {
+    fn from(e: E) -> Self {
+        Self { inner: Box::new(e) }
+    }
+}
+
+impl std::fmt::Debug for KeyboardData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("KeyboardData")
+            .field("key", &self.key())
+            .field("code", &self.code())
+            .field("modifiers", &self.modifiers())
+            .field("location", &self.location())
+            .field("is_auto_repeating", &self.is_auto_repeating())
+            .finish()
+    }
+}
+
+impl PartialEq for KeyboardData {
+    fn eq(&self, other: &Self) -> bool {
+        self.key() == other.key()
+            && self.code() == other.code()
+            && self.modifiers() == other.modifiers()
+            && self.location() == other.location()
+            && self.is_auto_repeating() == other.is_auto_repeating()
+    }
+}
+
+impl KeyboardData {
+    /// The value of the key pressed by the user, taking into consideration the state of modifier keys such as Shift as well as the keyboard locale and layout.
+    pub fn key(&self) -> Key {
+        self.inner.key()
+    }
+
+    /// A physical key on the keyboard (as opposed to the character generated by pressing the key). In other words, this property returns a value that isn't altered by keyboard layout or the state of the modifier keys.
+    pub fn code(&self) -> Code {
+        self.inner.code()
+    }
+
+    /// The set of modifier keys which were pressed when the event occurred
+    pub fn modifiers(&self) -> Modifiers {
+        self.inner.modifiers()
+    }
+
+    /// The location of the key on the keyboard or other input device.
+    pub fn location(&self) -> Location {
+        self.inner.location()
+    }
+
+    /// `true` iff the key is being held down such that it is automatically repeating.
+    pub fn is_auto_repeating(&self) -> bool {
+        self.inner.is_auto_repeating()
+    }
+
+    /// Downcast this KeyboardData to a concrete type.
+    pub fn downcast<T: 'static>(&self) -> Option<&T> {
+        self.inner.as_any().downcast_ref::<T>()
+    }
+}
+
+#[cfg(feature = "serialize")]
+#[derive(serde::Serialize, serde::Deserialize)]
+struct SerializedKeyboardData {
+    auto_repeating: bool,
+    #[serde(deserialize_with = "resilient_deserialize_code")]
     code: Code,
+    key: Key,
+    location: Location,
+    modifiers: Modifiers,
+}
+
+#[cfg(feature = "serialize")]
+impl From<&KeyboardData> for SerializedKeyboardData {
+    fn from(data: &KeyboardData) -> Self {
+        Self {
+            auto_repeating: data.is_auto_repeating(),
+            code: data.code(),
+            key: data.key(),
+            location: data.location(),
+            modifiers: data.modifiers(),
+        }
+    }
+}
 
-    /// Indicate if the `alt` modifier key was pressed during this keyboard event
-    #[deprecated(since = "0.3.0", note = "use modifiers() instead")]
-    pub alt_key: bool,
+#[cfg(feature = "serialize")]
+impl HasKeyboardData for SerializedKeyboardData {
+    fn key(&self) -> Key {
+        self.key.clone()
+    }
 
-    /// Indicate if the `ctrl` modifier key was pressed during this keyboard event
-    #[deprecated(since = "0.3.0", note = "use modifiers() instead")]
-    pub ctrl_key: bool,
+    fn code(&self) -> Code {
+        self.code
+    }
 
-    /// Indicate if the `meta` modifier key was pressed during this keyboard event
-    #[deprecated(since = "0.3.0", note = "use modifiers() instead")]
-    pub meta_key: bool,
+    fn modifiers(&self) -> Modifiers {
+        self.modifiers
+    }
 
-    /// Indicate if the `shift` modifier key was pressed during this keyboard event
-    #[deprecated(since = "0.3.0", note = "use modifiers() instead")]
-    pub shift_key: bool,
+    fn location(&self) -> Location {
+        self.location
+    }
 
-    #[deprecated(since = "0.3.0", note = "use location() instead")]
-    pub location: usize,
+    fn is_auto_repeating(&self) -> bool {
+        self.auto_repeating
+    }
 
-    #[deprecated(since = "0.3.0", note = "use is_auto_repeating() instead")]
-    pub repeat: bool,
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
 
-    #[deprecated(since = "0.3.0", note = "use code() or key() instead")]
-    pub which: usize,
+#[cfg(feature = "serialize")]
+impl serde::Serialize for KeyboardData {
+    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        SerializedKeyboardData::from(self).serialize(serializer)
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl<'de> serde::Deserialize<'de> for KeyboardData {
+    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        let data = SerializedKeyboardData::deserialize(deserializer)?;
+        Ok(Self {
+            inner: Box::new(data),
+        })
+    }
 }
 
 impl_event! {
@@ -82,94 +159,24 @@ impl_event! {
     onkeyup
 }
 
-impl KeyboardData {
-    pub fn new(
-        key: Key,
-        code: Code,
-        location: Location,
-        is_auto_repeating: bool,
-        modifiers: Modifiers,
-    ) -> Self {
-        #[allow(deprecated)]
-        KeyboardData {
-            char_code: key.legacy_charcode(),
-            key: key.to_string(),
-            key_code: KeyCode::from_raw_code(
-                key.legacy_keycode()
-                    .try_into()
-                    .expect("could not convert keycode to u8"),
-            ),
-            code,
-            alt_key: modifiers.contains(Modifiers::ALT),
-            ctrl_key: modifiers.contains(Modifiers::CONTROL),
-            meta_key: modifiers.contains(Modifiers::META),
-            shift_key: modifiers.contains(Modifiers::SHIFT),
-            location: encode_key_location(location),
-            repeat: is_auto_repeating,
-            which: key
-                .legacy_charcode()
-                .try_into()
-                .expect("could not convert charcode to usize"),
-        }
-    }
-
+pub trait HasKeyboardData: std::any::Any {
     /// The value of the key pressed by the user, taking into consideration the state of modifier keys such as Shift as well as the keyboard locale and layout.
-    pub fn key(&self) -> Key {
-        #[allow(deprecated)]
-        FromStr::from_str(&self.key).unwrap_or(Key::Unidentified)
-    }
+    fn key(&self) -> Key;
 
     /// A physical key on the keyboard (as opposed to the character generated by pressing the key). In other words, this property returns a value that isn't altered by keyboard layout or the state of the modifier keys.
-    pub fn code(&self) -> Code {
-        self.code
-    }
+    fn code(&self) -> Code;
 
     /// The set of modifier keys which were pressed when the event occurred
-    pub fn modifiers(&self) -> Modifiers {
-        let mut modifiers = Modifiers::empty();
-
-        #[allow(deprecated)]
-        {
-            if self.alt_key {
-                modifiers.insert(Modifiers::ALT);
-            }
-            if self.ctrl_key {
-                modifiers.insert(Modifiers::CONTROL);
-            }
-            if self.meta_key {
-                modifiers.insert(Modifiers::META);
-            }
-            if self.shift_key {
-                modifiers.insert(Modifiers::SHIFT);
-            }
-        }
-
-        modifiers
-    }
+    fn modifiers(&self) -> Modifiers;
 
     /// The location of the key on the keyboard or other input device.
-    pub fn location(&self) -> Location {
-        #[allow(deprecated)]
-        decode_key_location(self.location)
-    }
+    fn location(&self) -> Location;
 
     /// `true` iff the key is being held down such that it is automatically repeating.
-    pub fn is_auto_repeating(&self) -> bool {
-        #[allow(deprecated)]
-        self.repeat
-    }
-}
+    fn is_auto_repeating(&self) -> bool;
 
-impl Debug for KeyboardData {
-    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("KeyboardData")
-            .field("key", &self.key())
-            .field("code", &self.code())
-            .field("modifiers", &self.modifiers())
-            .field("location", &self.location())
-            .field("is_auto_repeating", &self.is_auto_repeating())
-            .finish()
-    }
+    /// return self as Any
+    fn as_any(&self) -> &dyn std::any::Any;
 }
 
 #[cfg(feature = "serialize")]

+ 69 - 4
packages/html/src/events/media.rs

@@ -1,9 +1,74 @@
 use dioxus_core::Event;
 
 pub type MediaEvent = Event<MediaData>;
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct MediaData {}
+pub struct MediaData {
+    inner: Box<dyn HasMediaData>,
+}
+
+impl<E: HasMediaData> From<E> for MediaData {
+    fn from(e: E) -> Self {
+        Self { inner: Box::new(e) }
+    }
+}
+
+impl std::fmt::Debug for MediaData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("MediaData").finish()
+    }
+}
+
+impl PartialEq for MediaData {
+    fn eq(&self, _: &Self) -> bool {
+        true
+    }
+}
+
+impl MediaData {
+    /// Downcast this event to a concrete event type
+    pub fn downcast<T: 'static>(&self) -> Option<&T> {
+        self.inner.as_any().downcast_ref::<T>()
+    }
+}
+
+#[cfg(feature = "serialize")]
+#[derive(serde::Serialize, serde::Deserialize)]
+struct SerializedMediaData {}
+
+#[cfg(feature = "serialize")]
+impl From<&MediaData> for SerializedMediaData {
+    fn from(_: &MediaData) -> Self {
+        Self {}
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl HasMediaData for SerializedMediaData {
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl serde::Serialize for MediaData {
+    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        SerializedMediaData::from(self).serialize(serializer)
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl<'de> serde::Deserialize<'de> for MediaData {
+    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        let data = SerializedMediaData::deserialize(deserializer)?;
+        Ok(Self {
+            inner: Box::new(data),
+        })
+    }
+}
+
+pub trait HasMediaData: std::any::Any {
+    /// return self as Any
+    fn as_any(&self) -> &dyn std::any::Any;
+}
 
 impl_event! [
     MediaData;
@@ -29,7 +94,7 @@ impl_event! [
     ///ended
     onended
 
-    // todo: this conflicts with image events
+    // todo: this conflicts with Media events
     // neither have data, so it's okay
     // ///error
     // onerror

+ 21 - 15
packages/html/src/events/mounted.rs

@@ -3,22 +3,18 @@
 use euclid::Rect;
 
 use std::{
-    any::Any,
     fmt::{Display, Formatter},
     future::Future,
     pin::Pin,
-    rc::Rc,
 };
 
 /// An Element that has been rendered and allows reading and modifying information about it.
 ///
 /// Different platforms will have different implementations and different levels of support for this trait. Renderers that do not support specific features will return `None` for those queries.
 // we can not use async_trait here because it does not create a trait that is object safe
-pub trait RenderedElementBacking {
-    /// Get the renderer specific element for the given id
-    fn get_raw_element(&self) -> MountedResult<&dyn Any> {
-        Err(MountedError::NotSupported)
-    }
+pub trait RenderedElementBacking: std::any::Any {
+    /// return self as Any
+    fn as_any(&self) -> &dyn std::any::Any;
 
     /// Get the bounding rectangle of the element relative to the viewport (this does not include the scroll position)
     #[allow(clippy::type_complexity)]
@@ -40,7 +36,11 @@ pub trait RenderedElementBacking {
     }
 }
 
-impl RenderedElementBacking for () {}
+impl RenderedElementBacking for () {
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
 
 /// The way that scrolling should be performed
 #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
@@ -57,22 +57,23 @@ pub enum ScrollBehavior {
 ///
 /// Different platforms will have different implementations and different levels of support for this trait. Renderers that do not support specific features will return `None` for those queries.
 pub struct MountedData {
-    inner: Rc<dyn RenderedElementBacking>,
+    inner: Box<dyn RenderedElementBacking>,
+}
+
+impl<E: RenderedElementBacking> From<E> for MountedData {
+    fn from(e: E) -> Self {
+        Self { inner: Box::new(e) }
+    }
 }
 
 impl MountedData {
     /// Create a new MountedData
     pub fn new(registry: impl RenderedElementBacking + 'static) -> Self {
         Self {
-            inner: Rc::new(registry),
+            inner: Box::new(registry),
         }
     }
 
-    /// Get the renderer specific element for the given id
-    pub fn get_raw_element(&self) -> MountedResult<&dyn Any> {
-        self.inner.get_raw_element()
-    }
-
     /// Get the bounding rectangle of the element relative to the viewport (this does not include the scroll position)
     pub async fn get_client_rect(&self) -> MountedResult<Rect<f64, f64>> {
         self.inner.get_client_rect().await
@@ -90,6 +91,11 @@ impl MountedData {
     pub fn set_focus(&self, focus: bool) -> Pin<Box<dyn Future<Output = MountedResult<()>>>> {
         self.inner.set_focus(focus)
     }
+
+    /// Downcast this event to a concrete event type
+    pub fn downcast<T: 'static>(&self) -> Option<&T> {
+        self.inner.as_any().downcast_ref::<T>()
+    }
 }
 
 use dioxus_core::Event;

+ 119 - 13
packages/html/src/events/mouse.rs

@@ -1,16 +1,47 @@
-use crate::point_interaction::{PointData, PointInteraction};
+use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint};
+use crate::input_data::{MouseButton, MouseButtonSet};
+use crate::point_interaction::PointInteraction;
 use dioxus_core::Event;
+use keyboard_types::Modifiers;
 
 pub type MouseEvent = Event<MouseData>;
 
 /// A synthetic event that wraps a web-style [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent)
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Debug, Clone, Default, PartialEq, Eq)]
 /// Data associated with a mouse event
 pub struct MouseData {
-    /// Common data for all pointer/mouse events
-    #[cfg_attr(feature = "serialize", serde(flatten))]
-    point_data: PointData,
+    inner: Box<dyn HasMouseData>,
+}
+
+impl<E: HasMouseData> From<E> for MouseData {
+    fn from(e: E) -> Self {
+        Self { inner: Box::new(e) }
+    }
+}
+
+impl std::fmt::Debug for MouseData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("MouseData")
+            .field("coordinates", &self.coordinates())
+            .field("modifiers", &self.modifiers())
+            .field("held_buttons", &self.held_buttons())
+            .field("trigger_button", &self.trigger_button())
+            .finish()
+    }
+}
+
+impl<E: HasMouseData> PartialEq<E> for MouseData {
+    fn eq(&self, other: &E) -> bool {
+        self.coordinates() == other.coordinates()
+            && self.modifiers() == other.modifiers()
+            && self.held_buttons() == other.held_buttons()
+            && self.trigger_button() == other.trigger_button()
+    }
+}
+
+/// A trait for any object that has the data for a mouse event
+pub trait HasMouseData: PointInteraction {
+    /// return self as Any
+    fn as_any(&self) -> &dyn std::any::Any;
 }
 
 impl_event! {
@@ -75,16 +106,91 @@ impl_event! {
 }
 
 impl MouseData {
-    /// Construct MouseData with the specified properties
-    ///
-    /// Note: the current implementation truncates coordinates. In the future, when we change the internal representation, it may also support a fractional part.
-    pub fn new(point_data: PointData) -> Self {
-        Self { point_data }
+    /// Downcast this event to a concrete event type
+    pub fn downcast<T: 'static>(&self) -> Option<&T> {
+        self.inner.as_any().downcast_ref::<T>()
     }
 }
 
 impl PointInteraction for MouseData {
-    fn get_point_data(&self) -> PointData {
-        self.point_data
+    /// The event's coordinates relative to the application's viewport (as opposed to the coordinate within the page).
+    ///
+    /// For example, clicking in the top left corner of the viewport will always result in a mouse event with client coordinates (0., 0.), regardless of whether the page is scrolled horizontally.
+    fn client_coordinates(&self) -> ClientPoint {
+        self.inner.client_coordinates()
+    }
+
+    /// The event's coordinates relative to the padding edge of the target element
+    ///
+    /// For example, clicking in the top left corner of an element will result in element coordinates (0., 0.)
+    fn element_coordinates(&self) -> ElementPoint {
+        self.inner.element_coordinates()
+    }
+
+    /// The event's coordinates relative to the entire document. This includes any portion of the document not currently visible.
+    ///
+    /// For example, if the page is scrolled 200 pixels to the right and 300 pixels down, clicking in the top left corner of the viewport would result in page coordinates (200., 300.)
+    fn page_coordinates(&self) -> PagePoint {
+        self.inner.page_coordinates()
+    }
+
+    /// The event's coordinates relative to the entire screen. This takes into account the window's offset.
+    fn screen_coordinates(&self) -> ScreenPoint {
+        self.inner.screen_coordinates()
+    }
+
+    fn coordinates(&self) -> Coordinates {
+        self.inner.coordinates()
+    }
+
+    /// The set of modifier keys which were pressed when the event occurred
+    fn modifiers(&self) -> Modifiers {
+        self.inner.modifiers()
+    }
+
+    /// The set of mouse buttons which were held when the event occurred.
+    fn held_buttons(&self) -> MouseButtonSet {
+        self.inner.held_buttons()
+    }
+
+    /// The mouse button that triggered the event
+    ///
+    // todo the following is kind of bad; should we just return None when the trigger_button is unreliable (and frankly irrelevant)? i guess we would need the event_type here
+    /// This is only guaranteed to indicate which button was pressed during events caused by pressing or releasing a button. As such, it is not reliable for events such as mouseenter, mouseleave, mouseover, mouseout, or mousemove. For example, a value of MouseButton::Primary may also indicate that no button was pressed.
+    fn trigger_button(&self) -> Option<MouseButton> {
+        self.inner.trigger_button()
+    }
+}
+
+impl PartialEq for MouseData {
+    fn eq(&self, other: &Self) -> bool {
+        self.coordinates() == other.coordinates()
+            && self.modifiers() == other.modifiers()
+            && self.held_buttons() == other.held_buttons()
+            && self.trigger_button() == other.trigger_button()
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl HasMouseData for crate::point_interaction::SerializedPointInteraction {
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl serde::Serialize for MouseData {
+    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        crate::point_interaction::SerializedPointInteraction::from(self).serialize(serializer)
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl<'de> serde::Deserialize<'de> for MouseData {
+    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        let data = crate::point_interaction::SerializedPointInteraction::deserialize(deserializer)?;
+        Ok(Self {
+            inner: Box::new(data),
+        })
     }
 }

+ 256 - 91
packages/html/src/events/pointer.rs

@@ -1,58 +1,87 @@
-use std::fmt::Debug;
-
 use dioxus_core::Event;
+use keyboard_types::Modifiers;
 
-use crate::point_interaction::{PointData, PointInteraction};
+use crate::{geometry::*, input_data::*, point_interaction::PointInteraction};
 
 /// A synthetic event that wraps a web-style [`PointerEvent`](https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent)
 pub type PointerEvent = Event<PointerData>;
 
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Debug, Clone, PartialEq)]
 pub struct PointerData {
-    /// Common data for all pointer/mouse events
-    #[cfg_attr(feature = "serialize", serde(flatten))]
-    point_data: PointData,
+    inner: Box<dyn HasPointerData>,
+}
 
-    /// The unique identifier of the pointer causing the event.
-    #[deprecated(since = "0.5.0", note = "use pointer_id() instead")]
-    pub pointer_id: i32,
+impl<E: HasPointerData> From<E> for PointerData {
+    fn from(e: E) -> Self {
+        Self { inner: Box::new(e) }
+    }
+}
 
-    /// The width (magnitude on the X axis), in CSS pixels, of the contact geometry of the pointer.
-    #[deprecated(since = "0.5.0", note = "use width() instead")]
-    pub width: i32,
+impl std::fmt::Debug for PointerData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("PointerData")
+            .field("pointer_id", &self.pointer_id())
+            .field("width", &self.width())
+            .field("height", &self.height())
+            .field("pressure", &self.pressure())
+            .field("tangential_pressure", &self.tangential_pressure())
+            .field("tilt_x", &self.tilt_x())
+            .field("tilt_y", &self.tilt_y())
+            .field("twist", &self.twist())
+            .field("pointer_type", &self.pointer_type())
+            .field("is_primary", &self.is_primary())
+            .finish()
+    }
+}
 
-    /// The height (magnitude on the Y axis), in CSS pixels, of the contact geometry of the pointer.
-    #[deprecated(since = "0.5.0", note = "use height() instead")]
-    pub height: i32,
+impl PartialEq for PointerData {
+    fn eq(&self, other: &Self) -> bool {
+        self.pointer_id() == other.pointer_id()
+            && self.width() == other.width()
+            && self.height() == other.height()
+            && self.pressure() == other.pressure()
+            && self.tangential_pressure() == other.tangential_pressure()
+            && self.tilt_x() == other.tilt_x()
+            && self.tilt_y() == other.tilt_y()
+            && self.twist() == other.twist()
+            && self.pointer_type() == other.pointer_type()
+            && self.is_primary() == other.is_primary()
+    }
+}
 
-    /// The normalized pressure of the pointer input in the range of 0 to 1,
-    #[deprecated(since = "0.5.0", note = "use pressure() instead")]
-    pub pressure: f32,
+/// A trait for any object that has the data for a pointer event
+pub trait HasPointerData: PointInteraction {
+    /// Gets the unique identifier of the pointer causing the event.
+    fn pointer_id(&self) -> i32;
 
-    /// The normalized tangential pressure of the pointer input (also known as barrel pressure or cylinder stress) in the range -1 to 1,
-    #[deprecated(since = "0.5.0", note = "use tangential_pressure() instead")]
-    pub tangential_pressure: f32,
+    /// Gets the width (magnitude on the X axis), in CSS pixels, of the contact geometry of the pointer.
+    fn width(&self) -> i32;
 
-    /// The plane angle (in degrees, in the range of -90 to 90) between the Y-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the Y axis.
-    #[deprecated(since = "0.5.0", note = "use tilt_x() instead")]
-    pub tilt_x: i32,
+    /// Gets the height (magnitude on the Y axis), in CSS pixels, of the contact geometry of the pointer.
+    fn height(&self) -> i32;
 
-    /// The plane angle (in degrees, in the range of -90 to 90) between the X-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the X axis.
-    #[deprecated(since = "0.5.0", note = "use tilt_y() instead")]
-    pub tilt_y: i32,
+    /// Gets the normalized pressure of the pointer input in the range of 0 to 1,
+    fn pressure(&self) -> f32;
 
-    /// The clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359.The clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359.
-    #[deprecated(since = "0.5.0", note = "use twist() instead")]
-    pub twist: i32,
+    /// Gets the normalized tangential pressure of the pointer input (also known as barrel pressure or cylinder stress) in the range -1 to 1,
+    fn tangential_pressure(&self) -> f32;
 
-    /// Indicates the device type that caused the event (mouse, pen, touch, etc.).
-    #[deprecated(since = "0.5.0", note = "use pointer_type() instead")]
-    pub pointer_type: String,
+    /// Gets the plane angle (in degrees, in the range of -90 to 90) between the Y-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the Y axis.
+    fn tilt_x(&self) -> i32;
 
-    /// Indicates if the pointer represents the primary pointer of this pointer type.
-    #[deprecated(since = "0.5.0", note = "use is_primary() instead")]
-    pub is_primary: bool,
+    /// Gets the plane angle (in degrees, in the range of -90 to 90) between the X-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the X axis.
+    fn tilt_y(&self) -> i32;
+
+    /// Gets the clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359.The clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359.
+    fn twist(&self) -> i32;
+
+    /// Gets the device type that caused the event (mouse, pen, touch, etc.).
+    fn pointer_type(&self) -> String;
+
+    /// Gets if the pointer represents the primary pointer of this pointer type.
+    fn is_primary(&self) -> bool;
+
+    /// return self as Any
+    fn as_any(&self) -> &dyn std::any::Any;
 }
 
 impl_event![
@@ -89,99 +118,235 @@ impl_event![
 ];
 
 impl PointerData {
-    #[allow(clippy::too_many_arguments)]
-    pub fn new(
-        point_data: PointData,
-        pointer_id: i32,
-        width: i32,
-        height: i32,
-        pressure: f32,
-        tangential_pressure: f32,
-        tilt_x: i32,
-        tilt_y: i32,
-        twist: i32,
-        pointer_type: String,
-        is_primary: bool,
-    ) -> Self {
-        #[allow(deprecated)]
-        Self {
-            point_data,
-            pointer_id,
-            width,
-            height,
-            pressure,
-            tangential_pressure,
-            tilt_x,
-            tilt_y,
-            twist,
-            pointer_type,
-            is_primary,
-        }
-    }
-
     /// Gets the unique identifier of the pointer causing the event.
     pub fn pointer_id(&self) -> i32 {
-        #[allow(deprecated)]
-        self.pointer_id
+        self.inner.pointer_id()
     }
 
     /// Gets the width (magnitude on the X axis), in CSS pixels, of the contact geometry of the pointer.
     pub fn width(&self) -> i32 {
-        #[allow(deprecated)]
-        self.width
+        self.inner.width()
     }
 
     /// Gets the height (magnitude on the Y axis), in CSS pixels, of the contact geometry of the pointer.
     pub fn height(&self) -> i32 {
-        #[allow(deprecated)]
-        self.height
+        self.inner.height()
     }
 
     /// Gets the normalized pressure of the pointer input in the range of 0 to 1,
     pub fn pressure(&self) -> f32 {
-        #[allow(deprecated)]
-        self.pressure
+        self.inner.pressure()
     }
 
     /// Gets the normalized tangential pressure of the pointer input (also known as barrel pressure or cylinder stress) in the range -1 to 1,
     pub fn tangential_pressure(&self) -> f32 {
-        #[allow(deprecated)]
-        self.tangential_pressure
+        self.inner.tangential_pressure()
     }
 
     /// Gets the plane angle (in degrees, in the range of -90 to 90) between the Y-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the Y axis.
     pub fn tilt_x(&self) -> i32 {
-        #[allow(deprecated)]
-        self.tilt_x
+        self.inner.tilt_x()
     }
 
     /// Gets the plane angle (in degrees, in the range of -90 to 90) between the X-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the X axis.
     pub fn tilt_y(&self) -> i32 {
-        #[allow(deprecated)]
-        self.tilt_y
+        self.inner.tilt_y()
     }
 
     /// Gets the clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359.The clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359.
     pub fn twist(&self) -> i32 {
-        #[allow(deprecated)]
-        self.twist
+        self.inner.twist()
     }
 
     /// Gets the device type that caused the event (mouse, pen, touch, etc.).
-    pub fn pointer_type(&self) -> &str {
-        #[allow(deprecated)]
-        self.pointer_type.as_str()
+    pub fn pointer_type(&self) -> String {
+        self.inner.pointer_type()
     }
 
     /// Gets if the pointer represents the primary pointer of this pointer type.
     pub fn is_primary(&self) -> bool {
-        #[allow(deprecated)]
-        self.is_primary
+        self.inner.is_primary()
     }
 }
 
 impl PointInteraction for PointerData {
-    fn get_point_data(&self) -> PointData {
-        self.point_data
+    fn client_coordinates(&self) -> ClientPoint {
+        self.inner.client_coordinates()
+    }
+
+    fn screen_coordinates(&self) -> ScreenPoint {
+        self.inner.screen_coordinates()
+    }
+
+    fn element_coordinates(&self) -> ElementPoint {
+        self.inner.element_coordinates()
+    }
+
+    fn page_coordinates(&self) -> PagePoint {
+        self.inner.page_coordinates()
+    }
+
+    fn modifiers(&self) -> Modifiers {
+        self.inner.modifiers()
+    }
+
+    fn held_buttons(&self) -> MouseButtonSet {
+        self.inner.held_buttons()
+    }
+
+    fn trigger_button(&self) -> Option<MouseButton> {
+        self.inner.trigger_button()
+    }
+}
+
+#[cfg(feature = "serialize")]
+#[derive(serde::Serialize, serde::Deserialize)]
+struct SerializedPointData {
+    /// Common data for all pointer/mouse events
+    #[serde(flatten)]
+    point_data: crate::point_interaction::SerializedPointInteraction,
+
+    /// The unique identifier of the pointer causing the event.
+    pointer_id: i32,
+
+    /// The width (magnitude on the X axis), in CSS pixels, of the contact geometry of the pointer.
+    width: i32,
+
+    /// The height (magnitude on the Y axis), in CSS pixels, of the contact geometry of the pointer.
+    height: i32,
+
+    /// The normalized pressure of the pointer input in the range of 0 to 1,
+    pressure: f32,
+
+    /// The normalized tangential pressure of the pointer input (also known as barrel pressure or cylinder stress) in the range -1 to 1,
+    tangential_pressure: f32,
+
+    /// The plane angle (in degrees, in the range of -90 to 90) between the Y-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the Y axis.
+    tilt_x: i32,
+
+    /// The plane angle (in degrees, in the range of -90 to 90) between the X-Z plane and the plane containing both the transducer (e.g. pen stylus) axis and the X axis.
+    tilt_y: i32,
+
+    /// The clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359.The clockwise rotation of the pointer (e.g. pen stylus) around its major axis in degrees, with a value in the range 0 to 359.
+    twist: i32,
+
+    /// Indicates the device type that caused the event (mouse, pen, touch, etc.).
+    pointer_type: String,
+
+    /// Indicates if the pointer represents the primary pointer of this pointer type.
+    is_primary: bool,
+}
+
+#[cfg(feature = "serialize")]
+impl HasPointerData for SerializedPointData {
+    fn pointer_id(&self) -> i32 {
+        self.pointer_id
+    }
+
+    fn width(&self) -> i32 {
+        self.width
+    }
+
+    fn height(&self) -> i32 {
+        self.height
+    }
+
+    fn pressure(&self) -> f32 {
+        self.pressure
+    }
+
+    fn tangential_pressure(&self) -> f32 {
+        self.tangential_pressure
+    }
+
+    fn tilt_x(&self) -> i32 {
+        self.tilt_x
+    }
+
+    fn tilt_y(&self) -> i32 {
+        self.tilt_y
+    }
+
+    fn twist(&self) -> i32 {
+        self.twist
+    }
+
+    fn pointer_type(&self) -> String {
+        self.pointer_type.clone()
+    }
+
+    fn is_primary(&self) -> bool {
+        self.is_primary
+    }
+
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl PointInteraction for SerializedPointData {
+    fn client_coordinates(&self) -> ClientPoint {
+        self.point_data.client_coordinates()
+    }
+
+    fn screen_coordinates(&self) -> ScreenPoint {
+        self.point_data.screen_coordinates()
+    }
+
+    fn element_coordinates(&self) -> ElementPoint {
+        self.point_data.element_coordinates()
+    }
+
+    fn page_coordinates(&self) -> PagePoint {
+        self.point_data.page_coordinates()
+    }
+
+    fn modifiers(&self) -> Modifiers {
+        self.point_data.modifiers()
+    }
+
+    fn held_buttons(&self) -> MouseButtonSet {
+        self.point_data.held_buttons()
+    }
+
+    fn trigger_button(&self) -> Option<MouseButton> {
+        self.point_data.trigger_button()
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl From<&PointerData> for SerializedPointData {
+    fn from(data: &PointerData) -> Self {
+        Self {
+            point_data: data.into(),
+            pointer_id: data.pointer_id(),
+            width: data.width(),
+            height: data.height(),
+            pressure: data.pressure(),
+            tangential_pressure: data.tangential_pressure(),
+            tilt_x: data.tilt_x(),
+            tilt_y: data.tilt_y(),
+            twist: data.twist(),
+            pointer_type: data.pointer_type().to_string(),
+            is_primary: data.is_primary(),
+        }
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl serde::Serialize for PointerData {
+    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        SerializedPointData::from(self).serialize(serializer)
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl<'de> serde::Deserialize<'de> for PointerData {
+    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        let data = SerializedPointData::deserialize(deserializer)?;
+        Ok(Self {
+            inner: Box::new(data),
+        })
     }
 }

+ 69 - 3
packages/html/src/events/scroll.rs

@@ -1,9 +1,75 @@
 use dioxus_core::Event;
 
 pub type ScrollEvent = Event<ScrollData>;
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct ScrollData {}
+
+pub struct ScrollData {
+    inner: Box<dyn HasScrollData>,
+}
+
+impl<E: HasScrollData> From<E> for ScrollData {
+    fn from(e: E) -> Self {
+        Self { inner: Box::new(e) }
+    }
+}
+
+impl ScrollData {
+    /// Downcast this event to a concrete event type
+    pub fn downcast<T: 'static>(&self) -> Option<&T> {
+        self.inner.as_any().downcast_ref::<T>()
+    }
+}
+
+impl std::fmt::Debug for ScrollData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("ScrollData").finish()
+    }
+}
+
+impl PartialEq for ScrollData {
+    fn eq(&self, _other: &Self) -> bool {
+        true
+    }
+}
+
+#[cfg(feature = "serialize")]
+#[derive(serde::Serialize, serde::Deserialize)]
+struct SerializedScrollData {}
+
+#[cfg(feature = "serialize")]
+impl From<&ScrollData> for SerializedScrollData {
+    fn from(_: &ScrollData) -> Self {
+        Self {}
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl HasScrollData for SerializedScrollData {
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl serde::Serialize for ScrollData {
+    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        SerializedScrollData::from(self).serialize(serializer)
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl<'de> serde::Deserialize<'de> for ScrollData {
+    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        let data = SerializedScrollData::deserialize(deserializer)?;
+        Ok(Self {
+            inner: Box::new(data),
+        })
+    }
+}
+
+pub trait HasScrollData: std::any::Any {
+    /// return self as Any
+    fn as_any(&self) -> &dyn std::any::Any;
+}
 
 impl_event! {
     ScrollData;

+ 69 - 3
packages/html/src/events/selection.rs

@@ -1,9 +1,75 @@
 use dioxus_core::Event;
 
 pub type SelectionEvent = Event<SelectionData>;
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct SelectionData {}
+
+pub struct SelectionData {
+    inner: Box<dyn HasSelectionData>,
+}
+
+impl SelectionData {
+    /// Downcast this event to a concrete event type
+    pub fn downcast<T: 'static>(&self) -> Option<&T> {
+        self.inner.as_any().downcast_ref::<T>()
+    }
+}
+
+impl<E: HasSelectionData> From<E> for SelectionData {
+    fn from(e: E) -> Self {
+        Self { inner: Box::new(e) }
+    }
+}
+
+impl std::fmt::Debug for SelectionData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("SelectionData").finish()
+    }
+}
+
+impl PartialEq for SelectionData {
+    fn eq(&self, _other: &Self) -> bool {
+        true
+    }
+}
+
+#[cfg(feature = "serialize")]
+#[derive(serde::Serialize, serde::Deserialize)]
+struct SerializedSelectionData {}
+
+#[cfg(feature = "serialize")]
+impl From<&SelectionData> for SerializedSelectionData {
+    fn from(_: &SelectionData) -> Self {
+        Self {}
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl HasSelectionData for SerializedSelectionData {
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl serde::Serialize for SelectionData {
+    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        SerializedSelectionData::from(self).serialize(serializer)
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl<'de> serde::Deserialize<'de> for SelectionData {
+    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        let data = SerializedSelectionData::deserialize(deserializer)?;
+        Ok(Self {
+            inner: Box::new(data),
+        })
+    }
+}
+
+pub trait HasSelectionData: std::any::Any {
+    /// return self as Any
+    fn as_any(&self) -> &dyn std::any::Any;
+}
 
 impl_event! [
     SelectionData;

+ 69 - 3
packages/html/src/events/toggle.rs

@@ -1,9 +1,75 @@
 use dioxus_core::Event;
 
 pub type ToggleEvent = Event<ToggleData>;
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct ToggleData {}
+
+pub struct ToggleData {
+    inner: Box<dyn HasToggleData>,
+}
+
+impl<E: HasToggleData> From<E> for ToggleData {
+    fn from(e: E) -> Self {
+        Self { inner: Box::new(e) }
+    }
+}
+
+impl std::fmt::Debug for ToggleData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("ToggleData").finish()
+    }
+}
+
+impl PartialEq for ToggleData {
+    fn eq(&self, _other: &Self) -> bool {
+        true
+    }
+}
+
+impl ToggleData {
+    /// Downcast this event to a concrete event type
+    pub fn downcast<T: 'static>(&self) -> Option<&T> {
+        self.inner.as_any().downcast_ref::<T>()
+    }
+}
+
+#[cfg(feature = "serialize")]
+#[derive(serde::Serialize, serde::Deserialize)]
+struct SerializedToggleData {}
+
+#[cfg(feature = "serialize")]
+impl From<&ToggleData> for SerializedToggleData {
+    fn from(_: &ToggleData) -> Self {
+        Self {}
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl HasToggleData for SerializedToggleData {
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl serde::Serialize for ToggleData {
+    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        SerializedToggleData::from(self).serialize(serializer)
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl<'de> serde::Deserialize<'de> for ToggleData {
+    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        let data = SerializedToggleData::deserialize(deserializer)?;
+        Ok(Self {
+            inner: Box::new(data),
+        })
+    }
+}
+
+pub trait HasToggleData: std::any::Any {
+    /// return self as Any
+    fn as_any(&self) -> &dyn std::any::Any;
+}
 
 impl_event! {
     ToggleData;

+ 132 - 10
packages/html/src/events/touch.rs

@@ -1,17 +1,139 @@
 use dioxus_core::Event;
 
 pub type TouchEvent = Event<TouchData>;
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Debug, Clone, PartialEq, Eq)]
 pub struct TouchData {
-    pub alt_key: bool,
-    pub ctrl_key: bool,
-    pub meta_key: bool,
-    pub shift_key: bool,
-    // get_modifier_state: bool,
-    // changedTouches: DOMTouchList,
-    // targetTouches: DOMTouchList,
-    // touches: DOMTouchList,
+    inner: Box<dyn HasTouchData>,
+}
+
+impl<E: HasTouchData> From<E> for TouchData {
+    fn from(e: E) -> Self {
+        Self { inner: Box::new(e) }
+    }
+}
+
+impl std::fmt::Debug for TouchData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("TouchData")
+            .field("alt_key", &self.alt_key())
+            .field("ctrl_key", &self.ctrl_key())
+            .field("meta_key", &self.meta_key())
+            .field("shift_key", &self.shift_key())
+            .finish()
+    }
+}
+
+impl PartialEq for TouchData {
+    fn eq(&self, other: &Self) -> bool {
+        self.alt_key() == other.alt_key()
+            && self.ctrl_key() == other.ctrl_key()
+            && self.meta_key() == other.meta_key()
+            && self.shift_key() == other.shift_key()
+    }
+}
+
+impl TouchData {
+    /// Returns true if the "ALT" key was down when the touch event was fired.
+    pub fn alt_key(&self) -> bool {
+        self.inner.alt_key()
+    }
+
+    /// Returns true if the "CTRL" key was down when the touch event was fired.
+    pub fn ctrl_key(&self) -> bool {
+        self.inner.ctrl_key()
+    }
+
+    /// Returns true if the "META" key was down when the touch event was fired.
+    pub fn meta_key(&self) -> bool {
+        self.inner.meta_key()
+    }
+
+    /// Returns true if the "SHIFT" key was down when the touch event was fired.
+    pub fn shift_key(&self) -> bool {
+        self.inner.shift_key()
+    }
+
+    /// Downcast this event to a concrete event type
+    pub fn downcast<T: 'static>(&self) -> Option<&T> {
+        self.inner.as_any().downcast_ref::<T>()
+    }
+}
+
+#[cfg(feature = "serialize")]
+#[derive(serde::Serialize, serde::Deserialize)]
+struct SerializedTouchData {
+    alt_key: bool,
+    ctrl_key: bool,
+    meta_key: bool,
+    shift_key: bool,
+}
+
+#[cfg(feature = "serialize")]
+impl From<&TouchData> for SerializedTouchData {
+    fn from(data: &TouchData) -> Self {
+        Self {
+            alt_key: data.alt_key(),
+            ctrl_key: data.ctrl_key(),
+            meta_key: data.meta_key(),
+            shift_key: data.shift_key(),
+        }
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl HasTouchData for SerializedTouchData {
+    fn alt_key(&self) -> bool {
+        self.alt_key
+    }
+
+    fn ctrl_key(&self) -> bool {
+        self.ctrl_key
+    }
+
+    fn meta_key(&self) -> bool {
+        self.meta_key
+    }
+
+    fn shift_key(&self) -> bool {
+        self.shift_key
+    }
+
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl serde::Serialize for TouchData {
+    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        SerializedTouchData::from(self).serialize(serializer)
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl<'de> serde::Deserialize<'de> for TouchData {
+    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        let data = SerializedTouchData::deserialize(deserializer)?;
+        Ok(Self {
+            inner: Box::new(data),
+        })
+    }
+}
+
+pub trait HasTouchData: std::any::Any {
+    /// Returns true if the "ALT" key was down when the touch event was fired.
+    fn alt_key(&self) -> bool;
+
+    /// Returns true if the "CTRL" key was down when the touch event was fired.
+    fn ctrl_key(&self) -> bool;
+
+    /// Returns true if the "META" key was down when the touch event was fired.
+    fn meta_key(&self) -> bool;
+
+    /// Returns true if the "SHIFT" key was down when the touch event was fired.
+    fn shift_key(&self) -> bool;
+
+    /// return self as Any
+    fn as_any(&self) -> &dyn std::any::Any;
 }
 
 impl_event! {

+ 96 - 5
packages/html/src/events/transition.rs

@@ -1,12 +1,103 @@
 use dioxus_core::Event;
 
 pub type TransitionEvent = Event<TransitionData>;
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Debug, Clone, PartialEq)]
+
 pub struct TransitionData {
-    pub property_name: String,
-    pub pseudo_element: String,
-    pub elapsed_time: f32,
+    inner: Box<dyn HasTransitionData>,
+}
+
+impl<E: HasTransitionData> From<E> for TransitionData {
+    fn from(e: E) -> Self {
+        Self { inner: Box::new(e) }
+    }
+}
+
+impl std::fmt::Debug for TransitionData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("TransitionData")
+            .field("property_name", &self.inner.property_name())
+            .field("pseudo_element", &self.inner.pseudo_element())
+            .field("elapsed_time", &self.inner.elapsed_time())
+            .finish()
+    }
+}
+
+impl PartialEq for TransitionData {
+    fn eq(&self, other: &Self) -> bool {
+        self.inner.property_name() == other.inner.property_name()
+            && self.inner.pseudo_element() == other.inner.pseudo_element()
+            && self.inner.elapsed_time() == other.inner.elapsed_time()
+    }
+}
+
+impl TransitionData {
+    /// Downcast this event to a concrete event type
+    pub fn downcast<T: 'static>(&self) -> Option<&T> {
+        self.inner.as_any().downcast_ref::<T>()
+    }
+}
+
+#[cfg(feature = "serialize")]
+#[derive(serde::Serialize, serde::Deserialize)]
+struct SerializedTransitionData {
+    property_name: String,
+    pseudo_element: String,
+    elapsed_time: f32,
+}
+
+#[cfg(feature = "serialize")]
+impl From<&TransitionData> for SerializedTransitionData {
+    fn from(data: &TransitionData) -> Self {
+        Self {
+            property_name: data.inner.property_name(),
+            pseudo_element: data.inner.pseudo_element(),
+            elapsed_time: data.inner.elapsed_time(),
+        }
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl HasTransitionData for SerializedTransitionData {
+    fn property_name(&self) -> String {
+        self.property_name.clone()
+    }
+
+    fn pseudo_element(&self) -> String {
+        self.pseudo_element.clone()
+    }
+
+    fn elapsed_time(&self) -> f32 {
+        self.elapsed_time
+    }
+
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl serde::Serialize for TransitionData {
+    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        SerializedTransitionData::from(self).serialize(serializer)
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl<'de> serde::Deserialize<'de> for TransitionData {
+    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        let data = SerializedTransitionData::deserialize(deserializer)?;
+        Ok(Self {
+            inner: Box::new(data),
+        })
+    }
+}
+
+pub trait HasTransitionData: std::any::Any {
+    fn property_name(&self) -> String;
+    fn pseudo_element(&self) -> String;
+    fn elapsed_time(&self) -> f32;
+    /// return self as Any
+    fn as_any(&self) -> &dyn std::any::Any;
 }
 
 impl_event! {

+ 80 - 57
packages/html/src/events/wheel.rs

@@ -1,78 +1,101 @@
 use dioxus_core::Event;
-use euclid::UnknownUnit;
-use std::fmt::{Debug, Formatter};
+use std::fmt::Formatter;
 
-use crate::geometry::{LinesVector, PagesVector, PixelsVector, WheelDelta};
+use crate::geometry::WheelDelta;
 
 pub type WheelEvent = Event<WheelData>;
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Clone, PartialEq, Default)]
+
 pub struct WheelData {
-    #[deprecated(since = "0.3.0", note = "use delta() instead")]
-    pub delta_mode: u32,
-    #[deprecated(since = "0.3.0", note = "use delta() instead")]
-    pub delta_x: f64,
-    #[deprecated(since = "0.3.0", note = "use delta() instead")]
-    pub delta_y: f64,
-    #[deprecated(since = "0.3.0", note = "use delta() instead")]
-    pub delta_z: f64,
+    inner: Box<dyn HasWheelData>,
 }
 
-impl_event![
-    WheelData;
-
-    /// Called when the mouse wheel is rotated over an element.
-    onwheel
-];
+impl<E: HasWheelData> From<E> for WheelData {
+    fn from(e: E) -> Self {
+        Self { inner: Box::new(e) }
+    }
+}
 
-impl WheelData {
-    /// Construct a new WheelData with the specified wheel movement delta
-    pub fn new(delta: WheelDelta) -> Self {
-        let (delta_mode, vector) = match delta {
-            WheelDelta::Pixels(v) => (0, v.cast_unit::<UnknownUnit>()),
-            WheelDelta::Lines(v) => (1, v.cast_unit::<UnknownUnit>()),
-            WheelDelta::Pages(v) => (2, v.cast_unit::<UnknownUnit>()),
-        };
-
-        #[allow(deprecated)]
-        WheelData {
-            delta_mode,
-            delta_x: vector.x,
-            delta_y: vector.y,
-            delta_z: vector.z,
-        }
+impl std::fmt::Debug for WheelData {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("WheelData")
+            .field("delta", &self.delta())
+            .finish()
     }
+}
 
-    /// Construct from the attributes of the web wheel event
-    pub fn from_web_attributes(delta_mode: u32, delta_x: f64, delta_y: f64, delta_z: f64) -> Self {
-        #[allow(deprecated)]
-        Self {
-            delta_mode,
-            delta_x,
-            delta_y,
-            delta_z,
-        }
+impl PartialEq for WheelData {
+    fn eq(&self, other: &Self) -> bool {
+        self.inner.delta() == other.inner.delta()
     }
+}
 
+impl WheelData {
     /// The amount of wheel movement
     #[allow(deprecated)]
     pub fn delta(&self) -> WheelDelta {
-        let x = self.delta_x;
-        let y = self.delta_y;
-        let z = self.delta_z;
-        match self.delta_mode {
-            0 => WheelDelta::Pixels(PixelsVector::new(x, y, z)),
-            1 => WheelDelta::Lines(LinesVector::new(x, y, z)),
-            2 => WheelDelta::Pages(PagesVector::new(x, y, z)),
-            _ => panic!("Invalid delta mode, {:?}", self.delta_mode),
+        self.inner.delta()
+    }
+
+    /// Downcast this event to a concrete event type
+    pub fn downcast<T: 'static>(&self) -> Option<&T> {
+        self.inner.as_any().downcast_ref::<T>()
+    }
+}
+
+#[cfg(feature = "serialize")]
+#[derive(serde::Serialize, serde::Deserialize)]
+struct SerializedWheelData {
+    delta: WheelDelta,
+}
+
+#[cfg(feature = "serialize")]
+impl From<&WheelData> for SerializedWheelData {
+    fn from(data: &WheelData) -> Self {
+        Self {
+            delta: data.inner.delta(),
         }
     }
 }
 
-impl Debug for WheelData {
-    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("WheelData")
-            .field("delta", &self.delta())
-            .finish()
+#[cfg(feature = "serialize")]
+impl HasWheelData for SerializedWheelData {
+    fn delta(&self) -> WheelDelta {
+        self.delta
     }
+
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl serde::Serialize for WheelData {
+    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+        SerializedWheelData::from(self).serialize(serializer)
+    }
+}
+
+#[cfg(feature = "serialize")]
+impl<'de> serde::Deserialize<'de> for WheelData {
+    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+        let data = SerializedWheelData::deserialize(deserializer)?;
+        Ok(Self {
+            inner: Box::new(data),
+        })
+    }
+}
+
+impl_event![
+    WheelData;
+
+    /// Called when the mouse wheel is rotated over an element.
+    onwheel
+];
+
+pub trait HasWheelData: std::any::Any {
+    /// The amount of wheel movement
+    fn delta(&self) -> WheelDelta;
+
+    /// return self as Any
+    fn as_any(&self) -> &dyn std::any::Any;
 }

+ 12 - 2
packages/html/src/geometry.rs

@@ -47,7 +47,7 @@ pub type PagesVector = Vector3D<f64, Pages>;
 /// A vector representing the amount the mouse wheel was moved
 ///
 /// This may be expressed in Pixels, Lines or Pages
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq)]
 #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
 pub enum WheelDelta {
     /// Movement in Pixels
@@ -59,6 +59,16 @@ pub enum WheelDelta {
 }
 
 impl WheelDelta {
+    /// Construct from the attributes of the web wheel event
+    pub fn from_web_attributes(delta_mode: u32, delta_x: f64, delta_y: f64, delta_z: f64) -> Self {
+        match delta_mode {
+            0 => WheelDelta::Pixels(PixelsVector::new(delta_x, delta_y, delta_z)),
+            1 => WheelDelta::Lines(LinesVector::new(delta_x, delta_y, delta_z)),
+            2 => WheelDelta::Pages(PagesVector::new(delta_x, delta_y, delta_z)),
+            _ => panic!("Invalid delta mode, {:?}", delta_mode),
+        }
+    }
+
     /// Convenience function for constructing a WheelDelta with pixel units
     pub fn pixels(x: f64, y: f64, z: f64) -> Self {
         WheelDelta::Pixels(PixelsVector::new(x, y, z))
@@ -96,7 +106,7 @@ impl WheelDelta {
 }
 
 /// Coordinates of a point in the app's interface
-#[derive(Debug)]
+#[derive(Debug, PartialEq)]
 pub struct Coordinates {
     screen: ScreenPoint,
     client: ClientPoint,

+ 1 - 0
packages/html/src/lib.rs

@@ -44,4 +44,5 @@ pub mod prelude {
     pub use crate::eval::*;
     pub use crate::events::*;
     pub use crate::point_interaction::PointInteraction;
+    pub use keyboard_types::{self, Code, Key, Modifiers};
 }

+ 134 - 152
packages/html/src/point_interaction.rs

@@ -1,185 +1,167 @@
-use std::fmt::{Debug, Formatter};
-
 use keyboard_types::Modifiers;
 
 use crate::{
     geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint},
-    input_data::{decode_mouse_button_set, encode_mouse_button_set, MouseButton, MouseButtonSet},
+    input_data::{MouseButton, MouseButtonSet},
 };
 
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Copy, Clone, Default, PartialEq, Eq)]
-pub struct PointData {
-    pub alt_key: bool,
-
-    /// The button number that was pressed (if applicable) when the mouse event was fired.
-    pub button: i16,
-
-    /// Indicates which buttons are pressed on the mouse (or other input device) when a mouse event is triggered.
-    ///
-    /// Each button that can be pressed is represented by a given number (see below). If more than one button is pressed, the button values are added together to produce a new number. For example, if the secondary (2) and auxiliary (4) buttons are pressed simultaneously, the value is 6 (i.e., 2 + 4).
-    ///
-    /// - 1: Primary button (usually the left button)
-    /// - 2: Secondary button (usually the right button)
-    /// - 4: Auxiliary button (usually the mouse wheel button or middle button)
-    /// - 8: 4th button (typically the "Browser Back" button)
-    /// - 16 : 5th button (typically the "Browser Forward" button)
-    pub buttons: u16,
-
-    /// The horizontal coordinate within the application's viewport at which the event occurred (as opposed to the coordinate within the page).
-    ///
-    /// For example, clicking on the left edge of the viewport will always result in a mouse event with a clientX value of 0, regardless of whether the page is scrolled horizontally.
-    pub client_x: i32,
-
-    /// The vertical coordinate within the application's viewport at which the event occurred (as opposed to the coordinate within the page).
-    ///
-    /// For example, clicking on the top edge of the viewport will always result in a mouse event with a clientY value of 0, regardless of whether the page is scrolled vertically.
-    pub client_y: i32,
-
-    /// True if the control key was down when the mouse event was fired.
-    pub ctrl_key: bool,
-
-    /// True if the meta key was down when the mouse event was fired.
-    pub meta_key: bool,
-
-    /// The offset in the X coordinate of the mouse pointer between that event and the padding edge of the target node.
-    pub offset_x: i32,
-
-    /// The offset in the Y coordinate of the mouse pointer between that event and the padding edge of the target node.
-    pub offset_y: i32,
-
-    /// The X (horizontal) coordinate (in pixels) of the mouse, relative to the left edge of the entire document. This includes any portion of the document not currently visible.
-    ///
-    /// Being based on the edge of the document as it is, this property takes into account any horizontal scrolling of the page. For example, if the page is scrolled such that 200 pixels of the left side of the document are scrolled out of view, and the mouse is clicked 100 pixels inward from the left edge of the view, the value returned by pageX will be 300.
-    pub page_x: i32,
-
-    /// The Y (vertical) coordinate in pixels of the event relative to the whole document.
-    ///
-    /// See `page_x`.
-    pub page_y: i32,
-
-    /// The X coordinate of the mouse pointer in global (screen) coordinates.
-    pub screen_x: i32,
-
-    /// The Y coordinate of the mouse pointer in global (screen) coordinates.
-    pub screen_y: i32,
-
-    /// True if the shift key was down when the mouse event was fired.
-    pub shift_key: bool,
-}
-
-impl PointData {
-    pub fn new(
-        trigger_button: Option<MouseButton>,
-        held_buttons: MouseButtonSet,
-        coordinates: Coordinates,
-        modifiers: Modifiers,
-    ) -> Self {
-        let alt_key = modifiers.contains(Modifiers::ALT);
-        let ctrl_key = modifiers.contains(Modifiers::CONTROL);
-        let meta_key = modifiers.contains(Modifiers::META);
-        let shift_key = modifiers.contains(Modifiers::SHIFT);
-
-        let [client_x, client_y]: [i32; 2] = coordinates.client().cast().into();
-        let [offset_x, offset_y]: [i32; 2] = coordinates.element().cast().into();
-        let [page_x, page_y]: [i32; 2] = coordinates.page().cast().into();
-        let [screen_x, screen_y]: [i32; 2] = coordinates.screen().cast().into();
-        Self {
-            button: trigger_button
-                .map_or(MouseButton::default(), |b| b)
-                .into_web_code(),
-            buttons: encode_mouse_button_set(held_buttons),
-            meta_key,
-            ctrl_key,
-            shift_key,
-            alt_key,
-            client_x,
-            client_y,
-            screen_x,
-            screen_y,
-            offset_x,
-            offset_y,
-            page_x,
-            page_y,
-        }
+// #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
+// #[derive(Copy, Clone, Default, PartialEq, Eq)]
+// pub struct PointData {
+//     pub alt_key: bool,
+
+//     /// The button number that was pressed (if applicable) when the mouse event was fired.
+//     pub button: i16,
+
+//     /// Indicates which buttons are pressed on the mouse (or other input device) when a mouse event is triggered.
+//     ///
+//     /// Each button that can be pressed is represented by a given number (see below). If more than one button is pressed, the button values are added together to produce a new number. For example, if the secondary (2) and auxiliary (4) buttons are pressed simultaneously, the value is 6 (i.e., 2 + 4).
+//     ///
+//     /// - 1: Primary button (usually the left button)
+//     /// - 2: Secondary button (usually the right button)
+//     /// - 4: Auxiliary button (usually the mouse wheel button or middle button)
+//     /// - 8: 4th button (typically the "Browser Back" button)
+//     /// - 16 : 5th button (typically the "Browser Forward" button)
+//     pub buttons: u16,
+
+//     /// The horizontal coordinate within the application's viewport at which the event occurred (as opposed to the coordinate within the page).
+//     ///
+//     /// For example, clicking on the left edge of the viewport will always result in a mouse event with a clientX value of 0, regardless of whether the page is scrolled horizontally.
+//     pub client_x: i32,
+
+//     /// The vertical coordinate within the application's viewport at which the event occurred (as opposed to the coordinate within the page).
+//     ///
+//     /// For example, clicking on the top edge of the viewport will always result in a mouse event with a clientY value of 0, regardless of whether the page is scrolled vertically.
+//     pub client_y: i32,
+
+//     /// True if the control key was down when the mouse event was fired.
+//     pub ctrl_key: bool,
+
+//     /// True if the meta key was down when the mouse event was fired.
+//     pub meta_key: bool,
+
+//     /// The offset in the X coordinate of the mouse pointer between that event and the padding edge of the target node.
+//     pub offset_x: i32,
+
+//     /// The offset in the Y coordinate of the mouse pointer between that event and the padding edge of the target node.
+//     pub offset_y: i32,
+
+//     /// The X (horizontal) coordinate (in pixels) of the mouse, relative to the left edge of the entire document. This includes any portion of the document not currently visible.
+//     ///
+//     /// Being based on the edge of the document as it is, this property takes into account any horizontal scrolling of the page. For example, if the page is scrolled such that 200 pixels of the left side of the document are scrolled out of view, and the mouse is clicked 100 pixels inward from the left edge of the view, the value returned by pageX will be 300.
+//     pub page_x: i32,
+
+//     /// The Y (vertical) coordinate in pixels of the event relative to the whole document.
+//     ///
+//     /// See `page_x`.
+//     pub page_y: i32,
+
+//     /// The X coordinate of the mouse pointer in global (screen) coordinates.
+//     pub screen_x: i32,
+
+//     /// The Y coordinate of the mouse pointer in global (screen) coordinates.
+//     pub screen_y: i32,
+
+//     /// True if the shift key was down when the mouse event was fired.
+//     pub shift_key: bool,
+// }
+
+pub trait PointInteraction: std::any::Any {
+    /// Gets the coordinates of the pointer event.
+    fn coordinates(&self) -> Coordinates {
+        Coordinates::new(
+            self.screen_coordinates(),
+            self.client_coordinates(),
+            self.element_coordinates(),
+            self.page_coordinates(),
+        )
     }
-}
 
-impl Debug for PointData {
-    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("PointInteraction")
-            .field("coordinates", &self.coordinates())
-            .field("modifiers", &self.modifiers())
-            .field("held_buttons", &self.held_buttons())
-            .field("trigger_button", &self.trigger_button())
-            .finish()
-    }
-}
+    /// Gets the coordinates of the pointer event relative to the browser viewport.
+    fn client_coordinates(&self) -> ClientPoint;
 
-impl PointInteraction for PointData {
-    fn get_point_data(&self) -> PointData {
-        *self
-    }
+    /// Gets the coordinates of the pointer event relative to the screen.
+    fn screen_coordinates(&self) -> ScreenPoint;
+
+    /// Gets the coordinates of the pointer event relative to the target element.
+    fn element_coordinates(&self) -> ElementPoint;
+
+    /// Gets the coordinates of the pointer event relative to the page.
+    fn page_coordinates(&self) -> PagePoint;
+
+    /// Gets the modifiers of the pointer event.
+    fn modifiers(&self) -> Modifiers;
+
+    /// Gets the buttons that are currently held down.
+    fn held_buttons(&self) -> MouseButtonSet;
+
+    /// Gets the button that triggered the event.
+    fn trigger_button(&self) -> Option<MouseButton>;
 }
 
-pub trait PointInteraction {
-    fn get_point_data(&self) -> PointData;
+#[cfg(feature = "serialize")]
+#[derive(serde::Serialize, serde::Deserialize)]
+pub(crate) struct SerializedPointInteraction {
+    pub(crate) client_coordinates: ClientPoint,
+    pub(crate) element_coordinates: ElementPoint,
+    pub(crate) page_coordinates: PagePoint,
+    pub(crate) screen_coordinates: ScreenPoint,
+    pub(crate) modifiers: Modifiers,
+    pub(crate) held_buttons: MouseButtonSet,
+    pub(crate) trigger_button: Option<MouseButton>,
+}
 
-    fn coordinates(&self) -> Coordinates {
-        let point_data = self.get_point_data();
-        Coordinates::new(
-            ScreenPoint::new(point_data.screen_x.into(), point_data.screen_y.into()),
-            ClientPoint::new(point_data.client_x.into(), point_data.client_y.into()),
-            ElementPoint::new(point_data.offset_x.into(), point_data.offset_y.into()),
-            PagePoint::new(point_data.page_x.into(), point_data.page_y.into()),
-        )
+#[cfg(feature = "serialize")]
+impl<E: PointInteraction> From<&E> for SerializedPointInteraction {
+    fn from(data: &E) -> Self {
+        Self {
+            client_coordinates: data.client_coordinates(),
+            element_coordinates: data.element_coordinates(),
+            page_coordinates: data.page_coordinates(),
+            screen_coordinates: data.screen_coordinates(),
+            modifiers: data.modifiers(),
+            held_buttons: data.held_buttons(),
+            trigger_button: data.trigger_button(),
+        }
     }
+}
 
+#[cfg(feature = "serialize")]
+impl PointInteraction for SerializedPointInteraction {
     fn client_coordinates(&self) -> ClientPoint {
-        let point_data = self.get_point_data();
-        ClientPoint::new(point_data.client_x.into(), point_data.client_y.into())
-    }
-
-    fn screen_coordinates(&self) -> ScreenPoint {
-        let point_data = self.get_point_data();
-        ScreenPoint::new(point_data.screen_x.into(), point_data.screen_y.into())
+        self.client_coordinates
     }
 
     fn element_coordinates(&self) -> ElementPoint {
-        let point_data = self.get_point_data();
-        ElementPoint::new(point_data.offset_x.into(), point_data.offset_y.into())
+        self.element_coordinates
     }
 
     fn page_coordinates(&self) -> PagePoint {
-        let point_data = self.get_point_data();
-        PagePoint::new(point_data.page_x.into(), point_data.page_y.into())
+        self.page_coordinates
     }
 
-    fn modifiers(&self) -> Modifiers {
-        let mut modifiers = Modifiers::empty();
-        let point_data = self.get_point_data();
+    fn screen_coordinates(&self) -> ScreenPoint {
+        self.screen_coordinates
+    }
 
-        if point_data.alt_key {
-            modifiers.insert(Modifiers::ALT);
-        }
-        if point_data.ctrl_key {
-            modifiers.insert(Modifiers::CONTROL);
-        }
-        if point_data.meta_key {
-            modifiers.insert(Modifiers::META);
-        }
-        if point_data.shift_key {
-            modifiers.insert(Modifiers::SHIFT);
-        }
+    fn coordinates(&self) -> Coordinates {
+        Coordinates::new(
+            self.screen_coordinates(),
+            self.client_coordinates(),
+            self.element_coordinates(),
+            self.page_coordinates(),
+        )
+    }
 
-        modifiers
+    fn modifiers(&self) -> Modifiers {
+        self.modifiers
     }
 
     fn held_buttons(&self) -> MouseButtonSet {
-        decode_mouse_button_set(self.get_point_data().buttons)
+        self.held_buttons
     }
 
     fn trigger_button(&self) -> Option<MouseButton> {
-        Some(MouseButton::from_web_code(self.get_point_data().button))
+        self.trigger_button
     }
 }

+ 2 - 2
packages/html/src/transit.rs

@@ -4,7 +4,7 @@ use crate::events::*;
 use dioxus_core::ElementId;
 use serde::{Deserialize, Serialize};
 
-#[derive(Serialize, Debug, Clone, PartialEq)]
+#[derive(Serialize, Debug, PartialEq)]
 pub struct HtmlEvent {
     pub element: ElementId,
     pub name: String,
@@ -136,7 +136,7 @@ impl HtmlEvent {
     }
 }
 
-#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
+#[derive(Deserialize, Serialize, Debug, PartialEq)]
 #[serde(untagged)]
 #[non_exhaustive]
 pub enum EventData {

+ 283 - 116
packages/html/src/web_sys_bind/events.rs

@@ -1,12 +1,12 @@
+use crate::events::HasKeyboardData;
 use crate::events::{
-    AnimationData, CompositionData, KeyboardData, MouseData, PointerData, TouchData,
+    AnimationData, CompositionData, HasTouchData, KeyboardData, MouseData, PointerData, TouchData,
     TransitionData, WheelData,
 };
-use crate::input_data::decode_key_location;
-use crate::point_interaction::PointData;
-use crate::{DragData, MountedData};
+use crate::geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint};
+use crate::input_data::{decode_key_location, decode_mouse_button_set, MouseButton};
+use crate::prelude::*;
 use keyboard_types::{Code, Key, Modifiers};
-use std::convert::TryInto;
 use std::str::FromStr;
 use wasm_bindgen::{JsCast, JsValue};
 use web_sys::{
@@ -20,7 +20,7 @@ macro_rules! uncheck_convert {
             #[inline]
             fn from(e: Event) -> Self {
                 let e: $t = e.unchecked_into();
-                Self::from(&e)
+                Self::from(e)
             }
         }
 
@@ -28,7 +28,7 @@ macro_rules! uncheck_convert {
             #[inline]
             fn from(e: &Event) -> Self {
                 let e: &$t = e.unchecked_ref();
-                Self::from(e)
+                Self::from(e.clone())
             }
         }
     };
@@ -38,152 +38,283 @@ macro_rules! uncheck_convert {
 }
 
 uncheck_convert![
-    CompositionEvent => CompositionData,
-    KeyboardEvent    => KeyboardData,
-    MouseEvent       => MouseData,
-    MouseEvent       => DragData,
-    TouchEvent       => TouchData,
-    PointerEvent     => PointerData,
-    WheelEvent       => WheelData,
-    AnimationEvent   => AnimationData,
-    TransitionEvent  => TransitionData,
+    web_sys::CompositionEvent => CompositionData,
+    web_sys::KeyboardEvent    => KeyboardData,
+    web_sys::MouseEvent       => MouseData,
+    web_sys::TouchEvent       => TouchData,
+    web_sys::PointerEvent     => PointerData,
+    web_sys::WheelEvent       => WheelData,
+    web_sys::AnimationEvent   => AnimationData,
+    web_sys::TransitionEvent  => TransitionData,
+    web_sys::MouseEvent       => DragData,
+    web_sys::FocusEvent       => FocusData,
 ];
 
-impl From<&CompositionEvent> for CompositionData {
-    fn from(e: &CompositionEvent) -> Self {
-        Self {
-            data: e.data().unwrap_or_default(),
+impl HasCompositionData for CompositionEvent {
+    fn data(&self) -> std::string::String {
+        self.data().unwrap_or_default()
+    }
+
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+impl HasKeyboardData for KeyboardEvent {
+    fn key(&self) -> Key {
+        Key::from_str(self.key().as_str()).unwrap_or(Key::Unidentified)
+    }
+
+    fn code(&self) -> Code {
+        Code::from_str(self.code().as_str()).unwrap_or(Code::Unidentified)
+    }
+
+    fn modifiers(&self) -> Modifiers {
+        let mut modifiers = Modifiers::empty();
+
+        if self.alt_key() {
+            modifiers.insert(Modifiers::ALT);
+        }
+        if self.ctrl_key() {
+            modifiers.insert(Modifiers::CONTROL);
         }
+        if self.meta_key() {
+            modifiers.insert(Modifiers::META);
+        }
+        if self.shift_key() {
+            modifiers.insert(Modifiers::SHIFT);
+        }
+
+        modifiers
+    }
+
+    fn location(&self) -> keyboard_types::Location {
+        decode_key_location(self.location() as usize)
+    }
+
+    fn is_auto_repeating(&self) -> bool {
+        self.repeat()
+    }
+
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
     }
 }
 
-impl From<&KeyboardEvent> for KeyboardData {
-    fn from(e: &KeyboardEvent) -> Self {
+impl HasDragData for MouseEvent {}
+
+impl PointInteraction for MouseEvent {
+    fn client_coordinates(&self) -> ClientPoint {
+        ClientPoint::new(self.client_x().into(), self.client_y().into())
+    }
+
+    fn element_coordinates(&self) -> ElementPoint {
+        ElementPoint::new(self.offset_x().into(), self.offset_y().into())
+    }
+
+    fn page_coordinates(&self) -> PagePoint {
+        PagePoint::new(self.page_x().into(), self.page_y().into())
+    }
+
+    fn screen_coordinates(&self) -> ScreenPoint {
+        ScreenPoint::new(self.screen_x().into(), self.screen_y().into())
+    }
+
+    fn modifiers(&self) -> Modifiers {
         let mut modifiers = Modifiers::empty();
 
-        if e.alt_key() {
+        if self.alt_key() {
             modifiers.insert(Modifiers::ALT);
         }
-        if e.ctrl_key() {
+        if self.ctrl_key() {
             modifiers.insert(Modifiers::CONTROL);
         }
-        if e.meta_key() {
+        if self.meta_key() {
             modifiers.insert(Modifiers::META);
         }
-        if e.shift_key() {
+        if self.shift_key() {
             modifiers.insert(Modifiers::SHIFT);
         }
 
-        Self::new(
-            Key::from_str(&e.key()).expect("could not parse key"),
-            Code::from_str(&e.code()).expect("could not parse code"),
-            decode_key_location(
-                e.location()
-                    .try_into()
-                    .expect("could not convert location to u32"),
-            ),
-            e.repeat(),
-            modifiers,
-        )
+        modifiers
+    }
+
+    fn held_buttons(&self) -> crate::input_data::MouseButtonSet {
+        decode_mouse_button_set(self.buttons())
+    }
+
+    fn trigger_button(&self) -> Option<MouseButton> {
+        Some(MouseButton::from_web_code(self.button()))
     }
 }
 
-impl From<&MouseEvent> for MouseData {
-    fn from(e: &MouseEvent) -> Self {
-        Self::new(PointData {
-            button: e.button(),
-            buttons: e.buttons(),
-            meta_key: e.meta_key(),
-            alt_key: e.alt_key(),
-            ctrl_key: e.ctrl_key(),
-            shift_key: e.shift_key(),
-            client_x: e.client_x(),
-            client_y: e.client_y(),
-            screen_x: e.screen_x(),
-            screen_y: e.screen_y(),
-            offset_x: e.offset_x(),
-            offset_y: e.offset_y(),
-            page_x: e.page_x(),
-            page_y: e.page_y(),
-        })
+impl HasMouseData for MouseEvent {
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
     }
 }
 
-impl From<&MouseEvent> for DragData {
-    fn from(value: &MouseEvent) -> Self {
-        Self {
-            mouse: MouseData::from(value),
-        }
+impl HasTouchData for TouchEvent {
+    fn alt_key(&self) -> bool {
+        self.alt_key()
+    }
+
+    fn ctrl_key(&self) -> bool {
+        self.ctrl_key()
+    }
+
+    fn meta_key(&self) -> bool {
+        self.meta_key()
+    }
+
+    fn shift_key(&self) -> bool {
+        self.shift_key()
+    }
+
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
     }
 }
 
-impl From<&TouchEvent> for TouchData {
-    fn from(e: &TouchEvent) -> Self {
-        Self {
-            alt_key: e.alt_key(),
-            ctrl_key: e.ctrl_key(),
-            meta_key: e.meta_key(),
-            shift_key: e.shift_key(),
+impl HasPointerData for PointerEvent {
+    fn pointer_id(&self) -> i32 {
+        self.pointer_id()
+    }
+
+    fn width(&self) -> i32 {
+        self.width()
+    }
+
+    fn height(&self) -> i32 {
+        self.height()
+    }
+
+    fn pressure(&self) -> f32 {
+        self.pressure()
+    }
+
+    fn tangential_pressure(&self) -> f32 {
+        self.tangential_pressure()
+    }
+
+    fn tilt_x(&self) -> i32 {
+        self.tilt_x()
+    }
+
+    fn tilt_y(&self) -> i32 {
+        self.tilt_y()
+    }
+
+    fn twist(&self) -> i32 {
+        self.twist()
+    }
+
+    fn pointer_type(&self) -> String {
+        self.pointer_type()
+    }
+
+    fn is_primary(&self) -> bool {
+        self.is_primary()
+    }
+
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+impl PointInteraction for PointerEvent {
+    fn client_coordinates(&self) -> ClientPoint {
+        ClientPoint::new(self.client_x().into(), self.client_y().into())
+    }
+
+    fn screen_coordinates(&self) -> ScreenPoint {
+        ScreenPoint::new(self.screen_x().into(), self.screen_y().into())
+    }
+
+    fn element_coordinates(&self) -> ElementPoint {
+        ElementPoint::new(self.offset_x().into(), self.offset_y().into())
+    }
+
+    fn page_coordinates(&self) -> PagePoint {
+        PagePoint::new(self.page_x().into(), self.page_y().into())
+    }
+
+    fn modifiers(&self) -> Modifiers {
+        let mut modifiers = Modifiers::empty();
+
+        if self.alt_key() {
+            modifiers.insert(Modifiers::ALT);
+        }
+        if self.ctrl_key() {
+            modifiers.insert(Modifiers::CONTROL);
+        }
+        if self.meta_key() {
+            modifiers.insert(Modifiers::META);
         }
+        if self.shift_key() {
+            modifiers.insert(Modifiers::SHIFT);
+        }
+
+        modifiers
+    }
+
+    fn held_buttons(&self) -> crate::input_data::MouseButtonSet {
+        decode_mouse_button_set(self.buttons())
+    }
+
+    fn trigger_button(&self) -> Option<MouseButton> {
+        Some(MouseButton::from_web_code(self.button()))
     }
 }
 
-impl From<&PointerEvent> for PointerData {
-    fn from(e: &PointerEvent) -> Self {
-        Self::new(
-            PointData {
-                button: e.button(),
-                buttons: e.buttons(),
-                meta_key: e.meta_key(),
-                alt_key: e.alt_key(),
-                ctrl_key: e.ctrl_key(),
-                shift_key: e.shift_key(),
-                client_x: e.client_x(),
-                client_y: e.client_y(),
-                screen_x: e.screen_x(),
-                screen_y: e.screen_y(),
-                offset_x: e.offset_x(),
-                offset_y: e.offset_y(),
-                page_x: e.page_x(),
-                page_y: e.page_y(),
-            },
-            e.pointer_id(),
-            e.width(),
-            e.height(),
-            e.pressure(),
-            e.tangential_pressure(),
-            e.tilt_x(),
-            e.tilt_y(),
-            e.twist(),
-            e.pointer_type(),
-            e.is_primary(),
+impl HasWheelData for WheelEvent {
+    fn delta(&self) -> crate::geometry::WheelDelta {
+        crate::geometry::WheelDelta::from_web_attributes(
+            self.delta_mode(),
+            self.delta_x(),
+            self.delta_y(),
+            self.delta_z(),
         )
     }
-}
 
-impl From<&WheelEvent> for WheelData {
-    fn from(e: &WheelEvent) -> Self {
-        WheelData::from_web_attributes(e.delta_mode(), e.delta_x(), e.delta_y(), e.delta_z())
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
     }
 }
 
-impl From<&AnimationEvent> for AnimationData {
-    fn from(e: &AnimationEvent) -> Self {
-        Self {
-            elapsed_time: e.elapsed_time(),
-            animation_name: e.animation_name(),
-            pseudo_element: e.pseudo_element(),
-        }
+impl HasAnimationData for AnimationEvent {
+    fn animation_name(&self) -> String {
+        self.animation_name()
+    }
+
+    fn pseudo_element(&self) -> String {
+        self.pseudo_element()
+    }
+
+    fn elapsed_time(&self) -> f32 {
+        self.elapsed_time()
+    }
+
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
     }
 }
 
-impl From<&TransitionEvent> for TransitionData {
-    fn from(e: &TransitionEvent) -> Self {
-        Self {
-            elapsed_time: e.elapsed_time(),
-            property_name: e.property_name(),
-            pseudo_element: e.pseudo_element(),
-        }
+impl HasTransitionData for TransitionEvent {
+    fn elapsed_time(&self) -> f32 {
+        self.elapsed_time()
+    }
+
+    fn property_name(&self) -> String {
+        self.property_name()
+    }
+
+    fn pseudo_element(&self) -> String {
+        self.pseudo_element()
+    }
+
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
     }
 }
 
@@ -209,8 +340,8 @@ impl crate::RenderedElementBacking for web_sys::Element {
         Box::pin(async { result })
     }
 
-    fn get_raw_element(&self) -> crate::MountedResult<&dyn std::any::Any> {
-        Ok(self)
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
     }
 
     fn scroll_to(
@@ -254,3 +385,39 @@ impl std::fmt::Display for FocusError {
 }
 
 impl std::error::Error for FocusError {}
+
+impl HasClipboardData for Event {
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+impl From<&Event> for ClipboardData {
+    fn from(e: &Event) -> Self {
+        ClipboardData::new(e.clone())
+    }
+}
+
+impl HasFocusData for web_sys::FocusEvent {
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+impl HasToggleData for web_sys::Event {
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+impl HasSelectionData for web_sys::Event {
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
+impl HasMediaData for web_sys::Event {
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}

+ 87 - 55
packages/web/src/dom.rs

@@ -10,12 +10,12 @@
 use dioxus_core::{
     BorrowedAttributeValue, ElementId, Mutation, Template, TemplateAttribute, TemplateNode,
 };
-use dioxus_html::{event_bubbles, CompositionData, FormData, MountedData};
+use dioxus_html::{event_bubbles, FileEngine, HasFormData, HasImageData, MountedData};
 use dioxus_interpreter_js::{get_node, minimal_bindings, save_template, Channel};
 use futures_channel::mpsc;
 use js_sys::Array;
 use rustc_hash::FxHashMap;
-use std::{any::Any, rc::Rc};
+use std::{any::Any, collections::HashMap, rc::Rc};
 use wasm_bindgen::{closure::Closure, prelude::wasm_bindgen, JsCast, JsValue};
 use web_sys::{Document, Element, Event};
 
@@ -269,30 +269,29 @@ pub fn virtual_event_from_websys_event(event: web_sys::Event, target: Element) -
     use dioxus_html::events::*;
 
     match event.type_().as_str() {
-        "copy" | "cut" | "paste" => Rc::new(ClipboardData {}),
+        "copy" | "cut" | "paste" => Rc::new(ClipboardData::from(event)),
         "compositionend" | "compositionstart" | "compositionupdate" => {
-            make_composition_event(&event)
+            Rc::new(CompositionData::from(event))
         }
         "keydown" | "keypress" | "keyup" => Rc::new(KeyboardData::from(event)),
-        "focus" | "blur" | "focusout" | "focusin" => Rc::new(FocusData {}),
+        "focus" | "blur" | "focusout" | "focusin" => Rc::new(FocusData::from(event)),
 
-        "change" | "input" | "invalid" | "reset" | "submit" => read_input_to_data(target),
+        "change" | "input" | "invalid" | "reset" | "submit" => {
+            Rc::new(WebFormData::new(target, event))
+        }
 
         "click" | "contextmenu" | "dblclick" | "doubleclick" | "mousedown" | "mouseenter"
         | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
             Rc::new(MouseData::from(event))
         }
         "drag" | "dragend" | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart"
-        | "drop" => {
-            let mouse = MouseData::from(event);
-            Rc::new(DragData { mouse })
-        }
+        | "drop" => Rc::new(DragData::from(event)),
 
         "pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
         | "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
             Rc::new(PointerData::from(event))
         }
-        "select" => Rc::new(SelectionData {}),
+        "select" => Rc::new(SelectionData::from(event)),
         "touchcancel" | "touchend" | "touchmove" | "touchstart" => Rc::new(TouchData::from(event)),
 
         "scroll" => Rc::new(()),
@@ -304,22 +303,15 @@ pub fn virtual_event_from_websys_event(event: web_sys::Event, target: Element) -
         "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
         | "ended" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
         | "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
-        | "timeupdate" | "volumechange" | "waiting" => Rc::new(MediaData {}),
-        "error" => Rc::new(ImageData { load_error: true }),
-        "load" => Rc::new(ImageData { load_error: false }),
-        "toggle" => Rc::new(ToggleData {}),
+        | "timeupdate" | "volumechange" | "waiting" => Rc::new(MediaData::from(event)),
+        "error" => Rc::new(WebImageEvent::new(event, true)),
+        "load" => Rc::new(WebImageEvent::new(event, false)),
+        "toggle" => Rc::new(ToggleData::from(event)),
 
         _ => Rc::new(()),
     }
 }
 
-fn make_composition_event(event: &Event) -> Rc<CompositionData> {
-    let evt: &web_sys::CompositionEvent = event.dyn_ref().unwrap();
-    Rc::new(CompositionData {
-        data: evt.data().unwrap_or_default(),
-    })
-}
-
 pub(crate) fn load_document() -> Document {
     web_sys::window()
         .expect("should have access to the Window")
@@ -327,11 +319,42 @@ pub(crate) fn load_document() -> Document {
         .expect("should have access to the Document")
 }
 
-fn read_input_to_data(target: Element) -> Rc<FormData> {
-    // todo: these handlers might get really slow if the input box gets large and allocation pressure is heavy
-    // don't have a good solution with the serialized event problem
+struct WebImageEvent {
+    raw: Event,
+    error: bool,
+}
+
+impl WebImageEvent {
+    fn new(raw: Event, error: bool) -> Self {
+        Self { raw, error }
+    }
+}
+
+impl HasImageData for WebImageEvent {
+    fn load_error(&self) -> bool {
+        self.error
+    }
+
+    fn as_any(&self) -> &dyn Any {
+        &self.raw
+    }
+}
 
-    let value: String = target
+struct WebFormData {
+    element: Element,
+    raw: Event,
+}
+
+impl WebFormData {
+    fn new(element: Element, raw: Event) -> Self {
+        Self { element, raw }
+    }
+}
+
+impl HasFormData for WebFormData {
+    fn value(&self) -> String {
+        let target = &self.element;
+        target
         .dyn_ref()
         .map(|input: &web_sys::HtmlInputElement| {
             // todo: special case more input types
@@ -364,44 +387,53 @@ fn read_input_to_data(target: Element) -> Rc<FormData> {
                 .unwrap()
                 .text_content()
         })
-        .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener");
+        .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener")
+    }
 
-    let mut values = std::collections::HashMap::new();
+    fn values(&self) -> HashMap<String, Vec<String>> {
+        let mut values = std::collections::HashMap::new();
 
-    // try to fill in form values
-    if let Some(form) = target.dyn_ref::<web_sys::HtmlFormElement>() {
-        let form_data = get_form_data(form);
-        for value in form_data.entries().into_iter().flatten() {
-            if let Ok(array) = value.dyn_into::<Array>() {
-                if let Some(name) = array.get(0).as_string() {
-                    if let Ok(item_values) = array.get(1).dyn_into::<Array>() {
-                        let item_values =
-                            item_values.iter().filter_map(|v| v.as_string()).collect();
+        // try to fill in form values
+        if let Some(form) = self.element.dyn_ref::<web_sys::HtmlFormElement>() {
+            let form_data = get_form_data(form);
+            for value in form_data.entries().into_iter().flatten() {
+                if let Ok(array) = value.dyn_into::<Array>() {
+                    if let Some(name) = array.get(0).as_string() {
+                        if let Ok(item_values) = array.get(1).dyn_into::<Array>() {
+                            let item_values =
+                                item_values.iter().filter_map(|v| v.as_string()).collect();
 
-                        values.insert(name, item_values);
+                            values.insert(name, item_values);
+                        }
                     }
                 }
             }
         }
+
+        values
     }
 
-    #[cfg(not(feature = "file_engine"))]
-    let files = None;
-    #[cfg(feature = "file_engine")]
-    let files = target
-        .dyn_ref()
-        .and_then(|input: &web_sys::HtmlInputElement| {
-            input.files().and_then(|files| {
-                crate::file_engine::WebFileEngine::new(files)
-                    .map(|f| std::sync::Arc::new(f) as std::sync::Arc<dyn dioxus_html::FileEngine>)
-            })
-        });
-
-    Rc::new(FormData {
-        value,
-        values,
-        files,
-    })
+    fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
+        #[cfg(not(feature = "file_engine"))]
+        let files = None;
+        #[cfg(feature = "file_engine")]
+        let files = self
+            .element
+            .dyn_ref()
+            .and_then(|input: &web_sys::HtmlInputElement| {
+                input.files().and_then(|files| {
+                    crate::file_engine::WebFileEngine::new(files).map(|f| {
+                        std::sync::Arc::new(f) as std::sync::Arc<dyn dioxus_html::FileEngine>
+                    })
+                })
+            });
+
+        files
+    }
+
+    fn as_any(&self) -> &dyn Any {
+        &self.raw
+    }
 }
 
 // web-sys does not expose the keys api for form data, so we need to manually bind to it