From 50faa7dd08d548321f9475ae0b20915599cbf168 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 19 Dec 2022 14:14:01 -0800 Subject: [PATCH] feat: add proper event type --- packages/desktop/src/main.js | 7 + packages/html/Cargo.toml | 8 +- packages/html/src/events/animation.rs | 2 +- packages/html/src/events/clipboard.rs | 2 +- packages/html/src/events/composition.rs | 2 +- packages/html/src/events/drag.rs | 7 +- packages/html/src/events/focus.rs | 2 +- packages/html/src/events/form.rs | 27 ++- packages/html/src/events/image.rs | 2 +- packages/html/src/events/keyboard.rs | 2 +- packages/html/src/events/media.rs | 2 +- packages/html/src/events/mouse.rs | 2 +- packages/html/src/events/pointer.rs | 2 +- packages/html/src/events/scroll.rs | 2 +- packages/html/src/events/selection.rs | 2 +- packages/html/src/events/toggle.rs | 2 +- packages/html/src/events/touch.rs | 2 +- packages/html/src/events/transition.rs | 2 +- packages/html/src/events/wheel.rs | 2 +- packages/html/src/lib.rs | 6 + packages/html/src/transit.rs | 217 ++++++++++++++++++++++++ packages/interpreter/src/interpreter.js | 18 +- 22 files changed, 273 insertions(+), 47 deletions(-) create mode 100644 packages/desktop/src/main.js create mode 100644 packages/html/src/transit.rs diff --git a/packages/desktop/src/main.js b/packages/desktop/src/main.js new file mode 100644 index 000000000..50a3bdc34 --- /dev/null +++ b/packages/desktop/src/main.js @@ -0,0 +1,7 @@ +export function main(rootname = "main") { + let root = window.document.getElementById(rootname); + if (root != null) { + window.interpreter = new Interpreter(root); + window.ipc.postMessage(serializeIpcMessage("initialize")); + } +} diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 446da9303..3436dcc93 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -19,6 +19,7 @@ euclid = "0.22.7" enumset = "1.0.11" keyboard-types = "0.6.2" async-trait = "0.1.58" +serde-value = "0.7.0" [dependencies.web-sys] optional = true @@ -39,7 +40,10 @@ features = [ "ClipboardEvent", ] +[dev-dependencies] +serde_json = "*" + [features] -default = [] -serialize = ["serde", "serde_repr", "euclid/serde", "keyboard-types/serde"] +default = ["serialize"] +serialize = ["serde", "serde_repr", "euclid/serde", "keyboard-types/serde", "dioxus-core/serialize"] wasm-bind = ["web-sys", "wasm-bindgen"] diff --git a/packages/html/src/events/animation.rs b/packages/html/src/events/animation.rs index fb6dc64ca..4495191fc 100644 --- a/packages/html/src/events/animation.rs +++ b/packages/html/src/events/animation.rs @@ -3,7 +3,7 @@ use dioxus_core::Event; pub type AnimationEvent = Event; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct AnimationData { pub animation_name: String, pub pseudo_element: String, diff --git a/packages/html/src/events/clipboard.rs b/packages/html/src/events/clipboard.rs index e7031d061..54390c6b0 100644 --- a/packages/html/src/events/clipboard.rs +++ b/packages/html/src/events/clipboard.rs @@ -2,7 +2,7 @@ use dioxus_core::Event; pub type ClipboardEvent = Event; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct ClipboardData { // DOMDataTransfer clipboardData } diff --git a/packages/html/src/events/composition.rs b/packages/html/src/events/composition.rs index 59009c1f0..480868f24 100644 --- a/packages/html/src/events/composition.rs +++ b/packages/html/src/events/composition.rs @@ -2,7 +2,7 @@ use dioxus_core::Event; pub type CompositionEvent = Event; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct CompositionData { pub data: String, } diff --git a/packages/html/src/events/drag.rs b/packages/html/src/events/drag.rs index 5dd7f5c71..f8ee6ff92 100644 --- a/packages/html/src/events/drag.rs +++ b/packages/html/src/events/drag.rs @@ -1,5 +1,3 @@ -use std::any::Any; - use dioxus_core::Event; use crate::MouseData; @@ -10,12 +8,11 @@ pub type DragEvent = Event; /// 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)] pub struct DragData { /// Inherit mouse data pub mouse: MouseData, - - /// And then add the rest of the drag data - pub data: Box, } impl_event! { diff --git a/packages/html/src/events/focus.rs b/packages/html/src/events/focus.rs index b2a4f5205..2092c972e 100644 --- a/packages/html/src/events/focus.rs +++ b/packages/html/src/events/focus.rs @@ -3,7 +3,7 @@ use dioxus_core::Event; pub type FocusEvent = Event; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct FocusData {/* DOMEventInner: Send + SyncTarget relatedTarget */} impl_event! [ diff --git a/packages/html/src/events/form.rs b/packages/html/src/events/form.rs index 5e0b6a17e..9f2740874 100644 --- a/packages/html/src/events/form.rs +++ b/packages/html/src/events/form.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, fmt::Debug, sync::Arc}; +use std::{collections::HashMap, fmt::Debug}; use dioxus_core::Event; @@ -6,14 +6,13 @@ pub type FormEvent = Event; /* DOMEvent: Send + SyncTarget relatedTarget */ #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone)] +#[derive(Clone, PartialEq, Default)] pub struct FormData { pub value: String, pub values: HashMap, - - #[cfg_attr(feature = "serialize", serde(skip))] - pub files: Option>, + // #[cfg_attr(feature = "serialize", serde(skip))] + // pub files: Option>, } impl Debug for FormData { @@ -25,17 +24,17 @@ impl Debug for FormData { } } -#[async_trait::async_trait(?Send)] -pub trait FileEngine { - // get a list of file names - fn files(&self) -> Vec; +// #[async_trait::async_trait(?Send)] +// pub trait FileEngine { +// // get a list of file names +// fn files(&self) -> Vec; - // read a file to bytes - async fn read_file(&self, file: &str) -> Option>; +// // read a file to bytes +// async fn read_file(&self, file: &str) -> Option>; - // read a file to string - async fn read_file_to_string(&self, file: &str) -> Option; -} +// // read a file to string +// async fn read_file_to_string(&self, file: &str) -> Option; +// } impl_event! { FormData; diff --git a/packages/html/src/events/image.rs b/packages/html/src/events/image.rs index 06430c4c2..bf5228409 100644 --- a/packages/html/src/events/image.rs +++ b/packages/html/src/events/image.rs @@ -2,7 +2,7 @@ use dioxus_core::Event; pub type ImageEvent = Event; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct ImageData { pub load_error: bool, } diff --git a/packages/html/src/events/keyboard.rs b/packages/html/src/events/keyboard.rs index ddf720770..ee803d948 100644 --- a/packages/html/src/events/keyboard.rs +++ b/packages/html/src/events/keyboard.rs @@ -7,7 +7,7 @@ use std::str::FromStr; pub type KeyboardEvent = Event; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] pub struct KeyboardData { #[deprecated( since = "0.3.0", diff --git a/packages/html/src/events/media.rs b/packages/html/src/events/media.rs index 0b2fe70e0..d2ba71c08 100644 --- a/packages/html/src/events/media.rs +++ b/packages/html/src/events/media.rs @@ -2,7 +2,7 @@ use dioxus_core::Event; pub type MediaEvent = Event; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct MediaData {} impl_event! [ diff --git a/packages/html/src/events/mouse.rs b/packages/html/src/events/mouse.rs index a0fda7be6..f8446b7d1 100644 --- a/packages/html/src/events/mouse.rs +++ b/packages/html/src/events/mouse.rs @@ -10,7 +10,7 @@ pub type MouseEvent = Event; /// 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(Clone, Default)] +#[derive(Clone, Default, PartialEq)] /// Data associated with a mouse event /// /// Do not use the deprecated fields; they may change or become private in the future. diff --git a/packages/html/src/events/pointer.rs b/packages/html/src/events/pointer.rs index 23b0aba71..4134ac852 100644 --- a/packages/html/src/events/pointer.rs +++ b/packages/html/src/events/pointer.rs @@ -2,7 +2,7 @@ use dioxus_core::Event; pub type PointerEvent = Event; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct PointerData { // Mouse only pub alt_key: bool, diff --git a/packages/html/src/events/scroll.rs b/packages/html/src/events/scroll.rs index 0c2dd3143..798218b02 100644 --- a/packages/html/src/events/scroll.rs +++ b/packages/html/src/events/scroll.rs @@ -2,7 +2,7 @@ use dioxus_core::Event; pub type ScrollEvent = Event; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct ScrollData {} impl_event! { diff --git a/packages/html/src/events/selection.rs b/packages/html/src/events/selection.rs index ff1496c96..249353e63 100644 --- a/packages/html/src/events/selection.rs +++ b/packages/html/src/events/selection.rs @@ -2,7 +2,7 @@ use dioxus_core::Event; pub type SelectionEvent = Event; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct SelectionData {} impl_event! [ diff --git a/packages/html/src/events/toggle.rs b/packages/html/src/events/toggle.rs index 1f0d3f6e7..4f225a8e6 100644 --- a/packages/html/src/events/toggle.rs +++ b/packages/html/src/events/toggle.rs @@ -2,7 +2,7 @@ use dioxus_core::Event; pub type ToggleEvent = Event; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct ToggleData {} impl_event! { diff --git a/packages/html/src/events/touch.rs b/packages/html/src/events/touch.rs index 1f219b22f..1e795b6da 100644 --- a/packages/html/src/events/touch.rs +++ b/packages/html/src/events/touch.rs @@ -2,7 +2,7 @@ use dioxus_core::Event; pub type TouchEvent = Event; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct TouchData { pub alt_key: bool, pub ctrl_key: bool, diff --git a/packages/html/src/events/transition.rs b/packages/html/src/events/transition.rs index 095496664..ecc3e1636 100644 --- a/packages/html/src/events/transition.rs +++ b/packages/html/src/events/transition.rs @@ -2,7 +2,7 @@ use dioxus_core::Event; pub type TransitionEvent = Event; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct TransitionData { pub property_name: String, pub pseudo_element: String, diff --git a/packages/html/src/events/wheel.rs b/packages/html/src/events/wheel.rs index e6e06501b..758fa7936 100644 --- a/packages/html/src/events/wheel.rs +++ b/packages/html/src/events/wheel.rs @@ -6,7 +6,7 @@ use crate::geometry::{LinesVector, PagesVector, PixelsVector, WheelDelta}; pub type WheelEvent = Event; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone)] +#[derive(Clone, PartialEq, Default)] pub struct WheelData { #[deprecated(since = "0.3.0", note = "use delta() instead")] pub delta_mode: u32, diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index ab2e49f1e..577899fe1 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -22,6 +22,12 @@ mod render_template; #[cfg(feature = "wasm-bind")] mod web_sys_bind; +#[cfg(feature = "serialize")] +mod transit; + +#[cfg(feature = "serialize")] +pub use transit::*; + pub use elements::*; pub use events::*; pub use global_attributes::*; diff --git a/packages/html/src/transit.rs b/packages/html/src/transit.rs new file mode 100644 index 000000000..651b7e4f6 --- /dev/null +++ b/packages/html/src/transit.rs @@ -0,0 +1,217 @@ +use std::{any::Any, rc::Rc}; + +use crate::events::*; +use dioxus_core::ElementId; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Debug, Clone, PartialEq)] +pub struct HtmlEvent { + pub element: ElementId, + pub name: String, + pub bubbles: bool, + pub data: EventData, +} + +impl<'de> Deserialize<'de> for HtmlEvent { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize, Debug, Clone)] + struct Inner { + element: ElementId, + name: String, + bubbles: bool, + data: serde_value::Value, + } + + let Inner { + element, + name, + bubbles, + data, + } = Inner::deserialize(deserializer)?; + + Ok(HtmlEvent { + data: fun_name(&name, data).unwrap(), + element, + bubbles, + name, + }) + } +} + +fn fun_name( + name: &str, + data: serde_value::Value, +) -> Result { + use EventData::*; + + // a little macro-esque thing to make the code below more readable + #[inline] + fn de<'de, F>(f: serde_value::Value) -> Result + where + F: Deserialize<'de>, + { + F::deserialize(f) + } + + let data = match name { + // Mouse + "click" | "contextmenu" | "dblclick" | "doubleclick" | "mousedown" | "mouseenter" + | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => Mouse(de(data)?), + + // Clipboard + "copy" | "cut" | "paste" => Clipboard(de(data)?), + + // Composition + "compositionend" | "compositionstart" | "compositionupdate" => Composition(de(data)?), + + // Keyboard + "keydown" | "keypress" | "keyup" => Keyboard(de(data)?), + + // Focus + "blur" | "focus" | "focusin" | "focusout" => Focus(de(data)?), + + // Form + "change" | "input" | "invalid" | "reset" | "submit" => Form(de(data)?), + + // Drag + "drag" | "dragend" | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart" + | "drop" => Drag(de(data)?), + + // Pointer + "pointerlockchange" | "pointerlockerror" | "pointerdown" | "pointermove" | "pointerup" + | "pointerover" | "pointerout" | "pointerenter" | "pointerleave" | "gotpointercapture" + | "lostpointercapture" => Pointer(de(data)?), + + // Selection + "selectstart" | "selectionchange" | "select" => Selection(de(data)?), + + // Touch + "touchcancel" | "touchend" | "touchmove" | "touchstart" => Touch(de(data)?), + + // Srcoll + "scroll" => Scroll(de(data)?), + + // Wheel + "wheel" => Wheel(de(data)?), + + // Media + "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted" + | "ended" | "interruptbegin" | "interruptend" | "loadeddata" | "loadedmetadata" + | "loadstart" | "pause" | "play" | "playing" | "progress" | "ratechange" | "seeked" + | "seeking" | "stalled" | "suspend" | "timeupdate" | "volumechange" | "waiting" + | "error" | "load" | "loadend" | "timeout" => Media(de(data)?), + + // Animation + "animationstart" | "animationend" | "animationiteration" => Animation(de(data)?), + + // Transition + "transitionend" => Transition(de(data)?), + + // Toggle + "toggle" => Toggle(de(data)?), + + // ImageData => "load" | "error"; + // OtherData => "abort" | "afterprint" | "beforeprint" | "beforeunload" | "hashchange" | "languagechange" | "message" | "offline" | "online" | "pagehide" | "pageshow" | "popstate" | "rejectionhandled" | "storage" | "unhandledrejection" | "unload" | "userproximity" | "vrdisplayactivate" | "vrdisplayblur" | "vrdisplayconnect" | "vrdisplaydeactivate" | "vrdisplaydisconnect" | "vrdisplayfocus" | "vrdisplaypointerrestricted" | "vrdisplaypointerunrestricted" | "vrdisplaypresentchange"; + other => { + return Err(serde_value::DeserializerError::UnknownVariant( + other.to_string(), + &[], + )) + } + }; + + Ok(data) +} + +impl HtmlEvent { + pub fn bubbles(&self) -> bool { + event_bubbles(&self.name) + } +} + +#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)] +#[serde(untagged)] +pub enum EventData { + Mouse(MouseData), + Clipboard(ClipboardData), + Composition(CompositionData), + Keyboard(KeyboardData), + Focus(FocusData), + Form(FormData), + Drag(DragData), + Pointer(PointerData), + Selection(SelectionData), + Touch(TouchData), + Scroll(ScrollData), + Wheel(WheelData), + Media(MediaData), + Animation(AnimationData), + Transition(TransitionData), + Toggle(ToggleData), +} + +impl EventData { + pub fn into_any(self) -> Rc { + match self { + EventData::Mouse(data) => Rc::new(data) as Rc, + EventData::Clipboard(data) => Rc::new(data) as Rc, + EventData::Composition(data) => Rc::new(data) as Rc, + EventData::Keyboard(data) => Rc::new(data) as Rc, + EventData::Focus(data) => Rc::new(data) as Rc, + EventData::Form(data) => Rc::new(data) as Rc, + EventData::Drag(data) => Rc::new(data) as Rc, + EventData::Pointer(data) => Rc::new(data) as Rc, + EventData::Selection(data) => Rc::new(data) as Rc, + EventData::Touch(data) => Rc::new(data) as Rc, + EventData::Scroll(data) => Rc::new(data) as Rc, + EventData::Wheel(data) => Rc::new(data) as Rc, + EventData::Media(data) => Rc::new(data) as Rc, + EventData::Animation(data) => Rc::new(data) as Rc, + EventData::Transition(data) => Rc::new(data) as Rc, + EventData::Toggle(data) => Rc::new(data) as Rc, + } + } +} + +#[test] +fn test_back_and_forth() { + let data = HtmlEvent { + element: ElementId(0), + data: EventData::Mouse(MouseData::default()), + name: "click".to_string(), + bubbles: true, + }; + + println!("{}", serde_json::to_string_pretty(&data).unwrap()); + + let o = r#" +{ + "element": 0, + "name": "click", + "bubbles": true, + "data": { + "alt_key": false, + "button": 0, + "buttons": 0, + "client_x": 0, + "client_y": 0, + "ctrl_key": false, + "meta_key": false, + "offset_x": 0, + "offset_y": 0, + "page_x": 0, + "page_y": 0, + "screen_x": 0, + "screen_y": 0, + "shift_key": false + } +} + "#; + + let p: HtmlEvent = serde_json::from_str(o).unwrap(); + + assert_eq!(data, p); +} diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js index 6367af68d..c38734cfc 100644 --- a/packages/interpreter/src/interpreter.js +++ b/packages/interpreter/src/interpreter.js @@ -1,11 +1,3 @@ -export function main(rootname = "main") { - let root = window.document.getElementById(rootname); - if (root != null) { - window.interpreter = new Interpreter(root); - window.ipc.postMessage(serializeIpcMessage("initialize")); - } -} - class ListenerMap { constructor(root) { // bubbling events can listen at the root element @@ -60,7 +52,7 @@ class ListenerMap { } } -export class Interpreter { +class Interpreter { constructor(root) { this.root = root; this.listeners = new ListenerMap(root); @@ -352,6 +344,9 @@ export class Interpreter { this.RemoveEventListener(edit.id, edit.name); break; case "NewEventListener": + + let bubbles = event_bubbles(edit.name); + // this handler is only provided on desktop implementations since this // method is not used by the web implementation let handler = (event) => { @@ -438,17 +433,18 @@ export class Interpreter { event: edit.name, mounted_dom_id: parseInt(realId), contents: contents, + bubbles, }) ); } }; - this.NewEventListener(edit.name, edit.id, event_bubbles(edit.name), handler); + this.NewEventListener(edit.name, edit.id, bubbles, handler); break; } } } -export function serialize_event(event) { +function serialize_event(event) { switch (event.type) { case "copy": case "cut":