diff --git a/Cargo.toml b/Cargo.toml index 44750d463..01d8c0370 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,18 +11,19 @@ documentation = "https://dioxuslabs.com" keywords = ["dom", "ui", "gui", "react", "wasm"] [dependencies] -dioxus-core = { path = "./packages/core", version = "^0.1.3" } -dioxus-html = { path = "./packages/html", optional = true } -dioxus-core-macro = { path = "./packages/core-macro", optional = true } +dioxus-core = { path = "./packages/core", version = "^0.1.4" } +dioxus-html = { path = "./packages/html", version = "^0.1.1", optional = true } +dioxus-core-macro = { path = "./packages/core-macro", version = "^0.1.3", optional = true } dioxus-hooks = { path = "./packages/hooks", optional = true } -dioxus-ssr = { path = "./packages/ssr", optional = true } -dioxus-web = { path = "./packages/web", optional = true } +dioxus-web = { path = "./packages/web", version = "^0.0.1", optional = true } dioxus-desktop = { path = "./packages/desktop", optional = true } +dioxus-ssr = { path = "./packages/ssr", optional = true } + dioxus-router = { path = "./packages/router", optional = true } -dioxus-mobile = { path = "./packages/mobile", optional = true } -dioxus-liveview = { path = "./packages/liveview", optional = true } +# dioxus-mobile = { path = "./packages/mobile", optional = true } +# dioxus-liveview = { path = "./packages/liveview", optional = true } [features] default = ["macro", "hooks", "html"] @@ -31,11 +32,13 @@ macro = ["dioxus-core-macro"] hooks = ["dioxus-hooks"] html = ["dioxus-html"] router = ["dioxus-router"] -liveview = ["dioxus-liveview"] ssr = ["dioxus-ssr"] web = ["dioxus-web", "dioxus-router/web"] desktop = ["dioxus-desktop", "dioxus-router/desktop"] -mobile = ["dioxus-mobile"] + + +# mobile = ["dioxus-mobile"] +# liveview = ["dioxus-liveview"] [workspace] @@ -60,7 +63,6 @@ im-rc = "15.0.0" fxhash = "0.2.1" anyhow = "1.0.51" serde_json = "1.0.73" -simple_logger = "1.16.0" [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] argh = "0.1.7" diff --git a/examples/README.md b/examples/README.md index eabb0f57d..eb0037683 100644 --- a/examples/README.md +++ b/examples/README.md @@ -64,7 +64,7 @@ Here's what a few common tasks look like in Dioxus: Nested components with children and internal state: ```rust -fn App(cx: Scope<()>) -> Element { +fn App(cx: Scope) -> Element { cx.render(rsx!( Toggle { "Toggle me" } )) } @@ -86,7 +86,7 @@ fn Toggle(cx: Scope) -> Element { Controlled inputs: ```rust -fn App(cx: Scope<()>) -> Element { +fn App(cx: Scope) -> Element { let value = use_state(&cx, String::new); cx.render(rsx!( input { @@ -100,7 +100,7 @@ fn App(cx: Scope<()>) -> Element { Lists and Conditional rendering: ```rust -fn App(cx: Scope<()>) -> Element { +fn App(cx: Scope) -> Element { let list = (0..10).map(|i| { rsx!(li { key: "{i}", "Value: {i}" }) }); @@ -128,7 +128,7 @@ static App: Component = |cx, _| rsx!(cx, div {"hello world!"}); Borrowed prop contents: ```rust -fn App(cx: Scope<()>) -> Element { +fn App(cx: Scope) -> Element { let name = use_state(&cx, || String::from("example")); rsx!(cx, Child { title: name.as_str() }) } @@ -145,12 +145,12 @@ Global State ```rust struct GlobalState { name: String } -fn App(cx: Scope<()>) -> Element { +fn App(cx: Scope) -> Element { use_provide_shared_state(cx, || GlobalState { name: String::from("Toby") }) rsx!(cx, Leaf {}) } -fn Leaf(cx: Scope<()>) -> Element { +fn Leaf(cx: Scope) -> Element { let state = use_consume_shared_state::(cx)?; rsx!(cx, "Hello {state.name}") } @@ -166,7 +166,7 @@ enum Route { Post(id) } -fn App(cx: Scope<()>) -> Element { +fn App(cx: Scope) -> Element { let route = use_router(cx, Route::parse); cx.render(rsx!(div { {match route { @@ -179,7 +179,7 @@ fn App(cx: Scope<()>) -> Element { Suspense ```rust -fn App(cx: Scope<()>) -> Element { +fn App(cx: Scope) -> Element { let doggo = use_suspense(cx, || async { reqwest::get("https://dog.ceo/api/breeds/image/random").await.unwrap().json::().await.unwrap() }, |response| cx.render(rsx!( img { src: "{response.message}" })) diff --git a/examples/async.rs b/examples/async.rs index c473ead72..e399ea016 100644 --- a/examples/async.rs +++ b/examples/async.rs @@ -8,19 +8,19 @@ use gloo_timers::future::TimeoutFuture; #[tokio::main] async fn main() { - dioxus::desktop::launch(App); + dioxus::desktop::launch(app); } -pub static App: Component = |cx| { +fn app(cx: Scope) -> Element { let count = use_state(&cx, || 0); - let mut direction = use_state(&cx, || 1); + let direction = use_state(&cx, || 1); let (async_count, dir) = (count.for_async(), *direction); let task = use_coroutine(&cx, move || async move { loop { TimeoutFuture::new(250).await; - *async_count.get_mut() += dir; + *async_count.modify() += dir; } }); @@ -37,9 +37,9 @@ pub static App: Component = |cx| { button { "Switch counting direcion" onclick: move |_| { - direction *= -1; + *direction.modify() *= -1; task.restart(); } } }) -}; +} diff --git a/examples/borrowed.rs b/examples/borrowed.rs index 3b05b6f9f..29c502e13 100644 --- a/examples/borrowed.rs +++ b/examples/borrowed.rs @@ -20,7 +20,7 @@ fn main() { dioxus::desktop::launch(App); } -fn App(cx: Scope<()>) -> Element { +fn App(cx: Scope) -> Element { let text: &mut Vec = cx.use_hook(|_| vec![String::from("abc=def")], |f| f); let first = text.get_mut(0).unwrap(); diff --git a/examples/calculator.rs b/examples/calculator.rs index c7490a1b4..c3814c088 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -1,3 +1,5 @@ +#![allow(non_snake_case)] + /* This example is a simple iOS-style calculator. This particular example can run any platform - Web, Mobile, Desktop. This calculator version uses React-style state management. All state is held as individual use_states. @@ -10,10 +12,10 @@ use dioxus::prelude::*; use separator::Separatable; fn main() { - dioxus::desktop::launch(APP); + dioxus::desktop::launch(app); } -static APP: Component = |cx| { +fn app(cx: Scope) -> Element { let cur_val = use_state(&cx, || 0.0_f64); let operator = use_state(&cx, || None as Option<&'static str>); let display_value = use_state(&cx, || String::from("")); @@ -21,109 +23,111 @@ static APP: Component = |cx| { let toggle_percent = move |_| todo!(); let input_digit = move |num: u8| display_value.modify().push_str(num.to_string().as_str()); - rsx!(cx, div { - class: "calculator", - onkeydown: move |evt| match evt.key_code { - KeyCode::Add => operator.set(Some("+")), - KeyCode::Subtract => operator.set(Some("-")), - KeyCode::Divide => operator.set(Some("/")), - KeyCode::Multiply => operator.set(Some("*")), - KeyCode::Num0 => input_digit(0), - KeyCode::Num1 => input_digit(1), - KeyCode::Num2 => input_digit(2), - KeyCode::Num3 => input_digit(3), - KeyCode::Num4 => input_digit(4), - KeyCode::Num5 => input_digit(5), - KeyCode::Num6 => input_digit(6), - KeyCode::Num7 => input_digit(7), - KeyCode::Num8 => input_digit(8), - KeyCode::Num9 => input_digit(9), - KeyCode::Backspace => { - if !display_value.as_str().eq("0") { - display_value.modify().pop(); - } - } - _ => {} - } - div { class: "calculator-display", {[format_args!("{}", cur_val.separated_string())]} } - div { class: "input-keys" - div { class: "function-keys" - CalculatorKey { - {[if display_value == "0" { "C" } else { "AC" }]} - name: "key-clear", - onclick: move |_| { - display_value.set("0".to_string()); - if display_value != "0" { - operator.set(None); - cur_val.set(0.0); - } + cx.render(rsx!( + div { class: "calculator", + onkeydown: move |evt| match evt.key_code { + KeyCode::Add => operator.set(Some("+")), + KeyCode::Subtract => operator.set(Some("-")), + KeyCode::Divide => operator.set(Some("/")), + KeyCode::Multiply => operator.set(Some("*")), + KeyCode::Num0 => input_digit(0), + KeyCode::Num1 => input_digit(1), + KeyCode::Num2 => input_digit(2), + KeyCode::Num3 => input_digit(3), + KeyCode::Num4 => input_digit(4), + KeyCode::Num5 => input_digit(5), + KeyCode::Num6 => input_digit(6), + KeyCode::Num7 => input_digit(7), + KeyCode::Num8 => input_digit(8), + KeyCode::Num9 => input_digit(9), + KeyCode::Backspace => { + if !display_value.as_str().eq("0") { + display_value.modify().pop(); } } - CalculatorKey { - "±" - name: "key-sign", - onclick: move |_| { - if display_value.starts_with("-") { - display_value.set(display_value.trim_start_matches("-").to_string()) - } else { - display_value.set(format!("-{}", *display_value)) - } - }, - } - CalculatorKey { - "%" - onclick: {toggle_percent} - name: "key-percent", - } + _ => {} } - div { class: "digit-keys" - CalculatorKey { name: "key-0", onclick: move |_| input_digit(0), "0" } - CalculatorKey { name: "key-dot", onclick: move |_| display_value.modify().push_str("."), "●" } + div { class: "calculator-display", {[cur_val.separated_string()]} } + div { class: "input-keys" + div { class: "function-keys" + CalculatorKey { + {[if display_value == "0" { "C" } else { "AC" }]} + name: "key-clear", + onclick: move |_| { + display_value.set("0".to_string()); + if display_value != "0" { + operator.set(None); + cur_val.set(0.0); + } + } + } + CalculatorKey { + "±" + name: "key-sign", + onclick: move |_| { + if display_value.starts_with("-") { + display_value.set(display_value.trim_start_matches("-").to_string()) + } else { + display_value.set(format!("-{}", *display_value)) + } + }, + } + CalculatorKey { + "%" + onclick: {toggle_percent} + name: "key-percent", + } + } + div { class: "digit-keys" + CalculatorKey { name: "key-0", onclick: move |_| input_digit(0), "0" } + CalculatorKey { name: "key-dot", onclick: move |_| display_value.modify().push_str("."), "●" } - {(1..9).map(|k| rsx!{ - CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_| input_digit(k), "{k}" } - })} - } - div { class: "operator-keys" - CalculatorKey { name: "key-divide", onclick: move |_| operator.set(Some("/")) "÷" } - CalculatorKey { name: "key-multiply", onclick: move |_| operator.set(Some("*")) "×" } - CalculatorKey { name: "key-subtract", onclick: move |_| operator.set(Some("-")) "−" } - CalculatorKey { name: "key-add", onclick: move |_| operator.set(Some("+")) "+" } - CalculatorKey { - "=" - name: "key-equals", - onclick: move |_| { - if let Some(op) = operator.as_ref() { - let rhs = display_value.parse::().unwrap(); - let new_val = match *op { - "+" => *cur_val + rhs, - "-" => *cur_val - rhs, - "*" => *cur_val * rhs, - "/" => *cur_val / rhs, - _ => unreachable!(), - }; - cur_val.set(new_val); - display_value.set(new_val.to_string()); - operator.set(None); - } - }, + {(1..9).map(|k| rsx!{ + CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_| input_digit(k), "{k}" } + })} + } + div { class: "operator-keys" + CalculatorKey { name: "key-divide", onclick: move |_| operator.set(Some("/")) "÷" } + CalculatorKey { name: "key-multiply", onclick: move |_| operator.set(Some("*")) "×" } + CalculatorKey { name: "key-subtract", onclick: move |_| operator.set(Some("-")) "−" } + CalculatorKey { name: "key-add", onclick: move |_| operator.set(Some("+")) "+" } + CalculatorKey { + "=" + name: "key-equals", + onclick: move |_| { + if let Some(op) = operator.as_ref() { + let rhs = display_value.parse::().unwrap(); + let new_val = match *op { + "+" => *cur_val + rhs, + "-" => *cur_val - rhs, + "*" => *cur_val * rhs, + "/" => *cur_val / rhs, + _ => unreachable!(), + }; + cur_val.set(new_val); + display_value.set(new_val.to_string()); + operator.set(None); + } + }, + } } } } - }) -}; + )) +} -#[derive(Props)] -struct CalculatorKeyProps<'a> { +#[inline_props] +fn CalculatorKey<'a>( + cx: Scope, name: &'static str, onclick: &'a dyn Fn(Arc), children: Element<'a>, -} - -fn CalculatorKey<'a>(cx: Scope<'a, CalculatorKeyProps<'a>>) -> Element { - rsx!(cx, button { - class: "calculator-key {cx.props.name}" - onclick: {cx.props.onclick} - {&cx.props.children} +) -> Element { + cx.render(rsx! { + button { + class: "calculator-key {name}" + onclick: {onclick} + {children} + } }) } diff --git a/examples/core/jsframework.rs b/examples/core/jsframework.rs index 15c25ec8d..f9fa9c431 100644 --- a/examples/core/jsframework.rs +++ b/examples/core/jsframework.rs @@ -15,7 +15,7 @@ fn main() { assert!(g.edits.len() > 1); } -fn App((cx, props): Scope<()>) -> 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); diff --git a/examples/desktop/todomvc.rs b/examples/desktop/todomvc.rs index ff982a585..e70ed1806 100644 --- a/examples/desktop/todomvc.rs +++ b/examples/desktop/todomvc.rs @@ -5,15 +5,10 @@ use dioxus_core::prelude::*; use dioxus_core_macro::*; use dioxus_hooks::*; use dioxus_html as dioxus_elements; -use simple_logger::SimpleLogger; use std::collections::HashMap; fn main() { - if cfg!(debug_assertions) { - SimpleLogger::new().init().unwrap(); - } - dioxus_desktop::launch(App) } diff --git a/examples/file_explorer.rs b/examples/file_explorer.rs index a2f168d6b..05f5c4ca7 100644 --- a/examples/file_explorer.rs +++ b/examples/file_explorer.rs @@ -8,7 +8,6 @@ use dioxus::prelude::*; fn main() { - // simple_logger::init_with_level(log::Level::Debug); dioxus::desktop::launch_cfg(App, |c| { c.with_window(|w| { w.with_resizable(true).with_inner_size( diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 24f54883a..ab7bfffa3 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -4,7 +4,7 @@ fn main() { dioxus::desktop::launch(app); } -fn app(cx: Scope<()>) -> Element { +fn app(cx: Scope) -> Element { cx.render(rsx! ( div { "Hello, world!" } )) diff --git a/examples/readme.rs b/examples/readme.rs index c3f0a3de7..9f0a288e3 100644 --- a/examples/readme.rs +++ b/examples/readme.rs @@ -3,18 +3,19 @@ //! The example from the README.md. use dioxus::prelude::*; + fn main() { dioxus::desktop::launch(App); } static App: Component = |cx| { - let mut count = use_state(&cx, || 0); + let count = use_state(&cx, || 0); cx.render(rsx! { div { h1 { "High-Five counter: {count}" } - button { onclick: move |_| count += 1, "Up high!" } - button { onclick: move |_| count -= 1, "Down low!" } + button { onclick: move |_| *count.modify() += 1, "Up high!" } + button { onclick: move |_| *count.modify() -= 1, "Down low!" } } }) }; diff --git a/examples/tasks.rs b/examples/tasks.rs index 0db459786..a378c24a6 100644 --- a/examples/tasks.rs +++ b/examples/tasks.rs @@ -2,26 +2,30 @@ //! //! The example from the README.md. +use dioxus::prelude::*; use std::time::Duration; -use dioxus::prelude::*; fn main() { dioxus::desktop::launch(app); } -fn app(cx: Scope<()>) -> Element { - let mut count = use_state(&cx, || 0); +fn app(cx: Scope) -> Element { + let count = use_state(&cx, || 0); - cx.push_future(|| async move { - tokio::time::sleep(Duration::from_millis(100)).await; - count += 1; + use_future(&cx, || { + for_async![count]; + async move { + while let _ = tokio::time::sleep(Duration::from_millis(1000)).await { + *count.modify() += 1; + } + } }); cx.render(rsx! { div { h1 { "High-Five counter: {count}" } button { - onclick: move |_| count +=1 , + onclick: move |_| *count.modify() += 1, "Click me!" } } diff --git a/examples/webview_web.rs b/examples/webview_web.rs index 367094b2c..5a7df845d 100644 --- a/examples/webview_web.rs +++ b/examples/webview_web.rs @@ -1,4 +1,3 @@ -#![allow(non_upper_case_globals, non_snake_case)] //! Example: Webview Renderer //! ------------------------- //! @@ -13,17 +12,17 @@ use dioxus::prelude::*; fn main() { - dioxus::web::launch(App); + dioxus::desktop::launch(app); } -static App: Component = |cx| { - let mut count = use_state(&cx, || 0); +fn app(cx: Scope) -> Element { + let count = use_state(&cx, || 0); cx.render(rsx! { div { h1 { "Hifive counter: {count}" } - button { onclick: move |_| count += 1, "Up high!" } - button { onclick: move |_| count -= 1, "Down low!" } + button { onclick: move |_| *count.modify() += 1, "Up high!" } + button { onclick: move |_| *count.modify() -= 1, "Down low!" } } }) -}; +} diff --git a/packages/core-macro/src/lib.rs b/packages/core-macro/src/lib.rs index a196441b8..2d17f4bbc 100644 --- a/packages/core-macro/src/lib.rs +++ b/packages/core-macro/src/lib.rs @@ -161,7 +161,7 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token /// pub struct BallerProps {} /// /// /// This component totally balls -/// pub fn Baller(cx: Scope<()>) -> DomTree { +/// pub fn Baller(cx: Scope) -> DomTree { /// todo!() /// } /// } diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index cd96fca04..53d169760 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -50,7 +50,6 @@ anyhow = "1.0.42" dioxus-html = { path = "../html" } fern = { version = "0.6.0", features = ["colored"] } rand = { version = "0.8.4", features = ["small_rng"] } -simple_logger = "1.13.0" dioxus-core-macro = { path = "../core-macro", version = "^0.1.2" } criterion = "0.3.5" thiserror = "1.0.30" diff --git a/packages/core/README.md b/packages/core/README.md index 672a1dc78..87d724415 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -6,7 +6,7 @@ To build new apps with Dioxus or to extend the ecosystem with new hooks or compo ```rust -fn app(cx: Scope<()>) -> Element { +fn app(cx: Scope) -> Element { rsx!(cx, div { "hello world" }) } diff --git a/packages/core/examples/component_children.rs b/packages/core/examples/component_children.rs index b6bbf8a86..73d0750a6 100644 --- a/packages/core/examples/component_children.rs +++ b/packages/core/examples/component_children.rs @@ -11,7 +11,7 @@ fn main() { dbg!(edits); } -fn parent(cx: Scope<()>) -> Element { +fn parent(cx: Scope) -> Element { let value = cx.use_hook(|_| String::new(), |f| f); cx.render(rsx! { diff --git a/packages/core/examples/works.rs b/packages/core/examples/works.rs index f521dcc42..d108b6d07 100644 --- a/packages/core/examples/works.rs +++ b/packages/core/examples/works.rs @@ -9,7 +9,7 @@ fn main() { let _ = VirtualDom::new(parent); } -fn parent(cx: Scope<()>) -> Element { +fn parent(cx: Scope) -> Element { let value = cx.use_hook(|_| String::new(), |f| f); cx.render(rsx! { diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index fc0e38f59..2ebd1e250 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -84,7 +84,7 @@ pub enum VNode<'src> { /// # Example /// /// ```rust, ignore - /// fn Example(cx: Scope<()>) -> Element { + /// fn Example(cx: Scope) -> Element { /// ... /// } /// @@ -740,6 +740,12 @@ impl IntoVNode<'_> for &'static str { } } +impl IntoVNode<'_> for String { + fn into_vnode(self, cx: NodeFactory) -> VNode { + cx.text(format_args!("{}", self)) + } +} + impl IntoVNode<'_> for Arguments<'_> { fn into_vnode(self, cx: NodeFactory) -> VNode { cx.text(self) @@ -758,3 +764,7 @@ impl<'a> IntoVNode<'a> for &VNode<'a> { self.decouple() } } + +trait IntoAcceptedVnode<'a> { + fn into_accepted_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>; +} diff --git a/packages/core/src/scopes.rs b/packages/core/src/scopes.rs index 42e16d223..5d35b298d 100644 --- a/packages/core/src/scopes.rs +++ b/packages/core/src/scopes.rs @@ -490,7 +490,7 @@ impl ScopeState { /// # Example /// /// ```rust, ignore - /// fn App(cx: Scope<()>) -> Element { + /// fn App(cx: Scope) -> Element { /// rsx!(cx, div { "Subtree {id}"}) /// }; /// ``` diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 09df37bb8..e96527e95 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -80,7 +80,7 @@ use std::{any::Any, collections::VecDeque, iter::FromIterator, pin::Pin, sync::A /// Putting everything together, you can build an event loop around Dioxus by using the methods outlined above. /// /// ```rust, ignore -/// fn App(cx: Scope<()>) -> Element { +/// fn App(cx: Scope) -> Element { /// cx.render(rsx!{ /// div { "Hello World" } /// }) @@ -127,7 +127,7 @@ impl VirtualDom { /// /// # Example /// ```rust, ignore - /// fn Example(cx: Scope<()>) -> Element { + /// fn Example(cx: Scope) -> Element { /// cx.render(rsx!( div { "hello world" } )) /// } /// @@ -388,7 +388,7 @@ impl VirtualDom { /// # Example /// /// ```rust, ignore - /// fn App(cx: Scope<()>) -> Element { + /// fn App(cx: Scope) -> Element { /// cx.render(rsx!( div {"hello"} )) /// } /// @@ -544,7 +544,7 @@ impl VirtualDom { /// Useful when needing to render nodes from outside the VirtualDom, such as in a test. /// /// ```rust - /// fn Base(cx: Scope<()>) -> Element { + /// fn Base(cx: Scope) -> Element { /// rsx!(cx, div {}) /// } /// @@ -564,7 +564,7 @@ impl VirtualDom { /// Useful when needing to render nodes from outside the VirtualDom, such as in a test. /// /// ```rust - /// fn Base(cx: Scope<()>) -> Element { + /// fn Base(cx: Scope) -> Element { /// rsx!(cx, div {}) /// } /// @@ -586,7 +586,7 @@ impl VirtualDom { /// /// /// ```rust - /// fn Base(cx: Scope<()>) -> Element { + /// fn Base(cx: Scope) -> Element { /// rsx!(cx, div {}) /// } /// @@ -609,7 +609,7 @@ impl VirtualDom { /// /// /// ```rust - /// fn Base(cx: Scope<()>) -> Element { + /// fn Base(cx: Scope) -> Element { /// rsx!(cx, div {}) /// } /// @@ -753,7 +753,7 @@ pub enum SchedulerMsg { /// /// # Example /// ```rust -/// fn App(cx: Scope<()>) -> Element { +/// fn App(cx: Scope) -> Element { /// rsx!(cx, div { /// onclick: move |_| println!("Clicked!") /// }) @@ -871,7 +871,7 @@ impl<'a, const A: bool> FragmentBuilder<'a, A> { /// ## Example /// /// ```rust, ignore -/// fn App(cx: Scope<()>) -> Element { +/// fn App(cx: Scope) -> Element { /// cx.render(rsx!{ /// CustomCard { /// h1 {}2 diff --git a/packages/core/tests/borrowedstate.rs b/packages/core/tests/borrowedstate.rs index f58e97635..c143c2fe1 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: Scope<()>) -> Element { +fn Parent(cx: Scope) -> Element { let value = cx.use_hook(|_| String::new(), |f| &*f); cx.render(rsx! { diff --git a/packages/core/tests/miri_stress.rs b/packages/core/tests/miri_stress.rs index 2091a8e3a..ebf38c7c1 100644 --- a/packages/core/tests/miri_stress.rs +++ b/packages/core/tests/miri_stress.rs @@ -28,7 +28,7 @@ fn new_dom(app: Component

