feat: some docs, cleaning
This commit is contained in:
1 parent
acad9ca622
commit
c321532a6c
19 files changed
+269
-195
No files matched your search
Vendored
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"rust-analyzer.inlayHints.enable": true
|
"rust-analyzer.inlayHints.enable": false
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ use dioxus_core::prelude::*;
|
|||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
||||||
pub static Example: FC<()> = |cx| {
|
pub static Example: FC<()> = |cx| {
|
||||||
let list = (0..10).map(|f| LazyNodes::new(move |f| todo!()));
|
let list = (0..10).map(|_f| LazyNodes::new(move |_f| todo!()));
|
||||||
|
|
||||||
cx.render(LazyNodes::new(move |cx| {
|
cx.render(LazyNodes::new(move |cx| {
|
||||||
cx.raw_element(
|
cx.raw_element(
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const App: FC<()> = |cx| {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Task: FC<()> = |cx| {
|
const Task: FC<()> = |cx| {
|
||||||
let (task, res) = use_task(cx, || async { true });
|
let (_task, _res) = use_task(cx, || async { true });
|
||||||
// task.pause();
|
// task.pause();
|
||||||
// task.restart();
|
// task.restart();
|
||||||
// task.stop();
|
// task.stop();
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ async fn main() {
|
|||||||
|
|
||||||
dom.rebuild();
|
dom.rebuild();
|
||||||
|
|
||||||
let mut deadline = async_std::task::sleep(Duration::from_millis(50));
|
let deadline = async_std::task::sleep(Duration::from_millis(50));
|
||||||
let fut = dom.run_with_deadline(deadline);
|
let _fut = dom.run_with_deadline(deadline);
|
||||||
}
|
}
|
||||||
@@ -60,9 +60,10 @@ impl<'a> Iterator for RealChildIterator<'a> {
|
|||||||
|
|
||||||
// For components, we load their root and push them onto the stack
|
// For components, we load their root and push them onto the stack
|
||||||
VNode::Component(sc) => {
|
VNode::Component(sc) => {
|
||||||
let scope =
|
let scope = self
|
||||||
unsafe { self.scopes.get_scope(sc.associated_scope.get().unwrap()) }
|
.scopes
|
||||||
.unwrap();
|
.get_scope(sc.associated_scope.get().unwrap())
|
||||||
|
.unwrap();
|
||||||
// let scope = self.scopes.get(sc.ass_scope.get().unwrap()).unwrap();
|
// let scope = self.scopes.get(sc.ass_scope.get().unwrap()).unwrap();
|
||||||
|
|
||||||
// Simply swap the current node on the stack with the root of the component
|
// Simply swap the current node on the stack with the root of the component
|
||||||
|
|||||||
@@ -87,6 +87,10 @@ impl<'src, P> Context<'src, P> {
|
|||||||
/// })
|
/// })
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Notes:
|
||||||
|
///
|
||||||
|
/// This method returns a "ScopeChildren" object. This object is copy-able and preserve the correct lifetime.
|
||||||
pub fn children(&self) -> ScopeChildren<'src> {
|
pub fn children(&self) -> ScopeChildren<'src> {
|
||||||
self.scope.child_nodes()
|
self.scope.child_nodes()
|
||||||
}
|
}
|
||||||
@@ -103,19 +107,6 @@ impl<'src, P> Context<'src, P> {
|
|||||||
self.scope.shared.schedule_any_immediate.clone()
|
self.scope.shared.schedule_any_immediate.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn schedule_effect(&self) -> Rc<dyn Fn() + 'static> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn schedule_layout_effect(&self) {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get's this component's unique identifier.
|
|
||||||
pub fn get_scope_id(&self) -> ScopeId {
|
|
||||||
self.scope.our_arena_idx.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
|
/// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
|
||||||
///
|
///
|
||||||
/// This function consumes the context and absorb the lifetime, so these VNodes *must* be returned.
|
/// This function consumes the context and absorb the lifetime, so these VNodes *must* be returned.
|
||||||
@@ -175,7 +166,7 @@ impl<'src, P> Context<'src, P> {
|
|||||||
let getter = &self.scope.shared.get_shared_context;
|
let getter = &self.scope.shared.get_shared_context;
|
||||||
let ty = TypeId::of::<T>();
|
let ty = TypeId::of::<T>();
|
||||||
let idx = self.scope.our_arena_idx;
|
let idx = self.scope.our_arena_idx;
|
||||||
getter(idx, ty).map(|f| f.downcast().unwrap())
|
getter(idx, ty).map(|f| f.downcast().expect("TypeID already validated"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Store a value between renders
|
/// Store a value between renders
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
|
|
||||||
fn prepare_move_node(&mut self, node: &'bump VNode<'bump>) {
|
fn prepare_move_node(&mut self, node: &'bump VNode<'bump>) {
|
||||||
for el in RealChildIterator::new(node, self.vdom) {
|
for el in RealChildIterator::new(node, self.vdom) {
|
||||||
self.mutations.push_root(el.direct_id());
|
self.mutations.push_root(el.mounted_id());
|
||||||
self.stack.add_child_count(1);
|
self.stack.add_child_count(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,7 +223,7 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
let mut iter = RealChildIterator::new(old, self.vdom);
|
let mut iter = RealChildIterator::new(old, self.vdom);
|
||||||
let first = iter.next().unwrap();
|
let first = iter.next().unwrap();
|
||||||
self.mutations
|
self.mutations
|
||||||
.replace_with(first.direct_id(), nodes_created as u32);
|
.replace_with(first.mounted_id(), nodes_created as u32);
|
||||||
self.remove_nodes(iter);
|
self.remove_nodes(iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1091,4 +1091,46 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
let long_listener: &'a Listener<'static> = unsafe { std::mem::transmute(listener) };
|
let long_listener: &'a Listener<'static> = unsafe { std::mem::transmute(listener) };
|
||||||
queue.push(long_listener as *const _)
|
queue.push(long_listener as *const _)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Destroy a scope and all of its descendents.
|
||||||
|
///
|
||||||
|
/// Calling this will run the destuctors on all hooks in the tree.
|
||||||
|
/// It will also add the destroyed nodes to the `seen_nodes` cache to prevent them from being renderered.
|
||||||
|
fn destroy_scopes(&mut self, old_scope: ScopeId) {
|
||||||
|
let mut nodes_to_delete = vec![old_scope];
|
||||||
|
let mut scopes_to_explore = vec![old_scope];
|
||||||
|
|
||||||
|
// explore the scope tree breadth first
|
||||||
|
while let Some(scope_id) = scopes_to_explore.pop() {
|
||||||
|
// If we're planning on deleting this node, then we don't need to both rendering it
|
||||||
|
self.seen_scopes.insert(scope_id);
|
||||||
|
let scope = self.vdom.get_scope(scope_id).unwrap();
|
||||||
|
for child in scope.descendents.borrow().iter() {
|
||||||
|
// Add this node to be explored
|
||||||
|
scopes_to_explore.push(child.clone());
|
||||||
|
|
||||||
|
// Also add it for deletion
|
||||||
|
nodes_to_delete.push(child.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete all scopes that we found as part of this subtree
|
||||||
|
for node in nodes_to_delete {
|
||||||
|
log::debug!("Removing scope {:#?}", node);
|
||||||
|
let _scope = self.vdom.try_remove(node).unwrap();
|
||||||
|
// do anything we need to do to delete the scope
|
||||||
|
// I think we need to run the destructors on the hooks
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare_strs(a: &str, b: &str) -> bool {
|
||||||
|
// Check by pointer, optimizing for static strs
|
||||||
|
if !std::ptr::eq(a, b) {
|
||||||
|
// If the pointers are different then check by value
|
||||||
|
a == b
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
/// Destroy a scope and all of its descendents.
|
|
||||||
///
|
|
||||||
/// Calling this will run the destuctors on all hooks in the tree.
|
|
||||||
/// It will also add the destroyed nodes to the `seen_nodes` cache to prevent them from being renderered.
|
|
||||||
fn destroy_scopes(&mut self, old_scope: ScopeId) {
|
|
||||||
let mut nodes_to_delete = vec![old_scope];
|
|
||||||
let mut scopes_to_explore = vec![old_scope];
|
|
||||||
|
|
||||||
// explore the scope tree breadth first
|
|
||||||
while let Some(scope_id) = scopes_to_explore.pop() {
|
|
||||||
// If we're planning on deleting this node, then we don't need to both rendering it
|
|
||||||
self.seen_scopes.insert(scope_id);
|
|
||||||
let scope = self.get_scope(&scope_id).unwrap();
|
|
||||||
for child in scope.descendents.borrow().iter() {
|
|
||||||
// Add this node to be explored
|
|
||||||
scopes_to_explore.push(child.clone());
|
|
||||||
|
|
||||||
// Also add it for deletion
|
|
||||||
nodes_to_delete.push(child.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete all scopes that we found as part of this subtree
|
|
||||||
for node in nodes_to_delete {
|
|
||||||
log::debug!("Removing scope {:#?}", node);
|
|
||||||
let _scope = self.vdom.try_remove(node).unwrap();
|
|
||||||
// do anything we need to do to delete the scope
|
|
||||||
// I think we need to run the destructors on the hooks
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_scope_mut(&mut self, id: &ScopeId) -> Option<&'bump mut Scope> {
|
|
||||||
// ensure we haven't seen this scope before
|
|
||||||
// if we have, then we're trying to alias it, which is not allowed
|
|
||||||
debug_assert!(!self.seen_scopes.contains(id));
|
|
||||||
|
|
||||||
unsafe { self.vdom.get_scope_mut(*id) }
|
|
||||||
}
|
|
||||||
pub(crate) fn get_scope(&mut self, id: &ScopeId) -> Option<&'bump Scope> {
|
|
||||||
// ensure we haven't seen this scope before
|
|
||||||
// if we have, then we're trying to alias it, which is not allowed
|
|
||||||
unsafe { self.vdom.get_scope(*id) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compare_strs(a: &str, b: &str) -> bool {
|
|
||||||
// Check by pointer, optimizing for static strs
|
|
||||||
if !std::ptr::eq(a, b) {
|
|
||||||
// If the pointers are different then check by value
|
|
||||||
a == b
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_first_real_node<'a>(
|
|
||||||
nodes: impl IntoIterator<Item = &'a VNode<'a>>,
|
|
||||||
scopes: &'a SharedResources,
|
|
||||||
) -> Option<&'a VNode<'a>> {
|
|
||||||
for node in nodes {
|
|
||||||
let mut iter = RealChildIterator::new(node, scopes);
|
|
||||||
if let Some(node) = iter.next() {
|
|
||||||
return Some(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_children(&mut self, old: &'bump [VNode<'bump>]) {
|
|
||||||
self.replace_and_create_many_with_many(old, None)
|
|
||||||
}
|
|
||||||
@@ -119,13 +119,11 @@ where
|
|||||||
let slot = task_dump.clone();
|
let slot = task_dump.clone();
|
||||||
|
|
||||||
let updater = cx.prepare_update();
|
let updater = cx.prepare_update();
|
||||||
let update_id = cx.get_scope_id();
|
let originator = cx.scope.our_arena_idx;
|
||||||
|
|
||||||
let originator = cx.scope.our_arena_idx.clone();
|
|
||||||
|
|
||||||
let handle = cx.submit_task(Box::pin(task_fut.then(move |output| async move {
|
let handle = cx.submit_task(Box::pin(task_fut.then(move |output| async move {
|
||||||
*slot.as_ref().borrow_mut() = Some(output);
|
*slot.as_ref().borrow_mut() = Some(output);
|
||||||
updater(update_id);
|
updater(originator);
|
||||||
originator
|
originator
|
||||||
})));
|
})));
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ pub mod mutations;
|
|||||||
pub mod nodes;
|
pub mod nodes;
|
||||||
pub mod scheduler;
|
pub mod scheduler;
|
||||||
pub mod scope;
|
pub mod scope;
|
||||||
pub mod signals;
|
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod virtual_dom;
|
pub mod virtual_dom;
|
||||||
|
|
||||||
|
|||||||
+197
-70
@@ -1,12 +1,11 @@
|
|||||||
//! Virtual Node Support
|
//! Virtual Node Support
|
||||||
//! --------------------
|
|
||||||
//! VNodes represent lazily-constructed VDom trees that support diffing and event handlers.
|
|
||||||
//!
|
//!
|
||||||
//! These VNodes should be *very* cheap and *very* fast to construct - building a full tree should be insanely quick.
|
//! VNodes represent lazily-constructed VDom trees that support diffing and event handlers. These VNodes should be *very*
|
||||||
use crate::{
|
//! cheap and *very* fast to construct - building a full tree should be quick.
|
||||||
events::SyntheticEvent,
|
|
||||||
innerlude::{empty_cell, Context, DomTree, ElementId, Properties, Scope, ScopeId, FC},
|
use crate::innerlude::{
|
||||||
SuspendedContext,
|
empty_cell, Context, DomTree, ElementId, Properties, Scope, ScopeId, SuspendedContext,
|
||||||
|
SyntheticEvent, FC,
|
||||||
};
|
};
|
||||||
use bumpalo::{boxed::Box as BumpBox, Bump};
|
use bumpalo::{boxed::Box as BumpBox, Bump};
|
||||||
use std::{
|
use std::{
|
||||||
@@ -16,26 +15,113 @@ use std::{
|
|||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Tools for the base unit of the virtual dom - the VNode
|
/// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM.
|
||||||
/// VNodes are intended to be quickly-allocated, lightweight enum values.
|
|
||||||
///
|
///
|
||||||
/// Components will be generating a lot of these very quickly, so we want to
|
/// VNodes are designed to be lightweight and used with with a bump alloactor. To create a VNode, you can use either of:
|
||||||
/// limit the amount of heap allocations / overly large enum sizes.
|
/// - the [`rsx`] macro
|
||||||
|
/// - the [`html`] macro
|
||||||
|
/// - the [`NodeFactory`] API
|
||||||
pub enum VNode<'src> {
|
pub enum VNode<'src> {
|
||||||
|
/// Text VNodes simply bump-allocated (or static) string slices
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// let node = cx.render(rsx!{ "hello" }).unwrap();
|
||||||
|
///
|
||||||
|
/// if let VNode::Text(vtext) = node {
|
||||||
|
/// assert_eq!(vtext.text, "hello");
|
||||||
|
/// assert_eq!(vtext.dom_id.get(), None);
|
||||||
|
/// assert_eq!(vtext.is_static, true);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
Text(VText<'src>),
|
Text(VText<'src>),
|
||||||
|
|
||||||
|
/// Element VNodes are VNodes that may contain attributes, listeners, a key, a tag, and children.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// let node = cx.render(rsx!{
|
||||||
|
/// div {
|
||||||
|
/// key: "a",
|
||||||
|
/// onclick: |e| log::info!("clicked"),
|
||||||
|
/// hidden: "true",
|
||||||
|
/// style: { background_color: "red" }
|
||||||
|
/// "hello"
|
||||||
|
/// }
|
||||||
|
/// }).unwrap();
|
||||||
|
/// if let VNode::Element(velement) = node {
|
||||||
|
/// assert_eq!(velement.tag_name, "div");
|
||||||
|
/// assert_eq!(velement.namespace, None);
|
||||||
|
/// assert_eq!(velement.key, Some("a));
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
Element(&'src VElement<'src>),
|
Element(&'src VElement<'src>),
|
||||||
|
|
||||||
|
/// Fragment nodes may contain many VNodes without a single root.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// rsx!{
|
||||||
|
/// a {}
|
||||||
|
/// link {}
|
||||||
|
/// style {}
|
||||||
|
/// "asd"
|
||||||
|
/// Example {}
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
Fragment(VFragment<'src>),
|
Fragment(VFragment<'src>),
|
||||||
|
|
||||||
|
/// Component nodes represent a mounted component with props, children, and a key.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// fn Example(cx: Context<()>) -> DomTree {
|
||||||
|
/// todo!()
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let node = cx.render(rsx!{
|
||||||
|
/// Example {}
|
||||||
|
/// }).unwrap();
|
||||||
|
///
|
||||||
|
/// if let VNode::Component(vcomp) = node {
|
||||||
|
/// assert_eq!(vcomp.user_fc, Example as *const ());
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
Component(&'src VComponent<'src>),
|
Component(&'src VComponent<'src>),
|
||||||
|
|
||||||
|
/// Suspended VNodes represent chunks of the UI tree that are not yet ready to be displayed.
|
||||||
|
///
|
||||||
|
/// These nodes currently can only be constructed via the [`use_suspense`] hook.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// rsx!{
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
Suspended(&'src VSuspended<'src>),
|
Suspended(&'src VSuspended<'src>),
|
||||||
|
|
||||||
|
/// Anchors are a type of placeholder VNode used when fragments don't contain any children.
|
||||||
|
///
|
||||||
|
/// Anchors cannot be directly constructed via public APIs.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// let node = cx.render(rsx! ( Fragment {} )).unwrap();
|
||||||
|
/// if let VNode::Fragment(frag) = node {
|
||||||
|
/// let root = &frag.children[0];
|
||||||
|
/// assert_eq!(root, VNode::Anchor);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
Anchor(VAnchor),
|
Anchor(VAnchor),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> VNode<'src> {
|
impl<'src> VNode<'src> {
|
||||||
|
/// Get the VNode's "key" used in the keyed diffing algorithm.
|
||||||
pub fn key(&self) -> Option<&'src str> {
|
pub fn key(&self) -> Option<&'src str> {
|
||||||
match &self {
|
match &self {
|
||||||
VNode::Element(el) => el.key,
|
VNode::Element(el) => el.key,
|
||||||
@@ -46,25 +132,35 @@ impl<'src> VNode<'src> {
|
|||||||
VNode::Anchor(_f) => None,
|
VNode::Anchor(_f) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn direct_id(&self) -> ElementId {
|
|
||||||
self.try_direct_id().unwrap()
|
/// Get the ElementID of the mounted VNode.
|
||||||
|
///
|
||||||
|
/// Panics if the mounted ID is None or if the VNode is not represented by a single Element.
|
||||||
|
pub fn mounted_id(&self) -> ElementId {
|
||||||
|
self.try_mounted_id().unwrap()
|
||||||
}
|
}
|
||||||
pub fn try_direct_id(&self) -> Option<ElementId> {
|
|
||||||
|
/// Try to get the ElementID of the mounted VNode.
|
||||||
|
///
|
||||||
|
/// Returns None if the VNode is not mounted, or if the VNode cannot be presented by a mounted ID (Fragment/Component)
|
||||||
|
pub fn try_mounted_id(&self) -> Option<ElementId> {
|
||||||
match &self {
|
match &self {
|
||||||
VNode::Text(el) => el.dom_id.get(),
|
VNode::Text(el) => el.dom_id.get(),
|
||||||
VNode::Element(el) => el.dom_id.get(),
|
VNode::Element(el) => el.dom_id.get(),
|
||||||
VNode::Anchor(el) => el.dom_id.get(),
|
VNode::Anchor(el) => el.dom_id.get(),
|
||||||
|
VNode::Suspended(el) => el.dom_id.get(),
|
||||||
VNode::Fragment(_) => None,
|
VNode::Fragment(_) => None,
|
||||||
VNode::Component(_) => None,
|
VNode::Component(_) => None,
|
||||||
VNode::Suspended(_) => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A placeholder node only generated when Fragments don't have any children.
|
||||||
pub struct VAnchor {
|
pub struct VAnchor {
|
||||||
pub dom_id: Cell<Option<ElementId>>,
|
pub dom_id: Cell<Option<ElementId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A bump-alloacted string slice and metadata.
|
||||||
pub struct VText<'src> {
|
pub struct VText<'src> {
|
||||||
pub text: &'src str,
|
pub text: &'src str,
|
||||||
|
|
||||||
@@ -73,6 +169,7 @@ pub struct VText<'src> {
|
|||||||
pub is_static: bool,
|
pub is_static: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A list of VNodes with no single root.
|
||||||
pub struct VFragment<'src> {
|
pub struct VFragment<'src> {
|
||||||
pub key: Option<&'src str>,
|
pub key: Option<&'src str>,
|
||||||
|
|
||||||
@@ -81,6 +178,7 @@ pub struct VFragment<'src> {
|
|||||||
pub is_static: bool,
|
pub is_static: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An element like a "div" with children, listeners, and attributes.
|
||||||
pub struct VElement<'a> {
|
pub struct VElement<'a> {
|
||||||
pub tag_name: &'static str,
|
pub tag_name: &'static str,
|
||||||
|
|
||||||
@@ -97,6 +195,22 @@ pub struct VElement<'a> {
|
|||||||
pub children: &'a [VNode<'a>],
|
pub children: &'a [VNode<'a>],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A trait for any generic Dioxus Element.
|
||||||
|
///
|
||||||
|
/// This trait provides the ability to use custom elements in the `rsx!` macro.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// struct my_element;
|
||||||
|
///
|
||||||
|
/// impl DioxusElement for my_element {
|
||||||
|
/// const TAG_NAME: "my_element";
|
||||||
|
/// const NAME_SPACE: None;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let _ = rsx!{
|
||||||
|
/// my_element {}
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
pub trait DioxusElement {
|
pub trait DioxusElement {
|
||||||
const TAG_NAME: &'static str;
|
const TAG_NAME: &'static str;
|
||||||
const NAME_SPACE: Option<&'static str>;
|
const NAME_SPACE: Option<&'static str>;
|
||||||
@@ -122,18 +236,21 @@ pub struct Attribute<'a> {
|
|||||||
|
|
||||||
pub is_volatile: bool,
|
pub is_volatile: bool,
|
||||||
|
|
||||||
// Doesn't exist in the html spec, mostly used to denote "style" tags - could be for any type of group
|
// Doesn't exist in the html spec.
|
||||||
|
// Used in Dioxus to denote "style" tags.
|
||||||
pub namespace: Option<&'static str>,
|
pub namespace: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An event listener.
|
/// An event listener.
|
||||||
/// IE onclick, onkeydown, etc
|
/// IE onclick, onkeydown, etc
|
||||||
pub struct Listener<'bump> {
|
pub struct Listener<'bump> {
|
||||||
/// The type of event to listen for.
|
|
||||||
pub(crate) event: &'static str,
|
|
||||||
|
|
||||||
pub mounted_node: Cell<Option<ElementId>>,
|
pub mounted_node: Cell<Option<ElementId>>,
|
||||||
|
|
||||||
|
/// The type of event to listen for.
|
||||||
|
///
|
||||||
|
/// IE "onclick" - whatever the renderer needs to attach the listener by name.
|
||||||
|
pub event: &'static str,
|
||||||
|
|
||||||
pub(crate) callback: RefCell<Option<BumpBox<'bump, dyn FnMut(SyntheticEvent) + 'bump>>>,
|
pub(crate) callback: RefCell<Option<BumpBox<'bump, dyn FnMut(SyntheticEvent) + 'bump>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,6 +261,11 @@ pub struct VComponent<'src> {
|
|||||||
|
|
||||||
pub associated_scope: Cell<Option<ScopeId>>,
|
pub associated_scope: Cell<Option<ScopeId>>,
|
||||||
|
|
||||||
|
pub is_static: bool,
|
||||||
|
|
||||||
|
// Function pointer to the FC that was used to generate this component
|
||||||
|
pub user_fc: *const (),
|
||||||
|
|
||||||
pub(crate) caller: Rc<dyn Fn(&Scope) -> DomTree>,
|
pub(crate) caller: Rc<dyn Fn(&Scope) -> DomTree>,
|
||||||
|
|
||||||
pub(crate) children: &'src [VNode<'src>],
|
pub(crate) children: &'src [VNode<'src>],
|
||||||
@@ -152,15 +274,10 @@ pub struct VComponent<'src> {
|
|||||||
|
|
||||||
pub(crate) drop_props: RefCell<Option<BumpBox<'src, dyn FnMut()>>>,
|
pub(crate) drop_props: RefCell<Option<BumpBox<'src, dyn FnMut()>>>,
|
||||||
|
|
||||||
pub is_static: bool,
|
|
||||||
|
|
||||||
pub(crate) can_memoize: bool,
|
pub(crate) can_memoize: bool,
|
||||||
|
|
||||||
// Raw pointer into the bump arena for the props of the component
|
// Raw pointer into the bump arena for the props of the component
|
||||||
pub(crate) raw_props: *const (),
|
pub(crate) raw_props: *const (),
|
||||||
|
|
||||||
// Function pointer to the FC that was used to generate this component
|
|
||||||
pub(crate) user_fc: *const (),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct VSuspended<'a> {
|
pub struct VSuspended<'a> {
|
||||||
@@ -203,7 +320,7 @@ impl<'a> NodeFactory<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used in a place or two to make it easier to build vnodes from dummy text
|
/// Directly pass in text blocks without the need to use the format_args macro.
|
||||||
pub fn static_text(&self, text: &'static str) -> VNode<'a> {
|
pub fn static_text(&self, text: &'static str) -> VNode<'a> {
|
||||||
VNode::Text(VText {
|
VNode::Text(VText {
|
||||||
dom_id: empty_cell(),
|
dom_id: empty_cell(),
|
||||||
@@ -220,9 +337,9 @@ impl<'a> NodeFactory<'a> {
|
|||||||
Some(static_str) => (static_str, true),
|
Some(static_str) => (static_str, true),
|
||||||
None => {
|
None => {
|
||||||
use bumpalo::core_alloc::fmt::Write;
|
use bumpalo::core_alloc::fmt::Write;
|
||||||
let mut s = bumpalo::collections::String::new_in(self.bump());
|
let mut str_buf = bumpalo::collections::String::new_in(self.bump());
|
||||||
s.write_fmt(args).unwrap();
|
str_buf.write_fmt(args).unwrap();
|
||||||
(s.into_bump_str(), false)
|
(str_buf.into_bump_str(), false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,7 +381,7 @@ impl<'a> NodeFactory<'a> {
|
|||||||
|
|
||||||
pub fn raw_element<L, A, V>(
|
pub fn raw_element<L, A, V>(
|
||||||
&self,
|
&self,
|
||||||
tag: &'static str,
|
tag_name: &'static str,
|
||||||
namespace: Option<&'static str>,
|
namespace: Option<&'static str>,
|
||||||
listeners: L,
|
listeners: L,
|
||||||
attributes: A,
|
attributes: A,
|
||||||
@@ -288,7 +405,7 @@ impl<'a> NodeFactory<'a> {
|
|||||||
let key = key.map(|f| self.raw_text(f).0);
|
let key = key.map(|f| self.raw_text(f).0);
|
||||||
|
|
||||||
VNode::Element(self.bump().alloc(VElement {
|
VNode::Element(self.bump().alloc(VElement {
|
||||||
tag_name: tag,
|
tag_name,
|
||||||
key,
|
key,
|
||||||
namespace,
|
namespace,
|
||||||
listeners,
|
listeners,
|
||||||
@@ -315,22 +432,6 @@ impl<'a> NodeFactory<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn attr_with_alloc_val(
|
|
||||||
&self,
|
|
||||||
name: &'static str,
|
|
||||||
val: &'a str,
|
|
||||||
namespace: Option<&'static str>,
|
|
||||||
is_volatile: bool,
|
|
||||||
) -> Attribute<'a> {
|
|
||||||
Attribute {
|
|
||||||
name,
|
|
||||||
value: val,
|
|
||||||
is_static: false,
|
|
||||||
namespace,
|
|
||||||
is_volatile,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn component<P, V>(
|
pub fn component<P, V>(
|
||||||
&self,
|
&self,
|
||||||
component: FC<P>,
|
component: FC<P>,
|
||||||
@@ -343,14 +444,9 @@ impl<'a> NodeFactory<'a> {
|
|||||||
V: 'a + AsRef<[VNode<'a>]>,
|
V: 'a + AsRef<[VNode<'a>]>,
|
||||||
{
|
{
|
||||||
let bump = self.bump();
|
let bump = self.bump();
|
||||||
|
|
||||||
// We don't want the fat part of the fat pointer
|
|
||||||
// THe "Fat" part is provided on next-render
|
|
||||||
let children: &'a V = bump.alloc(children);
|
let children: &'a V = bump.alloc(children);
|
||||||
let children = children.as_ref();
|
let children = children.as_ref();
|
||||||
|
|
||||||
let props = bump.alloc(props);
|
let props = bump.alloc(props);
|
||||||
|
|
||||||
let raw_props = props as *mut P as *mut ();
|
let raw_props = props as *mut P as *mut ();
|
||||||
let user_fc = component as *const ();
|
let user_fc = component as *const ();
|
||||||
|
|
||||||
@@ -379,36 +475,46 @@ impl<'a> NodeFactory<'a> {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// create a closure to drop the props
|
let drop_props = {
|
||||||
let mut has_dropped = false;
|
// create a closure to drop the props
|
||||||
let drop_props: &mut dyn FnMut() = bump.alloc_with(|| {
|
let mut has_dropped = false;
|
||||||
move || unsafe {
|
|
||||||
if !has_dropped {
|
|
||||||
let real_other = raw_props as *mut _ as *mut P;
|
|
||||||
let b = BumpBox::from_raw(real_other);
|
|
||||||
std::mem::drop(b);
|
|
||||||
|
|
||||||
has_dropped = true;
|
let drop_props: &mut dyn FnMut() = bump.alloc_with(|| {
|
||||||
|
move || unsafe {
|
||||||
|
if !has_dropped {
|
||||||
|
let real_other = raw_props as *mut _ as *mut P;
|
||||||
|
let b = BumpBox::from_raw(real_other);
|
||||||
|
std::mem::drop(b);
|
||||||
|
|
||||||
|
has_dropped = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
let drop_props = unsafe { BumpBox::from_raw(drop_props) };
|
let drop_props = unsafe { BumpBox::from_raw(drop_props) };
|
||||||
|
|
||||||
|
RefCell::new(Some(drop_props))
|
||||||
|
};
|
||||||
|
|
||||||
let is_static = children.len() == 0 && P::IS_STATIC && key.is_none();
|
let is_static = children.len() == 0 && P::IS_STATIC && key.is_none();
|
||||||
|
|
||||||
let key = key.map(|f| self.raw_text(f).0);
|
let key = key.map(|f| self.raw_text(f).0);
|
||||||
|
|
||||||
|
let caller = NodeFactory::create_component_caller(component, raw_props);
|
||||||
|
|
||||||
|
let can_memoize = children.len() == 0 && P::IS_STATIC;
|
||||||
|
|
||||||
VNode::Component(bump.alloc_with(|| VComponent {
|
VNode::Component(bump.alloc_with(|| VComponent {
|
||||||
user_fc,
|
user_fc,
|
||||||
comparator,
|
comparator,
|
||||||
raw_props,
|
raw_props,
|
||||||
children,
|
children,
|
||||||
caller: NodeFactory::create_component_caller(component, raw_props),
|
caller,
|
||||||
is_static,
|
is_static,
|
||||||
drop_props: RefCell::new(Some(drop_props)),
|
|
||||||
can_memoize: P::IS_STATIC,
|
|
||||||
associated_scope: Cell::new(None),
|
|
||||||
key,
|
key,
|
||||||
|
can_memoize,
|
||||||
|
drop_props,
|
||||||
|
associated_scope: Cell::new(None),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,21 +615,41 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Child nodes of the parent component.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// let children = cx.children();
|
||||||
|
/// let first_node = &children[0];
|
||||||
|
/// rsx!{
|
||||||
|
/// h1 { {first_node} }
|
||||||
|
/// p { {&children[1..]} }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
pub struct ScopeChildren<'a>(pub &'a [VNode<'a>]);
|
pub struct ScopeChildren<'a>(pub &'a [VNode<'a>]);
|
||||||
|
|
||||||
impl Copy for ScopeChildren<'_> {}
|
impl Copy for ScopeChildren<'_> {}
|
||||||
|
|
||||||
impl<'a> Clone for ScopeChildren<'a> {
|
impl<'a> Clone for ScopeChildren<'a> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
ScopeChildren(self.0)
|
ScopeChildren(self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScopeChildren<'_> {
|
impl ScopeChildren<'_> {
|
||||||
pub unsafe fn extend_lifetime(self) -> ScopeChildren<'static> {
|
// dangerous method - used to fix the associated lifetime
|
||||||
|
pub(crate) unsafe fn extend_lifetime(self) -> ScopeChildren<'static> {
|
||||||
std::mem::transmute(self)
|
std::mem::transmute(self)
|
||||||
}
|
}
|
||||||
pub unsafe fn unextend_lfetime<'a>(self) -> ScopeChildren<'a> {
|
|
||||||
|
// dangerous method - used to fix the associated lifetime
|
||||||
|
pub(crate) unsafe fn shorten_lifetime<'a>(self) -> ScopeChildren<'a> {
|
||||||
std::mem::transmute(self)
|
std::mem::transmute(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoVNodeList<'a> for ScopeChildren<'a> {
|
impl<'a> IntoVNodeList<'a> for ScopeChildren<'a> {
|
||||||
fn into_vnode_list(self, _: NodeFactory<'a>) -> &'a [VNode<'a>] {
|
fn into_vnode_list(self, _: NodeFactory<'a>) -> &'a [VNode<'a>] {
|
||||||
self.0
|
self.0
|
||||||
@@ -610,6 +736,7 @@ impl IntoVNode<'_> for Option<()> {
|
|||||||
cx.fragment_from_iter(None as Option<VNode>)
|
cx.fragment_from_iter(None as Option<VNode>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoVNode<'a> for Option<VNode<'a>> {
|
impl<'a> IntoVNode<'a> for Option<VNode<'a>> {
|
||||||
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
|
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
|
||||||
match self {
|
match self {
|
||||||
@@ -647,7 +774,7 @@ impl Debug for VNode<'_> {
|
|||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
VNode::Text(t) => write!(s, "VText {{ text: {} }}", t.text),
|
VNode::Text(t) => write!(s, "VText {{ text: {} }}", t.text),
|
||||||
VNode::Anchor(a) => write!(s, "VAnchor"),
|
VNode::Anchor(_) => write!(s, "VAnchor"),
|
||||||
|
|
||||||
VNode::Fragment(frag) => write!(s, "VFragment {{ children: {:?} }}", frag.children),
|
VNode::Fragment(frag) => write!(s, "VFragment {{ children: {:?} }}", frag.children),
|
||||||
VNode::Suspended { .. } => write!(s, "VSuspended"),
|
VNode::Suspended { .. } => write!(s, "VSuspended"),
|
||||||
|
|||||||
@@ -703,6 +703,8 @@ impl ResourcePool {
|
|||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn borrow_bumpframe(&self) {}
|
||||||
|
|
||||||
pub fn clean_up_garbage(&mut self) {
|
pub fn clean_up_garbage(&mut self) {
|
||||||
// let mut scopes_to_kill = Vec::new();
|
// let mut scopes_to_kill = Vec::new();
|
||||||
// let mut garbage_list = Vec::new();
|
// let mut garbage_list = Vec::new();
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
use crate::innerlude::*;
|
use crate::innerlude::*;
|
||||||
use bumpalo::boxed::Box as BumpBox;
|
|
||||||
use futures_channel::mpsc::UnboundedSender;
|
|
||||||
use fxhash::FxHashSet;
|
use fxhash::FxHashSet;
|
||||||
use std::{
|
use std::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
borrow::BorrowMut,
|
cell::RefCell,
|
||||||
cell::{Cell, RefCell},
|
collections::HashMap,
|
||||||
collections::{HashMap, HashSet},
|
|
||||||
future::Future,
|
future::Future,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
@@ -68,15 +65,10 @@ impl Scope {
|
|||||||
// Therefore, their lifetimes are connected exclusively to the virtual dom
|
// Therefore, their lifetimes are connected exclusively to the virtual dom
|
||||||
pub fn new<'creator_node>(
|
pub fn new<'creator_node>(
|
||||||
caller: Rc<WrappedCaller>,
|
caller: Rc<WrappedCaller>,
|
||||||
|
|
||||||
arena_idx: ScopeId,
|
arena_idx: ScopeId,
|
||||||
|
|
||||||
parent: Option<ScopeId>,
|
parent: Option<ScopeId>,
|
||||||
|
|
||||||
height: u32,
|
height: u32,
|
||||||
|
|
||||||
child_nodes: ScopeChildren,
|
child_nodes: ScopeChildren,
|
||||||
|
|
||||||
shared: EventChannel,
|
shared: EventChannel,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let child_nodes = unsafe { child_nodes.extend_lifetime() };
|
let child_nodes = unsafe { child_nodes.extend_lifetime() };
|
||||||
@@ -225,7 +217,7 @@ impl Scope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn child_nodes<'a>(&'a self) -> ScopeChildren {
|
pub fn child_nodes<'a>(&'a self) -> ScopeChildren {
|
||||||
unsafe { self.child_nodes.unextend_lfetime() }
|
unsafe { self.child_nodes.shorten_lifetime() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn consume_garbage(&self) -> Vec<&VNode> {
|
pub fn consume_garbage(&self) -> Vec<&VNode> {
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
//! Signas: Avoid the Diff Engine
|
|
||||||
//! -----------------------------
|
|
||||||
//!
|
|
||||||
//!
|
|
||||||
//! TODO!
|
|
||||||
@@ -14,13 +14,12 @@
|
|||||||
//! - The [`ActiveFrame`] object for managing the Scope`s microheap
|
//! - The [`ActiveFrame`] object for managing the Scope`s microheap
|
||||||
//! - The [`Context`] object for exposing VirtualDOM API to components
|
//! - The [`Context`] object for exposing VirtualDOM API to components
|
||||||
//! - The [`NodeFactory`] object for lazyily exposing the `Context` API to the nodebuilder API
|
//! - The [`NodeFactory`] object for lazyily exposing the `Context` API to the nodebuilder API
|
||||||
//! - The [`Hook`] object for exposing state management in components.
|
|
||||||
//!
|
//!
|
||||||
//! This module includes just the barebones for a complete VirtualDOM API.
|
//! This module includes just the barebones for a complete VirtualDOM API.
|
||||||
//! Additional functionality is defined in the respective files.
|
//! Additional functionality is defined in the respective files.
|
||||||
|
|
||||||
use crate::innerlude::*;
|
use crate::innerlude::*;
|
||||||
use futures_util::{pin_mut, Future, FutureExt};
|
use futures_util::{Future, FutureExt};
|
||||||
use std::{
|
use std::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ async fn test_iterative_create_components() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{(0..0).map(|f| rsx!{ div { "walalla"}})}
|
{(0..0).map(|_f| rsx!{ div { "walalla"}})}
|
||||||
p {}
|
p {}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ about 10ms, which is way more than enough for diffing/creating to happen.
|
|||||||
async fn batch() {
|
async fn batch() {
|
||||||
let (sender, mut recver) = futures_channel::mpsc::unbounded::<i32>();
|
let (sender, mut recver) = futures_channel::mpsc::unbounded::<i32>();
|
||||||
|
|
||||||
let handle = async_std::task::spawn(async move {
|
let _handle = async_std::task::spawn(async move {
|
||||||
let msg = recver.next().await;
|
let _msg = recver.next().await;
|
||||||
while let Ok(msg) = recver.try_next() {
|
while let Ok(msg) = recver.try_next() {
|
||||||
println!("{:#?}", msg);
|
println!("{:#?}", msg);
|
||||||
}
|
}
|
||||||
let msg = recver.next().await;
|
let _msg = recver.next().await;
|
||||||
while let Ok(msg) = recver.try_next() {
|
while let Ok(msg) = recver.try_next() {
|
||||||
println!("{:#?}", msg);
|
println!("{:#?}", msg);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
pub fn set_up_logging(enabled: bool) {
|
pub fn set_up_logging(enabled: bool) {
|
||||||
use fern::colors::{Color, ColoredLevelConfig};
|
use fern::colors::{Color, ColoredLevelConfig};
|
||||||
use log::debug;
|
|
||||||
|
|
||||||
if !enabled {
|
if !enabled {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ impl<'a> TextRenderer<'a> {
|
|||||||
//
|
//
|
||||||
// when the page is loaded, the `querySelectorAll` will be used to collect all the nodes, and then add
|
// when the page is loaded, the `querySelectorAll` will be used to collect all the nodes, and then add
|
||||||
// them interpreter's stack
|
// them interpreter's stack
|
||||||
match (self.cfg.pre_render, node.try_direct_id()) {
|
match (self.cfg.pre_render, node.try_mounted_id()) {
|
||||||
(true, Some(id)) => {
|
(true, Some(id)) => {
|
||||||
write!(f, " dio_el=\"{}\"", id)?;
|
write!(f, " dio_el=\"{}\"", id)?;
|
||||||
//
|
//
|
||||||
|
|||||||
Reference in new issue
Block a user