Ver código fonte

wip: more work on web

this commit adds complete event support for web-sys but with a few hole
Jonathan Kelley 3 anos atrás
pai
commit
3bf19d8

+ 10 - 5
Cargo.toml

@@ -19,7 +19,7 @@ dioxus-mobile = { path = "./packages/mobile", optional = true }
 
 [features]
 # core
-default = ["core", "ssr"]
+default = ["core", "ssr", "web"]
 core = ["macro", "hooks", "html"]
 macro = ["dioxus-core-macro"]
 hooks = ["dioxus-hooks"]
@@ -56,12 +56,17 @@ gloo-timers = "0.2.1"
 
 [target.'cfg(target_arch = "wasm32")'.dev-dependencies]
 gloo-timers = "0.2.1"
-surf = { version = "2.2.0", default-features = false, features = [
-    "wasm-client",
-], git = "https://github.com/jkelleyrtp/surf/", branch = "jk/fix-the-wasm" }
+wasm-logger = "0.2.0"
+console_error_panic_hook = "0.1.6"
+rand = { version = "0.8.4", features = ["small_rng"] }
+wasm-bindgen = { version = "0.2.71", features = ["enable-interning"] }
+
+# surf = { version = "2.2.0", default-features = false, features = [
+#     "wasm-client",
+# ], git = "https://github.com/jkelleyrtp/surf/", branch = "jk/fix-the-wasm" }
 
 
-[dependencies.getrandom]
+[dev-dependencies.getrandom]
 version = "0.2"
 features = ["js"]
 

+ 143 - 0
examples/web_tick.rs

@@ -0,0 +1,143 @@
+#![allow(non_upper_case_globals, non_snake_case)]
+//! Example: Webview Renderer
+//! -------------------------
+//!
+//! This example shows how to use the dioxus_desktop crate to build a basic desktop application.
+//!
+//! Under the hood, the dioxus_desktop crate bridges a native Dioxus VirtualDom with a custom prebuit application running
+//! in the webview runtime. Custom handlers are provided for the webview instance to consume patches and emit user events
+//! into the native VDom instance.
+//!
+//! Currently, NodeRefs won't work properly, but all other event functionality will.
+
+use dioxus::prelude::*;
+
+fn main() {
+    // Setup logging
+    // wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
+    // console_error_panic_hook::set_once();
+    for adj in ADJECTIVES {
+        wasm_bindgen::intern(adj);
+    }
+    for col in COLOURS {
+        wasm_bindgen::intern(col);
+    }
+    for no in NOUNS {
+        wasm_bindgen::intern(no);
+    }
+    wasm_bindgen::intern("col-md-1");
+    wasm_bindgen::intern("col-md-6");
+    wasm_bindgen::intern("glyphicon glyphicon-remove remove");
+    wasm_bindgen::intern("remove");
+    wasm_bindgen::intern("dioxus");
+    wasm_bindgen::intern("lbl");
+    wasm_bindgen::intern("true");
+
+    dioxus::web::launch(App, |c| c);
+}
+
+static App: FC<()> = |cx| {
+    // let mut count = use_state(cx, || 0);
+
+    let mut rng = SmallRng::from_entropy();
+    let rows = (0..1_000).map(|f| {
+        let label = Label::new(&mut rng);
+        rsx! {
+            Row {
+                row_id: f,
+                label: label
+            }
+        }
+    });
+    cx.render(rsx! {
+        table {
+            tbody {
+                {rows}
+            }
+        }
+    })
+    // cx.render(rsx! {
+    //     div {
+    //         // h1 { "Hifive counter: {count}" }
+    //         // {cx.children()}
+    //         // button { onclick: move |_| count += 1, "Up high!" }
+    //         // button { onclick: move |_| count -= 1, "Down low!" }
+    //         {(0..1000).map(|i| rsx!{ div { "Count: {count}" } })}
+    //     }
+    // })
+};
+
+#[derive(PartialEq, Props)]
+struct RowProps {
+    row_id: usize,
+    label: Label,
+}
+fn Row<'a>(cx: Context<'a, RowProps>) -> DomTree {
+    let [adj, col, noun] = cx.label.0;
+    cx.render(rsx! {
+        tr {
+            td { class:"col-md-1", "{cx.row_id}" }
+            td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
+                a { class: "lbl", "{adj}" "{col}" "{noun}" }
+            }
+            td { class: "col-md-1"
+                a { class: "remove", onclick: move |_| {/* remove */}
+                    span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" }
+                }
+            }
+            td { class: "col-md-6" }
+        }
+    })
+}
+use rand::prelude::*;
+
+#[derive(PartialEq)]
+struct Label([&'static str; 3]);
+
+impl Label {
+    fn new(rng: &mut SmallRng) -> Self {
+        Label([
+            ADJECTIVES.choose(rng).unwrap(),
+            COLOURS.choose(rng).unwrap(),
+            NOUNS.choose(rng).unwrap(),
+        ])
+    }
+}
+
+static ADJECTIVES: &[&str] = &[
+    "pretty",
+    "large",
+    "big",
+    "small",
+    "tall",
+    "short",
+    "long",
+    "handsome",
+    "plain",
+    "quaint",
+    "clean",
+    "elegant",
+    "easy",
+    "angry",
+    "crazy",
+    "helpful",
+    "mushy",
+    "odd",
+    "unsightly",
+    "adorable",
+    "important",
+    "inexpensive",
+    "cheap",
+    "expensive",
+    "fancy",
+];
+
+static COLOURS: &[&str] = &[
+    "red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black",
+    "orange",
+];
+
+static NOUNS: &[&str] = &[
+    "table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger",
+    "pizza", "mouse", "keyboard",
+];

+ 1 - 1
packages/core/Cargo.toml

@@ -26,7 +26,7 @@ fxhash = "0.2.1"
 longest-increasing-subsequence = "0.1.0"
 
 # internall used
-log = "0.4"
+log = { verison = "0.4", features = ["release_max_level_off"] }
 
 futures-util = "0.3.15"
 

+ 10 - 10
packages/core/src/arena.rs

@@ -90,17 +90,17 @@ impl SharedResources {
                 let comps = unsafe { &*components.get() };
 
                 if let Some(scope) = comps.get(idx.0) {
-                    todo!("implement immediates again")
+                    // todo!("implement immediates again")
+                    //
+
                     // queue
-                    //     .unbounded_send(EventTrigger::new(
-                    //         VirtualEvent::ScheduledUpdate {
-                    //             height: scope.height,
-                    //         },
-                    //         idx,
-                    //         None,
-                    //         EventPriority::High,
-                    //     ))
-                    //     .expect("The event queu receiver should *never* be dropped");
+                    // .unbounded_send(EventTrigger::new(
+                    //     V
+                    //     idx,
+                    //     None,
+                    //     EventPriority::High,
+                    // ))
+                    // .expect("The event queu receiver should *never* be dropped");
                 }
             }) as Rc<dyn Fn(ScopeId)>
         };

+ 6 - 8
packages/core/src/diff.rs

@@ -138,14 +138,12 @@ impl<'bump> DiffMachine<'bump> {
     }
 
     //
