fix: messed up how lifetimes worked, need to render once per component

This commit is contained in:
Jonathan Kelley 2021-10-29 21:43:21 -04:00
parent 0e9d5fc530
commit ba9e1dbb8f
33 changed files with 323 additions and 184 deletions

View file

@ -20,7 +20,7 @@ fn main() {
dioxus::desktop::launch(App, |c| c);
}
fn App((cx, props): Component<()>) -> Element {
fn App((cx, props): Scope<()>) -> Element {
let text: &mut Vec<String> = cx.use_hook(|_| vec![String::from("abc=def")], |f| f, |_| {});
let first = text.get_mut(0).unwrap();
@ -43,7 +43,7 @@ impl<'a> Drop for C1Props<'a> {
fn drop(&mut self) {}
}
fn Child1<'a>((cx, props): Component<'a, C1Props>) -> Element<'a> {
fn Child1<'a>((cx, props): Scope<'a, C1Props>) -> Element<'a> {
let (left, right) = props.text.split_once("=").unwrap();
cx.render(rsx! {
@ -59,7 +59,7 @@ struct C2Props<'a> {
text: &'a str,
}
fn Child2<'a>((cx, props): Component<'a, C2Props>) -> Element<'a> {
fn Child2<'a>((cx, props): Scope<'a, C2Props>) -> Element<'a> {
cx.render(rsx! {
Child3 {
text: props.text
@ -72,7 +72,7 @@ struct C3Props<'a> {
text: &'a str,
}
fn Child3<'a>((cx, props): Component<'a, C3Props>) -> Element<'a> {
fn Child3<'a>((cx, props): Scope<'a, C3Props>) -> Element<'a> {
cx.render(rsx! {
div { "{props.text}"}
})

View file

