2021-02-15 04:39:46 +00:00
|
|
|
// use crate::{changelist::EditList, nodes::VNode};
|
2021-02-24 06:32:50 +00:00
|
|
|
|
2021-03-09 05:58:20 +00:00
|
|
|
use crate::innerlude::*;
|
2021-03-05 20:02:36 +00:00
|
|
|
use crate::{
|
|
|
|
patch::Edit,
|
|
|
|
scope::{create_scoped, Scoped},
|
|
|
|
};
|
2021-02-03 07:26:04 +00:00
|
|
|
use bumpalo::Bump;
|
2021-03-03 07:27:26 +00:00
|
|
|
use generational_arena::Arena;
|
2021-03-11 00:42:31 +00:00
|
|
|
|
2021-02-03 07:26:04 +00:00
|
|
|
|
|
|
|
/// An integrated virtual node system that progresses events and diffs UI trees.
|
|
|
|
/// Differences are converted into patches which a renderer can use to draw the UI.
|
2021-02-13 08:19:35 +00:00
|
|
|
pub struct VirtualDom {
|
2021-02-03 07:26:04 +00:00
|
|
|
/// All mounted components are arena allocated to make additions, removals, and references easy to work with
|
2021-03-05 04:57:25 +00:00
|
|
|
/// A generational arena is used to re-use slots of deleted scopes without having to resize the underlying arena.
|
|
|
|
///
|
|
|
|
/// eventually, come up with a better datastructure that reuses boxes for known P types
|
|
|
|
/// 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>>,
|
2021-03-05 20:02:36 +00:00
|
|
|
// pub(crate) components: Rc<RefCell<Arena<Box<dyn Scoped>>>>,
|
2021-02-07 22:38:17 +00:00
|
|
|
/// The index of the root component.
|
2021-02-24 06:31:19 +00:00
|
|
|
/// Will not be ready if the dom is fresh
|
2021-03-05 20:02:36 +00:00
|
|
|
pub(crate) base_scope: ScopeIdx,
|
2021-02-24 06:31:19 +00:00
|
|
|
|
2021-03-04 17:03:22 +00:00
|
|
|
// Type of the original props. This is done so VirtualDom does not need to be generic.
|
2021-02-13 08:19:35 +00:00
|
|
|
#[doc(hidden)]
|
|
|
|
_root_prop_type: std::any::TypeId,
|
2021-03-05 20:02:36 +00:00
|
|
|
// ======================
|
|
|
|
// 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>,
|
2021-02-03 07:26:04 +00:00
|
|
|
}
|
|
|
|
|
2021-02-13 08:19:35 +00:00
|
|
|
impl VirtualDom {
|
2021-02-03 07:26:04 +00:00
|
|
|
/// Create a new instance of the Dioxus Virtual Dom with no properties for the root component.
|
|
|
|
///
|
|
|
|
/// This means that the root component must either consumes its own context, or statics are used to generate the page.
|
|
|
|
/// The root component can access things like routing in its context.
|
2021-03-09 05:58:20 +00:00
|
|
|
pub fn new(root: FC<()>) -> Self {
|
|
|
|
Self::new_with_props(root, ())
|
2021-02-03 07:26:04 +00:00
|
|
|
}
|
|
|
|
/// Start a new VirtualDom instance with a dependent props.
|
|
|
|
/// Later, the props can be updated by calling "update" with a new set of props, causing a set of re-renders.
|
|
|
|
///
|
|
|
|
/// This is useful when a component tree can be driven by external state (IE SSR) but it would be too expensive
|
|
|
|
/// to toss out the entire tree.
|
2021-03-11 00:42:31 +00:00
|
|
|
pub fn new_with_props<P: Properties + 'static>(_root: FC<P>, _root_props: P) -> Self {
|
2021-03-09 05:58:20 +00:00
|
|
|
// let mut components = Arena::new();
|
2021-03-08 02:28:20 +00:00
|
|
|
// let mut components = Arena::new();
|
2021-02-07 03:19:56 +00:00
|
|
|
|
|
|
|
// Create a reference to the component in the arena
|
2021-02-24 06:31:19 +00:00
|
|
|
// 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
|
2021-03-08 02:28:20 +00:00
|
|
|
// let base_scope = components.insert_with(|id| create_scoped(root, root_props, id, None));
|
2021-02-07 22:38:17 +00:00
|
|
|
|
2021-03-05 20:02:36 +00:00
|
|
|
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>(),
|
|
|
|
// }
|
2021-02-12 08:07:35 +00:00
|
|
|
}
|
2021-02-03 07:26:04 +00:00
|
|
|
|
2021-02-24 15:12:26 +00:00
|
|
|
/// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom.
|
2021-03-05 20:02:36 +00:00
|
|
|
|
|
|
|
// 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>> {
|
2021-02-24 08:51:26 +00:00
|
|
|
// 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();
|
|
|
|
|
2021-03-05 20:02:36 +00:00
|
|
|
self.components
|
2021-02-24 08:51:26 +00:00
|
|
|
.get_mut(self.base_scope)
|
2021-03-05 20:02:36 +00:00
|
|
|
.expect("Root should always exist")
|
|
|
|
.run();
|
2021-02-24 08:51:26 +00:00
|
|
|
|
2021-03-11 00:42:31 +00:00
|
|
|
let _b = Bump::new();
|
2021-02-24 08:51:26 +00:00
|
|
|
|
2021-03-08 02:28:20 +00:00
|
|
|
let mut diff_machine = DiffMachine::new(&self.diff_bump);
|
2021-03-05 20:02:36 +00:00
|
|
|
// let mut diff_machine = DiffMachine::new(self, &self.diff_bump, self.base_scope);
|
2021-02-24 08:51:26 +00:00
|
|
|
|
2021-03-08 02:28:20 +00:00
|
|
|
// todo!()
|
2021-03-05 20:02:36 +00:00
|
|
|
|
2021-03-08 02:28:20 +00:00
|
|
|
let component = self.components.get(self.base_scope).unwrap();
|
|
|
|
diff_machine.diff_node(component.old_frame(), component.new_frame());
|
|
|
|
let edits = diff_machine.consume();
|
2021-03-05 20:02:36 +00:00
|
|
|
// self.diff_bump = b;
|
2021-03-08 02:28:20 +00:00
|
|
|
Ok(edits)
|
2021-02-24 08:51:26 +00:00
|
|
|
}
|
|
|
|
|
2021-02-12 21:11:33 +00:00
|
|
|
/// 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
|
|
|
|
/// dom to completion, tagging components that need updates, compressing events together, and finally emitting a single
|
|
|
|
/// change list.
|
|
|
|
///
|
|
|
|
/// If implementing an external renderer, this is the perfect method to combine with an async event loop that waits on
|
|
|
|
/// listeners.
|
|
|
|
///
|
2021-02-24 06:31:19 +00:00
|
|
|
/// Note: this method is not async and does not provide suspense-like functionality. It is up to the renderer to provide the
|
|
|
|
/// executor and handlers for suspense as show in the example.
|
2021-02-12 21:11:33 +00:00
|
|
|
///
|
2021-02-24 06:31:19 +00:00
|
|
|
/// ```ignore
|
|
|
|
/// let (sender, receiver) = channel::new();
|
|
|
|
/// sender.send(EventTrigger::start());
|
2021-02-12 21:11:33 +00:00
|
|
|
///
|
2021-02-24 06:31:19 +00:00
|
|
|
/// let mut dom = VirtualDom::new();
|
|
|
|
/// dom.suspense_handler(|event| sender.send(event));
|
2021-02-12 21:11:33 +00:00
|
|
|
///
|
2021-02-24 06:31:19 +00:00
|
|
|
/// while let Ok(diffs) = dom.progress_with_event(receiver.recv().await) {
|
|
|
|
/// render(diffs);
|
|
|
|
/// }
|
2021-02-12 21:11:33 +00:00
|
|
|
///
|
|
|
|
/// ```
|
2021-03-11 00:42:31 +00:00
|
|
|
pub fn progress_with_event(&mut self, _event: EventTrigger) -> Result<EditList<'_>> {
|
2021-03-05 20:02:36 +00:00
|
|
|
// 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);
|
|
|
|
|
|
|
|
// .expect("Component should exist if an event was triggered");
|
2021-02-28 08:08:08 +00:00
|
|
|
// 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
|
2021-03-05 20:02:36 +00:00
|
|
|
// 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);
|
2021-02-28 08:08:08 +00:00
|
|
|
|
2021-03-05 20:02:36 +00:00
|
|
|
// component.run();
|
|
|
|
// diff_machine.diff_node(component.old_frame(), component.new_frame());
|
2021-02-28 08:08:08 +00:00
|
|
|
|
2021-03-05 20:02:36 +00:00
|
|
|
todo!()
|
|
|
|
// Ok(diff_machine.consume())
|
2021-02-28 08:08:08 +00:00
|
|
|
// Err(crate::error::Error::NoEvent)
|
2021-02-24 08:51:26 +00:00
|
|
|
// Mark dirty components. Descend from the highest node until all dirty nodes are updated.
|
2021-02-28 08:08:08 +00:00
|
|
|
// let mut affected_components = Vec::new();
|
2021-02-24 06:31:19 +00:00
|
|
|
|
2021-02-28 08:08:08 +00:00
|
|
|
// while let Some(event) = self.pop_event() {
|
|
|
|
// if let Some(component_idx) = event.index() {
|
|
|
|
// affected_components.push(component_idx);
|
|
|
|
// }
|
|
|
|
// self.process_lifecycle(event)?;
|
|
|
|
// }
|
2021-02-13 07:49:10 +00:00
|
|
|
|
2021-02-28 08:08:08 +00:00
|
|
|
// todo!()
|
2021-02-12 21:11:33 +00:00
|
|
|
}
|
2021-02-07 03:19:56 +00:00
|
|
|
}
|
2021-02-12 08:07:35 +00:00
|
|
|
|
2021-03-08 02:28:20 +00:00
|
|
|
enum LifeCycleEvent {
|
2021-03-11 00:42:10 +00:00
|
|
|
Mount {},
|
2021-03-08 02:28:20 +00:00
|
|
|
}
|
2021-03-11 00:42:10 +00:00
|
|
|
|
|
|
|
// todo: add some "handle" to the vdom. Or a way of giving out closures that can mutate the vdoms internal data.
|