Feat: wire up rebuild

This commit is contained in:
Jonathan Kelley committed 2021-02-24 03:51:26 -05:00
1 parent 4d01455729
commit 06ae4fc178
11 files changed
+209 -169

No files matched your search

+1 -1
View File
@@ -1,3 +1,3 @@
{ {
"rust-analyzer.inlayHints.enable": false "rust-analyzer.inlayHints.enable": true
} }
+1 -3
View File
@@ -1,6 +1,4 @@
use builder::button;
use builder::{button};
use dioxus_core::prelude::*; use dioxus_core::prelude::*;
fn main() {} fn main() {}
+11 -9
View File
@@ -1,10 +1,11 @@
use crate::nodes::VNode;
use crate::prelude::*; use crate::prelude::*;
use crate::{nodes::VNode};
use bumpalo::Bump; use bumpalo::Bump;
use hooks::Hook; use hooks::Hook;
use log::info;
use std::{ use std::{
any::TypeId, borrow::Borrow, cell::RefCell, future::Future, marker::PhantomData, any::TypeId, borrow::Borrow, cell::RefCell, future::Future, marker::PhantomData, ops::Deref,
sync::atomic::AtomicUsize, rc::Rc, sync::atomic::AtomicUsize,
}; };
/// Components in Dioxus use the "Context" object to interact with their lifecycle. /// Components in Dioxus use the "Context" object to interact with their lifecycle.
@@ -35,6 +36,8 @@ pub struct Context<'src> {
pub(crate) hooks: &'src RefCell<Vec<*mut Hook>>, pub(crate) hooks: &'src RefCell<Vec<*mut Hook>>,
pub(crate) bump: &'src Bump, pub(crate) bump: &'src Bump,
pub(crate) final_nodes: Rc<RefCell<Option<VNode<'static>>>>,
// holder for the src lifetime // holder for the src lifetime
// todo @jon remove this // todo @jon remove this
pub _p: std::marker::PhantomData<&'src ()>, pub _p: std::marker::PhantomData<&'src ()>,
@@ -73,10 +76,10 @@ impl<'a> Context<'a> {
/// } /// }
///``` ///```
pub fn view(self, lazy_nodes: impl FnOnce(&'a Bump) -> VNode<'a> + 'a) -> DomTree { pub fn view(self, lazy_nodes: impl FnOnce(&'a Bump) -> VNode<'a> + 'a) -> DomTree {
// pub fn view(self, lazy_nodes: impl for<'b> FnOnce(&'b Bump) -> VNode<'b> + 'a + 'p) -> DomTree { let safe_nodes = lazy_nodes(self.bump);
// pub fn view<'p>(self, lazy_nodes: impl FnOnce(&'a Bump) -> VNode<'a> + 'a + 'p) -> DomTree { let unsafe_nodes = unsafe { std::mem::transmute::<VNode<'a>, VNode<'static>>(safe_nodes) };
// pub fn view(self, lazy_nodes: impl FnOnce(&'a Bump) -> VNode<'a> + 'a) -> VNode<'a> { self.final_nodes.deref().borrow_mut().replace(unsafe_nodes);
let _g = lazy_nodes(self.bump); info!("lazy nodes have been generated");
DomTree {} DomTree {}
} }
@@ -187,8 +190,7 @@ mod context_api {
//! //!
//! //!
use std::ops::Deref;
use std::{ops::Deref};
pub struct RemoteState<T> { pub struct RemoteState<T> {
inner: *const T, inner: *const T,
+36 -25
View File
@@ -16,34 +16,45 @@ impl EventTrigger {
pub fn new() -> Self { pub fn new() -> Self {
todo!() todo!()
} }
/// Create a new "start" event that boots up the virtual dom if it is paused
pub fn start_event() -> Self {
todo!()
}
} }
pub enum VirtualEvent { pub enum VirtualEvent {
// the event to drain the current lifecycle queue
// Used to initate the dom
StartEvent,
// Real events // Real events
ClipboardEvent, ClipboardEvent(ClipboardEvent),
CompositionEvent, CompositionEvent(CompositionEvent),
KeyboardEvent, KeyboardEvent(KeyboardEvent),
FocusEvent, FocusEvent(FocusEvent),
FormEvent, FormEvent(FormEvent),
GenericEvent, GenericEvent(GenericEvent),
MouseEvent, MouseEvent(MouseEvent),
PointerEvent, PointerEvent(PointerEvent),
SelectionEvent, SelectionEvent(SelectionEvent),
TouchEvent, TouchEvent(TouchEvent),
UIEvent, UIEvent(UIEvent),
WheelEvent, WheelEvent(WheelEvent),
MediaEvent, MediaEvent(MediaEvent),
ImageEvent, ImageEvent(ImageEvent),
AnimationEvent, AnimationEvent(AnimationEvent),
TransitionEvent, TransitionEvent(TransitionEvent),
OtherEvent, OtherEvent,
} }
// these should reference the underlying event
pub struct ClipboardEvent {}
pub struct CompositionEvent {}
pub struct KeyboardEvent {}
pub struct FocusEvent {}
pub struct FormEvent {}
pub struct GenericEvent {}
pub struct MouseEvent {}
pub struct PointerEvent {}
pub struct SelectionEvent {}
pub struct TouchEvent {}
pub struct UIEvent {}
pub struct WheelEvent {}
pub struct MediaEvent {}
pub struct ImageEvent {}
pub struct AnimationEvent {}
pub struct TransitionEvent {}
+1 -3
View File
@@ -101,9 +101,7 @@ pub(crate) mod innerlude {
pub type FC<P> = for<'scope> fn(Context<'scope>, &'scope P) -> DomTree; pub type FC<P> = for<'scope> fn(Context<'scope>, &'scope P) -> DomTree;
mod fc2 { mod fc2 {}
}
// pub type FC<'a, P: 'a> = for<'scope> fn(Context<'scope>, &'scope P) -> DomTree; // pub type FC<'a, P: 'a> = for<'scope> fn(Context<'scope>, &'scope P) -> DomTree;
// pub type FC<P> = for<'scope, 'r> fn(Context<'scope>, &'scope P) -> DomTree; // pub type FC<P> = for<'scope, 'r> fn(Context<'scope>, &'scope P) -> DomTree;
// pub type FC<P> = for<'scope, 'r> fn(Context<'scope>, &'r P) -> VNode<'scope>; // pub type FC<P> = for<'scope, 'r> fn(Context<'scope>, &'r P) -> VNode<'scope>;
+65 -67
View File
@@ -12,6 +12,7 @@ use std::{
marker::PhantomData, marker::PhantomData,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
sync::atomic::AtomicUsize, sync::atomic::AtomicUsize,
sync::atomic::Ordering,
todo, todo,
}; };
@@ -40,34 +41,23 @@ pub struct Scope {
// lying, cheating reference >:( // lying, cheating reference >:(
pub props: Box<dyn std::any::Any>, pub props: Box<dyn std::any::Any>,
// pub props: Box<dyn Properties>,
//
// pub props_type: TypeId, // pub props_type: TypeId,
pub caller: *const (), pub caller: *const (),
} }
impl Scope { impl Scope {
// create a new scope from a function // create a new scope from a function
pub fn new<'a, P1, P2: 'static>( pub fn new<'a, P1, P2: 'static>(f: FC<P1>, props: P1, parent: Option<Index>) -> Self {
// pub fn new<'a, P: Properties, PFree: P + 'a, PLocked: P + 'static>(
f: FC<P1>,
props: P1,
parent: Option<Index>,
) -> Self
// where
// PFree: 'a,
// PLocked: 'static,
{
// Capture the props type
// let props_type = TypeId::of::<P>();
let hook_arena = typed_arena::Arena::new(); let hook_arena = typed_arena::Arena::new();
let hooks = RefCell::new(Vec::new()); let hooks = RefCell::new(Vec::new());
// Capture the caller // Capture the caller
let caller = f as *const (); let caller = f as *const ();
let listeners = Vec::new(); let listeners: Vec<Box<dyn Fn()>> = vec![Box::new(|| {
log::info!("Base listener called");
})];
let old_frame = BumpFrame { let old_frame = BumpFrame {
bump: Bump::new(), bump: Bump::new(),
@@ -84,16 +74,11 @@ impl Scope {
// box the props // box the props
let props = Box::new(props); let props = Box::new(props);
// erase the lifetime
// we'll manage this with dom lifecycle
let props = unsafe { std::mem::transmute::<_, Box<P2>>(props) }; let props = unsafe { std::mem::transmute::<_, Box<P2>>(props) };
// todo!()
Self { Self {
hook_arena, hook_arena,
hooks, hooks,
// props_type,
caller, caller,
frames, frames,
listeners, listeners,
@@ -102,13 +87,6 @@ impl Scope {
} }
} }
/// Update this component's props with a new set of props, remotely
///
///
pub(crate) fn update_props<'a, P>(&self, _new_props: P) -> crate::error::Result<()> {
Ok(())
}
/// Create a new context and run the component with references from the Virtual Dom /// Create a new context and run the component with references from the Virtual Dom
/// This function downcasts the function pointer based on the stored props_type /// This function downcasts the function pointer based on the stored props_type
/// ///
@@ -120,67 +98,67 @@ impl Scope {
frame frame
}; };
let node_slot = std::rc::Rc::new(RefCell::new(None));
let ctx: Context<'bump> = Context { let ctx: Context<'bump> = Context {
arena: &self.hook_arena, arena: &self.hook_arena,
hooks: &self.hooks, hooks: &self.hooks,
bump: &frame.bump, bump: &frame.bump,
idx: 0.into(), idx: 0.into(),
_p: PhantomData {}, _p: PhantomData {},
final_nodes: node_slot.clone(),
}; };
unsafe { unsafe {
/*
SAFETY ALERT
This particular usage of transmute is outlined in its docs https://doc.rust-lang.org/std/mem/fn.transmute.html
We hide the generic bound on the function item by casting it to raw pointer. When the function is actually called,
we transmute the function back using the props as reference.
we could do a better check to make sure that the TypeID is correct before casting
--
This is safe because we check that the generic type matches before casting.
*/
// we use plocked to be able to remove the borrowed lifetime // we use plocked to be able to remove the borrowed lifetime
// these lifetimes could be very broken, so we need to dynamically manage them // these lifetimes could be very broken, so we need to dynamically manage them
let caller = std::mem::transmute::<*const (), FC<PLocked>>(self.caller); let caller = std::mem::transmute::<*const (), FC<PLocked>>(self.caller);
let props = self.props.downcast_ref::<PLocked>().unwrap(); let props = self.props.downcast_ref::<PLocked>().unwrap();
// Note that the actual modification of the vnode head element occurs during this call
let _nodes: DomTree = caller(ctx, props); let _nodes: DomTree = caller(ctx, props);
todo!("absorb domtree into self")
// let nodes: VNode<'bump> = caller(ctx, props);
// let unsafe_node = std::mem::transmute::<VNode<'bump>, VNode<'static>>(nodes); /*
// frame.head_node = unsafe_node; SAFETY ALERT
DO NOT USE THIS VNODE WITHOUT THE APPOPRIATE ACCESSORS.
KEEPING THIS STATIC REFERENCE CAN LEAD TO UB.
Some things to note:
- The VNode itself is bound to the lifetime, but it itself is owned by scope.
- The VNode has a private API and can only be used from accessors.
- Public API cannot drop or destructure VNode
*/
// the nodes we care about have been unsafely extended to a static lifetime in context
frame.head_node = node_slot
.deref()
.borrow_mut()
.take()
.expect("Viewing did not happen");
} }
/*
SAFETY ALERT
This particular usage of transmute is outlined in its docs https://doc.rust-lang.org/std/mem/fn.transmute.html
We hide the generic bound on the function item by casting it to raw pointer. When the function is actually called,
we transmute the function back using the props as reference.
we could do a better check to make sure that the TypeID is correct before casting
--
This is safe because we check that the generic type matches before casting.
*/
/*
SAFETY ALERT
DO NOT USE THIS VNODE WITHOUT THE APPOPRIATE ACCESSORS.
KEEPING THIS STATIC REFERENCE CAN LEAD TO UB.
Some things to note:
- The VNode itself is bound to the lifetime, but it itself is owned by scope.
- The VNode has a private API and can only be used from accessors.
- Public API cannot drop or destructure VNode
*/
} }
/// Accessor to get the root node and its children (safely)\ /// Accessor to get the root node and its children (safely)\
/// Scope is self-referntial, so we are forced to use the 'static lifetime to cheat /// Scope is self-referntial, so we are forced to use the 'static lifetime to cheat
pub fn current_root_node<'bump>(&'bump self) -> &'bump VNode<'bump> { pub fn new_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
self.frames.current_head_node() self.frames.current_head_node()
} }
pub fn prev_root_node<'bump>(&'bump self) -> &'bump VNode<'bump> { pub fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
todo!() self.frames.prev_head_node()
} }
} }
pub struct BumpFrame {
pub bump: Bump,
pub head_node: VNode<'static>,
}
// todo, do better with the active frame stuff // todo, do better with the active frame stuff
// somehow build this vnode with a lifetime tied to self // somehow build this vnode with a lifetime tied to self
// This root node has "static" lifetime, but it's really not static. // This root node has "static" lifetime, but it's really not static.
@@ -192,6 +170,11 @@ pub struct ActiveFrame {
pub frames: [BumpFrame; 2], pub frames: [BumpFrame; 2],
} }
pub struct BumpFrame {
pub bump: Bump,
pub head_node: VNode<'static>,
}
impl ActiveFrame { impl ActiveFrame {
fn from_frames(a: BumpFrame, b: BumpFrame) -> Self { fn from_frames(a: BumpFrame, b: BumpFrame) -> Self {
Self { Self {
@@ -201,8 +184,23 @@ impl ActiveFrame {
} }
fn current_head_node<'b>(&'b self) -> &'b VNode<'b> { fn current_head_node<'b>(&'b self) -> &'b VNode<'b> {
let cur_idx = self.idx.borrow().load(std::sync::atomic::Ordering::Relaxed) % 1; let raw_node = match self.idx.borrow().load(Ordering::Relaxed) & 1 != 0 {
let raw_node = &self.frames[cur_idx]; true => &self.frames[0],
false => &self.frames[1],
};
unsafe {
let unsafe_head = &raw_node.head_node;
let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
safe_node
}
}
fn prev_head_node<'b>(&'b self) -> &'b VNode<'b> {
let raw_node = match self.idx.borrow().load(Ordering::Relaxed) & 1 == 0 {
true => &self.frames[0],
false => &self.frames[1],
};
unsafe { unsafe {
let unsafe_head = &raw_node.head_node; let unsafe_head = &raw_node.head_node;
let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head); let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
@@ -211,8 +209,8 @@ impl ActiveFrame {
} }
fn next(&mut self) -> &mut BumpFrame { fn next(&mut self) -> &mut BumpFrame {
self.idx.fetch_add(1, std::sync::atomic::Ordering::Relaxed); self.idx.fetch_add(1, Ordering::Relaxed);
let cur = self.idx.borrow().load(std::sync::atomic::Ordering::Relaxed); let cur = self.idx.borrow().load(Ordering::Relaxed);
match cur % 1 { match cur % 1 {
1 => &mut self.frames[1], 1 => &mut self.frames[1],
0 => &mut self.frames[0], 0 => &mut self.frames[0],
@@ -276,7 +274,7 @@ mod tests {
}) })
} }
fn child_example(ctx: Context, props: &ExampleProps) -> DomTree { fn child_example<'b>(ctx: Context<'b>, props: &'b ExampleProps) -> DomTree {
ctx.view(move |b| { ctx.view(move |b| {
div(b) div(b)
.child(text(props.name)) .child(text(props.name))
+69 -27
View File
@@ -78,6 +78,34 @@ impl VirtualDom {
} }
} }
//// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom
pub fn rebuild(&mut self) -> Result<EditList<'_>> {
// Reset and then build a new diff machine
// The previous edit list cannot be around while &mut is held
// Make sure variance doesnt break this
self.diff_bump.reset();
let mut diff_machine = DiffMachine::new(&self.diff_bump);
// this is still a WIP
// we'll need to re-fecth all the scopes that were changed and build the diff machine
// fetch the component again
let component = self
.components
.get_mut(self.base_scope)
.expect("Root should always exist");
component.run::<()>();
let old = component.old_frame();
let new = component.new_frame();
dbg!(old);
dbg!(new);
diff_machine.diff_node(old, new);
Ok(diff_machine.consume())
}
/// This method is the most sophisticated way of updating the virtual dom after an external event has been triggered. /// This method is the most sophisticated way of updating the virtual dom after an external event has been triggered.
/// ///
/// Given a synthetic event, the component that triggered the event, and the index of the callback, this runs the virtual /// Given a synthetic event, the component that triggered the event, and the index of the callback, this runs the virtual
@@ -102,17 +130,16 @@ impl VirtualDom {
/// } /// }
/// ///
/// ``` /// ```
pub fn progress_with_event(&mut self, evt: EventTrigger) -> Result<EditList<'_>> { pub fn progress_with_event(&mut self, event: EventTrigger) -> Result<EditList<'_>> {
let EventTrigger { let EventTrigger {
component_id, component_id,
listener_id, listener_id,
event: _, event: _,
} = evt; } = event;
let component = self let component = self
.components .components
.get(component_id) .get(component_id)
// todo: update this with a dedicated error type so implementors know what went wrong
.expect("Component should exist if an event was triggered"); .expect("Component should exist if an event was triggered");
let listener = component let listener = component
@@ -121,47 +148,37 @@ impl VirtualDom {
.expect("Listener should exist if it was triggered") .expect("Listener should exist if it was triggered")
.as_ref(); .as_ref();
// Run the callback // Run the callback with the user event
// This should cause internal state to progress, dumping events into the event queue
// todo: integrate this with a tracing mechanism exposed to a dev tool
listener(); listener();
// Run through our events, tagging which Indexes are receiving updates // Mark dirty components. Descend from the highest node until all dirty nodes are updated.
// Prop updates take prescedence over subscription updates
// Run all prop updates *first* as they will cascade into children.
// *then* run the non-prop updates that were not already covered by props
let mut affected_components = Vec::new(); let mut affected_components = Vec::new();
// It's essentially draining the vec, but with some dancing to release the RefMut while let Some(event) = self.pop_event() {
// We also want to be able to push events into the queue from processing the event
while let Some(event) = {
let new_evt = self.event_queue.as_ref().borrow_mut().pop_front();
new_evt
} {
if let Some(component_idx) = event.index() { if let Some(component_idx) = event.index() {
affected_components.push(component_idx); affected_components.push(component_idx);
} }
self.process_lifecycle(event)?; self.process_lifecycle(event)?;
} }
// Reset and then build a new diff machine todo!()
// The previous edit list cannot be around while &mut is held
// Make sure variance doesnt break this
self.diff_bump.reset();
let diff_machine = DiffMachine::new(&self.diff_bump);
Ok(diff_machine.consume())
} }
/// Using mutable access to the Virtual Dom, progress a given lifecycle event /// Using mutable access to the Virtual Dom, progress a given lifecycle event
fn process_lifecycle(&mut self, LifecycleEvent { event_type }: LifecycleEvent) -> Result<()> { fn process_lifecycle(&mut self, LifecycleEvent { event_type }: LifecycleEvent) -> Result<()> {
match event_type { match event_type {
// Component needs to be mounted to the virtual dom // Component needs to be mounted to the virtual dom
LifecycleType::Mount { to: _, under: _, props: _ } => {} LifecycleType::Mount {
to: _,
under: _,
props: _,
} => {}
// The parent for this component generated new props and the component needs update // The parent for this component generated new props and the component needs update
LifecycleType::PropsChanged { props: _, component: _ } => {} LifecycleType::PropsChanged {
props: _,
component: _,
} => {}
// Component was messaged via the internal subscription service // Component was messaged via the internal subscription service
LifecycleType::Callback { component: _ } => {} LifecycleType::Callback { component: _ } => {}
@@ -170,6 +187,11 @@ impl VirtualDom {
Ok(()) Ok(())
} }
/// Pop the top event of the internal lifecycle event queu
pub fn pop_event(&self) -> Option<LifecycleEvent> {
self.event_queue.as_ref().borrow_mut().pop_front()
}
/// With access to the virtual dom, schedule an update to the Root component's props. /// With access to the virtual dom, schedule an update to the Root component's props.
/// This generates the appropriate Lifecycle even. It's up to the renderer to actually feed this lifecycle event /// This generates the appropriate Lifecycle even. It's up to the renderer to actually feed this lifecycle event
/// back into the event system to get an edit list. /// back into the event system to get an edit list.
@@ -215,10 +237,30 @@ pub enum LifecycleType {
impl LifecycleEvent { impl LifecycleEvent {
fn index(&self) -> Option<Index> { fn index(&self) -> Option<Index> {
match &self.event_type { match &self.event_type {
LifecycleType::Mount { to: _, under: _, props: _ } => None, LifecycleType::Mount {
to: _,
under: _,
props: _,
} => None,
LifecycleType::PropsChanged { component, .. } LifecycleType::PropsChanged { component, .. }
| LifecycleType::Callback { component } => Some(component.clone()), | LifecycleType::Callback { component } => Some(component.clone()),
} }
} }
} }
mod tests {
use super::*;
#[test]
fn start_dom() {
let mut dom = VirtualDom::new(|ctx, props| {
ctx.view(|bump| {
use crate::builder::*;
div(bump).child(text("hello, world")).finish()
})
});
let edits = dom.rebuild().unwrap();
println!("{:#?}", edits);
}
}
+2 -7
View File
@@ -4,15 +4,10 @@ use dioxus_web::WebsysRenderer;
fn main() { fn main() {
// todo: set this up so the websys render can spawn itself rather than having to wrap it // todo: set this up so the websys render can spawn itself rather than having to wrap it
// almost like bundling an executor with the wasm version // almost like bundling an executor with the wasm version
wasm_bindgen_futures::spawn_local(async { wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example));
WebsysRenderer::new(Example)
.run()
.await
.expect("Dioxus Failed! This should *not* happen!")
});
} }
static Example: FC<()> = |ctx, props| { static Example: FC<()> = |ctx, _props| {
ctx.view(html! { ctx.view(html! {
<div> <div>
"Hello world!" "Hello world!"
+2 -2
View File
@@ -9,7 +9,7 @@ fn main() {
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug)); wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
WebsysRenderer::new(|ctx, _| { wasm_bindgen_futures::spawn_local(WebsysRenderer::start(|ctx, _| {
ctx.view(html! { ctx.view(html! {
<div> <div>
<div class="flex items-center justify-center flex-col"> <div class="flex items-center justify-center flex-col">
@@ -43,5 +43,5 @@ fn main() {
</div> </div>
</div> </div>
}) })
}); }));
} }
+2 -2
View File
@@ -1,6 +1,6 @@
use dioxus_core::changelist::Edit; use dioxus_core::changelist::Edit;
use fxhash::FxHashMap; use fxhash::FxHashMap;
use log::{debug, info, log}; use log::{debug};
use wasm_bindgen::{closure::Closure, JsCast}; use wasm_bindgen::{closure::Closure, JsCast};
use web_sys::{window, Document, Element, Event, Node}; use web_sys::{window, Document, Element, Event, Node};
@@ -95,7 +95,7 @@ impl PatchMachine {
self.templates.get(&id) self.templates.get(&id)
} }
pub fn init_events_trampoline(&mut self, mut trampoline: ()) { pub fn init_events_trampoline(&mut self, _trampoline: ()) {
todo!("Event trampoline not a thing anymore") todo!("Event trampoline not a thing anymore")
// pub fn init_events_trampoline(&mut self, mut trampoline: EventsTrampoline) { // pub fn init_events_trampoline(&mut self, mut trampoline: EventsTrampoline) {
// self.callback = Some(Closure::wrap(Box::new(move |event: &web_sys::Event| { // self.callback = Some(Closure::wrap(Box::new(move |event: &web_sys::Event| {
+19 -23
View File
@@ -5,20 +5,14 @@
use fxhash::FxHashMap; use fxhash::FxHashMap;
use web_sys::{window, Document, Element, Event, Node}; use web_sys::{window, Document, Element, Event, Node};
use dioxus::prelude::VElement;
pub use dioxus_core as dioxus; pub use dioxus_core as dioxus;
use dioxus_core::{ use dioxus_core::{
events::EventTrigger, events::EventTrigger,
prelude::{bumpalo::Bump, html, DiffMachine, VNode, VirtualDom, FC}, prelude::{VirtualDom, FC},
}; };
use futures::{ use futures::{channel::mpsc, SinkExt, StreamExt};
channel::mpsc::{self, Sender},
future, SinkExt, StreamExt,
};
use mpsc::UnboundedSender;
pub mod interpreter; pub mod interpreter;
use interpreter::PatchMachine;
/// The `WebsysRenderer` provides a way of rendering a Dioxus Virtual DOM to the browser's DOM. /// The `WebsysRenderer` provides a way of rendering a Dioxus Virtual DOM to the browser's DOM.
/// Under the hood, we leverage WebSys and interact directly with the DOM /// Under the hood, we leverage WebSys and interact directly with the DOM
@@ -27,7 +21,7 @@ pub struct WebsysRenderer {
internal_dom: VirtualDom, internal_dom: VirtualDom,
// this should be a component index // this should be a component index
event_map: FxHashMap<(u32, u32), u32>, _event_map: FxHashMap<(u32, u32), u32>,
} }
impl WebsysRenderer { impl WebsysRenderer {
@@ -57,34 +51,36 @@ impl WebsysRenderer {
pub fn from_vdom(dom: VirtualDom) -> Self { pub fn from_vdom(dom: VirtualDom) -> Self {
Self { Self {
internal_dom: dom, internal_dom: dom,
event_map: FxHashMap::default(), _event_map: FxHashMap::default(),
} }
} }
pub async fn run(&mut self) -> dioxus_core::error::Result<()> { pub async fn run(&mut self) -> dioxus_core::error::Result<()> {
let (mut sender, mut receiver) = mpsc::unbounded::<EventTrigger>(); let (sender, mut receiver) = mpsc::unbounded::<EventTrigger>();
sender
.send(EventTrigger::start_event())
.await
.expect("Should not fail");
let body = prepare_websys_dom(); let body = prepare_websys_dom();
let mut patch_machine = PatchMachine::new(body.clone()); let mut patch_machine = interpreter::PatchMachine::new(body.clone());
let root_node = body.first_child().unwrap(); let root_node = body.first_child().unwrap();
patch_machine.stack.push(root_node); patch_machine.stack.push(root_node);
self.internal_dom
.rebuild()?
.into_iter()
.for_each(|edit| patch_machine.handle_edit(&edit));
// Event loop waits for the receiver to finish up // Event loop waits for the receiver to finish up
// TODO! Connect the sender to the virtual dom's suspense system // TODO! Connect the sender to the virtual dom's suspense system
// Suspense is basically an external event that can force renders to specific nodes // Suspense is basically an external event that can force renders to specific nodes
while let Some(event) = receiver.next().await { while let Some(event) = receiver.next().await {
let diffs = self.internal_dom.progress_with_event(event)?; self.internal_dom
for edit in &diffs { .progress_with_event(event)?
patch_machine.handle_edit(edit); .into_iter()
} .for_each(|edit| {
patch_machine.handle_edit(&edit);
});
} }
Ok(()) // should actually never return from this, should be an error Ok(()) // should actually never return from this, should be an error, rustc just cant see it
} }
} }