@ -116,7 +116,7 @@ struct CalculatorKeyProps<'a> {
onclick: &'a dyn Fn(MouseEvent),
}
fn CalculatorKey<'a>((cx, props): Component<'a, CalculatorKeyProps>) -> Element<'a> {
fn CalculatorKey<'a>((cx, props): Scope<'a, CalculatorKeyProps>) -> Element<'a> {
rsx!(cx, button {
class: "calculator-key {props.name}"
onclick: {props.onclick}

View file

@ -80,7 +80,7 @@ struct ActionButtonProps<'a> {
onclick: &'a dyn Fn(MouseEvent),
}
fn ActionButton<'a>((cx, props): Component<'a, ActionButtonProps>) -> Element<'a> {
fn ActionButton<'a>((cx, props): Scope<'a, ActionButtonProps>) -> Element<'a> {
rsx!(cx, div { class: "col-sm-6 smallpad"
button { class:"btn btn-primary btn-block", r#type: "button", id: "{props.id}", onclick: {props.onclick},
"{props.name}"
@ -93,7 +93,7 @@ struct RowProps {
row_id: usize,
label: Rc<str>,
}
fn Row((cx, props): Component<RowProps>) -> Element {
fn Row((cx, props): Scope<RowProps>) -> Element {
rsx!(cx, tr {
td { class:"col-md-1", "{props.row_id}" }
td { class:"col-md-1", onclick: move |_| { /* run onselect */ }

View file

@ -4,7 +4,7 @@ fn main() {
dioxus::desktop::launch(App, |c| c);
}
fn App((cx, props): Component<()>) -> Element {
fn App((cx, props): Scope<()>) -> Element {
cx.render(rsx! (
div { "Hello, world!" }
))

View file

@ -76,7 +76,7 @@ struct CalculatorKeyProps<'a> {
onclick: &'a dyn Fn(MouseEvent),
}
fn CalculatorKey<'a>((cx, props): Component<'a, CalculatorKeyProps>) -> Element<'a> {
fn CalculatorKey<'a>((cx, props): Scope<'a, CalculatorKeyProps>) -> Element<'a> {
cx.render(rsx! {
button {
class: "calculator-key {props.name}"

View file

@ -184,7 +184,7 @@ mod baller {
pub struct BallerProps {}
/// This component totally balls
pub fn Baller(_: Component<BallerProps>) -> Element {
pub fn Baller(_: Scope<BallerProps>) -> Element {
todo!()
}
}
@ -195,7 +195,7 @@ pub struct TallerProps {
}
/// This component is taller than most :)
pub fn Taller(_: Component<TallerProps>) -> Element {
pub fn Taller(_: Scope<TallerProps>) -> Element {
let b = true;
todo!()
}

View file

@ -85,7 +85,7 @@ pub struct TodoEntryProps {
todo: Rc<TodoItem>,
}
pub fn TodoEntry((cx, props): Component<TodoEntryProps>) -> Element {
pub fn TodoEntry((cx, props): Scope<TodoEntryProps>) -> Element {
let is_editing = use_state(cx, || false);
let contents = use_state(cx, || String::from(""));
let todo = &props.todo;

View file

@ -45,7 +45,7 @@ struct RowProps {
row_id: usize,
label: Label,
}
fn Row((cx, props): Component<RowProps>) -> Element {
fn Row((cx, props): Scope<RowProps>) -> Element {
let [adj, col, noun] = props.label.0;
cx.render(rsx! {
tr {

View file

@ -57,11 +57,11 @@ impl ToTokens for CallBody {
}),
// Otherwise we just build the LazyNode wrapper
None => out_tokens.append_all(quote! {
Some(dioxus::prelude::LazyNodes::new(move |__cx: NodeFactory|{
dioxus::prelude::LazyNodes::new(move |__cx: NodeFactory|{
use dioxus_elements::{GlobalAttributes, SvgAttributes};
#inner
}))
})
}),
};
}

View file

@ -1,3 +0,0 @@
{
"rust-analyzer.inlayHints.enable": true
}

View file

@ -42,8 +42,6 @@ serde = { version = "1", features = ["derive"], optional = true }
serde_repr = { version = "0.1.7", optional = true }
stack_dst = "0.6.1"
[dev-dependencies]
anyhow = "1.0.42"
dioxus-html = { path = "../html" }

View file

@ -34,13 +34,13 @@ fn create_rows(c: &mut Criterion) {
}
}
});
rsx! {
cx.render(rsx! {
table {
tbody {
{rows}
}
}
}
})
};
c.bench_function("create rows", |b| {
@ -57,9 +57,9 @@ struct RowProps {
row_id: usize,
label: Label,
}
fn Row((cx, props): Component<RowProps>) -> Element {
fn Row((cx, props): Scope<RowProps>) -> Element {
let [adj, col, noun] = props.label.0;
rsx! {
cx.render(rsx! {
tr {
td { class:"col-md-1", "{props.row_id}" }
td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
@ -72,7 +72,7 @@ fn Row((cx, props): Component<RowProps>) -> Element {
}
td { class: "col-md-6" }
}
}
})
}
#[derive(PartialEq)]

View file

@ -9,7 +9,7 @@ fn main() {
pub static EXAMPLE: FC<()> = |(cx, _)| {
let list = (0..10).map(|_f| LazyNodes::new(move |_f| todo!()));
Some(LazyNodes::new(move |cx| {
cx.render(LazyNodes::new(move |cx| {
cx.raw_element(
"div",
None,

View file

@ -23,7 +23,7 @@ struct ListItem {
fn app<'a>(cx: Context<'a>, props: &AppProps) -> Element<'a> {
// let (val, set_val) = use_state_classic(cx, || 0);
Some(LazyNodes::new(move |_nodecx| {
cx.render(LazyNodes::new(move |_nodecx| {
todo!()
// builder::ElementBuilder::new(_nodecx, "div")
// .iter_child({
@ -57,7 +57,7 @@ struct ChildProps {
}
fn ChildItem<'a>(cx: Context<'a>, props: &'a ChildProps) -> Element<'a> {
Some(LazyNodes::new(move |__cx| todo!()))
cx.render(LazyNodes::new(move |__cx| todo!()))
}
impl PartialEq for ChildProps {

View file

@ -26,7 +26,7 @@ fn app<'a>(cx: Context<'a>, props: &()) -> Element<'a> {
)
}));
Some(LazyNodes::new(move |f| {
cx.render(LazyNodes::new(move |f| {
f.raw_element(
"div",
None,

View file

@ -1,6 +1,6 @@
#![allow(non_snake_case)]
use dioxus::component::Component;
use dioxus::component::Scope;
use dioxus::events::on::MouseEvent;
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
@ -15,7 +15,7 @@ fn main() {
assert!(g.edits.len() > 1);
}
fn App((cx, props): Component<()>) -> Element {
fn App((cx, props): Scope<()>) -> Element {
let mut rng = SmallRng::from_entropy();
let rows = (0..10_000_usize).map(|f| {
let label = Label::new(&mut rng);
@ -26,13 +26,13 @@ fn App((cx, props): Component<()>) -> Element {
}
}
});
rsx! {
cx.render(rsx! {
table {
tbody {
{rows}
}
}
}
})
}
#[derive(PartialEq, Props)]
@ -41,11 +41,11 @@ struct RowProps {
label: Label,
}
fn Row((cx, props): Component<RowProps>) -> Element {
fn Row((cx, props): Scope<RowProps>) -> Element {
let handler = move |evt: MouseEvent| {
let g = evt.button;
};
rsx! {
cx.render(rsx! {
tr {
td { class:"col-md-1", "{props.row_id}" }
td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
@ -58,7 +58,7 @@ fn Row((cx, props): Component<RowProps>) -> Element {
}
td { class: "col-md-6" }
}
}
})
}
#[derive(PartialEq)]

View file

@ -1,5 +1,8 @@
use dioxus::component::Component;
use std::marker::PhantomData;
use dioxus::component::Scope;
use dioxus::events::on::MouseEvent;
use dioxus::nodes::{IntoVNode, IntoVNodeList};
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_core_macro::*;
@ -20,20 +23,97 @@ fn html_usage() {
let items = ["bob", "bill", "jack"];
let f = items.iter().filter(|f| f.starts_with("b")).map(|f| {
rsx! {
"hello {f}"
}
});
let f = items
.iter()
.filter(|f| f.starts_with('b'))
.map(|f| rsx!("hello {f}"));
let p = rsx! {
div {
{f}
}
};
let p = rsx!(div { {f} });
}
static App2: FC<()> = |(cx, _)| cx.render(rsx!("hello world!"));
static App: FC<()> = |(cx, props)| {
//
rsx!(div {})
let name = cx.use_state(|| 0);
cx.render(rsx!(div {
h1 {}
h2 {}
}))
};
pub trait UseState<'a, T: 'static> {
fn use_state(self, f: impl FnOnce() -> T) -> &'a T;
}
impl<'a, T: 'static> UseState<'a, T> for Context<'a> {
fn use_state(self, f: impl FnOnce() -> T) -> &'a T {
todo!()
}
}
fn App3((cx, props): Scope<()>) -> Element {
let p = rsx! {
Child {
bame: 10,
}
};
cx.render(rsx!(Child {
bame: 102,
..ChildProps { bame: 10 }
}))
}
#[derive(Props, PartialEq, Debug)]
struct ChildProps {
bame: i32, // children: Children<'a>,
}
fn Child<'a>((cx, props): Scope<'a, ChildProps>) -> Element<'a> {
cx.render(rsx!(div {
// {props.children}
}))
}
// Some(LazyNodes::new(|f| {
// //
// // let r = f.fragment_from_iter(&props.children);
// r
// // todo!()
// }))
// todo!()
// rsx!({ Some(p) })
// todo!()
pub struct Children<'a> {
children: VNode<'static>,
_p: PhantomData<&'a ()>,
}
impl<'a> Children<'a> {
pub fn new(children: VNode<'a>) -> Self {
Self {
children: unsafe { std::mem::transmute(children) },
_p: PhantomData,
}
}
}
impl<'a> IntoVNodeList<'a> for &Children<'a> {
fn into_vnode_list(self, cx: NodeFactory<'a>) -> &'a [VNode<'a>] {
todo!()
}
}
static Bapp: FC<()> = |(cx, props)| {
let name = cx.use_state(|| 0);
cx.render(rsx!(
div {
div {
}
div {
}
}
))
};

View file

@ -4,7 +4,7 @@ use dioxus_core::prelude::*;
#[async_std::main]
async fn main() {
static App: FC<()> = |(cx, props)| Some(LazyNodes::new(|f| f.text(format_args!("hello"))));
static App: FC<()> = |(cx, props)| cx.render(LazyNodes::new(|f| f.text(format_args!("hello"))));
let mut dom = VirtualDom::new(App);

View file

@ -42,7 +42,7 @@ use crate::{
/// };
/// ```
///
pub type Component<'a, T> = (Context<'a>, &'a T);
pub type Scope<'a, T> = (Context<'a>, &'a T);
/// Create inline fragments using Component syntax.
///
@ -66,8 +66,8 @@ pub type Component<'a, T> = (Context<'a>, &'a T);
/// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
///
#[allow(non_upper_case_globals, non_snake_case)]
pub fn Fragment((cx, _): Component<()>) -> Element {
Some(LazyNodes::new(|f| f.fragment_from_iter(cx.children())))
pub fn Fragment((cx, _): Scope<()>) -> Element {
cx.render(LazyNodes::new(move |f| f.fragment_from_iter(cx.children())))
}
/// Every "Props" used for a component must implement the `Properties` trait. This trait gives some hints to Dioxus

View file

@ -32,7 +32,7 @@ use std::{any::TypeId, ops::Deref, rc::Rc};
/// }
/// ```
pub struct Context<'src> {
pub scope: &'src Scope,
pub scope: &'src ScopeInner,
}
impl<'src> Copy for Context<'src> {}
@ -45,7 +45,7 @@ impl<'src> Clone for Context<'src> {
// We currently deref to props, but it might make more sense to deref to Scope?
// This allows for code that takes cx.xyz instead of cx.props.xyz
impl<'a> Deref for Context<'a> {
type Target = &'a Scope;
type Target = &'a ScopeInner;
fn deref(&self) -> &Self::Target {
&self.scope
}
@ -137,7 +137,10 @@ impl<'src> Context<'src> {
/// cx.render(lazy_tree)
/// }
///```
pub fn render(self, lazy_nodes: LazyNodes<'src>) -> Option<VNode<'src>> {
pub fn render<F: FnOnce(NodeFactory<'src>) -> VNode<'src>>(
self,
lazy_nodes: LazyNodes<'src, F>,
) -> Option<VNode<'src>> {
let bump = &self.scope.frames.wip_frame().bump;
Some(lazy_nodes.into_vnode(NodeFactory { bump }))
}

View file

@ -342,7 +342,7 @@ impl<'bump> DiffMachine<'bump> {
let parent_scope = self.vdom.get_scope(parent_idx).unwrap();
let new_idx = self.vdom.insert_scope_with_key(|new_idx| {
Scope::new(
ScopeInner::new(
caller,
new_idx,
Some(parent_idx),
@ -1161,7 +1161,7 @@ impl<'bump> DiffMachine<'bump> {
}
/// Adds a listener closure to a scope during diff.
fn attach_listener_to_scope<'a>(&mut self, listener: &'a Listener<'a>, scope: &Scope) {
fn attach_listener_to_scope<'a>(&mut self, listener: &'a Listener<'a>, scope: &ScopeInner) {
let mut queue = scope.listeners.borrow_mut();
let long_listener: &'a Listener<'static> = unsafe { std::mem::transmute(listener) };
queue.push(long_listener as *const _)

View file

@ -135,32 +135,35 @@ where
None => {
let value = hook.value.clone();
Some(LazyNodes::new(|f| {
let bump = f.bump();
let id = hook.handle.our_id;
use bumpalo::boxed::Box as BumpBox;
todo!()
// Some(LazyNodes::new(move |f| {
// let bump = f.bump();
let f: &mut dyn FnMut(SuspendedContext<'src>) -> Element<'src> =
bump.alloc(move |sus| {
let val = value.borrow();
// use bumpalo::boxed::Box as BumpBox;
let out = val
.as_ref()
.unwrap()
.as_ref()
.downcast_ref::<Out>()
.unwrap();
// let f: &mut dyn FnMut(SuspendedContext<'src>) -> Element<'src> =
// bump.alloc(move |sus| {
// let val = value.borrow();
user_callback(sus, out)
});
let callback = unsafe { BumpBox::from_raw(f) };
// let out = val
// .as_ref()
// .unwrap()
// .as_ref()
// .downcast_ref::<Out>()
// .unwrap();
VNode::Suspended(bump.alloc(VSuspended {
dom_id: empty_cell(),
task_id: hook.handle.our_id,
callback: RefCell::new(Some(callback)),
}))
}))
// user_callback(sus, out)
// });
// let callback = unsafe { BumpBox::from_raw(f) };
// VNode::Suspended(bump.alloc(VSuspended {
// dom_id: empty_cell(),
// task_id: id,
// callback: RefCell::new(Some(callback)),
// }))
// }))
}
},
|_| {},
@ -177,10 +180,10 @@ pub struct SuspendedContext<'a> {
}
impl<'src> SuspendedContext<'src> {
pub fn render(
// pub fn render<F: FnOnce(NodeFactory<'src>) -> VNode<'src>>(
// pub fn render(
pub fn render<F: FnOnce(NodeFactory<'src>) -> VNode<'src>>(
self,
lazy_nodes: LazyNodes<'src>,
lazy_nodes: LazyNodes<'src, F>,
) -> Element<'src> {
let bump = &self.inner.scope.frames.wip_frame().bump;
todo!("suspense")

View file

@ -0,0 +1,113 @@
use std::marker::PhantomData;
/*
Remember: calls to rsx! are lazy - they are not evaluated immediately.
They also using dynamic dispatch, so we can return multiple rsx!'s from match statements and such.
If we allocated every rsx! call on the heap, it would be quite wasteful. Rsx! calls are FnOnce, so they can be stored in a stack.
Solutions like stackdst are useful, but they only support 'static closures.
All our closures are bound by the bump lifetime, so stack-dst will not work for us
Our solution is to try and manually allocate the closure onto the stack.
If it fails, then we default to Box.
*/
use crate::innerlude::{IntoVNode, NodeFactory, VNode};
/// A concrete type provider for closures that build VNode structures.
///
/// This struct wraps lazy structs that build VNode trees Normally, we cannot perform a blanket implementation over
/// closures, but if we wrap the closure in a concrete type, we can maintain separate implementations of IntoVNode.
///
///
/// ```rust
/// LazyNodes::new(|f| f.element("div", [], [], [] None))
/// ```
pub struct LazyNodes<'a, F: FnOnce(NodeFactory<'a>) -> VNode<'a>> {
inner: Box<F>,
_p: PhantomData<&'a ()>,
// inner: StackNodeStorage<'a>,
// inner: StackNodeStorage<'a>,
}
type StackHeapSize = [usize; 12];
enum StackNodeStorage<'a> {
Stack {
next_ofs: usize,
buf: StackHeapSize,
width: usize,
},
Heap(Box<dyn FnOnce(NodeFactory<'a>) -> VNode<'a>>),
}
impl<'a, F: FnOnce(NodeFactory<'a>) -> VNode<'a>> LazyNodes<'a, F> {
pub fn new(f: F) -> Self {
// let width = std::mem?::size_of::<F>();
// let b: Box<dyn FnOnce(NodeFactory<'a>) -> VNode<'a>> = Box::new(f);
todo!()
// Self { inner: b }
// todo!()
// if width > std::mem::size_of::<StackHeapSize>() {
// let g: Box<dyn for<'b> FnOnce(NodeFactory<'b>) -> VNode<'b> + 'g> = Box::new(f);
// LazyNodes {
// inner: StackNodeStorage::Heap(g),
// }
// } else {
// let mut buf = [0; 12];
// let mut next_ofs = 0;
// next_ofs += 1;
// LazyNodes {
// inner: StackNodeStorage::Stack {
// next_ofs,
// buf,
// width,
// },
// }
// }
}
}
// Our blanket impl
impl<'a, F> IntoIterator for LazyNodes<'a, F>
where
F: FnOnce(NodeFactory<'a>) -> VNode<'a>,
{
type Item = Self;
type IntoIter = std::iter::Once<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
std::iter::once(self)
}
}
// Our blanket impl
impl<'a, F: FnOnce(NodeFactory<'a>) -> VNode<'a>> IntoVNode<'a> for LazyNodes<'a, F> {
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
todo!()
// match self.inner {
// StackNodeStorage::Stack {
// buf,
// next_ofs,
// width,
// } => {
// // get the start of the allocation
// let r = &buf[0];
// // recast the allocation as dyn FnOnce
// // pretend the FnOnce is box
// let g: Box<dyn FnOnce(NodeFactory<'a>) -> VNode<'a>> = todo!();
// // Box::from_raw(r as *const usize as *mut dyn FnOnce(NodeFactory<'a>));
// // use Box's ability to act as FnOnce
// g(cx)
// }
// StackNodeStorage::Heap(b) => b(cx),
// }
}
}

View file

@ -23,6 +23,7 @@ pub mod events;
pub mod heuristics;
pub mod hooklist;
pub mod hooks;
pub mod lazynodes;
pub mod mutations;
pub mod nodes;
pub mod resources;
@ -45,6 +46,7 @@ pub(crate) mod innerlude {
pub use crate::heuristics::*;
pub(crate) use crate::hooklist::*;
pub use crate::hooks::*;
pub use crate::lazynodes::LazyNodes;
pub use crate::mutations::*;
pub use crate::nodes::*;
pub(crate) use crate::resources::*;
@ -55,10 +57,9 @@ pub(crate) mod innerlude {
pub use crate::threadsafe::*;
pub use crate::util::*;
pub use crate::virtual_dom::*;
// pub type Element<'a> = Option<VNode<'a>>;
pub type Element<'a> = Option<LazyNodes<'a>>;
pub type FC<P> = for<'a> fn(Component<'a, P>) -> Element<'a>;
pub type Element<'a> = Option<VNode<'a>>;
pub type FC<P> = for<'a> fn(Scope<'a, P>) -> Element<'a>;
}
pub use crate::innerlude::{
@ -68,7 +69,7 @@ pub use crate::innerlude::{
};
pub mod prelude {
pub use crate::component::{fc_to_builder, Component, Fragment, Properties};
pub use crate::component::{fc_to_builder, Fragment, Properties, Scope};
pub use crate::context::Context;
pub use crate::hooks::*;
pub use crate::innerlude::{DioxusElement, Element, LazyNodes, Mutations, NodeFactory, FC};

View file

@ -4,7 +4,8 @@
//! cheap and *very* fast to construct - building a full tree should be quick.
use crate::innerlude::{
empty_cell, Context, Element, ElementId, Properties, Scope, ScopeId, SuspendedContext, FC,
empty_cell, Context, Element, ElementId, LazyNodes, Properties, ScopeId, ScopeInner,
SuspendedContext, FC,
};
use bumpalo::{boxed::Box as BumpBox, Bump};
use std::{
@ -13,10 +14,6 @@ use std::{
marker::PhantomData,
};
use stack_dst::Value as StackDST;
pub type LazyNodeFactory<'a> = StackDST<dyn FnOnce(NodeFactory<'a>) -> VNode<'a>>;
/// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM.
///
/// VNodes are designed to be lightweight and used with with a bump allocator. To create a VNode, you can use either of:
@ -291,7 +288,7 @@ pub struct VComponent<'src> {
// Function pointer to the FC that was used to generate this component
pub user_fc: *const (),
pub(crate) caller: &'src dyn for<'b> Fn(&'b Scope) -> Element<'b>,
pub(crate) caller: &'src dyn for<'b> Fn(&'b ScopeInner) -> Element<'b>,
pub(crate) children: &'src [VNode<'src>],
@ -332,11 +329,11 @@ impl<'a> NodeFactory<'a> {
self.bump
}
pub fn render_directly<F>(&self, lazy_nodes: LazyNodes<'a>) -> Option<VNode<'a>>
// pub fn render_directly<F>(&self, lazy_nodes: LazyNodes<'a, F>) -> Element<'a>
// where
// F: FnOnce(NodeFactory<'a>) -> VNode<'a>,
pub fn render_directly<F>(&self, lazy_nodes: LazyNodes<'a, F>) -> Element<'a>
where
F: FnOnce(NodeFactory<'a>) -> VNode<'a>,
{
// pub fn render_directly<F>(&self, lazy_nodes: LazyNodes<'a>) -> Option<VNode<'a>> {
Some(lazy_nodes.into_vnode(NodeFactory { bump: self.bump }))
}
@ -531,8 +528,8 @@ impl<'a> NodeFactory<'a> {
let key = key.map(|f| self.raw_text(f).0);
let caller: &'a mut dyn for<'b> Fn(&'b Scope) -> Element<'b> =
bump.alloc(move |scope: &Scope| -> Element {
let caller: &'a mut dyn for<'b> Fn(&'b ScopeInner) -> Element<'b> =
bump.alloc(move |scope: &ScopeInner| -> Element {
log::debug!("calling component renderr {:?}", scope.our_arena_idx);
let props: &'_ P = unsafe { &*(raw_props as *const P) };
let res: Element = component((Context { scope }, props));
@ -687,60 +684,6 @@ impl<'a> IntoVNode<'a> for VNode<'a> {
}
}
/// A concrete type provider for closures that build VNode structures.
///
/// This struct wraps lazy structs that build VNode trees Normally, we cannot perform a blanket implementation over
/// closures, but if we wrap the closure in a concrete type, we can maintain separate implementations of IntoVNode.
///
///
/// ```rust
/// LazyNodes::new(|f| f.element("div", [], [], [] None))
/// ```
pub struct LazyNodes<'a> {
inner: LazyNodeFactory<'a>,
}
// where
// G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
impl<'a> LazyNodes<'a>
// where
// G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
{
pub fn new(f: impl FnOnce(NodeFactory<'a>) -> VNode<'a>) -> Self {
todo!()
// pub fn new<G: FnOnce(NodeFactory<'a>) -> VNode<'a>>(f: G) -> Self {
// let inner = LazyNodeFactory::new(f);
// Self { inner: f }
}
}
// Our blanket impl
impl<'a> IntoVNode<'a> for LazyNodes<'a>
// where
// G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
{
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
// let f = self.inner;
todo!("manually drop here")
// (self.inner)(cx)
}
}
// Our blanket impl
impl<'a> IntoIterator for LazyNodes<'a>
// where
// G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
// impl<'a, G> IntoIterator for LazyNodes<'a, G>
// where
// G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
{
type Item = Self;
type IntoIter = std::iter::Once<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
std::iter::once(self)
}
}
// Conveniently, we also support "null" (nothing) passed in
impl IntoVNode<'_> for () {
fn into_vnode(self, cx: NodeFactory) -> VNode {
@ -764,7 +707,7 @@ impl<'a> IntoVNode<'a> for Option<VNode<'a>> {
}
}
impl<'a> IntoVNode<'a> for Option<LazyNodes<'a>> {
impl<'a, F: FnOnce(NodeFactory<'a>) -> VNode<'a>> IntoVNode<'a> for Option<LazyNodes<'a, F>> {
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
match self {
Some(n) => n.into_vnode(cx),

View file

@ -15,7 +15,7 @@ pub(crate) struct ResourcePool {
Wrapped in Rc so the "get_shared_context" closure can walk the tree (immutably!)
*/
pub components: Rc<UnsafeCell<Slab<Scope>>>,
pub components: Rc<UnsafeCell<Slab<ScopeInner>>>,
/*
Yes, a slab of "nil". We use this for properly ordering ElementIDs - all we care about is the allocation strategy
@ -32,18 +32,18 @@ pub(crate) struct ResourcePool {
impl ResourcePool {
/// this is unsafe because the caller needs to track which other scopes it's already using
pub fn get_scope(&self, idx: ScopeId) -> Option<&Scope> {
pub fn get_scope(&self, idx: ScopeId) -> Option<&ScopeInner> {
let inner = unsafe { &*self.components.get() };
inner.get(idx.0)
}
/// this is unsafe because the caller needs to track which other scopes it's already using
pub fn get_scope_mut(&self, idx: ScopeId) -> Option<&mut Scope> {
pub fn get_scope_mut(&self, idx: ScopeId) -> Option<&mut ScopeInner> {
let inner = unsafe { &mut *self.components.get() };
inner.get_mut(idx.0)
}
pub fn try_remove(&self, id: ScopeId) -> Option<Scope> {
pub fn try_remove(&self, id: ScopeId) -> Option<ScopeInner> {
let inner = unsafe { &mut *self.components.get() };
Some(inner.remove(id.0))
// .try_remove(id.0)
@ -67,7 +67,7 @@ impl ResourcePool {
els.remove(id.0);
}
pub fn insert_scope_with_key(&self, f: impl FnOnce(ScopeId) -> Scope) -> ScopeId {
pub fn insert_scope_with_key(&self, f: impl FnOnce(ScopeId) -> ScopeInner) -> ScopeId {
let g = unsafe { &mut *self.components.get() };
let entry = g.vacant_entry();
let id = ScopeId(entry.key());

View file

@ -214,7 +214,7 @@ impl Scheduler {
let components = components.clone();
Rc::new(move |id, ty| {
let components = unsafe { &*components.get() };
let mut search: Option<&Scope> = components.get(id.0);
let mut search: Option<&ScopeInner> = components.get(id.0);
while let Some(inner) = search.take() {
if let Some(shared) = inner.shared_contexts.borrow().get(&ty) {
return Some(shared.clone());

View file

@ -18,7 +18,7 @@ use std::{
///
/// We expose the `Scope` type so downstream users can traverse the Dioxus VirtualDOM for whatever
/// use case they might have.
pub struct Scope {
pub struct ScopeInner {
// Book-keeping about our spot in the arena
pub(crate) parent_idx: Option<ScopeId>,
pub(crate) our_arena_idx: ScopeId,
@ -28,7 +28,7 @@ pub struct Scope {
// Nodes
pub(crate) frames: ActiveFrame,
pub(crate) caller: *const dyn for<'b> Fn(&'b Scope) -> Element<'b>,
pub(crate) caller: *const dyn for<'b> Fn(&'b ScopeInner) -> Element<'b>,
pub(crate) child_nodes: ScopeChildren<'static>,
/*
@ -55,7 +55,7 @@ pub struct Scope {
}
/// Public interface for Scopes.
impl Scope {
impl ScopeInner {
/// Get the root VNode for this Scope.
///
/// This VNode is the "entrypoint" VNode. If the component renders multiple nodes, then this VNode will be a fragment.
@ -167,7 +167,7 @@ impl Scope {
pub type FiberTask = Pin<Box<dyn Future<Output = ScopeId>>>;
/// Private interface for Scopes.
impl Scope {
impl ScopeInner {
// we are being created in the scope of an existing component (where the creator_node lifetime comes into play)
// we are going to break this lifetime by force in order to save it on ourselves.
// To make sure that the lifetime isn't truly broken, we receive a Weak RC so we can't keep it around after the parent dies.
@ -176,7 +176,7 @@ impl Scope {
// Scopes cannot be made anywhere else except for this file
// Therefore, their lifetimes are connected exclusively to the virtual dom
pub(crate) fn new(
caller: &dyn for<'b> Fn(&'b Scope) -> Element<'b>,
caller: &dyn for<'b> Fn(&'b ScopeInner) -> Element<'b>,
our_arena_idx: ScopeId,
parent_idx: Option<ScopeId>,
height: u32,
@ -217,7 +217,7 @@ impl Scope {
pub(crate) fn update_scope_dependencies<'creator_node>(
&mut self,
caller: &'creator_node dyn for<'b> Fn(&'b Scope) -> Element<'b>,
caller: &'creator_node dyn for<'b> Fn(&'b ScopeInner) -> Element<'b>,
child_nodes: ScopeChildren,
) {
log::debug!("Updating scope dependencies {:?}", self.our_arena_idx);
@ -350,7 +350,7 @@ impl Scope {
log::debug!("Borrowed stuff is successfully cleared");
// Cast the caller ptr from static to one with our own reference
let render: &dyn for<'b> Fn(&'b Scope) -> Element<'b> = unsafe { &*self.caller };
let render: &dyn for<'b> Fn(&'b ScopeInner) -> Element<'b> = unsafe { &*self.caller };
// Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
//
@ -376,7 +376,7 @@ impl Scope {
pub(crate) struct ScopeRenderer<'a> {
pub skip_components: bool,
pub show_fragments: bool,
pub _scope: &'a Scope,
pub _scope: &'a ScopeInner,
pub _pre_render: bool,
pub _newline: bool,
pub _indent: bool,

View file

@ -143,15 +143,16 @@ impl VirtualDom {
let _p = root_props.clone();
// Safety: this callback is only valid for the lifetime of the root props
let root_caller: Rc<dyn Fn(&Scope) -> Element> = Rc::new(move |scope: &Scope| unsafe {
let props = _p.downcast_ref::<P>().unwrap();
std::mem::transmute(root((Context { scope }, props)))
});
let root_caller: Rc<dyn Fn(&ScopeInner) -> Element> =
Rc::new(move |scope: &ScopeInner| unsafe {
let props = _p.downcast_ref::<P>().unwrap();
std::mem::transmute(root((Context { scope }, props)))
});
let scheduler = Scheduler::new(sender, receiver);
let base_scope = scheduler.pool.insert_scope_with_key(|myidx| {
Scope::new(
ScopeInner::new(
root_caller.as_ref(),
myidx,
None,
@ -175,12 +176,12 @@ impl VirtualDom {
///
/// This is useful for traversing the tree from the root for heuristics or alternsative renderers that use Dioxus
/// directly.
pub fn base_scope(&self) -> &Scope {
pub fn base_scope(&self) -> &ScopeInner {
self.scheduler.pool.get_scope(self.base_scope).unwrap()
}
/// Get the [`Scope`] for a component given its [`ScopeId`]
pub fn get_scope(&self, id: ScopeId) -> Option<&Scope> {
pub fn get_scope(&self, id: ScopeId) -> Option<&ScopeInner> {
self.scheduler.pool.get_scope(id)
}
@ -219,8 +220,8 @@ impl VirtualDom {
let root = *self.root_fc.downcast_ref::<FC<P>>().unwrap();
let root_caller: Box<dyn Fn(&Scope) -> Element> =
Box::new(move |scope: &Scope| unsafe {
let root_caller: Box<dyn Fn(&ScopeInner) -> Element> =
Box::new(move |scope: &ScopeInner| unsafe {
let props: &'_ P = &*(props_ptr as *const P);
std::mem::transmute(root((Context { scope }, props)))
});
@ -410,4 +411,4 @@ impl std::fmt::Display for VirtualDom {
}
// we never actually use the contents of this root caller
struct RootCaller(Rc<dyn for<'b> Fn(&'b Scope) -> Element<'b> + 'static>);
struct RootCaller(Rc<dyn for<'b> Fn(&'b ScopeInner) -> Element<'b> + 'static>);

View file

@ -10,7 +10,7 @@ fn test_borrowed_state() {
let _ = VirtualDom::new(Parent);
}
fn Parent((cx, _): Component<()>) -> Element {
fn Parent((cx, _): Scope<()>) -> Element {
let value = cx.use_hook(|_| String::new(), |f| &*f, |_| {});
rsx! {
@ -28,7 +28,7 @@ struct ChildProps<'a> {
name: &'a str,
}
fn Child<'a>((cx, props): Component<'a, ChildProps>) -> Element<'a> {
fn Child<'a>((cx, props): Scope<'a, ChildProps>) -> Element<'a> {
rsx! {
div {
h1 { "it's nested" }
@ -42,7 +42,7 @@ struct Grandchild<'a> {
name: &'a str,
}
fn Child2<'a>((cx, props): Component<'a, Grandchild>) -> Element<'a> {
fn Child2<'a>((cx, props): Scope<'a, Grandchild>) -> Element<'a> {
rsx! {
div { "Hello {props.name}!" }
}

View file

@ -92,7 +92,7 @@ fn child_components() {
#[test]
fn suspended_works() {
static App: FC<()> = |(cx, props)| {
let title = use_suspense(cx, || async { "bob" }, |cx, f| rsx! { "{f}"});
let title = use_suspense(cx, || async { "bob" }, move |cx, f| rsx! { "{f}"});
rsx!("hello" { title })
};

View file

@ -136,7 +136,7 @@ pub struct TodoEntryProps {
id: u32,
}
pub fn TodoEntry((cx, props): Component<TodoEntryProps>) -> Element {
pub fn TodoEntry((cx, props): Scope<TodoEntryProps>) -> Element {
let todos = use_shared_state::<Todos>(cx)?;
let _todos = todos.read();

View file

@ -1,6 +1,6 @@
use std::cell::RefCell;
use dioxus::prelude::Component;
use dioxus::prelude::Scope;
use dioxus_core as dioxus;
use dioxus_core::{Context, Element, LazyNodes, NodeFactory, Properties};
use dioxus_core_macro::Props;
@ -54,7 +54,7 @@ pub struct WebviewWindowProps<'a> {
///
///
///
pub fn WebviewWindow<'a>((cx, props): Component<'a, WebviewWindowProps>) -> Element<'a> {
pub fn WebviewWindow<'a>((cx, props): Scope<'a, WebviewWindowProps>) -> Element<'a> {
let dtcx = cx.consume_state::<RefCell<DesktopContext>>()?;
cx.use_hook(