examples: upgrade to new version of dioxus core.
also add the inline_props macro
This commit is contained in:
1 parent
21e00c114e
commit
cda759c659
41 files changed
+556
-253
No files matched your search
@@ -1,6 +1,6 @@
|
||||
//! This module contains the stateful DiffMachine and all methods to diff VNodes, their properties, and their children.
|
||||
//! This module contains the stateful DiffState and all methods to diff VNodes, their properties, and their children.
|
||||
//!
|
||||
//! The [`DiffMachine`] calculates the diffs between the old and new frames, updates the new nodes, and generates a set
|
||||
//! The [`DiffState`] calculates the diffs between the old and new frames, updates the new nodes, and generates a set
|
||||
//! of mutations for the RealDom to apply.
|
||||
//!
|
||||
//! ## Notice:
|
||||
@@ -86,14 +86,14 @@
|
||||
//! - Certain web-dom-specific optimizations.
|
||||
//!
|
||||
//! More info on how to improve this diffing algorithm:
|
||||
//! - 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 fxhash::{FxHashMap, FxHashSet};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use DomEdit::*;
|
||||
|
||||
/// Our DiffMachine is an iterative tree differ.
|
||||
/// Our DiffState is an iterative tree differ.
|
||||
///
|
||||
/// It uses techniques of a stack machine to allow pausing and restarting of the diff algorithm. This
|
||||
/// was originally implemented using recursive techniques, but Rust lacks the ability to call async functions recursively,
|
||||
@@ -490,7 +490,7 @@ impl<'bump> DiffState<'bump> {
|
||||
(Fragment(old), Fragment(new)) => self.diff_fragment_nodes(old, new),
|
||||
|
||||
// The normal pathway still works, but generates slightly weird instructions
|
||||
// This pathway just ensures we get create and replace
|
||||
// This pathway ensures uses the ReplaceAll, not the InsertAfter and remove
|
||||
(Placeholder(_), Fragment(new)) => {
|
||||
self.stack
|
||||
.create_children(new.children, MountType::Replace { old: old_node });
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
//! This can be done either through boxing directly, or by using dynamic-sized-types and a custom allocator. In our case,
|
||||
//! we build a tiny alloactor in the stack and allocate the closure into that.
|
||||
//!
|
||||
//! The logic for this was borrowed from https://docs.rs/stack_dst/0.6.1/stack_dst/. Unfortunately, this crate does not
|
||||
//! The logic for this was borrowed from <https://docs.rs/stack_dst/0.6.1/stack_dst/>. Unfortunately, this crate does not
|
||||
//! support non-static closures, so we've implemented the core logic of `ValueA` in this module.
|
||||
|
||||
use crate::prelude::{NodeFactory, VNode};
|
||||
use crate::innerlude::{NodeFactory, VNode};
|
||||
use std::mem;
|
||||
|
||||
/// A concrete type provider for closures that build VNode structures.
|
||||
@@ -35,15 +35,15 @@ enum StackNodeStorage<'a, 'b> {
|
||||
}
|
||||
|
||||
impl<'a, 'b> LazyNodes<'a, 'b> {
|
||||
pub fn new_some<F>(_val: F) -> Option<Self>
|
||||
pub fn new_some<F>(_val: F) -> Self
|
||||
where
|
||||
F: FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b,
|
||||
{
|
||||
Some(Self::new(_val))
|
||||
Self::new(_val)
|
||||
}
|
||||
|
||||
/// force this call onto the stack
|
||||
pub fn new_boxed<F>(_val: F) -> Option<Self>
|
||||
pub fn new_boxed<F>(_val: F) -> Self
|
||||
where
|
||||
F: FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b,
|
||||
{
|
||||
@@ -55,9 +55,9 @@ impl<'a, 'b> LazyNodes<'a, 'b> {
|
||||
fac.map(inner)
|
||||
};
|
||||
|
||||
Some(Self {
|
||||
Self {
|
||||
inner: StackNodeStorage::Heap(Box::new(val)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new<F>(_val: F) -> Self
|
||||
@@ -249,9 +249,7 @@ fn it_works() {
|
||||
let caller = LazyNodes::new_some(|f| {
|
||||
//
|
||||
f.text(format_args!("hello world!"))
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
});
|
||||
let g = caller.call(factory);
|
||||
|
||||
dbg!(g);
|
||||
@@ -291,7 +289,6 @@ fn it_drops() {
|
||||
log::debug!("main closure");
|
||||
f.fragment_from_iter(it)
|
||||
})
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let _ = caller.call(factory);
|
||||
|
||||
@@ -57,19 +57,25 @@ pub(crate) mod innerlude {
|
||||
/// // ...
|
||||
/// };
|
||||
/// ```
|
||||
pub type Component<P> = fn(Scope<P>) -> Element;
|
||||
pub type Component<P = ()> = fn(Scope<P>) -> Element;
|
||||
|
||||
/// A list of attributes
|
||||
///
|
||||
pub type Attributes<'a> = Option<&'a [Attribute<'a>]>;
|
||||
}
|
||||
|
||||
pub use crate::innerlude::{
|
||||
Attribute, Component, DioxusElement, DomEdit, Element, ElementId, EventHandler, EventPriority,
|
||||
IntoVNode, LazyNodes, Listener, Mutations, NodeFactory, Properties, SchedulerMsg, Scope,
|
||||
ScopeId, ScopeState, UserEvent, VElement, VFragment, VNode, VirtualDom,
|
||||
ScopeId, ScopeState, UserEvent, VComponent, VElement, VFragment, VNode, VPlaceholder, VText,
|
||||
VirtualDom,
|
||||
};
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::innerlude::Scope;
|
||||
pub use crate::innerlude::{
|
||||
Component, DioxusElement, Element, EventHandler, LazyNodes, NodeFactory, ScopeState,
|
||||
Attributes, Component, DioxusElement, Element, EventHandler, LazyNodes, NodeFactory,
|
||||
ScopeState,
|
||||
};
|
||||
pub use crate::nodes::VNode;
|
||||
pub use crate::virtual_dom::{fc_to_builder, Fragment, Properties};
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
//! Instructions returned by the VirtualDOM on how to modify the Real DOM.
|
||||
//!
|
||||
//! This module contains an internal API to generate these instructions.
|
||||
//!
|
||||
//! Beware that changing code in this module will break compatibility with
|
||||
//! interpreters for these types of DomEdits.
|
||||
|
||||
use crate::innerlude::*;
|
||||
use std::{any::Any, fmt::Debug};
|
||||
|
||||
@@ -20,7 +20,7 @@ use std::{
|
||||
///
|
||||
/// VNodes are designed to be lightweight and used with with a bump allocator. To create a VNode, you can use either of:
|
||||
///
|
||||
/// - the [`rsx`] macro
|
||||
/// - the `rsx!` macro
|
||||
/// - the [`NodeFactory`] API
|
||||
pub enum VNode<'src> {
|
||||
/// Text VNodes simply bump-allocated (or static) string slices
|
||||
@@ -51,7 +51,7 @@ pub enum VNode<'src> {
|
||||
/// key: "a",
|
||||
/// onclick: |e| log::info!("clicked"),
|
||||
/// hidden: "true",
|
||||
/// style: { background_color: "red" }
|
||||
/// style: { background_color: "red" },
|
||||
/// "hello"
|
||||
/// }
|
||||
/// });
|
||||
@@ -59,7 +59,7 @@ pub enum VNode<'src> {
|
||||
/// if let VNode::Element(velement) = node {
|
||||
/// assert_eq!(velement.tag_name, "div");
|
||||
/// assert_eq!(velement.namespace, None);
|
||||
/// assert_eq!(velement.key, Some("a));
|
||||
/// assert_eq!(velement.key, Some("a"));
|
||||
/// }
|
||||
/// ```
|
||||
Element(&'src VElement<'src>),
|
||||
@@ -720,6 +720,14 @@ impl<'a> IntoVNode<'a> for Option<LazyNodes<'a, '_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> IntoIterator for LazyNodes<'a, 'b> {
|
||||
type Item = LazyNodes<'a, 'b>;
|
||||
type IntoIter = std::iter::Once<Self::Item>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
std::iter::once(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> IntoVNode<'a> for LazyNodes<'a, 'b> {
|
||||
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
|
||||
self.call(cx)
|
||||
|
||||
+37
-16
@@ -351,7 +351,7 @@ impl ScopeArena {
|
||||
/// cx.render(rsx!{ div {"Hello, {cx.props.name}"} })
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Scope<'a, P> {
|
||||
pub struct Scope<'a, P = ()> {
|
||||
pub scope: &'a ScopeState,
|
||||
pub props: &'a P,
|
||||
}
|
||||
@@ -366,6 +366,26 @@ impl<P> Clone for Scope<'_, P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'src, P> Scope<'src, P> {
|
||||
pub fn register_task(self, task: impl Future<Output = ()> + 'src) {
|
||||
let fut: &'src mut dyn Future<Output = ()> = self.scope.bump().alloc(task);
|
||||
let boxed_fut: BumpBox<'src, dyn Future<Output = ()> + 'src> =
|
||||
unsafe { BumpBox::from_raw(fut) };
|
||||
let pinned_fut: Pin<BumpBox<'src, _>> = boxed_fut.into();
|
||||
|
||||
// erase the 'src lifetime for self-referential storage
|
||||
// todo: provide a miri test around this
|
||||
// concerned about segfaulting
|
||||
let self_ref_fut = unsafe { std::mem::transmute(pinned_fut) };
|
||||
|
||||
self.sender
|
||||
.unbounded_send(SchedulerMsg::NewTask(self.our_arena_idx))
|
||||
.unwrap();
|
||||
|
||||
self.scope.items.borrow_mut().tasks.push(self_ref_fut);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, P> std::ops::Deref for Scope<'a, P> {
|
||||
// rust will auto deref again to the original 'a lifetime at the call site
|
||||
type Target = &'a ScopeState;
|
||||
@@ -578,7 +598,7 @@ impl ScopeState {
|
||||
|
||||
/// Schedule an update for any component given its ScopeId.
|
||||
///
|
||||
/// A component's ScopeId can be obtained from `use_hook` or the [`Context::scope_id`] method.
|
||||
/// A component's ScopeId can be obtained from `use_hook` or the [`ScopeState::scope_id`] method.
|
||||
///
|
||||
/// This method should be used when you want to schedule an update for a component
|
||||
pub fn schedule_update_any(&self) -> Rc<dyn Fn(ScopeId)> {
|
||||
@@ -662,29 +682,33 @@ impl ScopeState {
|
||||
/// Pushes the future onto the poll queue to be polled after the component renders.
|
||||
///
|
||||
/// The future is forcibly dropped if the component is not ready by the next render
|
||||
pub fn push_task<'src, F>(&'src self, fut: impl FnOnce() -> F + 'src) -> usize
|
||||
pub fn push_task<'src, F>(&'src self, fut: F) -> usize
|
||||
where
|
||||
F: Future<Output = ()>,
|
||||
F::Output: 'src,
|
||||
F: 'src,
|
||||
{
|
||||
self.sender
|
||||
.unbounded_send(SchedulerMsg::NewTask(self.our_arena_idx))
|
||||
.unwrap();
|
||||
|
||||
// wrap it in a type that will actually drop the contents
|
||||
//
|
||||
// Safety: we just made the pointer above and will promise not to alias it!
|
||||
// The main reason we do this through from_raw is because Bumpalo's box does
|
||||
// not support unsized coercion
|
||||
let fut: &mut dyn Future<Output = ()> = self.bump().alloc(fut());
|
||||
let boxed_fut: BumpBox<dyn Future<Output = ()>> = unsafe { BumpBox::from_raw(fut) };
|
||||
let pinned_fut: Pin<BumpBox<_>> = boxed_fut.into();
|
||||
|
||||
let fut: &'src mut dyn Future<Output = ()> = self.bump().alloc(fut);
|
||||
let boxed_fut: BumpBox<'src, dyn Future<Output = ()> + 'src> =
|
||||
unsafe { BumpBox::from_raw(fut) };
|
||||
let pinned_fut: Pin<BumpBox<'src, _>> = boxed_fut.into();
|
||||
|
||||
// erase the 'src lifetime for self-referential storage
|
||||
// todo: provide a miri test around this
|
||||
// concerned about segfaulting
|
||||
let self_ref_fut = unsafe { std::mem::transmute(pinned_fut) };
|
||||
let self_ref_fut = unsafe {
|
||||
std::mem::transmute::<Pin<BumpBox<'src, _>>, Pin<BumpBox<'static, _>>>(pinned_fut)
|
||||
};
|
||||
|
||||
self.sender
|
||||
.unbounded_send(SchedulerMsg::NewTask(self.our_arena_idx))
|
||||
.unwrap();
|
||||
|
||||
// Push the future into the tasks
|
||||
let mut items = self.items.borrow_mut();
|
||||
@@ -707,14 +731,11 @@ impl ScopeState {
|
||||
/// cx.render(lazy_tree)
|
||||
/// }
|
||||
///```
|
||||
pub fn render<'src>(&'src self, rsx: Option<LazyNodes<'src, '_>>) -> Option<VNode<'src>> {
|
||||
pub fn render<'src>(&'src self, rsx: LazyNodes<'src, '_>) -> Option<VNode<'src>> {
|
||||
let fac = NodeFactory {
|
||||
bump: &self.wip_frame().bump,
|
||||
};
|
||||
match rsx {
|
||||
Some(s) => Some(s.call(fac)),
|
||||
None => todo!("oh no no nodes"),
|
||||
}
|
||||
Some(rsx.call(fac))
|
||||
}
|
||||
|
||||
/// Store a value between renders
|
||||
|
||||
@@ -15,7 +15,7 @@ use std::{any::Any, collections::VecDeque, iter::FromIterator, pin::Pin, sync::A
|
||||
///
|
||||
/// ## Guide
|
||||
///
|
||||
/// Components are defined as simple functions that take [`Context`] and a [`Properties`] type and return an [`Element`].
|
||||
/// Components are defined as simple functions that take [`Scope`] and return an [`Element`].
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// #[derive(Props, PartialEq)]
|
||||
@@ -552,11 +552,11 @@ impl VirtualDom {
|
||||
/// let dom = VirtualDom::new(Base);
|
||||
/// let nodes = dom.render_nodes(rsx!("div"));
|
||||
/// ```
|
||||
pub fn render_vnodes<'a>(&'a self, lazy_nodes: Option<LazyNodes<'a, '_>>) -> &'a VNode<'a> {
|
||||
pub fn render_vnodes<'a>(&'a self, lazy_nodes: LazyNodes<'a, '_>) -> &'a VNode<'a> {
|
||||
let scope = self.scopes.get_scope(ScopeId(0)).unwrap();
|
||||
let frame = scope.wip_frame();
|
||||
let factory = NodeFactory { bump: &frame.bump };
|
||||
let node = lazy_nodes.unwrap().call(factory);
|
||||
let node = lazy_nodes.call(factory);
|
||||
frame.bump.alloc(node)
|
||||
}
|
||||
|
||||
@@ -594,7 +594,7 @@ impl VirtualDom {
|
||||
/// let dom = VirtualDom::new(Base);
|
||||
/// let nodes = dom.render_nodes(rsx!("div"));
|
||||
/// ```
|
||||
pub fn create_vnodes<'a>(&'a self, nodes: Option<LazyNodes<'a, '_>>) -> Mutations<'a> {
|
||||
pub fn create_vnodes<'a>(&'a self, nodes: LazyNodes<'a, '_>) -> Mutations<'a> {
|
||||
let mut machine = DiffState::new(&self.scopes);
|
||||
machine.stack.element_stack.push(ElementId(0));
|
||||
machine
|
||||
@@ -619,8 +619,8 @@ impl VirtualDom {
|
||||
/// ```
|
||||
pub fn diff_lazynodes<'a>(
|
||||
&'a self,
|
||||
left: Option<LazyNodes<'a, '_>>,
|
||||
right: Option<LazyNodes<'a, '_>>,
|
||||
left: LazyNodes<'a, '_>,
|
||||
right: LazyNodes<'a, '_>,
|
||||
) -> (Mutations<'a>, Mutations<'a>) {
|
||||
let (old, new) = (self.render_vnodes(left), self.render_vnodes(right));
|
||||
|
||||
@@ -947,7 +947,7 @@ impl<'a> Properties for FragmentProps<'a> {
|
||||
#[allow(non_upper_case_globals, non_snake_case)]
|
||||
pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element {
|
||||
let i = cx.props.0.as_ref().map(|f| f.decouple());
|
||||
cx.render(Some(LazyNodes::new(|f| f.fragment_from_iter(i))))
|
||||
cx.render(LazyNodes::new(|f| f.fragment_from_iter(i)))
|
||||
}
|
||||
|
||||
/// Every "Props" used for a component must implement the `Properties` trait. This trait gives some hints to Dioxus
|
||||
|
||||
Reference in new issue
Block a user