wip: more overhaul on virtualevents
This commit is contained in:
1 parent
cef116aa3a
commit
41cc42919d
10 files changed
+165
-181
No files matched your search
@@ -251,7 +251,7 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
fn create_suspended_node(&mut self, suspended: &'bump VSuspended) {
|
fn create_suspended_node(&mut self, suspended: &'bump VSuspended) {
|
||||||
let real_id = self.vdom.reserve_node();
|
let real_id = self.vdom.reserve_node();
|
||||||
self.mutations.create_placeholder(real_id);
|
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);
|
self.stack.add_child_count(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,7 +372,7 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
(Component(old), Component(new)) => self.diff_component_nodes(old, new),
|
(Component(old), Component(new)) => self.diff_component_nodes(old, new),
|
||||||
(Fragment(old), Fragment(new)) => self.diff_fragment_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()),
|
(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),
|
(Element(old), Element(new)) => self.diff_element_nodes(old, new),
|
||||||
|
|
||||||
// Anything else is just a basic replace and create
|
// Anything else is just a basic replace and create
|
||||||
@@ -952,7 +952,7 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
match &search_node.take().unwrap() {
|
match &search_node.take().unwrap() {
|
||||||
VNode::Text(t) => break t.dom_id.get(),
|
VNode::Text(t) => break t.dom_id.get(),
|
||||||
VNode::Element(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::Anchor(t) => break t.dom_id.get(),
|
||||||
|
|
||||||
VNode::Fragment(frag) => {
|
VNode::Fragment(frag) => {
|
||||||
@@ -983,7 +983,7 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
}
|
}
|
||||||
VNode::Text(t) => break t.dom_id.get(),
|
VNode::Text(t) => break t.dom_id.get(),
|
||||||
VNode::Element(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::Anchor(t) => break t.dom_id.get(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1016,7 +1016,7 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
VNode::Suspended(s) => {
|
VNode::Suspended(s) => {
|
||||||
s.node.get().map(|id| {
|
s.dom_id.get().map(|id| {
|
||||||
self.mutations.remove(id.as_u64());
|
self.mutations.remove(id.as_u64());
|
||||||
self.vdom.collect_garbage(id);
|
self.vdom.collect_garbage(id);
|
||||||
});
|
});
|
||||||
|
|||||||
+27
-33
@@ -3,16 +3,7 @@
|
|||||||
//!
|
//!
|
||||||
//! 3rd party renderers are responsible for converting their native events into these virtual event types. Events might
|
//! 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.
|
//! be heavy or need to interact through FFI, so the events themselves are designed to be lazy.
|
||||||
|
use crate::innerlude::{ElementId, ScopeId};
|
||||||
use std::{
|
|
||||||
cell::{Cell, RefCell},
|
|
||||||
rc::Rc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
innerlude::{ElementId, ScopeId},
|
|
||||||
VNode,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct EventTrigger {
|
pub struct EventTrigger {
|
||||||
@@ -23,12 +14,15 @@ pub struct EventTrigger {
|
|||||||
pub mounted_dom_id: Option<ElementId>,
|
pub mounted_dom_id: Option<ElementId>,
|
||||||
|
|
||||||
/// The type of event
|
/// 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 discrete: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum VirtualEvent {
|
pub enum SyntheticEvent {
|
||||||
KeyboardEvent(on::KeyboardEvent),
|
KeyboardEvent(on::KeyboardEvent),
|
||||||
TouchEvent(on::TouchEvent),
|
TouchEvent(on::TouchEvent),
|
||||||
MouseEvent(on::MouseEvent),
|
MouseEvent(on::MouseEvent),
|
||||||
@@ -47,24 +41,24 @@ pub enum VirtualEvent {
|
|||||||
// ImageEvent(event_data::ImageEvent),
|
// 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 {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let name = match self {
|
let name = match self {
|
||||||
VirtualEvent::ClipboardEvent(_) => "ClipboardEvent",
|
SyntheticEvent::ClipboardEvent(_) => "ClipboardEvent",
|
||||||
VirtualEvent::CompositionEvent(_) => "CompositionEvent",
|
SyntheticEvent::CompositionEvent(_) => "CompositionEvent",
|
||||||
VirtualEvent::KeyboardEvent(_) => "KeyboardEvent",
|
SyntheticEvent::KeyboardEvent(_) => "KeyboardEvent",
|
||||||
VirtualEvent::FocusEvent(_) => "FocusEvent",
|
SyntheticEvent::FocusEvent(_) => "FocusEvent",
|
||||||
VirtualEvent::FormEvent(_) => "FormEvent",
|
SyntheticEvent::FormEvent(_) => "FormEvent",
|
||||||
VirtualEvent::SelectionEvent(_) => "SelectionEvent",
|
SyntheticEvent::SelectionEvent(_) => "SelectionEvent",
|
||||||
VirtualEvent::TouchEvent(_) => "TouchEvent",
|
SyntheticEvent::TouchEvent(_) => "TouchEvent",
|
||||||
VirtualEvent::UIEvent(_) => "UIEvent",
|
SyntheticEvent::UIEvent(_) => "UIEvent",
|
||||||
VirtualEvent::WheelEvent(_) => "WheelEvent",
|
SyntheticEvent::WheelEvent(_) => "WheelEvent",
|
||||||
VirtualEvent::MediaEvent(_) => "MediaEvent",
|
SyntheticEvent::MediaEvent(_) => "MediaEvent",
|
||||||
VirtualEvent::AnimationEvent(_) => "AnimationEvent",
|
SyntheticEvent::AnimationEvent(_) => "AnimationEvent",
|
||||||
VirtualEvent::TransitionEvent(_) => "TransitionEvent",
|
SyntheticEvent::TransitionEvent(_) => "TransitionEvent",
|
||||||
VirtualEvent::ToggleEvent(_) => "ToggleEvent",
|
SyntheticEvent::ToggleEvent(_) => "ToggleEvent",
|
||||||
VirtualEvent::MouseEvent(_) => "MouseEvent",
|
SyntheticEvent::MouseEvent(_) => "MouseEvent",
|
||||||
VirtualEvent::PointerEvent(_) => "PointerEvent",
|
SyntheticEvent::PointerEvent(_) => "PointerEvent",
|
||||||
};
|
};
|
||||||
|
|
||||||
f.debug_struct("VirtualEvent").field("type", &name).finish()
|
f.debug_struct("VirtualEvent").field("type", &name).finish()
|
||||||
@@ -90,7 +84,7 @@ pub mod on {
|
|||||||
};
|
};
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
|
||||||
use super::VirtualEvent;
|
use super::SyntheticEvent;
|
||||||
|
|
||||||
macro_rules! event_directory {
|
macro_rules! event_directory {
|
||||||
( $(
|
( $(
|
||||||
@@ -126,12 +120,12 @@ pub mod on {
|
|||||||
{
|
{
|
||||||
let bump = &c.bump();
|
let bump = &c.bump();
|
||||||
|
|
||||||
let cb: &mut dyn FnMut(VirtualEvent) = bump.alloc(move |evt: VirtualEvent| match evt {
|
let cb: &mut dyn FnMut(SyntheticEvent) = bump.alloc(move |evt: SyntheticEvent| match evt {
|
||||||
VirtualEvent::$wrapper(event) => callback(event),
|
SyntheticEvent::$wrapper(event) => callback(event),
|
||||||
_ => unreachable!("Downcasted VirtualEvent to wrong event type - this is an internal bug!")
|
_ => 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 event_name = stringify!($name);
|
||||||
let shortname: &'static str = &event_name[2..];
|
let shortname: &'static str = &event_name[2..];
|
||||||
|
|||||||
+54
-57
@@ -126,14 +126,7 @@ where
|
|||||||
let handle = cx.submit_task(Box::pin(task_fut.then(move |output| async move {
|
let handle = cx.submit_task(Box::pin(task_fut.then(move |output| async move {
|
||||||
*slot.as_ref().borrow_mut() = Some(output);
|
*slot.as_ref().borrow_mut() = Some(output);
|
||||||
updater(update_id);
|
updater(update_id);
|
||||||
EventTrigger {
|
originator
|
||||||
event: VirtualEvent::AsyncEvent {
|
|
||||||
should_rerender: false,
|
|
||||||
},
|
|
||||||
scope: originator,
|
|
||||||
priority: EventPriority::Low,
|
|
||||||
mounted_dom_id: None,
|
|
||||||
}
|
|
||||||
})));
|
})));
|
||||||
|
|
||||||
TaskHook {
|
TaskHook {
|
||||||
@@ -172,71 +165,75 @@ where
|
|||||||
Out: 'static,
|
Out: 'static,
|
||||||
Cb: for<'a> Fn(SuspendedContext<'a>, &Out) -> DomTree<'a> + '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(
|
cx.use_hook(
|
||||||
move |hook_idx| {
|
move |hook_idx| {
|
||||||
let value = Rc::new(RefCell::new(None));
|
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 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 originator = cx.scope.our_arena_idx.clone();
|
||||||
let task_fut = task_initializer();
|
|
||||||
let domnode = dom_node_id.clone();
|
|
||||||
|
|
||||||
let slot = value.clone();
|
let handle = cx.submit_task(Box::pin(task_initializer().then(
|
||||||
cx.submit_task(Box::pin(task_fut.then(move |output| async move {
|
move |output| async move {
|
||||||
// When the new value arrives, set the hooks internal slot
|
*slot.borrow_mut() = Some(Box::new(output) as Box<dyn Any>);
|
||||||
// Dioxus will call the user's callback to generate new nodes outside of the diffing system
|
originator
|
||||||
*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,
|
|
||||||
}
|
|
||||||
})));
|
|
||||||
|
|
||||||
SuspenseHook {
|
SuspenseHook { handle, value }
|
||||||
value,
|
|
||||||
callback,
|
|
||||||
dom_node_id,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
move |hook| {
|
move |hook| match hook.value.borrow().as_ref() {
|
||||||
let cx = Context {
|
Some(value) => {
|
||||||
scope: &cx.scope,
|
let out = value.downcast_ref::<Out>().unwrap();
|
||||||
props: &(),
|
let sus = SuspendedContext {
|
||||||
};
|
inner: Context {
|
||||||
let csx = SuspendedContext { inner: cx };
|
props: &(),
|
||||||
(&hook.callback)(csx)
|
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(crate) struct SuspenseHook {
|
||||||
|
pub handle: TaskHandle,
|
||||||
pub value: Rc<RefCell<Option<Box<dyn Any>>>>,
|
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>>;
|
type SuspendedCallback = Box<dyn for<'a> Fn(SuspendedContext<'a>) -> DomTree<'a>>;
|
||||||
pub struct SuspendedContext<'a> {
|
pub struct SuspendedContext<'a> {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
pub use crate::innerlude::{
|
pub use crate::innerlude::{
|
||||||
format_args_f, html, rsx, Context, DiffInstruction, DioxusElement, DomEdit, DomTree, ElementId,
|
format_args_f, html, rsx, Context, DiffInstruction, DioxusElement, DomEdit, DomTree, ElementId,
|
||||||
EventPriority, EventTrigger, LazyNodes, MountType, Mutations, NodeFactory, Properties, ScopeId,
|
EventPriority, EventTrigger, LazyNodes, MountType, Mutations, NodeFactory, Properties, ScopeId,
|
||||||
SuspendedContext, VNode, VirtualDom, VirtualEvent, FC,
|
SuspendedContext, SyntheticEvent, VNode, VirtualDom, FC,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
//!
|
//!
|
||||||
//! These VNodes should be *very* cheap and *very* fast to construct - building a full tree should be insanely quick.
|
//! These VNodes should be *very* cheap and *very* fast to construct - building a full tree should be insanely quick.
|
||||||
use crate::{
|
use crate::{
|
||||||
events::VirtualEvent,
|
events::SyntheticEvent,
|
||||||
innerlude::{empty_cell, Context, DomTree, ElementId, Properties, Scope, ScopeId, FC},
|
innerlude::{empty_cell, Context, DomTree, ElementId, Properties, Scope, ScopeId, FC},
|
||||||
|
SuspendedContext,
|
||||||
};
|
};
|
||||||
use bumpalo::{boxed::Box as BumpBox, Bump};
|
use bumpalo::{boxed::Box as BumpBox, Bump};
|
||||||
use std::{
|
use std::{
|
||||||
@@ -30,7 +31,7 @@ pub enum VNode<'src> {
|
|||||||
|
|
||||||
Component(&'src VComponent<'src>),
|
Component(&'src VComponent<'src>),
|
||||||
|
|
||||||
Suspended(VSuspended),
|
Suspended(&'src VSuspended<'src>),
|
||||||
|
|
||||||
Anchor(VAnchor),
|
Anchor(VAnchor),
|
||||||
}
|
}
|
||||||
@@ -127,7 +128,7 @@ pub struct Listener<'bump> {
|
|||||||
|
|
||||||
pub mounted_node: Cell<Option<ElementId>>,
|
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<'_> {
|
impl Listener<'_> {
|
||||||
@@ -168,8 +169,10 @@ pub struct VComponent<'src> {
|
|||||||
pub(crate) user_fc: *const (),
|
pub(crate) user_fc: *const (),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct VSuspended {
|
pub struct VSuspended<'a> {
|
||||||
pub node: Rc<Cell<Option<ElementId>>>,
|
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.
|
/// 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(
|
pub fn attr(
|
||||||
&self,
|
&self,
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ pub struct EventChannel {
|
|||||||
|
|
||||||
pub enum SchedulerMsg {
|
pub enum SchedulerMsg {
|
||||||
Immediate(ScopeId),
|
Immediate(ScopeId),
|
||||||
UiEvent(),
|
UiEvent(EventTrigger),
|
||||||
SubmitTask(u64),
|
SubmitTask(u64),
|
||||||
ToggleTask(u64),
|
ToggleTask(u64),
|
||||||
PauseTask(u64),
|
PauseTask(u64),
|
||||||
@@ -232,16 +232,6 @@ impl Scheduler {
|
|||||||
id
|
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) {
|
pub fn clean_up_garbage(&mut self) {
|
||||||
let mut scopes_to_kill = Vec::new();
|
let mut scopes_to_kill = Vec::new();
|
||||||
let mut garbage_list = Vec::new();
|
let mut garbage_list = Vec::new();
|
||||||
@@ -330,11 +320,11 @@ impl Scheduler {
|
|||||||
pub fn consume_pending_events(&mut self) -> Result<()> {
|
pub fn consume_pending_events(&mut self) -> Result<()> {
|
||||||
while let Some(trigger) = self.pending_events.pop_back() {
|
while let Some(trigger) = self.pending_events.pop_back() {
|
||||||
match &trigger.event {
|
match &trigger.event {
|
||||||
VirtualEvent::AsyncEvent { .. } => {}
|
SyntheticEvent::AsyncEvent { .. } => {}
|
||||||
|
|
||||||
// This suspense system works, but it's not the most elegant solution.
|
// This suspense system works, but it's not the most elegant solution.
|
||||||
// TODO: Replace this system
|
// TODO: Replace this system
|
||||||
VirtualEvent::SuspenseEvent { hook_idx, domnode } => {
|
SyntheticEvent::SuspenseEvent { hook_idx, domnode } => {
|
||||||
todo!("suspense needs to be converted into its own channel");
|
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
|
// // 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(_)
|
SyntheticEvent::ClipboardEvent(_)
|
||||||
| VirtualEvent::CompositionEvent(_)
|
| SyntheticEvent::CompositionEvent(_)
|
||||||
| VirtualEvent::KeyboardEvent(_)
|
| SyntheticEvent::KeyboardEvent(_)
|
||||||
| VirtualEvent::FocusEvent(_)
|
| SyntheticEvent::FocusEvent(_)
|
||||||
| VirtualEvent::FormEvent(_)
|
| SyntheticEvent::FormEvent(_)
|
||||||
| VirtualEvent::SelectionEvent(_)
|
| SyntheticEvent::SelectionEvent(_)
|
||||||
| VirtualEvent::TouchEvent(_)
|
| SyntheticEvent::TouchEvent(_)
|
||||||
| VirtualEvent::UIEvent(_)
|
| SyntheticEvent::UIEvent(_)
|
||||||
| VirtualEvent::WheelEvent(_)
|
| SyntheticEvent::WheelEvent(_)
|
||||||
| VirtualEvent::MediaEvent(_)
|
| SyntheticEvent::MediaEvent(_)
|
||||||
| VirtualEvent::AnimationEvent(_)
|
| SyntheticEvent::AnimationEvent(_)
|
||||||
| VirtualEvent::TransitionEvent(_)
|
| SyntheticEvent::TransitionEvent(_)
|
||||||
| VirtualEvent::ToggleEvent(_)
|
| SyntheticEvent::ToggleEvent(_)
|
||||||
| VirtualEvent::MouseEvent(_)
|
| SyntheticEvent::MouseEvent(_)
|
||||||
| VirtualEvent::PointerEvent(_) => {
|
| SyntheticEvent::PointerEvent(_) => {
|
||||||
if let Some(scope) = self.get_scope_mut(trigger.scope) {
|
if let Some(scope) = self.get_scope_mut(trigger.scope) {
|
||||||
if let Some(element) = trigger.mounted_dom_id {
|
if let Some(element) = trigger.mounted_dom_id {
|
||||||
scope.call_listener(trigger.event, element)?;
|
scope.call_listener(trigger.event, element)?;
|
||||||
@@ -528,8 +518,8 @@ impl Scheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct TaskHandle {
|
pub struct TaskHandle {
|
||||||
channel: EventChannel,
|
pub channel: EventChannel,
|
||||||
our_id: u64,
|
pub our_id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TaskHandle {
|
impl TaskHandle {
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ pub struct Scope {
|
|||||||
pub(crate) listeners: RefCell<Vec<*const Listener<'static>>>,
|
pub(crate) listeners: RefCell<Vec<*const Listener<'static>>>,
|
||||||
pub(crate) borrowed_props: RefCell<Vec<*const VComponent<'static>>>,
|
pub(crate) borrowed_props: RefCell<Vec<*const VComponent<'static>>>,
|
||||||
|
|
||||||
|
pub(crate) suspended_nodes: RefCell<HashMap<u64, *const VNode<'static>>>,
|
||||||
|
|
||||||
// State
|
// State
|
||||||
pub(crate) hooks: HookList,
|
pub(crate) hooks: HookList,
|
||||||
pub(crate) shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
|
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
|
// The type of closure that wraps calling components
|
||||||
pub type WrappedCaller = dyn for<'b> Fn(&'b Scope) -> DomTree<'b>;
|
pub type WrappedCaller = dyn for<'b> Fn(&'b Scope) -> DomTree<'b>;
|
||||||
|
|
||||||
// The type of task that gets sent to the task scheduler
|
/// The type of task that gets sent to the task scheduler
|
||||||
pub type FiberTask = Pin<Box<dyn Future<Output = EventTrigger>>>;
|
/// 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 {
|
impl Scope {
|
||||||
// we are being created in the scope of an existing component (where the creator_node lifetime comes into play)
|
// 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,
|
height,
|
||||||
frames: ActiveFrame::new(),
|
frames: ActiveFrame::new(),
|
||||||
hooks: Default::default(),
|
hooks: Default::default(),
|
||||||
|
suspended_nodes: Default::default(),
|
||||||
shared_contexts: Default::default(),
|
shared_contexts: Default::default(),
|
||||||
listeners: Default::default(),
|
listeners: Default::default(),
|
||||||
borrowed_props: Default::default(),
|
borrowed_props: Default::default(),
|
||||||
@@ -194,7 +198,11 @@ impl Scope {
|
|||||||
// A safe wrapper around calling listeners
|
// 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 listners = self.listeners.borrow_mut();
|
||||||
|
|
||||||
let raw_listener = listners.iter().find(|lis| {
|
let raw_listener = listners.iter().find(|lis| {
|
||||||
|
|||||||
@@ -100,24 +100,31 @@ impl VirtualDom {
|
|||||||
///
|
///
|
||||||
/// Note: the VirtualDOM is not progressed, you must either "run_with_deadline" or use "rebuild" to progress it.
|
/// 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 {
|
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 _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_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 caller = NodeFactory::create_component_caller(root, props_ptr as *const _);
|
||||||
let name = type_name_of(root);
|
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 {
|
Self {
|
||||||
base_scope,
|
base_scope,
|
||||||
_root_props: root_props,
|
_root_props,
|
||||||
scheduler: components,
|
scheduler,
|
||||||
_root_prop_type: TypeId::of::<P>(),
|
_root_prop_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,15 +225,6 @@ impl VirtualDom {
|
|||||||
// .expect("this future will always resolve immediately")
|
// .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.
|
/// Run the virtualdom with a deadline.
|
||||||
///
|
///
|
||||||
/// This method will progress async tasks until the deadline is reached. If tasks are completed before the 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> {
|
pub fn get_event_sender(&self) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> {
|
||||||
self.scheduler.ui_event_sender.clone()
|
self.scheduler.channel.sender.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_work(&self) -> bool {
|
pub fn has_work(&self) -> bool {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use std::rc::Rc;
|
|||||||
use dioxus_core::{
|
use dioxus_core::{
|
||||||
events::{
|
events::{
|
||||||
on::{MouseEvent, MouseEventInner},
|
on::{MouseEvent, MouseEventInner},
|
||||||
VirtualEvent,
|
SyntheticEvent,
|
||||||
},
|
},
|
||||||
ElementId, EventPriority, EventTrigger, ScopeId,
|
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 mut data: Vec<ImEvent> = serde_json::from_value(val).unwrap();
|
||||||
let data = data.drain(..).next().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 scope = ScopeId(data.scope as usize);
|
||||||
let mounted_dom_id = Some(ElementId(data.mounted_dom_id as usize));
|
let mounted_dom_id = Some(ElementId(data.mounted_dom_id as usize));
|
||||||
let priority = EventPriority::High;
|
let priority = EventPriority::High;
|
||||||
|
|||||||
+19
-19
@@ -1,7 +1,7 @@
|
|||||||
use std::{collections::HashMap, fmt::Debug, rc::Rc, sync::Arc};
|
use std::{collections::HashMap, fmt::Debug, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
use dioxus_core::{
|
use dioxus_core::{
|
||||||
events::{on::GenericEventInner, EventTrigger, VirtualEvent},
|
events::{on::GenericEventInner, EventTrigger, SyntheticEvent},
|
||||||
mutations::NodeRefMutation,
|
mutations::NodeRefMutation,
|
||||||
DomEdit, ElementId, ScopeId,
|
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 crate::events::*;
|
||||||
use dioxus_core::events::on::*;
|
use dioxus_core::events::on::*;
|
||||||
match event.type_().as_str() {
|
match event.type_().as_str() {
|
||||||
"copy" | "cut" | "paste" => {
|
"copy" | "cut" | "paste" => {
|
||||||
VirtualEvent::ClipboardEvent(ClipboardEvent(Rc::new(WebsysClipboardEvent(event))))
|
SyntheticEvent::ClipboardEvent(ClipboardEvent(Rc::new(WebsysClipboardEvent(event))))
|
||||||
}
|
}
|
||||||
"compositionend" | "compositionstart" | "compositionupdate" => {
|
"compositionend" | "compositionstart" | "compositionupdate" => {
|
||||||
let evt: web_sys::CompositionEvent = event.clone().dyn_into().unwrap();
|
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" => {
|
"keydown" | "keypress" | "keyup" => {
|
||||||
let evt: web_sys::KeyboardEvent = event.dyn_into().unwrap();
|
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" => {
|
"focus" | "blur" => {
|
||||||
let evt: web_sys::FocusEvent = event.dyn_into().unwrap();
|
let evt: web_sys::FocusEvent = event.dyn_into().unwrap();
|
||||||
VirtualEvent::FocusEvent(FocusEvent(Rc::new(WebsysFocusEvent(evt))))
|
SyntheticEvent::FocusEvent(FocusEvent(Rc::new(WebsysFocusEvent(evt))))
|
||||||
}
|
}
|
||||||
"change" => {
|
"change" => {
|
||||||
let evt = event.dyn_into().unwrap();
|
let evt = event.dyn_into().unwrap();
|
||||||
VirtualEvent::UIEvent(UIEvent(Rc::new(WebsysGenericUiEvent(evt))))
|
SyntheticEvent::UIEvent(UIEvent(Rc::new(WebsysGenericUiEvent(evt))))
|
||||||
}
|
}
|
||||||
"input" | "invalid" | "reset" | "submit" => {
|
"input" | "invalid" | "reset" | "submit" => {
|
||||||
let evt: web_sys::InputEvent = event.clone().dyn_into().unwrap();
|
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"
|
"click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
|
||||||
| "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
|
| "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
|
||||||
| "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
|
| "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
|
||||||
let evt: web_sys::MouseEvent = event.clone().dyn_into().unwrap();
|
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"
|
"pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
|
||||||
| "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
|
| "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
|
||||||
let evt: web_sys::PointerEvent = event.clone().dyn_into().unwrap();
|
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" => {
|
"select" => {
|
||||||
let evt: web_sys::UiEvent = event.clone().dyn_into().unwrap();
|
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" => {
|
"touchcancel" | "touchend" | "touchmove" | "touchstart" => {
|
||||||
let evt: web_sys::TouchEvent = event.clone().dyn_into().unwrap();
|
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" => {
|
"scroll" => {
|
||||||
let evt: web_sys::UiEvent = event.clone().dyn_into().unwrap();
|
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" => {
|
"wheel" => {
|
||||||
let evt: web_sys::WheelEvent = event.clone().dyn_into().unwrap();
|
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" => {
|
"animationstart" | "animationend" | "animationiteration" => {
|
||||||
let evt: web_sys::AnimationEvent = event.clone().dyn_into().unwrap();
|
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" => {
|
"transitionend" => {
|
||||||
let evt: web_sys::TransitionEvent = event.clone().dyn_into().unwrap();
|
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"
|
"abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
|
||||||
| "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
|
| "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
|
||||||
| "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
|
| "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
|
||||||
| "timeupdate" | "volumechange" | "waiting" => {
|
| "timeupdate" | "volumechange" | "waiting" => {
|
||||||
let evt: web_sys::UiEvent = event.clone().dyn_into().unwrap();
|
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" => {
|
"toggle" => {
|
||||||
let evt: web_sys::UiEvent = event.clone().dyn_into().unwrap();
|
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();
|
let evt: web_sys::UiEvent = event.clone().dyn_into().unwrap();
|
||||||
VirtualEvent::UIEvent(UIEvent(Rc::new(WebsysGenericUiEvent(evt))))
|
SyntheticEvent::UIEvent(UIEvent(Rc::new(WebsysGenericUiEvent(evt))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user