, props: P) -> VirtualDom { /// In debug, this should also toss a warning. #[test] fn test_memory_leak() { - fn app(cx: Scope<()>) -> Element { + fn app(cx: Scope) -> Element { let val = cx.use_hook(|_| 0, |f| f); *val += 1; @@ -68,7 +68,7 @@ fn test_memory_leak() { }) } - fn child(cx: Scope<()>) -> Element { + fn child(cx: Scope) -> Element { rsx!(cx, div { "goodbye world" }) } @@ -85,7 +85,7 @@ fn test_memory_leak() { #[test] fn memo_works_properly() { - fn app(cx: Scope<()>) -> Element { + fn app(cx: Scope) -> Element { let val = cx.use_hook(|_| 0, |f| f); *val += 1; @@ -164,7 +164,7 @@ fn free_works_on_root_props() { #[test] fn free_works_on_borrowed() { - fn app(cx: Scope<()>) -> Element { + fn app(cx: Scope) -> Element { cx.render(rsx! { child(a: "alpha", b: "asd".to_string()) }) @@ -203,7 +203,7 @@ fn free_works_on_root_hooks() { } } - fn app(cx: Scope<()>) -> Element { + fn app(cx: Scope) -> Element { let name = cx.use_hook(|_| Droppable(String::from("asd")), |f| f); rsx!(cx, div { "{name.0}" }) } @@ -214,7 +214,7 @@ fn free_works_on_root_hooks() { #[test] fn old_props_arent_stale() { - fn app(cx: Scope<()>) -> Element { + fn app(cx: Scope) -> Element { dbg!("rendering parent"); let cnt = cx.use_hook(|_| 0, |f| f); *cnt += 1; @@ -261,7 +261,7 @@ fn old_props_arent_stale() { #[test] fn basic() { - fn app(cx: Scope<()>) -> Element { + fn app(cx: Scope) -> Element { rsx!(cx, div { child(a: "abcdef".to_string()) }) diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index a132bc513..d088aa04c 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -37,4 +37,3 @@ tokio_runtime = ["tokio"] [dev-dependencies] dioxus-hooks = { path = "../hooks" } -simple_logger = "1.13.0" diff --git a/packages/desktop/examples/async.rs b/packages/desktop/examples/async.rs index 2c6901f85..056e7b591 100644 --- a/packages/desktop/examples/async.rs +++ b/packages/desktop/examples/async.rs @@ -9,22 +9,18 @@ fn main() { dioxus_desktop::launch(app); } -fn app(cx: Scope<()>) -> Element { +fn app(cx: Scope) -> Element { let count = use_state(&cx, || 0); - // push the futureo on initialization - cx.use_hook( - |_| { - cx.push_future({ - let count = count.for_async(); - async move { - tokio::time::sleep(Duration::from_millis(1000)).await; - *count.get_mut() += 1; - } - }); - }, - |_| {}, - ); + use_future(&cx, || { + let count = count.for_async(); + async move { + loop { + tokio::time::sleep(Duration::from_millis(1000)).await; + *count.modify() += 1; + } + } + }); cx.render(rsx! { div { diff --git a/packages/hooks/README.md b/packages/hooks/README.md index 11e008c44..18133e40b 100644 --- a/packages/hooks/README.md +++ b/packages/hooks/README.md @@ -4,58 +4,19 @@ This crate includes some basic useful hooks for dioxus: - use_state - use_ref -- use_collection -- use_task -- use_signal +- use_future +- use_coroutine ## use_state -The king daddy of state hooks. +The primary mechanism of stored state. You can always use it "normally" with the `split` method: ```rust -// Normal usage: +// Rusty-smart-pointer usage: let value = use_state(&cx, || 10); // "Classic" usage: let (value, set_value) = use_state(&cx, || 0).split(); ``` - -## use_ref - - -## use_rwlock -A multithreaded form of RwLock for use in tasks -```rust -let val = use_rwlock(cx, || 10); -use_task((), || async loop { - *val.write().unwrap() += 1; - async_std::task::delay(Duration::from_ms(1000)).await; -}); -use_task((), || async loop { - *val.write().unwrap() -= 1; - async_std::task::delay(Duration::from_ms(500)).await; -}); -``` - -## use_hashmap -Store a memoized collection with similar semantics to use_state. Comes with a bunch of utility methods to make working with collections easier. Is essentially a wrapper over the immutable hashmap in im-rc. - -```rust -let todos = use_hashmap(cx, |map| map.insert("bob", "bill")); -cx.render(rsx!( - button { onclick: move |_| todos.insert("bob", "bill") - "add random todo" - } -) - -``` - -## use_task - -use_task submits a task to the dioxus task queue to be progressed during Dioxus's async event loop. The task must not return anything - - -## use_signal - diff --git a/packages/hooks/src/lib.rs b/packages/hooks/src/lib.rs index 1a38ed172..0050d1779 100644 --- a/packages/hooks/src/lib.rs +++ b/packages/hooks/src/lib.rs @@ -1,5 +1,5 @@ mod usestate; -pub use usestate::{use_state, AsyncUseState, UseState}; +pub use usestate::{use_state, UseState}; mod useref; pub use useref::*; @@ -16,5 +16,45 @@ pub use usefuture::*; mod usesuspense; pub use usesuspense::*; -// mod usemodel; -// pub use usemodel::*; +#[macro_export] +macro_rules! to_owned { + ($($es:ident),+) => {$( + #[allow(unused_mut)] + let mut $es = $es.to_owned(); + )*} +} + +/// Calls `for_async` on the series of paramters. +/// +/// If the type is Clone, then it will be cloned. However, if the type is not `clone` +/// then it must have a `for_async` method for Rust to lower down into. +/// +/// See: how use_state implements `for_async` but *not* through the trait. +#[macro_export] +macro_rules! for_async { + ($($es:ident),+) => {$( + #[allow(unused_mut)] + let mut $es = $es.for_async(); + )*} +} + +/// This is a marker trait that uses decoherence. +/// +/// It is *not* meant for hooks to actually implement, but rather defer to their +/// underlying implementation if they *don't* implement the trait. +/// +/// +pub trait AsyncHook { + type Output; + fn for_async(self) -> Self::Output; +} + +impl AsyncHook for T +where + T: ToOwned, +{ + type Output = T; + fn for_async(self) -> Self::Output { + self + } +} diff --git a/packages/hooks/src/usestate.rs b/packages/hooks/src/usestate.rs index 88caea82d..6ef67efcb 100644 --- a/packages/hooks/src/usestate.rs +++ b/packages/hooks/src/usestate.rs @@ -2,20 +2,9 @@ use dioxus_core::prelude::*; use std::{ cell::{Cell, Ref, RefCell, RefMut}, fmt::{Debug, Display}, - ops::Not, rc::Rc, }; -pub trait UseStateA<'a, T> { - fn use_state(&self, initial_state_fn: impl FnOnce() -> T) -> UseState<'a, T>; -} - -impl<'a, P, T> UseStateA<'a, T> for Scope<'a, P> { - fn use_state(&self, initial_state_fn: impl FnOnce() -> T) -> UseState<'a, T> { - use_state(self.scope, initial_state_fn) - } -} - /// Store state between component renders! /// /// ## Dioxus equivalent of useState, designed for Rust @@ -109,6 +98,7 @@ where UseState { inner: self.inner } } } + impl Debug for UseState<'_, T> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self.inner.current_val) @@ -157,21 +147,21 @@ impl<'a, T: 'static> UseState<'a, T> { }) } - pub fn for_async(&self) -> AsyncUseState { - AsyncUseState { - re_render: self.inner.update_callback.clone(), - wip: self.inner.wip.clone(), - inner: self.inner.current_val.clone(), - } + pub fn for_async(self) -> UseState<'static, T> { + todo!() } - pub fn split_for_async(&'a self) -> (&'a Self, AsyncUseState) { - (self, self.for_async()) + pub fn wtih(self, f: impl FnOnce(&mut T)) { + let mut val = self.inner.wip.borrow_mut(); + + if let Some(inner) = val.as_mut() { + f(inner); + } } } impl<'a, T: 'static + ToOwned> UseState<'a, T> { - /// Gain mutable access to the new value via RefMut. + /// Gain mutable access to the new value via [`RefMut`]. /// /// If `modify` is called, then the component will re-render. /// @@ -207,57 +197,10 @@ impl<'a, T> std::ops::Deref for UseState<'a, T> { } } -use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; - -impl<'a, T: Copy + Add> Add for UseState<'a, T> { - type Output = T; - - fn add(self, rhs: T) -> Self::Output { - self.inner.current_val.add(rhs) - } -} -impl<'a, T: Copy + Add> AddAssign for UseState<'a, T> { - fn add_assign(&mut self, rhs: T) { - self.set(self.inner.current_val.add(rhs)); - } -} -impl<'a, T: Copy + Sub> Sub for UseState<'a, T> { - type Output = T; - - fn sub(self, rhs: T) -> Self::Output { - self.inner.current_val.sub(rhs) - } -} -impl<'a, T: Copy + Sub> SubAssign for UseState<'a, T> { - fn sub_assign(&mut self, rhs: T) { - self.set(self.inner.current_val.sub(rhs)); - } -} - -/// MUL -impl<'a, T: Copy + Mul> Mul for UseState<'a, T> { - type Output = T; - - fn mul(self, rhs: T) -> Self::Output { - self.inner.current_val.mul(rhs) - } -} -impl<'a, T: Copy + Mul> MulAssign for UseState<'a, T> { - fn mul_assign(&mut self, rhs: T) { - self.set(self.inner.current_val.mul(rhs)); - } -} -/// DIV -impl<'a, T: Copy + Div> Div for UseState<'a, T> { - type Output = T; - - fn div(self, rhs: T) -> Self::Output { - self.inner.current_val.div(rhs) - } -} -impl<'a, T: Copy + Div> DivAssign for UseState<'a, T> { - fn div_assign(&mut self, rhs: T) { - self.set(self.inner.current_val.div(rhs)); +// enable displaty for the handle +impl<'a, T: 'static + Display> std::fmt::Display for UseState<'a, T> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.inner.current_val) } } impl<'a, V, T: PartialEq> PartialEq for UseState<'a, T> { @@ -265,57 +208,10 @@ impl<'a, V, T: PartialEq> PartialEq for UseState<'a, T> { self.get() == other } } -impl<'a, O, T: Not + Copy> Not for UseState<'a, T> { +impl<'a, O, T: std::ops::Not + Copy> std::ops::Not for UseState<'a, T> { type Output = O; fn not(self) -> Self::Output { !*self.get() } } - -// enable displaty for the handle -impl<'a, T: 'static + Display> std::fmt::Display for UseState<'a, T> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.inner.current_val) - } -} - -/// A less ergonmic but still capable form of use_state that's valid for `static lifetime -pub struct AsyncUseState { - inner: Rc, - re_render: Rc, - wip: Rc>>, -} - -impl> AsyncUseState { - pub fn get_mut<'a>(&'a self) -> RefMut<'a, T> { - // make sure we get processed - { - let mut wip = self.wip.borrow_mut(); - if wip.is_none() { - *wip = Some(self.inner.as_ref().to_owned()); - } - (self.re_render)(); - } - // Bring out the new value, cloning if it we need to - // "get_mut" is locked behind "ToOwned" to make it explicit that cloning occurs to use this - RefMut::map(self.wip.borrow_mut(), |slot| { - // - slot.as_mut().unwrap() - }) - } -} -impl AsyncUseState { - pub fn set(&mut self, val: T) { - (self.re_render)(); - *self.wip.borrow_mut() = Some(val); - } - - // pub fn get(&self) -> Ref<'_, T> { - // self.wip.borrow - // } - - pub fn get_rc(&self) -> &Rc { - &self.inner - } -} diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index b00ae38f4..755508d58 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -9,14 +9,12 @@ repository = "https://github.com/DioxusLabs/dioxus/" homepage = "https://dioxuslabs.com" documentation = "https://docs.rs/dioxus" keywords = ["dom", "ui", "gui", "react", "wasm"] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] dioxus-core = { path = "../core", version = "^0.1.4" } serde = { version = "1", features = ["derive"], optional = true } serde_repr = { version = "0.1", optional = true } - [features] default = [] serialize = ["serde", "serde_repr"] diff --git a/packages/liveview/README.md b/packages/liveview/README.md index 5bbd8b75a..1d708eab2 100644 --- a/packages/liveview/README.md +++ b/packages/liveview/README.md @@ -39,7 +39,7 @@ async fn order_shoes(mut req: WebsocketRequest) -> Response { dioxus::liveview::launch(App, stream).await; } -fn App(cx: Scope<()>) -> Element { +fn App(cx: Scope) -> Element { let mut count = use_state(&cx, || 0); cx.render(rsx!( button { onclick: move |_| count += 1, "Incr" } diff --git a/packages/mobile/Cargo.toml b/packages/mobile/Cargo.toml index ef05f2eee..22aefeac2 100644 --- a/packages/mobile/Cargo.toml +++ b/packages/mobile/Cargo.toml @@ -12,7 +12,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] [dependencies] anyhow = "1.0" # cacao = { git = "https://github.com/ryanmcgrath/cacao" } -dioxus-core = { path = "../core", version ="^0.1.3"} +dioxus-core = { path = "../core", version = "^0.1.3" } log = "0.4.14" serde = "1.0.126" serde_json = "1.0.64" @@ -23,6 +23,3 @@ wry = "0.12.2" android_logger = "0.9.0" log = "0.4.11" ndk-glue = "0.2.1" - -[target.'cfg(not(target_os = "android"))'.dependencies] -simple_logger = "1.11.0" diff --git a/packages/mobile/src/lib.rs b/packages/mobile/src/lib.rs index 938d4e957..685722718 100644 --- a/packages/mobile/src/lib.rs +++ b/packages/mobile/src/lib.rs @@ -20,10 +20,6 @@ use wry::{ mod dom; use dom::*; -fn init_logging() { - simple_logger::SimpleLogger::new().init().unwrap(); -} - static HTML_CONTENT: &'static str = include_str!("../../desktop/src/index.html"); pub fn launch(root: Component, builder: fn(WindowBuilder) -> WindowBuilder) -> anyhow::Result<()> { @@ -187,7 +183,6 @@ impl WebviewRenderer { } fn main() { - init_logging(); let event_loop = EventLoop::new(); let mut weviews = HashMap::new(); diff --git a/src/lib.rs b/src/lib.rs index a5e89bddb..5259377b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,7 +46,7 @@ //! dioxus::desktop::launch(app); //! } //! -//! fn app(cx: Scope<()>) -> Element { +//! fn app(cx: Scope) -> Element { //! cx.render(rsx!("hello world!")) //! } //! ``` @@ -57,7 +57,7 @@ //! For components with no explicit properties, we can use the `()` type. In Dioxus, all properties are memoized by default! //! //! ```rust -//! fn App(cx: Scope<()>) -> Element { +//! fn App(cx: Scope) -> Element { //! cx.render(rsx!( //! Header { //! title: "My App", @@ -91,7 +91,7 @@ //! While components are reusable forms of UI elements, hooks are reusable forms of logic. All hooks start with `use_`. We can use hooks to declare state. //! //! ```rust -//! fn app(cx: Scope<()>) -> Element { +//! fn app(cx: Scope) -> Element { //! let name = use_state(&cx, || "world"); //! //! rsx!(cx, "hello {name}!") @@ -137,7 +137,7 @@ //! dioxus::desktop::launch(App); //! } //! -//! fn App(cx: Scope<()>) -> Element { +//! fn App(cx: Scope) -> Element { //! let mut count = use_state(&cx, || 0); //! //! cx.render(rsx!( @@ -187,20 +187,20 @@ pub use dioxus_core as core; #[cfg(feature = "hooks")] pub use dioxus_hooks as hooks; +#[cfg(feature = "router")] +pub use dioxus_router as router; + #[cfg(feature = "ssr")] pub use dioxus_ssr as ssr; #[cfg(feature = "web")] pub use dioxus_web as web; -#[cfg(feature = "mobile")] -pub use dioxus_mobile as mobile; - #[cfg(feature = "desktop")] pub use dioxus_desktop as desktop; -#[cfg(feature = "router")] -pub use dioxus_router as router; +// #[cfg(feature = "mobile")] +// pub use dioxus_mobile as mobile; pub mod events { #[cfg(feature = "html")]