mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-26 06:00:21 +00:00
Feat: wire up rebuild
This commit is contained in:
parent
4d01455729
commit
06ae4fc178
11 changed files with 210 additions and 170 deletions
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"rust-analyzer.inlayHints.enable": false
|
||||
"rust-analyzer.inlayHints.enable": true
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
|
||||
|
||||
use builder::{button};
|
||||
use builder::button;
|
||||
use dioxus_core::prelude::*;
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use crate::nodes::VNode;
|
||||
use crate::prelude::*;
|
||||
use crate::{nodes::VNode};
|
||||
use bumpalo::Bump;
|
||||
use hooks::Hook;
|
||||
use log::info;
|
||||
use std::{
|
||||
any::TypeId, borrow::Borrow, cell::RefCell, future::Future, marker::PhantomData,
|
||||
sync::atomic::AtomicUsize,
|
||||
any::TypeId, borrow::Borrow, cell::RefCell, future::Future, marker::PhantomData, ops::Deref,
|
||||
rc::Rc, sync::atomic::AtomicUsize,
|
||||
};
|
||||
|
||||
/// 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) bump: &'src Bump,
|
||||
|
||||
pub(crate) final_nodes: Rc<RefCell<Option<VNode<'static>>>>,
|
||||
|
||||
// holder for the src lifetime
|
||||
// todo @jon remove this
|
||||
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 for<'b> FnOnce(&'b Bump) -> VNode<'b> + 'a + 'p) -> DomTree {
|
||||
// pub fn view<'p>(self, lazy_nodes: impl FnOnce(&'a Bump) -> VNode<'a> + 'a + 'p) -> DomTree {
|
||||
// pub fn view(self, lazy_nodes: impl FnOnce(&'a Bump) -> VNode<'a> + 'a) -> VNode<'a> {
|
||||
let _g = lazy_nodes(self.bump);
|
||||
let safe_nodes = lazy_nodes(self.bump);
|
||||
let unsafe_nodes = unsafe { std::mem::transmute::<VNode<'a>, VNode<'static>>(safe_nodes) };
|
||||
self.final_nodes.deref().borrow_mut().replace(unsafe_nodes);
|
||||
info!("lazy nodes have been generated");
|
||||
DomTree {}
|
||||
}
|
||||
|
||||
|
@ -186,9 +189,8 @@ mod context_api {
|
|||
//! a failure of implementation.
|
||||
//!
|
||||
//!
|
||||
|
||||
|
||||
use std::{ops::Deref};
|
||||
use std::ops::Deref;
|
||||
|
||||
pub struct RemoteState<T> {
|
||||
inner: *const T,
|
||||
|
|
|
@ -16,34 +16,45 @@ impl EventTrigger {
|
|||
pub fn new() -> Self {
|
||||
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 {
|
||||
// the event to drain the current lifecycle queue
|
||||
// Used to initate the dom
|
||||
StartEvent,
|
||||
|
||||
// Real events
|
||||
ClipboardEvent,
|
||||
CompositionEvent,
|
||||
KeyboardEvent,
|
||||
FocusEvent,
|
||||
FormEvent,
|
||||
GenericEvent,
|
||||
MouseEvent,
|
||||
PointerEvent,
|
||||
SelectionEvent,
|
||||
TouchEvent,
|
||||
UIEvent,
|
||||
WheelEvent,
|
||||
MediaEvent,
|
||||
ImageEvent,
|
||||
AnimationEvent,
|
||||
TransitionEvent,
|
||||
ClipboardEvent(ClipboardEvent),
|
||||
CompositionEvent(CompositionEvent),
|
||||
KeyboardEvent(KeyboardEvent),
|
||||
FocusEvent(FocusEvent),
|
||||
FormEvent(FormEvent),
|
||||
GenericEvent(GenericEvent),
|
||||
MouseEvent(MouseEvent),
|
||||
PointerEvent(PointerEvent),
|
||||
SelectionEvent(SelectionEvent),
|
||||
TouchEvent(TouchEvent),
|
||||
UIEvent(UIEvent),
|
||||
WheelEvent(WheelEvent),
|
||||
MediaEvent(MediaEvent),
|
||||
ImageEvent(ImageEvent),
|
||||
AnimationEvent(AnimationEvent),
|
||||
TransitionEvent(TransitionEvent),
|
||||
|
||||
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 {}
|
||||
|
|
|
@ -88,7 +88,7 @@ pub mod builder {
|
|||
// types used internally that are important
|
||||
pub(crate) mod innerlude {
|
||||
// pub(crate) use crate::component::Properties;
|
||||
|
||||
|
||||
pub(crate) use crate::context::Context;
|
||||
pub(crate) use crate::error::{Error, Result};
|
||||
use crate::nodes;
|
||||
|
@ -101,9 +101,7 @@ pub(crate) mod innerlude {
|
|||
|
||||
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<P> = for<'scope, 'r> fn(Context<'scope>, &'scope P) -> DomTree;
|
||||
// pub type FC<P> = for<'scope, 'r> fn(Context<'scope>, &'r P) -> VNode<'scope>;
|
||||
|
|
|
@ -12,6 +12,7 @@ use std::{
|
|||
marker::PhantomData,
|
||||
ops::{Deref, DerefMut},
|
||||
sync::atomic::AtomicUsize,
|
||||
sync::atomic::Ordering,
|
||||
todo,
|
||||
};
|
||||
|
||||
|
@ -40,34 +41,23 @@ pub struct Scope {
|
|||
|
||||
// lying, cheating reference >:(
|
||||
pub props: Box<dyn std::any::Any>,
|
||||
// pub props: Box<dyn Properties>,
|
||||
|
||||
//
|
||||
// pub props_type: TypeId,
|
||||
pub caller: *const (),
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
// create a new scope from a function
|
||||
pub fn new<'a, P1, P2: 'static>(
|
||||
// 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>();
|
||||
pub fn new<'a, P1, P2: 'static>(f: FC<P1>, props: P1, parent: Option<Index>) -> Self {
|
||||
let hook_arena = typed_arena::Arena::new();
|
||||
let hooks = RefCell::new(Vec::new());
|
||||
|
||||
// Capture the caller
|
||||
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 {
|
||||
bump: Bump::new(),
|
||||
|
@ -84,16 +74,11 @@ impl Scope {
|
|||
// box the 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) };
|
||||
|
||||
// todo!()
|
||||
Self {
|
||||
hook_arena,
|
||||
hooks,
|
||||
// props_type,
|
||||
caller,
|
||||
frames,
|
||||
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
|
||||
/// This function downcasts the function pointer based on the stored props_type
|
||||
///
|
||||
|
@ -120,67 +98,67 @@ impl Scope {
|
|||
frame
|
||||
};
|
||||
|
||||
let node_slot = std::rc::Rc::new(RefCell::new(None));
|
||||
let ctx: Context<'bump> = Context {
|
||||
arena: &self.hook_arena,
|
||||
hooks: &self.hooks,
|
||||
bump: &frame.bump,
|
||||
idx: 0.into(),
|
||||
_p: PhantomData {},
|
||||
final_nodes: node_slot.clone(),
|
||||
};
|
||||
|
||||
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
|
||||
// these lifetimes could be very broken, so we need to dynamically manage them
|
||||
let caller = std::mem::transmute::<*const (), FC<PLocked>>(self.caller);
|
||||
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);
|
||||
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)\
|
||||
/// 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()
|
||||
}
|
||||
|
||||
pub fn prev_root_node<'bump>(&'bump self) -> &'bump VNode<'bump> {
|
||||
todo!()
|
||||
pub fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
|
||||
self.frames.prev_head_node()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BumpFrame {
|
||||
pub bump: Bump,
|
||||
pub head_node: VNode<'static>,
|
||||
}
|
||||
|
||||
// todo, do better with the active frame stuff
|
||||
// somehow build this vnode with a lifetime tied to self
|
||||
// This root node has "static" lifetime, but it's really not static.
|
||||
|
@ -192,6 +170,11 @@ pub struct ActiveFrame {
|
|||
pub frames: [BumpFrame; 2],
|
||||
}
|
||||
|
||||
pub struct BumpFrame {
|
||||
pub bump: Bump,
|
||||
pub head_node: VNode<'static>,
|
||||
}
|
||||
|
||||
impl ActiveFrame {
|
||||
fn from_frames(a: BumpFrame, b: BumpFrame) -> Self {
|
||||
Self {
|
||||
|
@ -201,8 +184,23 @@ impl ActiveFrame {
|
|||
}
|
||||
|
||||
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 = &self.frames[cur_idx];
|
||||
let raw_node = match self.idx.borrow().load(Ordering::Relaxed) & 1 != 0 {
|
||||
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 {
|
||||
let unsafe_head = &raw_node.head_node;
|
||||
let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
|
||||
|
@ -211,8 +209,8 @@ impl ActiveFrame {
|
|||
}
|
||||
|
||||
fn next(&mut self) -> &mut BumpFrame {
|
||||
self.idx.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
let cur = self.idx.borrow().load(std::sync::atomic::Ordering::Relaxed);
|
||||
self.idx.fetch_add(1, Ordering::Relaxed);
|
||||
let cur = self.idx.borrow().load(Ordering::Relaxed);
|
||||
match cur % 1 {
|
||||
1 => &mut self.frames[1],
|
||||
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| {
|
||||
div(b)
|
||||
.child(text(props.name))
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
/// 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 {
|
||||
component_id,
|
||||
listener_id,
|
||||
event: _,
|
||||
} = evt;
|
||||
} = event;
|
||||
|
||||
let component = self
|
||||
.components
|
||||
.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");
|
||||
|
||||
let listener = component
|
||||
|
@ -121,47 +148,37 @@ impl VirtualDom {
|
|||
.expect("Listener should exist if it was triggered")
|
||||
.as_ref();
|
||||
|
||||
// Run the callback
|
||||
// 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
|
||||
// Run the callback with the user event
|
||||
listener();
|
||||
|
||||
// Run through our events, tagging which Indexes are receiving updates
|
||||
// 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
|
||||
|
||||
// Mark dirty components. Descend from the highest node until all dirty nodes are updated.
|
||||
let mut affected_components = Vec::new();
|
||||
|
||||
// It's essentially draining the vec, but with some dancing to release the RefMut
|
||||
// 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
|
||||
} {
|
||||
while let Some(event) = self.pop_event() {
|
||||
if let Some(component_idx) = event.index() {
|
||||
affected_components.push(component_idx);
|
||||
}
|
||||
self.process_lifecycle(event)?;
|
||||
}
|
||||
|
||||
// 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 diff_machine = DiffMachine::new(&self.diff_bump);
|
||||
|
||||
Ok(diff_machine.consume())
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Using mutable access to the Virtual Dom, progress a given lifecycle event
|
||||
fn process_lifecycle(&mut self, LifecycleEvent { event_type }: LifecycleEvent) -> Result<()> {
|
||||
match event_type {
|
||||
// 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
|
||||
LifecycleType::PropsChanged { props: _, component: _ } => {}
|
||||
LifecycleType::PropsChanged {
|
||||
props: _,
|
||||
component: _,
|
||||
} => {}
|
||||
|
||||
// Component was messaged via the internal subscription service
|
||||
LifecycleType::Callback { component: _ } => {}
|
||||
|
@ -170,6 +187,11 @@ impl VirtualDom {
|
|||
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.
|
||||
/// 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.
|
||||
|
@ -215,10 +237,30 @@ pub enum LifecycleType {
|
|||
impl LifecycleEvent {
|
||||
fn index(&self) -> Option<Index> {
|
||||
match &self.event_type {
|
||||
LifecycleType::Mount { to: _, under: _, props: _ } => None,
|
||||
LifecycleType::Mount {
|
||||
to: _,
|
||||
under: _,
|
||||
props: _,
|
||||
} => None,
|
||||
|
||||
LifecycleType::PropsChanged { component, .. }
|
||||
| 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,15 +4,10 @@ use dioxus_web::WebsysRenderer;
|
|||
fn main() {
|
||||
// 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
|
||||
wasm_bindgen_futures::spawn_local(async {
|
||||
WebsysRenderer::new(Example)
|
||||
.run()
|
||||
.await
|
||||
.expect("Dioxus Failed! This should *not* happen!")
|
||||
});
|
||||
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example));
|
||||
}
|
||||
|
||||
static Example: FC<()> = |ctx, props| {
|
||||
static Example: FC<()> = |ctx, _props| {
|
||||
ctx.view(html! {
|
||||
<div>
|
||||
"Hello world!"
|
||||
|
|
|
@ -9,7 +9,7 @@ fn main() {
|
|||
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
WebsysRenderer::new(|ctx, _| {
|
||||
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(|ctx, _| {
|
||||
ctx.view(html! {
|
||||
<div>
|
||||
<div class="flex items-center justify-center flex-col">
|
||||
|
@ -43,5 +43,5 @@ fn main() {
|
|||
</div>
|
||||
</div>
|
||||
})
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use dioxus_core::changelist::Edit;
|
||||
use fxhash::FxHashMap;
|
||||
use log::{debug, info, log};
|
||||
use log::{debug};
|
||||
use wasm_bindgen::{closure::Closure, JsCast};
|
||||
use web_sys::{window, Document, Element, Event, Node};
|
||||
|
||||
|
@ -95,7 +95,7 @@ impl PatchMachine {
|
|||
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")
|
||||
// pub fn init_events_trampoline(&mut self, mut trampoline: EventsTrampoline) {
|
||||
// self.callback = Some(Closure::wrap(Box::new(move |event: &web_sys::Event| {
|
||||
|
|
|
@ -5,20 +5,14 @@
|
|||
use fxhash::FxHashMap;
|
||||
use web_sys::{window, Document, Element, Event, Node};
|
||||
|
||||
use dioxus::prelude::VElement;
|
||||
|
||||
pub use dioxus_core as dioxus;
|
||||
use dioxus_core::{
|
||||
events::EventTrigger,
|
||||
prelude::{bumpalo::Bump, html, DiffMachine, VNode, VirtualDom, FC},
|
||||
prelude::{VirtualDom, FC},
|
||||
};
|
||||
use futures::{
|
||||
channel::mpsc::{self, Sender},
|
||||
future, SinkExt, StreamExt,
|
||||
};
|
||||
use mpsc::UnboundedSender;
|
||||
use futures::{channel::mpsc, SinkExt, StreamExt};
|
||||
|
||||
pub mod interpreter;
|
||||
use interpreter::PatchMachine;
|
||||
|
||||
/// 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
|
||||
|
@ -27,7 +21,7 @@ pub struct WebsysRenderer {
|
|||
internal_dom: VirtualDom,
|
||||
|
||||
// this should be a component index
|
||||
event_map: FxHashMap<(u32, u32), u32>,
|
||||
_event_map: FxHashMap<(u32, u32), u32>,
|
||||
}
|
||||
|
||||
impl WebsysRenderer {
|
||||
|
@ -57,34 +51,36 @@ impl WebsysRenderer {
|
|||
pub fn from_vdom(dom: VirtualDom) -> Self {
|
||||
Self {
|
||||
internal_dom: dom,
|
||||
event_map: FxHashMap::default(),
|
||||
_event_map: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) -> dioxus_core::error::Result<()> {
|
||||
let (mut sender, mut receiver) = mpsc::unbounded::<EventTrigger>();
|
||||
|
||||
sender
|
||||
.send(EventTrigger::start_event())
|
||||
.await
|
||||
.expect("Should not fail");
|
||||
let (sender, mut receiver) = mpsc::unbounded::<EventTrigger>();
|
||||
|
||||
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();
|
||||
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
|
||||
// TODO! Connect the sender to the virtual dom's suspense system
|
||||
// Suspense is basically an external event that can force renders to specific nodes
|
||||
while let Some(event) = receiver.next().await {
|
||||
let diffs = self.internal_dom.progress_with_event(event)?;
|
||||
for edit in &diffs {
|
||||
patch_machine.handle_edit(edit);
|
||||
}
|
||||
self.internal_dom
|
||||
.progress_with_event(event)?
|
||||
.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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue