wip: move testdom methods into virtualdom
This commit is contained in:
1 parent
f2e343c154
commit
d2f0547692
11 files changed
+247
-310
No files matched your search
@@ -49,7 +49,7 @@
|
|||||||
Dioxus is a portable, performant, and ergonomic framework for building cross-platform user experiences in Rust.
|
Dioxus is a portable, performant, and ergonomic framework for building cross-platform user experiences in Rust.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn App((cx, props): Scope<()>) -> Element {
|
fn App(cx: Context, props: &()) -> Element {
|
||||||
let mut count = use_state(cx, || 0);
|
let mut count = use_state(cx, || 0);
|
||||||
|
|
||||||
cx.render(rsx!(
|
cx.render(rsx!(
|
||||||
@@ -141,16 +141,16 @@ Dioxus is heavily inspired by React, but we want your transition to feel like an
|
|||||||
| NodeRef | ✅ | ✅ | gain direct access to nodes |
|
| NodeRef | ✅ | ✅ | gain direct access to nodes |
|
||||||
| Re-hydration | ✅ | ✅ | Pre-render to HTML to speed up first contentful paint |
|
| Re-hydration | ✅ | ✅ | Pre-render to HTML to speed up first contentful paint |
|
||||||
| Jank-Free Rendering | ✅ | ✅ | Large diffs are segmented across frames for silky-smooth transitions |
|
| Jank-Free Rendering | ✅ | ✅ | Large diffs are segmented across frames for silky-smooth transitions |
|
||||||
| Cooperative Scheduling | ✅ | ✅ | Prioritize important events over non-important events |
|
| Effects | ✅ | ✅ | Run effects after a component has been committed to render |
|
||||||
|
| Cooperative Scheduling | 🛠 | ✅ | Prioritize important events over non-important events |
|
||||||
|
| Server Components | 🛠 | ✅ | Hybrid components for SPA and Server |
|
||||||
|
| Bundle Splitting | 👀 | ✅ | Efficiently and asynchronously load the app |
|
||||||
|
| Lazy Components | 👀 | ✅ | Dynamically load the new components as the page is loaded |
|
||||||
|
| 1st class global state | ✅ | ✅ | redux/recoil/mobx on top of context |
|
||||||
| Runs natively | ✅ | ❓ | runs as a portable binary w/o a runtime (Node) |
|
| Runs natively | ✅ | ❓ | runs as a portable binary w/o a runtime (Node) |
|
||||||
| 1st class global state | ✅ | ❓ | redux/recoil/mobx on top of context |
|
|
||||||
| Subtree Memoization | ✅ | ❓ | skip diffing static element subtrees |
|
| Subtree Memoization | ✅ | ❓ | skip diffing static element subtrees |
|
||||||
| Compile-time correct | ✅ | ❓ | Throw errors on invalid template layouts |
|
| Compile-time correct | ✅ | ❓ | Throw errors on invalid template layouts |
|
||||||
| Heuristic Engine | ✅ | ❓ | track component memory usage to minimize future allocations |
|
| Heuristic Engine | ✅ | ❓ | track component memory usage to minimize future allocations |
|
||||||
| Effects | 🛠 | ✅ | Run effects after a component has been committed to render |
|
|
||||||
| Server Components | 🛠 | ✅ | Hybrid components for SPA and Server |
|
|
||||||
| Bundle Splitting | 👀 | ✅ | Hybrid components for SPA and Server |
|
|
||||||
| Lazy Components | 👀 | ✅ | Dynamically load the new components as the page is loaded |
|
|
||||||
| Fine-grained reactivity | 👀 | ❓ | Skip diffing for fine-grain updates |
|
| Fine-grained reactivity | 👀 | ❓ | Skip diffing for fine-grain updates |
|
||||||
|
|
||||||
- ✅ = implemented and working
|
- ✅ = implemented and working
|
||||||
|
|||||||
+18
-39
@@ -89,9 +89,7 @@
|
|||||||
//! - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
|
//! - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
|
||||||
|
|
||||||
use crate::innerlude::*;
|
use crate::innerlude::*;
|
||||||
use futures_channel::mpsc::UnboundedSender;
|
|
||||||
use fxhash::{FxHashMap, FxHashSet};
|
use fxhash::{FxHashMap, FxHashSet};
|
||||||
use slab::Slab;
|
|
||||||
use DomEdit::*;
|
use DomEdit::*;
|
||||||
|
|
||||||
/// Our DiffMachine is an iterative tree differ.
|
/// Our DiffMachine is an iterative tree differ.
|
||||||
@@ -325,30 +323,20 @@ impl<'bump> DiffState<'bump> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_component_node(&mut self, vcomponent: &'bump VComponent<'bump>) {
|
fn create_component_node(&mut self, vcomponent: &'bump VComponent<'bump>) {
|
||||||
// let caller = vcomponent.caller;
|
|
||||||
|
|
||||||
let parent_idx = self.stack.current_scope().unwrap();
|
let parent_idx = self.stack.current_scope().unwrap();
|
||||||
|
|
||||||
let shared: UnboundedSender<SchedulerMsg> = todo!();
|
|
||||||
// let shared: UnboundedSender<SchedulerMsg> = self.sender.clone();
|
|
||||||
|
|
||||||
// Insert a new scope into our component list
|
// Insert a new scope into our component list
|
||||||
let parent_scope = self.scopes.get_scope(&parent_idx).unwrap();
|
let parent_scope = self.scopes.get_scope(&parent_idx).unwrap();
|
||||||
|
let height = parent_scope.height + 1;
|
||||||
|
let subtree = parent_scope.subtree.get();
|
||||||
|
|
||||||
let new_idx: ScopeId = todo!();
|
let parent_scope = unsafe { self.scopes.get_scope_mut(&parent_idx) };
|
||||||
// self
|
let caller = unsafe { std::mem::transmute(vcomponent.caller as *const _) };
|
||||||
// .new_with_key(fc_ptr, vcomp, parent_scope, height, subtree, sender);
|
let fc_ptr = vcomponent.user_fc;
|
||||||
|
|
||||||
// .(|new_idx| {
|
let new_idx = self
|
||||||
// // ScopeInner::new(
|
.scopes
|
||||||
// vcomponent,
|
.new_with_key(fc_ptr, caller, parent_scope, height, subtree);
|
||||||
// new_idx,
|
|
||||||
// Some(parent_idx),
|
|
||||||
// parent_scope.height + 1,
|
|
||||||
// parent_scope.subtree(),
|
|
||||||
// shared,
|
|
||||||
// // )
|
|
||||||
// });
|
|
||||||
|
|
||||||
// Actually initialize the caller's slot with the right address
|
// Actually initialize the caller's slot with the right address
|
||||||
vcomponent.associated_scope.set(Some(new_idx));
|
vcomponent.associated_scope.set(Some(new_idx));
|
||||||
@@ -356,7 +344,7 @@ impl<'bump> DiffState<'bump> {
|
|||||||
if !vcomponent.can_memoize {
|
if !vcomponent.can_memoize {
|
||||||
let cur_scope = self.scopes.get_scope(&parent_idx).unwrap();
|
let cur_scope = self.scopes.get_scope(&parent_idx).unwrap();
|
||||||
let extended = unsafe { std::mem::transmute(vcomponent) };
|
let extended = unsafe { std::mem::transmute(vcomponent) };
|
||||||
cur_scope.items.get_mut().borrowed_props.push(extended);
|
cur_scope.items.borrow_mut().borrowed_props.push(extended);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
@@ -368,28 +356,19 @@ impl<'bump> DiffState<'bump> {
|
|||||||
log::debug!(
|
log::debug!(
|
||||||
"initializing component {:?} with height {:?}",
|
"initializing component {:?} with height {:?}",
|
||||||
new_idx,
|
new_idx,
|
||||||
parent_scope.height + 1
|
height + 1
|
||||||
);
|
);
|
||||||
|
|
||||||
// Run the scope for one iteration to initialize it
|
// Run the scope for one iteration to initialize it
|
||||||
//
|
if self.scopes.run_scope(&new_idx) {
|
||||||
todo!("run scope");
|
// Take the node that was just generated from running the component
|
||||||
// if new_component.run_scope(self) {
|
let nextnode = self.scopes.fin_head(&new_idx);
|
||||||
// // Take the node that was just generated from running the component
|
self.stack.create_component(new_idx, nextnode);
|
||||||
// let nextnode = new_component.frames.fin_head();
|
|
||||||
// self.stack.create_component(new_idx, nextnode);
|
|
||||||
|
|
||||||
// //
|
if new_component.is_subtree_root.get() {
|
||||||
// /*
|
self.stack.push_subtree();
|
||||||
// tree_item {
|
}
|
||||||
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// */
|
|
||||||
// if new_component.is_subtree_root.get() {
|
|
||||||
// self.stack.push_subtree();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Finally, insert this scope as a seen node.
|
// Finally, insert this scope as a seen node.
|
||||||
self.seen_scopes.insert(new_idx);
|
self.seen_scopes.insert(new_idx);
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ pub(crate) mod mutations;
|
|||||||
pub(crate) mod nodes;
|
pub(crate) mod nodes;
|
||||||
pub(crate) mod scope;
|
pub(crate) mod scope;
|
||||||
pub(crate) mod scopearena;
|
pub(crate) mod scopearena;
|
||||||
pub(crate) mod test_dom;
|
|
||||||
pub(crate) mod virtual_dom;
|
pub(crate) mod virtual_dom;
|
||||||
|
|
||||||
pub(crate) mod innerlude {
|
pub(crate) mod innerlude {
|
||||||
@@ -34,7 +33,6 @@ pub(crate) mod innerlude {
|
|||||||
pub use crate::nodes::*;
|
pub use crate::nodes::*;
|
||||||
pub use crate::scope::*;
|
pub use crate::scope::*;
|
||||||
pub use crate::scopearena::*;
|
pub use crate::scopearena::*;
|
||||||
pub use crate::test_dom::*;
|
|
||||||
pub use crate::virtual_dom::*;
|
pub use crate::virtual_dom::*;
|
||||||
|
|
||||||
pub type Element = Option<NodeLink>;
|
pub type Element = Option<NodeLink>;
|
||||||
@@ -42,9 +40,10 @@ pub(crate) mod innerlude {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub use crate::innerlude::{
|
pub use crate::innerlude::{
|
||||||
Attribute, Context, DioxusElement, DomEdit, Element, ElementId, EventPriority, LazyNodes,
|
Attribute, Context, DioxusElement, DomEdit, Element, ElementId, EventPriority, IntoVNode,
|
||||||
Listener, MountType, Mutations, NodeFactory, Properties, SchedulerMsg, ScopeChildren, ScopeId,
|
LazyNodes, Listener, MountType, Mutations, NodeFactory, Properties, SchedulerMsg,
|
||||||
TestDom, UserEvent, VAnchor, VElement, VFragment, VNode, VSuspended, VirtualDom, FC,
|
ScopeChildren, ScopeId, UserEvent, VAnchor, VElement, VFragment, VNode, VSuspended, VirtualDom,
|
||||||
|
FC,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ use std::{
|
|||||||
///
|
///
|
||||||
/// It is used during the diffing/rendering process as a runtime key into an existing set of nodes. The "render" key
|
/// It is used during the diffing/rendering process as a runtime key into an existing set of nodes. The "render" key
|
||||||
/// is essentially a unique key to guarantee safe usage of the Node.
|
/// is essentially a unique key to guarantee safe usage of the Node.
|
||||||
|
///
|
||||||
|
/// todo: remove "clone"
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct NodeLink {
|
pub struct NodeLink {
|
||||||
pub(crate) link_idx: usize,
|
pub(crate) link_idx: usize,
|
||||||
@@ -177,7 +179,7 @@ impl<'src> VNode<'src> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn children(&self) -> &[VNode<'src>] {
|
pub(crate) fn children(&self) -> &[VNode<'src>] {
|
||||||
match &self {
|
match &self {
|
||||||
VNode::Fragment(f) => f.children,
|
VNode::Fragment(f) => f.children,
|
||||||
VNode::Component(c) => todo!("children are not accessible through this"),
|
VNode::Component(c) => todo!("children are not accessible through this"),
|
||||||
@@ -396,7 +398,6 @@ pub struct VComponent<'src> {
|
|||||||
pub(crate) comparator: Option<&'src dyn Fn(&VComponent) -> bool>,
|
pub(crate) comparator: Option<&'src dyn Fn(&VComponent) -> bool>,
|
||||||
|
|
||||||
pub(crate) drop_props: RefCell<Option<BumpBox<'src, dyn FnMut()>>>,
|
pub(crate) drop_props: RefCell<Option<BumpBox<'src, dyn FnMut()>>>,
|
||||||
// pub(crate) comparator: Option<BumpBox<'src, dyn Fn(&VComponent) -> bool + 'src>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct VSuspended<'a> {
|
pub struct VSuspended<'a> {
|
||||||
@@ -825,7 +826,7 @@ impl IntoVNode<'_> for Arguments<'_> {
|
|||||||
/// ## Example
|
/// ## Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// const App: FC<()> = |(cx, props)|{
|
/// const App: FC<()> = |cx, props|{
|
||||||
/// cx.render(rsx!{
|
/// cx.render(rsx!{
|
||||||
/// CustomCard {
|
/// CustomCard {
|
||||||
/// h1 {}
|
/// h1 {}
|
||||||
@@ -834,7 +835,7 @@ impl IntoVNode<'_> for Arguments<'_> {
|
|||||||
/// })
|
/// })
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// const CustomCard: FC<()> = |(cx, props)|{
|
/// const CustomCard: FC<()> = |cx, props|{
|
||||||
/// cx.render(rsx!{
|
/// cx.render(rsx!{
|
||||||
/// div {
|
/// div {
|
||||||
/// h1 {"Title card"}
|
/// h1 {"Title card"}
|
||||||
|
|||||||
+14
-11
@@ -26,7 +26,7 @@ use bumpalo::{boxed::Box as BumpBox, Bump};
|
|||||||
/// name: String
|
/// name: String
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn Example((cx, props): Scope<Props>) -> Element {
|
/// fn Example(cx: Context, props: &ExampleProps) -> Element {
|
||||||
/// cx.render(rsx!{ div {"Hello, {props.name}"} })
|
/// cx.render(rsx!{ div {"Hello, {props.name}"} })
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
@@ -42,8 +42,6 @@ pub type Context<'a> = &'a Scope;
|
|||||||
/// We expose the `Scope` type so downstream users can traverse the Dioxus VirtualDOM for whatever
|
/// We expose the `Scope` type so downstream users can traverse the Dioxus VirtualDOM for whatever
|
||||||
/// use case they might have.
|
/// use case they might have.
|
||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
// Book-keeping about our spot in the arena
|
|
||||||
|
|
||||||
// safety:
|
// safety:
|
||||||
//
|
//
|
||||||
// pointers to scopes are *always* valid since they are bump allocated and never freed until this scope is also freed
|
// pointers to scopes are *always* valid since they are bump allocated and never freed until this scope is also freed
|
||||||
@@ -113,7 +111,7 @@ impl Scope {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// let mut dom = VirtualDom::new(|(cx, props)|cx.render(rsx!{ div {} }));
|
/// let mut dom = VirtualDom::new(|cx, props|cx.render(rsx!{ div {} }));
|
||||||
/// dom.rebuild();
|
/// dom.rebuild();
|
||||||
///
|
///
|
||||||
/// let base = dom.base_scope();
|
/// let base = dom.base_scope();
|
||||||
@@ -131,7 +129,7 @@ impl Scope {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// let mut dom = VirtualDom::new(|(cx, props)|cx.render(rsx!{ div {} }));
|
/// let mut dom = VirtualDom::new(|cx, props|cx.render(rsx!{ div {} }));
|
||||||
/// dom.rebuild();
|
/// dom.rebuild();
|
||||||
///
|
///
|
||||||
/// let base = dom.base_scope();
|
/// let base = dom.base_scope();
|
||||||
@@ -151,7 +149,7 @@ impl Scope {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// let mut dom = VirtualDom::new(|(cx, props)|cx.render(rsx!{ div {} }));
|
/// let mut dom = VirtualDom::new(|cx, props|cx.render(rsx!{ div {} }));
|
||||||
/// dom.rebuild();
|
/// dom.rebuild();
|
||||||
///
|
///
|
||||||
/// let base = dom.base_scope();
|
/// let base = dom.base_scope();
|
||||||
@@ -172,7 +170,7 @@ impl Scope {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// let mut dom = VirtualDom::new(|(cx, props)|cx.render(rsx!{ div {} }));
|
/// let mut dom = VirtualDom::new(|cx, props|cx.render(rsx!{ div {} }));
|
||||||
/// dom.rebuild();
|
/// dom.rebuild();
|
||||||
/// let base = dom.base_scope();
|
/// let base = dom.base_scope();
|
||||||
///
|
///
|
||||||
@@ -307,12 +305,12 @@ impl Scope {
|
|||||||
/// ```
|
/// ```
|
||||||
/// struct SharedState(&'static str);
|
/// struct SharedState(&'static str);
|
||||||
///
|
///
|
||||||
/// static App: FC<()> = |(cx, props)|{
|
/// static App: FC<()> = |cx, props|{
|
||||||
/// cx.use_hook(|_| cx.provide_state(SharedState("world")), |_| {}, |_| {});
|
/// cx.use_hook(|_| cx.provide_state(SharedState("world")), |_| {}, |_| {});
|
||||||
/// rsx!(cx, Child {})
|
/// rsx!(cx, Child {})
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// static Child: FC<()> = |(cx, props)|{
|
/// static Child: FC<()> = |cx, props|{
|
||||||
/// let state = cx.consume_state::<SharedState>();
|
/// let state = cx.consume_state::<SharedState>();
|
||||||
/// rsx!(cx, div { "hello {state.0}" })
|
/// rsx!(cx, div { "hello {state.0}" })
|
||||||
/// }
|
/// }
|
||||||
@@ -356,7 +354,7 @@ impl Scope {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// static App: FC<()> = |(cx, props)| {
|
/// static App: FC<()> = |cx, props| {
|
||||||
/// todo!();
|
/// todo!();
|
||||||
/// rsx!(cx, div { "Subtree {id}"})
|
/// rsx!(cx, div { "Subtree {id}"})
|
||||||
/// };
|
/// };
|
||||||
@@ -373,7 +371,7 @@ impl Scope {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// static App: FC<()> = |(cx, props)| {
|
/// static App: FC<()> = |cx, props| {
|
||||||
/// let id = cx.get_current_subtree();
|
/// let id = cx.get_current_subtree();
|
||||||
/// rsx!(cx, div { "Subtree {id}"})
|
/// rsx!(cx, div { "Subtree {id}"})
|
||||||
/// };
|
/// };
|
||||||
@@ -529,3 +527,8 @@ impl BumpFrame {
|
|||||||
nodes.len() - 1
|
nodes.len() - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sizeof() {
|
||||||
|
dbg!(std::mem::size_of::<Scope>());
|
||||||
|
}
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
use std::{
|
use std::cell::{Cell, RefCell};
|
||||||
borrow::BorrowMut,
|
|
||||||
cell::{Cell, RefCell},
|
|
||||||
};
|
|
||||||
|
|
||||||
use bumpalo::{boxed::Box as BumpBox, Bump};
|
use bumpalo::{boxed::Box as BumpBox, Bump};
|
||||||
use futures_channel::mpsc::UnboundedSender;
|
use futures_channel::mpsc::UnboundedSender;
|
||||||
@@ -23,8 +20,8 @@ pub struct Heuristic {
|
|||||||
// has an internal heuristics engine to pre-allocate arenas to the right size
|
// has an internal heuristics engine to pre-allocate arenas to the right size
|
||||||
pub(crate) struct ScopeArena {
|
pub(crate) struct ScopeArena {
|
||||||
bump: Bump,
|
bump: Bump,
|
||||||
scopes: Vec<*mut Scope>,
|
scopes: RefCell<Vec<*mut Scope>>,
|
||||||
free_scopes: Vec<ScopeId>,
|
free_scopes: RefCell<Vec<ScopeId>>,
|
||||||
nodes: RefCell<Slab<*const VNode<'static>>>,
|
nodes: RefCell<Slab<*const VNode<'static>>>,
|
||||||
pub(crate) sender: UnboundedSender<SchedulerMsg>,
|
pub(crate) sender: UnboundedSender<SchedulerMsg>,
|
||||||
}
|
}
|
||||||
@@ -33,38 +30,38 @@ impl ScopeArena {
|
|||||||
pub fn new(sender: UnboundedSender<SchedulerMsg>) -> Self {
|
pub fn new(sender: UnboundedSender<SchedulerMsg>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bump: Bump::new(),
|
bump: Bump::new(),
|
||||||
scopes: Vec::new(),
|
scopes: RefCell::new(Vec::new()),
|
||||||
free_scopes: Vec::new(),
|
free_scopes: RefCell::new(Vec::new()),
|
||||||
nodes: RefCell::new(Slab::new()),
|
nodes: RefCell::new(Slab::new()),
|
||||||
sender,
|
sender,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_scope(&self, id: &ScopeId) -> Option<&Scope> {
|
pub fn get_scope(&self, id: &ScopeId) -> Option<&Scope> {
|
||||||
unsafe { Some(&*self.scopes[id.0]) }
|
unsafe { Some(&*self.scopes.borrow()[id.0]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is unsafe
|
// this is unsafe
|
||||||
pub unsafe fn get_scope_mut(&self, id: &ScopeId) -> Option<*mut Scope> {
|
pub unsafe fn get_scope_mut(&self, id: &ScopeId) -> Option<*mut Scope> {
|
||||||
Some(self.scopes[id.0])
|
Some(self.scopes.borrow()[id.0])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_key(
|
pub fn new_with_key(
|
||||||
&mut self,
|
&self,
|
||||||
fc_ptr: *const (),
|
fc_ptr: *const (),
|
||||||
caller: *const dyn Fn(&Scope) -> Element,
|
caller: *const dyn Fn(&Scope) -> Element,
|
||||||
parent_scope: Option<*mut Scope>,
|
parent_scope: Option<*mut Scope>,
|
||||||
height: u32,
|
height: u32,
|
||||||
subtree: u32,
|
subtree: u32,
|
||||||
) -> ScopeId {
|
) -> ScopeId {
|
||||||
if let Some(id) = self.free_scopes.pop() {
|
if let Some(id) = self.free_scopes.borrow_mut().pop() {
|
||||||
// have already called drop on it - the slot is still chillin tho
|
// have already called drop on it - the slot is still chillin tho
|
||||||
let scope = unsafe { &mut *self.scopes[id.0 as usize] };
|
// let scope = unsafe { &mut *self.scopes.borrow()[id.0 as usize] };
|
||||||
|
|
||||||
todo!("override the scope contents");
|
todo!("override the scope contents");
|
||||||
id
|
id
|
||||||
} else {
|
} else {
|
||||||
let scope_id = ScopeId(self.scopes.len());
|
let scope_id = ScopeId(self.scopes.borrow().len());
|
||||||
|
|
||||||
let old_root = NodeLink {
|
let old_root = NodeLink {
|
||||||
link_idx: 0,
|
link_idx: 0,
|
||||||
@@ -93,8 +90,6 @@ impl ScopeArena {
|
|||||||
|
|
||||||
old_root: RefCell::new(Some(old_root)),
|
old_root: RefCell::new(Some(old_root)),
|
||||||
new_root: RefCell::new(Some(new_root)),
|
new_root: RefCell::new(Some(new_root)),
|
||||||
// old_root: RefCell::new(Some(old_root)),
|
|
||||||
// new_root: RefCell::new(None),
|
|
||||||
items: RefCell::new(SelfReferentialItems {
|
items: RefCell::new(SelfReferentialItems {
|
||||||
listeners: Default::default(),
|
listeners: Default::default(),
|
||||||
borrowed_props: Default::default(),
|
borrowed_props: Default::default(),
|
||||||
@@ -105,7 +100,7 @@ impl ScopeArena {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let stable = self.bump.alloc(new_scope);
|
let stable = self.bump.alloc(new_scope);
|
||||||
self.scopes.push(stable);
|
self.scopes.borrow_mut().push(stable);
|
||||||
scope_id
|
scope_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,8 +114,8 @@ impl ScopeArena {
|
|||||||
let entry = els.vacant_entry();
|
let entry = els.vacant_entry();
|
||||||
let key = entry.key();
|
let key = entry.key();
|
||||||
let id = ElementId(key);
|
let id = ElementId(key);
|
||||||
let node = node as *const _;
|
let node: *const VNode = node as *const _;
|
||||||
let node = unsafe { std::mem::transmute(node) };
|
let node = unsafe { std::mem::transmute::<*const VNode, *const VNode>(node) };
|
||||||
entry.insert(node);
|
entry.insert(node);
|
||||||
id
|
id
|
||||||
|
|
||||||
@@ -183,7 +178,6 @@ impl ScopeArena {
|
|||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.listeners
|
.listeners
|
||||||
.drain(..)
|
.drain(..)
|
||||||
.map(|li| unsafe { &*li })
|
|
||||||
.for_each(|listener| drop(listener.callback.borrow_mut().take()));
|
.for_each(|listener| drop(listener.callback.borrow_mut().take()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,39 +250,17 @@ impl ScopeArena {
|
|||||||
let wip_frame = scope.wip_frame();
|
let wip_frame = scope.wip_frame();
|
||||||
let nodes = wip_frame.nodes.borrow();
|
let nodes = wip_frame.nodes.borrow();
|
||||||
let node = nodes.get(0).unwrap();
|
let node = nodes.get(0).unwrap();
|
||||||
unsafe { std::mem::transmute(node) }
|
unsafe { std::mem::transmute::<&VNode, &VNode>(node) }
|
||||||
// let root = scope.old_root.borrow();
|
|
||||||
// let link = root.as_ref().unwrap();
|
|
||||||
// dbg!(link);
|
|
||||||
|
|
||||||
// // for now, components cannot pass portals through each other
|
|
||||||
// assert_eq!(link.scope_id, *id);
|
|
||||||
// // assert_eq!(link.gen_id, scope.generation.get() - 1);
|
|
||||||
|
|
||||||
// // let items = scope.items.borrow();
|
|
||||||
// let nodes = scope.wip_frame().nodes.borrow();
|
|
||||||
// let node = nodes.get(link.link_idx).unwrap();
|
|
||||||
// unsafe { std::mem::transmute(node) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fin_head(&self, id: &ScopeId) -> &VNode {
|
pub fn fin_head(&self, id: &ScopeId) -> &VNode {
|
||||||
let scope = self.get_scope(id).unwrap();
|
let scope = self.get_scope(id).unwrap();
|
||||||
let wip_frame = scope.fin_frame();
|
let wip_frame = scope.fin_frame();
|
||||||
let nodes = wip_frame.nodes.borrow();
|
let nodes = wip_frame.nodes.borrow();
|
||||||
let node = nodes.get(0).unwrap();
|
let node: &VNode = nodes.get(0).unwrap();
|
||||||
unsafe { std::mem::transmute(node) }
|
unsafe { std::mem::transmute::<&VNode, &VNode>(node) }
|
||||||
// let scope = self.get_scope(id).unwrap();
|
|
||||||
// let root = scope.new_root.borrow();
|
|
||||||
// let link = root.as_ref().unwrap();
|
|
||||||
|
|
||||||
// // for now, components cannot pass portals through each other
|
|
||||||
// assert_eq!(link.scope_id, *id);
|
|
||||||
// // assert_eq!(link.gen_id, scope.generation.get());
|
|
||||||
|
|
||||||
// let nodes = scope.fin_frame().nodes.borrow();
|
|
||||||
// let node = nodes.get(link.link_idx).unwrap();
|
|
||||||
// unsafe { std::mem::transmute(node) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn root_node(&self, id: &ScopeId) -> &VNode {
|
pub fn root_node(&self, id: &ScopeId) -> &VNode {
|
||||||
self.wip_head(id)
|
self.wip_head(id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
//! A DOM for testing - both internal and external code.
|
|
||||||
use bumpalo::Bump;
|
|
||||||
|
|
||||||
use crate::innerlude::*;
|
|
||||||
use crate::nodes::IntoVNode;
|
|
||||||
|
|
||||||
pub struct TestDom {
|
|
||||||
bump: Bump,
|
|
||||||
// scheduler: Scheduler,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TestDom {
|
|
||||||
pub fn new() -> TestDom {
|
|
||||||
let bump = Bump::new();
|
|
||||||
let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>();
|
|
||||||
todo!()
|
|
||||||
// let scheduler = Scheduler::new(sender, receiver, 10, 100);
|
|
||||||
// TestDom { bump, scheduler }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_factory(&self) -> NodeFactory {
|
|
||||||
NodeFactory::new(&self.bump)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_direct<'a>(&'a self, lazy_nodes: Option<LazyNodes<'a, '_>>) -> VNode<'a> {
|
|
||||||
lazy_nodes.into_vnode(NodeFactory::new(&self.bump))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render<'a>(&'a self, lazy_nodes: Option<LazyNodes<'a, '_>>) -> &'a VNode<'a> {
|
|
||||||
self.bump
|
|
||||||
.alloc(lazy_nodes.into_vnode(NodeFactory::new(&self.bump)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn diff<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Mutations<'a> {
|
|
||||||
let mutations = Mutations::new();
|
|
||||||
let mut machine: DiffState = todo!();
|
|
||||||
// let mut machine = DiffState::new(mutations);
|
|
||||||
// let mut machine = DiffState::new(mutations);
|
|
||||||
machine.stack.push(DiffInstruction::Diff { new, old });
|
|
||||||
machine.mutations
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create<'a>(&'a self, left: Option<LazyNodes<'a, '_>>) -> Mutations<'a> {
|
|
||||||
let old = self.bump.alloc(self.render_direct(left));
|
|
||||||
|
|
||||||
let mut machine: DiffState = todo!();
|
|
||||||
// let mut machine = DiffState::new(Mutations::new());
|
|
||||||
// let mut machine = DiffState::new(Mutations::new());
|
|
||||||
|
|
||||||
machine.stack.create_node(old, MountType::Append);
|
|
||||||
|
|
||||||
todo!()
|
|
||||||
|
|
||||||
// machine.work(&mut || false);
|
|
||||||
|
|
||||||
// machine.mutations
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lazy_diff<'a>(
|
|
||||||
&'a self,
|
|
||||||
left: Option<LazyNodes<'a, '_>>,
|
|
||||||
right: Option<LazyNodes<'a, '_>>,
|
|
||||||
) -> (Mutations<'a>, Mutations<'a>) {
|
|
||||||
let (old, new) = (self.render(left), self.render(right));
|
|
||||||
|
|
||||||
let mut machine: DiffState = todo!();
|
|
||||||
// let mut machine = DiffState::new(Mutations::new());
|
|
||||||
|
|
||||||
machine.stack.create_node(old, MountType::Append);
|
|
||||||
|
|
||||||
todo!()
|
|
||||||
|
|
||||||
// machine.work(|| false);
|
|
||||||
// let create_edits = machine.mutations;
|
|
||||||
|
|
||||||
// let mut machine: DiffState = todo!();
|
|
||||||
// // let mut machine = DiffState::new(Mutations::new());
|
|
||||||
|
|
||||||
// machine.stack.push(DiffInstruction::Diff { old, new });
|
|
||||||
|
|
||||||
// machine.work(&mut || false);
|
|
||||||
|
|
||||||
// let edits = machine.mutations;
|
|
||||||
|
|
||||||
// (create_edits, edits)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TestDom {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +1,6 @@
|
|||||||
//! # VirtualDOM Implementation for Rust
|
//! # VirtualDOM Implementation for Rust
|
||||||
//!
|
//!
|
||||||
//! This module provides the primary mechanics to create a hook-based, concurrent VDOM for Rust.
|
//! This module provides the primary mechanics to create a hook-based, concurrent VDOM for Rust.
|
||||||
//!
|
|
||||||
//! In this file, multiple items are defined. This file is big, but should be documented well to
|
|
||||||
//! navigate the inner workings of the Dom. We try to keep these main mechanics in this file to limit
|
|
||||||
//! the possible exposed API surface (keep fields private). This particular implementation of VDOM
|
|
||||||
//! is extremely efficient, but relies on some unsafety under the hood to do things like manage
|
|
||||||
//! micro-heaps for components. We are currently working on refactoring the safety out into safe(r)
|
|
||||||
//! abstractions, but current tests (MIRI and otherwise) show no issues with the current implementation.
|
|
||||||
//!
|
|
||||||
//! Included is:
|
|
||||||
//! - The [`VirtualDom`] itself
|
|
||||||
//! - The [`Scope`] object for managing component lifecycle
|
|
||||||
//! - The [`ActiveFrame`] object for managing the Scope`s microheap
|
|
||||||
//! - The [`Context`] object for exposing VirtualDOM API to components
|
|
||||||
//! - The [`NodeFactory`] object for lazily exposing the `Context` API to the nodebuilder API
|
|
||||||
//!
|
|
||||||
//! This module includes just the barebones for a complete VirtualDOM API.
|
|
||||||
//! Additional functionality is defined in the respective files.
|
|
||||||
|
|
||||||
use crate::innerlude::*;
|
use crate::innerlude::*;
|
||||||
use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
|
use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
|
||||||
@@ -28,33 +11,92 @@ use std::pin::Pin;
|
|||||||
use std::task::Poll;
|
use std::task::Poll;
|
||||||
use std::{any::Any, collections::VecDeque};
|
use std::{any::Any, collections::VecDeque};
|
||||||
|
|
||||||
/// An integrated virtual node system that progresses events and diffs UI trees.
|
/// A virtual node system that progresses user events and diffs UI trees.
|
||||||
///
|
///
|
||||||
/// Differences are converted into patches which a renderer can use to draw the UI.
|
/// Components are defined as simple functions that take [`Context`] and a [`Properties`] type and return an [`Element`].
|
||||||
///
|
///
|
||||||
/// If you are building an App with Dioxus, you probably won't want to reach for this directly, instead opting to defer
|
|
||||||
/// to a particular crate's wrapper over the [`VirtualDom`] API.
|
|
||||||
///
|
|
||||||
/// Example
|
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// static App: FC<()> = |(cx, props)|{
|
/// #[derive(Props, PartialEq)]
|
||||||
|
/// struct AppProps {
|
||||||
|
/// title: String
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn App(cx: Context, props: &AppProps) -> Element {
|
||||||
|
/// cx.render(rsx!(
|
||||||
|
/// div {"hello, {props.title}"}
|
||||||
|
/// ))
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Components may be composed to make complex apps.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// fn App(cx: Context, props: &AppProps) -> Element {
|
||||||
|
/// cx.render(rsx!(
|
||||||
|
/// NavBar { routes: ROUTES }
|
||||||
|
/// Title { "{props.title}" }
|
||||||
|
/// Footer {}
|
||||||
|
/// ))
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// To start an app, create a [`VirtualDom`] and call [`VirtualDom::rebuild`] to get the list of edits required to
|
||||||
|
/// draw the UI.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// let mut vdom = VirtualDom::new(App);
|
||||||
|
/// let edits = vdom.rebuild();
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// To inject UserEvents into the VirtualDom, call [`VirtualDom::get_scheduler_channel`] to get access to the scheduler.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// let channel = vdom.get_scheduler_channel();
|
||||||
|
/// channel.send_unbounded(SchedulerMsg::UserEvent(UserEvent {
|
||||||
|
/// // ...
|
||||||
|
/// }))
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// While waiting for UserEvents to occur, call [`VirtualDom::wait_for_work`] to poll any futures inside the VirtualDom.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// vdom.wait_for_work().await;
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Once work is ready, call [`VirtualDom::work_with_deadline`] to compute the differences between the previous and
|
||||||
|
/// current UI trees. This will return a [`Mutations`] object that contains Edits, Effects, and NodeRefs that need to be
|
||||||
|
/// handled by the renderer.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// let mutations = vdom.work_with_deadline(|| false);
|
||||||
|
/// for edit in mutations {
|
||||||
|
/// apply(edit);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Building an event loop around Dioxus:
|
||||||
|
///
|
||||||
|
/// Putting everything together, you can build an event loop around Dioxus by using the methods outlined above.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// fn App(cx: Context, props: &()) -> Element {
|
||||||
/// cx.render(rsx!{
|
/// cx.render(rsx!{
|
||||||
/// div {
|
/// div { "Hello World" }
|
||||||
/// "Hello World"
|
|
||||||
/// }
|
|
||||||
/// })
|
/// })
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// async fn main() {
|
/// async fn main() {
|
||||||
/// let mut dom = VirtualDom::new(App);
|
/// let mut dom = VirtualDom::new(App);
|
||||||
|
///
|
||||||
/// let mut inital_edits = dom.rebuild();
|
/// let mut inital_edits = dom.rebuild();
|
||||||
/// initialize_screen(inital_edits);
|
/// apply_edits(inital_edits);
|
||||||
///
|
///
|
||||||
/// loop {
|
/// loop {
|
||||||
/// let next_frame = TimeoutFuture::new(Duration::from_millis(16));
|
/// dom.wait_for_work().await;
|
||||||
/// let edits = dom.run_with_deadline(next_frame).await;
|
/// let frame_timeout = TimeoutFuture::new(Duration::from_millis(16));
|
||||||
|
/// let deadline = || (&mut frame_timeout).now_or_never();
|
||||||
|
/// let edits = dom.run_with_deadline(deadline).await;
|
||||||
/// apply_edits(edits);
|
/// apply_edits(edits);
|
||||||
/// render_frame();
|
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
@@ -145,9 +187,9 @@ impl VirtualDom {
|
|||||||
sender: UnboundedSender<SchedulerMsg>,
|
sender: UnboundedSender<SchedulerMsg>,
|
||||||
receiver: UnboundedReceiver<SchedulerMsg>,
|
receiver: UnboundedReceiver<SchedulerMsg>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut scopes = ScopeArena::new(sender.clone());
|
let scopes = ScopeArena::new(sender.clone());
|
||||||
|
|
||||||
let caller = Box::new(move |f: &Scope| -> Element { root(f, &root_props) });
|
let caller = Box::new(move |scp: &Scope| -> Element { root(scp, &root_props) });
|
||||||
let caller_ref: *mut dyn Fn(&Scope) -> Element = Box::into_raw(caller);
|
let caller_ref: *mut dyn Fn(&Scope) -> Element = Box::into_raw(caller);
|
||||||
let base_scope = scopes.new_with_key(root as _, caller_ref, None, 0, 0);
|
let base_scope = scopes.new_with_key(root as _, caller_ref, None, 0, 0);
|
||||||
|
|
||||||
@@ -164,7 +206,7 @@ impl VirtualDom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the [`ScopeState`] for the root component.
|
/// Get the [`Scope`] for the root component.
|
||||||
///
|
///
|
||||||
/// This is useful for traversing the tree from the root for heuristics or alternsative renderers that use Dioxus
|
/// This is useful for traversing the tree from the root for heuristics or alternsative renderers that use Dioxus
|
||||||
/// directly.
|
/// directly.
|
||||||
@@ -174,7 +216,7 @@ impl VirtualDom {
|
|||||||
self.get_scope(&self.base_scope).unwrap()
|
self.get_scope(&self.base_scope).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the [`ScopeState`] for a component given its [`ScopeId`]
|
/// Get the [`Scope`] for a component given its [`ScopeId`]
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@@ -305,7 +347,7 @@ impl VirtualDom {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// static App: FC<()> = |(cx, props)|rsx!(cx, div {"hello"} );
|
/// static App: FC<()> = |cx, props|rsx!(cx, div {"hello"} );
|
||||||
///
|
///
|
||||||
/// let mut dom = VirtualDom::new(App);
|
/// let mut dom = VirtualDom::new(App);
|
||||||
///
|
///
|
||||||
@@ -436,7 +478,7 @@ impl VirtualDom {
|
|||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
/// static App: FC<()> = |(cx, props)| cx.render(rsx!{ "hello world" });
|
/// static App: FC<()> = |cx, props| cx.render(rsx!{ "hello world" });
|
||||||
/// let mut dom = VirtualDom::new();
|
/// let mut dom = VirtualDom::new();
|
||||||
/// let edits = dom.rebuild();
|
/// let edits = dom.rebuild();
|
||||||
///
|
///
|
||||||
@@ -453,7 +495,7 @@ impl VirtualDom {
|
|||||||
|
|
||||||
diff_state.stack.scope_stack.push(scope_id);
|
diff_state.stack.scope_stack.push(scope_id);
|
||||||
|
|
||||||
let work_completed = diff_state.work(|| false);
|
diff_state.work(|| false);
|
||||||
}
|
}
|
||||||
|
|
||||||
diff_state.mutations
|
diff_state.mutations
|
||||||
@@ -474,7 +516,7 @@ impl VirtualDom {
|
|||||||
/// value: Shared<&'static str>,
|
/// value: Shared<&'static str>,
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// static App: FC<AppProps> = |(cx, props)|{
|
/// static App: FC<AppProps> = |cx, props|{
|
||||||
/// let val = cx.value.borrow();
|
/// let val = cx.value.borrow();
|
||||||
/// cx.render(rsx! { div { "{val}" } })
|
/// cx.render(rsx! { div { "{val}" } })
|
||||||
/// };
|
/// };
|
||||||
@@ -501,7 +543,7 @@ impl VirtualDom {
|
|||||||
|
|
||||||
diff_machine.force_diff = true;
|
diff_machine.force_diff = true;
|
||||||
|
|
||||||
let work_completed = diff_machine.work(|| false);
|
diff_machine.work(|| false);
|
||||||
// let scopes = &mut self.scopes;
|
// let scopes = &mut self.scopes;
|
||||||
// crate::diff::diff_scope(&mut diff_machine, scope_id);
|
// crate::diff::diff_scope(&mut diff_machine, scope_id);
|
||||||
// self.scopes.diff_scope(&mut diff_machine, scope_id);
|
// self.scopes.diff_scope(&mut diff_machine, scope_id);
|
||||||
@@ -523,6 +565,78 @@ impl VirtualDom {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Renders an `rsx` call into the Base Scope's allocator.
|
||||||
|
///
|
||||||
|
/// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
|
||||||
|
pub fn render_vnodes<'a>(&'a self, lazy_nodes: Option<LazyNodes<'a, '_>>) -> &'a VNode<'a> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders an `rsx` call into the Base Scope's allocator.
|
||||||
|
///
|
||||||
|
/// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
|
||||||
|
pub fn diff_vnodes<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Mutations<'a> {
|
||||||
|
todo!()
|
||||||
|
// let mutations = Mutations::new();
|
||||||
|
// let mut machine: DiffState = todo!();
|
||||||
|
// // let mut machine = DiffState::new(mutations);
|
||||||
|
// // let mut machine = DiffState::new(mutations);
|
||||||
|
// machine.stack.push(DiffInstruction::Diff { new, old });
|
||||||
|
// machine.mutations
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders an `rsx` call into the Base Scope's allocator.
|
||||||
|
///
|
||||||
|
/// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
|
||||||
|
pub fn create_vnodes<'a>(&'a self, left: Option<LazyNodes<'a, '_>>) -> Mutations<'a> {
|
||||||
|
todo!()
|
||||||
|
// let old = self.bump.alloc(self.render_direct(left));
|
||||||
|
|
||||||
|
// let mut machine: DiffState = todo!();
|
||||||
|
// // let mut machine = DiffState::new(Mutations::new());
|
||||||
|
// // let mut machine = DiffState::new(Mutations::new());
|
||||||
|
|
||||||
|
// machine.stack.create_node(old, MountType::Append);
|
||||||
|
|
||||||
|
// todo!()
|
||||||
|
|
||||||
|
// // machine.work(&mut || false);
|
||||||
|
|
||||||
|
// // machine.mutations
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders an `rsx` call into the Base Scope's allocator.
|
||||||
|
///
|
||||||
|
/// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
|
||||||
|
pub fn diff_lazynodes<'a>(
|
||||||
|
&'a self,
|
||||||
|
left: Option<LazyNodes<'a, '_>>,
|
||||||
|
right: Option<LazyNodes<'a, '_>>,
|
||||||
|
) -> (Mutations<'a>, Mutations<'a>) {
|
||||||
|
let (old, new) = (self.render_vnodes(left), self.render_vnodes(right));
|
||||||
|
|
||||||
|
// let mut machine: DiffState = todo!();
|
||||||
|
// let mut machine = DiffState::new(Mutations::new());
|
||||||
|
|
||||||
|
// machine.stack.create_node(old, MountType::Append);
|
||||||
|
|
||||||
|
todo!()
|
||||||
|
|
||||||
|
// machine.work(|| false);
|
||||||
|
// let create_edits = machine.mutations;
|
||||||
|
|
||||||
|
// let mut machine: DiffState = todo!();
|
||||||
|
// // let mut machine = DiffState::new(Mutations::new());
|
||||||
|
|
||||||
|
// machine.stack.push(DiffInstruction::Diff { old, new });
|
||||||
|
|
||||||
|
// machine.work(&mut || false);
|
||||||
|
|
||||||
|
// let edits = machine.mutations;
|
||||||
|
|
||||||
|
// (create_edits, edits)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum SchedulerMsg {
|
pub enum SchedulerMsg {
|
||||||
|
|||||||
@@ -5,17 +5,17 @@
|
|||||||
//!
|
//!
|
||||||
//! It does not validated that component lifecycles work properly. This is done in another test file.
|
//! It does not validated that component lifecycles work properly. This is done in another test file.
|
||||||
|
|
||||||
use dioxus::{prelude::*, DomEdit, TestDom, VSuspended};
|
use dioxus::{prelude::*, DomEdit, VSuspended};
|
||||||
use dioxus_core as dioxus;
|
use dioxus_core as dioxus;
|
||||||
use dioxus_core_macro::*;
|
use dioxus_core_macro::*;
|
||||||
use dioxus_html as dioxus_elements;
|
use dioxus_html as dioxus_elements;
|
||||||
|
|
||||||
mod test_logging;
|
mod test_logging;
|
||||||
|
|
||||||
fn new_dom() -> TestDom {
|
fn new_dom() -> VirtualDom {
|
||||||
const IS_LOGGING_ENABLED: bool = false;
|
const IS_LOGGING_ENABLED: bool = false;
|
||||||
test_logging::set_up_logging(IS_LOGGING_ENABLED);
|
test_logging::set_up_logging(IS_LOGGING_ENABLED);
|
||||||
TestDom::new()
|
VirtualDom::new(|cx, props| todo!())
|
||||||
}
|
}
|
||||||
|
|
||||||
use DomEdit::*;
|
use DomEdit::*;
|
||||||
@@ -24,7 +24,7 @@ use DomEdit::*;
|
|||||||
#[test]
|
#[test]
|
||||||
fn html_and_rsx_generate_the_same_output() {
|
fn html_and_rsx_generate_the_same_output() {
|
||||||
let dom = new_dom();
|
let dom = new_dom();
|
||||||
let (create, change) = dom.lazy_diff(
|
let (create, change) = dom.diff_lazynodes(
|
||||||
rsx! ( div { "Hello world" } ),
|
rsx! ( div { "Hello world" } ),
|
||||||
rsx! ( div { "Goodbye world" } ),
|
rsx! ( div { "Goodbye world" } ),
|
||||||
);
|
);
|
||||||
@@ -58,7 +58,7 @@ fn html_and_rsx_generate_the_same_output() {
|
|||||||
fn fragments_create_properly() {
|
fn fragments_create_properly() {
|
||||||
let dom = new_dom();
|
let dom = new_dom();
|
||||||
|
|
||||||
let create = dom.create(rsx! {
|
let create = dom.create_vnodes(rsx! {
|
||||||
div { "Hello a" }
|
div { "Hello a" }
|
||||||
div { "Hello b" }
|
div { "Hello b" }
|
||||||
div { "Hello c" }
|
div { "Hello c" }
|
||||||
@@ -107,7 +107,7 @@ fn empty_fragments_create_anchors() {
|
|||||||
let left = rsx!({ (0..0).map(|_f| rsx! { div {}}) });
|
let left = rsx!({ (0..0).map(|_f| rsx! { div {}}) });
|
||||||
let right = rsx!({ (0..1).map(|_f| rsx! { div {}}) });
|
let right = rsx!({ (0..1).map(|_f| rsx! { div {}}) });
|
||||||
|
|
||||||
let (create, change) = dom.lazy_diff(left, right);
|
let (create, change) = dom.diff_lazynodes(left, right);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
create.edits,
|
create.edits,
|
||||||
@@ -133,7 +133,7 @@ fn empty_fragments_create_many_anchors() {
|
|||||||
let left = rsx!({ (0..0).map(|_f| rsx! { div {}}) });
|
let left = rsx!({ (0..0).map(|_f| rsx! { div {}}) });
|
||||||
let right = rsx!({ (0..5).map(|_f| rsx! { div {}}) });
|
let right = rsx!({ (0..5).map(|_f| rsx! { div {}}) });
|
||||||
|
|
||||||
let (create, change) = dom.lazy_diff(left, right);
|
let (create, change) = dom.diff_lazynodes(left, right);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
create.edits,
|
create.edits,
|
||||||
[CreatePlaceholder { root: 0 }, AppendChildren { many: 1 }]
|
[CreatePlaceholder { root: 0 }, AppendChildren { many: 1 }]
|
||||||
@@ -179,7 +179,7 @@ fn empty_fragments_create_anchors_with_many_children() {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let (create, change) = dom.lazy_diff(left, right);
|
let (create, change) = dom.diff_lazynodes(left, right);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
create.edits,
|
create.edits,
|
||||||
[CreatePlaceholder { root: 0 }, AppendChildren { many: 1 }]
|
[CreatePlaceholder { root: 0 }, AppendChildren { many: 1 }]
|
||||||
@@ -231,7 +231,7 @@ fn many_items_become_fragment() {
|
|||||||
});
|
});
|
||||||
let right = rsx!({ (0..0).map(|_| rsx! { div {} }) });
|
let right = rsx!({ (0..0).map(|_| rsx! { div {} }) });
|
||||||
|
|
||||||
let (create, change) = dom.lazy_diff(left, right);
|
let (create, change) = dom.diff_lazynodes(left, right);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
create.edits,
|
create.edits,
|
||||||
[
|
[
|
||||||
@@ -284,7 +284,7 @@ fn two_equal_fragments_are_equal() {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let (_create, change) = dom.lazy_diff(left, right);
|
let (_create, change) = dom.diff_lazynodes(left, right);
|
||||||
assert!(change.edits.is_empty());
|
assert!(change.edits.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,7 +302,7 @@ fn two_fragments_with_differrent_elements_are_differet() {
|
|||||||
p {}
|
p {}
|
||||||
);
|
);
|
||||||
|
|
||||||
let (_create, changes) = dom.lazy_diff(left, right);
|
let (_create, changes) = dom.diff_lazynodes(left, right);
|
||||||
log::debug!("{:#?}", &changes);
|
log::debug!("{:#?}", &changes);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
changes.edits,
|
changes.edits,
|
||||||
@@ -335,7 +335,7 @@ fn two_fragments_with_differrent_elements_are_differet_shorter() {
|
|||||||
p {}
|
p {}
|
||||||
);
|
);
|
||||||
|
|
||||||
let (create, change) = dom.lazy_diff(left, right);
|
let (create, change) = dom.diff_lazynodes(left, right);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
create.edits,
|
create.edits,
|
||||||
[
|
[
|
||||||
@@ -391,7 +391,7 @@ fn two_fragments_with_same_elements_are_differet() {
|
|||||||
p {}
|
p {}
|
||||||
);
|
);
|
||||||
|
|
||||||
let (create, change) = dom.lazy_diff(left, right);
|
let (create, change) = dom.diff_lazynodes(left, right);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
create.edits,
|
create.edits,
|
||||||
[
|
[
|
||||||
@@ -441,7 +441,7 @@ fn keyed_diffing_order() {
|
|||||||
p {"e"}
|
p {"e"}
|
||||||
);
|
);
|
||||||
|
|
||||||
let (create, change) = dom.lazy_diff(left, right);
|
let (create, change) = dom.diff_lazynodes(left, right);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
change.edits,
|
change.edits,
|
||||||
[Remove { root: 2 }, Remove { root: 3 }, Remove { root: 4 },]
|
[Remove { root: 2 }, Remove { root: 3 }, Remove { root: 4 },]
|
||||||
@@ -465,7 +465,7 @@ fn keyed_diffing_out_of_order() {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let (_, changes) = dom.lazy_diff(left, right);
|
let (_, changes) = dom.diff_lazynodes(left, right);
|
||||||
log::debug!("{:?}", &changes);
|
log::debug!("{:?}", &changes);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
changes.edits,
|
changes.edits,
|
||||||
@@ -490,7 +490,7 @@ fn keyed_diffing_out_of_order_adds() {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let (_, change) = dom.lazy_diff(left, right);
|
let (_, change) = dom.diff_lazynodes(left, right);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
change.edits,
|
change.edits,
|
||||||
[
|
[
|
||||||
@@ -517,7 +517,7 @@ fn keyed_diffing_out_of_order_adds_2() {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let (_, change) = dom.lazy_diff(left, right);
|
let (_, change) = dom.diff_lazynodes(left, right);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
change.edits,
|
change.edits,
|
||||||
[
|
[
|
||||||
@@ -545,7 +545,7 @@ fn keyed_diffing_out_of_order_adds_3() {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let (_, change) = dom.lazy_diff(left, right);
|
let (_, change) = dom.diff_lazynodes(left, right);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
change.edits,
|
change.edits,
|
||||||
[
|
[
|
||||||
@@ -573,7 +573,7 @@ fn keyed_diffing_out_of_order_adds_4() {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let (_, change) = dom.lazy_diff(left, right);
|
let (_, change) = dom.diff_lazynodes(left, right);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
change.edits,
|
change.edits,
|
||||||
[
|
[
|
||||||
@@ -601,7 +601,7 @@ fn keyed_diffing_out_of_order_adds_5() {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let (_, change) = dom.lazy_diff(left, right);
|
let (_, change) = dom.diff_lazynodes(left, right);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
change.edits,
|
change.edits,
|
||||||
[PushRoot { root: 4 }, InsertBefore { n: 1, root: 3 }]
|
[PushRoot { root: 4 }, InsertBefore { n: 1, root: 3 }]
|
||||||
@@ -624,7 +624,7 @@ fn keyed_diffing_additions() {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let (_, change) = dom.lazy_diff(left, right);
|
let (_, change) = dom.diff_lazynodes(left, right);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
change.edits,
|
change.edits,
|
||||||
[
|
[
|
||||||
@@ -657,7 +657,7 @@ fn keyed_diffing_additions_and_moves_on_ends() {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let (_, change) = dom.lazy_diff(left, right);
|
let (_, change) = dom.diff_lazynodes(left, right);
|
||||||
log::debug!("{:?}", change);
|
log::debug!("{:?}", change);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
change.edits,
|
change.edits,
|
||||||
@@ -696,7 +696,7 @@ fn keyed_diffing_additions_and_moves_in_middle() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// LIS: 4, 5, 6
|
// LIS: 4, 5, 6
|
||||||
let (_, change) = dom.lazy_diff(left, right);
|
let (_, change) = dom.diff_lazynodes(left, right);
|
||||||
log::debug!("{:#?}", change);
|
log::debug!("{:#?}", change);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
change.edits,
|
change.edits,
|
||||||
@@ -745,7 +745,7 @@ fn controlled_keyed_diffing_out_of_order() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// LIS: 5, 6
|
// LIS: 5, 6
|
||||||
let (_, changes) = dom.lazy_diff(left, right);
|
let (_, changes) = dom.diff_lazynodes(left, right);
|
||||||
log::debug!("{:#?}", &changes);
|
log::debug!("{:#?}", &changes);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
changes.edits,
|
changes.edits,
|
||||||
@@ -787,7 +787,7 @@ fn controlled_keyed_diffing_out_of_order_max_test() {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let (_, changes) = dom.lazy_diff(left, right);
|
let (_, changes) = dom.diff_lazynodes(left, right);
|
||||||
log::debug!("{:#?}", &changes);
|
log::debug!("{:#?}", &changes);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
changes.edits,
|
changes.edits,
|
||||||
@@ -808,7 +808,7 @@ fn suspense() {
|
|||||||
let dom = new_dom();
|
let dom = new_dom();
|
||||||
|
|
||||||
todo!()
|
todo!()
|
||||||
// let edits = dom.create(Some(LazyNodes::new(|f| {
|
// let edits = dom.create_vnodes(Some(LazyNodes::new(|f| {
|
||||||
// use std::cell::{Cell, RefCell};
|
// use std::cell::{Cell, RefCell};
|
||||||
// VNode::Suspended(f.bump().alloc(VSuspended {
|
// VNode::Suspended(f.bump().alloc(VSuspended {
|
||||||
// task_id: 0,
|
// task_id: 0,
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
#![allow(unused, non_upper_case_globals)]
|
|
||||||
|
|
||||||
//! test that we can display the virtualdom properly
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
|
||||||
use dioxus_core as dioxus;
|
|
||||||
use dioxus_core_macro::*;
|
|
||||||
use dioxus_html as dioxus_elements;
|
|
||||||
mod test_logging;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn please_work() {
|
|
||||||
static App: FC<()> = |cx, props| {
|
|
||||||
cx.render(rsx! {
|
|
||||||
div {
|
|
||||||
hidden: "true"
|
|
||||||
"hello"
|
|
||||||
div { "hello" }
|
|
||||||
// Child {}
|
|
||||||
// Child {}
|
|
||||||
// Child {}
|
|
||||||
}
|
|
||||||
// div { "hello" }
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
static Child: FC<()> = |cx, props| {
|
|
||||||
cx.render(rsx! {
|
|
||||||
div { "child" }
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut dom = VirtualDom::new(App);
|
|
||||||
dom.rebuild();
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#![allow(unused, non_upper_case_globals)]
|
#![allow(unused, non_upper_case_globals)]
|
||||||
|
|
||||||
use dioxus::{prelude::*, DomEdit, Mutations, TestDom};
|
use dioxus::{prelude::*, DomEdit, Mutations};
|
||||||
use dioxus_core as dioxus;
|
use dioxus_core as dioxus;
|
||||||
use dioxus_core_macro::*;
|
use dioxus_core_macro::*;
|
||||||
use dioxus_html as dioxus_elements;
|
use dioxus_html as dioxus_elements;
|
||||||
|
|||||||
Reference in new issue
Block a user