Przeglądaj źródła

wip: overhaul event system

Jonathan Kelley 3 lat temu
rodzic
commit
7a03c1d

+ 1 - 0
Cargo.toml

@@ -85,6 +85,7 @@ members = [
     "packages/ssr",
     "packages/desktop",
     "packages/mobile",
+    "packages/webview-client"
 ]
 
 

+ 6 - 1
packages/core/examples/jsframework.rs

@@ -1,3 +1,5 @@
+use dioxus::events::on::MouseEvent;
+use dioxus::events::DioxusEvent;
 use dioxus_core as dioxus;
 use dioxus_core::prelude::*;
 use dioxus_core_macro::*;
@@ -37,6 +39,9 @@ struct RowProps {
     label: Label,
 }
 fn Row<'a>(cx: Context<'a>, props: &'a RowProps) -> DomTree<'a> {
+    let handler = move |evt: MouseEvent| {
+        let g = evt.button;
+    };
     cx.render(rsx! {
         tr {
             td { class:"col-md-1", "{props.row_id}" }
@@ -44,7 +49,7 @@ fn Row<'a>(cx: Context<'a>, props: &'a RowProps) -> DomTree<'a> {
                 a { class: "lbl", "{props.label}" }
             }
             td { class: "col-md-1"
-                a { class: "remove", onclick: move |_| {/* remove */}
+                a { class: "remove", onclick: {handler}
                     span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" }
                 }
             }

+ 1023 - 0
packages/core/src/events.old.rs

@@ -0,0 +1,1023 @@
+//! This module provides a set of common events for all Dioxus apps to target, regardless of host platform.
+//! -------------------------------------------------------------------------------------------------------
+//!
+//! 3rd party renderers are responsible for converting their native events into these virtual event types. Events might
+//! be heavy or need to interact through FFI, so the events themselves are designed to be lazy.
+use crate::{
+    innerlude::Listener,
+    innerlude::{ElementId, NodeFactory, ScopeId},
+};
+use bumpalo::boxed::Box as BumpBox;
+use std::{
+    any::Any,
+    cell::{Cell, RefCell},
+    fmt::Debug,
+    ops::Deref,
+    rc::Rc,
+    sync::Arc,
+};
+
+#[derive(Debug)]
+pub struct UserEvent {
+    /// The originator of the event trigger
+    pub scope: ScopeId,
+
+    /// The optional real node associated with the trigger
+    pub mounted_dom_id: Option<ElementId>,
+
+    /// The event type IE "onclick" or "onmouseover"
+    ///
+    /// The name that the renderer will use to mount the listener.
+    pub name: &'static str,
+
+    /// The type of event
+    pub event: SyntheticEvent,
+}
+
+pub enum SyntheticEvent {
+    AnimationEvent(on::AnimationEvent),
+    ClipboardEvent(on::ClipboardEvent),
+    CompositionEvent(on::CompositionEvent),
+    FocusEvent(on::FocusEvent),
+    FormEvent(on::FormEvent),
+    KeyboardEvent(on::KeyboardEvent),
+    GenericEvent(on::GenericEvent),
+    TouchEvent(on::TouchEvent),
+    ToggleEvent(on::ToggleEvent),
+    MediaEvent(on::MediaEvent),
+    MouseEvent(on::MouseEvent),
+    WheelEvent(on::WheelEvent),
+    SelectionEvent(on::SelectionEvent),
+    TransitionEvent(on::TransitionEvent),
+    PointerEvent(on::PointerEvent),
+}
+// ImageEvent(event_data::ImageEvent),
+
+impl std::fmt::Debug for SyntheticEvent {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        let name = match self {
+            SyntheticEvent::ClipboardEvent(_) => "ClipboardEvent",
+            SyntheticEvent::CompositionEvent(_) => "CompositionEvent",
+            SyntheticEvent::KeyboardEvent(_) => "KeyboardEvent",
+            SyntheticEvent::FocusEvent(_) => "FocusEvent",
+            SyntheticEvent::FormEvent(_) => "FormEvent",
+            SyntheticEvent::SelectionEvent(_) => "SelectionEvent",
+            SyntheticEvent::TouchEvent(_) => "TouchEvent",
+            SyntheticEvent::WheelEvent(_) => "WheelEvent",
+            SyntheticEvent::MediaEvent(_) => "MediaEvent",
+            SyntheticEvent::AnimationEvent(_) => "AnimationEvent",
+            SyntheticEvent::TransitionEvent(_) => "TransitionEvent",
+            SyntheticEvent::ToggleEvent(_) => "ToggleEvent",
+            SyntheticEvent::MouseEvent(_) => "MouseEvent",
+            SyntheticEvent::PointerEvent(_) => "PointerEvent",
+            SyntheticEvent::GenericEvent(_) => "GenericEvent",
+        };
+
+        f.debug_struct("VirtualEvent").field("type", &name).finish()
+    }
+}
+
+/// Priority of Event Triggers.
+///
+/// Internally, Dioxus will abort work that's taking too long if new, more important, work arrives. Unlike React, Dioxus
+/// won't be afraid to pause work or flush changes to the RealDOM. This is called "cooperative scheduling". Some Renderers
+/// implement this form of scheduling internally, however Dioxus will perform its own scheduling as well.
+///
+/// The ultimate goal of the scheduler is to manage latency of changes, prioritizing "flashier" changes over "subtler" changes.
+///
+/// React has a 5-tier priority system. However, they break things into "Continuous" and "Discrete" priority. For now,
+/// we keep it simple, and just use a 3-tier priority system.
+///
+/// - NoPriority = 0
+/// - LowPriority = 1
+/// - NormalPriority = 2
+/// - UserBlocking = 3
+/// - HighPriority = 4
+/// - ImmediatePriority = 5
+///
+/// We still have a concept of discrete vs continuous though - discrete events won't be batched, but continuous events will.
+/// This means that multiple "scroll" events will be processed in a single frame, but multiple "click" events will be
+/// flushed before proceeding. Multiple discrete events is highly unlikely, though.
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
+pub enum EventPriority {
+    /// Work that must be completed during the EventHandler phase.
+    ///
+    /// Currently this is reserved for controlled inputs.
+    Immediate = 3,
+
+    /// "High Priority" work will not interrupt other high priority work, but will interrupt medium and low priority work.
+    ///
+    /// This is typically reserved for things like user interaction.
+    ///
+    /// React calls these "discrete" events, but with an extra category of "user-blocking" (Immediate).
+    High = 2,
+
+    /// "Medium priority" work is generated by page events not triggered by the user. These types of events are less important
+    /// than "High Priority" events and will take presedence over low priority events.
+    ///
+    /// This is typically reserved for VirtualEvents that are not related to keyboard or mouse input.
+    ///
+    /// React calls these "continuous" events (e.g. mouse move, mouse wheel, touch move, etc).
+    Medium = 1,
+
+    /// "Low Priority" work will always be pre-empted unless the work is significantly delayed, in which case it will be
+    /// advanced to the front of the work queue until completed.
+    ///
+    /// The primary user of Low Priority work is the asynchronous work system (suspense).
+    ///
+    /// This is considered "idle" work or "background" work.
+    Low = 0,
+}
+
+pub(crate) fn event_meta(event: &UserEvent) -> (bool, EventPriority) {
+    use EventPriority::*;
+
+    match event.name {
+        // clipboard
+        "copy" | "cut" | "paste" => (true, Medium),
+
+        // Composition
+        "compositionend" | "compositionstart" | "compositionupdate" => (true, Low),
+
+        // Keyboard
+        "keydown" | "keypress" | "keyup" => (true, High),
+
+        // Focus
+        "focus" | "blur" => (true, Low),
+
+        // Form
+        "change" | "input" | "invalid" | "reset" | "submit" => (true, Medium),
+
+        // Mouse
+        "click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
+        | "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
+        | "mouseleave" | "mouseout" | "mouseover" | "mouseup" => (true, High),
+
+        "mousemove" => (false, Medium),
+
+        // Pointer
+        "pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
+        | "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
+            (true, Medium)
+        }
+
+        // Selection
+        "select" | "touchcancel" | "touchend" => (true, Medium),
+
+        // Touch
+        "touchmove" | "touchstart" => (true, Medium),
+
+        // Wheel
+        "scroll" | "wheel" => (false, Medium),
+
+        // Media
+        "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
+        | "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
+        | "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
+        | "timeupdate" | "volumechange" | "waiting" => (true, Medium),
+
+        // Animation
+        "animationstart" | "animationend" | "animationiteration" => (true, Medium),
+
+        // Transition
+        "transitionend" => (true, Medium),
+
+        // Toggle
+        "toggle" => (true, Medium),
+
+        _ => (true, Low),
+    }
+}
+
+pub use on::{
+    AnimationEvent, ClipboardEvent, CompositionEvent, FocusEvent, FormEvent, GenericEvent, KeyCode,
+    KeyboardEvent, MediaEvent, MouseEvent, PointerEvent, SelectionEvent, ToggleEvent, TouchEvent,
+    TransitionEvent, WheelEvent,
+};
+
+pub mod on {
+    //! This module defines the synthetic events that all Dioxus apps enable. No matter the platform, every dioxus renderer
+    //! will implement the same events and same behavior (bubbling, cancelation, etc).
+    //!
+    //! Synthetic events are immutable and wrapped in Arc. It is the intention for Dioxus renderers to re-use the underyling
+    //! Arc allocation through "get_mut"
+    //!
+    //! React recently dropped support for re-using event allocation and just passes the real event along.
+    use super::*;
+
+    macro_rules! event_directory {
+        ( $(
+            $( #[$attr:meta] )*
+            $eventdata:ident($wrapper:ident): [
+                $(
+                    $( #[$method_attr:meta] )*
+                    $name:ident
+                )*
+            ];
+        )* ) => {
+            $(
+                $(#[$attr])*
+                pub struct $wrapper(pub Arc<dyn $eventdata>);
+
+                // todo: derefing to the event is fine (and easy) but breaks some IDE stuff like (go to source)
+                // going to source in fact takes you to the source of Rc which is... less than useful
+                // Either we ask for this to be changed in Rust-analyzer or manually impkement the trait
+                impl Deref for $wrapper {
+                    type Target = Arc<dyn $eventdata>;
+                    fn deref(&self) -> &Self::Target {
+                        &self.0
+                    }
+                }
+
+                $(
+                    $(#[$method_attr])*
+                    pub fn $name<'a, F>(
+                        c: NodeFactory<'a>,
+                        mut callback: F,
+                    ) -> Listener<'a>
+                        where F: FnMut($wrapper) + 'a
+                    {
+                        let bump = &c.bump();
+
+                        let cb: &mut dyn FnMut(SyntheticEvent) = bump.alloc(move |evt: SyntheticEvent| match evt {
+                            SyntheticEvent::$wrapper(event) => callback(event),
+                            _ => unreachable!("Downcasted SyntheticEvent to wrong event type - this is an internal bug!")
+                        });
+
+                        let callback: BumpBox<dyn FnMut(SyntheticEvent) + 'a> = unsafe { BumpBox::from_raw(cb) };
+
+
+                        // ie oncopy
+                        let event_name = stringify!($name);
+
+                        // ie copy
+                        let shortname: &'static str = &event_name[2..];
+
+                        Listener {
+                            event: shortname,
+                            mounted_node: Cell::new(None),
+                            callback: RefCell::new(Some(callback)),
+                        }
+                    }
+                )*
+            )*
+        };
+    }
+
+    // The Dioxus Synthetic event system
+    event_directory! {
+        ClipboardEventInner(ClipboardEvent): [
+            /// Called when "copy"
+            oncopy
+
+            /// oncut
+            oncut
+
+            /// onpaste
+            onpaste
+        ];
+
+        CompositionEventInner(CompositionEvent): [
+            /// oncompositionend
+            oncompositionend
+
+            /// oncompositionstart
+            oncompositionstart
+
+            /// oncompositionupdate
+            oncompositionupdate
+        ];
+
+        KeyboardEventInner(KeyboardEvent): [
+            /// onkeydown
+            onkeydown
+
+            /// onkeypress
+            onkeypress
+
+            /// onkeyup
+            onkeyup
+        ];
+
+        FocusEventInner(FocusEvent): [
+            /// onfocus
+            onfocus
+
+            /// onblur
+            onblur
+        ];
+
+
+        FormEventInner(FormEvent): [
+            /// onchange
+            onchange
+
+            /// oninput handler
+            oninput
+
+            /// oninvalid
+            oninvalid
+
+            /// onreset
+            onreset
+
+            /// onsubmit
+            onsubmit
+        ];
+
+
+        /// A synthetic event that wraps a web-style [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent)
+        ///
+        ///
+        /// The MouseEvent interface represents events that occur due to the user interacting with a pointing device (such as a mouse).
+        ///
+        /// ## Trait implementation:
+        /// ```rust
+        ///     fn alt_key(&self) -> bool;
+        ///     fn button(&self) -> i16;
+        ///     fn buttons(&self) -> u16;
+        ///     fn client_x(&self) -> i32;
+        ///     fn client_y(&self) -> i32;
+        ///     fn ctrl_key(&self) -> bool;
+        ///     fn meta_key(&self) -> bool;
+        ///     fn page_x(&self) -> i32;
+        ///     fn page_y(&self) -> i32;
+        ///     fn screen_x(&self) -> i32;
+        ///     fn screen_y(&self) -> i32;
+        ///     fn shift_key(&self) -> bool;
+        ///     fn get_modifier_state(&self, key_code: &str) -> bool;
+        /// ```
+        ///
+        /// ## Event Handlers
+        /// - [`onclick`]
+        /// - [`oncontextmenu`]
+        /// - [`ondoubleclick`]
+        /// - [`ondrag`]
+        /// - [`ondragend`]
+        /// - [`ondragenter`]
+        /// - [`ondragexit`]
+        /// - [`ondragleave`]
+        /// - [`ondragover`]
+        /// - [`ondragstart`]
+        /// - [`ondrop`]
+        /// - [`onmousedown`]
+        /// - [`onmouseenter`]
+        /// - [`onmouseleave`]
+        /// - [`onmousemove`]
+        /// - [`onmouseout`]
+        /// - [`onmouseover`]
+        /// - [`onmouseup`]
+        MouseEventInner(MouseEvent): [
+            /// Execute a callback when a button is clicked.
+            ///
+            /// ## Description
+            ///
+            /// An element receives a click event when a pointing device button (such as a mouse's primary mouse button)
+            /// is both pressed and released while the pointer is located inside the element.
+            ///
+            /// - Bubbles: Yes
+            /// - Cancelable: Yes
+            /// - Interface: [`MouseEvent`]
+            ///
+            /// If the button is pressed on one element and the pointer is moved outside the element before the button
+            /// is released, the event is fired on the most specific ancestor element that contained both elements.
+            /// `click` fires after both the `mousedown` and `mouseup` events have fired, in that order.
+            ///
+            /// ## Example
+            /// ```
+            /// rsx!( button { "click me", onclick: move |_| log::info!("Clicked!`") } )
+            /// ```
+            ///
+            /// ## Reference
+            /// - https://www.w3schools.com/tags/ev_onclick.asp
+            /// - https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event
+            onclick
+
+            /// oncontextmenu
+            oncontextmenu
+
+            /// ondoubleclick
+            ondoubleclick
+
+            /// ondrag
+            ondrag
+
+            /// ondragend
+            ondragend
+
+            /// ondragenter
+            ondragenter
+
+            /// ondragexit
+            ondragexit
+
+            /// ondragleave
+            ondragleave
+
+            /// ondragover
+            ondragover
+
+            /// ondragstart
+            ondragstart
+
+            /// ondrop
+            ondrop
+
+            /// onmousedown
+            onmousedown
+
+            /// onmouseenter
+            onmouseenter
+
+            /// onmouseleave
+            onmouseleave
+
+            /// onmousemove
+            onmousemove
+
+            /// onmouseout
+            onmouseout
+
+            ///
+            onscroll
+
+            /// onmouseover
+            ///
+            /// Triggered when the users's mouse hovers over an element.
+            onmouseover
+
+            /// onmouseup
+            onmouseup
+        ];
+
+        PointerEventInner(PointerEvent): [
+            /// pointerdown
+            onpointerdown
+
+            /// pointermove
+            onpointermove
+
+            /// pointerup
+            onpointerup
+
+            /// pointercancel
+            onpointercancel
+
+            /// gotpointercapture
+            ongotpointercapture
+
+            /// lostpointercapture
+            onlostpointercapture
+
+            /// pointerenter
+            onpointerenter
+
+            /// pointerleave
+            onpointerleave
+
+            /// pointerover
+            onpointerover
+
+            /// pointerout
+            onpointerout
+        ];
+
+        SelectionEventInner(SelectionEvent): [
+            /// onselect
+            onselect
+        ];
+
+        TouchEventInner(TouchEvent): [
+            /// ontouchcancel
+            ontouchcancel
+
+            /// ontouchend
+            ontouchend
+
+            /// ontouchmove
+            ontouchmove
+
+            /// ontouchstart
+            ontouchstart
+        ];
+
+        WheelEventInner(WheelEvent): [
+            ///
+            onwheel
+        ];
+
+        MediaEventInner(MediaEvent): [
+            ///abort
+            onabort
+
+            ///canplay
+            oncanplay
+
+            ///canplaythrough
+            oncanplaythrough
+
+            ///durationchange
+            ondurationchange
+
+            ///emptied
+            onemptied
+
+            ///encrypted
+            onencrypted
+
+            ///ended
+            onended
+
+            ///error
+            onerror
+
+            ///loadeddata
+            onloadeddata
+
+            ///loadedmetadata
+            onloadedmetadata
+
+            ///loadstart
+            onloadstart
+
+            ///pause
+            onpause
+
+            ///play
+            onplay
+
+            ///playing
+            onplaying
+
+            ///progress
+            onprogress
+
+            ///ratechange
+            onratechange
+
+            ///seeked
+            onseeked
+
+            ///seeking
+            onseeking
+
+            ///stalled
+            onstalled
+
+            ///suspend
+            onsuspend
+
+            ///timeupdate
+            ontimeupdate
+
+            ///volumechange
+            onvolumechange
+
+            ///waiting
+            onwaiting
+        ];
+
+        AnimationEventInner(AnimationEvent): [
+            /// onanimationstart
+            onanimationstart
+
+            /// onanimationend
+            onanimationend
+
+            /// onanimationiteration
+            onanimationiteration
+        ];
+
+        TransitionEventInner(TransitionEvent): [
+            ///
+            ontransitionend
+        ];
+
+        ToggleEventInner(ToggleEvent): [
+            ///
+            ontoggle
+        ];
+    }
+
+    pub struct GenericEvent(pub Arc<dyn GenericEventInner>);
+
+    pub trait GenericEventInner: Send + Sync {
+        /// Return a reference to the raw event. User will need to downcast the event to the right platform-specific type.
+        fn raw_event(&self) -> &dyn Any;
+        /// Returns whether or not a specific event is a bubbling event
+        fn bubbles(&self) -> bool;
+        /// Sets or returns whether the event should propagate up the hierarchy or not
+        fn cancel_bubble(&self);
+        /// Returns whether or not an event can have its default action prevented
+        fn cancelable(&self) -> bool;
+        /// Returns whether the event is composed or not
+        fn composed(&self) -> bool;
+
+        // Currently not supported because those no way we could possibly support it
+        // just cast the event to the right platform-specific type and return it
+        // /// Returns the event's path
+        // fn composed_path(&self) -> String;
+
+        /// Returns the element whose event listeners triggered the event
+        fn current_target(&self);
+        /// Returns whether or not the preventDefault method was called for the event
+        fn default_prevented(&self) -> bool;
+        /// Returns which phase of the event flow is currently being evaluated
+        fn event_phase(&self) -> u16;
+        /// Returns whether or not an event is trusted
+        fn is_trusted(&self) -> bool;
+        /// Cancels the event if it is cancelable, meaning that the default action that belongs to the event will
+        fn prevent_default(&self);
+        /// Prevents other listeners of the same event from being called
+        fn stop_immediate_propagation(&self);
+        /// Prevents further propagation of an event during event flow
+        fn stop_propagation(&self);
+        /// Returns the element that triggered the event
+        fn target(&self);
+        /// Returns the time (in milliseconds relative to the epoch) at which the event was created
+        fn time_stamp(&self) -> f64;
+    }
+
+    pub trait ClipboardEventInner: Send + Sync {
+        // DOMDataTransfer clipboardData
+    }
+
+    pub trait CompositionEventInner: Send + Sync {
+        fn data(&self) -> String;
+    }
+
+    pub trait KeyboardEventInner: Send + Sync {
+        fn alt_key(&self) -> bool;
+
+        fn char_code(&self) -> u32;
+
+        /// Identify which "key" was entered.
+        ///
+        /// This is the best method to use for all languages. They key gets mapped to a String sequence which you can match on.
+        /// The key isn't an enum because there are just so many context-dependent keys.
+        ///
+        /// A full list on which keys to use is available at:
+        /// <https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values>
+        ///
+        /// # Example
+        ///
+        /// ```rust
+        /// match event.key().as_str() {
+        ///     "Esc" | "Escape" => {}
+        ///     "ArrowDown" => {}
+        ///     "ArrowLeft" => {}
+        ///      _ => {}
+        /// }
+        /// ```
+        ///
+        fn key(&self) -> String;
+
+        /// Get the key code as an enum Variant.
+        ///
+        /// This is intended for things like arrow keys, escape keys, function keys, and other non-international keys.
+        /// To match on unicode sequences, use the [`key`] method - this will return a string identifier instead of a limited enum.
+        ///
+        ///
+        /// ## Example
+        ///
+        /// ```rust
+        /// use dioxus::KeyCode;
+        /// match event.key_code() {
+        ///     KeyCode::Escape => {}
+        ///     KeyCode::LeftArrow => {}
+        ///     KeyCode::RightArrow => {}
+        ///     _ => {}
+        /// }
+        /// ```
+        ///
+        fn key_code(&self) -> KeyCode;
+
+        /// Check if the ctrl key was pressed down
+        fn ctrl_key(&self) -> bool;
+
+        fn get_modifier_state(&self, key_code: &str) -> bool;
+
+        fn locale(&self) -> String;
+        fn location(&self) -> usize;
+        fn meta_key(&self) -> bool;
+        fn repeat(&self) -> bool;
+        fn shift_key(&self) -> bool;
+        fn which(&self) -> usize;
+    }
+
+    pub trait FocusEventInner: Send + Sync {
+        /* DOMEventInner:  Send + SyncTarget relatedTarget */
+    }
+
+    pub trait FormEventInner: Send + Sync {
+        fn value(&self) -> String;
+    }
+
+    pub trait MouseEventInner: Send + Sync {
+        fn alt_key(&self) -> bool;
+        fn button(&self) -> i16;
+        fn buttons(&self) -> u16;
+        /// Get the X coordinate of the mouse relative to the window
+        fn client_x(&self) -> i32;
+        fn client_y(&self) -> i32;
+        fn ctrl_key(&self) -> bool;
+        fn meta_key(&self) -> bool;
+        fn page_x(&self) -> i32;
+        fn page_y(&self) -> i32;
+        fn screen_x(&self) -> i32;
+        fn screen_y(&self) -> i32;
+        fn shift_key(&self) -> bool;
+        fn get_modifier_state(&self, key_code: &str) -> bool;
+    }
+
+    pub trait PointerEventInner: Send + Sync {
+        // Mouse only
+        fn alt_key(&self) -> bool;
+        fn button(&self) -> i16;
+        fn buttons(&self) -> u16;
+        fn client_x(&self) -> i32;
+        fn client_y(&self) -> i32;
+        fn ctrl_key(&self) -> bool;
+        fn meta_key(&self) -> bool;
+        fn page_x(&self) -> i32;
+        fn page_y(&self) -> i32;
+        fn screen_x(&self) -> i32;
+        fn screen_y(&self) -> i32;
+        fn shift_key(&self) -> bool;
+        fn get_modifier_state(&self, key_code: &str) -> bool;
+        fn pointer_id(&self) -> i32;
+        fn width(&self) -> i32;
+        fn height(&self) -> i32;
+        fn pressure(&self) -> f32;
+        fn tangential_pressure(&self) -> f32;
+        fn tilt_x(&self) -> i32;
+        fn tilt_y(&self) -> i32;
+        fn twist(&self) -> i32;
+        fn pointer_type(&self) -> String;
+        fn is_primary(&self) -> bool;
+    }
+
+    pub trait SelectionEventInner: Send + Sync {}
+
+    pub trait TouchEventInner: Send + Sync {
+        fn alt_key(&self) -> bool;
+        fn ctrl_key(&self) -> bool;
+        fn meta_key(&self) -> bool;
+        fn shift_key(&self) -> bool;
+        fn get_modifier_state(&self, key_code: &str) -> bool;
+    }
+    // changedTouches: DOMTouchList,
+    // targetTouches: DOMTouchList,
+    // touches: DOMTouchList,
+
+    pub trait UIEventInner: Send + Sync {
+        fn detail(&self) -> i32;
+    }
+    // DOMAbstractView view
+
+    pub trait WheelEventInner: Send + Sync {
+        fn delta_mode(&self) -> u32;
+        fn delta_x(&self) -> f64;
+        fn delta_y(&self) -> f64;
+        fn delta_z(&self) -> f64;
+    }
+
+    pub trait MediaEventInner: Send + Sync {}
+
+    pub trait ImageEventInner: Send + Sync {
+        //     load error
+    }
+
+    pub trait AnimationEventInner: Send + Sync {
+        fn animation_name(&self) -> String;
+        fn pseudo_element(&self) -> String;
+        fn elapsed_time(&self) -> f32;
+    }
+
+    pub trait TransitionEventInner: Send + Sync {
+        fn property_name(&self) -> String;
+        fn pseudo_element(&self) -> String;
+        fn elapsed_time(&self) -> f32;
+    }
+
+    pub trait ToggleEventInner: Send + Sync {}
+
+    pub use util::KeyCode;
+    mod util {
+
+        #[derive(Clone, Copy)]
+        pub enum KeyCode {
+            Backspace = 8,
+            Tab = 9,
+            Enter = 13,
+            Shift = 16,
+            Ctrl = 17,
+            Alt = 18,
+            Pause = 19,
+            CapsLock = 20,
+            Escape = 27,
+            PageUp = 33,
+            PageDown = 34,
+            End = 35,
+            Home = 36,
+            LeftArrow = 37,
+            UpArrow = 38,
+            RightArrow = 39,
+            DownArrow = 40,
+            Insert = 45,
+            Delete = 46,
+            Num0 = 48,
+            Num1 = 49,
+            Num2 = 50,
+            Num3 = 51,
+            Num4 = 52,
+            Num5 = 53,
+            Num6 = 54,
+            Num7 = 55,
+            Num8 = 56,
+            Num9 = 57,
+            A = 65,
+            B = 66,
+            C = 67,
+            D = 68,
+            E = 69,
+            F = 70,
+            G = 71,
+            H = 72,
+            I = 73,
+            J = 74,
+            K = 75,
+            L = 76,
+            M = 77,
+            N = 78,
+            O = 79,
+            P = 80,
+            Q = 81,
+            R = 82,
+            S = 83,
+            T = 84,
+            U = 85,
+            V = 86,
+            W = 87,
+            X = 88,
+            Y = 89,
+            Z = 90,
+            LeftWindow = 91,
+            RightWindow = 92,
+            SelectKey = 93,
+            Numpad0 = 96,
+            Numpad1 = 97,
+            Numpad2 = 98,
+            Numpad3 = 99,
+            Numpad4 = 100,
+            Numpad5 = 101,
+            Numpad6 = 102,
+            Numpad7 = 103,
+            Numpad8 = 104,
+            Numpad9 = 105,
+            Multiply = 106,
+            Add = 107,
+            Subtract = 109,
+            DecimalPoint = 110,
+            Divide = 111,
+            F1 = 112,
+            F2 = 113,
+            F3 = 114,
+            F4 = 115,
+            F5 = 116,
+            F6 = 117,
+            F7 = 118,
+            F8 = 119,
+            F9 = 120,
+            F10 = 121,
+            F11 = 122,
+            F12 = 123,
+            NumLock = 144,
+            ScrollLock = 145,
+            Semicolon = 186,
+            EqualSign = 187,
+            Comma = 188,
+            Dash = 189,
+            Period = 190,
+            ForwardSlash = 191,
+            GraveAccent = 192,
+            OpenBracket = 219,
+            BackSlash = 220,
+            CloseBraket = 221,
+            SingleQuote = 222,
+            Unknown,
+        }
+
+        impl KeyCode {
+            pub fn from_raw_code(i: u8) -> Self {
+                use KeyCode::*;
+                match i {
+                    8 => Backspace,
+                    9 => Tab,
+                    13 => Enter,
+                    16 => Shift,
+                    17 => Ctrl,
+                    18 => Alt,
+                    19 => Pause,
+                    20 => CapsLock,
+                    27 => Escape,
+                    33 => PageUp,
+                    34 => PageDown,
+                    35 => End,
+                    36 => Home,
+                    37 => LeftArrow,
+                    38 => UpArrow,
+                    39 => RightArrow,
+                    40 => DownArrow,
+                    45 => Insert,
+                    46 => Delete,
+                    48 => Num0,
+                    49 => Num1,
+                    50 => Num2,
+                    51 => Num3,
+                    52 => Num4,
+                    53 => Num5,
+                    54 => Num6,
+                    55 => Num7,
+                    56 => Num8,
+                    57 => Num9,
+                    65 => A,
+                    66 => B,
+                    67 => C,
+                    68 => D,
+                    69 => E,
+                    70 => F,
+                    71 => G,
+                    72 => H,
+                    73 => I,
+                    74 => J,
+                    75 => K,
+                    76 => L,
+                    77 => M,
+                    78 => N,
+                    79 => O,
+                    80 => P,
+                    81 => Q,
+                    82 => R,
+                    83 => S,
+                    84 => T,
+                    85 => U,
+                    86 => V,
+                    87 => W,
+                    88 => X,
+                    89 => Y,
+                    90 => Z,
+                    91 => LeftWindow,
+                    92 => RightWindow,
+                    93 => SelectKey,
+                    96 => Numpad0,
+                    97 => Numpad1,
+                    98 => Numpad2,
+                    99 => Numpad3,
+                    100 => Numpad4,
+                    101 => Numpad5,
+                    102 => Numpad6,
+                    103 => Numpad7,
+                    104 => Numpad8,
+                    105 => Numpad9,
+                    106 => Multiply,
+                    107 => Add,
+                    109 => Subtract,
+                    110 => DecimalPoint,
+                    111 => Divide,
+                    112 => F1,
+                    113 => F2,
+                    114 => F3,
+                    115 => F4,
+                    116 => F5,
+                    117 => F6,
+                    118 => F7,
+                    119 => F8,
+                    120 => F9,
+                    121 => F10,
+                    122 => F11,
+                    123 => F12,
+                    144 => NumLock,
+                    145 => ScrollLock,
+                    186 => Semicolon,
+                    187 => EqualSign,
+                    188 => Comma,
+                    189 => Dash,
+                    190 => Period,
+                    191 => ForwardSlash,
+                    192 => GraveAccent,
+                    219 => OpenBracket,
+                    220 => BackSlash,
+                    221 => CloseBraket,
+                    222 => SingleQuote,
+                    _ => Unknown,
+                }
+            }
+
+            // get the raw code
+            pub fn raw_code(&self) -> u32 {
+                *self as u32
+            }
+        }
+    }
+}

+ 456 - 439
packages/core/src/events.rs

@@ -1,8 +1,8 @@
-//! This module provides a set of common events for all Dioxus apps to target, regardless of host platform.
-//! -------------------------------------------------------------------------------------------------------
+//! An event system that's less confusing than Traits + RC;
+//! This should hopefully make it easier to port to other platforms.
 //!
-//! 3rd party renderers are responsible for converting their native events into these virtual event types. Events might
-//! be heavy or need to interact through FFI, so the events themselves are designed to be lazy.
+//! Unfortunately, it is less efficient than the original, but hopefully it's negligible.
+
 use crate::{
     innerlude::Listener,
     innerlude::{ElementId, NodeFactory, ScopeId},
@@ -34,6 +34,7 @@ pub struct UserEvent {
     pub event: SyntheticEvent,
 }
 
+#[derive(Debug)]
 pub enum SyntheticEvent {
     AnimationEvent(on::AnimationEvent),
     ClipboardEvent(on::ClipboardEvent),
@@ -41,7 +42,7 @@ pub enum SyntheticEvent {
     FocusEvent(on::FocusEvent),
     FormEvent(on::FormEvent),
     KeyboardEvent(on::KeyboardEvent),
-    GenericEvent(on::GenericEvent),
+    GenericEvent(DioxusEvent<()>),
     TouchEvent(on::TouchEvent),
     ToggleEvent(on::ToggleEvent),
     MediaEvent(on::MediaEvent),
@@ -51,31 +52,6 @@ pub enum SyntheticEvent {
     TransitionEvent(on::TransitionEvent),
     PointerEvent(on::PointerEvent),
 }
-// ImageEvent(event_data::ImageEvent),
-
-impl std::fmt::Debug for SyntheticEvent {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        let name = match self {
-            SyntheticEvent::ClipboardEvent(_) => "ClipboardEvent",
-            SyntheticEvent::CompositionEvent(_) => "CompositionEvent",
-            SyntheticEvent::KeyboardEvent(_) => "KeyboardEvent",
-            SyntheticEvent::FocusEvent(_) => "FocusEvent",
-            SyntheticEvent::FormEvent(_) => "FormEvent",
-            SyntheticEvent::SelectionEvent(_) => "SelectionEvent",
-            SyntheticEvent::TouchEvent(_) => "TouchEvent",
-            SyntheticEvent::WheelEvent(_) => "WheelEvent",
-            SyntheticEvent::MediaEvent(_) => "MediaEvent",
-            SyntheticEvent::AnimationEvent(_) => "AnimationEvent",
-            SyntheticEvent::TransitionEvent(_) => "TransitionEvent",
-            SyntheticEvent::ToggleEvent(_) => "ToggleEvent",
-            SyntheticEvent::MouseEvent(_) => "MouseEvent",
-            SyntheticEvent::PointerEvent(_) => "PointerEvent",
-            SyntheticEvent::GenericEvent(_) => "GenericEvent",
-        };
-
-        f.debug_struct("VirtualEvent").field("type", &name).finish()
-    }
-}
 
 /// Priority of Event Triggers.
 ///
@@ -129,82 +105,101 @@ pub enum EventPriority {
     Low = 0,
 }
 
-pub(crate) fn event_meta(event: &UserEvent) -> (bool, EventPriority) {
-    use EventPriority::*;
-
-    match event.name {
-        // clipboard
-        "copy" | "cut" | "paste" => (true, Medium),
-
-        // Composition
-        "compositionend" | "compositionstart" | "compositionupdate" => (true, Low),
-
-        // Keyboard
-        "keydown" | "keypress" | "keyup" => (true, High),
+#[derive(Debug)]
+pub struct DioxusEvent<T: Send> {
+    inner: T,
+    raw: Box<dyn Any + Send>,
+}
 
-        // Focus
-        "focus" | "blur" => (true, Low),
+impl<T: Send + Sync> DioxusEvent<T> {
+    pub fn new<F: Send + 'static>(inner: T, raw: F) -> Self {
+        let raw = Box::new(raw);
+        Self { inner, raw }
+    }
 
-        // Form
-        "change" | "input" | "invalid" | "reset" | "submit" => (true, Medium),
+    /// Return a reference to the raw event. User will need to downcast the event to the right platform-specific type.
+    pub fn native<E: 'static>(&self) -> Option<&E> {
+        self.raw.downcast_ref()
+    }
 
-        // Mouse
-        "click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
-        | "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
-        | "mouseleave" | "mouseout" | "mouseover" | "mouseup" => (true, High),
+    /// Returns whether or not a specific event is a bubbling event
+    pub fn bubbles(&self) -> bool {
+        todo!()
+    }
+    /// Sets or returns whether the event should propagate up the hierarchy or not
+    pub fn cancel_bubble(&self) {
+        todo!()
+    }
+    /// Returns whether or not an event can have its default action prevented
+    pub fn cancelable(&self) -> bool {
+        todo!()
+    }
+    /// Returns whether the event is composed or not
+    pub fn composed(&self) -> bool {
+        todo!()
+    }
 
-        "mousemove" => (false, Medium),
+    // Currently not supported because those no way we could possibly support it
+    // just cast the event to the right platform-specific type and return it
+    // /// Returns the event's path
+    // pub fn composed_path(&self) -> String {
+    //     todo!()
+    // }
 
-        // Pointer
-        "pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
-        | "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
-            (true, Medium)
-        }
+    /// Returns the element whose event listeners triggered the event
+    pub fn current_target(&self) {
+        todo!()
+    }
+    /// Returns whether or not the preventDefault method was called for the event
+    pub fn default_prevented(&self) -> bool {
+        todo!()
+    }
+    /// Returns which phase of the event flow is currently being evaluated
+    pub fn event_phase(&self) -> u16 {
+        todo!()
+    }
 
-        // Selection
-        "select" | "touchcancel" | "touchend" => (true, Medium),
+    /// Returns whether or not an event is trusted
+    pub fn is_trusted(&self) -> bool {
+        todo!()
+    }
 
-        // Touch
-        "touchmove" | "touchstart" => (true, Medium),
+    /// Cancels the event if it is cancelable, meaning that the default action that belongs to the event will
+    pub fn prevent_default(&self) {
+        todo!()
+    }
 
-        // Wheel
-        "scroll" | "wheel" => (false, Medium),
+    /// Prevents other listeners of the same event from being called
+    pub fn stop_immediate_propagation(&self) {
+        todo!()
+    }
 
-        // Media
-        "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
-        | "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
-        | "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
-        | "timeupdate" | "volumechange" | "waiting" => (true, Medium),
+    /// Prevents further propagation of an event during event flow
+    pub fn stop_propagation(&self) {
+        todo!()
+    }
 
-        // Animation
-        "animationstart" | "animationend" | "animationiteration" => (true, Medium),
+    /// Returns the element that triggered the event
+    pub fn target(&self) -> Option<Box<dyn Any>> {
+        todo!()
+    }
 
-        // Transition
-        "transitionend" => (true, Medium),
+    /// Returns the time (in milliseconds relative to the epoch) at which the event was created
+    pub fn time_stamp(&self) -> f64 {
+        todo!()
+    }
+}
 
-        // Toggle
-        "toggle" => (true, Medium),
+impl<T: Send + Sync> std::ops::Deref for DioxusEvent<T> {
+    type Target = T;
 
-        _ => (true, Low),
+    fn deref(&self) -> &Self::Target {
+        &self.inner
     }
 }
 
-pub use on::{
-    AnimationEvent, ClipboardEvent, CompositionEvent, FocusEvent, FormEvent, GenericEvent, KeyCode,
-    KeyboardEvent, MediaEvent, MouseEvent, PointerEvent, SelectionEvent, ToggleEvent, TouchEvent,
-    TransitionEvent, WheelEvent,
-};
-
 pub mod on {
-    //! This module defines the synthetic events that all Dioxus apps enable. No matter the platform, every dioxus renderer
-    //! will implement the same events and same behavior (bubbling, cancelation, etc).
-    //!
-    //! Synthetic events are immutable and wrapped in Arc. It is the intention for Dioxus renderers to re-use the underyling
-    //! Arc allocation through "get_mut"
-    //!
-    //! React recently dropped support for re-using event allocation and just passes the real event along.
     use super::*;
-
     macro_rules! event_directory {
         ( $(
             $( #[$attr:meta] )*
@@ -217,13 +212,14 @@ pub mod on {
         )* ) => {
             $(
                 $(#[$attr])*
-                pub struct $wrapper(pub Arc<dyn $eventdata>);
+                #[derive(Debug)]
+                pub struct $wrapper(pub DioxusEvent<$eventdata>);
 
                 // todo: derefing to the event is fine (and easy) but breaks some IDE stuff like (go to source)
                 // going to source in fact takes you to the source of Rc which is... less than useful
                 // Either we ask for this to be changed in Rust-analyzer or manually impkement the trait
                 impl Deref for $wrapper {
-                    type Target = Arc<dyn $eventdata>;
+                    type Target = DioxusEvent<$eventdata>;
                     fn deref(&self) -> &Self::Target {
                         &self.0
                     }
@@ -599,57 +595,20 @@ pub mod on {
         ];
     }
 
-    pub struct GenericEvent(pub Arc<dyn GenericEventInner>);
-
-    pub trait GenericEventInner {
-        /// Return a reference to the raw event. User will need to downcast the event to the right platform-specific type.
-        fn raw_event(&self) -> &dyn Any;
-        /// Returns whether or not a specific event is a bubbling event
-        fn bubbles(&self) -> bool;
-        /// Sets or returns whether the event should propagate up the hierarchy or not
-        fn cancel_bubble(&self);
-        /// Returns whether or not an event can have its default action prevented
-        fn cancelable(&self) -> bool;
-        /// Returns whether the event is composed or not
-        fn composed(&self) -> bool;
-
-        // Currently not supported because those no way we could possibly support it
-        // just cast the event to the right platform-specific type and return it
-        // /// Returns the event's path
-        // fn composed_path(&self) -> String;
-
-        /// Returns the element whose event listeners triggered the event
-        fn current_target(&self);
-        /// Returns whether or not the preventDefault method was called for the event
-        fn default_prevented(&self) -> bool;
-        /// Returns which phase of the event flow is currently being evaluated
-        fn event_phase(&self) -> u16;
-        /// Returns whether or not an event is trusted
-        fn is_trusted(&self) -> bool;
-        /// Cancels the event if it is cancelable, meaning that the default action that belongs to the event will
-        fn prevent_default(&self);
-        /// Prevents other listeners of the same event from being called
-        fn stop_immediate_propagation(&self);
-        /// Prevents further propagation of an event during event flow
-        fn stop_propagation(&self);
-        /// Returns the element that triggered the event
-        fn target(&self);
-        /// Returns the time (in milliseconds relative to the epoch) at which the event was created
-        fn time_stamp(&self) -> f64;
-    }
-
-    pub trait ClipboardEventInner {
+    #[derive(Debug)]
+    pub struct ClipboardEventInner(
         // DOMDataTransfer clipboardData
-    }
+    );
 
-    pub trait CompositionEventInner {
-        fn data(&self) -> String;
+    #[derive(Debug)]
+    pub struct CompositionEventInner {
+        pub data: String,
     }
 
-    pub trait KeyboardEventInner {
-        fn alt_key(&self) -> bool;
-
-        fn char_code(&self) -> u32;
+    #[derive(Debug)]
+    pub struct KeyboardEventInner {
+        pub alt_key: bool,
+        pub char_code: u32,
 
         /// Identify which "key" was entered.
         ///
@@ -669,8 +628,8 @@ pub mod on {
         ///      _ => {}
         /// }
         /// ```
-        ///
-        fn key(&self) -> String;
+        ///    
+        pub key: String,
 
         /// Get the key code as an enum Variant.
         ///
@@ -689,335 +648,393 @@ pub mod on {
         ///     _ => {}
         /// }
         /// ```
-        ///
-        fn key_code(&self) -> KeyCode;
-
-        /// Check if the ctrl key was pressed down
-        fn ctrl_key(&self) -> bool;
-
-        fn get_modifier_state(&self, key_code: &str) -> bool;
-
-        fn locale(&self) -> String;
-        fn location(&self) -> usize;
-        fn meta_key(&self) -> bool;
-        fn repeat(&self) -> bool;
-        fn shift_key(&self) -> bool;
-        fn which(&self) -> usize;
+        ///    
+        pub key_code: KeyCode,
+        pub ctrl_key: bool,
+        pub locale: String,
+        pub location: usize,
+        pub meta_key: bool,
+        pub repeat: bool,
+        pub shift_key: bool,
+        pub which: usize,
+        // get_modifier_state: bool,
     }
 
-    pub trait FocusEventInner {
-        /* DOMEventInnerTarget relatedTarget */
-    }
+    #[derive(Debug)]
+    pub struct FocusEventInner {/* DOMEventInner:  Send + SyncTarget relatedTarget */}
 
-    pub trait FormEventInner {
-        fn value(&self) -> String;
+    #[derive(Debug)]
+    pub struct FormEventInner {
+        /* DOMEventInner:  Send + SyncTarget relatedTarget */
+        pub value: String,
     }
 
-    pub trait MouseEventInner {
-        fn alt_key(&self) -> bool;
-        fn button(&self) -> i16;
-        fn buttons(&self) -> u16;
-        /// Get the X coordinate of the mouse relative to the window
-        fn client_x(&self) -> i32;
-        fn client_y(&self) -> i32;
-        fn ctrl_key(&self) -> bool;
-        fn meta_key(&self) -> bool;
-        fn page_x(&self) -> i32;
-        fn page_y(&self) -> i32;
-        fn screen_x(&self) -> i32;
-        fn screen_y(&self) -> i32;
-        fn shift_key(&self) -> bool;
-        fn get_modifier_state(&self, key_code: &str) -> bool;
+    #[derive(Debug)]
+    pub struct MouseEventInner {
+        pub alt_key: bool,
+        pub button: i16,
+        pub buttons: u16,
+        pub client_x: i32,
+        pub client_y: i32,
+        pub ctrl_key: bool,
+        pub meta_key: bool,
+        pub page_x: i32,
+        pub page_y: i32,
+        pub screen_x: i32,
+        pub screen_y: i32,
+        pub shift_key: bool,
+        // fn get_modifier_state(&self, key_code: &str) -> bool;
     }
 
-    pub trait PointerEventInner {
+    #[derive(Debug)]
+    pub struct PointerEventInner {
         // Mouse only
-        fn alt_key(&self) -> bool;
-        fn button(&self) -> i16;
-        fn buttons(&self) -> u16;
-        fn client_x(&self) -> i32;
-        fn client_y(&self) -> i32;
-        fn ctrl_key(&self) -> bool;
-        fn meta_key(&self) -> bool;
-        fn page_x(&self) -> i32;
-        fn page_y(&self) -> i32;
-        fn screen_x(&self) -> i32;
-        fn screen_y(&self) -> i32;
-        fn shift_key(&self) -> bool;
-        fn get_modifier_state(&self, key_code: &str) -> bool;
-        fn pointer_id(&self) -> i32;
-        fn width(&self) -> i32;
-        fn height(&self) -> i32;
-        fn pressure(&self) -> f32;
-        fn tangential_pressure(&self) -> f32;
-        fn tilt_x(&self) -> i32;
-        fn tilt_y(&self) -> i32;
-        fn twist(&self) -> i32;
-        fn pointer_type(&self) -> String;
-        fn is_primary(&self) -> bool;
+        pub alt_key: bool,
+        pub button: i16,
+        pub buttons: u16,
+        pub client_x: i32,
+        pub client_y: i32,
+        pub ctrl_key: bool,
+        pub meta_key: bool,
+        pub page_x: i32,
+        pub page_y: i32,
+        pub screen_x: i32,
+        pub screen_y: i32,
+        pub shift_key: bool,
+        pub pointer_id: i32,
+        pub width: i32,
+        pub height: i32,
+        pub pressure: f32,
+        pub tangential_pressure: f32,
+        pub tilt_x: i32,
+        pub tilt_y: i32,
+        pub twist: i32,
+        pub pointer_type: String,
+        pub is_primary: bool,
+        // pub get_modifier_state: bool,
     }
 
-    pub trait SelectionEventInner {}
+    #[derive(Debug)]
+    pub struct SelectionEventInner {}
 
-    pub trait TouchEventInner {
-        fn alt_key(&self) -> bool;
-        fn ctrl_key(&self) -> bool;
-        fn meta_key(&self) -> bool;
-        fn shift_key(&self) -> bool;
-        fn get_modifier_state(&self, key_code: &str) -> bool;
+    #[derive(Debug)]
+    pub struct TouchEventInner {
+        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,
     }
 
-    pub trait UIEventInner {
-        // DOMAbstractView view
-        fn detail(&self) -> i32;
+    #[derive(Debug)]
+    pub struct WheelEventInner {
+        pub delta_mode: u32,
+        pub delta_x: f64,
+        pub delta_y: f64,
+        pub delta_z: f64,
     }
 
-    pub trait WheelEventInner {
-        fn delta_mode(&self) -> u32;
-        fn delta_x(&self) -> f64;
-        fn delta_y(&self) -> f64;
-        fn delta_z(&self) -> f64;
-    }
+    #[derive(Debug)]
+    pub struct MediaEventInner {}
 
-    pub trait MediaEventInner {}
-
-    pub trait ImageEventInner {
+    #[derive(Debug)]
+    pub struct ImageEventInner {
         //     load error
+        pub load_error: bool,
     }
 
-    pub trait AnimationEventInner {
-        fn animation_name(&self) -> String;
-        fn pseudo_element(&self) -> String;
-        fn elapsed_time(&self) -> f32;
+    #[derive(Debug)]
+    pub struct AnimationEventInner {
+        pub animation_name: String,
+        pub pseudo_element: String,
+        pub elapsed_time: f32,
     }
 
-    pub trait TransitionEventInner {
-        fn property_name(&self) -> String;
-        fn pseudo_element(&self) -> String;
-        fn elapsed_time(&self) -> f32;
+    #[derive(Debug)]
+    pub struct TransitionEventInner {
+        pub property_name: String,
+        pub pseudo_element: String,
+        pub elapsed_time: f32,
     }
 
-    pub trait ToggleEventInner {}
-
-    pub use util::KeyCode;
-    mod util {
-
-        #[derive(Clone, Copy)]
-        pub enum KeyCode {
-            Backspace = 8,
-            Tab = 9,
-            Enter = 13,
-            Shift = 16,
-            Ctrl = 17,
-            Alt = 18,
-            Pause = 19,
-            CapsLock = 20,
-            Escape = 27,
-            PageUp = 33,
-            PageDown = 34,
-            End = 35,
-            Home = 36,
-            LeftArrow = 37,
-            UpArrow = 38,
-            RightArrow = 39,
-            DownArrow = 40,
-            Insert = 45,
-            Delete = 46,
-            Num0 = 48,
-            Num1 = 49,
-            Num2 = 50,
-            Num3 = 51,
-            Num4 = 52,
-            Num5 = 53,
-            Num6 = 54,
-            Num7 = 55,
-            Num8 = 56,
-            Num9 = 57,
-            A = 65,
-            B = 66,
-            C = 67,
-            D = 68,
-            E = 69,
-            F = 70,
-            G = 71,
-            H = 72,
-            I = 73,
-            J = 74,
-            K = 75,
-            L = 76,
-            M = 77,
-            N = 78,
-            O = 79,
-            P = 80,
-            Q = 81,
-            R = 82,
-            S = 83,
-            T = 84,
-            U = 85,
-            V = 86,
-            W = 87,
-            X = 88,
-            Y = 89,
-            Z = 90,
-            LeftWindow = 91,
-            RightWindow = 92,
-            SelectKey = 93,
-            Numpad0 = 96,
-            Numpad1 = 97,
-            Numpad2 = 98,
-            Numpad3 = 99,
-            Numpad4 = 100,
-            Numpad5 = 101,
-            Numpad6 = 102,
-            Numpad7 = 103,
-            Numpad8 = 104,
-            Numpad9 = 105,
-            Multiply = 106,
-            Add = 107,
-            Subtract = 109,
-            DecimalPoint = 110,
-            Divide = 111,
-            F1 = 112,
-            F2 = 113,
-            F3 = 114,
-            F4 = 115,
-            F5 = 116,
-            F6 = 117,
-            F7 = 118,
-            F8 = 119,
-            F9 = 120,
-            F10 = 121,
-            F11 = 122,
-            F12 = 123,
-            NumLock = 144,
-            ScrollLock = 145,
-            Semicolon = 186,
-            EqualSign = 187,
-            Comma = 188,
-            Dash = 189,
-            Period = 190,
-            ForwardSlash = 191,
-            GraveAccent = 192,
-            OpenBracket = 219,
-            BackSlash = 220,
-            CloseBraket = 221,
-            SingleQuote = 222,
-            Unknown,
+    #[derive(Debug)]
+    pub struct ToggleEventInner {}
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum KeyCode {
+    Backspace = 8,
+    Tab = 9,
+    Enter = 13,
+    Shift = 16,
+    Ctrl = 17,
+    Alt = 18,
+    Pause = 19,
+    CapsLock = 20,
+    Escape = 27,
+    PageUp = 33,
+    PageDown = 34,
+    End = 35,
+    Home = 36,
+    LeftArrow = 37,
+    UpArrow = 38,
+    RightArrow = 39,
+    DownArrow = 40,
+    Insert = 45,
+    Delete = 46,
+    Num0 = 48,
+    Num1 = 49,
+    Num2 = 50,
+    Num3 = 51,
+    Num4 = 52,
+    Num5 = 53,
+    Num6 = 54,
+    Num7 = 55,
+    Num8 = 56,
+    Num9 = 57,
+    A = 65,
+    B = 66,
+    C = 67,
+    D = 68,
+    E = 69,
+    F = 70,
+    G = 71,
+    H = 72,
+    I = 73,
+    J = 74,
+    K = 75,
+    L = 76,
+    M = 77,
+    N = 78,
+    O = 79,
+    P = 80,
+    Q = 81,
+    R = 82,
+    S = 83,
+    T = 84,
+    U = 85,
+    V = 86,
+    W = 87,
+    X = 88,
+    Y = 89,
+    Z = 90,
+    LeftWindow = 91,
+    RightWindow = 92,
+    SelectKey = 93,
+    Numpad0 = 96,
+    Numpad1 = 97,
+    Numpad2 = 98,
+    Numpad3 = 99,
+    Numpad4 = 100,
+    Numpad5 = 101,
+    Numpad6 = 102,
+    Numpad7 = 103,
+    Numpad8 = 104,
+    Numpad9 = 105,
+    Multiply = 106,
+    Add = 107,
+    Subtract = 109,
+    DecimalPoint = 110,
+    Divide = 111,
+    F1 = 112,
+    F2 = 113,
+    F3 = 114,
+    F4 = 115,
+    F5 = 116,
+    F6 = 117,
+    F7 = 118,
+    F8 = 119,
+    F9 = 120,
+    F10 = 121,
+    F11 = 122,
+    F12 = 123,
+    NumLock = 144,
+    ScrollLock = 145,
+    Semicolon = 186,
+    EqualSign = 187,
+    Comma = 188,
+    Dash = 189,
+    Period = 190,
+    ForwardSlash = 191,
+    GraveAccent = 192,
+    OpenBracket = 219,
+    BackSlash = 220,
+    CloseBraket = 221,
+    SingleQuote = 222,
+    Unknown,
+}
+
+impl KeyCode {
+    pub fn from_raw_code(i: u8) -> Self {
+        use KeyCode::*;
+        match i {
+            8 => Backspace,
+            9 => Tab,
+            13 => Enter,
+            16 => Shift,
+            17 => Ctrl,
+            18 => Alt,
+            19 => Pause,
+            20 => CapsLock,
+            27 => Escape,
+            33 => PageUp,
+            34 => PageDown,
+            35 => End,
+            36 => Home,
+            37 => LeftArrow,
+            38 => UpArrow,
+            39 => RightArrow,
+            40 => DownArrow,
+            45 => Insert,
+            46 => Delete,
+            48 => Num0,
+            49 => Num1,
+            50 => Num2,
+            51 => Num3,
+            52 => Num4,
+            53 => Num5,
+            54 => Num6,
+            55 => Num7,
+            56 => Num8,
+            57 => Num9,
+            65 => A,
+            66 => B,
+            67 => C,
+            68 => D,
+            69 => E,
+            70 => F,
+            71 => G,
+            72 => H,
+            73 => I,
+            74 => J,
+            75 => K,
+            76 => L,
+            77 => M,
+            78 => N,
+            79 => O,
+            80 => P,
+            81 => Q,
+            82 => R,
+            83 => S,
+            84 => T,
+            85 => U,
+            86 => V,
+            87 => W,
+            88 => X,
+            89 => Y,
+            90 => Z,
+            91 => LeftWindow,
+            92 => RightWindow,
+            93 => SelectKey,
+            96 => Numpad0,
+            97 => Numpad1,
+            98 => Numpad2,
+            99 => Numpad3,
+            100 => Numpad4,
+            101 => Numpad5,
+            102 => Numpad6,
+            103 => Numpad7,
+            104 => Numpad8,
+            105 => Numpad9,
+            106 => Multiply,
+            107 => Add,
+            109 => Subtract,
+            110 => DecimalPoint,
+            111 => Divide,
+            112 => F1,
+            113 => F2,
+            114 => F3,
+            115 => F4,
+            116 => F5,
+            117 => F6,
+            118 => F7,
+            119 => F8,
+            120 => F9,
+            121 => F10,
+            122 => F11,
+            123 => F12,
+            144 => NumLock,
+            145 => ScrollLock,
+            186 => Semicolon,
+            187 => EqualSign,
+            188 => Comma,
+            189 => Dash,
+            190 => Period,
+            191 => ForwardSlash,
+            192 => GraveAccent,
+            219 => OpenBracket,
+            220 => BackSlash,
+            221 => CloseBraket,
+            222 => SingleQuote,
+            _ => Unknown,
         }
+    }
 
-        impl KeyCode {
-            pub fn from_raw_code(i: u8) -> Self {
-                use KeyCode::*;
-                match i {
-                    8 => Backspace,
-                    9 => Tab,
-                    13 => Enter,
-                    16 => Shift,
-                    17 => Ctrl,
-                    18 => Alt,
-                    19 => Pause,
-                    20 => CapsLock,
-                    27 => Escape,
-                    33 => PageUp,
-                    34 => PageDown,
-                    35 => End,
-                    36 => Home,
-                    37 => LeftArrow,
-                    38 => UpArrow,
-                    39 => RightArrow,
-                    40 => DownArrow,
-                    45 => Insert,
-                    46 => Delete,
-                    48 => Num0,
-                    49 => Num1,
-                    50 => Num2,
-                    51 => Num3,
-                    52 => Num4,
-                    53 => Num5,
-                    54 => Num6,
-                    55 => Num7,
-                    56 => Num8,
-                    57 => Num9,
-                    65 => A,
-                    66 => B,
-                    67 => C,
-                    68 => D,
-                    69 => E,
-                    70 => F,
-                    71 => G,
-                    72 => H,
-                    73 => I,
-                    74 => J,
-                    75 => K,
-                    76 => L,
-                    77 => M,
-                    78 => N,
-                    79 => O,
-                    80 => P,
-                    81 => Q,
-                    82 => R,
-                    83 => S,
-                    84 => T,
-                    85 => U,
-                    86 => V,
-                    87 => W,
-                    88 => X,
-                    89 => Y,
-                    90 => Z,
-                    91 => LeftWindow,
-                    92 => RightWindow,
-                    93 => SelectKey,
-                    96 => Numpad0,
-                    97 => Numpad1,
-                    98 => Numpad2,
-                    99 => Numpad3,
-                    100 => Numpad4,
-                    101 => Numpad5,
-                    102 => Numpad6,
-                    103 => Numpad7,
-                    104 => Numpad8,
-                    105 => Numpad9,
-                    106 => Multiply,
-                    107 => Add,
-                    109 => Subtract,
-                    110 => DecimalPoint,
-                    111 => Divide,
-                    112 => F1,
-                    113 => F2,
-                    114 => F3,
-                    115 => F4,
-                    116 => F5,
-                    117 => F6,
-                    118 => F7,
-                    119 => F8,
-                    120 => F9,
-                    121 => F10,
-                    122 => F11,
-                    123 => F12,
-                    144 => NumLock,
-                    145 => ScrollLock,
-                    186 => Semicolon,
-                    187 => EqualSign,
-                    188 => Comma,
-                    189 => Dash,
-                    190 => Period,
-                    191 => ForwardSlash,
-                    192 => GraveAccent,
-                    219 => OpenBracket,
-                    220 => BackSlash,
-                    221 => CloseBraket,
-                    222 => SingleQuote,
-                    _ => Unknown,
-                }
-            }
+    // get the raw code
+    pub fn raw_code(&self) -> u32 {
+        *self as u32
+    }
+}
 
-            // get the raw code
-            pub fn raw_code(&self) -> u32 {
-                *self as u32
-            }
+pub(crate) fn event_meta(event: &UserEvent) -> (bool, EventPriority) {
+    use EventPriority::*;
+
+    match event.name {
+        // clipboard
+        "copy" | "cut" | "paste" => (true, Medium),
+
+        // Composition
+        "compositionend" | "compositionstart" | "compositionupdate" => (true, Low),
+
+        // Keyboard
+        "keydown" | "keypress" | "keyup" => (true, High),
+
+        // Focus
+        "focus" | "blur" => (true, Low),
+
+        // Form
+        "change" | "input" | "invalid" | "reset" | "submit" => (true, Medium),
+
+        // Mouse
+        "click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
+        | "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
+        | "mouseleave" | "mouseout" | "mouseover" | "mouseup" => (true, High),
+
+        "mousemove" => (false, Medium),
+
+        // Pointer
+        "pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
+        | "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
+            (true, Medium)
         }
+
+        // Selection
+        "select" | "touchcancel" | "touchend" => (true, Medium),
+
+        // Touch
+        "touchmove" | "touchstart" => (true, Medium),
+
+        // Wheel
+        "scroll" | "wheel" => (false, Medium),
+
+        // Media
+        "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
+        | "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
+        | "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
+        | "timeupdate" | "volumechange" | "waiting" => (true, Medium),
+
+        // Animation
+        "animationstart" | "animationend" | "animationiteration" => (true, Medium),
+
+        // Transition
+        "transitionend" => (true, Medium),
+
+        // Toggle
+        "toggle" => (true, Medium),
+
+        _ => (true, Low),
     }
 }

+ 4 - 2
packages/core/src/lib.rs

@@ -19,6 +19,7 @@ pub mod context;
 pub mod diff;
 pub mod diff_stack;
 pub mod events;
+// pub mod events2;
 pub mod heuristics;
 pub mod hooklist;
 pub mod hooks;
@@ -61,8 +62,8 @@ pub(crate) mod innerlude {
 
 pub use crate::innerlude::{
     Context, DioxusElement, DomEdit, DomTree, ElementId, EventPriority, LazyNodes, MountType,
-    Mutations, NodeFactory, Properties, ScopeId, SuspendedContext, SyntheticEvent, TaskHandle,
-    TestDom, ThreadsafeVirtualDom, UserEvent, VNode, VirtualDom, FC,
+    Mutations, NodeFactory, Properties, SchedulerMsg, ScopeId, SuspendedContext, SyntheticEvent,
+    TaskHandle, TestDom, ThreadsafeVirtualDom, UserEvent, VNode, VirtualDom, FC,
 };
 
 pub mod prelude {
@@ -78,4 +79,5 @@ pub mod exports {
     //! Important dependencies that are used by the rest of the library
     // the foundation of this library
     pub use bumpalo;
+    pub use futures_channel;
 }

+ 0 - 2
packages/core/src/mutations.rs

@@ -185,7 +185,6 @@ pub enum DomEdit<'bump> {
         root: u64,
     },
 
-    RemoveAllChildren,
     CreateTextNode {
         text: &'bump str,
         id: u64,
@@ -232,7 +231,6 @@ impl DomEdit<'_> {
             DomEdit::AppendChildren { .. } => id == "AppendChildren",
             DomEdit::ReplaceWith { .. } => id == "ReplaceWith",
             DomEdit::Remove { .. } => id == "Remove",
-            DomEdit::RemoveAllChildren => id == "RemoveAllChildren",
             DomEdit::CreateTextNode { .. } => id == "CreateTextNode",
             DomEdit::CreateElement { .. } => id == "CreateElement",
             DomEdit::CreateElementNs { .. } => id == "CreateElementNs",

+ 0 - 10
packages/core/src/resources.rs

@@ -43,16 +43,6 @@ impl ResourcePool {
         inner.get_mut(idx.0)
     }
 
-    // return a bumpframe with a lifetime attached to the arena borrow
-    // this is useful for merging lifetimes
-    pub fn with_scope_vnode<'b>(
-        &self,
-        _id: ScopeId,
-        _f: impl FnOnce(&mut Scope) -> &VNode<'b>,
-    ) -> Option<&VNode<'b>> {
-        todo!()
-    }
-
     pub fn try_remove(&self, id: ScopeId) -> Option<Scope> {
         let inner = unsafe { &mut *self.components.get() };
         Some(inner.remove(id.0))

+ 15 - 8
packages/core/src/scheduler.rs

@@ -106,7 +106,8 @@ pub enum SchedulerMsg {
 }
 
 pub enum TaskMsg {
-    SubmitTask(FiberTask, u64),
+    // SubmitTask(FiberTask, u64),
+    // SubmitTask(FiberTask, u64),
     ToggleTask(u64),
     PauseTask(u64),
     ResumeTask(u64),
@@ -163,7 +164,10 @@ pub(crate) struct Scheduler {
 }
 
 impl Scheduler {
-    pub(crate) fn new() -> Self {
+    pub(crate) fn new(
+        sender: UnboundedSender<SchedulerMsg>,
+        receiver: UnboundedReceiver<SchedulerMsg>,
+    ) -> Self {
         /*
         Preallocate 2000 elements and 100 scopes to avoid dynamic allocation.
         Perhaps this should be configurable from some external config?
@@ -173,7 +177,6 @@ impl Scheduler {
 
         let heuristics = HeuristicsEngine::new();
 
-        let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>();
         let task_counter = Rc::new(Cell::new(0));
 
         let channel = EventChannel {
@@ -183,15 +186,19 @@ impl Scheduler {
                 let sender = sender.clone();
                 Rc::new(move |id| sender.unbounded_send(SchedulerMsg::Immediate(id)).unwrap())
             },
+            // todo: we want to get the futures out of the scheduler message
+            // the scheduler message should be send/sync
             submit_task: {
                 Rc::new(move |fiber_task| {
                     let task_id = task_counter.get();
                     task_counter.set(task_id + 1);
-                    sender
-                        .unbounded_send(SchedulerMsg::Task(TaskMsg::SubmitTask(
-                            fiber_task, task_id,
-                        )))
-                        .unwrap();
+
+                    todo!();
+                    // sender
+                    //     .unbounded_send(SchedulerMsg::Task(TaskMsg::SubmitTask(
+                    //         fiber_task, task_id,
+                    //     )))
+                    //     .unwrap();
                     TaskHandle {
                         our_id: task_id,
                         sender: sender.clone(),

+ 2 - 1
packages/core/src/test_dom.rs

@@ -12,7 +12,8 @@ pub struct TestDom {
 impl TestDom {
     pub fn new() -> TestDom {
         let bump = Bump::new();
-        let scheduler = Scheduler::new();
+        let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>();
+        let scheduler = Scheduler::new(sender, receiver);
         TestDom { bump, scheduler }
     }
 

+ 19 - 4
packages/core/src/threadsafe.rs

@@ -1,5 +1,12 @@
 //! A threadsafe wrapper for the VirtualDom
-
+//!
+//! This is an experimental module, and must be explicitly opted-into.
+//!
+//! It's not guaranteed that this module produces safe results, so use at your own peril.
+//!
+//! The only real "right" answer to a Send VirtualDom is by ensuring all hook data is Send
+//!
+//!
 use std::sync::{Arc, Mutex, MutexGuard};
 
 use crate::VirtualDom;
@@ -18,9 +25,17 @@ use crate::VirtualDom;
 /// directly. Even then, it's not possible to access any hook data. This means that non-Send types are only "in play"
 /// while the VirtualDom is locked with a non-Send marker.
 ///
-/// Note that calling "wait for work" on the regular VirtualDom is inherently non-Send. If there are async tasks that
-/// need to be awaited, they must be done thread-local since we don't place any requirements on user tasks. This can be
-/// done with the function "spawn_local" in either tokio or async_std.
+/// Calling "wait for work" on the ThreadsafeVirtualDom does indeed work, because this method only accesses `Send` types.
+/// Otherwise, the VirtualDom must be unlocked on the current thread to modify any data.
+///
+/// Dioxus does have the concept of local tasks and non-local tasks.
+///
+/// For the ThreadsafeVirtualDom, non-Send tasks are not ran - and will error out during a Debug build if one is submitted.
+///
+///
+///
+/// When Tasks are submitted to a thread-local executor,
+///
 pub struct ThreadsafeVirtualDom {
     inner: Arc<Mutex<VirtualDom>>,
 }

+ 58 - 52
packages/core/src/virtual_dom.rs

@@ -19,8 +19,10 @@
 //! This module includes just the barebones for a complete VirtualDOM API.
 //! Additional functionality is defined in the respective files.
 
+use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
+
 use crate::innerlude::*;
-use std::{any::Any, rc::Rc, sync::Arc};
+use std::{any::Any, rc::Rc};
 
 /// An integrated virtual node system that progresses events and diffs UI trees.
 ///
@@ -121,6 +123,20 @@ impl VirtualDom {
     /// let mutations = dom.rebuild();
     /// ```
     pub fn new_with_props<P: 'static + Send>(root: FC<P>, root_props: P) -> Self {
+        let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>();
+        Self::new_with_props_and_scheduler(root, root_props, sender, receiver)
+    }
+
+    /// Launch the VirtualDom, but provide your own channel for receiving and sending messages into the scheduler.
+    ///
+    /// This is useful when the VirtualDom must be driven from outside a thread and it doesn't make sense to wait for the
+    /// VirtualDom to be created just to retrive its channel receiver.
+    pub fn new_with_props_and_scheduler<P: 'static + Send>(
+        root: FC<P>,
+        root_props: P,
+        sender: UnboundedSender<SchedulerMsg>,
+        receiver: UnboundedReceiver<SchedulerMsg>,
+    ) -> Self {
         let root_fc = Box::new(root);
 
         let root_props: Rc<dyn Any + Send> = Rc::new(root_props);
@@ -132,7 +148,7 @@ impl VirtualDom {
             std::mem::transmute(root(Context { scope }, props))
         });
 
-        let scheduler = Scheduler::new();
+        let scheduler = Scheduler::new(sender, receiver);
 
         let base_scope = scheduler.pool.insert_scope_with_key(|myidx| {
             Scope::new(
@@ -343,47 +359,46 @@ impl VirtualDom {
     /// Waits for the scheduler to have work
     /// This lets us poll async tasks during idle periods without blocking the main thread.
     pub async fn wait_for_work(&mut self) {
-        todo!("making vdom send right now");
-        // if self.scheduler.has_any_work() {
-        //     log::debug!("No need to wait for work, we already have some");
-        //     return;
-        // }
-
-        // log::debug!("No active work.... waiting for some...");
-        // use futures_util::StreamExt;
-
-        // // right now this won't poll events if there is ongoing work
-        // // in the future we want to prioritize some events over ongoing work
-        // // this is coming in the "priorities" PR
-
-        // // Wait for any new events if we have nothing to do
-        // // todo: poll the events once even if there is work to do to prevent starvation
-        // futures_util::select! {
-        //     _ = self.scheduler.async_tasks.next() => {}
-        //     msg = self.scheduler.receiver.next() => {
-        //         match msg.unwrap() {
-        //             SchedulerMsg::Task(t) => todo!(),
-        //             SchedulerMsg::Immediate(im) => {
-        //                 self.scheduler.dirty_scopes.insert(im);
-        //             }
-        //             SchedulerMsg::UiEvent(evt) => {
-        //                 self.scheduler.ui_events.push_back(evt);
-        //             }
-        //         }
-        //     },
-        // }
-
-        // while let Ok(Some(msg)) = self.scheduler.receiver.try_next() {
-        //     match msg {
-        //         SchedulerMsg::Task(t) => todo!(),
-        //         SchedulerMsg::Immediate(im) => {
-        //             self.scheduler.dirty_scopes.insert(im);
-        //         }
-        //         SchedulerMsg::UiEvent(evt) => {
-        //             self.scheduler.ui_events.push_back(evt);
-        //         }
-        //     }
-        // }
+        if self.scheduler.has_any_work() {
+            log::debug!("No need to wait for work, we already have some");
+            return;
+        }
+
+        log::debug!("No active work.... waiting for some...");
+        use futures_util::StreamExt;
+
+        // right now this won't poll events if there is ongoing work
+        // in the future we want to prioritize some events over ongoing work
+        // this is coming in the "priorities" PR
+
+        // Wait for any new events if we have nothing to do
+        // todo: poll the events once even if there is work to do to prevent starvation
+        futures_util::select! {
+            _ = self.scheduler.async_tasks.next() => {}
+            msg = self.scheduler.receiver.next() => {
+                match msg.unwrap() {
+                    SchedulerMsg::Task(t) => todo!(),
+                    SchedulerMsg::Immediate(im) => {
+                        self.scheduler.dirty_scopes.insert(im);
+                    }
+                    SchedulerMsg::UiEvent(evt) => {
+                        self.scheduler.ui_events.push_back(evt);
+                    }
+                }
+            },
+        }
+
+        while let Ok(Some(msg)) = self.scheduler.receiver.try_next() {
+            match msg {
+                SchedulerMsg::Task(t) => todo!(),
+                SchedulerMsg::Immediate(im) => {
+                    self.scheduler.dirty_scopes.insert(im);
+                }
+                SchedulerMsg::UiEvent(evt) => {
+                    self.scheduler.ui_events.push_back(evt);
+                }
+            }
+        }
     }
 }
 
@@ -407,14 +422,5 @@ impl std::fmt::Display for VirtualDom {
     }
 }
 
-/*
-Send safety...
-
-The VirtualDom can only be "send" if the internals exposed to user code are also "send".
-
-IE it's okay to move an Rc from one thread to another thread as long as it's only used internally
-
-*/
-
 // we never actually use the contents of this root caller
 struct RootCaller(Rc<dyn for<'b> Fn(&'b Scope) -> DomTree<'b> + 'static>);

+ 0 - 4
packages/core/tests/eventsystem.rs

@@ -16,8 +16,4 @@ async fn event_queue_works() {
 
     let mut dom = VirtualDom::new(App);
     let edits = dom.rebuild();
-
-    async_std::task::spawn_local(async move {
-        // let mutations = dom.run_unbounded().await;
-    });
 }

+ 2 - 1
packages/desktop/.vscode/settings.json

@@ -1,3 +1,4 @@
 {
-  "rust-analyzer.inlayHints.enable": true
+  "rust-analyzer.inlayHints.enable": true,
+  "rust-analyzer.cargo.allFeatures": true
 }

+ 4 - 3
packages/desktop/Cargo.toml

@@ -19,15 +19,16 @@ thiserror = "1.0.23"
 log = "0.4.13"
 fern = { version = "0.6.0", features = ["colored"] }
 html-escape = "0.2.9"
-wry = "0.11.0"
+# wry = { version = "0.12.2", git = "https://github.com/jkelleyrtp/wry.git", branch = "jk/fnmut_rpc" }
+wry = "0.12.2"
 tokio = { version = "1.12.0", features = ["full"] }
+futures-channel = "0.3.16"
 
 
 [dev-dependencies]
 dioxus-html = { path = "../html" }
-tide = "0.15.0"
-tide-websockets = "0.3.0"
 dioxus-core-macro = { path = "../core-macro" }
+dioxus-hooks = { path = "../hooks" }
 # thiserror = "1.0.23"
 # log = "0.4.13"
 # fern = { version = "0.6.0", features = ["colored"] }

+ 17 - 19
packages/desktop/README.md

@@ -6,26 +6,19 @@ Dioxus-webview is an attempt at making a simpler "Tauri" where creating desktop
 
 ```rust
 // main.rs
-#[async_std::main]
-async fn main() {
-   dioxus_desktop::new(|cx, props|{
-       let (count, set_count) = use_state(cx, || 0);
-       cx.render(html! {
-            <div>
-                <h1> "Dioxus Desktop Demo" </h1>
-                <p> "Count is {count}"</p>
-                <button onclick=|_| set_count(count + 1) >
-                    "Click to increment"
-                </button>
-            </div>
-       })
-   })
-   .configure_webview(|view| {
-      // custom webview config options
-   })
-   .launch()
-   .await;
+fn main() {
+    dioxus_desktop::new(App, |c| c)
+    .launch()
+    .await;
 }
+static App: FC<()> = |cx, props|{
+    let (count, set_count) = use_state(cx, || 0);
+    rsx!(cx, div {
+        h1 { "Dioxus Desktop Demo" }
+        p { "Count is {count}"}
+        button { onclick: move |_| count += 1}
+    })
+};
 ```
 
 and then to create a native .app:
@@ -45,3 +38,8 @@ By bridging the native process, desktop apps can access full multithreading powe
 Dioxus-desktop is a pure liveview application where all of the state and event handlers are proxied through the liveview and into the native process. For pure server-based liveview, this would normally be too slow (in both render performance and latency), but because the VDom is local, desktop apps are just as fast as Electron.
 
 Dioxus-desktop leverages dioxus-liveview under the hood, but with convenience wrappers around setting up the VDom bridge, proxying events, and serving the initial WebSys-Renderer. The backend is served by Tide, so an async runtime _is_ needed - we recommend async-std in Tokio mode.
+
+
+## Async Runtime
+
+

+ 33 - 0
packages/desktop/examples/core.rs

@@ -0,0 +1,33 @@
+use dioxus_core::prelude::*;
+use dioxus_core_macro::*;
+use dioxus_html as dioxus_elements;
+
+fn main() {
+    let (window_loop, tasks) = dioxus_desktop::start(App, |c| c);
+
+    std::thread::spawn(move || {
+        //
+        let runtime = tokio::runtime::Builder::new_multi_thread()
+            .enable_all()
+            .build()
+            .unwrap();
+
+        runtime.block_on(async move {
+            let mut vir = VirtualDom::new_with_props(root, props);
+            let channel = vir.get_event_sender();
+            loop {
+                vir.wait_for_work().await;
+                let edits = vir.run_with_deadline(|| false);
+                let edit_string = serde_json::to_string(&edits[0].edits).unwrap();
+                event_tx.send(edit_string).unwrap();
+            }
+        })
+    });
+
+    window_loop.run();
+}
+
+static App: FC<()> = |cx| {
+    //
+    cx.render(rsx!(div {}))
+};

+ 111 - 0
packages/desktop/examples/crm.rs

@@ -0,0 +1,111 @@
+use dioxus_core as dioxus;
+use dioxus_core::prelude::*;
+use dioxus_core_macro::*;
+use dioxus_hooks::*;
+
+use dioxus_html as dioxus_elements;
+
+fn main() {
+    dioxus_desktop::set_up_logging(true);
+    dioxus_desktop::launch(App, |c| c).unwrap();
+}
+
+enum Scene {
+    ClientsList,
+    NewClientForm,
+    Settings,
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct Client {
+    pub first_name: String,
+    pub last_name: String,
+    pub description: String,
+}
+
+static App: FC<()> = |cx, _| {
+    let scene = use_state(cx, || Scene::ClientsList);
+    let clients = use_ref(cx, || vec![] as Vec<Client>);
+
+    let firstname = use_state(cx, || String::new());
+    let lastname = use_state(cx, || String::new());
+    let description = use_state(cx, || String::new());
+
+    let scene = match *scene {
+        Scene::ClientsList => {
+            rsx!(cx, div { class: "crm"
+                h2 { "List of clients" margin_bottom: "10px" }
+                div { class: "clients" margin_left: "10px"
+                    {clients.read().iter().map(|client| rsx!(
+                        div { class: "client" style: "margin-bottom: 50px"
+                            p { "First Name: {client.first_name}" }
+                            p { "Last Name: {client.last_name}" }
+                            p {"Description: {client.description}"}
+                        })
+                    )}
+                }
+                button { class: "pure-button pure-button-primary" onclick: move |_| scene.set(Scene::NewClientForm), "Add New" }
+                button { class: "pure-button" onclick: move |_| scene.set(Scene::Settings), "Settings" }
+            })
+        }
+        Scene::NewClientForm => {
+            let add_new = move |_| {
+                clients.write().push(Client {
+                    description: (*description).clone(),
+                    first_name: (*firstname).clone(),
+                    last_name: (*lastname).clone(),
+                });
+                description.set(String::new());
+                firstname.set(String::new());
+                lastname.set(String::new());
+            };
+            rsx!(cx, div { class: "crm"
+                h2 {"Add new client" margin_bottom: "10px" }
+                form { class: "pure-form"
+                    input { class: "new-client firstname" placeholder: "First name" value: "{firstname}"
+                        oninput: move |evt| firstname.set(evt.value())
+                    }
+                    input { class: "new-client lastname" placeholder: "Last name" value: "{lastname}"
+                        oninput: move |evt| lastname.set(evt.value())
+                    }
+                    textarea { class: "new-client description" placeholder: "Description" value: "{description}"
+                        oninput: move |evt| description.set(evt.value())
+                    }
+                }
+                button { class: "pure-button pure-button-primary", onclick: {add_new}, "Add New" }
+                button { class: "pure-button", onclick: move |_| scene.set(Scene::ClientsList), "Go Back" }
+            })
+        }
+        Scene::Settings => {
+            rsx!(cx, div {
+                h2 { "Settings" margin_bottom: "10px" }
+                button {
+                    background: "rgb(202, 60, 60)"
+                    class: "pure-button pure-button-primary"
+                    onclick: move |_| {
+                        clients.write().clear();
+                        scene.set(Scene::ClientsList);
+                    },
+                    "Remove all clients"
+                }
+                button {
+                    class: "pure-button pure-button-primary"
+                    onclick: move |_| scene.set(Scene::ClientsList),
+                    "Go Back"
+                }
+            })
+        }
+    };
+
+    rsx!(cx, body {
+        link {
+            rel: "stylesheet"
+            href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css"
+            integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5"
+            crossorigin: "anonymous"
+        }
+        margin_left: "35%"
+        h1 {"Dioxus CRM Example"}
+        {scene}
+    })
+};

+ 2 - 8
packages/desktop/examples/demo.rs

@@ -5,20 +5,14 @@ use dioxus_core_macro::*;
 use dioxus_html as dioxus_elements;
 
 fn main() {
-    std::thread::spawn(|| {
-        let mut vdom = VirtualDom::new(App);
-        let f = async_std::task::block_on(vdom.wait_for_work());
-    });
-    let a = 10;
-    // async_std::task::spawn_blocking(|| async move {
-    // });
+    dioxus_desktop::launch(App, |c| c).unwrap();
 }
 
 static App: FC<()> = |cx, props| {
-    //
     cx.render(rsx!(
         div {
             "hello world!"
         }
+        {(0..10).map(|f| rsx!( div {"abc {f}"}))}
     ))
 };

+ 11 - 0
packages/desktop/examples/tauri.rs

@@ -0,0 +1,11 @@
+fn main() {
+    tauri::AppBuilder::default().setup(move |app, name| {
+        //
+        let window = app.get_window();
+
+        let window = app.get_window();
+        tauri::spawn(|| async move {
+            //
+        });
+    });
+}

+ 10 - 4
packages/desktop/src/cfg.rs

@@ -4,30 +4,36 @@ use dioxus_core::DomEdit;
 use wry::{
     application::{
         error::OsError,
-        event_loop::EventLoopWindowTarget,
+        event_loop::{EventLoop, EventLoopWindowTarget},
         menu::MenuBar,
         window::{Fullscreen, Icon, Window, WindowBuilder},
     },
-    webview::{RpcRequest, RpcResponse},
+    webview::{RpcRequest, RpcResponse, WebView},
 };
 
 pub struct DesktopConfig<'a> {
     pub window: WindowBuilder,
-    pub(crate) manual_edits: Option<DomEdit<'a>>,
+    pub(crate) manual_edits: Option<Vec<DomEdit<'a>>>,
     pub(crate) pre_rendered: Option<String>,
+    pub(crate) event_handler: Option<Box<dyn Fn(&mut EventLoop<()>, &mut WebView)>>,
 }
 
-impl DesktopConfig<'_> {
+impl<'a> DesktopConfig<'a> {
     /// Initializes a new `WindowBuilder` with default values.
     #[inline]
     pub fn new() -> Self {
         Self {
+            event_handler: None,
             window: Default::default(),
             pre_rendered: None,
             manual_edits: None,
         }
     }
 
+    pub fn with_edits(&mut self, edits: Vec<DomEdit<'a>>) {
+        self.manual_edits = Some(edits);
+    }
+
     pub fn with_prerendered(&mut self, content: String) -> &mut Self {
         self.pre_rendered = Some(content);
         self

+ 86 - 55
packages/desktop/src/events.rs

@@ -22,69 +22,100 @@ pub fn trigger_from_serialized(val: serde_json::Value) -> UserEvent {
     let mut data: Vec<ImEvent> = serde_json::from_value(val).unwrap();
     let data = data.drain(..).next().unwrap();
 
-    let event = SyntheticEvent::MouseEvent(MouseEvent(Arc::new(WebviewMouseEvent)));
     let scope = ScopeId(data.scope as usize);
     let mounted_dom_id = Some(ElementId(data.mounted_dom_id as usize));
+
     UserEvent {
-        name: todo!(),
+        name: "click",
         event,
         scope,
         mounted_dom_id,
     }
 }
 
-#[derive(Debug)]
-struct WebviewMouseEvent;
-impl MouseEventInner for WebviewMouseEvent {
-    fn alt_key(&self) -> bool {
-        todo!()
-    }
-
-    fn button(&self) -> i16 {
-        todo!()
-    }
-
-    fn buttons(&self) -> u16 {
-        todo!()
-    }
-
-    fn client_x(&self) -> i32 {
-        todo!()
-    }
-
-    fn client_y(&self) -> i32 {
-        todo!()
-    }
-
-    fn ctrl_key(&self) -> bool {
-        todo!()
-    }
-
-    fn meta_key(&self) -> bool {
-        todo!()
-    }
-
-    fn page_x(&self) -> i32 {
-        todo!()
-    }
-
-    fn page_y(&self) -> i32 {
-        todo!()
-    }
-
-    fn screen_x(&self) -> i32 {
-        todo!()
-    }
-
-    fn screen_y(&self) -> i32 {
-        todo!()
-    }
-
-    fn shift_key(&self) -> bool {
-        todo!()
-    }
-
-    fn get_modifier_state(&self, key_code: &str) -> bool {
-        todo!()
+fn event_name_from_typ(typ: &str) -> &'static str {
+    match typ {
+        "copy" => "copy",
+        "cut" => "cut",
+        "paste" => "paste",
+        "compositionend" => "compositionend",
+        "compositionstart" => "compositionstart",
+        "compositionupdate" => "compositionupdate",
+        "keydown" => "keydown",
+        "keypress" => "keypress",
+        "keyup" => "keyup",
+        "focus" => "focus",
+        "blur" => "blur",
+        "change" => "change",
+        "input" => "input",
+        "invalid" => "invalid",
+        "reset" => "reset",
+        "submit" => "submit",
+        "click" => "click",
+        "contextmenu" => "contextmenu",
+        "doubleclick" => "doubleclick",
+        "drag" => "drag",
+        "dragend" => "dragend",
+        "dragenter" => "dragenter",
+        "dragexit" => "dragexit",
+        "dragleave" => "dragleave",
+        "dragover" => "dragover",
+        "dragstart" => "dragstart",
+        "drop" => "drop",
+        "mousedown" => "mousedown",
+        "mouseenter" => "mouseenter",
+        "mouseleave" => "mouseleave",
+        "mousemove" => "mousemove",
+        "mouseout" => "mouseout",
+        "mouseover" => "mouseover",
+        "mouseup" => "mouseup",
+        "pointerdown" => "pointerdown",
+        "pointermove" => "pointermove",
+        "pointerup" => "pointerup",
+        "pointercancel" => "pointercancel",
+        "gotpointercapture" => "gotpointercapture",
+        "lostpointercapture" => "lostpointercapture",
+        "pointerenter" => "pointerenter",
+        "pointerleave" => "pointerleave",
+        "pointerover" => "pointerover",
+        "pointerout" => "pointerout",
+        "select" => "select",
+        "touchcancel" => "touchcancel",
+        "touchend" => "touchend",
+        "touchmove" => "touchmove",
+        "touchstart" => "touchstart",
+        "scroll" => "scroll",
+        "wheel" => "wheel",
+        "animationstart" => "animationstart",
+        "animationend" => "animationend",
+        "animationiteration" => "animationiteration",
+        "transitionend" => "transitionend",
+        "abort" => "abort",
+        "canplay" => "canplay",
+        "canplaythrough" => "canplaythrough",
+        "durationchange" => "durationchange",
+        "emptied" => "emptied",
+        "encrypted" => "encrypted",
+        "ended" => "ended",
+        "error" => "error",
+        "loadeddata" => "loadeddata",
+        "loadedmetadata" => "loadedmetadata",
+        "loadstart" => "loadstart",
+        "pause" => "pause",
+        "play" => "play",
+        "playing" => "playing",
+        "progress" => "progress",
+        "ratechange" => "ratechange",
+        "seeked" => "seeked",
+        "seeking" => "seeking",
+        "stalled" => "stalled",
+        "suspend" => "suspend",
+        "timeupdate" => "timeupdate",
+        "volumechange" => "volumechange",
+        "waiting" => "waiting",
+        "toggle" => "toggle",
+        _ => {
+            panic!("unsupported event type")
+        }
     }
 }

+ 1 - 206
packages/desktop/src/index.html

@@ -9,212 +9,7 @@
     <div id="_dioxusroot">
     </div>
 </body>
-<script>
-    class Interpreter {
-        constructor(root) {
-            this.root = root;
-            this.stack = [root];
-            this.listeners = {
-                "onclick": {}
-            };
-            this.lastNodeWasText = false;
-            this.nodes = [root, root, root, root];
-        }
-
-        top() {
-            return this.stack[this.stack.length - 1];
-        }
-
-        pop() {
-            return this.stack.pop();
-        }
-
-        PushRoot(edit) {
-            const id = edit.id;
-            const node = this.nodes[id];
-            console.log("pushing root ", node, "with id", id);
-            this.stack.push(node);
-        }
-
-        PopRoot(edit) {
-            this.stack.pop();
-        }
-
-        AppendChildren(edit) {
-            let root = this.stack[this.stack.length - (edit.many + 1)];
-            for (let i = 0; i < edit.many; i++) {
-                console.log("popping ", i, edit.many);
-                let node = this.pop();
-                root.appendChild(node);
-            }
-        }
-
-        ReplaceWith(edit) {
-
-            let root = this.stack[this.stack.length - (edit.many + 1)];
-            let els = [];
-
-            for (let i = 0; i < edit.many; i++) {
-                els.push(this.pop());
-            }
-
-            root.replaceWith(...els);
-        }
-
-        Remove(edit) {
-            const node = this.stack.pop();
-            node.remove();
-        }
-
-        RemoveAllChildren(edit) {}
-
-        CreateTextNode(edit) {
-            const node = document.createTextNode(edit.text);
-            this.nodes[edit.id] = node;
-            this.stack.push(node);
-        }
-
-        CreateElement(edit) {
-            const tagName = edit.tag;
-            const el = document.createElement(tagName);
-            this.nodes[edit.id] = el;
-            console.log(`creating element: `, edit);
-            this.stack.push(el);
-        }
-
-        CreateElementNs(edit) {
-            const tagName = edit.tag;
-            console.log(`creating namespaced element: `, edit);
-            this.stack.push(document.createElementNS(edit.ns, edit.tag));
-        }
-
-        CreatePlaceholder(edit) {
-            const a = `this.stack.push(document.createElement(" pre"))`;
-            this.stack.push(document.createComment("vroot"));
-        }
-
-        NewEventListener(edit) {
-            const element_id = edit.element_id;
-            const event_name = edit.event_name;
-            const mounted_node_id = edit.mounted_node_id;
-            const scope = edit.scope;
-
-            const element = this.top();
-            element.setAttribute(`dioxus-event-${event_name}`, `${scope}.${mounted_node_id}`);
-
-            console.log("listener map is", this.listeners);
-            if (this.listeners[event_name] === undefined) {
-                console.log("adding listener!");
-                this.listeners[event_name] = "bla";
-                this.root.addEventListener(event_name, (event) => {
-                    const target = event.target;
-                    const type = event.type;
-                    const val = target.getAttribute(`dioxus-event-${event_name}`);
-                    const fields = val.split(".");
-                    const scope_id = parseInt(fields[0]);
-                    const real_id = parseInt(fields[1]);
-
-                    console.log(`parsed event with scope_id ${scope_id} and real_id ${real_id}`);
-
-                    rpc.call('user_event', {
-                        event: event_name,
-                        scope: scope_id,
-                        mounted_dom_id: real_id,
-                    }).then((reply) => {
-                        console.log(reply);
-                        this.stack.push(this.root);
-
-                        let edits = reply.edits;
-
-                        for (let x = 0; x < edits.length; x++) {
-                            let edit = edits[x];
-                            console.log(edit);
-
-                            let f = this[edit.type];
-                            f.call(this, edit);
-                        }
-
-                        console.log("initiated");
-                    }).catch((err) => {
-                        console.log("failed to initiate", err);
-                    });
-                });
-            }
-        }
-
-        RemoveEventListener(edit) {}
-
-        SetText(edit) {
-            this.top().textContent = edit.text;
-        }
-
-        SetAttribute(edit) {
-            const name = edit.field;
-            const value = edit.value;
-            const ns = edit.ns;
-            const node = this.top(this.stack);
-            if (ns == "style") {
-                node.style[name] = value;
-            } else if (ns !== undefined) {
-                node.setAttributeNS(ns, name, value);
-            } else {
-                node.setAttribute(name, value);
-            }
-            if (name === "value") {
-                node.value = value;
-            }
-            if (name === "checked") {
-                node.checked = true;
-            }
-            if (name === "selected") {
-                node.selected = true;
-            }
-        }
-        RemoveAttribute(edit) {
-            const name = edit.field;
-            const node = this.top(this.stack);
-            node.removeAttribute(name);
-
-            if (name === "value") {
-                node.value = null;
-            }
-            if (name === "checked") {
-                node.checked = false;
-            }
-            if (name === "selected") {
-                node.selected = false;
-            }
-        }
-    }
-
-
-
-
-    async function initialize() {
-        const reply = await rpc.call('initiate');
-        let root = window.document.getElementById("_dioxusroot");
-        const interpreter = new Interpreter(root);
-        console.log(reply);
-
-        let pre_rendered = reply.pre_rendered;
-        if (pre_rendered !== undefined) {
-            root.innerHTML = pre_rendered;
-        }
-
-        const edits = reply.edits;
-
-        for (let x = 0; x < edits.length; x++) {
-            let edit = edits[x];
-            console.log(edit);
-
-            let f = interpreter[edit.type];
-            f.call(interpreter, edit);
-        }
-
-        console.log("stack completed: ", interpreter.stack);
-    }
-    console.log("initializing...");
-    initialize();
+<script type="text/javascript" src="/index.js">
 </script>
 
 </html>

+ 216 - 0
packages/desktop/src/index.js

@@ -0,0 +1,216 @@
+
+class Interpreter {
+  constructor(root) {
+    this.root = root;
+    this.stack = [root];
+    this.listeners = {
+      "onclick": {}
+    };
+    this.lastNodeWasText = false;
+    this.nodes = [root, root, root, root];
+  }
+
+  top() {
+    return this.stack[this.stack.length - 1];
+  }
+
+  pop() {
+    return this.stack.pop();
+  }
+
+  PushRoot(edit) {
+    const id = edit.id;
+    const node = this.nodes[id];
+    console.log("pushing root ", node, "with id", id);
+    this.stack.push(node);
+  }
+
+  PopRoot(_edit) {
+    this.stack.pop();
+  }
+
+  AppendChildren(edit) {
+    let root = this.stack[this.stack.length - (1 + edit.many)];
+
+    let to_add = this.stack.splice(this.stack.length - edit.many);
+
+    for (let i = 0; i < edit.many; i++) {
+      root.appendChild(to_add[i]);
+    }
+  }
+
+  ReplaceWith(edit) {
+    console.log(edit);
+    let root = this.nodes[edit.root];
+    let els = this.stack.splice(this.stack.length - edit.m);
+
+    console.log(root);
+    console.log(els);
+
+
+    root.replaceWith(...els);
+  }
+
+  Remove(edit) {
+    let node = this.nodes[edit.element_id];
+    node.remove();
+  }
+
+  CreateTextNode(edit) {
+    const node = document.createTextNode(edit.text);
+    this.nodes[edit.id] = node;
+    this.stack.push(node);
+  }
+
+  CreateElement(edit) {
+    const tagName = edit.tag;
+    const el = document.createElement(tagName);
+    this.nodes[edit.id] = el;
+    this.stack.push(el);
+  }
+
+  CreateElementNs(edit) {
+    let el = document.createElementNS(edit.ns, edit.tag);
+    this.stack.push(el);
+    this.nodes[edit.id] = el;
+  }
+
+  CreatePlaceholder(edit) {
+    let el = document.createElement("pre");
+    // let el = document.createComment("vroot");
+    this.stack.push(el);
+    this.nodes[edit.id] = el;
+  }
+
+  RemoveEventListener(edit) { }
+
+  SetText(edit) {
+    this.top().textContent = edit.text;
+  }
+
+  SetAttribute(edit) {
+    const name = edit.field;
+    const value = edit.value;
+    const ns = edit.ns;
+    const node = this.top(this.stack);
+    if (ns == "style") {
+      node.style[name] = value;
+    } else if (ns !== undefined) {
+      node.setAttributeNS(ns, name, value);
+    } else {
+      node.setAttribute(name, value);
+    }
+    if (name === "value") {
+      node.value = value;
+    }
+    if (name === "checked") {
+      node.checked = true;
+    }
+    if (name === "selected") {
+      node.selected = true;
+    }
+  }
+  RemoveAttribute(edit) {
+    const name = edit.field;
+    const node = this.top(this.stack);
+    node.removeAttribute(name);
+
+    if (name === "value") {
+      node.value = null;
+    }
+    if (name === "checked") {
+      node.checked = false;
+    }
+    if (name === "selected") {
+      node.selected = false;
+    }
+  }
+
+  InsertAfter(edit) {
+    let old = this.nodes[edit.element_id];
+    let new_nodes = this.stack.splice(edit.many);
+    old.after(...new_nodes);
+  }
+
+  InsertBefore(edit) {
+    let old = this.nodes[edit.element_id];
+    let new_nodes = this.stack.splice(edit.many);
+    old.before(...new_nodes);
+  }
+
+  NewEventListener(edit) {
+    const event_name = edit.event_name;
+    const mounted_node_id = edit.mounted_node_id;
+    const scope = edit.scope;
+
+    const element = this.top();
+    element.setAttribute(`dioxus-event-${event_name}`, `${scope}.${mounted_node_id}`);
+
+    console.log("listener map is", this.listeners);
+    if (this.listeners[event_name] === undefined) {
+      console.log("adding listener!");
+      this.listeners[event_name] = "bla";
+      this.root.addEventListener(event_name, (event) => {
+        const target = event.target;
+        const val = target.getAttribute(`dioxus-event-${event_name}`);
+        const fields = val.split(".");
+        const scope_id = parseInt(fields[0]);
+        const real_id = parseInt(fields[1]);
+
+        console.log(`parsed event with scope_id ${scope_id} and real_id ${real_id}`);
+
+        rpc.call('user_event', {
+          event: event_name,
+          scope: scope_id,
+          mounted_dom_id: real_id,
+        }).then((reply) => {
+          console.log(reply);
+          this.stack.push(this.root);
+
+          let edits = reply.edits;
+
+          for (let x = 0; x < edits.length; x++) {
+            let edit = edits[x];
+            console.log(edit);
+
+            let f = this[edit.type];
+            f.call(this, edit);
+          }
+
+          console.log("initiated");
+        }).catch((err) => {
+          console.log("failed to initiate", err);
+        });
+      });
+    }
+  }
+}
+
+async function initialize() {
+  const reply = await rpc.call('initiate');
+  let root = window.document.getElementById("_dioxusroot");
+  const interpreter = new Interpreter(root);
+  console.log(reply);
+
+  let pre_rendered = reply.pre_rendered;
+  if (pre_rendered !== undefined) {
+    root.innerHTML = pre_rendered;
+  }
+
+  const edits = reply.edits;
+
+  apply_edits(edits, interpreter);
+}
+
+function apply_edits(edits, interpreter) {
+  for (let x = 0; x < edits.length; x++) {
+    let edit = edits[x];
+    console.log(edit);
+    let f = interpreter[edit.type];
+    f.call(interpreter, edit);
+  }
+
+  console.log("stack completed: ", interpreter.stack);
+}
+
+initialize();

+ 159 - 111
packages/desktop/src/lib.rs

@@ -4,20 +4,28 @@
 //!
 
 use std::borrow::BorrowMut;
+use std::cell::RefCell;
 use std::ops::{Deref, DerefMut};
+use std::rc::Rc;
 use std::sync::atomic::AtomicBool;
 use std::sync::mpsc::channel;
 use std::sync::{Arc, RwLock};
 
 use cfg::DesktopConfig;
+use dioxus_core::scheduler::SchedulerMsg;
 use dioxus_core::*;
+// use futures_channel::mpsc::UnboundedSender;
 use serde::{Deserialize, Serialize};
+
+mod logging;
+
+pub use logging::set_up_logging;
 pub use wry;
 
 use wry::application::event::{Event, WindowEvent};
-use wry::application::event_loop::{ControlFlow, EventLoop};
+use wry::application::event_loop::{self, ControlFlow, EventLoop};
 use wry::application::window::Fullscreen;
-use wry::webview::WebViewBuilder;
+use wry::webview::{WebView, WebViewBuilder};
 use wry::{
     application::window::{Window, WindowBuilder},
     webview::{RpcRequest, RpcResponse},
@@ -45,10 +53,16 @@ pub fn launch_with_props<P: Properties + 'static + Send + Sync>(
     run(root, props, builder)
 }
 
+#[derive(Serialize)]
 enum RpcEvent<'a> {
     Initialize { edits: Vec<DomEdit<'a>> },
 }
 
+enum BridgeEvent {
+    Initialize(serde_json::Value),
+    Update(serde_json::Value),
+}
+
 #[derive(Serialize)]
 struct Response<'a> {
     pre_rendered: Option<String>,
@@ -60,72 +74,175 @@ pub fn run<T: Properties + 'static + Send + Sync>(
     props: T,
     user_builder: impl for<'a, 'b> FnOnce(&'b mut DesktopConfig<'a>) -> &'b mut DesktopConfig<'a>,
 ) -> anyhow::Result<()> {
-    run_with_edits(root, props, user_builder, None)
-}
-
-pub fn run_with_edits<
-    F: for<'a, 'b> FnOnce(&'a mut DesktopConfig<'b>) -> &'a mut DesktopConfig<'b>,
-    T: Properties + 'static + Send + Sync,
->(
-    root: FC<T>,
-    props: T,
-    user_builder: F,
-    redits: Option<Vec<DomEdit<'static>>>,
-) -> anyhow::Result<()> {
-    /*
-
-
-    */
-
     let mut cfg = DesktopConfig::new();
     user_builder(&mut cfg);
     let DesktopConfig {
         window,
         manual_edits,
         pre_rendered,
+        ..
     } = cfg;
 
     let event_loop = EventLoop::new();
     let window = window.build(&event_loop)?;
 
-    let (event_tx, mut event_rx) = tokio::sync::mpsc::unbounded_channel::<String>();
+    let (event_tx, mut event_rx) = tokio::sync::mpsc::unbounded_channel();
+
+    let sender = launch_vdom_with_tokio(root, props, event_tx.clone());
+
+    let locked_receiver = Rc::new(RefCell::new(event_rx));
+
+    let webview = WebViewBuilder::new(window)?
+        .with_url("wry://src/index.html")?
+        .with_rpc_handler(move |_window: &Window, mut req: RpcRequest| {
+            match req.method.as_str() {
+                "initiate" => {
+                    //
+                    let mut rx = (*locked_receiver).borrow_mut();
+
+                    match rx.try_recv() {
+                        Ok(BridgeEvent::Initialize(edits)) => {
+                            Some(RpcResponse::new_result(req.id.take(), Some(edits)))
+                        }
+                        _ => None,
+                    }
+                }
+                "user_event" => {
+                    //
+                    let data = req.params.unwrap();
+                    log::debug!("Data: {:#?}", data);
+                    let event = events::trigger_from_serialized(data);
+                    sender.unbounded_send(SchedulerMsg::UiEvent(event)).unwrap();
+
+                    let mut rx = (*locked_receiver).borrow_mut();
+
+                    match rx.blocking_recv() {
+                        Some(BridgeEvent::Update(edits)) => {
+                            log::info!("Passing response back");
+                            Some(RpcResponse::new_result(req.id.take(), Some(edits)))
+                        }
+                        None => {
+                            log::error!("Sender half is gone");
+                            None
+                        }
+                        _ => {
+                            log::error!("No update event received");
+                            None
+                        }
+                    }
+                }
+                _ => todo!("this message failed"),
+            }
+        })
+        // this isn't quite portable unfortunately :(
+        // todo: figure out a way to allow us to create the index.html with the index.js file separately
+        // it's a bit easier to hack with
+        .with_custom_protocol("wry".into(), move |request| {
+            use std::fs::{canonicalize, read};
+            use wry::http::ResponseBuilder;
+            // Remove url scheme
+            let path = request.uri().replace("wry://", "");
+            // Read the file content from file path
+            let content = read(canonicalize(&path)?)?;
+
+            // Return asset contents and mime types based on file extentions
+            // If you don't want to do this manually, there are some crates for you.
+            // Such as `infer` and `mime_guess`.
+            let (data, meta) = if path.ends_with(".html") {
+                (content, "text/html")
+            } else if path.ends_with(".js") {
+                (content, "text/javascript")
+            } else if path.ends_with(".png") {
+                (content, "image/png")
+            } else {
+                unimplemented!();
+            };
+
+            ResponseBuilder::new().mimetype(meta).body(data)
+        })
+        .build()?;
+
+    run_event_loop(event_loop, webview, event_tx);
 
+    Ok(())
+}
+
+pub fn start<P: 'static + Send>(
+    root: FC<P>,
+    config_builder: impl for<'a, 'b> FnOnce(&'b mut DesktopConfig<'a>) -> &'b mut DesktopConfig<'a>,
+) -> ((), ()) {
+    //
+    ((), ())
+}
+
+// Create a new tokio runtime on a dedicated thread and then launch the apps VirtualDom.
+fn launch_vdom_with_tokio<C: Send + 'static>(
+    root: FC<C>,
+    props: C,
+    event_tx: tokio::sync::mpsc::UnboundedSender<BridgeEvent>,
+) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> {
     // Spawn the virtualdom onto its own thread
     // if it wants to spawn multithreaded tasks, it can use the executor directly
+
+    let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>();
+
+    let sender_2 = sender.clone();
     std::thread::spawn(move || {
-        //
-        let runtime = tokio::runtime::Builder::new_current_thread()
+        // We create the runtim as multithreaded, so you can still "spawn" onto multiple threads
+        let runtime = tokio::runtime::Builder::new_multi_thread()
             .enable_all()
             .build()
             .unwrap();
 
         runtime.block_on(async move {
-            let mut vir = VirtualDom::new_with_props(root, props);
-            let channel = vir.get_event_sender();
+            let mut vir = VirtualDom::new_with_props_and_scheduler(root, props, sender, receiver);
+            let _ = vir.get_event_sender();
+
+            let edits = vir.rebuild();
+
+            #[derive(Serialize)]
+            struct Evt<'a> {
+                edits: Vec<DomEdit<'a>>,
+            }
+
+            // let msg = RpcEvent::Initialize { edits: edits.edits };
+            let edit_string = serde_json::to_value(Evt { edits: edits.edits }).unwrap();
+            match event_tx.send(BridgeEvent::Initialize(edit_string)) {
+                Ok(_) => {}
+                Err(_) => {}
+            }
+
             loop {
                 vir.wait_for_work().await;
-                let edits = vir.run_with_deadline(|| false);
-                let edit_string = serde_json::to_string(&edits[0].edits).unwrap();
-                event_tx.send(edit_string).unwrap();
-            }
-        })
-    });
+                log::info!("{}", vir);
 
-    let dioxus_requsted = Arc::new(AtomicBool::new(false));
+                let mut muts = vir.run_with_deadline(|| false);
+                log::info!("muts {:#?}", muts);
+                while let Some(edit) = muts.pop() {
+                    let edit_string = serde_json::to_value(Evt { edits: edit.edits }).unwrap();
+                    match event_tx.send(BridgeEvent::Update(edit_string)) {
+                        Ok(_) => {}
+                        Err(er) => {
+                            log::error!("Sending should not fail {}", er);
+                        }
+                    }
+                }
 
-    let webview = WebViewBuilder::new(window)?
-        .with_url(&format!("data:text/html,{}", HTML_CONTENT))?
-        .with_rpc_handler(move |_window: &Window, mut req: RpcRequest| {
-            match req.method.as_str() {
-                "initiate" => {}
-                "user_event" => {}
-                _ => todo!("this message failed"),
+                log::info!("mutations sent on channel");
             }
-            todo!()
         })
-        .build()?;
+    });
+
+    sender_2
+}
 
-    event_loop.run(move |event, _, control_flow| {
+fn run_event_loop(
+    event_loop: EventLoop<()>,
+    webview: WebView,
+    event_tx: tokio::sync::mpsc::UnboundedSender<BridgeEvent>,
+) {
+    let _ = event_tx.clone();
+    event_loop.run(move |event, target, control_flow| {
         *control_flow = ControlFlow::Wait;
 
         match event {
@@ -146,74 +263,5 @@ pub fn run_with_edits<
 
             _ => {}
         }
-    });
-
-    Ok(())
+    })
 }
-
-// let edits = if let Some(edits) = &redits {
-//     serde_json::to_value(edits).unwrap()
-// } else {
-//     let mut lock = vdom.write().unwrap();
-//     // let mut reg_lock = registry.write().unwrap();
-//     // Create the thin wrapper around the registry to collect the edits into
-//     let mut real = dom::WebviewDom::new();
-//     let pre = pre_rendered.clone();
-//     let response = match pre {
-//         Some(content) => {
-//             lock.rebuild_in_place().unwrap();
-//             Response {
-//                 edits: Vec::new(),
-//                 pre_rendered: Some(content),
-//             }
-//         }
-//         None => {
-//             //
-//             let edits = {
-//                 // let mut edits = Vec::new();
-//                 todo!()
-//                 // lock.rebuild(&mut real, &mut edits).unwrap();
-//                 // edits
-//             };
-//             Response {
-//                 edits,
-//                 pre_rendered: None,
-//             }
-//         }
-//     };
-//     serde_json::to_value(&response).unwrap()
-// };
-// // Return the edits into the webview runtime
-// Some(RpcResponse::new_result(req.id.take(), Some(edits)))
-
-// log::debug!("User event received");
-// // let registry = registry.clone();
-// let vdom = vdom.clone();
-// let response = async_std::task::block_on(async move {
-//     let mut lock = vdom.write().unwrap();
-//     // let mut reg_lock = registry.write().unwrap();
-//     // a deserialized event
-//     let data = req.params.unwrap();
-//     log::debug!("Data: {:#?}", data);
-//     let event = trigger_from_serialized(data);
-//     // lock.queue_event(event);
-//     // Create the thin wrapper around the registry to collect the edits into
-//     let mut real = dom::WebviewDom::new();
-//     // Serialize the edit stream
-//     //
-//     let mut edits = Vec::new();
-//     // lock.run(&mut real, &mut edits)
-//     //     .await
-//     //     .expect("failed to progress");
-//     let response = Response {
-//         edits,
-//         pre_rendered: None,
-//     };
-//     let response = serde_json::to_value(&response).unwrap();
-//     // Give back the registry into its slot
-//     // *reg_lock = Some(real.consume());
-//     // Return the edits into the webview runtime
-//     Some(RpcResponse::new_result(req.id.take(), Some(response)))
-// });
-// response
-// // spawn a task to clean up the garbage

+ 51 - 0
packages/desktop/src/logging.rs

@@ -0,0 +1,51 @@
+pub fn set_up_logging(enabled: bool) {
+    use fern::colors::{Color, ColoredLevelConfig};
+
+    if !enabled {
+        return;
+    }
+
+    // configure colors for the whole line
+    let colors_line = ColoredLevelConfig::new()
+        .error(Color::Red)
+        .warn(Color::Yellow)
+        // we actually don't need to specify the color for debug and info, they are white by default
+        .info(Color::White)
+        .debug(Color::White)
+        // depending on the terminals color scheme, this is the same as the background color
+        .trace(Color::BrightBlack);
+
+    // configure colors for the name of the level.
+    // since almost all of them are the same as the color for the whole line, we
+    // just clone `colors_line` and overwrite our changes
+    let colors_level = colors_line.clone().info(Color::Green);
+    // here we set up our fern Dispatch
+
+    // when running tests in batch, the logger is re-used, so ignore the logger error
+    let _ = fern::Dispatch::new()
+        .format(move |out, message, record| {
+            out.finish(format_args!(
+                "{color_line}[{level}{color_line}] {message}\x1B[0m",
+                color_line = format_args!(
+                    "\x1B[{}m",
+                    colors_line.get_color(&record.level()).to_fg_str()
+                ),
+                level = colors_level.color(record.level()),
+                message = message,
+            ));
+        })
+        // set the default log level. to filter out verbose log messages from dependencies, set
+        // this to Warn and overwrite the log level for your crate.
+        .level(log::LevelFilter::Debug)
+        // .level(log::LevelFilter::Warn)
+        // change log levels for individual modules. Note: This looks for the record's target
+        // field which defaults to the module path but can be overwritten with the `target`
+        // parameter:
+        // `info!(target="special_target", "This log message is about special_target");`
+        // .level_for("dioxus", log::LevelFilter::Debug)
+        // .level_for("dioxus", log::LevelFilter::Info)
+        // .level_for("pretty_colored", log::LevelFilter::Trace)
+        // output to stdout
+        .chain(std::io::stdout())
+        .apply();
+}

+ 1 - 1
packages/mobile/Cargo.toml

@@ -13,7 +13,7 @@ dioxus-core = { path = "../core", version = "0.1.2" }
 log = "0.4.14"
 serde = "1.0.126"
 serde_json = "1.0.64"
-wry = "0.11.0"
+wry = "0.12.2"
 
 
 [target.'cfg(target_os = "android")'.dependencies]

+ 1 - 1
packages/web/Cargo.toml

@@ -72,9 +72,9 @@ crate-type = ["cdylib", "rlib"]
 im-rc = "15.0.0"
 separator = "0.4.1"
 uuid = { version = "0.8.2", features = ["v4", "wasm-bindgen"] }
-dioxus-hooks = { path = "../hooks" }
 serde = { version = "1.0.126", features = ["derive"] }
 reqwest = { version = "0.11", features = ["json"] }
+dioxus-hooks = { path = "../hooks" }
 dioxus-core-macro = { path = "../core-macro" }
 # rand = { version="0.8.4", features=["small_rng"] }
 # surf = { version = "2.3.1", default-features = false, features = [

+ 2 - 2
packages/web/examples/blah.rs

@@ -44,9 +44,9 @@ static App: FC<()> = |cx, props| {
                         "dynamic subtree {state}"
                     }
                     div {
-                        button { onclick: move |_| state+=1, "incr" }
+                        button { onclick: move |e| state+=1, "incr" }
                         br {}
-                        button { onclick: move |_| state-=1, "decr" }
+                        button { onclick: move |e| state-=1, "decr" }
                     }
                 }
             }

+ 180 - 59
packages/web/src/dom.rs

@@ -8,7 +8,7 @@
 //! - Partial delegation?>
 
 use dioxus_core::{
-    events::{SyntheticEvent, UserEvent},
+    events::{DioxusEvent, KeyCode, SyntheticEvent, UserEvent},
     mutations::NodeRefMutation,
     scheduler::SchedulerMsg,
     DomEdit, ElementId, ScopeId,
@@ -110,7 +110,6 @@ impl WebsysDom {
                 DomEdit::AppendChildren { many } => self.append_children(many),
                 DomEdit::ReplaceWith { m, root } => self.replace_with(m, root),
                 DomEdit::Remove { root } => self.remove(root),
-                DomEdit::RemoveAllChildren => self.remove_all_children(),
                 DomEdit::CreateTextNode { text, id } => self.create_text_node(text, id),
                 DomEdit::CreateElement { tag, id } => self.create_element(tag, None, id),
                 DomEdit::CreateElementNs { tag, id, ns } => self.create_element(tag, Some(ns), id),
@@ -208,10 +207,6 @@ impl WebsysDom {
         }
     }
 
-    fn remove_all_children(&mut self) {
-        todo!()
-    }
-
     fn create_placeholder(&mut self, id: u64) {
         self.create_element("pre", None, id);
         self.set_attribute("hidden", "", None);
@@ -464,87 +459,213 @@ impl Stack {
     }
 }
 
+pub struct DioxusWebsysEvent(web_sys::Event);
+unsafe impl Send for DioxusWebsysEvent {}
+unsafe impl Sync for DioxusWebsysEvent {}
+
+// trait MyTrait {}
+// impl MyTrait for web_sys::Event {}
+
 // todo: some of these events are being casted to the wrong event type.
 // We need tests that simulate clicks/etc and make sure every event type works.
 fn virtual_event_from_websys_event(event: web_sys::Event) -> SyntheticEvent {
     use crate::events::*;
     use dioxus_core::events::on::*;
     match event.type_().as_str() {
-        "copy" | "cut" | "paste" => {
-            SyntheticEvent::ClipboardEvent(ClipboardEvent(Arc::new(WebsysClipboardEvent(event))))
-        }
+        "copy" | "cut" | "paste" => SyntheticEvent::ClipboardEvent(ClipboardEvent(
+            DioxusEvent::new(ClipboardEventInner(), DioxusWebsysEvent(event)),
+        )),
         "compositionend" | "compositionstart" | "compositionupdate" => {
-            let evt: web_sys::CompositionEvent = event.dyn_into().unwrap();
-            SyntheticEvent::CompositionEvent(CompositionEvent(Arc::new(WebsysCompositionEvent(
-                evt,
-            ))))
+            let evt: &web_sys::CompositionEvent = event.dyn_ref().unwrap();
+            SyntheticEvent::CompositionEvent(CompositionEvent(DioxusEvent::new(
+                CompositionEventInner {
+                    data: evt.data().unwrap_or_default(),
+                },
+                DioxusWebsysEvent(event),
+            )))
         }
         "keydown" | "keypress" | "keyup" => {
-            let evt: web_sys::KeyboardEvent = event.dyn_into().unwrap();
-            SyntheticEvent::KeyboardEvent(KeyboardEvent(Arc::new(WebsysKeyboardEvent(evt))))
-        }
-        "focus" | "blur" => {
-            let evt: web_sys::FocusEvent = event.dyn_into().unwrap();
-            SyntheticEvent::FocusEvent(FocusEvent(Arc::new(WebsysFocusEvent(evt))))
-        }
-        "change" => {
-            let evt = event.dyn_into().unwrap();
-            SyntheticEvent::GenericEvent(GenericEvent(Arc::new(WebsysGenericUiEvent(evt))))
-        }
+            let evt: &web_sys::KeyboardEvent = event.dyn_ref().unwrap();
+            SyntheticEvent::KeyboardEvent(KeyboardEvent(DioxusEvent::new(
+                KeyboardEventInner {
+                    alt_key: evt.alt_key(),
+                    char_code: evt.char_code(),
+                    key: evt.key(),
+                    key_code: KeyCode::from_raw_code(evt.key_code() as u8),
+                    ctrl_key: evt.ctrl_key(),
+                    locale: "not implemented".to_string(),
+                    location: evt.location() as usize,
+                    meta_key: evt.meta_key(),
+                    repeat: evt.repeat(),
+                    shift_key: evt.shift_key(),
+                    which: evt.which() as usize,
+                },
+                DioxusWebsysEvent(event),
+            )))
+        }
+        "focus" | "blur" => SyntheticEvent::FocusEvent(FocusEvent(DioxusEvent::new(
+            FocusEventInner {},
+            DioxusWebsysEvent(event),
+        ))),
+        "change" => SyntheticEvent::GenericEvent(DioxusEvent::new((), DioxusWebsysEvent(event))),
+
+        // 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
         "input" | "invalid" | "reset" | "submit" => {
-            let evt: web_sys::Event = event.dyn_into().unwrap();
-            SyntheticEvent::FormEvent(FormEvent(Arc::new(WebsysFormEvent(evt))))
+            let evt: &web_sys::Event = event.dyn_ref().unwrap();
+
+            let target: web_sys::EventTarget = evt.target().unwrap();
+            let value: String = (&target)
+                .dyn_ref()
+                .map(|input: &web_sys::HtmlInputElement| input.value())
+                .or_else(|| {
+                    target
+                        .dyn_ref()
+                        .map(|input: &web_sys::HtmlTextAreaElement| input.value())
+                })
+                // select elements are NOT input events - because - why woudn't they be??
+                .or_else(|| {
+                    target
+                        .dyn_ref()
+                        .map(|input: &web_sys::HtmlSelectElement| input.value())
+                })
+                .or_else(|| {
+                    target
+                        .dyn_ref::<web_sys::HtmlElement>()
+                        .unwrap()
+                        .text_content()
+                })
+                .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener");
+
+            SyntheticEvent::FormEvent(FormEvent(DioxusEvent::new(
+                FormEventInner { value },
+                DioxusWebsysEvent(event),
+            )))
         }
         "click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
         | "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
         | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
-            let evt: web_sys::MouseEvent = event.dyn_into().unwrap();
-            SyntheticEvent::MouseEvent(MouseEvent(Arc::new(WebsysMouseEvent(evt))))
+            let evt: &web_sys::MouseEvent = event.dyn_ref().unwrap();
+            SyntheticEvent::MouseEvent(MouseEvent(DioxusEvent::new(
+                MouseEventInner {
+                    alt_key: evt.alt_key(),
+                    button: evt.button(),
+                    buttons: evt.buttons(),
+                    client_x: evt.client_x(),
+                    client_y: evt.client_y(),
+                    ctrl_key: evt.ctrl_key(),
+                    meta_key: evt.meta_key(),
+                    screen_x: evt.screen_x(),
+                    screen_y: evt.screen_y(),
+                    shift_key: evt.shift_key(),
+                    page_x: evt.page_x(),
+                    page_y: evt.page_y(),
+                },
+                DioxusWebsysEvent(event),
+            )))
         }
         "pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
         | "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
-            let evt: web_sys::PointerEvent = event.dyn_into().unwrap();
-            SyntheticEvent::PointerEvent(PointerEvent(Arc::new(WebsysPointerEvent(evt))))
-        }
-        "select" => {
-            let evt: web_sys::UiEvent = event.dyn_into().unwrap();
-            SyntheticEvent::SelectionEvent(SelectionEvent(Arc::new(WebsysGenericUiEvent(evt))))
-        }
+            let evt: &web_sys::PointerEvent = event.dyn_ref().unwrap();
+            SyntheticEvent::PointerEvent(PointerEvent(DioxusEvent::new(
+                PointerEventInner {
+                    alt_key: evt.alt_key(),
+                    button: evt.button(),
+                    buttons: evt.buttons(),
+                    client_x: evt.client_x(),
+                    client_y: evt.client_y(),
+                    ctrl_key: evt.ctrl_key(),
+                    meta_key: evt.meta_key(),
+                    page_x: evt.page_x(),
+                    page_y: evt.page_y(),
+                    screen_x: evt.screen_x(),
+                    screen_y: evt.screen_y(),
+                    shift_key: evt.shift_key(),
+                    pointer_id: evt.pointer_id(),
+                    width: evt.width(),
+                    height: evt.height(),
+                    pressure: evt.pressure(),
+                    tangential_pressure: evt.tangential_pressure(),
+                    tilt_x: evt.tilt_x(),
+                    tilt_y: evt.tilt_y(),
+                    twist: evt.twist(),
+                    pointer_type: evt.pointer_type(),
+                    is_primary: evt.is_primary(),
+                    // get_modifier_state: evt.get_modifier_state(),
+                },
+                DioxusWebsysEvent(event),
+            )))
+        }
+        "select" => SyntheticEvent::SelectionEvent(SelectionEvent(DioxusEvent::new(
+            SelectionEventInner {},
+            DioxusWebsysEvent(event),
+        ))),
+
         "touchcancel" | "touchend" | "touchmove" | "touchstart" => {
-            let evt: web_sys::TouchEvent = event.dyn_into().unwrap();
-            SyntheticEvent::TouchEvent(TouchEvent(Arc::new(WebsysTouchEvent(evt))))
-        }
-        "scroll" => {
-            let evt: web_sys::UiEvent = event.dyn_into().unwrap();
-            SyntheticEvent::GenericEvent(GenericEvent(Arc::new(WebsysGenericUiEvent(evt))))
+            let evt: &web_sys::TouchEvent = event.dyn_ref().unwrap();
+            SyntheticEvent::TouchEvent(TouchEvent(DioxusEvent::new(
+                TouchEventInner {
+                    alt_key: evt.alt_key(),
+                    ctrl_key: evt.ctrl_key(),
+                    meta_key: evt.meta_key(),
+                    shift_key: evt.shift_key(),
+                },
+                DioxusWebsysEvent(event),
+            )))
         }
+
+        "scroll" => SyntheticEvent::GenericEvent(DioxusEvent::new((), DioxusWebsysEvent(event))),
+
         "wheel" => {
-            let evt: web_sys::WheelEvent = event.dyn_into().unwrap();
-            SyntheticEvent::WheelEvent(WheelEvent(Arc::new(WebsysWheelEvent(evt))))
+            let evt: &web_sys::WheelEvent = event.dyn_ref().unwrap();
+            SyntheticEvent::WheelEvent(WheelEvent(DioxusEvent::new(
+                WheelEventInner {
+                    delta_x: evt.delta_x(),
+                    delta_y: evt.delta_y(),
+                    delta_z: evt.delta_z(),
+                    delta_mode: evt.delta_mode(),
+                },
+                DioxusWebsysEvent(event),
+            )))
         }
+
         "animationstart" | "animationend" | "animationiteration" => {
-            let evt: web_sys::AnimationEvent = event.dyn_into().unwrap();
-            SyntheticEvent::AnimationEvent(AnimationEvent(Arc::new(WebsysAnimationEvent(evt))))
+            let evt: &web_sys::AnimationEvent = event.dyn_ref().unwrap();
+            SyntheticEvent::AnimationEvent(AnimationEvent(DioxusEvent::new(
+                AnimationEventInner {
+                    elapsed_time: evt.elapsed_time(),
+                    animation_name: evt.animation_name(),
+                    pseudo_element: evt.pseudo_element(),
+                },
+                DioxusWebsysEvent(event),
+            )))
         }
+
         "transitionend" => {
-            let evt: web_sys::TransitionEvent = event.dyn_into().unwrap();
-            SyntheticEvent::TransitionEvent(TransitionEvent(Arc::new(WebsysTransitionEvent(evt))))
+            let evt: &web_sys::TransitionEvent = event.dyn_ref().unwrap();
+            SyntheticEvent::TransitionEvent(TransitionEvent(DioxusEvent::new(
+                TransitionEventInner {
+                    elapsed_time: evt.elapsed_time(),
+                    property_name: evt.property_name(),
+                    pseudo_element: evt.pseudo_element(),
+                },
+                DioxusWebsysEvent(event),
+            )))
         }
+
         "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
         | "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
         | "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
-        | "timeupdate" | "volumechange" | "waiting" => {
-            let evt: web_sys::UiEvent = event.dyn_into().unwrap();
-            SyntheticEvent::MediaEvent(MediaEvent(Arc::new(WebsysMediaEvent(evt))))
-        }
-        "toggle" => {
-            let evt: web_sys::UiEvent = event.dyn_into().unwrap();
-            SyntheticEvent::ToggleEvent(ToggleEvent(Arc::new(WebsysToggleEvent(evt))))
-        }
-        _ => {
-            let evt: web_sys::UiEvent = event.dyn_into().unwrap();
-            SyntheticEvent::GenericEvent(GenericEvent(Arc::new(WebsysGenericUiEvent(evt))))
-        }
+        | "timeupdate" | "volumechange" | "waiting" => SyntheticEvent::MediaEvent(MediaEvent(
+            DioxusEvent::new(MediaEventInner {}, DioxusWebsysEvent(event)),
+        )),
+
+        "toggle" => SyntheticEvent::ToggleEvent(ToggleEvent(DioxusEvent::new(
+            ToggleEventInner {},
+            DioxusWebsysEvent(event),
+        ))),
+
+        _ => SyntheticEvent::GenericEvent(DioxusEvent::new((), DioxusWebsysEvent(event))),
     }
 }
 

+ 0 - 477
packages/web/src/events.rs

@@ -3,480 +3,3 @@
 use dioxus_core::events::on::*;
 use wasm_bindgen::JsCast;
 use web_sys::{Event, UiEvent};
-
-/// All events implement the generic event type - they're all `UI events`
-trait WebsysGenericEvent {
-    fn as_ui_event(&self) -> &UiEvent;
-}
-
-impl GenericEventInner for &dyn WebsysGenericEvent {
-    /// On WebSys, this returns an &UiEvent which can be casted via dyn_ref into the correct sub type.
-    fn raw_event(&self) -> &dyn std::any::Any {
-        self.as_ui_event()
-    }
-
-    fn bubbles(&self) -> bool {
-        self.as_ui_event().bubbles()
-    }
-
-    fn cancel_bubble(&self) {
-        self.as_ui_event().cancel_bubble();
-    }
-
-    fn cancelable(&self) -> bool {
-        self.as_ui_event().cancelable()
-    }
-
-    fn composed(&self) -> bool {
-        self.as_ui_event().composed()
-    }
-
-    fn current_target(&self) {
-        if cfg!(debug_assertions) {
-            todo!("Current target does not return anything useful.\nPlease try casting the event directly.");
-        }
-        // self.as_ui_event().current_target();
-    }
-
-    fn default_prevented(&self) -> bool {
-        self.as_ui_event().default_prevented()
-    }
-
-    fn event_phase(&self) -> u16 {
-        self.as_ui_event().event_phase()
-    }
-
-    fn is_trusted(&self) -> bool {
-        self.as_ui_event().is_trusted()
-    }
-
-    fn prevent_default(&self) {
-        self.as_ui_event().prevent_default()
-    }
-
-    fn stop_immediate_propagation(&self) {
-        self.as_ui_event().stop_immediate_propagation()
-    }
-
-    fn stop_propagation(&self) {
-        self.as_ui_event().stop_propagation()
-    }
-
-    fn target(&self) {
-        todo!()
-    }
-
-    fn time_stamp(&self) -> f64 {
-        self.as_ui_event().time_stamp()
-    }
-}
-
-macro_rules! implement_generic_event {
-    (
-        $($event:ident),*
-    ) => {
-        $(
-            impl WebsysGenericEvent for $event {
-                fn as_ui_event(&self) -> &UiEvent {
-                    self.0.dyn_ref().unwrap()
-                }
-            }
-        )*
-    };
-}
-
-implement_generic_event! {
-    WebsysClipboardEvent,
-    WebsysCompositionEvent,
-    WebsysKeyboardEvent,
-    WebsysGenericUiEvent,
-    WebsysFocusEvent,
-    WebsysFormEvent,
-    WebsysMouseEvent,
-    WebsysPointerEvent,
-    WebsysWheelEvent,
-    WebsysAnimationEvent,
-    WebsysTransitionEvent,
-    WebsysTouchEvent,
-    WebsysMediaEvent,
-    WebsysToggleEvent
-}
-
-// unfortunately, currently experimental, and web_sys needs to be configured to use it :>(
-pub struct WebsysClipboardEvent(pub Event);
-
-impl ClipboardEventInner for WebsysClipboardEvent {}
-
-pub struct WebsysCompositionEvent(pub web_sys::CompositionEvent);
-
-impl CompositionEventInner for WebsysCompositionEvent {
-    fn data(&self) -> String {
-        self.0.data().unwrap_or_else(|| String::new())
-    }
-}
-
-pub struct WebsysKeyboardEvent(pub web_sys::KeyboardEvent);
-impl KeyboardEventInner for WebsysKeyboardEvent {
-    fn alt_key(&self) -> bool {
-        self.0.alt_key()
-    }
-    fn char_code(&self) -> u32 {
-        self.0.char_code()
-    }
-    fn key(&self) -> String {
-        self.0.key()
-    }
-
-    fn key_code(&self) -> KeyCode {
-        KeyCode::from_raw_code(self.0.key_code() as u8)
-    }
-
-    fn ctrl_key(&self) -> bool {
-        self.0.ctrl_key()
-    }
-
-    fn get_modifier_state(&self, key_code: &str) -> bool {
-        self.0.get_modifier_state(key_code)
-    }
-
-    fn locale(&self) -> String {
-        if cfg!(debug_assertions) {
-            todo!("Locale is currently not supported. :(")
-        } else {
-            String::from("en-US")
-        }
-    }
-
-    fn location(&self) -> usize {
-        self.0.location() as usize
-    }
-
-    fn meta_key(&self) -> bool {
-        self.0.meta_key()
-    }
-
-    fn repeat(&self) -> bool {
-        self.0.repeat()
-    }
-
-    fn shift_key(&self) -> bool {
-        self.0.shift_key()
-    }
-
-    fn which(&self) -> usize {
-        self.0.which() as usize
-    }
-}
-
-pub struct WebsysGenericUiEvent(pub UiEvent);
-impl GenericEventInner for WebsysGenericUiEvent {
-    fn raw_event(&self) -> &dyn std::any::Any {
-        // self.0.raw_event()
-        todo!()
-    }
-
-    fn bubbles(&self) -> bool {
-        self.0.bubbles()
-    }
-
-    fn cancel_bubble(&self) {
-        self.0.cancel_bubble();
-    }
-
-    fn cancelable(&self) -> bool {
-        self.0.cancelable()
-    }
-
-    fn composed(&self) -> bool {
-        self.0.composed()
-    }
-
-    fn current_target(&self) {
-        // self.0.current_target()
-    }
-
-    fn default_prevented(&self) -> bool {
-        self.0.default_prevented()
-    }
-
-    fn event_phase(&self) -> u16 {
-        self.0.event_phase()
-    }
-
-    fn is_trusted(&self) -> bool {
-        self.0.is_trusted()
-    }
-
-    fn prevent_default(&self) {
-        self.0.prevent_default()
-    }
-
-    fn stop_immediate_propagation(&self) {
-        self.0.stop_immediate_propagation()
-    }
-
-    fn stop_propagation(&self) {
-        self.0.stop_propagation()
-    }
-
-    fn target(&self) {
-        // self.0.target()
-    }
-
-    fn time_stamp(&self) -> f64 {
-        self.0.time_stamp()
-    }
-}
-
-impl UIEventInner for WebsysGenericUiEvent {
-    fn detail(&self) -> i32 {
-        todo!()
-    }
-}
-
-impl SelectionEventInner for WebsysGenericUiEvent {}
-
-pub struct WebsysFocusEvent(pub web_sys::FocusEvent);
-impl FocusEventInner for WebsysFocusEvent {}
-
-pub struct WebsysFormEvent(pub web_sys::Event);
-impl FormEventInner for WebsysFormEvent {
-    // technically a controlled component, so we need to manually grab out the target data
-    fn value(&self) -> String {
-        let this: web_sys::EventTarget = self.0.target().unwrap();
-        (&this)
-                .dyn_ref()
-                .map(|input: &web_sys::HtmlInputElement| input.value())
-                .or_else(|| {
-                    this
-                        .dyn_ref()
-                        .map(|input: &web_sys::HtmlTextAreaElement| input.value())
-                })
-                // select elements are NOT input events - because - why woudn't they be??
-                .or_else(|| {
-                    this
-                        .dyn_ref()
-                        .map(|input: &web_sys::HtmlSelectElement| input.value())
-                })
-                .or_else(|| {
-                    this
-                        .dyn_ref::<web_sys::HtmlElement>()
-                        .unwrap()
-                        .text_content()
-                })
-                .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener")
-    }
-}
-
-pub struct WebsysMouseEvent(pub web_sys::MouseEvent);
-impl MouseEventInner for WebsysMouseEvent {
-    fn alt_key(&self) -> bool {
-        self.0.alt_key()
-    }
-    fn button(&self) -> i16 {
-        self.0.button()
-    }
-    fn buttons(&self) -> u16 {
-        self.0.buttons()
-    }
-    fn client_x(&self) -> i32 {
-        self.0.client_x()
-    }
-    fn client_y(&self) -> i32 {
-        self.0.client_y()
-    }
-    fn ctrl_key(&self) -> bool {
-        self.0.ctrl_key()
-    }
-    fn meta_key(&self) -> bool {
-        self.0.meta_key()
-    }
-    fn page_x(&self) -> i32 {
-        self.0.page_x()
-    }
-    fn page_y(&self) -> i32 {
-        self.0.page_y()
-    }
-    fn screen_x(&self) -> i32 {
-        self.0.screen_x()
-    }
-    fn screen_y(&self) -> i32 {
-        self.0.screen_y()
-    }
-    fn shift_key(&self) -> bool {
-        self.0.shift_key()
-    }
-
-    // yikes
-    // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
-    fn get_modifier_state(&self, key_code: &str) -> bool {
-        self.0.get_modifier_state(key_code)
-    }
-}
-
-pub struct WebsysPointerEvent(pub web_sys::PointerEvent);
-impl PointerEventInner for WebsysPointerEvent {
-    fn alt_key(&self) -> bool {
-        self.0.alt_key()
-    }
-    fn button(&self) -> i16 {
-        self.0.button()
-    }
-    fn buttons(&self) -> u16 {
-        self.0.buttons()
-    }
-    fn client_x(&self) -> i32 {
-        self.0.client_x()
-    }
-    fn client_y(&self) -> i32 {
-        self.0.client_y()
-    }
-    fn ctrl_key(&self) -> bool {
-        self.0.ctrl_key()
-    }
-    fn meta_key(&self) -> bool {
-        self.0.meta_key()
-    }
-    fn page_x(&self) -> i32 {
-        self.0.page_x()
-    }
-    fn page_y(&self) -> i32 {
-        self.0.page_y()
-    }
-    fn screen_x(&self) -> i32 {
-        self.0.screen_x()
-    }
-    fn screen_y(&self) -> i32 {
-        self.0.screen_y()
-    }
-    fn shift_key(&self) -> bool {
-        self.0.shift_key()
-    }
-
-    // yikes
-    // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
-    fn get_modifier_state(&self, key_code: &str) -> bool {
-        self.0.get_modifier_state(key_code)
-    }
-
-    fn pointer_id(&self) -> i32 {
-        self.0.pointer_id()
-    }
-
-    fn width(&self) -> i32 {
-        self.0.width()
-    }
-
-    fn height(&self) -> i32 {
-        self.0.height()
-    }
-
-    fn pressure(&self) -> f32 {
-        self.0.pressure()
-    }
-
-    fn tangential_pressure(&self) -> f32 {
-        self.0.tangential_pressure()
-    }
-
-    fn tilt_x(&self) -> i32 {
-        self.0.tilt_x()
-    }
-
-    fn tilt_y(&self) -> i32 {
-        self.0.tilt_y()
-    }
-
-    fn twist(&self) -> i32 {
-        self.0.twist()
-    }
-
-    fn pointer_type(&self) -> String {
-        self.0.pointer_type()
-    }
-
-    fn is_primary(&self) -> bool {
-        self.0.is_primary()
-    }
-}
-
-pub struct WebsysWheelEvent(pub web_sys::WheelEvent);
-impl WheelEventInner for WebsysWheelEvent {
-    fn delta_mode(&self) -> u32 {
-        self.0.delta_mode()
-    }
-
-    fn delta_x(&self) -> f64 {
-        self.0.delta_x()
-    }
-
-    fn delta_y(&self) -> f64 {
-        self.0.delta_y()
-    }
-
-    fn delta_z(&self) -> f64 {
-        self.0.delta_z()
-    }
-}
-pub struct WebsysAnimationEvent(pub web_sys::AnimationEvent);
-impl AnimationEventInner for WebsysAnimationEvent {
-    fn animation_name(&self) -> String {
-        self.0.animation_name()
-    }
-
-    fn pseudo_element(&self) -> String {
-        self.0.pseudo_element()
-    }
-
-    fn elapsed_time(&self) -> f32 {
-        self.0.elapsed_time()
-    }
-}
-
-pub struct WebsysTransitionEvent(pub web_sys::TransitionEvent);
-impl TransitionEventInner for WebsysTransitionEvent {
-    fn property_name(&self) -> String {
-        self.0.property_name()
-    }
-
-    fn pseudo_element(&self) -> String {
-        self.0.pseudo_element()
-    }
-
-    fn elapsed_time(&self) -> f32 {
-        self.0.elapsed_time()
-    }
-}
-
-pub struct WebsysTouchEvent(pub web_sys::TouchEvent);
-impl TouchEventInner for WebsysTouchEvent {
-    fn alt_key(&self) -> bool {
-        self.0.alt_key()
-    }
-
-    fn ctrl_key(&self) -> bool {
-        self.0.ctrl_key()
-    }
-
-    fn meta_key(&self) -> bool {
-        self.0.meta_key()
-    }
-
-    fn shift_key(&self) -> bool {
-        self.0.shift_key()
-    }
-
-    fn get_modifier_state(&self, key_code: &str) -> bool {
-        if cfg!(debug_assertions) {
-            todo!("get_modifier_state is not currently supported for touch events");
-        } else {
-            false
-        }
-    }
-}
-
-pub struct WebsysMediaEvent(pub web_sys::UiEvent);
-impl MediaEventInner for WebsysMediaEvent {}
-
-pub struct WebsysToggleEvent(pub web_sys::UiEvent);
-impl ToggleEventInner for WebsysToggleEvent {}

+ 2 - 2
packages/web/src/lib.rs

@@ -116,7 +116,7 @@ pub fn launch(root_component: FC<()>, configuration: impl FnOnce(WebConfig) -> W
 /// ```
 pub fn launch_with_props<T, F>(root_component: FC<T>, root_properties: T, configuration_builder: F)
 where
-    T: Properties + 'static,
+    T: Send + 'static,
     F: FnOnce(WebConfig) -> WebConfig,
 {
     let config = configuration_builder(WebConfig::default());
@@ -135,7 +135,7 @@ where
 ///     wasm_bindgen_futures::spawn_local(app_fut);
 /// }
 /// ```
-pub async fn run_with_props<T: Properties + 'static>(root: FC<T>, root_props: T, cfg: WebConfig) {
+pub async fn run_with_props<T: 'static + Send>(root: FC<T>, root_props: T, cfg: WebConfig) {
     let mut dom = VirtualDom::new_with_props(root, root_props);
 
     intern_cached_strings();

+ 8 - 0
packages/webview-client/Cargo.toml

@@ -0,0 +1,8 @@
+[package]
+name = "dioxus-webview-client"
+version = "0.0.0"
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]

+ 8 - 0
packages/webview-client/README.md

@@ -0,0 +1,8 @@
+# the client part of the vdom
+
+
+this crate is designed to be the "receiving end" of the dioxus virtualdom, using wasm_bindgen to expose an API for the receiving end.
+
+
+
+

+ 3 - 0
packages/webview-client/src/main.rs

@@ -0,0 +1,3 @@
+fn main() {
+    println!("Hello, world!");
+}