diff --git a/examples/borrowed.rs b/examples/borrowed.rs index c9de40d89..dc4805500 100644 --- a/examples/borrowed.rs +++ b/examples/borrowed.rs @@ -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 = 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}"} }) diff --git a/examples/calculator.rs b/examples/calculator.rs index 6d2d9b2ed..a00ad98f3 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -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} diff --git a/examples/framework_benchmark.rs b/examples/framework_benchmark.rs index b63b1e98d..5ca02fb49 100644 --- a/examples/framework_benchmark.rs +++ b/examples/framework_benchmark.rs @@ -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, } -fn Row((cx, props): Component) -> Element { +fn Row((cx, props): Scope) -> Element { rsx!(cx, tr { td { class:"col-md-1", "{props.row_id}" } td { class:"col-md-1", onclick: move |_| { /* run onselect */ } diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 1115b7e60..7eaf74d7d 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -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!" } )) diff --git a/examples/pattern_model.rs b/examples/pattern_model.rs index 7c24d80de..33d2063a4 100644 --- a/examples/pattern_model.rs +++ b/examples/pattern_model.rs @@ -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}" diff --git a/examples/rsx_usage.rs b/examples/rsx_usage.rs index b14ced9e2..dffd440b4 100644 --- a/examples/rsx_usage.rs +++ b/examples/rsx_usage.rs @@ -184,7 +184,7 @@ mod baller { pub struct BallerProps {} /// This component totally balls - pub fn Baller(_: Component) -> Element { + pub fn Baller(_: Scope) -> Element { todo!() } } @@ -195,7 +195,7 @@ pub struct TallerProps { } /// This component is taller than most :) -pub fn Taller(_: Component) -> Element { +pub fn Taller(_: Scope) -> Element { let b = true; todo!() } diff --git a/examples/todomvc.rs b/examples/todomvc.rs index f0da8d256..294ae36db 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -85,7 +85,7 @@ pub struct TodoEntryProps { todo: Rc, } -pub fn TodoEntry((cx, props): Component) -> Element { +pub fn TodoEntry((cx, props): Scope) -> Element { let is_editing = use_state(cx, || false); let contents = use_state(cx, || String::from("")); let todo = &props.todo; diff --git a/examples/web_tick.rs b/examples/web_tick.rs index f0c9d40df..ea2e087f8 100644 --- a/examples/web_tick.rs +++ b/examples/web_tick.rs @@ -45,7 +45,7 @@ struct RowProps { row_id: usize, label: Label, } -fn Row((cx, props): Component) -> Element { +fn Row((cx, props): Scope) -> Element { let [adj, col, noun] = props.label.0; cx.render(rsx! { tr { diff --git a/packages/core-macro/src/rsx/body.rs b/packages/core-macro/src/rsx/body.rs index 3e28e481e..b5b487459 100644 --- a/packages/core-macro/src/rsx/body.rs +++ b/packages/core-macro/src/rsx/body.rs @@ -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 - })) + }) }), }; } diff --git a/packages/core/.vscode/settings.json b/packages/core/.vscode/settings.json deleted file mode 100644 index 1c68f97cc..000000000 --- a/packages/core/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "rust-analyzer.inlayHints.enable": true -} diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index 7c3dadbdb..9e7dcb279 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -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" } diff --git a/packages/core/benches/jsframework.rs b/packages/core/benches/jsframework.rs index 713d1fafa..3fc3c273c 100644 --- a/packages/core/benches/jsframework.rs +++ b/packages/core/benches/jsframework.rs @@ -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) -> Element { +fn Row((cx, props): Scope) -> 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) -> Element { } td { class: "col-md-6" } } - } + }) } #[derive(PartialEq)] diff --git a/packages/core/examples/alternative.rs b/packages/core/examples/alternative.rs index 3344aa4b2..edf037f89 100644 --- a/packages/core/examples/alternative.rs +++ b/packages/core/examples/alternative.rs @@ -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, diff --git a/packages/core/examples/borrowed.rs b/packages/core/examples/borrowed.rs index f29159c8a..302bb138c 100644 --- a/packages/core/examples/borrowed.rs +++ b/packages/core/examples/borrowed.rs @@ -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 { diff --git a/packages/core/examples/fragment_from_iter.rs b/packages/core/examples/fragment_from_iter.rs index 9cfd6cabe..6daa7a690 100644 --- a/packages/core/examples/fragment_from_iter.rs +++ b/packages/core/examples/fragment_from_iter.rs @@ -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, diff --git a/packages/core/examples/jsframework.rs b/packages/core/examples/jsframework.rs index af698ec26..c0c7af057 100644 --- a/packages/core/examples/jsframework.rs +++ b/packages/core/examples/jsframework.rs @@ -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) -> Element { +fn Row((cx, props): Scope) -> 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) -> Element { } td { class: "col-md-6" } } - } + }) } #[derive(PartialEq)] diff --git a/packages/core/examples/syntax.rs b/packages/core/examples/syntax.rs index b2a8aeaf8..e4bc82fa3 100644 --- a/packages/core/examples/syntax.rs +++ b/packages/core/examples/syntax.rs @@ -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 { + + } + } + )) }; diff --git a/packages/core/examples/vdom_usage.rs b/packages/core/examples/vdom_usage.rs index cd381bf5a..caf58faaf 100644 --- a/packages/core/examples/vdom_usage.rs +++ b/packages/core/examples/vdom_usage.rs @@ -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); diff --git a/packages/core/src/component.rs b/packages/core/src/component.rs index 901f4e4d6..a81a4078d 100644 --- a/packages/core/src/component.rs +++ b/packages/core/src/component.rs @@ -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 diff --git a/packages/core/src/context.rs b/packages/core/src/context.rs index 86d4749cb..5d52a6e4e 100644 --- a/packages/core/src/context.rs +++ b/packages/core/src/context.rs @@ -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> { + pub fn render) -> VNode<'src>>( + self, + lazy_nodes: LazyNodes<'src, F>, + ) -> Option> { let bump = &self.scope.frames.wip_frame().bump; Some(lazy_nodes.into_vnode(NodeFactory { bump })) } diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index d84e59395..9788a75c1 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -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 _) diff --git a/packages/core/src/hooks.rs b/packages/core/src/hooks.rs index 0154dce3b..c1de46bfb 100644 --- a/packages/core/src/hooks.rs +++ b/packages/core/src/hooks.rs @@ -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::() - .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::() + // .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) -> VNode<'src>>( + // pub fn render( + pub fn render) -> 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") diff --git a/packages/core/src/lazynodes.rs b/packages/core/src/lazynodes.rs new file mode 100644 index 000000000..f10cd2134 --- /dev/null +++ b/packages/core/src/lazynodes.rs @@ -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, + _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) -> 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::(); + // let b: Box) -> VNode<'a>> = Box::new(f); + + todo!() + // Self { inner: b } + // todo!() + + // if width > std::mem::size_of::() { + // let g: Box 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; + 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) -> 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), + // } + } +} diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index 02d6f8422..df0804fc8 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -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>; - pub type Element<'a> = Option>; - pub type FC

