feat: upgrade syntax

This commit is contained in:
Jonathan Kelley committed 2021-11-10 17:09:52 -05:00
1 parent 574d7fdb9e
commit fd93ee89c1
64 files changed
+522 -437

No files matched your search

+1 -1
View File
@@ -5,7 +5,7 @@ fn main() {
dom.rebuild(); dom.rebuild();
} }
const App: FC<()> = |(cx, props)| { const App: FC<()> = |cx, props| {
let id = cx.scope_id(); let id = cx.scope_id();
// cx.submit_task(Box::pin(async move { id })); // cx.submit_task(Box::pin(async move { id }));
+1 -1
View File
@@ -6,7 +6,7 @@ struct SomeContext {
} }
#[allow(unused)] #[allow(unused)]
static Example: FC<()> = |(cx, props)| { static Example: FC<()> = |cx, props| {
todo!() todo!()
// let value = cx.use_context(|c: &SomeContext| c.items.last().unwrap()); // let value = cx.use_context(|c: &SomeContext| c.items.last().unwrap());
+3 -3
View File
@@ -33,7 +33,7 @@ fn html_usage() {
static App2: FC<()> = |(cx, _)| cx.render(rsx!("hello world!")); static App2: FC<()> = |(cx, _)| cx.render(rsx!("hello world!"));
static App: FC<()> = |(cx, props)| { static App: FC<()> = |cx, props| {
let name = cx.use_state(|| 0); let name = cx.use_state(|| 0);
cx.render(rsx!(div { cx.render(rsx!(div {
@@ -99,7 +99,7 @@ impl<'a> Children<'a> {
} }
} }
static Bapp: FC<()> = |(cx, props)| { static Bapp: FC<()> = |cx, props| {
let name = cx.use_state(|| 0); let name = cx.use_state(|| 0);
cx.render(rsx!( cx.render(rsx!(
@@ -114,7 +114,7 @@ static Bapp: FC<()> = |(cx, props)| {
)) ))
}; };
static Match: FC<()> = |(cx, props)| { static Match: FC<()> = |cx, props| {
// //
let b: Box<dyn Fn(NodeFactory) -> VNode> = Box::new(|f| todo!()); let b: Box<dyn Fn(NodeFactory) -> VNode> = Box::new(|f| todo!());
+1 -1
View File
@@ -5,7 +5,7 @@ use dioxus_core::{lazynodes::LazyNodes, prelude::*};
// #[async_std::main] // #[async_std::main]
fn main() { fn main() {
static App: FC<()> = static App: FC<()> =
|(cx, props)| cx.render(Some(LazyNodes::new(move |f| f.text(format_args!("hello"))))); |cx, props| cx.render(Some(LazyNodes::new(move |f| f.text(format_args!("hello")))));
let mut dom = VirtualDom::new(App); let mut dom = VirtualDom::new(App);
+8 -8
View File
@@ -32,7 +32,7 @@ use dioxus::prelude::*;
struct NoKeysProps { struct NoKeysProps {
data: std::collections::HashMap<u32, String>, data: std::collections::HashMap<u32, String>,
} }
static AntipatternNoKeys: FC<NoKeysProps> = |(cx, props)| { static AntipatternNoKeys: FC<NoKeysProps> = |cx, props| {
// WRONG: Make sure to add keys! // WRONG: Make sure to add keys!
rsx!(cx, ul { rsx!(cx, ul {
{props.data.iter().map(|(k, v)| rsx!(li { "List item: {v}" }))} {props.data.iter().map(|(k, v)| rsx!(li { "List item: {v}" }))}
@@ -54,7 +54,7 @@ static AntipatternNoKeys: FC<NoKeysProps> = |(cx, props)| {
/// ///
/// Only Component and Fragment nodes are susceptible to this issue. Dioxus mitigates this with components by providing /// Only Component and Fragment nodes are susceptible to this issue. Dioxus mitigates this with components by providing
/// an API for registering shared state without the ContextProvider pattern. /// an API for registering shared state without the ContextProvider pattern.
static AntipatternNestedFragments: FC<()> = |(cx, props)| { static AntipatternNestedFragments: FC<()> = |cx, props| {
// Try to avoid heavily nesting fragments // Try to avoid heavily nesting fragments
rsx!(cx, rsx!(cx,
Fragment { Fragment {
@@ -82,7 +82,7 @@ static AntipatternNestedFragments: FC<()> = |(cx, props)| {
/// However, calling set_state will *not* update the current version of state in the component. This should be easy to /// However, calling set_state will *not* update the current version of state in the component. This should be easy to
/// recognize from the function signature, but Dioxus will not update the "live" version of state. Calling `set_state` /// recognize from the function signature, but Dioxus will not update the "live" version of state. Calling `set_state`
/// merely places a new value in the queue and schedules the component for a future update. /// merely places a new value in the queue and schedules the component for a future update.
static AntipatternRelyingOnSetState: FC<()> = |(cx, props)| { static AntipatternRelyingOnSetState: FC<()> = |cx, props| {
let (state, set_state) = use_state(cx, || "Hello world").classic(); let (state, set_state) = use_state(cx, || "Hello world").classic();
set_state("New state"); set_state("New state");
// This will return false! `state` will *still* be "Hello world" // This will return false! `state` will *still* be "Hello world"
@@ -99,7 +99,7 @@ static AntipatternRelyingOnSetState: FC<()> = |(cx, props)| {
/// - All components must start with an uppercase character /// - All components must start with an uppercase character
/// ///
/// i.e.: the following component will be rejected when attempted to be used in the rsx! macro /// i.e.: the following component will be rejected when attempted to be used in the rsx! macro
static antipattern_component: FC<()> = |(cx, props)| todo!(); static antipattern_component: FC<()> = |cx, props| todo!();
/// Antipattern: Misusing hooks /// Antipattern: Misusing hooks
/// --------------------------- /// ---------------------------
@@ -120,7 +120,7 @@ static antipattern_component: FC<()> = |(cx, props)| todo!();
struct MisuedHooksProps { struct MisuedHooksProps {
should_render_state: bool, should_render_state: bool,
} }
static AntipatternMisusedHooks: FC<MisuedHooksProps> = |(cx, props)| { static AntipatternMisusedHooks: FC<MisuedHooksProps> = |cx, props| {
if props.should_render_state { if props.should_render_state {
// do not place a hook in the conditional! // do not place a hook in the conditional!
// prefer to move it out of the conditional // prefer to move it out of the conditional
@@ -153,7 +153,7 @@ static AntipatternMisusedHooks: FC<MisuedHooksProps> = |(cx, props)| {
/// } /// }
/// } /// }
/// }) /// })
static _example: FC<()> = |(cx, props)| todo!(); static _example: FC<()> = |cx, props| todo!();
/// Antipattern: publishing components and hooks with all features enabled /// Antipattern: publishing components and hooks with all features enabled
/// ---------------------------------------------------------------------- /// ----------------------------------------------------------------------
@@ -171,9 +171,9 @@ static _example: FC<()> = |(cx, props)| todo!();
/// ///
/// This will only include the `core` dioxus crate which is relatively slim and fast to compile and avoids target-specific /// This will only include the `core` dioxus crate which is relatively slim and fast to compile and avoids target-specific
/// libraries. /// libraries.
static __example: FC<()> = |(cx, props)| todo!(); static __example: FC<()> = |cx, props| todo!();
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
AntipatternNoKeys { data: std::collections::HashMap::new() } AntipatternNoKeys { data: std::collections::HashMap::new() }
AntipatternNestedFragments {} AntipatternNestedFragments {}
+2 -2
View File
@@ -9,7 +9,7 @@
use dioxus::prelude::*; use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
div { div {
Greeting { Greeting {
@@ -25,7 +25,7 @@ struct GreetingProps {
name: &'static str, name: &'static str,
} }
static Greeting: FC<GreetingProps> = |(cx, props)| { static Greeting: FC<GreetingProps> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
div { div {
h1 { "Hello, {props.name}!" } h1 { "Hello, {props.name}!" }
+2 -2
View File
@@ -18,7 +18,7 @@
use dioxus::prelude::*; use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
div { div {
Banner { Banner {
@@ -31,7 +31,7 @@ pub static Example: FC<()> = |(cx, props)| {
}) })
}; };
pub static Banner: FC<()> = |(cx, props)| { pub static Banner: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
div { div {
h1 { "This is a great banner!" } h1 { "This is a great banner!" }
@@ -16,7 +16,7 @@ use dioxus::prelude::*;
pub struct MyProps { pub struct MyProps {
should_show: bool, should_show: bool,
} }
pub static Example0: FC<MyProps> = |(cx, props)| { pub static Example0: FC<MyProps> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
div { div {
{props.should_show.then(|| rsx!{ {props.should_show.then(|| rsx!{
@@ -39,7 +39,7 @@ pub static Example0: FC<MyProps> = |(cx, props)| {
pub struct MyProps1 { pub struct MyProps1 {
should_show: bool, should_show: bool,
} }
pub static Example1: FC<MyProps1> = |(cx, props)| { pub static Example1: FC<MyProps1> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
div { div {
// With matching // With matching
@@ -77,7 +77,7 @@ pub enum Color {
pub struct MyProps2 { pub struct MyProps2 {
color: Color, color: Color,
} }
pub static Example2: FC<MyProps2> = |(cx, props)| { pub static Example2: FC<MyProps2> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
div { div {
{match props.color { {match props.color {
@@ -89,7 +89,7 @@ pub static Example2: FC<MyProps2> = |(cx, props)| {
}) })
}; };
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
let should_show = use_state(cx, || false); let should_show = use_state(cx, || false);
let mut color_index = use_state(cx, || 0); let mut color_index = use_state(cx, || 0);
let color = match *color_index % 2 { let color = match *color_index % 2 {
+3 -3
View File
@@ -1,7 +1,7 @@
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {} fn main() {}
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
div { div {
@@ -10,7 +10,7 @@ pub static Example: FC<()> = |(cx, props)| {
}; };
// A controlled component: // A controlled component:
static ControlledSelect: FC<()> = |(cx, props)| { static ControlledSelect: FC<()> = |cx, props| {
let value = use_state(cx, || String::from("Grapefruit")); let value = use_state(cx, || String::from("Grapefruit"));
cx.render(rsx! { cx.render(rsx! {
select { value: "{value}", onchange: move |evt| value.set(evt.value()), select { value: "{value}", onchange: move |evt| value.set(evt.value()),
@@ -23,7 +23,7 @@ static ControlledSelect: FC<()> = |(cx, props)| {
}; };
// TODO - how do uncontrolled things work? // TODO - how do uncontrolled things work?
static UncontrolledSelect: FC<()> = |(cx, props)| { static UncontrolledSelect: FC<()> = |cx, props| {
let value = use_state(cx, || String::new()); let value = use_state(cx, || String::new());
cx.render(rsx! { cx.render(rsx! {
+1 -1
View File
@@ -11,7 +11,7 @@
use dioxus::prelude::*; use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
div { div {
custom_element { custom_element {
+1 -1
View File
@@ -5,4 +5,4 @@
use dioxus::prelude::*; use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| cx.render(rsx! { Fragment {} }); pub static Example: FC<()> = |cx, props| cx.render(rsx! { Fragment {} });
+6 -6
View File
@@ -23,14 +23,14 @@ fn main() {}
/// This is one way to go about error handling (just toss things away with unwrap). /// This is one way to go about error handling (just toss things away with unwrap).
/// However, if you get it wrong, the whole app will crash. /// However, if you get it wrong, the whole app will crash.
/// This is pretty flimsy. /// This is pretty flimsy.
static App: FC<()> = |(cx, props)| { static App: FC<()> = |cx, props| {
let data = get_data().unwrap(); let data = get_data().unwrap();
cx.render(rsx!( div { "{data}" } )) cx.render(rsx!( div { "{data}" } ))
}; };
/// This is a pretty verbose way of error handling /// This is a pretty verbose way of error handling
/// However, it's still pretty good since we don't panic, just fail to render anything /// However, it's still pretty good since we don't panic, just fail to render anything
static App1: FC<()> = |(cx, props)| { static App1: FC<()> = |cx, props| {
let data = match get_data() { let data = match get_data() {
Some(data) => data, Some(data) => data,
None => return None, None => return None,
@@ -46,7 +46,7 @@ static App1: FC<()> = |(cx, props)| {
/// a user is logged in. /// a user is logged in.
/// ///
/// Dioxus will throw an error in the console if the None-path is ever taken. /// Dioxus will throw an error in the console if the None-path is ever taken.
static App2: FC<()> = |(cx, props)| { static App2: FC<()> = |cx, props| {
let data = get_data()?; let data = get_data()?;
cx.render(rsx!( div { "{data}" } )) cx.render(rsx!( div { "{data}" } ))
}; };
@@ -54,14 +54,14 @@ static App2: FC<()> = |(cx, props)| {
/// This is top-tier error handling since it displays a failure state. /// This is top-tier error handling since it displays a failure state.
/// ///
/// However, the error is lacking in context. /// However, the error is lacking in context.
static App3: FC<()> = |(cx, props)| match get_data() { static App3: FC<()> = |cx, props| match get_data() {
Some(data) => cx.render(rsx!( div { "{data}" } )), Some(data) => cx.render(rsx!( div { "{data}" } )),
None => cx.render(rsx!( div { "Failed to load data :(" } )), None => cx.render(rsx!( div { "Failed to load data :(" } )),
}; };
/// For errors that return results, it's possible to short-circuit the match-based error handling with `.ok()` which converts /// For errors that return results, it's possible to short-circuit the match-based error handling with `.ok()` which converts
/// a Result<T, V> into an Option<T> and lets you abort rendering by early-returning `None` /// a Result<T, V> into an Option<T> and lets you abort rendering by early-returning `None`
static App4: FC<()> = |(cx, props)| { static App4: FC<()> = |cx, props| {
let data = get_data_err().ok()?; let data = get_data_err().ok()?;
cx.render(rsx!( div { "{data}" } )) cx.render(rsx!( div { "{data}" } ))
}; };
@@ -69,7 +69,7 @@ static App4: FC<()> = |(cx, props)| {
/// This is great error handling since it displays a failure state... with context! /// This is great error handling since it displays a failure state... with context!
/// ///
/// Hopefully you'll never need to display a screen like this. It's rather bad taste /// Hopefully you'll never need to display a screen like this. It's rather bad taste
static App5: FC<()> = |(cx, props)| match get_data_err() { static App5: FC<()> = |cx, props| match get_data_err() {
Ok(data) => cx.render(rsx!( div { "{data}" } )), Ok(data) => cx.render(rsx!( div { "{data}" } )),
Err(c) => cx.render(rsx!( div { "Failed to load data: {c}" } )), Err(c) => cx.render(rsx!( div { "Failed to load data: {c}" } )),
}; };
+4 -4
View File
@@ -11,7 +11,7 @@
use dioxus::prelude::*; use dioxus::prelude::*;
// Returning multiple elements with rsx! or html! // Returning multiple elements with rsx! or html!
static App1: FC<()> = |(cx, props)| { static App1: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
h1 { } h1 { }
h2 { } h2 { }
@@ -20,7 +20,7 @@ static App1: FC<()> = |(cx, props)| {
}; };
// Using the Fragment component // Using the Fragment component
static App2: FC<()> = |(cx, props)| { static App2: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
Fragment { Fragment {
div {} div {}
@@ -31,7 +31,7 @@ static App2: FC<()> = |(cx, props)| {
}; };
// Using the `fragment` method on the NodeFactory // Using the `fragment` method on the NodeFactory
static App3: FC<()> = |(cx, props)| { static App3: FC<()> = |cx, props| {
cx.render(LazyNodes::new(move |fac| { cx.render(LazyNodes::new(move |fac| {
fac.fragment_from_iter([ fac.fragment_from_iter([
fac.text(format_args!("A")), fac.text(format_args!("A")),
@@ -42,7 +42,7 @@ static App3: FC<()> = |(cx, props)| {
})) }))
}; };
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
App1 {} App1 {}
App2 {} App2 {}
+1 -1
View File
@@ -19,7 +19,7 @@ h1 {color: blue;}
p {color: red;} p {color: red;}
"#; "#;
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
head { style { "{STYLE}" } } head { style { "{STYLE}" } }
body { body {
+2 -2
View File
@@ -10,7 +10,7 @@
use dioxus::prelude::*; use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
head { head {
style: { background_color: "powderblue" } style: { background_color: "powderblue" }
@@ -29,7 +29,7 @@ pub static Example: FC<()> = |(cx, props)| {
// .... technically the rsx! macro is slightly broken at the moment and allows styles not wrapped in style {} // .... technically the rsx! macro is slightly broken at the moment and allows styles not wrapped in style {}
// I haven't noticed any name collisions yet, and am tentatively leaving this behavior in.. // I haven't noticed any name collisions yet, and am tentatively leaving this behavior in..
// Don't rely on it. // Don't rely on it.
static Example2: FC<()> = |(cx, props)| { static Example2: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
div { color: "red" div { color: "red"
"hello world!" "hello world!"
+1 -1
View File
@@ -12,7 +12,7 @@
use dioxus::prelude::*; use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
let example_data = use_state(cx, || 0); let example_data = use_state(cx, || 0);
let v = (0..10).map(|f| { let v = (0..10).map(|f| {
+4 -4
View File
@@ -7,7 +7,7 @@
use dioxus::prelude::*; use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
ButtonList {} ButtonList {}
NonUpdatingEvents {} NonUpdatingEvents {}
@@ -16,7 +16,7 @@ pub static Example: FC<()> = |(cx, props)| {
}; };
/// We can use `set_name` in multiple closures; the closures automatically *copy* the reference to set_name. /// We can use `set_name` in multiple closures; the closures automatically *copy* the reference to set_name.
static ButtonList: FC<()> = |(cx, props)| { static ButtonList: FC<()> = |cx, props| {
let name = use_state(cx, || "...?"); let name = use_state(cx, || "...?");
let names = ["jack", "jill", "john", "jane"] let names = ["jack", "jill", "john", "jane"]
@@ -33,7 +33,7 @@ static ButtonList: FC<()> = |(cx, props)| {
/// This shows how listeners may be without a visible change in the display. /// This shows how listeners may be without a visible change in the display.
/// Check the console. /// Check the console.
static NonUpdatingEvents: FC<()> = |(cx, props)| { static NonUpdatingEvents: FC<()> = |cx, props| {
rsx!(cx, div { rsx!(cx, div {
button { button {
onclick: move |_| log::info!("Did not cause any updates!") onclick: move |_| log::info!("Did not cause any updates!")
@@ -42,7 +42,7 @@ static NonUpdatingEvents: FC<()> = |(cx, props)| {
}) })
}; };
static DisablePropagation: FC<()> = |(cx, props)| { static DisablePropagation: FC<()> = |cx, props| {
rsx!(cx, rsx!(cx,
div { div {
onclick: move |_| log::info!("event propagated to the div!") onclick: move |_| log::info!("event propagated to the div!")
+3 -3
View File
@@ -21,7 +21,7 @@ use dioxus::prelude::*;
// By default, components with no props are always memoized. // By default, components with no props are always memoized.
// A props of () is considered empty. // A props of () is considered empty.
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
div { "100% memoized!" } div { "100% memoized!" }
}) })
@@ -35,7 +35,7 @@ pub struct MyProps1 {
name: String, name: String,
} }
pub static Example1: FC<MyProps1> = |(cx, props)| { pub static Example1: FC<MyProps1> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
div { "100% memoized! {props.name}" } div { "100% memoized! {props.name}" }
}) })
@@ -49,7 +49,7 @@ pub struct MyProps2 {
name: std::rc::Rc<str>, name: std::rc::Rc<str>,
} }
pub static Example2: FC<MyProps2> = |(cx, props)| { pub static Example2: FC<MyProps2> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
div { "100% memoized! {props.name}" } div { "100% memoized! {props.name}" }
}) })
+1 -1
View File
@@ -1,7 +1,7 @@
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {} fn main() {}
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
let p = 10; let p = 10;
cx.render(rsx! { cx.render(rsx! {
+1 -1
View File
@@ -1,7 +1,7 @@
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {} fn main() {}
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
div { div {
+1 -1
View File
@@ -9,7 +9,7 @@
use dioxus::prelude::*; use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
let props = MyProps { let props = MyProps {
count: 0, count: 0,
live: true, live: true,
+1 -1
View File
@@ -1,7 +1,7 @@
use dioxus::prelude::*; use dioxus::prelude::*;
fn main() {} fn main() {}
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
div { div {
+1 -1
View File
@@ -14,7 +14,7 @@ struct DogApi {
} }
const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random"; const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random";
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
let doggo = use_suspense( let doggo = use_suspense(
cx, cx,
|| surf::get(ENDPOINT).recv_json::<DogApi>(), || surf::get(ENDPOINT).recv_json::<DogApi>(),
+1 -1
View File
@@ -24,7 +24,7 @@
use dioxus::prelude::*; use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
let count = use_state(cx, || 0); let count = use_state(cx, || 0);
let mut direction = use_state(cx, || 1); let mut direction = use_state(cx, || 1);
+1 -1
View File
@@ -1,6 +1,6 @@
use dioxus::prelude::*; use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
div { div {
+2 -2
View File
@@ -1,7 +1,7 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use dioxus::ssr; use dioxus::ssr;
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
let as_string = use_state(cx, || { let as_string = use_state(cx, || {
// Currently, SSR is only supported for whole VirtualDOMs // Currently, SSR is only supported for whole VirtualDOMs
// This is an easy/low hanging fruit to improve upon // This is an easy/low hanging fruit to improve upon
@@ -15,7 +15,7 @@ pub static Example: FC<()> = |(cx, props)| {
}) })
}; };
static SomeApp: FC<()> = |(cx, props)| { static SomeApp: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
div { style: {background_color: "blue"} div { style: {background_color: "blue"}
h1 {"Some amazing app or component"} h1 {"Some amazing app or component"}
+1 -1
View File
@@ -27,7 +27,7 @@ fn main() {
use dioxus::prelude::*; use dioxus::prelude::*;
static App: FC<()> = |(cx, props)| { static App: FC<()> = |cx, props| {
let p1 = use_state(cx, || 0); let p1 = use_state(cx, || 0);
let p2 = use_state(cx, || 0); let p2 = use_state(cx, || 0);
+1 -1
View File
@@ -8,7 +8,7 @@ fn main() {
dioxus_desktop::launch(App, |c| c); dioxus_desktop::launch(App, |c| c);
} }
static App: FC<()> = |(cx, props)| { static App: FC<()> = |cx, props| {
cx.render(rsx!( cx.render(rsx!(
div { div {
"hello world!" "hello world!"
+1 -1
View File
@@ -18,7 +18,7 @@ fn main() {
}); });
} }
static App: FC<()> = |(cx, props)| { static App: FC<()> = |cx, props| {
let file_manager = use_ref(cx, || Files::new()); let file_manager = use_ref(cx, || Files::new());
let files = file_manager.read(); let files = file_manager.read();
+1 -1
View File
@@ -19,7 +19,7 @@ fn main() {
dioxus::desktop::launch(App, |c| c.with_prerendered(content)); dioxus::desktop::launch(App, |c| c.with_prerendered(content));
} }
static App: FC<()> = |(cx, props)| { static App: FC<()> = |cx, props| {
let mut val = use_state(cx, || 0); let mut val = use_state(cx, || 0);
cx.render(rsx! { cx.render(rsx! {
+1 -1
View File
@@ -31,7 +31,7 @@ fn main() {
}); });
} }
static App: FC<()> = |(cx, props)| { static App: FC<()> = |cx, props| {
let state = use_ref(cx, || Calculator::new()); let state = use_ref(cx, || Calculator::new());
let clear_display = state.read().display_value.eq("0"); let clear_display = state.read().display_value.eq("0");
+1 -1
View File
@@ -7,7 +7,7 @@ fn main() {
dioxus::desktop::launch(App, |c| c); dioxus::desktop::launch(App, |c| c);
} }
static App: FC<()> = |(cx, props)| { static App: FC<()> = |cx, props| {
let mut count = use_state(cx, || 0); let mut count = use_state(cx, || 0);
cx.render(rsx! { cx.render(rsx! {
+1 -1
View File
@@ -23,7 +23,7 @@ pub enum Route {
NotFound, NotFound,
} }
static App: FC<()> = |(cx, props)| { static App: FC<()> = |cx, props| {
let route = use_router(cx, Route::parse)?; let route = use_router(cx, Route::parse)?;
cx.render(rsx! { cx.render(rsx! {
+1 -1
View File
@@ -49,7 +49,7 @@ const NONE_ELEMENT: Option<()> = None;
use baller::Baller; use baller::Baller;
use dioxus::prelude::*; use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| { pub static Example: FC<()> = |cx, props| {
let formatting = "formatting!"; let formatting = "formatting!";
let formatting_tuple = ("a", "b"); let formatting_tuple = ("a", "b");
let lazy_fmt = format_args!("lazily formatted text"); let lazy_fmt = format_args!("lazily formatted text");
+1 -1
View File
@@ -9,7 +9,7 @@ fn main() {
println!("{}", ssr::render_vdom(&vdom, |c| c)); println!("{}", ssr::render_vdom(&vdom, |c| c));
} }
static App: FC<()> = |(cx, props)| { static App: FC<()> = |cx, props| {
cx.render(rsx!( cx.render(rsx!(
div { div {
h1 { "Title" } h1 { "Title" }
+1 -1
View File
@@ -12,7 +12,7 @@ fn main() {
) )
} }
pub static App: FC<()> = |(cx, props)| { pub static App: FC<()> = |cx, props| {
cx.render(rsx!( cx.render(rsx!(
div { div {
class: "overflow-hidden" class: "overflow-hidden"
+1 -1
View File
@@ -42,7 +42,7 @@ fn main() {}
// initial_name: String, // initial_name: String,
// } // }
// static Example: FC<ExampleProps> = |(cx, props)| { // static Example: FC<ExampleProps> = |cx, props| {
// let dispaly_name = use_state(cx, move || props.initial_name.clone()); // let dispaly_name = use_state(cx, move || props.initial_name.clone());
// cx.render(rsx! { // cx.render(rsx! {
+6 -6
View File
@@ -24,7 +24,7 @@ fn main() {
.unwrap(); .unwrap();
} }
pub static App: FC<()> = |(cx, props)| { pub static App: FC<()> = |cx, props| {
cx.render(rsx!( cx.render(rsx!(
div { class: "overflow-hidden" div { class: "overflow-hidden"
link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" } link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" }
@@ -39,7 +39,7 @@ pub static App: FC<()> = |(cx, props)| {
)) ))
}; };
pub static Header: FC<()> = |(cx, props)| { pub static Header: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
div { div {
header { class: "text-gray-400 bg-gray-900 body-font" header { class: "text-gray-400 bg-gray-900 body-font"
@@ -65,7 +65,7 @@ pub static Header: FC<()> = |(cx, props)| {
}) })
}; };
pub static Hero: FC<()> = |(cx, props)| { pub static Hero: FC<()> = |cx, props| {
// //
cx.render(rsx! { cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font" section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -103,7 +103,7 @@ pub static Hero: FC<()> = |(cx, props)| {
} }
}) })
}; };
pub static Entry: FC<()> = |(cx, props)| { pub static Entry: FC<()> = |cx, props| {
// //
cx.render(rsx! { cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font" section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -116,7 +116,7 @@ pub static Entry: FC<()> = |(cx, props)| {
}) })
}; };
pub static StacksIcon: FC<()> = |(cx, props)| { pub static StacksIcon: FC<()> = |cx, props| {
cx.render(rsx!( cx.render(rsx!(
svg { svg {
xmlns: "http://www.w3.org/2000/svg" xmlns: "http://www.w3.org/2000/svg"
@@ -131,7 +131,7 @@ pub static StacksIcon: FC<()> = |(cx, props)| {
} }
)) ))
}; };
pub static RightArrowIcon: FC<()> = |(cx, props)| { pub static RightArrowIcon: FC<()> = |cx, props| {
cx.render(rsx!( cx.render(rsx!(
svg { svg {
fill: "none" fill: "none"
+6 -6
View File
@@ -14,7 +14,7 @@ fn main() {
const STYLE: &str = "body {overflow:hidden;}"; const STYLE: &str = "body {overflow:hidden;}";
pub static App: FC<()> = |(cx, props)| { pub static App: FC<()> = |cx, props| {
cx.render(rsx!( cx.render(rsx!(
div { class: "overflow-hidden" div { class: "overflow-hidden"
style { "{STYLE}" } style { "{STYLE}" }
@@ -30,7 +30,7 @@ pub static App: FC<()> = |(cx, props)| {
)) ))
}; };
pub static Header: FC<()> = |(cx, props)| { pub static Header: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
div { div {
header { class: "text-gray-400 bg-gray-900 body-font" header { class: "text-gray-400 bg-gray-900 body-font"
@@ -56,7 +56,7 @@ pub static Header: FC<()> = |(cx, props)| {
}) })
}; };
pub static Hero: FC<()> = |(cx, props)| { pub static Hero: FC<()> = |cx, props| {
// //
cx.render(rsx! { cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font" section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -94,7 +94,7 @@ pub static Hero: FC<()> = |(cx, props)| {
} }
}) })
}; };
pub static Entry: FC<()> = |(cx, props)| { pub static Entry: FC<()> = |cx, props| {
// //
cx.render(rsx! { cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font" section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -107,7 +107,7 @@ pub static Entry: FC<()> = |(cx, props)| {
}) })
}; };
pub static StacksIcon: FC<()> = |(cx, props)| { pub static StacksIcon: FC<()> = |cx, props| {
cx.render(rsx!( cx.render(rsx!(
svg { svg {
// xmlns: "http://www.w3.org/2000/svg" // xmlns: "http://www.w3.org/2000/svg"
@@ -122,7 +122,7 @@ pub static StacksIcon: FC<()> = |(cx, props)| {
} }
)) ))
}; };
pub static RightArrowIcon: FC<()> = |(cx, props)| { pub static RightArrowIcon: FC<()> = |cx, props| {
cx.render(rsx!( cx.render(rsx!(
svg { svg {
fill: "none" fill: "none"
+23
View File
@@ -0,0 +1,23 @@
//! Example: README.md showcase
//!
//! The example from the README.md.
use dioxus::prelude::*;
fn main() {
dioxus::desktop::launch(App, |c| c);
}
static App: FC<()> = |cx, props| {
let mut count = use_state(cx, || 0);
cx.push_task(async {
panic!("polled future");
//
});
cx.render(rsx! {
div {
h1 { "High-Five counter: {count}" }
}
})
};
+2 -2
View File
@@ -22,7 +22,7 @@ pub struct TodoItem {
} }
const STYLE: &str = include_str!("./assets/todomvc.css"); const STYLE: &str = include_str!("./assets/todomvc.css");
const App: FC<()> = |(cx, props)| { const App: FC<()> = |cx, props| {
let mut draft = use_state(cx, || "".to_string()); let mut draft = use_state(cx, || "".to_string());
let mut todos = use_state(cx, || HashMap::<u32, Rc<TodoItem>>::new()); let mut todos = use_state(cx, || HashMap::<u32, Rc<TodoItem>>::new());
let mut filter = use_state(cx, || FilterState::All); let mut filter = use_state(cx, || FilterState::All);
@@ -85,7 +85,7 @@ pub struct TodoEntryProps {
todo: Rc<TodoItem>, todo: Rc<TodoItem>,
} }
pub fn TodoEntry((cx, props): Scope<TodoEntryProps>) -> Element { pub fn TodoEntry(cx: Context, props: &TodoEntryProps) -> Element {
let mut is_editing = use_state(cx, || false); let mut is_editing = use_state(cx, || false);
let mut contents = use_state(cx, || String::from("")); let mut contents = use_state(cx, || String::from(""));
let todo = &props.todo; let todo = &props.todo;
+2 -2
View File
@@ -12,7 +12,7 @@ fn main() {
const ENDPOINT: &str = "https://api.openweathermap.org/data/2.5/weather"; const ENDPOINT: &str = "https://api.openweathermap.org/data/2.5/weather";
static App: FC<()> = |(cx, props)| { static App: FC<()> = |cx, props| {
// //
let body = use_suspense( let body = use_suspense(
cx, cx,
@@ -40,7 +40,7 @@ static App: FC<()> = |(cx, props)| {
#[derive(PartialEq, Props)] #[derive(PartialEq, Props)]
struct WeatherProps {} struct WeatherProps {}
static WeatherDisplay: FC<WeatherProps> = |(cx, props)| { static WeatherDisplay: FC<WeatherProps> = |cx, props| {
// //
cx.render(rsx!( cx.render(rsx!(
div { class: "flex items-center justify-center flex-col" div { class: "flex items-center justify-center flex-col"
+1 -1
View File
@@ -24,7 +24,7 @@ fn main() {
dioxus_web::launch(App, |c| c) dioxus_web::launch(App, |c| c)
} }
static App: FC<()> = |(cx, props)| { static App: FC<()> = |cx, props| {
let mut state = use_state(cx, || 0); let mut state = use_state(cx, || 0);
cx.render(rsx! { cx.render(rsx! {
div { div {
+2 -2
View File
@@ -23,7 +23,7 @@ fn main() {
// dioxus::web::launch(App, |c| c); // dioxus::web::launch(App, |c| c);
} }
static App: FC<()> = |(cx, props)| { static App: FC<()> = |cx, props| {
dbg!("rednering parent"); dbg!("rednering parent");
cx.render(rsx! { cx.render(rsx! {
div { div {
@@ -40,7 +40,7 @@ static App: FC<()> = |(cx, props)| {
}) })
}; };
static But: FC<()> = |(cx, props)| { static But: FC<()> = |cx, props| {
let mut count = use_state(cx, || 0); let mut count = use_state(cx, || 0);
// let d = Dropper { name: "asd" }; // let d = Dropper { name: "asd" };
+6 -6
View File
@@ -24,7 +24,7 @@ fn main() {
dioxus_web::launch(App, |c| c) dioxus_web::launch(App, |c| c)
} }
pub static App: FC<()> = |(cx, props)| { pub static App: FC<()> = |cx, props| {
cx.render(rsx!( cx.render(rsx!(
div { class: "overflow-hidden" div { class: "overflow-hidden"
link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" } link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" }
@@ -39,7 +39,7 @@ pub static App: FC<()> = |(cx, props)| {
)) ))
}; };
pub static Header: FC<()> = |(cx, props)| { pub static Header: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
div { div {
header { class: "text-gray-400 bg-gray-900 body-font" header { class: "text-gray-400 bg-gray-900 body-font"
@@ -65,7 +65,7 @@ pub static Header: FC<()> = |(cx, props)| {
}) })
}; };
pub static Hero: FC<()> = |(cx, props)| { pub static Hero: FC<()> = |cx, props| {
// //
cx.render(rsx! { cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font" section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -103,7 +103,7 @@ pub static Hero: FC<()> = |(cx, props)| {
} }
}) })
}; };
pub static Entry: FC<()> = |(cx, props)| { pub static Entry: FC<()> = |cx, props| {
// //
cx.render(rsx! { cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font" section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -116,7 +116,7 @@ pub static Entry: FC<()> = |(cx, props)| {
}) })
}; };
pub static StacksIcon: FC<()> = |(cx, props)| { pub static StacksIcon: FC<()> = |cx, props| {
cx.render(rsx!( cx.render(rsx!(
svg { svg {
// xmlns: "http://www.w3.org/2000/svg" // xmlns: "http://www.w3.org/2000/svg"
@@ -131,7 +131,7 @@ pub static StacksIcon: FC<()> = |(cx, props)| {
} }
)) ))
}; };
pub static RightArrowIcon: FC<()> = |(cx, props)| { pub static RightArrowIcon: FC<()> = |cx, props| {
cx.render(rsx!( cx.render(rsx!(
svg { svg {
fill: "none" fill: "none"
+1 -1
View File
@@ -19,7 +19,7 @@ fn main() {
dioxus::web::launch(App, |c| c); dioxus::web::launch(App, |c| c);
} }
static App: FC<()> = |(cx, props)| { static App: FC<()> = |cx, props| {
let mut rng = SmallRng::from_entropy(); let mut rng = SmallRng::from_entropy();
let rows = (0..1_000).map(|f| { let rows = (0..1_000).map(|f| {
let label = Label::new(&mut rng); let label = Label::new(&mut rng);
+1 -1
View File
@@ -16,7 +16,7 @@ fn main() {
dioxus::web::launch(App, |c| c); dioxus::web::launch(App, |c| c);
} }
static App: FC<()> = |(cx, props)| { static App: FC<()> = |cx, props| {
let mut count = use_state(cx, || 0); let mut count = use_state(cx, || 0);
cx.render(rsx! { cx.render(rsx! {
+1 -1
View File
@@ -173,7 +173,7 @@ impl ToTokens for Component {
if !self.children.is_empty() { if !self.children.is_empty() {
let childs = &self.children; let childs = &self.children;
toks.append_all(quote! { toks.append_all(quote! {
.children(ScopeChildren::new(__cx.fragment_from_iter([ #( #childs ),* ]))) .children(__cx.create_children([ #( #childs ),* ]))
}); });
} }
+22 -24
View File
@@ -321,14 +321,15 @@ impl<'bump> DiffState<'bump> {
} }
MountType::Replace { old } => { MountType::Replace { old } => {
// todo: a bug here where we remove a node that is alread being replaced
if let Some(old_id) = old.try_mounted_id() { if let Some(old_id) = old.try_mounted_id() {
self.mutations.replace_with(old_id, nodes_created as u32); self.mutations.replace_with(old_id, nodes_created as u32);
self.remove_nodes(Some(old), true); self.remove_nodes(Some(old), false);
} else { } else {
if let Some(id) = self.find_first_element_id(old) { if let Some(id) = self.find_first_element_id(old) {
self.mutations.replace_with(id, nodes_created as u32); self.mutations.replace_with(id, nodes_created as u32);
} }
self.remove_nodes(Some(old), true); self.remove_nodes(Some(old), false);
} }
} }
@@ -487,7 +488,11 @@ impl<'bump> DiffState<'bump> {
} }
fn create_linked_node(&mut self, link: &'bump NodeLink) { fn create_linked_node(&mut self, link: &'bump NodeLink) {
todo!() if let Some(cur_scope) = self.stack.current_scope() {
link.scope_id.set(Some(cur_scope));
let node: &'bump VNode<'static> = unsafe { &*link.node };
self.create_node(unsafe { std::mem::transmute(node) });
}
} }
// ================================= // =================================
@@ -532,7 +537,6 @@ impl<'bump> DiffState<'bump> {
fn diff_element_nodes( fn diff_element_nodes(
&mut self, &mut self,
old: &'bump VElement<'bump>, old: &'bump VElement<'bump>,
new: &'bump VElement<'bump>, new: &'bump VElement<'bump>,
old_node: &'bump VNode<'bump>, old_node: &'bump VNode<'bump>,
@@ -629,10 +633,8 @@ impl<'bump> DiffState<'bump> {
fn diff_component_nodes( fn diff_component_nodes(
&mut self, &mut self,
old_node: &'bump VNode<'bump>, old_node: &'bump VNode<'bump>,
new_node: &'bump VNode<'bump>, new_node: &'bump VNode<'bump>,
old: &'bump VComponent<'bump>, old: &'bump VComponent<'bump>,
new: &'bump VComponent<'bump>, new: &'bump VComponent<'bump>,
) { ) {
@@ -648,21 +650,20 @@ impl<'bump> DiffState<'bump> {
new.associated_scope.set(Some(scope_addr)); new.associated_scope.set(Some(scope_addr));
// make sure the component's caller function is up to date // make sure the component's caller function is up to date
let scope = self.scopes.get_scope(&scope_addr).unwrap(); let scope = unsafe { self.scopes.get_scope_mut(&scope_addr).unwrap() };
let mut items = scope.items.borrow_mut(); scope.caller = unsafe { std::mem::transmute(new.caller) };
// React doesn't automatically memoize, but we do. // React doesn't automatically memoize, but we do.
let props_are_the_same = todo!("reworking component memoization"); let props_are_the_same = old.comparator.unwrap();
// let props_are_the_same = todo!("reworking component memoization");
// let props_are_the_same = old.comparator.unwrap();
// if self.cfg.force_diff || !props_are_the_same(new) { if self.force_diff || !props_are_the_same(new) {
// let succeeded = scope.run_scope(self); if self.scopes.run_scope(&scope_addr) {
self.diff_node(
// if succeeded { self.scopes.wip_head(&scope_addr),
// self.diff_node(scope.frames.wip_head(), scope.frames.fin_head()); self.scopes.fin_head(&scope_addr),
// } );
// } }
}
self.stack.scope_stack.pop(); self.stack.scope_stack.pop();
} else { } else {
@@ -1198,7 +1199,6 @@ impl<'bump> DiffState<'bump> {
/// remove can happen whenever /// remove can happen whenever
fn remove_nodes( fn remove_nodes(
&mut self, &mut self,
nodes: impl IntoIterator<Item = &'bump VNode<'bump>>, nodes: impl IntoIterator<Item = &'bump VNode<'bump>>,
gen_muts: bool, gen_muts: bool,
) { ) {
@@ -1244,18 +1244,16 @@ impl<'bump> DiffState<'bump> {
} }
VNode::Linked(l) => { VNode::Linked(l) => {
todo!() let node = unsafe { std::mem::transmute(&*l.node) };
self.remove_nodes(Some(node), gen_muts);
} }
VNode::Component(c) => { VNode::Component(c) => {
let scope_id = c.associated_scope.get().unwrap(); let scope_id = c.associated_scope.get().unwrap();
// let scope = self.scopes.get_scope(&scope_id).unwrap();
let root = self.scopes.root_node(&scope_id); let root = self.scopes.root_node(&scope_id);
self.remove_nodes(Some(root), gen_muts); self.remove_nodes(Some(root), gen_muts);
log::debug!("Destroying scope {:?}", scope_id); log::debug!("Destroying scope {:?}", scope_id);
let mut s = self.scopes.try_remove(&scope_id).unwrap(); self.scopes.try_remove(&scope_id).unwrap();
s.hooks.clear_hooks();
} }
} }
} }
+1 -1
View File
@@ -245,7 +245,7 @@ fn it_drops() {
simple_logger::init().unwrap(); simple_logger::init().unwrap();
let factory = NodeFactory { bump: &bump }; // let factory = NodeFactory { scope: &bump };
struct DropInner { struct DropInner {
id: i32, id: i32,
+16 -5
View File
@@ -3,21 +3,32 @@
//! This module contains an internal API to generate these instructions. //! This module contains an internal API to generate these instructions.
use crate::innerlude::*; use crate::innerlude::*;
use std::any::Any; use std::{any::Any, fmt::Debug};
#[derive(Debug)]
pub struct Mutations<'a> { pub struct Mutations<'a> {
pub edits: Vec<DomEdit<'a>>, pub edits: Vec<DomEdit<'a>>,
pub noderefs: Vec<NodeRefMutation<'a>>, pub noderefs: Vec<NodeRefMutation<'a>>,
pub effects: Vec<&'a dyn FnMut()>,
}
impl Debug for Mutations<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Mutations")
.field("edits", &self.edits)
.field("noderefs", &self.noderefs)
// .field("effects", &self.effects)
.finish()
}
} }
use DomEdit::*; use DomEdit::*;
impl<'a> Mutations<'a> { impl<'a> Mutations<'a> {
pub(crate) fn new() -> Self { pub(crate) fn new() -> Self {
let edits = Vec::new(); Self {
let noderefs = Vec::new(); edits: Vec::new(),
Self { edits, noderefs } noderefs: Vec::new(),
effects: Vec::new(),
}
} }
// Navigation // Navigation
+114 -32
View File
@@ -181,7 +181,7 @@ impl<'src> VNode<'src> {
pub(crate) fn children(&self) -> &[VNode<'src>] { pub(crate) fn children(&self) -> &[VNode<'src>] {
match &self { match &self {
VNode::Fragment(f) => f.children, VNode::Fragment(f) => f.children,
VNode::Component(c) => todo!("children are not accessible through this"), VNode::Component(_c) => todo!("children are not accessible through this"),
_ => &[], _ => &[],
} }
} }
@@ -199,9 +199,9 @@ impl<'src> VNode<'src> {
key: f.key, key: f.key,
}), }),
VNode::Linked(c) => VNode::Linked(NodeLink { VNode::Linked(c) => VNode::Linked(NodeLink {
gen_id: c.gen_id, scope_id: c.scope_id.clone(),
scope_id: c.scope_id, link_idx: c.link_idx.clone(),
link_idx: c.link_idx, node: c.node.clone(),
}), }),
} }
} }
@@ -224,11 +224,7 @@ impl Debug for VNode<'_> {
} }
VNode::Suspended { .. } => write!(s, "VNode::VSuspended"), VNode::Suspended { .. } => write!(s, "VNode::VSuspended"),
VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc), VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
VNode::Linked(c) => write!( VNode::Linked(c) => write!(s, "VNode::VCached {{ scope_id: {:?} }}", c.scope_id.get()),
s,
"VNode::VCached {{ gen_id: {}, scope_id: {:?} }}",
c.gen_id, c.scope_id
),
} }
} }
} }
@@ -419,9 +415,25 @@ pub struct VSuspended<'a> {
/// an `rsx!` call. /// an `rsx!` call.
#[derive(Debug)] #[derive(Debug)]
pub struct NodeLink { pub struct NodeLink {
pub(crate) link_idx: usize, pub(crate) link_idx: Cell<usize>,
pub(crate) gen_id: u32, pub(crate) scope_id: Cell<Option<ScopeId>>,
pub(crate) scope_id: ScopeId, pub(crate) node: *const VNode<'static>,
}
impl PartialEq for NodeLink {
fn eq(&self, other: &Self) -> bool {
self.node == other.node
}
}
impl NodeLink {
// we don't want to let users clone NodeLinks
pub(crate) fn clone_inner(&self) -> Self {
Self {
link_idx: self.link_idx.clone(),
scope_id: self.scope_id.clone(),
node: self.node.clone(),
}
}
} }
/// This struct provides an ergonomic API to quickly build VNodes. /// This struct provides an ergonomic API to quickly build VNodes.
@@ -440,7 +452,7 @@ impl<'a> NodeFactory<'a> {
#[inline] #[inline]
pub fn bump(&self) -> &'a bumpalo::Bump { pub fn bump(&self) -> &'a bumpalo::Bump {
self.bump &self.bump
} }
/// Directly pass in text blocks without the need to use the format_args macro. /// Directly pass in text blocks without the need to use the format_args macro.
@@ -460,7 +472,7 @@ impl<'a> NodeFactory<'a> {
Some(static_str) => (static_str, true), Some(static_str) => (static_str, true),
None => { None => {
use bumpalo::core_alloc::fmt::Write; use bumpalo::core_alloc::fmt::Write;
let mut str_buf = bumpalo::collections::String::new_in(self.bump()); let mut str_buf = bumpalo::collections::String::new_in(self.bump);
str_buf.write_fmt(args).unwrap(); str_buf.write_fmt(args).unwrap();
(str_buf.into_bump_str(), false) (str_buf.into_bump_str(), false)
} }
@@ -516,18 +528,18 @@ impl<'a> NodeFactory<'a> {
A: 'a + AsRef<[Attribute<'a>]>, A: 'a + AsRef<[Attribute<'a>]>,
V: 'a + AsRef<[VNode<'a>]>, V: 'a + AsRef<[VNode<'a>]>,
{ {
let listeners: &'a L = self.bump().alloc(listeners); let listeners: &'a L = self.bump.alloc(listeners);
let listeners = listeners.as_ref(); let listeners = listeners.as_ref();
let attributes: &'a A = self.bump().alloc(attributes); let attributes: &'a A = self.bump.alloc(attributes);
let attributes = attributes.as_ref(); let attributes = attributes.as_ref();
let children: &'a V = self.bump().alloc(children); let children: &'a V = self.bump.alloc(children);
let children = children.as_ref(); let children = children.as_ref();
let key = key.map(|f| self.raw_text(f).0); let key = key.map(|f| self.raw_text(f).0);
VNode::Element(self.bump().alloc(VElement { VNode::Element(self.bump.alloc(VElement {
tag_name, tag_name,
key, key,
namespace, namespace,
@@ -565,7 +577,7 @@ impl<'a> NodeFactory<'a> {
where where
P: Properties + 'a, P: Properties + 'a,
{ {
let bump = self.bump(); let bump = self.bump;
let props = bump.alloc(props); let props = bump.alloc(props);
let bump_props = props as *mut P as *mut (); let bump_props = props as *mut P as *mut ();
let user_fc = component as *const (); let user_fc = component as *const ();
@@ -598,7 +610,6 @@ impl<'a> NodeFactory<'a> {
let drop_props: &mut dyn FnMut() = bump.alloc_with(|| { let drop_props: &mut dyn FnMut() = bump.alloc_with(|| {
move || unsafe { move || unsafe {
log::debug!("dropping props!");
if !has_dropped { if !has_dropped {
let real_other = bump_props as *mut _ as *mut P; let real_other = bump_props as *mut _ as *mut P;
let b = BumpBox::from_raw(real_other); let b = BumpBox::from_raw(real_other);
@@ -620,7 +631,6 @@ impl<'a> NodeFactory<'a> {
let caller: &'a mut dyn Fn(&'a Scope) -> Element = let caller: &'a mut dyn Fn(&'a Scope) -> Element =
bump.alloc(move |scope: &Scope| -> Element { bump.alloc(move |scope: &Scope| -> Element {
log::debug!("calling component renderr {:?}", scope.our_arena_idx);
let props: &'_ P = unsafe { &*(bump_props as *const P) }; let props: &'_ P = unsafe { &*(bump_props as *const P) };
let res = component(scope, props); let res = component(scope, props);
res res
@@ -657,7 +667,7 @@ impl<'a> NodeFactory<'a> {
self, self,
node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>, node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
) -> VNode<'a> { ) -> VNode<'a> {
let bump = self.bump(); let bump = self.bump;
let mut nodes = bumpalo::collections::Vec::new_in(bump); let mut nodes = bumpalo::collections::Vec::new_in(bump);
for node in node_iter { for node in node_iter {
@@ -697,6 +707,58 @@ impl<'a> NodeFactory<'a> {
}) })
} }
// this isn't quite feasible yet
// I think we need some form of interior mutability or state on nodefactory that stores which subtree was created
pub fn create_children(
self,
node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
) -> Element {
let bump = self.bump;
let mut nodes = bumpalo::collections::Vec::new_in(bump);
for node in node_iter {
nodes.push(node.into_vnode(self));
}
if nodes.is_empty() {
nodes.push(VNode::Anchor(bump.alloc(VAnchor {
dom_id: empty_cell(),
})));
}
let children = nodes.into_bump_slice();
// TODO
// We need a dedicated path in the rsx! macro that will trigger the "you need keys" warning
//
// if cfg!(debug_assertions) {
// if children.len() > 1 {
// if children.last().unwrap().key().is_none() {
// log::error!(
// r#"
// Warning: Each child in an array or iterator should have a unique "key" prop.
// Not providing a key will lead to poor performance with lists.
// See docs.rs/dioxus for more information.
// ---
// To help you identify where this error is coming from, we've generated a backtrace.
// "#,
// );
// }
// }
// }
let frag = VNode::Fragment(VFragment {
children,
key: None,
});
let ptr = self.bump.alloc(frag) as *const _;
Some(NodeLink {
link_idx: Default::default(),
scope_id: Default::default(),
node: unsafe { std::mem::transmute(ptr) },
})
}
pub fn annotate_lazy<'z, 'b>( pub fn annotate_lazy<'z, 'b>(
f: impl FnOnce(NodeFactory<'z>) -> VNode<'z> + 'b, f: impl FnOnce(NodeFactory<'z>) -> VNode<'z> + 'b,
) -> Option<LazyNodes<'z, 'b>> { ) -> Option<LazyNodes<'z, 'b>> {
@@ -796,28 +858,48 @@ impl IntoVNode<'_> for Arguments<'_> {
} }
} }
// called cx.render from a helper function
impl IntoVNode<'_> for Option<NodeLink> { impl IntoVNode<'_> for Option<NodeLink> {
fn into_vnode(self, cx: NodeFactory) -> VNode { fn into_vnode(self, _cx: NodeFactory) -> VNode {
todo!() match self {
Some(node) => VNode::Linked(node),
None => {
todo!()
}
}
} }
} }
// essentially passing elements through props
// just build a new element in place
impl IntoVNode<'_> for &Option<NodeLink> { impl IntoVNode<'_> for &Option<NodeLink> {
fn into_vnode(self, cx: NodeFactory) -> VNode { fn into_vnode(self, _cx: NodeFactory) -> VNode {
todo!() match self {
Some(node) => VNode::Linked(NodeLink {
link_idx: node.link_idx.clone(),
scope_id: node.scope_id.clone(),
node: node.node,
}),
None => {
//
todo!()
}
}
} }
} }
// Conveniently, we also support "None"
impl IntoVNode<'_> for NodeLink { impl IntoVNode<'_> for NodeLink {
fn into_vnode(self, cx: NodeFactory) -> VNode { fn into_vnode(self, _cx: NodeFactory) -> VNode {
todo!() VNode::Linked(self)
} }
} }
// Conveniently, we also support "None"
impl IntoVNode<'_> for &NodeLink { impl IntoVNode<'_> for &NodeLink {
fn into_vnode(self, cx: NodeFactory) -> VNode { fn into_vnode(self, _cx: NodeFactory) -> VNode {
todo!() VNode::Linked(NodeLink {
link_idx: self.link_idx.clone(),
scope_id: self.scope_id.clone(),
node: self.node,
})
} }
} }
+45 -22
View File
@@ -2,6 +2,7 @@ use crate::innerlude::*;
use futures_channel::mpsc::UnboundedSender; use futures_channel::mpsc::UnboundedSender;
use fxhash::FxHashMap; use fxhash::FxHashMap;
use smallvec::SmallVec;
use std::{ use std::{
any::{Any, TypeId}, any::{Any, TypeId},
cell::{Cell, RefCell}, cell::{Cell, RefCell},
@@ -62,9 +63,6 @@ pub struct Scope {
// The double-buffering situation that we will use // The double-buffering situation that we will use
pub(crate) frames: [BumpFrame; 2], pub(crate) frames: [BumpFrame; 2],
pub(crate) old_root: RefCell<Option<NodeLink>>,
pub(crate) new_root: RefCell<Option<NodeLink>>,
pub(crate) caller: *const dyn Fn(&Scope) -> Element, pub(crate) caller: *const dyn Fn(&Scope) -> Element,
/* /*
@@ -241,19 +239,22 @@ impl Scope {
/// } /// }
///``` ///```
pub fn render<'src>(&'src self, lazy_nodes: Option<LazyNodes<'src, '_>>) -> Option<NodeLink> { pub fn render<'src>(&'src self, lazy_nodes: Option<LazyNodes<'src, '_>>) -> Option<NodeLink> {
let bump = &self.wip_frame().bump; let frame = self.wip_frame();
let bump = &frame.bump;
let factory = NodeFactory { bump }; let factory = NodeFactory { bump };
let node = lazy_nodes.map(|f| f.call(factory))?; let node = lazy_nodes.map(|f| f.call(factory))?;
let node = bump.alloc(node);
let idx = self let node_ptr = node as *mut _;
.wip_frame() let node_ptr = unsafe { std::mem::transmute(node_ptr) };
.add_node(unsafe { std::mem::transmute(node) });
Some(NodeLink { let link = NodeLink {
gen_id: self.generation.get(), scope_id: Cell::new(Some(self.our_arena_idx)),
scope_id: self.our_arena_idx, link_idx: Cell::new(0),
link_idx: idx, node: node_ptr,
}) };
Some(link)
} }
/// Push an effect to be ran after the component has been successfully mounted to the dom /// Push an effect to be ran after the component has been successfully mounted to the dom
@@ -435,6 +436,7 @@ impl Scope {
false => &self.frames[1], false => &self.frames[1],
} }
} }
pub(crate) fn fin_frame(&self) -> &BumpFrame { pub(crate) fn fin_frame(&self) -> &BumpFrame {
match self.generation.get() & 1 == 1 { match self.generation.get() & 1 == 1 {
true => &self.frames[0], true => &self.frames[0],
@@ -496,28 +498,42 @@ impl Scope {
effect(); effect();
} }
} }
pub fn root_node(&self) -> &VNode {
let node = *self.wip_frame().nodes.borrow().get(0).unwrap();
unsafe { std::mem::transmute(&*node) }
}
} }
pub struct BumpFrame { pub(crate) struct BumpFrame {
pub bump: Bump, pub bump: Bump,
pub nodes: RefCell<Vec<VNode<'static>>>, pub nodes: RefCell<Vec<*const VNode<'static>>>,
} }
impl BumpFrame { impl BumpFrame {
pub fn new() -> Self { pub fn new(capacity: usize) -> Self {
let bump = Bump::new(); let bump = Bump::with_capacity(capacity);
let node = &*bump.alloc(VText { let node = &*bump.alloc(VText {
text: "asd", text: "asd",
dom_id: Default::default(), dom_id: Default::default(),
is_static: false, is_static: false,
}); });
let nodes = RefCell::new(vec![VNode::Text(unsafe { std::mem::transmute(node) })]); let node = bump.alloc(VNode::Text(unsafe { std::mem::transmute(node) }));
let nodes = RefCell::new(vec![node as *const _]);
Self { bump, nodes } Self { bump, nodes }
} }
fn add_node(&self, node: VNode<'static>) -> usize {
pub fn allocated_bytes(&self) -> usize {
self.bump.allocated_bytes()
}
pub fn assign_nodelink(&self, node: &NodeLink) {
let mut nodes = self.nodes.borrow_mut(); let mut nodes = self.nodes.borrow_mut();
nodes.push(node);
nodes.len() - 1 let len = nodes.len();
nodes.push(node.node);
node.link_idx.set(len);
} }
} }
@@ -531,11 +547,18 @@ impl BumpFrame {
#[derive(Default)] #[derive(Default)]
pub(crate) struct HookList { pub(crate) struct HookList {
arena: Bump, arena: Bump,
vals: RefCell<Vec<*mut dyn Any>>, vals: RefCell<SmallVec<[*mut dyn Any; 5]>>,
idx: Cell<usize>, idx: Cell<usize>,
} }
impl HookList { impl HookList {
pub fn new(capacity: usize) -> Self {
Self {
arena: Bump::with_capacity(capacity),
..Default::default()
}
}
pub(crate) fn next<T: 'static>(&self) -> Option<&mut T> { pub(crate) fn next<T: 'static>(&self) -> Option<&mut T> {
self.vals.borrow().get(self.idx.get()).and_then(|inn| { self.vals.borrow().get(self.idx.get()).and_then(|inn| {
self.idx.set(self.idx.get() + 1); self.idx.set(self.idx.get() + 1);
@@ -571,7 +594,7 @@ impl HookList {
self.cur_idx() >= self.len() self.cur_idx() >= self.len()
} }
pub fn clear_hooks(&mut self) { pub fn clear(&mut self) {
self.vals.borrow_mut().drain(..).for_each(|state| { self.vals.borrow_mut().drain(..).for_each(|state| {
let as_mut = unsafe { &mut *state }; let as_mut = unsafe { &mut *state };
let boxed = unsafe { bumpalo::boxed::Box::from_raw(as_mut) }; let boxed = unsafe { bumpalo::boxed::Box::from_raw(as_mut) };
+104 -73
View File
@@ -1,3 +1,4 @@
use fxhash::FxHashMap;
use slab::Slab; use slab::Slab;
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
@@ -7,7 +8,6 @@ use futures_channel::mpsc::UnboundedSender;
use crate::innerlude::*; use crate::innerlude::*;
pub type FcSlot = *const (); pub type FcSlot = *const ();
// pub heuristics: FxHashMap<FcSlot, Heuristic>,
pub struct Heuristic { pub struct Heuristic {
hook_arena_size: usize, hook_arena_size: usize,
@@ -21,6 +21,7 @@ pub struct Heuristic {
pub(crate) struct ScopeArena { pub(crate) struct ScopeArena {
bump: Bump, bump: Bump,
scopes: RefCell<Vec<*mut Scope>>, scopes: RefCell<Vec<*mut Scope>>,
pub heuristics: RefCell<FxHashMap<FcSlot, Heuristic>>,
free_scopes: RefCell<Vec<ScopeId>>, free_scopes: RefCell<Vec<ScopeId>>,
nodes: RefCell<Slab<*const VNode<'static>>>, nodes: RefCell<Slab<*const VNode<'static>>>,
pub(crate) sender: UnboundedSender<SchedulerMsg>, pub(crate) sender: UnboundedSender<SchedulerMsg>,
@@ -31,6 +32,7 @@ impl ScopeArena {
Self { Self {
bump: Bump::new(), bump: Bump::new(),
scopes: RefCell::new(Vec::new()), scopes: RefCell::new(Vec::new()),
heuristics: RefCell::new(FxHashMap::default()),
free_scopes: RefCell::new(Vec::new()), free_scopes: RefCell::new(Vec::new()),
nodes: RefCell::new(Slab::new()), nodes: RefCell::new(Slab::new()),
sender, sender,
@@ -68,33 +70,53 @@ impl ScopeArena {
} else { } else {
let scope_id = ScopeId(self.scopes.borrow().len()); let scope_id = ScopeId(self.scopes.borrow().len());
let old_root = NodeLink { let (node_capacity, hook_capacity) = {
link_idx: 0, let heuristics = self.heuristics.borrow();
gen_id: 0, if let Some(heuristic) = heuristics.get(&fc_ptr) {
scope_id, (heuristic.node_arena_size, heuristic.hook_arena_size)
}; } else {
let new_root = NodeLink { (0, 0)
link_idx: 0, }
gen_id: 0,
scope_id,
}; };
let new_scope = Scope { let mut frames = [BumpFrame::new(node_capacity), BumpFrame::new(node_capacity)];
frames[0].nodes.get_mut().push({
let vnode = frames[0]
.bump
.alloc(VNode::Text(frames[0].bump.alloc(VText {
dom_id: Default::default(),
is_static: false,
text: "",
})));
unsafe { std::mem::transmute(vnode as *mut VNode) }
});
frames[1].nodes.get_mut().push({
let vnode = frames[1]
.bump
.alloc(VNode::Text(frames[1].bump.alloc(VText {
dom_id: Default::default(),
is_static: false,
text: "",
})));
unsafe { std::mem::transmute(vnode as *mut VNode) }
});
let scope = self.bump.alloc(Scope {
sender: self.sender.clone(), sender: self.sender.clone(),
parent_scope, parent_scope,
our_arena_idx: scope_id, our_arena_idx: scope_id,
height, height,
subtree: Cell::new(subtree), subtree: Cell::new(subtree),
is_subtree_root: Cell::new(false), is_subtree_root: Cell::new(false),
frames: [BumpFrame::new(), BumpFrame::new()], frames,
hooks: Default::default(), hooks: HookList::new(hook_capacity),
shared_contexts: Default::default(), shared_contexts: Default::default(),
caller, caller,
generation: 0.into(), generation: 0.into(),
old_root: RefCell::new(Some(old_root)),
new_root: RefCell::new(Some(new_root)),
items: RefCell::new(SelfReferentialItems { items: RefCell::new(SelfReferentialItems {
listeners: Default::default(), listeners: Default::default(),
borrowed_props: Default::default(), borrowed_props: Default::default(),
@@ -102,16 +124,42 @@ impl ScopeArena {
tasks: Default::default(), tasks: Default::default(),
pending_effects: Default::default(), pending_effects: Default::default(),
}), }),
}; });
let stable = self.bump.alloc(new_scope); self.scopes.borrow_mut().push(scope);
self.scopes.borrow_mut().push(stable);
scope_id scope_id
} }
} }
pub fn try_remove(&self, id: &ScopeId) -> Option<Scope> { pub fn try_remove(&self, id: &ScopeId) -> Option<()> {
todo!() self.ensure_drop_safety(id);
let mut scope = unsafe { &mut *self.get_scope_raw(id)? };
// we're just reusing scopes so we need to clear it out
scope.hooks.clear();
scope.shared_contexts.get_mut().clear();
scope.parent_scope = None;
scope.generation.set(0);
scope.is_subtree_root.set(false);
scope.subtree.set(0);
let SelfReferentialItems {
borrowed_props,
listeners,
pending_effects,
suspended_nodes,
tasks,
} = scope.items.get_mut();
borrowed_props.clear();
listeners.clear();
pending_effects.clear();
suspended_nodes.clear();
tasks.clear();
self.free_scopes.borrow_mut().push(*id);
Some(())
} }
pub fn reserve_node(&self, node: &VNode) -> ElementId { pub fn reserve_node(&self, node: &VNode) -> ElementId {
@@ -123,16 +171,10 @@ impl ScopeArena {
let node = unsafe { std::mem::transmute::<*const VNode, *const VNode>(node) }; let node = unsafe { std::mem::transmute::<*const VNode, *const VNode>(node) };
entry.insert(node); entry.insert(node);
id id
// let nodes = self.nodes.borrow_mut();
// let id = nodes.insert(());
// let node_id = ElementId(id);
// node = Some(node_id);
// node_id
} }
pub fn collect_garbage(&self, id: ElementId) { pub fn collect_garbage(&self, id: ElementId) {
todo!() self.nodes.borrow_mut().remove(id.0);
} }
// These methods would normally exist on `scope` but they need access to *all* of the scopes // These methods would normally exist on `scope` but they need access to *all* of the scopes
@@ -152,35 +194,25 @@ impl ScopeArena {
pub(crate) fn ensure_drop_safety(&self, scope_id: &ScopeId) { pub(crate) fn ensure_drop_safety(&self, scope_id: &ScopeId) {
let scope = self.get_scope(scope_id).unwrap(); let scope = self.get_scope(scope_id).unwrap();
let mut items = scope.items.borrow_mut();
// make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we // make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we
// run the hooks (which hold an &mut Reference) // run the hooks (which hold an &mut Reference)
// right now, we don't drop // recursively call ensure_drop_safety on all children
scope items.borrowed_props.drain(..).for_each(|comp| {
.items let scope_id = comp
.borrow_mut() .associated_scope
.borrowed_props .get()
.drain(..) .expect("VComponents should be associated with a valid Scope");
.for_each(|comp| {
// First drop the component's undropped references
let scope_id = comp
.associated_scope
.get()
.expect("VComponents should be associated with a valid Scope");
todo!("move this onto virtualdom"); self.ensure_drop_safety(&scope_id);
// let scope = unsafe { &mut *scope_id };
// scope.ensure_drop_safety(); let mut drop_props = comp.drop_props.borrow_mut().take().unwrap();
drop_props();
todo!("drop the component's props"); });
// let mut drop_props = comp.drop_props.borrow_mut().take().unwrap();
// drop_props();
});
// Now that all the references are gone, we can safely drop our own references in our listeners. // Now that all the references are gone, we can safely drop our own references in our listeners.
scope items
.items
.borrow_mut()
.listeners .listeners
.drain(..) .drain(..)
.for_each(|listener| drop(listener.callback.borrow_mut().take())); .for_each(|listener| drop(listener.callback.borrow_mut().take()));
@@ -212,59 +244,58 @@ impl ScopeArena {
// just forget about our suspended nodes while we're at it // just forget about our suspended nodes while we're at it
items.suspended_nodes.clear(); items.suspended_nodes.clear();
items.tasks.clear();
items.pending_effects.clear();
// guarantee that we haven't screwed up - there should be no latent references anywhere // guarantee that we haven't screwed up - there should be no latent references anywhere
debug_assert!(items.listeners.is_empty()); debug_assert!(items.listeners.is_empty());
debug_assert!(items.suspended_nodes.is_empty());
debug_assert!(items.borrowed_props.is_empty()); debug_assert!(items.borrowed_props.is_empty());
debug_assert!(items.suspended_nodes.is_empty());
debug_assert!(items.tasks.is_empty());
debug_assert!(items.pending_effects.is_empty());
log::debug!("Borrowed stuff is successfully cleared"); // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
scope.wip_frame().nodes.borrow_mut().clear();
// temporarily cast the vcomponent to the right lifetime
// let vcomp = scope.load_vcomp();
} }
let render: &dyn Fn(&Scope) -> Element = unsafe { &*scope.caller }; let render: &dyn Fn(&Scope) -> Element = unsafe { &*scope.caller };
// Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders. if let Some(link) = render(scope) {
scope.wip_frame().nodes.borrow_mut().clear(); // right now, it's a panic to render a nodelink from another scope
if let Some(key) = render(scope) { // todo: enable this. it should (reasonably) work even if it doesnt make much sense
dbg!(key); assert_eq!(link.scope_id.get(), Some(*id));
dbg!(&scope.wip_frame().nodes.borrow_mut()); // nodelinks are not assigned when called and must be done so through the create/diff phase
// let mut old = scope.old_root.borrow_mut(); // however, we need to link this one up since it will never be used in diffing
// let mut new = scope.new_root.borrow_mut(); scope.wip_frame().assign_nodelink(&link);
debug_assert_eq!(scope.wip_frame().nodes.borrow().len(), 1);
// let new_old = new.clone(); if !scope.items.borrow().tasks.is_empty() {
// *old = new_old; // self.
// *new = Some(key); }
// dbg!(&old);
// dbg!(&new);
// the user's component succeeded. We can safely cycle to the next frame
// scope.frames.wip_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
scope.cycle_frame(); scope.cycle_frame();
true true
} else { } else {
false false
} }
} }
// The head of the bumpframe is the first linked NodeLink
pub fn wip_head(&self, id: &ScopeId) -> &VNode { pub fn wip_head(&self, id: &ScopeId) -> &VNode {
let scope = self.get_scope(id).unwrap(); let scope = self.get_scope(id).unwrap();
let wip_frame = scope.wip_frame(); let wip_frame = scope.wip_frame();
let nodes = wip_frame.nodes.borrow(); let nodes = wip_frame.nodes.borrow();
let node = nodes.get(0).unwrap(); let node: &VNode = unsafe { &**nodes.get(0).unwrap() };
unsafe { std::mem::transmute::<&VNode, &VNode>(node) } unsafe { std::mem::transmute::<&VNode, &VNode>(node) }
} }
// The head of the bumpframe is the first linked NodeLink
pub fn fin_head(&self, id: &ScopeId) -> &VNode { pub fn fin_head(&self, id: &ScopeId) -> &VNode {
let scope = self.get_scope(id).unwrap(); let scope = self.get_scope(id).unwrap();
let wip_frame = scope.fin_frame(); let wip_frame = scope.fin_frame();
let nodes = wip_frame.nodes.borrow(); let nodes = wip_frame.nodes.borrow();
let node: &VNode = nodes.get(0).unwrap(); let node: &VNode = unsafe { &**nodes.get(0).unwrap() };
unsafe { std::mem::transmute::<&VNode, &VNode>(node) } unsafe { std::mem::transmute::<&VNode, &VNode>(node) }
} }
+48 -77
View File
@@ -13,6 +13,9 @@ use std::{any::Any, collections::VecDeque};
/// A virtual node system that progresses user events and diffs UI trees. /// A virtual node system that progresses user events and diffs UI trees.
/// ///
///
/// ## 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 [`Context`] and a [`Properties`] type and return an [`Element`].
/// ///
/// ```rust /// ```rust
@@ -105,14 +108,16 @@ pub struct VirtualDom {
_root_caller: *mut dyn Fn(&Scope) -> Element, _root_caller: *mut dyn Fn(&Scope) -> Element,
pub(crate) scopes: Box<ScopeArena>, scopes: Box<ScopeArena>,
receiver: UnboundedReceiver<SchedulerMsg>, receiver: UnboundedReceiver<SchedulerMsg>,
pub(crate) sender: UnboundedSender<SchedulerMsg>,
// Every component that has futures that need to be polled sender: UnboundedSender<SchedulerMsg>,
pending_futures: FxHashSet<ScopeId>, pending_futures: FxHashSet<ScopeId>,
pending_messages: VecDeque<SchedulerMsg>, pending_messages: VecDeque<SchedulerMsg>,
dirty_scopes: IndexSet<ScopeId>, dirty_scopes: IndexSet<ScopeId>,
} }
@@ -130,7 +135,7 @@ impl VirtualDom {
/// ///
/// # Example /// # Example
/// ``` /// ```
/// fn Example(cx: Context<()>) -> DomTree { /// fn Example(cx: Context, props: &()) -> Element {
/// cx.render(rsx!( div { "hello world" } )) /// cx.render(rsx!( div { "hello world" } ))
/// } /// }
/// ///
@@ -159,7 +164,7 @@ impl VirtualDom {
/// name: &'static str /// name: &'static str
/// } /// }
/// ///
/// fn Example(cx: Context<SomeProps>) -> DomTree { /// fn Example(cx: Context, props: &SomeProps) -> Element {
/// cx.render(rsx!{ div{ "hello {cx.name}" } }) /// cx.render(rsx!{ div{ "hello {cx.name}" } })
/// } /// }
/// ///
@@ -172,7 +177,7 @@ impl VirtualDom {
/// let mut dom = VirtualDom::new_with_props(Example, SomeProps { name: "jane" }); /// let mut dom = VirtualDom::new_with_props(Example, SomeProps { name: "jane" });
/// let mutations = dom.rebuild(); /// let mutations = dom.rebuild();
/// ``` /// ```
pub fn new_with_props<P: 'static + Send>(root: FC<P>, root_props: P) -> Self { pub fn new_with_props<P: 'static>(root: FC<P>, root_props: P) -> Self {
let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>(); let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>();
Self::new_with_props_and_scheduler(root, root_props, sender, receiver) Self::new_with_props_and_scheduler(root, root_props, sender, receiver)
} }
@@ -230,8 +235,10 @@ impl VirtualDom {
/// ///
/// # Example /// # Example
/// ///
/// ```rust
/// ///
/// ///
/// ```
pub fn get_scheduler_channel(&self) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> { pub fn get_scheduler_channel(&self) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> {
self.sender.clone() self.sender.clone()
} }
@@ -240,8 +247,10 @@ impl VirtualDom {
/// ///
/// # Example /// # Example
/// ///
/// ```rust
/// ///
/// ///
/// ```
pub fn has_any_work(&self) -> bool { pub fn has_any_work(&self) -> bool {
!(self.dirty_scopes.is_empty() && self.pending_messages.is_empty()) !(self.dirty_scopes.is_empty() && self.pending_messages.is_empty())
} }
@@ -347,7 +356,9 @@ impl VirtualDom {
/// # Example /// # Example
/// ///
/// ```no_run /// ```no_run
/// static App: FC<()> = |cx, props|rsx!(cx, div {"hello"} ); /// fn App(cx: Context, props: &()) -> Element {
/// cx.render(rsx!( div {"hello"} ))
/// }
/// ///
/// let mut dom = VirtualDom::new(App); /// let mut dom = VirtualDom::new(App);
/// ///
@@ -368,6 +379,7 @@ impl VirtualDom {
/// applied the edits. /// applied the edits.
/// ///
/// Mutations are the only link between the RealDOM and the VirtualDOM. /// Mutations are the only link between the RealDOM and the VirtualDOM.
///
pub fn work_with_deadline(&mut self, mut deadline: impl FnMut() -> bool) -> Vec<Mutations> { pub fn work_with_deadline(&mut self, mut deadline: impl FnMut() -> bool) -> Vec<Mutations> {
let mut committed_mutations = vec![]; let mut committed_mutations = vec![];
@@ -436,10 +448,7 @@ impl VirtualDom {
} }
} }
// let scopes = &mut self.scopes;
let work_completed = diff_state.work(&mut deadline); let work_completed = diff_state.work(&mut deadline);
// let work_completed = crate::diff::work(&mut diff_state, &mut deadline);
// let work_completed = crate::diff::work(&mut diff_state, &mut deadline);
if work_completed { if work_completed {
let DiffState { let DiffState {
@@ -454,7 +463,7 @@ impl VirtualDom {
} }
// I think the stack should be empty at the end of diffing? // I think the stack should be empty at the end of diffing?
debug_assert_eq!(stack.scope_stack.len(), 0); debug_assert_eq!(stack.scope_stack.len(), 1);
committed_mutations.push(mutations); committed_mutations.push(mutations);
} else { } else {
@@ -536,74 +545,45 @@ impl VirtualDom {
/// let edits = dom.diff(); /// let edits = dom.diff();
/// ``` /// ```
pub fn hard_diff<'a>(&'a mut self, scope_id: &ScopeId) -> Option<Mutations<'a>> { pub fn hard_diff<'a>(&'a mut self, scope_id: &ScopeId) -> Option<Mutations<'a>> {
log::debug!("hard diff {:?}", scope_id); let mut diff_machine = DiffState::new(&self.scopes);
if self.scopes.run_scope(scope_id) { if self.scopes.run_scope(scope_id) {
let mut diff_machine = DiffState::new(&self.scopes);
diff_machine.force_diff = true; diff_machine.force_diff = true;
diff_machine.diff_scope(scope_id);
diff_machine.work(|| false);
// let scopes = &mut self.scopes;
// crate::diff::diff_scope(&mut diff_machine, scope_id);
// self.scopes.diff_scope(&mut diff_machine, scope_id);
// dbg!(&diff_machine.mutations);
// let DiffState {
// mutations,
// stack,
// seen_scopes,
// force_diff,
// ..
// } = mutations;
// let mutations = diff_machine.mutations;
// Some(unsafe { std::mem::transmute(mutations) })
todo!()
} else {
None
} }
Some(diff_machine.mutations)
} }
/// Renders an `rsx` call into the Base Scope's allocator. /// Renders an `rsx` call into the Base Scope's allocator.
/// ///
/// Useful when needing to render nodes from outside the VirtualDom, such as in a test. /// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
pub fn render_vnodes<'a>(&'a self, lazy_nodes: Option<LazyNodes<'a, '_>>) -> &'a VNode<'a> { pub fn render_vnodes<'a>(&'a self, lazy_nodes: Option<LazyNodes<'a, '_>>) -> &'a VNode<'a> {
todo!() let scope = self.scopes.get_scope(&self.base_scope).unwrap();
let frame = scope.wip_frame();
let factory = NodeFactory { bump: &frame.bump };
let node = lazy_nodes.unwrap().call(factory);
frame.bump.alloc(node)
} }
/// Renders an `rsx` call into the Base Scope's allocator. /// Renders an `rsx` call into the Base Scope's allocator.
/// ///
/// Useful when needing to render nodes from outside the VirtualDom, such as in a test. /// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
pub fn diff_vnodes<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Mutations<'a> { pub fn diff_vnodes<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Mutations<'a> {
todo!() let mut machine = DiffState::new(&self.scopes);
// let mutations = Mutations::new(); machine.stack.push(DiffInstruction::Diff { new, old });
// let mut machine: DiffState = todo!(); machine.stack.scope_stack.push(self.base_scope);
// // let mut machine = DiffState::new(mutations); machine.work(|| false);
// // let mut machine = DiffState::new(mutations); machine.mutations
// machine.stack.push(DiffInstruction::Diff { new, old });
// machine.mutations
} }
/// Renders an `rsx` call into the Base Scope's allocator. /// Renders an `rsx` call into the Base Scope's allocator.
/// ///
/// Useful when needing to render nodes from outside the VirtualDom, such as in a test. /// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
pub fn create_vnodes<'a>(&'a self, left: Option<LazyNodes<'a, '_>>) -> Mutations<'a> { pub fn create_vnodes<'a>(&'a self, left: Option<LazyNodes<'a, '_>>) -> Mutations<'a> {
todo!() let nodes = self.render_vnodes(left);
// let old = self.bump.alloc(self.render_direct(left)); let mut machine = DiffState::new(&self.scopes);
machine.stack.create_node(nodes, MountType::Append);
// let mut machine: DiffState = todo!(); machine.work(|| false);
// // let mut machine = DiffState::new(Mutations::new()); machine.mutations
// // let mut machine = DiffState::new(Mutations::new());
// machine.stack.create_node(old, MountType::Append);
// todo!()
// // machine.work(&mut || false);
// // machine.mutations
} }
/// Renders an `rsx` call into the Base Scope's allocator. /// Renders an `rsx` call into the Base Scope's allocator.
@@ -616,26 +596,17 @@ impl VirtualDom {
) -> (Mutations<'a>, Mutations<'a>) { ) -> (Mutations<'a>, Mutations<'a>) {
let (old, new) = (self.render_vnodes(left), self.render_vnodes(right)); let (old, new) = (self.render_vnodes(left), self.render_vnodes(right));
// let mut machine: DiffState = todo!(); let mut create = DiffState::new(&self.scopes);
// let mut machine = DiffState::new(Mutations::new()); create.stack.scope_stack.push(self.base_scope);
create.stack.create_node(old, MountType::Append);
create.work(|| false);
// machine.stack.create_node(old, MountType::Append); let mut edit = DiffState::new(&self.scopes);
create.stack.scope_stack.push(self.base_scope);
edit.stack.push(DiffInstruction::Diff { old, new });
edit.work(&mut || false);
todo!() (create.mutations, edit.mutations)
// machine.work(|| false);
// let create_edits = machine.mutations;
// let mut machine: DiffState = todo!();
// // let mut machine = DiffState::new(Mutations::new());
// machine.stack.push(DiffInstruction::Diff { old, new });
// machine.work(&mut || false);
// let edits = machine.mutations;
// (create_edits, edits)
} }
} }
+4 -22
View File
@@ -215,12 +215,12 @@ fn create_components() {
}) })
}; };
#[derive(Props)] #[derive(Props, PartialEq)]
struct ChildProps<'a> { struct ChildProps {
children: ScopeChildren<'a>, children: Element,
} }
fn Child<'a>(cx: Context<'a>, props: &ChildProps<'a>) -> Element { fn Child(cx: Context, props: &ChildProps) -> Element {
cx.render(rsx! { cx.render(rsx! {
h1 {} h1 {}
div { {&props.children} } div { {&props.children} }
@@ -299,21 +299,3 @@ fn anchors() {
] ]
); );
} }
#[test]
fn suspended() {
todo!()
// static App: FC<()> = |cx, props| {
// let val = use_suspense(cx, || async {}, |p| todo!());
// cx.render(rsx! { {val} })
// };
// let mut dom = new_dom(App, ());
// let mutations = dom.rebuild();
// assert_eq!(
// mutations.edits,
// [CreatePlaceholder { root: 0 }, AppendChildren { many: 1 },]
// );
}
+3 -22
View File
@@ -257,12 +257,12 @@ fn many_items_become_fragment() {
] ]
); );
// hmmmmmmmmm worried about reusing IDs that we shouldnt be // note: the ID gets reused
assert_eq!( assert_eq!(
change.edits, change.edits,
[ [
Remove { root: 2 }, Remove { root: 2 },
CreatePlaceholder { root: 4 }, CreatePlaceholder { root: 3 },
ReplaceWith { root: 0, m: 1 }, ReplaceWith { root: 0, m: 1 },
] ]
); );
@@ -433,7 +433,7 @@ fn keyed_diffing_order() {
let dom = new_dom(); let dom = new_dom();
let left = rsx!( let left = rsx!(
// {(0..5).map(|f| {rsx! { div { key: "{f}" }}})} {(0..5).map(|f| {rsx! { div { key: "{f}" }}})}
p {"e"} p {"e"}
); );
let right = rsx!( let right = rsx!(
@@ -802,22 +802,3 @@ fn controlled_keyed_diffing_out_of_order_max_test() {
] ]
); );
} }
#[test]
fn suspense() {
let dom = new_dom();
todo!()
// let edits = dom.create_vnodes(Some(LazyNodes::new(|f| {
// use std::cell::{Cell, RefCell};
// VNode::Suspended(f.bump().alloc(VSuspended {
// task_id: 0,
// callback: RefCell::new(None),
// dom_id: Cell::new(None),
// }))
// })));
// assert_eq!(
// edits.edits,
// [CreatePlaceholder { root: 0 }, AppendChildren { many: 1 }]
// );
}
+3 -4
View File
@@ -1,7 +1,6 @@
use std::cell::RefCell; use std::cell::RefCell;
use dioxus::prelude::Scope; use dioxus::prelude::Scope;
use dioxus::ScopeChildren;
use dioxus_core as dioxus; use dioxus_core as dioxus;
use dioxus_core::{Context, Element, LazyNodes, NodeFactory, Properties}; use dioxus_core::{Context, Element, LazyNodes, NodeFactory, Properties};
use dioxus_core_macro::Props; use dioxus_core_macro::Props;
@@ -44,7 +43,7 @@ pub struct WebviewWindowProps<'a> {
/// focuse me /// focuse me
onfocused: &'a dyn FnMut(()), onfocused: &'a dyn FnMut(()),
children: ScopeChildren<'a>, children: Element,
} }
/// A handle to a /// A handle to a
@@ -57,7 +56,7 @@ pub struct WebviewWindowProps<'a> {
/// ///
/// ///
/// ///
pub fn WebviewWindow((cx, props): Scope<WebviewWindowProps>) -> Element { pub fn WebviewWindow(cx: Context, props: &WebviewWindowProps) -> Element {
let dtcx = cx.consume_state::<RefCell<DesktopContext>>()?; let dtcx = cx.consume_state::<RefCell<DesktopContext>>()?;
cx.use_hook( cx.use_hook(
@@ -91,7 +90,7 @@ fn syntax_works() {
use dioxus_hooks::*; use dioxus_hooks::*;
use dioxus_html as dioxus_elements; use dioxus_html as dioxus_elements;
static App: FC<()> = |(cx, props)| { static App: FC<()> = |cx, props| {
cx.render(rsx! { cx.render(rsx! {
// left window // left window
WebviewWindow { WebviewWindow {
+3 -4
View File
@@ -166,7 +166,7 @@ pub fn run<T: 'static + Send + Sync>(
sender.unbounded_send(SchedulerMsg::UiEvent(event)).unwrap(); sender.unbounded_send(SchedulerMsg::UiEvent(event)).unwrap();
if let Some(BridgeEvent::Update(edits)) = rx.blocking_recv() { if let Some(BridgeEvent::Update(edits)) = rx.blocking_recv() {
log::info!("bridge received message {:?}", edits); log::info!("bridge received message");
Some(RpcResponse::new_result(req.id.take(), Some(edits))) Some(RpcResponse::new_result(req.id.take(), Some(edits)))
} else { } else {
log::info!("none received message"); log::info!("none received message");
@@ -276,7 +276,7 @@ pub(crate) fn launch_vdom_with_tokio<P: Send + 'static>(
runtime.block_on(async move { runtime.block_on(async move {
let mut vir = VirtualDom::new_with_props_and_scheduler(root, props, sender, receiver); let mut vir = VirtualDom::new_with_props_and_scheduler(root, props, sender, receiver);
let _ = vir.get_event_sender(); let _ = vir.get_scheduler_channel();
let edits = vir.rebuild(); let edits = vir.rebuild();
@@ -298,14 +298,13 @@ pub(crate) fn launch_vdom_with_tokio<P: Send + 'static>(
// todo: maybe we want to schedule ourselves in // todo: maybe we want to schedule ourselves in
// on average though, the virtualdom running natively is stupid fast // on average though, the virtualdom running natively is stupid fast
let mut muts = vir.run_with_deadline(|| false); let mut muts = vir.work_with_deadline(|| false);
log::debug!("finished running with deadline"); log::debug!("finished running with deadline");
let mut edits = vec![]; let mut edits = vec![];
while let Some(edit) = muts.pop() { while let Some(edit) = muts.pop() {
log::debug!("sending message on channel with edit {:?}", edit);
let edit_string = serde_json::to_value(Evt { edits: edit.edits }) let edit_string = serde_json::to_value(Evt { edits: edit.edits })
.expect("serializing edits should never fail"); .expect("serializing edits should never fail");
edits.push(edit_string); edits.push(edit_string);
+1
View File
@@ -0,0 +1 @@
asd
+11 -9
View File
@@ -14,7 +14,7 @@ use std::fmt::{Display, Formatter};
use dioxus_core::exports::bumpalo; use dioxus_core::exports::bumpalo;
use dioxus_core::exports::bumpalo::Bump; use dioxus_core::exports::bumpalo::Bump;
use dioxus_core::nodes::IntoVNode; use dioxus_core::IntoVNode;
use dioxus_core::*; use dioxus_core::*;
/// A memory pool for rendering /// A memory pool for rendering
@@ -94,7 +94,7 @@ pub fn render_vdom_scope(vdom: &VirtualDom, scope: ScopeId) -> Option<String> {
"{:}", "{:}",
TextRenderer { TextRenderer {
cfg: SsrConfig::default(), cfg: SsrConfig::default(),
root: vdom.get_scope(scope).unwrap().root_node(), root: vdom.get_scope(&scope).unwrap().root_node(),
vdom: Some(vdom) vdom: Some(vdom)
} }
)) ))
@@ -158,6 +158,9 @@ impl<'a> TextRenderer<'a, '_> {
} }
write!(f, "<!-- -->")?; write!(f, "<!-- -->")?;
} }
VNode::Linked(link) => {
todo!();
}
VNode::Element(el) => { VNode::Element(el) => {
if self.cfg.indent { if self.cfg.indent {
for _ in 0..il { for _ in 0..il {
@@ -244,7 +247,7 @@ impl<'a> TextRenderer<'a, '_> {
let idx = vcomp.associated_scope.get().unwrap(); let idx = vcomp.associated_scope.get().unwrap();
if let (Some(vdom), false) = (self.vdom, self.cfg.skip_components) { if let (Some(vdom), false) = (self.vdom, self.cfg.skip_components) {
let new_node = vdom.get_scope(idx).unwrap().root_node(); let new_node = vdom.get_scope(&idx).unwrap().root_node();
self.html_render(new_node, f, il + 1)?; self.html_render(new_node, f, il + 1)?;
} else { } else {
} }
@@ -297,18 +300,17 @@ impl SsrConfig {
mod tests { mod tests {
use super::*; use super::*;
use dioxus_core as dioxus;
use dioxus_core::prelude::*; use dioxus_core::prelude::*;
use dioxus_core_macro::*; use dioxus_core_macro::*;
use dioxus_html as dioxus_elements; use dioxus_html as dioxus_elements;
static SIMPLE_APP: FC<()> = |(cx, _)| { static SIMPLE_APP: FC<()> = |cx, _| {
cx.render(rsx!(div { cx.render(rsx!(div {
"hello world!" "hello world!"
})) }))
}; };
static SLIGHTLY_MORE_COMPLEX: FC<()> = |(cx, _)| { static SLIGHTLY_MORE_COMPLEX: FC<()> = |cx, _| {
cx.render(rsx! { cx.render(rsx! {
div { div {
title: "About W3Schools" title: "About W3Schools"
@@ -327,14 +329,14 @@ mod tests {
}) })
}; };
static NESTED_APP: FC<()> = |(cx, _)| { static NESTED_APP: FC<()> = |cx, _| {
cx.render(rsx!( cx.render(rsx!(
div { div {
SIMPLE_APP {} SIMPLE_APP {}
} }
)) ))
}; };
static FRAGMENT_APP: FC<()> = |(cx, _)| { static FRAGMENT_APP: FC<()> = |cx, _| {
cx.render(rsx!( cx.render(rsx!(
div { "f1" } div { "f1" }
div { "f2" } div { "f2" }
@@ -390,7 +392,7 @@ mod tests {
#[test] #[test]
fn styles() { fn styles() {
static STLYE_APP: FC<()> = |(cx, _)| { static STLYE_APP: FC<()> = |cx, _| {
cx.render(rsx! { cx.render(rsx! {
div { color: "blue", font_size: "46px" } div { color: "blue", font_size: "46px" }
}) })
+22 -34
View File
@@ -7,17 +7,12 @@
//! - tests to ensure dyn_into works for various event types. //! - tests to ensure dyn_into works for various event types.
//! - Partial delegation?> //! - Partial delegation?>
use dioxus_core::{ use dioxus_core::{DomEdit, ElementId, SchedulerMsg, ScopeId, UserEvent};
events::{KeyCode, UserEvent},
mutations::NodeRefMutation,
scheduler::SchedulerMsg,
DomEdit, ElementId, ScopeId,
};
use fxhash::FxHashMap; use fxhash::FxHashMap;
use std::{any::Any, fmt::Debug, rc::Rc, sync::Arc}; use std::{any::Any, fmt::Debug, rc::Rc};
use wasm_bindgen::{closure::Closure, JsCast}; use wasm_bindgen::{closure::Closure, JsCast};
use web_sys::{ use web_sys::{
Attr, CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement, CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement,
HtmlOptionElement, HtmlTextAreaElement, Node, NodeList, HtmlOptionElement, HtmlTextAreaElement, Node, NodeList,
}; };
@@ -55,7 +50,7 @@ impl WebsysDom {
let document = load_document(); let document = load_document();
let mut nodes = NodeSlab::new(2000); let mut nodes = NodeSlab::new(2000);
let mut listeners = FxHashMap::default(); let listeners = FxHashMap::default();
// re-hydrate the page - only supports one virtualdom per page // re-hydrate the page - only supports one virtualdom per page
if cfg.hydrate { if cfg.hydrate {
@@ -90,17 +85,17 @@ impl WebsysDom {
} }
} }
pub fn apply_refs(&mut self, refs: &[NodeRefMutation]) { // pub fn apply_refs(&mut self, refs: &[NodeRefMutation]) {
for item in refs { // for item in refs {
if let Some(bla) = &item.element { // if let Some(bla) = &item.element {
let node = self.nodes[item.element_id.as_u64() as usize] // let node = self.nodes[item.element_id.as_u64() as usize]
.as_ref() // .as_ref()
.unwrap() // .unwrap()
.clone(); // .clone();
bla.set(Box::new(node)).unwrap(); // bla.set(Box::new(node)).unwrap();
} // }
} // }
} // }
pub fn process_edits(&mut self, edits: &mut Vec<DomEdit>) { pub fn process_edits(&mut self, edits: &mut Vec<DomEdit>) {
for edit in edits.drain(..) { for edit in edits.drain(..) {
@@ -309,8 +304,8 @@ impl WebsysDom {
} }
} }
fn remove_event_listener(&mut self, event: &str, root: u64) { fn remove_event_listener(&mut self, _event: &str, _root: u64) {
// todo!() todo!()
} }
fn set_text(&mut self, text: &str, root: u64) { fn set_text(&mut self, text: &str, root: u64) {
@@ -489,8 +484,9 @@ unsafe impl Sync for DioxusWebsysEvent {}
// todo: some of these events are being casted to the wrong event type. // todo: some of these events are being casted to the wrong event type.
// We need tests that simulate clicks/etc and make sure every event type works. // We need tests that simulate clicks/etc and make sure every event type works.
fn virtual_event_from_websys_event(event: web_sys::Event) -> Box<dyn Any + Send> { fn virtual_event_from_websys_event(event: web_sys::Event) -> Box<dyn Any + Send> {
use crate::events::*; use dioxus_html::on::*;
use dioxus_core::events::on::*; use dioxus_html::KeyCode;
// use dioxus_core::events::on::*;
match event.type_().as_str() { match event.type_().as_str() {
"copy" | "cut" | "paste" => Box::new(ClipboardEvent {}), "copy" | "cut" | "paste" => Box::new(ClipboardEvent {}),
"compositionend" | "compositionstart" | "compositionupdate" => { "compositionend" | "compositionstart" | "compositionupdate" => {
@@ -682,15 +678,6 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<UserEvent> {
let typ = event.type_(); let typ = event.type_();
// TODO: clean this up
if cfg!(debug_assertions) {
let attrs = target.attributes();
for x in 0..attrs.length() {
let attr: Attr = attrs.item(x).unwrap();
// log::debug!("attrs include: {:#?}, {:#?}", attr.name(), attr.value());
}
}
use anyhow::Context; use anyhow::Context;
// The error handling here is not very descriptive and needs to be replaced with a zero-cost error system // The error handling here is not very descriptive and needs to be replaced with a zero-cost error system
@@ -716,7 +703,8 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<UserEvent> {
name: event_name_from_typ(&typ), name: event_name_from_typ(&typ),
event: virtual_event_from_websys_event(event.clone()), event: virtual_event_from_websys_event(event.clone()),
mounted_dom_id: Some(ElementId(real_id as usize)), mounted_dom_id: Some(ElementId(real_id as usize)),
scope: ScopeId(triggered_scope as usize), scope_id: ScopeId(triggered_scope as usize),
priority: dioxus_core::EventPriority::Medium,
}) })
} }
-5
View File
@@ -1,5 +0,0 @@
//! Ported events into Dioxus Synthetic Event system
use dioxus_core::events::on::*;
use wasm_bindgen::JsCast;
use web_sys::{Event, UiEvent};
+6 -7
View File
@@ -57,8 +57,8 @@ use std::rc::Rc;
pub use crate::cfg::WebConfig; pub use crate::cfg::WebConfig;
use crate::dom::load_document; use crate::dom::load_document;
use cache::intern_cached_strings; use cache::intern_cached_strings;
use dioxus::prelude::Properties; use dioxus::SchedulerMsg;
use dioxus::virtual_dom::VirtualDom; use dioxus::VirtualDom;
pub use dioxus_core as dioxus; pub use dioxus_core as dioxus;
use dioxus_core::prelude::FC; use dioxus_core::prelude::FC;
use futures_util::FutureExt; use futures_util::FutureExt;
@@ -66,7 +66,6 @@ use futures_util::FutureExt;
mod cache; mod cache;
mod cfg; mod cfg;
mod dom; mod dom;
mod events;
mod nodeslab; mod nodeslab;
mod ric_raf; mod ric_raf;
@@ -144,9 +143,10 @@ pub async fn run_with_props<T: 'static + Send>(root: FC<T>, root_props: T, cfg:
let root_el = load_document().get_element_by_id(&cfg.rootname).unwrap(); let root_el = load_document().get_element_by_id(&cfg.rootname).unwrap();
let tasks = dom.get_event_sender(); let tasks = dom.get_scheduler_channel();
let sender_callback = Rc::new(move |event| tasks.unbounded_send(event).unwrap()); let sender_callback: Rc<dyn Fn(SchedulerMsg)> =
Rc::new(move |event| tasks.unbounded_send(event).unwrap());
let mut websys_dom = dom::WebsysDom::new(root_el, cfg, sender_callback); let mut websys_dom = dom::WebsysDom::new(root_el, cfg, sender_callback);
@@ -170,13 +170,12 @@ pub async fn run_with_props<T: 'static + Send>(root: FC<T>, root_props: T, cfg:
let mut deadline = work_loop.wait_for_idle_time().await; let mut deadline = work_loop.wait_for_idle_time().await;
// run the virtualdom work phase until the frame deadline is reached // run the virtualdom work phase until the frame deadline is reached
let mutations = dom.run_with_deadline(|| (&mut deadline).now_or_never().is_some()); let mutations = dom.work_with_deadline(|| (&mut deadline).now_or_never().is_some());
// wait for the animation frame to fire so we can apply our changes // wait for the animation frame to fire so we can apply our changes
work_loop.wait_for_raf().await; work_loop.wait_for_raf().await;
for mut edit in mutations { for mut edit in mutations {
// log::debug!("edits are {:#?}", edit);
// actually apply our changes during the animation frame // actually apply our changes during the animation frame
websys_dom.process_edits(&mut edit.edits); websys_dom.process_edits(&mut edit.edits);
} }