2021-02-03 07:26:04 +00:00
|
|
|
/*
|
|
|
|
The Dioxus Virtual Dom integrates an event system and virtual nodes to create reactive user interfaces.
|
|
|
|
|
|
|
|
The Dioxus VDom uses the same underlying mechanics as Dodrio (double buffering, bump dom, etc).
|
|
|
|
Instead of making the allocator very obvious, we choose to parametrize over the DomTree trait. For our purposes,
|
|
|
|
the DomTree trait is simply an abstraction over a lazy dom builder, much like the iterator trait.
|
|
|
|
|
|
|
|
This means we can accept DomTree anywhere as well as return it. All components therefore look like this:
|
|
|
|
```ignore
|
2021-02-07 03:19:56 +00:00
|
|
|
function Component(ctx: Context<()>) -> VNode {
|
|
|
|
ctx.view(html! {<div> "hello world" </div>})
|
2021-02-03 07:26:04 +00:00
|
|
|
}
|
|
|
|
```
|
|
|
|
It's not quite as sexy as statics, but there's only so much you can do. The goal is to get statics working with the FC macro,
|
|
|
|
so types don't get in the way of you and your component writing. Fortunately, this is all generic enough to be split out
|
|
|
|
into its own lib (IE, lazy loading wasm chunks by function (exciting stuff!))
|
|
|
|
|
|
|
|
```ignore
|
|
|
|
#[fc] // gets translated into a function.
|
|
|
|
static Component: FC = |ctx| {
|
2021-02-07 03:19:56 +00:00
|
|
|
ctx.view(html! {<div> "hello world" </div>})
|
2021-02-03 07:26:04 +00:00
|
|
|
}
|
|
|
|
```
|
|
|
|
*/
|
2021-02-07 22:38:17 +00:00
|
|
|
use crate::inner::*;
|
2021-02-03 07:26:04 +00:00
|
|
|
use crate::nodes::VNode;
|
2021-02-07 21:21:38 +00:00
|
|
|
use any::Any;
|
2021-02-03 07:26:04 +00:00
|
|
|
use bumpalo::Bump;
|
2021-02-07 03:19:56 +00:00
|
|
|
use generational_arena::{Arena, Index};
|
|
|
|
use std::{
|
2021-02-07 21:21:38 +00:00
|
|
|
any::{self, TypeId},
|
2021-02-07 03:19:56 +00:00
|
|
|
cell::{RefCell, UnsafeCell},
|
|
|
|
future::Future,
|
2021-02-07 19:59:34 +00:00
|
|
|
marker::PhantomData,
|
2021-02-07 03:19:56 +00:00
|
|
|
sync::atomic::AtomicUsize,
|
|
|
|
};
|
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-07 03:19:56 +00:00
|
|
|
pub struct VirtualDom<P: Properties> {
|
2021-02-03 07:26:04 +00:00
|
|
|
/// All mounted components are arena allocated to make additions, removals, and references easy to work with
|
|
|
|
/// A generational arean is used to re-use slots of deleted scopes without having to resize the underlying arena.
|
|
|
|
components: Arena<Scope>,
|
|
|
|
|
2021-02-07 22:38:17 +00:00
|
|
|
/// The index of the root component.
|
2021-02-07 03:19:56 +00:00
|
|
|
base_scope: Index,
|
|
|
|
|
2021-02-03 07:26:04 +00:00
|
|
|
/// Components generate lifecycle events
|
|
|
|
event_queue: Vec<LifecycleEvent>,
|
|
|
|
|
2021-02-07 03:19:56 +00:00
|
|
|
root_props: P,
|
2021-02-03 07:26:04 +00:00
|
|
|
}
|
|
|
|
|
2021-02-07 03:19:56 +00:00
|
|
|
/// Implement VirtualDom with no props for components that initialize their state internal to the VDom rather than externally.
|
|
|
|
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-02-03 19:07:07 +00:00
|
|
|
pub fn new(root: FC<()>) -> Self {
|
2021-02-07 03:19:56 +00:00
|
|
|
Self::new_with_props(root, ())
|
2021-02-03 07:26:04 +00:00
|
|
|
}
|
2021-02-07 03:19:56 +00:00
|
|
|
}
|
2021-02-03 07:26:04 +00:00
|
|
|
|
2021-02-07 03:19:56 +00:00
|
|
|
/// Implement the VirtualDom for any Properties
|
|
|
|
impl<P: Properties + 'static> VirtualDom<P> {
|
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-02-07 03:19:56 +00:00
|
|
|
pub fn new_with_props(root: FC<P>, root_props: P) -> Self {
|
2021-02-07 22:38:17 +00:00
|
|
|
// 1. Create the component arena
|
|
|
|
// 2. Create the base scope (can never be removed)
|
|
|
|
// 3. Create the lifecycle queue
|
|
|
|
// 4. Create the event queue
|
2021-02-07 03:19:56 +00:00
|
|
|
|
|
|
|
// Arena allocate all the components
|
|
|
|
// This should make it *really* easy to store references in events and such
|
|
|
|
let mut components = Arena::new();
|
|
|
|
|
|
|
|
// Create a reference to the component in the arena
|
|
|
|
let base_scope = components.insert(Scope::new(root));
|
|
|
|
|
2021-02-07 22:38:17 +00:00
|
|
|
// Create a new mount event with no root container
|
|
|
|
let first_event = LifecycleEvent::mount(base_scope, None, 0);
|
|
|
|
|
2021-02-07 03:19:56 +00:00
|
|
|
// Create an event queue with a mount for the base scope
|
2021-02-07 22:38:17 +00:00
|
|
|
let event_queue = vec![first_event];
|
2021-02-07 03:19:56 +00:00
|
|
|
|
2021-02-03 07:26:04 +00:00
|
|
|
Self {
|
2021-02-07 03:19:56 +00:00
|
|
|
components,
|
|
|
|
base_scope,
|
|
|
|
event_queue,
|
|
|
|
root_props,
|
2021-02-03 07:26:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Pop an event off the even queue and process it
|
2021-02-07 03:19:56 +00:00
|
|
|
pub fn progress(&mut self) -> Result<(), ()> {
|
|
|
|
let LifecycleEvent { index, event_type } = self.event_queue.pop().ok_or(())?;
|
2021-02-03 07:26:04 +00:00
|
|
|
|
2021-02-07 03:19:56 +00:00
|
|
|
let scope = self.components.get(index).ok_or(())?;
|
|
|
|
|
|
|
|
match event_type {
|
|
|
|
// Component needs to be mounted to the virtual dom
|
2021-02-07 22:38:17 +00:00
|
|
|
LifecycleType::Mount { to, under } => {
|
2021-02-07 03:19:56 +00:00
|
|
|
// todo! run the FC with the bump allocator
|
|
|
|
// Run it with its properties
|
2021-02-07 22:38:17 +00:00
|
|
|
if let Some(other) = to {
|
|
|
|
// mount to another component
|
|
|
|
let p = ();
|
|
|
|
} else {
|
|
|
|
// mount to the root
|
|
|
|
}
|
2021-02-07 03:19:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// The parent for this component generated new props and the component needs update
|
|
|
|
LifecycleType::PropsChanged {} => {}
|
2021-02-03 07:26:04 +00:00
|
|
|
|
2021-02-07 03:19:56 +00:00
|
|
|
// Component was successfully mounted to the dom
|
|
|
|
LifecycleType::Mounted {} => {}
|
|
|
|
|
|
|
|
// Component was removed from the DOM
|
|
|
|
// Run any destructors and cleanup for the hooks and the dump the component
|
|
|
|
LifecycleType::Removed {} => {
|
|
|
|
let f = self.components.remove(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Component was messaged via the internal subscription service
|
|
|
|
LifecycleType::Messaged => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Update the root props, causing a full event cycle
|
|
|
|
pub fn update_props(&mut self, new_props: P) {}
|
|
|
|
|
|
|
|
/// Run through every event in the event queue until the events are empty.
|
|
|
|
/// Function is asynchronous to allow for async components to finish their work.
|
|
|
|
pub async fn progess_completely() {}
|
|
|
|
|
|
|
|
/// Create a new context object for a given component and scope
|
|
|
|
fn new_context<T: Properties>(&self) -> Context<T> {
|
|
|
|
todo!()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Stop writing to the current buffer and start writing to the new one.
|
|
|
|
/// This should be done inbetween CallbackEvent handling, but not between lifecycle events.
|
|
|
|
pub fn swap_buffers(&mut self) {}
|
2021-02-03 07:26:04 +00:00
|
|
|
}
|
|
|
|
|
2021-02-07 03:19:56 +00:00
|
|
|
pub struct LifecycleEvent {
|
|
|
|
pub index: Index,
|
|
|
|
pub event_type: LifecycleType,
|
|
|
|
}
|
|
|
|
impl LifecycleEvent {
|
2021-02-07 22:38:17 +00:00
|
|
|
fn mount(which: Index, to: Option<Index>, under: usize) -> Self {
|
2021-02-07 03:19:56 +00:00
|
|
|
Self {
|
2021-02-07 22:38:17 +00:00
|
|
|
index: which,
|
|
|
|
event_type: LifecycleType::Mount { to, under },
|
2021-02-07 03:19:56 +00:00
|
|
|
}
|
2021-02-03 07:26:04 +00:00
|
|
|
}
|
|
|
|
}
|
2021-02-07 03:19:56 +00:00
|
|
|
/// The internal lifecycle event system is managed by these
|
|
|
|
pub enum LifecycleType {
|
2021-02-07 22:38:17 +00:00
|
|
|
Mount { to: Option<Index>, under: usize },
|
2021-02-07 03:19:56 +00:00
|
|
|
PropsChanged,
|
|
|
|
Mounted,
|
|
|
|
Removed,
|
|
|
|
Messaged,
|
|
|
|
}
|