= for<'a> fn(Component<'a, P>) -> Element<'a>; + pub type Element<'a> = Option>; + pub type FC

= 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}; diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 142eab4b7..10f4d25f9 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -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) -> 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(&self, lazy_nodes: LazyNodes<'a>) -> Option> -// pub fn render_directly(&self, lazy_nodes: LazyNodes<'a, F>) -> Element<'a> - // where - // F: FnOnce(NodeFactory<'a>) -> VNode<'a>, + pub fn render_directly(&self, lazy_nodes: LazyNodes<'a, F>) -> Element<'a> + where + F: FnOnce(NodeFactory<'a>) -> VNode<'a>, { + // pub fn render_directly(&self, lazy_nodes: LazyNodes<'a>) -> Option> { 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) -> 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; - 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> { } } -impl<'a> IntoVNode<'a> for Option> { +impl<'a, F: FnOnce(NodeFactory<'a>) -> VNode<'a>> IntoVNode<'a> for Option> { fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> { match self { Some(n) => n.into_vnode(cx), diff --git a/packages/core/src/resources.rs b/packages/core/src/resources.rs index c3113e826..4163e30d3 100644 --- a/packages/core/src/resources.rs +++ b/packages/core/src/resources.rs @@ -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>>, + pub components: Rc>>, /* 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 { + pub fn try_remove(&self, id: ScopeId) -> Option { 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()); diff --git a/packages/core/src/scheduler.rs b/packages/core/src/scheduler.rs index 745ac1905..fb0ee8f69 100644 --- a/packages/core/src/scheduler.rs +++ b/packages/core/src/scheduler.rs @@ -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()); diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index 348e1b74b..bb20a32f1 100644 --- a/packages/core/src/scope.rs +++ b/packages/core/src/scope.rs @@ -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, 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>>; /// 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, 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, diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index ff0600a08..3092d60b2 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -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 Element> = Rc::new(move |scope: &Scope| unsafe { - let props = _p.downcast_ref::

().unwrap(); - std::mem::transmute(root((Context { scope }, props))) - }); + let root_caller: Rc Element> = + Rc::new(move |scope: &ScopeInner| unsafe { + let props = _p.downcast_ref::

().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::>().unwrap(); - let root_caller: Box Element> = - Box::new(move |scope: &Scope| unsafe { + let root_caller: Box 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 Fn(&'b Scope) -> Element<'b> + 'static>); +struct RootCaller(Rc Fn(&'b ScopeInner) -> Element<'b> + 'static>); diff --git a/packages/core/tests/borrowedstate.rs b/packages/core/tests/borrowedstate.rs index 09f319ac0..f0e3fa544 100644 --- a/packages/core/tests/borrowedstate.rs +++ b/packages/core/tests/borrowedstate.rs @@ -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}!" } } diff --git a/packages/core/tests/vdom_rebuild.rs b/packages/core/tests/vdom_rebuild.rs index 2350774c5..a992cd449 100644 --- a/packages/core/tests/vdom_rebuild.rs +++ b/packages/core/tests/vdom_rebuild.rs @@ -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 }) }; diff --git a/packages/desktop/examples/todomvc.rs b/packages/desktop/examples/todomvc.rs index 5d0ebd5cd..cd78eb523 100644 --- a/packages/desktop/examples/todomvc.rs +++ b/packages/desktop/examples/todomvc.rs @@ -136,7 +136,7 @@ pub struct TodoEntryProps { id: u32, } -pub fn TodoEntry((cx, props): Component) -> Element { +pub fn TodoEntry((cx, props): Scope) -> Element { let todos = use_shared_state::(cx)?; let _todos = todos.read(); diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index b5c1ab78f..1cf369d9f 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -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::>()?; cx.use_hook(