wip: more overhaul on virtualevents

This commit is contained in:
Jonathan Kelley 2021-08-25 16:40:18 -04:00
parent cef116aa3a
commit 41cc42919d
10 changed files with 165 additions and 181 deletions

View file

@ -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);
});

View file

@ -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<ElementId>,
/// 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<dyn FnMut(VirtualEvent) + 'a> = unsafe { BumpBox::from_raw(cb) };
let callback: BumpBox<dyn FnMut(SyntheticEvent) + 'a> = unsafe { BumpBox::from_raw(cb) };
let event_name = stringify!($name);
let shortname: &'static str = &event_name[2..];

View file

@ -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<Option<Box<dyn Any>>> = slot.as_ref().borrow();
match v.as_ref() {
Some(a) => {
let v: &dyn Any = a.as_ref();
let real_val = v.downcast_ref::<Out>().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<dyn Any>);
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<dyn Any>);
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::<Out>().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::<Out>()
.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<RefCell<Option<Box<dyn Any>>>>,
pub callback: SuspendedCallback,
pub dom_node_id: Rc<Cell<Option<ElementId>>>,
}
type SuspendedCallback = Box<dyn for<'a> Fn(SuspendedContext<'a>) -> DomTree<'a>>;
pub struct SuspendedContext<'a> {

View file

@ -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 {

View file

@ -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<Option<ElementId>>,
pub(crate) callback: RefCell<Option<BumpBox<'bump, dyn FnMut(VirtualEvent) + 'bump>>>,
pub(crate) callback: RefCell<Option<BumpBox<'bump, dyn FnMut(SyntheticEvent) + 'bump>>>,
}
impl Listener<'_> {
@ -168,8 +169,10 @@ pub struct VComponent<'src> {
pub(crate) user_fc: *const (),
}
pub struct VSuspended {
pub node: Rc<Cell<Option<ElementId>>>,
pub struct VSuspended<'a> {
pub dom_id: Cell<Option<ElementId>>,
pub task_id: u64,
pub callback: RefCell<Option<&'a dyn FnOnce(SuspendedContext<'a>) -> 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,

View file

@ -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 {

View file

@ -40,6 +40,8 @@ pub struct Scope {
pub(crate) listeners: RefCell<Vec<*const Listener<'static>>>,
pub(crate) borrowed_props: RefCell<Vec<*const VComponent<'static>>>,
pub(crate) suspended_nodes: RefCell<HashMap<u64, *const VNode<'static>>>,
// State
pub(crate) hooks: HookList,
pub(crate) shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
@ -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<Box<dyn Future<Output = EventTrigger>>>;
/// 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<Box<dyn Future<Output = ScopeId>>>;
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| {

View file

@ -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<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
let components = Scheduler::new();
let scheduler = Scheduler::new();
let root_props: Pin<Box<dyn Any>> = Box::pin(root_props);
let props_ptr = root_props.as_ref().downcast_ref::<P>().unwrap() as *const P;
let _root_props: Pin<Box<dyn Any>> = Box::pin(root_props);
let _root_prop_type = TypeId::of::<P>();
let update_sender = components.immediate_sender.clone();
let props_ptr = _root_props.as_ref().downcast_ref::<P>().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::<P>(),
_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<EventTrigger> {
self.scheduler.ui_event_sender.clone()
pub fn get_event_sender(&self) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> {
self.scheduler.channel.sender.clone()
}
pub fn has_work(&self) -> bool {

View file

@ -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<ImEvent> = 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;

View file

@ -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))))
}
}
}