mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-23 04:33:06 +00:00
WIP: still a bit stumped on DFS vs BFS
This commit is contained in:
parent
89f2290692
commit
3740f81383
6 changed files with 205 additions and 291 deletions
|
@ -1,34 +1,19 @@
|
|||
//! An example that shows how to:
|
||||
//! create a scope,
|
||||
//! render a component,
|
||||
//! change some data
|
||||
//! render it again
|
||||
//! consume the diffs and write that to a renderer
|
||||
|
||||
use dioxus_core::prelude::*;
|
||||
|
||||
fn main() -> Result<(), ()> {
|
||||
let p1 = Props { name: "bob".into() };
|
||||
|
||||
let _vdom = VirtualDom::new_with_props(Example, p1);
|
||||
// vdom.progress()?;
|
||||
let mut vdom = VirtualDom::new_with_props(Example, p1);
|
||||
vdom.update_props(|p: &mut Props| {});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct Props {
|
||||
name: String,
|
||||
}
|
||||
|
||||
// impl Properties for Props {
|
||||
// fn call(&self, ptr: *const ()) {}
|
||||
|
||||
// // fn new() -> Self {
|
||||
// // todo!()
|
||||
// // }
|
||||
// }
|
||||
|
||||
static Example: FC<Props> = |ctx, _props| {
|
||||
ctx.render(html! {
|
||||
<div>
|
||||
|
|
|
@ -32,10 +32,14 @@
|
|||
//!
|
||||
//! More info on how to improve this diffing algorithm:
|
||||
//! - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
|
||||
use crate::innerlude::*;
|
||||
use crate::{
|
||||
innerlude::*,
|
||||
scope::{create_scoped, Scoped},
|
||||
};
|
||||
use bumpalo::Bump;
|
||||
use fxhash::{FxHashMap, FxHashSet};
|
||||
use std::cmp::Ordering;
|
||||
use generational_arena::Arena;
|
||||
use std::{cell::RefCell, cmp::Ordering, collections::VecDeque, rc::Rc};
|
||||
|
||||
/// The DiffState is a cursor internal to the VirtualDOM's diffing algorithm that allows persistence of state while
|
||||
/// diffing trees of components. This means we can "re-enter" a subtree of a component by queuing a "NeedToDiff" event.
|
||||
|
@ -49,20 +53,23 @@ use std::cmp::Ordering;
|
|||
/// The order of these re-entrances is stored in the DiffState itself. The DiffState comes pre-loaded with a set of components
|
||||
/// that were modified by the eventtrigger. This prevents doubly evaluating components if they wereboth updated via
|
||||
/// subscriptions and props changes.
|
||||
pub struct DiffMachine<'a> {
|
||||
pub struct DiffMachine<'a, 'b> {
|
||||
pub change_list: EditMachine<'a>,
|
||||
immediate_queue: Vec<ScopeIdx>,
|
||||
diffed: FxHashSet<ScopeIdx>,
|
||||
need_to_diff: FxHashSet<ScopeIdx>,
|
||||
|
||||
pub vdom: &'b VirtualDom,
|
||||
pub cur_idx: ScopeIdx,
|
||||
pub diffed: FxHashSet<ScopeIdx>,
|
||||
pub need_to_diff: FxHashSet<ScopeIdx>,
|
||||
}
|
||||
|
||||
impl<'a> DiffMachine<'a> {
|
||||
pub fn new(bump: &'a Bump) -> Self {
|
||||
impl<'a, 'b> DiffMachine<'a, 'b> {
|
||||
pub fn new(vdom: &'b VirtualDom, bump: &'a Bump, idx: ScopeIdx) -> Self {
|
||||
Self {
|
||||
cur_idx: idx,
|
||||
change_list: EditMachine::new(bump),
|
||||
immediate_queue: Vec::new(),
|
||||
diffed: FxHashSet::default(),
|
||||
need_to_diff: FxHashSet::default(),
|
||||
vdom,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,6 +78,7 @@ impl<'a> DiffMachine<'a> {
|
|||
}
|
||||
|
||||
pub fn diff_node(&mut self, old: &VNode<'a>, new: &VNode<'a>) {
|
||||
// pub fn diff_node(&mut self, old: &VNode<'a>, new: &VNode<'a>) {
|
||||
/*
|
||||
For each valid case, we "commit traversal", meaning we save this current position in the tree.
|
||||
Then, we diff and queue an edit event (via chagelist). s single trees - when components show up, we save that traversal and then re-enter later.
|
||||
|
@ -120,7 +128,45 @@ impl<'a> DiffMachine<'a> {
|
|||
todo!("Usage of component VNode not currently supported");
|
||||
}
|
||||
|
||||
(_, VNode::Component(_)) | (VNode::Component(_), _) => {
|
||||
(_, VNode::Component(new)) => {
|
||||
// let VComponent {
|
||||
// props,
|
||||
// props_type,
|
||||
// comp,
|
||||
// caller,
|
||||
// assigned_scope,
|
||||
// ..
|
||||
// } = *new;
|
||||
|
||||
// make the component
|
||||
// let idx = unsafe {
|
||||
// // let vdom = &mut *self.vdom;
|
||||
// vdom.insert_with(|f| {
|
||||
// todo!()
|
||||
// //
|
||||
// // create_scoped(caller, props, myidx, parent)
|
||||
// })
|
||||
// };
|
||||
|
||||
// we have no stable reference to work from
|
||||
// push the lifecycle event onto the queue
|
||||
// self.lifecycle_events
|
||||
// .borrow_mut()
|
||||
// .push_back(LifecycleEvent {
|
||||
// event_type: LifecycleType::Mount {
|
||||
// props: new.props,
|
||||
// to: self.cur_idx,
|
||||
// },
|
||||
// });
|
||||
// we need to associaote this new component with a scope...
|
||||
|
||||
// self.change_list.save_known_root(id)
|
||||
self.change_list.commit_traversal();
|
||||
|
||||
// push the current
|
||||
}
|
||||
|
||||
(VNode::Component(old), _) => {
|
||||
todo!("Usage of component VNode not currently supported");
|
||||
}
|
||||
|
||||
|
@ -137,7 +183,8 @@ impl<'a> DiffMachine<'a> {
|
|||
// [... node]
|
||||
//
|
||||
// The change list stack is left unchanged.
|
||||
fn diff_listeners(&mut self, old: &[Listener<'a>], new: &[Listener<'a>]) {
|
||||
fn diff_listeners(&mut self, old: &[Listener<'_>], new: &[Listener<'_>]) {
|
||||
// fn diff_listeners(&mut self, old: &[Listener<'a>], new: &[Listener<'a>]) {
|
||||
if !old.is_empty() || !new.is_empty() {
|
||||
self.change_list.commit_traversal();
|
||||
}
|
||||
|
|
|
@ -296,11 +296,13 @@ mod vtext {
|
|||
/// Virtual Components for custom user-defined components
|
||||
/// Only supports the functional syntax
|
||||
mod vcomponent {
|
||||
use crate::innerlude::{Context, FC};
|
||||
use std::{any::TypeId, marker::PhantomData};
|
||||
use crate::innerlude::{Context, ScopeIdx, FC};
|
||||
use std::{any::TypeId, cell::RefCell, marker::PhantomData, rc::Rc};
|
||||
|
||||
use super::DomTree;
|
||||
|
||||
pub type StableScopeAddres = Rc<RefCell<Option<ScopeIdx>>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VComponent<'src> {
|
||||
_p: PhantomData<&'src ()>,
|
||||
|
@ -308,6 +310,11 @@ mod vcomponent {
|
|||
pub(crate) props_type: TypeId,
|
||||
pub(crate) comp: *const (),
|
||||
pub(crate) caller: Caller,
|
||||
|
||||
// once a component gets mounted, its parent gets a stable address.
|
||||
// this way we can carry the scope index from between renders
|
||||
// genius, really!
|
||||
pub assigned_scope: StableScopeAddres,
|
||||
}
|
||||
|
||||
pub struct Caller(Box<dyn Fn(Context) -> DomTree>);
|
||||
|
@ -323,10 +330,10 @@ mod vcomponent {
|
|||
// - perform comparisons when diffing (memoization)
|
||||
// -
|
||||
pub fn new<P>(comp: FC<P>, props: P) -> Self {
|
||||
let caller = move |ctx: Context| {
|
||||
let t = comp(ctx, &props);
|
||||
t
|
||||
};
|
||||
// let caller = move |ctx: Context| {
|
||||
// let t = comp(ctx, &props);
|
||||
// t
|
||||
// };
|
||||
// let _caller = comp as *const ();
|
||||
// let _props = Box::new(props);
|
||||
|
||||
|
|
|
@ -126,6 +126,7 @@ pub struct EditMachine<'src> {
|
|||
pub traversal: Traversal,
|
||||
next_temporary: u32,
|
||||
forcing_new_listeners: bool,
|
||||
|
||||
pub emitter: EditList<'src>,
|
||||
}
|
||||
|
||||
|
@ -214,10 +215,6 @@ impl<'a> EditMachine<'a> {
|
|||
debug_assert!(self.traversal_is_committed());
|
||||
debug_assert!(start < end);
|
||||
let temp_base = self.next_temporary;
|
||||
// debug!(
|
||||
// "emit: save_children_to_temporaries({}, {}, {})",
|
||||
// temp_base, start, end
|
||||
// );
|
||||
self.next_temporary = temp_base + (end - start) as u32;
|
||||
self.emitter.push(Edit::SaveChildrenToTemporaries {
|
||||
temp: temp_base,
|
||||
|
@ -367,6 +364,11 @@ impl<'a> EditMachine<'a> {
|
|||
// debug!("emit: remove_event_listener({:?})", event);
|
||||
}
|
||||
|
||||
pub fn save_known_root(&mut self, id: ScopeIdx) {
|
||||
log::debug!("emit: save_known_root({:?})", id);
|
||||
self.emitter.push(Edit::MakeKnown { node: id })
|
||||
}
|
||||
|
||||
// pub fn save_template(&mut self, id: CacheId) {
|
||||
// debug_assert!(self.traversal_is_committed());
|
||||
// debug_assert!(!self.has_template(id));
|
||||
|
|
|
@ -12,11 +12,14 @@ use std::{
|
|||
};
|
||||
|
||||
pub trait Properties: PartialEq {}
|
||||
impl Properties for () {}
|
||||
// just for now
|
||||
impl<T: PartialEq> Properties for T {}
|
||||
|
||||
pub trait Scoped {
|
||||
fn run(&mut self);
|
||||
fn compare_props(&self, new: &dyn std::any::Any) -> bool;
|
||||
fn call_listener(&mut self, trigger: EventTrigger);
|
||||
|
||||
fn new_frame<'bump>(&'bump self) -> &'bump VNode<'bump>;
|
||||
fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump>;
|
||||
}
|
||||
|
@ -46,6 +49,7 @@ pub struct Scope<P: Properties> {
|
|||
|
||||
// These hooks are actually references into the hook arena
|
||||
// These two could be combined with "OwningRef" to remove unsafe usage
|
||||
// or we could dedicate a tiny bump arena just for them
|
||||
// could also use ourborous
|
||||
pub hooks: RefCell<Vec<*mut Hook>>,
|
||||
pub hook_arena: typed_arena::Arena<Hook>,
|
||||
|
@ -95,8 +99,51 @@ pub fn create_scoped<P: Properties + 'static>(
|
|||
}
|
||||
|
||||
impl<P: Properties + 'static> Scoped for Scope<P> {
|
||||
fn run(&mut self) {
|
||||
self.run()
|
||||
/// 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
|
||||
///
|
||||
/// Props is ?Sized because we borrow the props and don't need to know the size. P (sized) is used as a marker (unsized)
|
||||
fn run<'bump>(&'bump mut self) {
|
||||
let frame = {
|
||||
let frame = self.frames.next();
|
||||
frame.bump.reset();
|
||||
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(),
|
||||
scope: self.myidx,
|
||||
listeners: &self.listeners,
|
||||
};
|
||||
|
||||
// Note that the actual modification of the vnode head element occurs during this call
|
||||
// let _: DomTree = caller(ctx, props);
|
||||
let _: DomTree = (self.caller)(ctx, &self.props);
|
||||
|
||||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
frame.head_node = node_slot
|
||||
.deref()
|
||||
.borrow_mut()
|
||||
.take()
|
||||
.expect("Viewing did not happen");
|
||||
}
|
||||
|
||||
fn compare_props(&self, new: &Any) -> bool {
|
||||
|
@ -145,103 +192,10 @@ impl<P: Properties + 'static> Scoped for Scope<P> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<P: Properties> Scope<P> {
|
||||
/// 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
|
||||
///
|
||||
/// Props is ?Sized because we borrow the props and don't need to know the size. P (sized) is used as a marker (unsized)
|
||||
pub fn run<'bump>(&'bump mut self) {
|
||||
// pub fn run<'bump, PLocked: Sized + 'static>(&'bump mut self) {
|
||||
let frame = {
|
||||
let frame = self.frames.next();
|
||||
frame.bump.reset();
|
||||
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(),
|
||||
scope: self.myidx,
|
||||
listeners: &self.listeners,
|
||||
};
|
||||
|
||||
// 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();
|
||||
let caller = self.caller;
|
||||
let props = &self.props;
|
||||
|
||||
// Note that the actual modification of the vnode head element occurs during this call
|
||||
let _: DomTree = caller(ctx, props);
|
||||
|
||||
/*
|
||||
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");
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
fn retrieve_listeners(node: &VNode<'static>, listeners: &mut Vec<&Listener>) {
|
||||
if let VNode::Element(el) = *node {
|
||||
for listener in el.listeners {
|
||||
// let g = listener as *const Listener;
|
||||
listeners.push(listener);
|
||||
}
|
||||
for child in el.children {
|
||||
retrieve_listeners(child, listeners);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================
|
||||
// Active-frame related code
|
||||
// ==========================
|
||||
|
||||
// impl<P: Properties> Scope<P> {
|
||||
// /// 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 new_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
|
||||
// self.frames.current_head_node()
|
||||
// }
|
||||
|
||||
// pub fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
|
||||
// self.frames.prev_head_node()
|
||||
// }
|
||||
// }
|
||||
|
||||
// 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.
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
// use crate::{changelist::EditList, nodes::VNode};
|
||||
|
||||
use crate::scope::{create_scoped, Scoped};
|
||||
use crate::{innerlude::*, scope::Properties};
|
||||
use crate::{
|
||||
patch::Edit,
|
||||
scope::{create_scoped, Scoped},
|
||||
};
|
||||
use bumpalo::Bump;
|
||||
use generational_arena::Arena;
|
||||
use std::{
|
||||
|
@ -25,19 +28,31 @@ pub struct VirtualDom {
|
|||
/// like a generational typemap bump arena
|
||||
/// -> IE a cache line for each P type with soem heuristics on optimizing layout
|
||||
pub(crate) components: Arena<Box<dyn Scoped>>,
|
||||
|
||||
// pub(crate) components: Rc<RefCell<Arena<Box<dyn Scoped>>>>,
|
||||
/// The index of the root component.
|
||||
/// Will not be ready if the dom is fresh
|
||||
base_scope: ScopeIdx,
|
||||
|
||||
event_queue: RefCell<VecDeque<LifecycleEvent>>,
|
||||
|
||||
// todo: encapsulate more state into this so we can better reuse it
|
||||
diff_bump: Bump,
|
||||
pub(crate) base_scope: ScopeIdx,
|
||||
|
||||
// Type of the original props. This is done so VirtualDom does not need to be generic.
|
||||
#[doc(hidden)]
|
||||
_root_prop_type: std::any::TypeId,
|
||||
// ======================
|
||||
// DIFF RELATED ITEMs
|
||||
// ======================
|
||||
// // todo: encapsulate more state into this so we can better reuse it
|
||||
pub(crate) diff_bump: Bump,
|
||||
// // be very very very very very careful
|
||||
// pub change_list: EditMachine<'static>,
|
||||
|
||||
// // vdom: &'a VirtualDom,
|
||||
// vdom: *mut Arena<Box<dyn Scoped>>,
|
||||
|
||||
// // vdom: Rc<RefCell<Arena<Box<dyn Scoped>>>>,
|
||||
// pub cur_idx: ScopeIdx,
|
||||
|
||||
// // todo
|
||||
// // do an indexmap sorted by height
|
||||
// dirty_nodes: fxhash::FxHashSet<ScopeIdx>,
|
||||
}
|
||||
|
||||
impl VirtualDom {
|
||||
|
@ -57,51 +72,53 @@ impl VirtualDom {
|
|||
pub fn new_with_props<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
|
||||
let mut components = Arena::new();
|
||||
|
||||
let event_queue = RefCell::new(VecDeque::new());
|
||||
|
||||
// Create a reference to the component in the arena
|
||||
// Note: we are essentially running the "Mount" lifecycle event manually while the vdom doesnt yet exist
|
||||
// This puts the dom in a usable state on creation, rather than being potentially invalid
|
||||
let base_scope = components.insert_with(|id| create_scoped(root, root_props, id, None));
|
||||
|
||||
// evaluate the component, pushing any updates its generates into the lifecycle queue
|
||||
// todo!
|
||||
|
||||
let _root_prop_type = TypeId::of::<P>();
|
||||
let diff_bump = Bump::new();
|
||||
|
||||
Self {
|
||||
components,
|
||||
base_scope,
|
||||
event_queue,
|
||||
diff_bump,
|
||||
_root_prop_type,
|
||||
}
|
||||
todo!()
|
||||
// Self {
|
||||
// // components: RefCell::new(components),
|
||||
// components: components,
|
||||
// // components: Rc::new(RefCell::new(components)),
|
||||
// base_scope,
|
||||
// // event_queue: RefCell::new(VecDeque::new()),
|
||||
// diff_bump: Bump::new(),
|
||||
// _root_prop_type: TypeId::of::<P>(),
|
||||
// }
|
||||
}
|
||||
|
||||
/// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom.
|
||||
///
|
||||
///
|
||||
pub fn rebuild(&mut self) -> Result<EditList<'_>> {
|
||||
|
||||
// pub fn rebuild<'s>(&'s mut self) -> Result<> {
|
||||
// pub fn rebuild<'s>(&'s mut self) -> Result<std::cell::Ref<'_, Arena<Box<dyn Scoped>>>> {
|
||||
pub fn rebuild<'s>(&'s mut self) -> Result<EditList<'s>> {
|
||||
// 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
|
||||
self.components
|
||||
.get_mut(self.base_scope)
|
||||
.expect("Root should always exist");
|
||||
.expect("Root should always exist")
|
||||
.run();
|
||||
|
||||
component.run();
|
||||
let b = Bump::new();
|
||||
|
||||
diff_machine.diff_node(component.old_frame(), component.new_frame());
|
||||
let mut diff_machine = DiffMachine::new(self, &b, self.base_scope);
|
||||
// let mut diff_machine = DiffMachine::new(self, &self.diff_bump, self.base_scope);
|
||||
|
||||
Ok(diff_machine.consume())
|
||||
todo!()
|
||||
// let component = self.components.get(self.base_scope).unwrap();
|
||||
|
||||
// diff_machine.diff_node(component.old_frame(), component.new_frame());
|
||||
|
||||
// let edits = diff_machine.consume();
|
||||
|
||||
// self.diff_bump = b;
|
||||
|
||||
// Ok(edits)
|
||||
}
|
||||
|
||||
/// This method is the most sophisticated way of updating the virtual dom after an external event has been triggered.
|
||||
|
@ -129,23 +146,32 @@ impl VirtualDom {
|
|||
///
|
||||
/// ```
|
||||
pub fn progress_with_event(&mut self, event: EventTrigger) -> Result<EditList<'_>> {
|
||||
let component = self
|
||||
.components
|
||||
.get_mut(event.component_id)
|
||||
.expect("Component should exist if an event was triggered");
|
||||
// self.components
|
||||
// .borrow_mut()
|
||||
// .get_mut(event.component_id)
|
||||
// .map(|f| {
|
||||
// f.call_listener(event);
|
||||
// f
|
||||
// })
|
||||
// .map(|f| f.run())
|
||||
// .expect("Borrowing should not fail");
|
||||
|
||||
component.call_listener(event);
|
||||
// component.call_listener(event);
|
||||
|
||||
// .expect("Component should exist if an event was triggered");
|
||||
// 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);
|
||||
// self.diff_bump.reset();
|
||||
// let mut diff_machine = DiffMachine::new(&mut self, event.component_id);
|
||||
// let mut diff_machine =
|
||||
// DiffMachine::new(&self.diff_bump, &mut self.components, event.component_id);
|
||||
|
||||
component.run();
|
||||
diff_machine.diff_node(component.old_frame(), component.new_frame());
|
||||
// component.run();
|
||||
// diff_machine.diff_node(component.old_frame(), component.new_frame());
|
||||
|
||||
Ok(diff_machine.consume())
|
||||
todo!()
|
||||
// Ok(diff_machine.consume())
|
||||
// Err(crate::error::Error::NoEvent)
|
||||
// Mark dirty components. Descend from the highest node until all dirty nodes are updated.
|
||||
// let mut affected_components = Vec::new();
|
||||
|
@ -159,115 +185,8 @@ impl VirtualDom {
|
|||
|
||||
// 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: _,
|
||||
} => {}
|
||||
|
||||
// The parent for this component generated new props and the component needs update
|
||||
LifecycleType::PropsChanged {
|
||||
props: _,
|
||||
component: _,
|
||||
} => {}
|
||||
|
||||
// Component was messaged via the internal subscription service
|
||||
LifecycleType::Callback { component: _ } => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Pop the top event of the internal lifecycle event queu
|
||||
pub fn pop_event(&self) -> Option<LifecycleEvent> {
|
||||
self.event_queue.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.
|
||||
/// todo
|
||||
/// change this to accept a modification closure, so the user gets 0-cost access to the props item.
|
||||
/// Good for cases where the props is changed remotely (or something similar) and building a whole new props item would
|
||||
/// be wasteful
|
||||
pub fn update_props<P: 'static>(
|
||||
&mut self,
|
||||
updater: impl FnOnce(&mut P),
|
||||
) -> Result<LifecycleEvent> {
|
||||
todo!()
|
||||
// // pub fn update_props<P: 'static>(&mut self, new_props: P) -> Result<LifecycleEvent> {
|
||||
// // Ensure the props match
|
||||
// if TypeId::of::<P>() != self._root_prop_type {
|
||||
// return Err(Error::WrongProps);
|
||||
// }
|
||||
|
||||
// Ok(LifecycleEvent {
|
||||
// event_type: LifecycleType::PropsChanged {
|
||||
// props: Box::new(new_props),
|
||||
// component: self.base_scope,
|
||||
// },
|
||||
// })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LifecycleEvent {
|
||||
pub event_type: LifecycleType,
|
||||
}
|
||||
|
||||
pub enum LifecycleType {
|
||||
// Component needs to be mounted, but its scope doesn't exist yet
|
||||
Mount {
|
||||
to: ScopeIdx,
|
||||
under: usize,
|
||||
props: Box<dyn std::any::Any>,
|
||||
},
|
||||
|
||||
// Parent was evalauted causing new props to generate
|
||||
PropsChanged {
|
||||
props: Box<dyn std::any::Any>,
|
||||
component: ScopeIdx,
|
||||
},
|
||||
|
||||
// Hook for the subscription API
|
||||
Callback {
|
||||
component: ScopeIdx,
|
||||
},
|
||||
}
|
||||
|
||||
impl LifecycleEvent {
|
||||
fn index(&self) -> Option<ScopeIdx> {
|
||||
match &self.event_type {
|
||||
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| {
|
||||
todo!()
|
||||
// ctx.render(|ctx| {
|
||||
// use crate::builder::*;
|
||||
// let bump = ctx.bump();
|
||||
// div(bump).child(text("hello, world")).finish()
|
||||
// })
|
||||
});
|
||||
let edits = dom.rebuild().unwrap();
|
||||
println!("{:#?}", edits);
|
||||
}
|
||||
}
|
||||
// struct LockedEdits<'src> {
|
||||
// edits:
|
||||
// }
|
||||
|
|
Loading…
Reference in a new issue