examples: upgrade to new version of dioxus core.

also add the inline_props macro
This commit is contained in:
Jonathan Kelley committed 2021-12-25 17:18:05 -05:00
1 parent 21e00c114e
commit cda759c659
41 files changed
+556 -253

No files matched your search

+5 -5
View File
@@ -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 -11
View File
@@ -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);
+9 -3
View File
@@ -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};
+3
View File
@@ -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};
+11 -3
View File
@@ -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
View File
@@ -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
+7 -7
View File
@@ -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