Feat: wire up rebuild
This commit is contained in:
1 parent
4d01455729
commit
06ae4fc178
11 files changed
+209
-169
No files matched your search
Vendored
+1
-1
@@ -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::*;
|
use dioxus_core::prelude::*;
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|||||||
@@ -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
@@ -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 {}
|
||||||
@@ -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
@@ -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))
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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!"
|
||||||
|
|||||||
@@ -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>
|
||||||
})
|
})
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
@@ -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
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user