diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 47d8d378c..a922cceaf 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -251,7 +251,7 @@ impl<'bump> DiffMachine<'bump> { fn create_suspended_node(&mut self, suspended: &'bump VSuspended) { let real_id = self.vdom.reserve_node(); self.mutations.create_placeholder(real_id); - suspended.node.set(Some(real_id)); + suspended.dom_id.set(Some(real_id)); self.stack.add_child_count(1); } @@ -372,7 +372,7 @@ impl<'bump> DiffMachine<'bump> { (Component(old), Component(new)) => self.diff_component_nodes(old, new), (Fragment(old), Fragment(new)) => self.diff_fragment_nodes(old, new), (Anchor(old), Anchor(new)) => new.dom_id.set(old.dom_id.get()), - (Suspended(old), Suspended(new)) => new.node.set(old.node.get()), + (Suspended(old), Suspended(new)) => new.dom_id.set(old.dom_id.get()), (Element(old), Element(new)) => self.diff_element_nodes(old, new), // Anything else is just a basic replace and create @@ -952,7 +952,7 @@ impl<'bump> DiffMachine<'bump> { match &search_node.take().unwrap() { VNode::Text(t) => break t.dom_id.get(), VNode::Element(t) => break t.dom_id.get(), - VNode::Suspended(t) => break t.node.get(), + VNode::Suspended(t) => break t.dom_id.get(), VNode::Anchor(t) => break t.dom_id.get(), VNode::Fragment(frag) => { @@ -983,7 +983,7 @@ impl<'bump> DiffMachine<'bump> { } VNode::Text(t) => break t.dom_id.get(), VNode::Element(t) => break t.dom_id.get(), - VNode::Suspended(t) => break t.node.get(), + VNode::Suspended(t) => break t.dom_id.get(), VNode::Anchor(t) => break t.dom_id.get(), } } @@ -1016,7 +1016,7 @@ impl<'bump> DiffMachine<'bump> { }); } VNode::Suspended(s) => { - s.node.get().map(|id| { + s.dom_id.get().map(|id| { self.mutations.remove(id.as_u64()); self.vdom.collect_garbage(id); }); diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index f74aace14..454975a7d 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -3,16 +3,7 @@ //! //! 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 std::{ - cell::{Cell, RefCell}, - rc::Rc, -}; - -use crate::{ - innerlude::{ElementId, ScopeId}, - VNode, -}; +use crate::innerlude::{ElementId, ScopeId}; #[derive(Debug)] pub struct EventTrigger { @@ -23,12 +14,15 @@ pub struct EventTrigger { pub mounted_dom_id: Option, /// The type of event - pub event: VirtualEvent, + pub event: SyntheticEvent, + /// Is consistency important for this event? + /// UI events are the only events where consistency is important. + /// All else may be batched. pub discrete: bool, } -pub enum VirtualEvent { +pub enum SyntheticEvent { KeyboardEvent(on::KeyboardEvent), TouchEvent(on::TouchEvent), MouseEvent(on::MouseEvent), @@ -47,24 +41,24 @@ pub enum VirtualEvent { // ImageEvent(event_data::ImageEvent), } -impl std::fmt::Debug for VirtualEvent { +impl std::fmt::Debug for SyntheticEvent { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let name = match self { - VirtualEvent::ClipboardEvent(_) => "ClipboardEvent", - VirtualEvent::CompositionEvent(_) => "CompositionEvent", - VirtualEvent::KeyboardEvent(_) => "KeyboardEvent", - VirtualEvent::FocusEvent(_) => "FocusEvent", - VirtualEvent::FormEvent(_) => "FormEvent", - VirtualEvent::SelectionEvent(_) => "SelectionEvent", - VirtualEvent::TouchEvent(_) => "TouchEvent", - VirtualEvent::UIEvent(_) => "UIEvent", - VirtualEvent::WheelEvent(_) => "WheelEvent", - VirtualEvent::MediaEvent(_) => "MediaEvent", - VirtualEvent::AnimationEvent(_) => "AnimationEvent", - VirtualEvent::TransitionEvent(_) => "TransitionEvent", - VirtualEvent::ToggleEvent(_) => "ToggleEvent", - VirtualEvent::MouseEvent(_) => "MouseEvent", - VirtualEvent::PointerEvent(_) => "PointerEvent", + SyntheticEvent::ClipboardEvent(_) => "ClipboardEvent", + SyntheticEvent::CompositionEvent(_) => "CompositionEvent", + SyntheticEvent::KeyboardEvent(_) => "KeyboardEvent", + SyntheticEvent::FocusEvent(_) => "FocusEvent", + SyntheticEvent::FormEvent(_) => "FormEvent", + SyntheticEvent::SelectionEvent(_) => "SelectionEvent", + SyntheticEvent::TouchEvent(_) => "TouchEvent", + SyntheticEvent::UIEvent(_) => "UIEvent", + SyntheticEvent::WheelEvent(_) => "WheelEvent", + SyntheticEvent::MediaEvent(_) => "MediaEvent", + SyntheticEvent::AnimationEvent(_) => "AnimationEvent", + SyntheticEvent::TransitionEvent(_) => "TransitionEvent", + SyntheticEvent::ToggleEvent(_) => "ToggleEvent", + SyntheticEvent::MouseEvent(_) => "MouseEvent", + SyntheticEvent::PointerEvent(_) => "PointerEvent", }; f.debug_struct("VirtualEvent").field("type", &name).finish() @@ -90,7 +84,7 @@ pub mod on { }; use std::cell::Cell; - use super::VirtualEvent; + use super::SyntheticEvent; macro_rules! event_directory { ( $( @@ -126,12 +120,12 @@ pub mod on { { let bump = &c.bump(); - let cb: &mut dyn FnMut(VirtualEvent) = bump.alloc(move |evt: VirtualEvent| match evt { - VirtualEvent::$wrapper(event) => callback(event), - _ => unreachable!("Downcasted VirtualEvent to wrong event type - this is an internal bug!") + 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 = unsafe { BumpBox::from_raw(cb) }; + let callback: BumpBox = unsafe { BumpBox::from_raw(cb) }; let event_name = stringify!($name); let shortname: &'static str = &event_name[2..]; diff --git a/packages/core/src/hooks.rs b/packages/core/src/hooks.rs index 912427efc..ebe448ed5 100644 --- a/packages/core/src/hooks.rs +++ b/packages/core/src/hooks.rs @@ -126,14 +126,7 @@ where let handle = cx.submit_task(Box::pin(task_fut.then(move |output| async move { *slot.as_ref().borrow_mut() = Some(output); updater(update_id); - EventTrigger { - event: VirtualEvent::AsyncEvent { - should_rerender: false, - }, - scope: originator, - priority: EventPriority::Low, - mounted_dom_id: None, - } + originator }))); TaskHook { @@ -172,71 +165,75 @@ where Out: 'static, Cb: for<'a> Fn(SuspendedContext<'a>, &Out) -> DomTree<'a> + 'static, { + /* + General strategy: + - Create a slot for the future to dump its output into + - Create a new future feeding off the user's future that feeds the output into that slot + - Submit that future as a task + - Take the task handle id and attach that to our suspended node + - when the hook runs, check if the value exists + - if it does, then we can render the node directly + - if it doesn't, then we render a suspended node along with with the callback and task id + */ cx.use_hook( move |hook_idx| { let value = Rc::new(RefCell::new(None)); - - let dom_node_id = Rc::new(empty_cell()); - let domnode = dom_node_id.clone(); - let slot = value.clone(); - let callback: SuspendedCallback = Box::new(move |ctx: SuspendedContext| { - let v: std::cell::Ref>> = slot.as_ref().borrow(); - match v.as_ref() { - Some(a) => { - let v: &dyn Any = a.as_ref(); - let real_val = v.downcast_ref::().unwrap(); - user_callback(ctx, real_val) - } - None => { - // - Some(VNode::Suspended(VSuspended { - node: domnode.clone(), - })) - } - } - }); - let originator = cx.scope.our_arena_idx.clone(); - let task_fut = task_initializer(); - let domnode = dom_node_id.clone(); - let slot = value.clone(); - cx.submit_task(Box::pin(task_fut.then(move |output| async move { - // When the new value arrives, set the hooks internal slot - // Dioxus will call the user's callback to generate new nodes outside of the diffing system - *slot.borrow_mut() = Some(Box::new(output) as Box); - EventTrigger { - event: VirtualEvent::SuspenseEvent { hook_idx, domnode }, - scope: originator, - priority: EventPriority::Low, - mounted_dom_id: None, - } - }))); + let handle = cx.submit_task(Box::pin(task_initializer().then( + move |output| async move { + *slot.borrow_mut() = Some(Box::new(output) as Box); + originator + }, + ))); - SuspenseHook { - value, - callback, - dom_node_id, - } + SuspenseHook { handle, value } }, - move |hook| { - let cx = Context { - scope: &cx.scope, - props: &(), - }; - let csx = SuspendedContext { inner: cx }; - (&hook.callback)(csx) + move |hook| match hook.value.borrow().as_ref() { + Some(value) => { + let out = value.downcast_ref::().unwrap(); + let sus = SuspendedContext { + inner: Context { + props: &(), + scope: cx.scope, + }, + }; + user_callback(sus, out) + } + None => { + let value = hook.value.clone(); + + cx.render(LazyNodes::new(|f| { + let bump = f.bump(); + let g: &dyn FnOnce(SuspendedContext<'src>) -> DomTree<'src> = + bump.alloc(|sus| { + let out = value + .borrow() + .as_ref() + .unwrap() + .as_ref() + .downcast_ref::() + .unwrap(); + user_callback(sus, out) + }); + + VNode::Suspended(bump.alloc(VSuspended { + dom_id: empty_cell(), + task_id: hook.handle.our_id, + callback: RefCell::new(Some(g)), + })) + })) + } }, |_| {}, ) } pub(crate) struct SuspenseHook { + pub handle: TaskHandle, pub value: Rc>>>, - pub callback: SuspendedCallback, - pub dom_node_id: Rc>>, } type SuspendedCallback = Box Fn(SuspendedContext<'a>) -> DomTree<'a>>; pub struct SuspendedContext<'a> { diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index 5db7e88d4..d6d4fece6 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -13,7 +13,7 @@ pub use crate::innerlude::{ format_args_f, html, rsx, Context, DiffInstruction, DioxusElement, DomEdit, DomTree, ElementId, EventPriority, EventTrigger, LazyNodes, MountType, Mutations, NodeFactory, Properties, ScopeId, - SuspendedContext, VNode, VirtualDom, VirtualEvent, FC, + SuspendedContext, SyntheticEvent, VNode, VirtualDom, FC, }; pub mod prelude { diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 8671cf45d..5e2db6ef8 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -4,8 +4,9 @@ //! //! These VNodes should be *very* cheap and *very* fast to construct - building a full tree should be insanely quick. use crate::{ - events::VirtualEvent, + events::SyntheticEvent, innerlude::{empty_cell, Context, DomTree, ElementId, Properties, Scope, ScopeId, FC}, + SuspendedContext, }; use bumpalo::{boxed::Box as BumpBox, Bump}; use std::{ @@ -30,7 +31,7 @@ pub enum VNode<'src> { Component(&'src VComponent<'src>), - Suspended(VSuspended), + Suspended(&'src VSuspended<'src>), Anchor(VAnchor), } @@ -127,7 +128,7 @@ pub struct Listener<'bump> { pub mounted_node: Cell>, - pub(crate) callback: RefCell>>, + pub(crate) callback: RefCell>>, } impl Listener<'_> { @@ -168,8 +169,10 @@ pub struct VComponent<'src> { pub(crate) user_fc: *const (), } -pub struct VSuspended { - pub node: Rc>>, +pub struct VSuspended<'a> { + pub dom_id: Cell>, + pub task_id: u64, + pub callback: RefCell) -> DomTree<'a>>>, } /// This struct provides an ergonomic API to quickly build VNodes. @@ -301,12 +304,6 @@ impl<'a> NodeFactory<'a> { })) } - pub fn suspended() -> VNode<'static> { - VNode::Suspended(VSuspended { - node: Rc::new(empty_cell()), - }) - } - pub fn attr( &self, name: &'static str, diff --git a/packages/core/src/scheduler.rs b/packages/core/src/scheduler.rs index 51e3eca85..49d4bb656 100644 --- a/packages/core/src/scheduler.rs +++ b/packages/core/src/scheduler.rs @@ -34,7 +34,7 @@ pub struct EventChannel { pub enum SchedulerMsg { Immediate(ScopeId), - UiEvent(), + UiEvent(EventTrigger), SubmitTask(u64), ToggleTask(u64), PauseTask(u64), @@ -232,16 +232,6 @@ impl Scheduler { id } - pub fn make_trigger_key(&self, trigger: &EventTrigger) -> EventKey { - let height = self.get_scope(trigger.scope).map(|f| f.height).unwrap(); - - EventKey { - height, - originator: trigger.scope, - priority: trigger.priority, - } - } - pub fn clean_up_garbage(&mut self) { let mut scopes_to_kill = Vec::new(); let mut garbage_list = Vec::new(); @@ -330,11 +320,11 @@ impl Scheduler { pub fn consume_pending_events(&mut self) -> Result<()> { while let Some(trigger) = self.pending_events.pop_back() { match &trigger.event { - VirtualEvent::AsyncEvent { .. } => {} + SyntheticEvent::AsyncEvent { .. } => {} // This suspense system works, but it's not the most elegant solution. // TODO: Replace this system - VirtualEvent::SuspenseEvent { hook_idx, domnode } => { + SyntheticEvent::SuspenseEvent { hook_idx, domnode } => { todo!("suspense needs to be converted into its own channel"); // // Safety: this handler is the only thing that can mutate shared items at this moment in tim @@ -369,21 +359,21 @@ impl Scheduler { // } } - VirtualEvent::ClipboardEvent(_) - | VirtualEvent::CompositionEvent(_) - | VirtualEvent::KeyboardEvent(_) - | VirtualEvent::FocusEvent(_) - | VirtualEvent::FormEvent(_) - | VirtualEvent::SelectionEvent(_) - | VirtualEvent::TouchEvent(_) - | VirtualEvent::UIEvent(_) - | VirtualEvent::WheelEvent(_) - | VirtualEvent::MediaEvent(_) - | VirtualEvent::AnimationEvent(_) - | VirtualEvent::TransitionEvent(_) - | VirtualEvent::ToggleEvent(_) - | VirtualEvent::MouseEvent(_) - | VirtualEvent::PointerEvent(_) => { + SyntheticEvent::ClipboardEvent(_) + | SyntheticEvent::CompositionEvent(_) + | SyntheticEvent::KeyboardEvent(_) + | SyntheticEvent::FocusEvent(_) + | SyntheticEvent::FormEvent(_) + | SyntheticEvent::SelectionEvent(_) + | SyntheticEvent::TouchEvent(_) + | SyntheticEvent::UIEvent(_) + | SyntheticEvent::WheelEvent(_) + | SyntheticEvent::MediaEvent(_) + | SyntheticEvent::AnimationEvent(_) + | SyntheticEvent::TransitionEvent(_) + | SyntheticEvent::ToggleEvent(_) + | SyntheticEvent::MouseEvent(_) + | SyntheticEvent::PointerEvent(_) => { if let Some(scope) = self.get_scope_mut(trigger.scope) { if let Some(element) = trigger.mounted_dom_id { scope.call_listener(trigger.event, element)?; @@ -528,8 +518,8 @@ impl Scheduler { } pub struct TaskHandle { - channel: EventChannel, - our_id: u64, + pub channel: EventChannel, + pub our_id: u64, } impl TaskHandle { diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index fda9960f5..2222a7335 100644 --- a/packages/core/src/scope.rs +++ b/packages/core/src/scope.rs @@ -40,6 +40,8 @@ pub struct Scope { pub(crate) listeners: RefCell>>, pub(crate) borrowed_props: RefCell>>, + pub(crate) suspended_nodes: RefCell>>, + // State pub(crate) hooks: HookList, pub(crate) shared_contexts: RefCell>>, @@ -52,8 +54,9 @@ pub struct Scope { // The type of closure that wraps calling components pub type WrappedCaller = dyn for<'b> Fn(&'b Scope) -> DomTree<'b>; -// The type of task that gets sent to the task scheduler -pub type FiberTask = Pin>>; +/// The type of task that gets sent to the task scheduler +/// Submitting a fiber task returns a handle to that task, which can be used to wake up suspended nodes +pub type FiberTask = Pin>>; impl Scope { // we are being created in the scope of an existing component (where the creator_node lifetime comes into play) @@ -91,6 +94,7 @@ impl Scope { height, frames: ActiveFrame::new(), hooks: Default::default(), + suspended_nodes: Default::default(), shared_contexts: Default::default(), listeners: Default::default(), borrowed_props: Default::default(), @@ -194,7 +198,11 @@ impl Scope { // A safe wrapper around calling listeners // // - pub(crate) fn call_listener(&mut self, event: VirtualEvent, element: ElementId) -> Result<()> { + pub(crate) fn call_listener( + &mut self, + event: SyntheticEvent, + element: ElementId, + ) -> Result<()> { let listners = self.listeners.borrow_mut(); let raw_listener = listners.iter().find(|lis| { diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 558bb4129..524a5ff4c 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -100,24 +100,31 @@ impl VirtualDom { /// /// Note: the VirtualDOM is not progressed, you must either "run_with_deadline" or use "rebuild" to progress it. pub fn new_with_props(root: FC

, root_props: P) -> Self { - let components = Scheduler::new(); + let scheduler = Scheduler::new(); - let root_props: Pin> = Box::pin(root_props); - let props_ptr = root_props.as_ref().downcast_ref::

().unwrap() as *const P; + let _root_props: Pin> = Box::pin(root_props); + let _root_prop_type = TypeId::of::

(); - let update_sender = components.immediate_sender.clone(); + let props_ptr = _root_props.as_ref().downcast_ref::

().unwrap() as *const P; - let base_scope = components.insert_scope_with_key(move |myidx| { + let base_scope = scheduler.insert_scope_with_key(|myidx| { let caller = NodeFactory::create_component_caller(root, props_ptr as *const _); let name = type_name_of(root); - Scope::new(caller, myidx, None, 0, ScopeChildren(&[]), update_sender) + Scope::new( + caller, + myidx, + None, + 0, + ScopeChildren(&[]), + scheduler.channel.clone(), + ) }); Self { base_scope, - _root_props: root_props, - scheduler: components, - _root_prop_type: TypeId::of::

(), + _root_props, + scheduler, + _root_prop_type, } } @@ -218,15 +225,6 @@ impl VirtualDom { // .expect("this future will always resolve immediately") } - /// Runs the virtualdom with no time limit. - /// - /// If there are pending tasks, they will be progressed before returning. This is useful when rendering an application - /// that has suspended nodes or suspended tasks. Be warned - any async tasks running forever will prevent this method - /// from completing. Consider using `run` and specifing a deadline. - pub async fn run_unbounded<'s>(&'s mut self) -> Mutations<'s> { - self.run_with_deadline(async {}).await.unwrap() - } - /// Run the virtualdom with a deadline. /// /// This method will progress async tasks until the deadline is reached. If tasks are completed before the deadline, @@ -327,8 +325,8 @@ impl VirtualDom { } } - pub fn get_event_sender(&self) -> futures_channel::mpsc::UnboundedSender { - self.scheduler.ui_event_sender.clone() + pub fn get_event_sender(&self) -> futures_channel::mpsc::UnboundedSender { + self.scheduler.channel.sender.clone() } pub fn has_work(&self) -> bool { diff --git a/packages/desktop/src/events.rs b/packages/desktop/src/events.rs index 308726f51..6d4c8ddd5 100644 --- a/packages/desktop/src/events.rs +++ b/packages/desktop/src/events.rs @@ -6,7 +6,7 @@ use std::rc::Rc; use dioxus_core::{ events::{ on::{MouseEvent, MouseEventInner}, - VirtualEvent, + SyntheticEvent, }, ElementId, EventPriority, EventTrigger, ScopeId, }; @@ -21,7 +21,7 @@ pub fn trigger_from_serialized(val: serde_json::Value) -> EventTrigger { let mut data: Vec = serde_json::from_value(val).unwrap(); let data = data.drain(..).next().unwrap(); - let event = VirtualEvent::MouseEvent(MouseEvent(Rc::new(WebviewMouseEvent))); + let event = SyntheticEvent::MouseEvent(MouseEvent(Rc::new(WebviewMouseEvent))); let scope = ScopeId(data.scope as usize); let mounted_dom_id = Some(ElementId(data.mounted_dom_id as usize)); let priority = EventPriority::High; diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index d48e8d187..cb907cdc6 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, fmt::Debug, rc::Rc, sync::Arc}; use dioxus_core::{ - events::{on::GenericEventInner, EventTrigger, VirtualEvent}, + events::{on::GenericEventInner, EventTrigger, SyntheticEvent}, mutations::NodeRefMutation, DomEdit, ElementId, ScopeId, }; @@ -436,82 +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) -> SyntheticEvent { use crate::events::*; use dioxus_core::events::on::*; match event.type_().as_str() { "copy" | "cut" | "paste" => { - VirtualEvent::ClipboardEvent(ClipboardEvent(Rc::new(WebsysClipboardEvent(event)))) + SyntheticEvent::ClipboardEvent(ClipboardEvent(Rc::new(WebsysClipboardEvent(event)))) } "compositionend" | "compositionstart" | "compositionupdate" => { let evt: web_sys::CompositionEvent = event.clone().dyn_into().unwrap(); - VirtualEvent::CompositionEvent(CompositionEvent(Rc::new(WebsysCompositionEvent(evt)))) + SyntheticEvent::CompositionEvent(CompositionEvent(Rc::new(WebsysCompositionEvent(evt)))) } "keydown" | "keypress" | "keyup" => { let evt: web_sys::KeyboardEvent = event.dyn_into().unwrap(); - VirtualEvent::KeyboardEvent(KeyboardEvent(Rc::new(WebsysKeyboardEvent(evt)))) + SyntheticEvent::KeyboardEvent(KeyboardEvent(Rc::new(WebsysKeyboardEvent(evt)))) } "focus" | "blur" => { let evt: web_sys::FocusEvent = event.dyn_into().unwrap(); - VirtualEvent::FocusEvent(FocusEvent(Rc::new(WebsysFocusEvent(evt)))) + SyntheticEvent::FocusEvent(FocusEvent(Rc::new(WebsysFocusEvent(evt)))) } "change" => { let evt = event.dyn_into().unwrap(); - VirtualEvent::UIEvent(UIEvent(Rc::new(WebsysGenericUiEvent(evt)))) + SyntheticEvent::UIEvent(UIEvent(Rc::new(WebsysGenericUiEvent(evt)))) } "input" | "invalid" | "reset" | "submit" => { let evt: web_sys::InputEvent = event.clone().dyn_into().unwrap(); - VirtualEvent::FormEvent(FormEvent(Rc::new(WebsysFormEvent(evt)))) + SyntheticEvent::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(); - VirtualEvent::MouseEvent(MouseEvent(Rc::new(WebsysMouseEvent(evt)))) + SyntheticEvent::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(); - VirtualEvent::PointerEvent(PointerEvent(Rc::new(WebsysPointerEvent(evt)))) + SyntheticEvent::PointerEvent(PointerEvent(Rc::new(WebsysPointerEvent(evt)))) } "select" => { let evt: web_sys::UiEvent = event.clone().dyn_into().unwrap(); - VirtualEvent::SelectionEvent(SelectionEvent(Rc::new(WebsysGenericUiEvent(evt)))) + SyntheticEvent::SelectionEvent(SelectionEvent(Rc::new(WebsysGenericUiEvent(evt)))) } "touchcancel" | "touchend" | "touchmove" | "touchstart" => { let evt: web_sys::TouchEvent = event.clone().dyn_into().unwrap(); - VirtualEvent::TouchEvent(TouchEvent(Rc::new(WebsysTouchEvent(evt)))) + SyntheticEvent::TouchEvent(TouchEvent(Rc::new(WebsysTouchEvent(evt)))) } "scroll" => { let evt: web_sys::UiEvent = event.clone().dyn_into().unwrap(); - VirtualEvent::UIEvent(UIEvent(Rc::new(WebsysGenericUiEvent(evt)))) + SyntheticEvent::UIEvent(UIEvent(Rc::new(WebsysGenericUiEvent(evt)))) } "wheel" => { let evt: web_sys::WheelEvent = event.clone().dyn_into().unwrap(); - VirtualEvent::WheelEvent(WheelEvent(Rc::new(WebsysWheelEvent(evt)))) + SyntheticEvent::WheelEvent(WheelEvent(Rc::new(WebsysWheelEvent(evt)))) } "animationstart" | "animationend" | "animationiteration" => { let evt: web_sys::AnimationEvent = event.clone().dyn_into().unwrap(); - VirtualEvent::AnimationEvent(AnimationEvent(Rc::new(WebsysAnimationEvent(evt)))) + SyntheticEvent::AnimationEvent(AnimationEvent(Rc::new(WebsysAnimationEvent(evt)))) } "transitionend" => { let evt: web_sys::TransitionEvent = event.clone().dyn_into().unwrap(); - VirtualEvent::TransitionEvent(TransitionEvent(Rc::new(WebsysTransitionEvent(evt)))) + SyntheticEvent::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" => { let evt: web_sys::UiEvent = event.clone().dyn_into().unwrap(); - VirtualEvent::MediaEvent(MediaEvent(Rc::new(WebsysMediaEvent(evt)))) + SyntheticEvent::MediaEvent(MediaEvent(Rc::new(WebsysMediaEvent(evt)))) } "toggle" => { let evt: web_sys::UiEvent = event.clone().dyn_into().unwrap(); - VirtualEvent::ToggleEvent(ToggleEvent(Rc::new(WebsysToggleEvent(evt)))) + SyntheticEvent::ToggleEvent(ToggleEvent(Rc::new(WebsysToggleEvent(evt)))) } _ => { let evt: web_sys::UiEvent = event.clone().dyn_into().unwrap(); - VirtualEvent::UIEvent(UIEvent(Rc::new(WebsysGenericUiEvent(evt)))) + SyntheticEvent::UIEvent(UIEvent(Rc::new(WebsysGenericUiEvent(evt)))) } } }