-    pub async fn diff_scope(&mut self, id: ScopeId) -> Result<()> {
-        let component = self
-            .vdom
-            .get_scope_mut(id)
-            .ok_or_else(|| Error::NotMounted)?;
-        let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
-        self.diff_node(old, new);
-        Ok(())
+    pub async fn diff_scope(&mut self, id: ScopeId) {
+        if let Some(component) = self.vdom.get_scope_mut(id) {
+            let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
+            self.stack.push(DiffInstruction::DiffNode { new, old });
+            self.work().await;
+        }
     }
 
     /// Progress the diffing for this "fiber"

+ 25 - 18
packages/core/src/events.rs

@@ -153,6 +153,7 @@ pub enum VirtualEvent {
     MouseEvent(on::MouseEvent),
     PointerEvent(on::PointerEvent),
 }
+
 impl VirtualEvent {
     pub fn is_input_event(&self) -> bool {
         match self {
@@ -215,7 +216,7 @@ pub mod on {
 
     #![allow(unused)]
     use bumpalo::boxed::Box as BumpBox;
-    use std::{cell::RefCell, fmt::Debug, ops::Deref, rc::Rc};
+    use std::{any::Any, cell::RefCell, fmt::Debug, ops::Deref, rc::Rc};
 
     use crate::{
         innerlude::NodeFactory,
@@ -563,6 +564,8 @@ pub mod on {
     }
 
     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
@@ -571,14 +574,18 @@ pub mod on {
         fn cancelable(&self) -> bool;
         /// Returns whether the event is composed or not
         fn composed(&self) -> bool;
-        /// Returns the event's path
-        fn composed_path(&self) -> String;
+
+        // 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) -> usize;
+        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
@@ -590,7 +597,7 @@ pub mod on {
         /// 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) -> usize;
+        fn time_stamp(&self) -> f64;
     }
 
     pub trait ClipboardEventInner {
@@ -688,8 +695,8 @@ pub mod on {
     pub trait PointerEventInner {
         // Mouse only
         fn alt_key(&self) -> bool;
-        fn button(&self) -> usize;
-        fn buttons(&self) -> usize;
+        fn button(&self) -> i16;
+        fn buttons(&self) -> u16;
         fn client_x(&self) -> i32;
         fn client_y(&self) -> i32;
         fn ctrl_key(&self) -> bool;
@@ -699,12 +706,12 @@ pub mod on {
         fn screen_x(&self) -> i32;
         fn screen_y(&self) -> i32;
         fn shift_key(&self) -> bool;
-        fn get_modifier_state(&self, key_code: usize) -> bool;
-        fn pointer_id(&self) -> usize;
-        fn width(&self) -> usize;
-        fn height(&self) -> usize;
-        fn pressure(&self) -> usize;
-        fn tangential_pressure(&self) -> usize;
+        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;
@@ -719,7 +726,7 @@ pub mod on {
         fn ctrl_key(&self) -> bool;
         fn meta_key(&self) -> bool;
         fn shift_key(&self) -> bool;
-        fn get_modifier_state(&self, key_code: usize) -> bool;
+        fn get_modifier_state(&self, key_code: &str) -> bool;
         // changedTouches: DOMTouchList,
         // targetTouches: DOMTouchList,
         // touches: DOMTouchList,
@@ -731,10 +738,10 @@ pub mod on {
     }
 
     pub trait WheelEventInner {
-        fn delta_mode(&self) -> i32;
-        fn delta_x(&self) -> i32;
-        fn delta_y(&self) -> i32;
-        fn delta_z(&self) -> i32;
+        fn delta_mode(&self) -> u32;
+        fn delta_x(&self) -> f64;
+        fn delta_y(&self) -> f64;
+        fn delta_z(&self) -> f64;
     }
 
     pub trait MediaEventInner {}

+ 47 - 39
packages/core/src/scheduler.rs

@@ -50,15 +50,13 @@ pub struct Scheduler {
 
     shared: SharedResources,
 
-    waypoints: VecDeque<Waypoint>,
-
     high_priorty: PriortySystem,
     medium_priority: PriortySystem,
     low_priority: PriortySystem,
 }
 
 pub enum FiberResult<'a> {
-    Done(&'a mut Mutations<'a>),
+    Done(Mutations<'a>),
     Interrupted,
 }
 
@@ -75,7 +73,6 @@ impl Scheduler {
             garbage_scopes: HashSet::new(),
 
             current_priority: EventPriority::Low,
-            waypoints: VecDeque::new(),
 
             high_priorty: PriortySystem::new(),
             medium_priority: PriortySystem::new(),
@@ -202,7 +199,9 @@ impl Scheduler {
     }
 
     pub fn has_work(&self) -> bool {
-        self.waypoints.len() > 0
+        self.high_priorty.has_work()
+            || self.medium_priority.has_work()
+            || self.low_priority.has_work()
     }
 
     pub fn has_pending_garbage(&self) -> bool {
@@ -219,10 +218,10 @@ impl Scheduler {
     }
 
     /// If a the fiber finishes its works (IE needs to be committed) the scheduler will drop the dirty scope
-    pub fn work_with_deadline(
-        &mut self,
-        mut deadline: &mut Pin<Box<impl FusedFuture<Output = ()>>>,
-    ) -> FiberResult {
+    pub async fn work_with_deadline<'a>(
+        &'a mut self,
+        deadline: &mut Pin<Box<impl FusedFuture<Output = ()>>>,
+    ) -> FiberResult<'a> {
         // check if we need to elevate priority
         self.current_priority = match (
             self.high_priorty.has_work(),
@@ -234,11 +233,46 @@ impl Scheduler {
             (false, false, _) => EventPriority::Low,
         };
 
-        let mut is_ready = || -> bool { (&mut deadline).now_or_never().is_some() };
+        let mut machine = DiffMachine::new_headless(&self.shared);
+
+        let dirty_root = {
+            let dirty_roots = match self.current_priority {
+                EventPriority::High => &self.high_priorty.dirty_scopes,
+                EventPriority::Medium => &self.medium_priority.dirty_scopes,
+                EventPriority::Low => &self.low_priority.dirty_scopes,
+            };
+            let mut height = 0;
+            let mut dirty_root = {
+                let root = dirty_roots.iter().next();
+                if root.is_none() {
+                    return FiberResult::Done(machine.mutations);
+                }
+                root.unwrap()
+            };
+
+            for root in dirty_roots {
+                if let Some(scope) = self.shared.get_scope(*root) {
+                    if scope.height < height {
+                        height = scope.height;
+                        dirty_root = root;
+                    }
+                }
+            }
+            dirty_root
+        };
+
+        match {
+            let fut = machine.diff_scope(*dirty_root).fuse();
+            pin_mut!(fut);
 
-        // TODO: remove this unwrap - proprogate errors out
-        // self.get_current_fiber().work(is_ready).unwrap()
-        todo!()
+            match futures_util::future::select(deadline, fut).await {
+                futures_util::future::Either::Left((deadline, work_fut)) => true,
+                futures_util::future::Either::Right((_, deadline_fut)) => false,
+            }
+        } {
+            true => FiberResult::Done(machine.mutations),
+            false => FiberResult::Interrupted,
+        }
     }
 
     // waits for a trigger, canceling early if the deadline is reached
@@ -356,32 +390,6 @@ pub struct DirtyScope {
     start_tick: u32,
 }
 
-/*
-A "waypoint" represents a frozen unit in time for the DiffingMachine to resume from. Whenever the deadline runs out
-while diffing, the diffing algorithm generates a Waypoint in order to easily resume from where it left off. Waypoints are
-fairly expensive to create, especially for big trees, so it's a good idea to pre-allocate them.
-
-Waypoints are created pessimisticly, and are only generated when an "Error" state is bubbled out of the diffing machine.
-This saves us from wasting cycles book-keeping waypoints for 99% of edits where the deadline is not reached.
-*/
-pub struct Waypoint {
-    // the progenitor of this waypoint
-    root: ScopeId,
-
-    edits: Vec<DomEdit<'static>>,
-
-    // a saved position in the tree
-    // these indicies continue to map through the tree into children nodes.
-    // A sequence of usizes is all that is needed to represent the path to a node.
-    tree_position: SmallVec<[usize; 10]>,
-
-    seen_scopes: HashSet<ScopeId>,
-
-    invalidate_scopes: HashSet<ScopeId>,
-
-    priority_level: EventPriority,
-}
-
 pub struct PriortySystem {
     pub pending_scopes: Vec<ScopeId>,
     pub dirty_scopes: HashSet<ScopeId>,

+ 6 - 3
packages/core/src/virtual_dom.rs

@@ -298,11 +298,11 @@ impl VirtualDom {
             }
 
             // Create work from the pending event queue
-            self.scheduler.consume_pending_events();
+            self.scheduler.consume_pending_events().unwrap();
 
             // Work through the current subtree, and commit the results when it finishes
             // When the deadline expires, give back the work
-            match self.scheduler.work_with_deadline(&mut deadline) {
+            match self.scheduler.work_with_deadline(&mut deadline).await {
                 FiberResult::Done(mut mutations) => {
                     committed_mutations.extend(&mut mutations);
 
@@ -332,7 +332,10 @@ impl VirtualDom {
         true
     }
 
-    pub async fn wait_for_any_work(&self) {}
+    pub async fn wait_for_any_work(&mut self) {
+        let mut timeout = Box::pin(futures_util::future::pending().fuse());
+        self.scheduler.wait_for_any_trigger(&mut timeout).await;
+    }
 }
 
 // TODO!

+ 1 - 1
packages/web/Cargo.toml

@@ -14,9 +14,9 @@ js-sys = "0.3"
 wasm-bindgen = { version = "0.2.71", features = ["enable-interning"] }
 lazy_static = "1.4.0"
 wasm-bindgen-futures = "0.4.20"
-wasm-logger = "0.2.0"
 log = "0.4.14"
 fxhash = "0.2.1"
+wasm-logger = "0.2.0"
 console_error_panic_hook = "0.1.6"
 generational-arena = "0.2.8"
 wasm-bindgen-test = "0.3.21"

+ 31 - 233
packages/web/src/dom.rs

@@ -1,7 +1,7 @@
-use std::{collections::HashMap, rc::Rc, sync::Arc};
+use std::{collections::HashMap, fmt::Debug, rc::Rc, sync::Arc};
 
 use dioxus_core::{
-    events::{EventTrigger, VirtualEvent},
+    events::{on::GenericEventInner, EventTrigger, VirtualEvent},
     mutations::NodeRefMutation,
     DomEdit, ElementId, ScopeId,
 };
@@ -9,7 +9,7 @@ use fxhash::FxHashMap;
 use wasm_bindgen::{closure::Closure, JsCast};
 use web_sys::{
     window, CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement,
-    HtmlOptionElement, Node, NodeList,
+    HtmlOptionElement, Node, NodeList, UiEvent,
 };
 
 use crate::{nodeslab::NodeSlab, WebConfig};
@@ -436,284 +436,82 @@ impl Stack {
     }
 }
 
-fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent {
+fn virtual_event_from_websys_event(event: web_sys::Event) -> VirtualEvent {
+    use crate::events::*;
     use dioxus_core::events::on::*;
     match event.type_().as_str() {
         "copy" | "cut" | "paste" => {
-            struct WebsysClipboardEvent();
-            impl ClipboardEventInner for WebsysClipboardEvent {}
-            VirtualEvent::ClipboardEvent(ClipboardEvent(Rc::new(WebsysClipboardEvent())))
+            VirtualEvent::ClipboardEvent(ClipboardEvent(Rc::new(WebsysClipboardEvent(event))))
         }
-
         "compositionend" | "compositionstart" | "compositionupdate" => {
             let evt: web_sys::CompositionEvent = event.clone().dyn_into().unwrap();
-            struct WebsysCompositionEvent(web_sys::CompositionEvent);
-            impl CompositionEventInner for WebsysCompositionEvent {
-                fn data(&self) -> String {
-                    todo!()
-                }
-            }
             VirtualEvent::CompositionEvent(CompositionEvent(Rc::new(WebsysCompositionEvent(evt))))
         }
-
         "keydown" | "keypress" | "keyup" => {
-            struct Event(web_sys::KeyboardEvent);
-            impl KeyboardEventInner for Event {
-                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 {
-                    todo!("Locale is currently not supported. :(")
-                }
-
-                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
-                }
-            }
-            let evt: web_sys::KeyboardEvent = event.clone().dyn_into().unwrap();
-            VirtualEvent::KeyboardEvent(KeyboardEvent(Rc::new(Event(evt))))
+            let evt: web_sys::KeyboardEvent = event.dyn_into().unwrap();
+            VirtualEvent::KeyboardEvent(KeyboardEvent(Rc::new(WebsysKeyboardEvent(evt))))
         }
-
         "focus" | "blur" => {
-            struct Event(web_sys::FocusEvent);
-            impl FocusEventInner for Event {}
-            let evt: web_sys::FocusEvent = event.clone().dyn_into().unwrap();
-            VirtualEvent::FocusEvent(FocusEvent(Rc::new(Event(evt))))
+            let evt: web_sys::FocusEvent = event.dyn_into().unwrap();
+            VirtualEvent::FocusEvent(FocusEvent(Rc::new(WebsysFocusEvent(evt))))
         }
-
         "change" => {
-            // struct Event(web_sys::Event);
-            // impl GenericEventInner for Event {
-            //     fn bubbles(&self) -> bool {
-            //         todo!()
-            //     }
-
-            //     fn cancel_bubble(&self) {
-            //         todo!()
-            //     }
-
-            //     fn cancelable(&self) -> bool {
-            //         todo!()
-            //     }
-
-            //     fn composed(&self) -> bool {
-            //         todo!()
-            //     }
-
-            //     fn composed_path(&self) -> String {
-            //         todo!()
-            //     }
-
-            //     fn current_target(&self) {
-            //         todo!()
-            //     }
-
-            //     fn default_prevented(&self) -> bool {
-            //         todo!()
-            //     }
-
-            //     fn event_phase(&self) -> usize {
-            //         todo!()
-            //     }
-
-            //     fn is_trusted(&self) -> bool {
-            //         todo!()
-            //     }
-
-            //     fn prevent_default(&self) {
-            //         todo!()
-            //     }
-
-            //     fn stop_immediate_propagation(&self) {
-            //         todo!()
-            //     }
-
-            //     fn stop_propagation(&self) {
-            //         todo!()
-            //     }
-
-            //     fn target(&self) {
-            //         todo!()
-            //     }
-
-            //     fn time_stamp(&self) -> usize {
-            //         todo!()
-            //     }
-            // }
-            // let evt: web_sys::Event = event.clone().dyn_into().expect("wrong error typ");
-            // VirtualEvent::Event(GenericEvent(Rc::new(Event(evt))))
-            todo!()
+            let evt = event.dyn_into().unwrap();
+            VirtualEvent::UIEvent(UIEvent(Rc::new(WebsysGenericUiEvent(evt))))
         }
-
         "input" | "invalid" | "reset" | "submit" => {
-            // is a special react events
-            let evt: web_sys::InputEvent = event.clone().dyn_into().expect("wrong event type");
-            let this: web_sys::EventTarget = evt.target().unwrap();
-
-            let value = (&this)
-                .dyn_ref()
-                .map(|input: &web_sys::HtmlInputElement| input.value())
-                .or_else(|| {
-                    (&this)
-                        .dyn_ref()
-                        .map(|input: &web_sys::HtmlTextAreaElement| 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");
-
-            todo!()
-            // VirtualEvent::FormEvent(FormEvent { value })
+            let evt: web_sys::InputEvent = event.clone().dyn_into().unwrap();
+            VirtualEvent::FormEvent(FormEvent(Rc::new(WebsysFormEvent(evt))))
         }
-
         "click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
         | "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
         | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
             let evt: web_sys::MouseEvent = event.clone().dyn_into().unwrap();
-
-            #[derive(Debug)]
-            pub struct CustomMouseEvent(web_sys::MouseEvent);
-            impl dioxus_core::events::on::MouseEventInner for CustomMouseEvent {
-                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)
-                }
-            }
-            VirtualEvent::MouseEvent(MouseEvent(Rc::new(CustomMouseEvent(evt))))
+            VirtualEvent::MouseEvent(MouseEvent(Rc::new(WebsysMouseEvent(evt))))
         }
-
         "pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
         | "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
             let evt: web_sys::PointerEvent = event.clone().dyn_into().unwrap();
-            todo!()
+            VirtualEvent::PointerEvent(PointerEvent(Rc::new(WebsysPointerEvent(evt))))
         }
-
         "select" => {
-            // let evt: web_sys::SelectionEvent = event.clone().dyn_into().unwrap();
-            // not required to construct anything special beyond standard event stuff
-            todo!()
+            let evt: web_sys::UiEvent = event.clone().dyn_into().unwrap();
+            VirtualEvent::SelectionEvent(SelectionEvent(Rc::new(WebsysGenericUiEvent(evt))))
         }
-
         "touchcancel" | "touchend" | "touchmove" | "touchstart" => {
             let evt: web_sys::TouchEvent = event.clone().dyn_into().unwrap();
-            todo!()
+            VirtualEvent::TouchEvent(TouchEvent(Rc::new(WebsysTouchEvent(evt))))
         }
-
         "scroll" => {
-            // let evt: web_sys::UIEvent = event.clone().dyn_into().unwrap();
-            todo!()
+            let evt: web_sys::UiEvent = event.clone().dyn_into().unwrap();
+            VirtualEvent::UIEvent(UIEvent(Rc::new(WebsysGenericUiEvent(evt))))
         }
-
         "wheel" => {
             let evt: web_sys::WheelEvent = event.clone().dyn_into().unwrap();
-            todo!()
+            VirtualEvent::WheelEvent(WheelEvent(Rc::new(WebsysWheelEvent(evt))))
         }
         "animationstart" | "animationend" | "animationiteration" => {
             let evt: web_sys::AnimationEvent = event.clone().dyn_into().unwrap();
-            todo!()
+            VirtualEvent::AnimationEvent(AnimationEvent(Rc::new(WebsysAnimationEvent(evt))))
         }
-
         "transitionend" => {
             let evt: web_sys::TransitionEvent = event.clone().dyn_into().unwrap();
-            todo!()
+            VirtualEvent::TransitionEvent(TransitionEvent(Rc::new(WebsysTransitionEvent(evt))))
         }
-
         "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
         | "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
         | "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
         | "timeupdate" | "volumechange" | "waiting" => {
-            // not required to construct anything special beyond standard event stuff
-
-            // let evt: web_sys::MediaEvent = event.clone().dyn_into().unwrap();
-            // let evt: web_sys::MediaEvent = event.clone().dyn_into().unwrap();
-            todo!()
+            let evt: web_sys::UiEvent = event.clone().dyn_into().unwrap();
+            VirtualEvent::MediaEvent(MediaEvent(Rc::new(WebsysMediaEvent(evt))))
         }
-
         "toggle" => {
-            // not required to construct anything special beyond standard event stuff (target)
-
-            // let evt: web_sys::ToggleEvent = event.clone().dyn_into().unwrap();
-            todo!()
+            let evt: web_sys::UiEvent = event.clone().dyn_into().unwrap();
+            VirtualEvent::ToggleEvent(ToggleEvent(Rc::new(WebsysToggleEvent(evt))))
         }
         _ => {
-            todo!()
+            let evt: web_sys::UiEvent = event.clone().dyn_into().unwrap();
+            VirtualEvent::UIEvent(UIEvent(Rc::new(WebsysGenericUiEvent(evt))))
         }
     }
 }
@@ -763,7 +561,7 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<EventTrigger> {
     // let triggered_scope: ScopeId = KeyData::from_ffi(gi_id).into();
     log::debug!("Triggered scope is {:#?}", triggered_scope);
     Ok(EventTrigger::new(
-        virtual_event_from_websys_event(event),
+        virtual_event_from_websys_event(event.clone()),
         ScopeId(triggered_scope as usize),
         Some(ElementId(real_id as usize)),
         dioxus_core::events::EventPriority::High,

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

@@ -0,0 +1,418 @@
+//! Ported events into Dioxus Synthetic Event system
+//!
+//! event porting is pretty boring, sorry.
+
+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 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::InputEvent);
+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())
+                })
+                .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 {}

+ 4 - 0
packages/web/src/lib.rs

@@ -56,6 +56,7 @@ use std::rc::Rc;
 
 pub use crate::cfg::WebConfig;
 use crate::dom::load_document;
+use cache::intern_cache;
 use dioxus::prelude::Properties;
 use dioxus::virtual_dom::VirtualDom;
 pub use dioxus_core as dioxus;
@@ -64,6 +65,7 @@ use dioxus_core::prelude::FC;
 mod cache;
 mod cfg;
 mod dom;
+mod events;
 mod nodeslab;
 mod ric_raf;
 
@@ -114,6 +116,8 @@ where
 pub async fn run_with_props<T: Properties + 'static>(root: FC<T>, root_props: T, cfg: WebConfig) {
     let mut dom = VirtualDom::new_with_props(root, root_props);
 
+    intern_cache();
+
     let hydrating = cfg.hydrate;
 
     let root_el = load_document().get_element_by_id(&cfg.rootname).unwrap();

+ 7 - 37
packages/web/src/ric_raf.rs

@@ -1,10 +1,7 @@
 //! RequestAnimationFrame and RequestIdleCallback port and polyfill.
 
-use std::{cell::RefCell, fmt, rc::Rc};
-
 use gloo_timers::future::TimeoutFuture;
 use js_sys::Function;
-use wasm_bindgen::prelude::*;
 use wasm_bindgen::JsCast;
 use wasm_bindgen::{prelude::Closure, JsValue};
 use web_sys::Window;
@@ -21,13 +18,15 @@ impl RafLoop {
     pub fn new() -> Self {
         let (raf_sender, raf_receiver) = async_channel::unbounded();
 
-        let raf_closure: Closure<dyn Fn(JsValue)> =
-            Closure::wrap(Box::new(move |v: JsValue| raf_sender.try_send(()).unwrap()));
+        let raf_closure: Closure<dyn Fn(JsValue)> = Closure::wrap(Box::new(move |_v: JsValue| {
+            raf_sender.try_send(()).unwrap()
+        }));
 
         let (ric_sender, ric_receiver) = async_channel::unbounded();
 
-        let ric_closure: Closure<dyn Fn(JsValue)> =
-            Closure::wrap(Box::new(move |v: JsValue| ric_sender.try_send(()).unwrap()));
+        let ric_closure: Closure<dyn Fn(JsValue)> = Closure::wrap(Box::new(move |_v: JsValue| {
+            ric_sender.try_send(()).unwrap()
+        }));
 
         // execute the polyfill for safari
         Function::new_no_args(include_str!("./ricpolyfill.js"))
@@ -44,45 +43,16 @@ impl RafLoop {
     }
     /// waits for some idle time and returns a timeout future that expires after the idle time has passed
     pub async fn wait_for_idle_time(&self) -> TimeoutFuture {
-        // comes with its own safari polyfill :)
-
         let ric_fn = self.ric_closure.as_ref().dyn_ref::<Function>().unwrap();
         let deadline: u32 = self.window.request_idle_callback(ric_fn).unwrap();
-
         self.ric_receiver.recv().await.unwrap();
-
         let deadline = TimeoutFuture::new(deadline);
         deadline
     }
 
     pub async fn wait_for_raf(&self) {
         let raf_fn = self.raf_closure.as_ref().dyn_ref::<Function>().unwrap();
-        let id: i32 = self.window.request_animation_frame(raf_fn).unwrap();
+        let _id: i32 = self.window.request_animation_frame(raf_fn).unwrap();
         self.raf_receiver.recv().await.unwrap();
     }
 }
-
-#[derive(Debug)]
-pub struct AnimationFrame {
-    render_id: i32,
-    closure: Closure<dyn Fn(JsValue)>,
-    callback_wrapper: Rc<RefCell<Option<CallbackWrapper>>>,
-}
-
-struct CallbackWrapper(Box<dyn FnOnce(f64) + 'static>);
-impl fmt::Debug for CallbackWrapper {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str("CallbackWrapper")
-    }
-}
-
-impl Drop for AnimationFrame {
-    fn drop(&mut self) {
-        if self.callback_wrapper.borrow_mut().is_some() {
-            web_sys::window()
-                .unwrap_throw()
-                .cancel_animation_frame(self.render_id)
-                .unwrap_throw()
-        }
-    }
-}