diff --git a/examples/calculator.rs b/examples/calculator.rs index 9cb3c957f..af71984a9 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -26,15 +26,15 @@ fn app() -> Element { val.set(String::new()); } - val.make_mut().push_str(num.to_string().as_str()); + val.write().push_str(num.to_string().as_str()); }; - let input_operator = move |key: &str| val.make_mut().push_str(key); + let input_operator = move |key: &str| val.write().push_str(key); let handle_key_down_event = move |evt: KeyboardEvent| match evt.key() { Key::Backspace => { - if !val.len() != 0 { - val.make_mut().pop(); + if !val().is_empty() { + val.write().pop(); } } Key::Character(character) => match character.as_str() { @@ -72,16 +72,16 @@ fn app() -> Element { class: "calculator-key key-clear", onclick: move |_| { val.set(String::new()); - if !val.is_empty(){ + if !val().is_empty(){ val.set("0".into()); } }, - if val.is_empty() { "C" } else { "AC" } + if val().is_empty() { "C" } else { "AC" } } button { class: "calculator-key key-sign", onclick: move |_| { - let temp = calc_val(val.as_str()); + let temp = calc_val(val().as_str()); if temp > 0.0 { val.set(format!("-{temp}")); } else { @@ -94,7 +94,7 @@ fn app() -> Element { class: "calculator-key key-percent", onclick: move |_| { val.set( - format!("{}", calc_val(val.as_str()) / 100.0) + format!("{}", calc_val(val().as_str()) / 100.0) ); }, "%" @@ -102,7 +102,7 @@ fn app() -> Element { } div { class: "digit-keys", button { class: "calculator-key key-0", onclick: move |_| input_digit(0), "0" } - button { class: "calculator-key key-dot", onclick: move |_| val.make_mut().push('.'), "●" } + button { class: "calculator-key key-dot", onclick: move |_| val.write().push('.'), "●" } for k in 1..10 { button { class: "calculator-key {k}", @@ -120,7 +120,7 @@ fn app() -> Element { button { class: "calculator-key key-add", onclick: move |_| input_operator("+"), "+" } button { class: "calculator-key key-equals", - onclick: move |_| val.set(format!("{}", calc_val(val.as_str()))), + onclick: move |_| val.set(format!("{}", calc_val(val().as_str()))), "=" } } diff --git a/examples/control_focus.rs b/examples/control_focus.rs index 9d1999ca7..28b411165 100644 --- a/examples/control_focus.rs +++ b/examples/control_focus.rs @@ -10,9 +10,9 @@ fn app() -> Element { let elements = use_signal(Vec::>::new); let running = use_signal(|| true); - use_future(|| async move { + use_future(move || async move { let mut focused = 0; - if *running.current() { + if *running() { loop { tokio::time::sleep(std::time::Duration::from_millis(10)).await; if let Some(element) = elements.with(|f| f.get(focused).cloned()) { diff --git a/examples/counter.rs b/examples/counter.rs index d1c5771c5..c766e45ed 100644 --- a/examples/counter.rs +++ b/examples/counter.rs @@ -13,22 +13,22 @@ fn app() -> Element { render! { div { - button { onclick: move |_| counters.make_mut().push(0), "Add counter" } - button { onclick: move |_| { counters.make_mut().pop(); }, "Remove counter" } + button { onclick: move |_| counters.write().push(0), "Add counter" } + button { onclick: move |_| { counters.write().pop(); }, "Remove counter" } p { "Total: {sum}" } for (i, counter) in counters.iter().enumerate() { li { - button { onclick: move |_| counters.make_mut()[i] -= 1, "-1" } + button { onclick: move |_| counters.write()[i] -= 1, "-1" } input { value: "{counter}", oninput: move |e| { if let Ok(value) = e.value().parse::() { - counters.make_mut()[i] = value; + counters.write()[i] = value; } } } - button { onclick: move |_| counters.make_mut()[i] += 1, "+1" } - button { onclick: move |_| { counters.make_mut().remove(i); }, "x" } + button { onclick: move |_| counters.write()[i] += 1, "+1" } + button { onclick: move |_| { counters.write().remove(i); }, "x" } } } } diff --git a/examples/crm.rs b/examples/crm.rs index cbaa73e7f..536e0aeef 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -27,7 +27,7 @@ pub struct Client { type ClientContext = Vec; #[component] -fn App(props: ()) -> Element { +fn App() -> Element { use_shared_state_provider::(Default::default); render! { @@ -58,15 +58,10 @@ fn ClientList() -> Element { rsx! { h2 { "List of Clients" } - Link { to: Route::ClientAdd {}, class: "pure-button pure-button-primary", "Add Client" } Link { to: Route::Settings {}, class: "pure-button", "Settings" } - for client in clients.read().iter() { - div { - class: "client", - style: "margin-bottom: 50px", - + div { class: "client", style: "margin-bottom: 50px", p { "Name: {client.first_name} {client.last_name}" } p { "Description: {client.description}" } } @@ -75,7 +70,7 @@ fn ClientList() -> Element { } #[component] -fn ClientAdd(props: ()) -> Element { +fn ClientAdd() -> Element { let clients = use_shared_state::().unwrap(); let first_name = use_signal(String::new); let last_name = use_signal(String::new); @@ -142,7 +137,7 @@ fn ClientAdd(props: ()) -> Element { } #[component] -fn Settings(props: ()) -> Element { +fn Settings() -> Element { let clients = use_shared_state::().unwrap(); rsx! { diff --git a/examples/disabled.rs b/examples/disabled.rs index a639c43c0..dd3f91597 100644 --- a/examples/disabled.rs +++ b/examples/disabled.rs @@ -9,17 +9,13 @@ fn app() -> Element { rsx! { div { - button { - onclick: move |_| disabled.set(!disabled), + button { onclick: move |_| disabled.toggle(), "click to " - if disabled == true { "enable" } else { "disable" } + if *disabled() { "enable" } else { "disable" } " the lower button" } - button { - disabled: "{disabled}", - "lower button" - } + button { disabled, "lower button" } } } } diff --git a/examples/dog_app.rs b/examples/dog_app.rs index 766730f79..8d90dfb5e 100644 --- a/examples/dog_app.rs +++ b/examples/dog_app.rs @@ -2,7 +2,7 @@ use dioxus::prelude::*; use std::collections::HashMap; fn main() { - dioxus_desktop::launch(|cx| render!(AppRoot {})); + dioxus_desktop::launch(app); } #[derive(Debug, Clone, PartialEq, serde::Deserialize)] @@ -10,8 +10,7 @@ struct ListBreeds { message: HashMap>, } -#[component] -fn AppRoot(_: ()) -> Element { +fn app() -> Element { let breed = use_signal(|| "deerhound".to_string()); let breeds = use_future(|| async move { diff --git a/examples/error_handle.rs b/examples/error_handle.rs index 2ed97ae62..eb340a57f 100644 --- a/examples/error_handle.rs +++ b/examples/error_handle.rs @@ -4,8 +4,7 @@ fn main() { dioxus_desktop::launch(App); } -#[component] -fn App(_: ()) -> Element { +fn app() -> Element { rsx! { ErrorBoundary { handle_error: |error: CapturedError| rsx! {"Found error {error}"}, diff --git a/examples/fermi.rs b/examples/fermi.rs deleted file mode 100644 index a45f52f00..000000000 --- a/examples/fermi.rs +++ /dev/null @@ -1,57 +0,0 @@ -#![allow(non_snake_case)] - -use dioxus::prelude::*; -use fermi::*; - -fn main() { - dioxus_desktop::launch(app) -} - -static NAME: Atom = Atom(|_| "world".to_string()); - -fn app() -> Element { - use_init_atom_root(cx); - let name = use_read(&NAME); - - rsx! { - div { "hello {name}!" } - Child {} - ChildWithRef {} - } -} - -fn Child() -> Element { - let set_name = use_set(&NAME); - - rsx! { - button { - onclick: move |_| set_name("dioxus".to_string()), - "reset name" - } - } -} - -static NAMES: AtomRef> = AtomRef(|_| vec!["world".to_string()]); - -fn ChildWithRef() -> Element { - let names = use_atom_ref(&NAMES); - - rsx! { - div { - ul { - for name in names.read().iter() { - li { "hello: {name}" } - } - } - button { - onclick: move |_| { - let names = names.clone(); - cx.spawn(async move { - names.write().push("asd".to_string()); - }) - }, - "Add name" - } - } - } -} diff --git a/examples/file_explorer.rs b/examples/file_explorer.rs index 5399aec44..92a900676 100644 --- a/examples/file_explorer.rs +++ b/examples/file_explorer.rs @@ -12,20 +12,19 @@ use dioxus::prelude::*; use dioxus_desktop::{Config, WindowBuilder}; fn main() { - dioxus_desktop::launch_cfg( - app, - Config::new().with_window(WindowBuilder::new().with_resizable(true)), - ); + Config::new() + .with_window(WindowBuilder::new().with_resizable(true)) + .launch(app) } const _STYLE: &str = manganis::mg!(file("./examples/assets/fileexplorer.css")); -fn app() -> Element { +fn app(_props: ()) -> Element { let files = use_signal(Files::new); rsx! { div { - link { href:"https://fonts.googleapis.com/icon?family=Material+Icons", rel:"stylesheet", } + link { href:"https://fonts.googleapis.com/icon?family=Material+Icons", rel:"stylesheet" } header { i { class: "material-icons icon-menu", "menu" } h1 { "Files: ", {files.read().current()} } diff --git a/examples/file_upload.rs b/examples/file_upload.rs index b5fe38bb0..da7bf6517 100644 --- a/examples/file_upload.rs +++ b/examples/file_upload.rs @@ -9,16 +9,33 @@ fn main() { fn App() -> Element { let enable_directory_upload = use_signal(|| false); - let files_uploaded: Signal> = use_signal(Vec::new); + let files_uploaded = use_signal(|| Vec::new() as Vec); + + let upload_files = move |evt: FormEvent| async move { + for file_name in evt.files().unwrap().files() { + // no files on form inputs? + sleep(std::time::Duration::from_secs(1)).await; + files_uploaded.write().push(file_name); + } + }; + + let handle_file_drop = move |evt: DragEvent| async move { + if let Some(file_engine) = &evt.files() { + let files = file_engine.files(); + for file_name in &files { + if let Some(file) = file_engine.read_file_to_string(file_name).await { + files_uploaded.write().push(file); + } + } + } + }; rsx! { label { input { r#type: "checkbox", - checked: "{enable_directory_upload}", - oninput: move |evt| { - enable_directory_upload.set(evt.value().parse().unwrap()); - }, + checked: enable_directory_upload, + oninput: move |evt| enable_directory_upload.set(evt.value().parse().unwrap()), }, "Enable directory upload" } @@ -27,41 +44,16 @@ fn App() -> Element { r#type: "file", accept: ".txt,.rs", multiple: true, - directory: **enable_directory_upload, - onchange: |evt| { - to_owned![files_uploaded]; - async move { - if let Some(file_engine) = &evt.files() { - let files = file_engine.files(); - for file_name in files { - sleep(std::time::Duration::from_secs(1)).await; - files_uploaded.write().push(file_name); - } - } - } - }, + directory: enable_directory_upload, + onchange: upload_files, } div { width: "100px", height: "100px", border: "1px solid black", prevent_default: "ondrop dragover dragenter", - ondrop: move |evt| { - to_owned![files_uploaded]; - async move { - if let Some(file_engine) = &evt.files() { - let files = file_engine.files(); - for file_name in &files { - if let Some(file) = file_engine.read_file_to_string(file_name).await{ - files_uploaded.write().push(file); - } - } - } - } - }, - ondragover: move |event: DragEvent| { - event.stop_propagation(); - }, + ondrop: handle_file_drop, + ondragover: move |event| event.stop_propagation(), "Drop files here" } diff --git a/examples/filedragdrop.rs b/examples/filedragdrop.rs index ba39f38f6..5f873e290 100644 --- a/examples/filedragdrop.rs +++ b/examples/filedragdrop.rs @@ -2,15 +2,15 @@ use dioxus::prelude::*; use dioxus_desktop::Config; fn main() { - let cfg = Config::new().with_file_drop_handler(|_w, e| { - println!("{e:?}"); - true - }); - - dioxus_desktop::launch_with_props(app, (), cfg); + Config::new() + .with_file_drop_handler(|_w, e| { + println!("{e:?}"); + true + }) + .launch(app) } -fn app() -> Element { +fn app(_props: ()) -> Element { rsx!( div { h1 { "drag a file here and check your console" } diff --git a/examples/flat_router.rs b/examples/flat_router.rs index b205b7e98..56f04d072 100644 --- a/examples/flat_router.rs +++ b/examples/flat_router.rs @@ -5,20 +5,17 @@ use dioxus_router::prelude::*; fn main() { env_logger::init(); - let cfg = Config::new().with_window( - WindowBuilder::new() - .with_inner_size(LogicalSize::new(600, 1000)) - .with_resizable(false), - ); - - dioxus_desktop::launch_cfg(App, cfg) -} - -#[component] -fn App() -> Element { - render! { - Router:: {} - } + Config::new() + .with_window( + WindowBuilder::new() + .with_inner_size(LogicalSize::new(600, 1000)) + .with_resizable(false), + ) + .launch(|| { + render! { + Router:: {} + } + }); } #[derive(Routable, Clone)] diff --git a/examples/generic_component.rs b/examples/generic_component.rs index 18ed632e6..d5e5e1daa 100644 --- a/examples/generic_component.rs +++ b/examples/generic_component.rs @@ -12,13 +12,13 @@ fn app() -> Element { } } -#[derive(PartialEq, Props)] -struct GenericChildProps { +#[derive(PartialEq, Props, Clone)] +struct GenericChildProps { data: T, } -fn generic_child(cx: Scope>) -> Element { +fn generic_child(props: GenericChildProps) -> Element { render! { - div { "{&cx.props.data}" } + div { "{&props.data}" } } } diff --git a/examples/hydration.rs b/examples/hydration.rs index e3377a8b1..61342c44e 100644 --- a/examples/hydration.rs +++ b/examples/hydration.rs @@ -13,23 +13,24 @@ use dioxus::prelude::*; use dioxus_desktop::Config; fn main() { - let mut vdom = VirtualDom::new(app); - let _ = vdom.rebuild(); - let content = dioxus_ssr::pre_render(&vdom); + Config::new() + .with_prerendered({ + // We build the dom a first time, then pre-render it to HTML + let pre_rendered_dom = VirtualDom::prebuilt(app); - dioxus_desktop::launch_cfg(app, Config::new().with_prerendered(content)); + // We then launch the app with the pre-rendered HTML + dioxus_ssr::pre_render(&pre_rendered_dom) + }) + .launch(app); } fn app() -> Element { - let val = use_signal(|| 0); + let mut val = use_signal(|| 0); rsx! { div { h1 { "hello world. Count: {val}" } - button { - onclick: move |_| *val.make_mut() += 1, - "click to increment" - } + button { onclick: move |_| val += 1, "click to increment" } } } } diff --git a/examples/read_size.rs b/examples/read_size.rs index d6afb375f..bbc81cc8e 100644 --- a/examples/read_size.rs +++ b/examples/read_size.rs @@ -26,35 +26,31 @@ fn main() { } fn app() -> Element { - let div_element: Signal>> = use_signal(|| None); + let div_element = use_signal(|| None as Option>); + let dimensions = use_signal(Rect::zero); - let dimentions = use_signal(Rect::zero); + let read_dims = move |_| async move { + let read = div_element.read(); + let client_rect = read.as_ref().map(|el| el.get_client_rect()); + if let Some(client_rect) = client_rect { + if let Ok(rect) = client_rect.await { + dimensions.set(rect); + } + } + }; rsx!( div { width: "50%", height: "50%", background_color: "red", - onmounted: move |cx| { - div_element.set(Some(cx.inner().clone())); - }, - "This element is {dimentions.read():?}" + onmounted: move |cx| div_element.set(Some(cx.inner().clone())), + "This element is {dimensions.read():?}" } button { - onclick: move |_| { - to_owned![div_element, dimentions]; - async move { - let read = div_element.read(); - let client_rect = read.as_ref().map(|el| el.get_client_rect()); - if let Some(client_rect) = client_rect { - if let Ok(rect) = client_rect.await { - dimentions.set(rect); - } - } - } - }, - "Read dimentions" + onclick: read_dims, + "Read dimensions" } ) } diff --git a/examples/rsx_compile_fail.rs b/examples/rsx_compile_fail.rs deleted file mode 100644 index fa1407c24..000000000 --- a/examples/rsx_compile_fail.rs +++ /dev/null @@ -1,58 +0,0 @@ -//! This example just flexes the ability to use arbitrary expressions within RSX. -//! It also proves that lifetimes work properly, especially when used with use_ref - -use dioxus::prelude::*; - -fn main() { - let mut vdom = VirtualDom::new(example); - _ = vdom.rebuild(); - - let mut renderer = dioxus_ssr::Renderer::new(); - renderer.pretty = true; - renderer.render(&vdom); -} - -fn example() -> Element { - let items = use_signal(|| { - vec![Thing { - a: "asd".to_string(), - b: 10, - }] - }); - - let things = use_signal(|| { - vec![Thing { - a: "asd".to_string(), - b: 10, - }] - }); - let things_list = things.read(); - - let mything = use_signal(|| Some(String::from("asd"))); - let mything_read = mything.read(); - - rsx!( - div { - div { id: "asd", - "your neighborhood spiderman" - - for item in items.iter().cycle().take(5) { - div { "{item.a}" } - } - - for thing in things_list.iter() { - div { "{thing.a}" "{thing.b}" } - } - - if let Some(f) = mything_read.as_ref() { - div { "{f}" } - } - } - } - )) -} - -struct Thing { - a: String, - b: u32, -} diff --git a/examples/rsx_usage.rs b/examples/rsx_usage.rs index 1e450bb87..778e0dc10 100644 --- a/examples/rsx_usage.rs +++ b/examples/rsx_usage.rs @@ -241,12 +241,10 @@ fn lowercase_helper() -> Element { mod baller { use super::*; - #[derive(Props, PartialEq, Eq)] - pub struct BallerProps {} #[component] /// This component totally balls - pub fn Baller(props: BallerProps) -> Element { + pub fn Baller() -> Element { todo!() } @@ -259,16 +257,13 @@ mod baller { } } -#[derive(Props)] -pub struct TallerProps<'a> { +/// Documention for this component is visible within the rsx macro +#[component] +pub fn Taller( /// Fields are documented and accessible in rsx! a: &'static str, children: Element, -} - -/// Documention for this component is visible within the rsx macro -#[component] -pub fn Taller(props: TallerProps) -> Element { +) -> Element { rsx! { {&cx.props.children} } diff --git a/examples/scroll_to_top.rs b/examples/scroll_to_top.rs index a27445508..cc13fca85 100644 --- a/examples/scroll_to_top.rs +++ b/examples/scroll_to_top.rs @@ -19,7 +19,7 @@ fn app() -> Element { } button { - onclick: async move |_| move { + onclick: move |_| async move { if let Some(header) = header_element.read().as_ref().cloned() { let _ = header.scroll_to(ScrollBehavior::Smooth).await; } diff --git a/examples/suspense.rs b/examples/suspense.rs index 045bd770c..8d73201f5 100644 --- a/examples/suspense.rs +++ b/examples/suspense.rs @@ -17,18 +17,13 @@ use dioxus::prelude::*; use dioxus_desktop::{Config, LogicalSize, WindowBuilder}; fn main() { - let cfg = Config::new().with_window( - WindowBuilder::new() - .with_title("Doggo Fetcher") - .with_inner_size(LogicalSize::new(600.0, 800.0)), - ); - - dioxus_desktop::launch_cfg(app, cfg); -} - -#[derive(serde::Deserialize)] -struct DogApi { - message: String, + Config::new() + .with_window( + WindowBuilder::new() + .with_title("Doggo Fetcher") + .with_inner_size(LogicalSize::new(600.0, 800.0)), + ) + .launch(app) } fn app() -> Element { @@ -54,6 +49,11 @@ fn app() -> Element { /// actually renders the data. fn Doggo() -> Element { let fut = use_future(|_| async move { + #[derive(serde::Deserialize)] + struct DogApi { + message: String, + } + reqwest::get("https://dog.ceo/api/breeds/image/random/") .await .unwrap() @@ -61,7 +61,7 @@ fn Doggo() -> Element { .await }); - cx.render(match fut.value() { + match fut.value() { Some(Ok(resp)) => rsx! { button { onclick: move |_| fut.restart(), diff --git a/examples/svg_basic.rs b/examples/svg_basic.rs index 9a53539dd..0ea178281 100644 --- a/examples/svg_basic.rs +++ b/examples/svg_basic.rs @@ -1,77 +1,79 @@ use dioxus::prelude::*; fn app() -> Element { - rsx!( svg { - width: "200", - height: "250", - xmlns: "http://www.w3.org/2000/svg", - version: "1.1", - rect { - x: "10", - y: "10", - width: "30", - height: "30", - stroke: "black", - fill: "transparent", - stroke_width: "5", - } - rect { - x: "60", - y: "10", - width: "30", - height: "30", - stroke: "black", - fill: "transparent", - stroke_width: "5", - } - circle { - cx: "25", - cy: "75", - r: "20", - stroke: "red", - fill: "transparent", - stroke_width: "5", - } - ellipse { - cx: "75", - cy: "75", - rx: "20", - ry: "5", - stroke: "red", - fill: "transparent", - stroke_width: "5", - } - line { - x1: "10", - x2: "50", - y1: "110", - y2: "150", - stroke: "orange", - stroke_width: "5", - } - polyline { - points: "60 110 65 120 70 115 75 130 80 125 85 140 90 135 95 150 100 145", - stroke: "orange", - fill: "transparent", - stroke_width: "5", - } - polygon { - points: "50 160 55 180 70 180 60 190 65 205 50 195 35 205 40 190 30 180 45 180", - stroke: "green", - fill: "transparent", - stroke_width: "5", - } - path { - d: "M20,230 Q40,205 50,230 T90,230", - fill: "none", - stroke: "blue", - stroke_width: "5", - } - path { - d: "M9.00001 9C9 62 103.5 124 103.5 178", - stroke: "#3CC4DC", - "stroke-linecap": "square", - "stroke-width": "square", + rsx! { + svg { + width: "200", + height: "250", + xmlns: "http://www.w3.org/2000/svg", + version: "1.1", + rect { + x: "10", + y: "10", + width: "30", + height: "30", + stroke: "black", + fill: "transparent", + stroke_width: "5", + } + rect { + x: "60", + y: "10", + width: "30", + height: "30", + stroke: "black", + fill: "transparent", + stroke_width: "5", + } + circle { + cx: "25", + cy: "75", + r: "20", + stroke: "red", + fill: "transparent", + stroke_width: "5", + } + ellipse { + cx: "75", + cy: "75", + rx: "20", + ry: "5", + stroke: "red", + fill: "transparent", + stroke_width: "5", + } + line { + x1: "10", + x2: "50", + y1: "110", + y2: "150", + stroke: "orange", + stroke_width: "5", + } + polyline { + points: "60 110 65 120 70 115 75 130 80 125 85 140 90 135 95 150 100 145", + stroke: "orange", + fill: "transparent", + stroke_width: "5", + } + polygon { + points: "50 160 55 180 70 180 60 190 65 205 50 195 35 205 40 190 30 180 45 180", + stroke: "green", + fill: "transparent", + stroke_width: "5", + } + path { + d: "M20,230 Q40,205 50,230 T90,230", + fill: "none", + stroke: "blue", + stroke_width: "5", + } + path { + d: "M9.00001 9C9 62 103.5 124 103.5 178", + stroke: "#3CC4DC", + "stroke-linecap": "square", + "stroke-width": "square", + } } } } diff --git a/examples/todomvc.rs b/examples/todomvc.rs index b45fef870..7d34d6286 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -21,7 +21,7 @@ pub struct TodoItem { pub contents: String, } -pub fn app(_props: ()) -> Element { +pub fn app() -> Element { let todos = use_signal(|| HashMap::::new()); let filter = use_signal(|| FilterState::All); diff --git a/examples/window_event.rs b/examples/window_event.rs index aa995647b..126d5fa8a 100644 --- a/examples/window_event.rs +++ b/examples/window_event.rs @@ -1,34 +1,25 @@ use dioxus::prelude::*; -use dioxus_desktop::{Config, WindowBuilder}; +use dioxus_desktop::{window, Config, WindowBuilder}; fn main() { - let cfg = Config::new().with_window( - WindowBuilder::new() - .with_title("Borderless Window") - .with_decorations(false), - ); - - dioxus_desktop::launch_cfg(app, cfg); + Config::new() + .with_window( + WindowBuilder::new() + .with_title("Borderless Window") + .with_decorations(false), + ) + .launch(app) } fn app() -> Element { - let window = dioxus_desktop::use_window(cx); - - // if you want to make window fullscreen, you need close the resizable. - // window.set_fullscreen(true); - // window.set_resizable(false); - let fullscreen = use_signal(|| false); let always_on_top = use_signal(|| false); let decorations = use_signal(|| false); rsx!( link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel:"stylesheet" } - header { - class: "text-gray-400 bg-gray-900 body-font", - onmousedown: move |_| window.drag(), - div { - class: "container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center", + header { class: "text-gray-400 bg-gray-900 body-font", onmousedown: move |_| window().drag(), + div { class: "container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center", a { class: "flex title-font font-medium items-center text-white mb-4 md:mb-0", span { class: "ml-3 text-xl", "Dioxus"} } @@ -36,24 +27,23 @@ fn app() -> Element { button { class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0", onmousedown: |evt| evt.stop_propagation(), - onclick: move |_| window.set_minimized(true), + onclick: move |_| window().set_minimized(true), "Minimize" } button { class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0", onmousedown: |evt| evt.stop_propagation(), onclick: move |_| { - - window.set_fullscreen(!**fullscreen); - window.set_resizable(**fullscreen); - fullscreen.modify(|f| !*f); + window().set_fullscreen(!*fullscreen()); + window().set_resizable(*fullscreen()); + fullscreen.toggle(); }, "Fullscreen" } button { class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0", onmousedown: |evt| evt.stop_propagation(), - onclick: move |_| window.close(), + onclick: move |_| window().close(), "Close" } } @@ -68,8 +58,8 @@ fn app() -> Element { class: "inline-flex items-center text-white bg-green-500 border-0 py-1 px-3 hover:bg-green-700 rounded", onmousedown: |evt| evt.stop_propagation(), onclick: move |_| { - window.set_always_on_top(!always_on_top); - always_on_top.set(!always_on_top); + window().set_always_on_top(!*always_on_top()); + always_on_top.toggle(); }, "Always On Top" } @@ -79,8 +69,8 @@ fn app() -> Element { class: "inline-flex items-center text-white bg-blue-500 border-0 py-1 px-3 hover:bg-green-700 rounded", onmousedown: |evt| evt.stop_propagation(), onclick: move |_| { - window.set_decorations(!decorations); - decorations.set(!decorations); + window().set_decorations(!*decorations()); + decorations.toggle(); }, "Set Decorations" } @@ -89,7 +79,7 @@ fn app() -> Element { button { class: "inline-flex items-center text-white bg-blue-500 border-0 py-1 px-3 hover:bg-green-700 rounded", onmousedown: |evt| evt.stop_propagation(), - onclick: move |_| window.set_title("Dioxus Application"), + onclick: move |_| window().set_title("Dioxus Application"), "Change Title" } } diff --git a/examples/window_focus.rs b/examples/window_focus.rs index 6fb07e001..93a373da9 100644 --- a/examples/window_focus.rs +++ b/examples/window_focus.rs @@ -5,37 +5,33 @@ use dioxus_desktop::use_wry_event_handler; use dioxus_desktop::{Config, WindowCloseBehaviour}; fn main() { - let cfg = Config::new().with_close_behaviour(WindowCloseBehaviour::CloseWindow); - - dioxus_desktop::launch_cfg(app, cfg); + Config::new() + .with_close_behaviour(WindowCloseBehaviour::CloseWindow) + .launch(app) } fn app() -> Element { let focused = use_signal(|| false); - use_wry_event_handler(move |event, _| { - if let WryEvent::WindowEvent { + use_wry_event_handler(move |event, _| match event { + WryEvent::WindowEvent { event: WindowEvent::Focused(new_focused), .. - } = event - { - focused.set(*new_focused); - } + } => focused.set(*new_focused), + _ => {} }); rsx! { - div{ + div { width: "100%", height: "100%", display: "flex", flex_direction: "column", align_items: "center", - { - if *focused.get() { - "This window is focused!" - } else { - "This window is not focused!" - } + if *focused() { + "This window is focused!" + } else { + "This window is not focused!" } } } diff --git a/examples/window_zoom.rs b/examples/window_zoom.rs index d58ab76fd..1a7607df2 100644 --- a/examples/window_zoom.rs +++ b/examples/window_zoom.rs @@ -11,7 +11,7 @@ fn app() -> Element { input { r#type: "number", value: "{level}", - oninput: |e| { + oninput: move |e| { if let Ok(new_zoom) = e.value().parse::() { level.set(new_zoom); dioxus_desktop::window().webview.zoom(new_zoom); diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index e756c5d0a..8b307e646 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -230,6 +230,13 @@ impl VirtualDom { Self::new_with_props(|app| app(), app) } + /// Create a new virtualdom and build it immediately + pub fn prebuilt(app: fn() -> Element) -> Self { + let mut dom = Self::new(app); + dom.rebuild_in_place(); + dom + } + /// Create a new VirtualDom with the given properties for the root component. /// /// # Description @@ -568,6 +575,14 @@ impl VirtualDom { to.append_children(ElementId(0), m); } + /// Rebuild the virtualdom without handling any of the mutations + /// + /// This is useful for testing purposes and in cases where you render the output of the virtualdom without + /// handling any of its mutations. + pub fn rebuild_in_place(&mut self) { + self.rebuild(&mut NoOpMutations); + } + /// [`VirtualDom::rebuild`] to a vector of mutations for testing purposes pub fn rebuild_to_vec(&mut self) -> Mutations { let mut mutations = Mutations::default(); diff --git a/packages/desktop/src/config.rs b/packages/desktop/src/config.rs index dbbdea6e5..efe235f54 100644 --- a/packages/desktop/src/config.rs +++ b/packages/desktop/src/config.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use std::path::PathBuf; -use dioxus_core::prelude::Component; +use dioxus_core::prelude::{Component, Element}; use tao::window::{Icon, WindowBuilder, WindowId}; use wry::{ http::{Request as HttpRequest, Response as HttpResponse}, @@ -74,8 +74,8 @@ impl Config { /// Launch a Dioxus app using the given component and config /// /// See the [`crate::launch::launch`] function for more details. - pub fn launch(self, root: Component<()>) { - crate::launch::launch_cfg(root, self) + pub fn launch(self, app: fn() -> Element) { + self.launch_with_props(|props| props(), app) } /// Launch a Dioxus app using the given component, config, and props diff --git a/packages/desktop/src/hooks.rs b/packages/desktop/src/hooks.rs index 1cdef8f10..a269fe5cd 100644 --- a/packages/desktop/src/hooks.rs +++ b/packages/desktop/src/hooks.rs @@ -2,6 +2,7 @@ use crate::{ assets::*, ipc::UserWindowEvent, shortcut::IntoAccelerator, window, DesktopContext, ShortcutHandle, ShortcutRegistryError, WryEventHandler, }; +use dioxus_core::use_hook; use tao::{event::Event, event_loop::EventLoopWindowTarget}; use wry::RequestAsyncResponder; @@ -12,22 +13,21 @@ use wry::RequestAsyncResponder; // .unwrap() // } -// /// Get a closure that executes any JavaScript in the WebView context. -// pub fn use_wry_event_handler( -// , -// handler: impl FnMut(&Event, &EventLoopWindowTarget) + 'static, -// ) -> &WryEventHandler { -// cx.use_hook(move || { -// let desktop = window(); +/// Get a closure that executes any JavaScript in the WebView context. +pub fn use_wry_event_handler( + handler: impl FnMut(&Event, &EventLoopWindowTarget) + 'static, +) -> WryEventHandler { + use_hook(move || { + let desktop = window(); -// let id = desktop.create_wry_event_handler(handler); + let id = desktop.create_wry_event_handler(handler); -// WryEventHandler { -// handlers: desktop.shared.event_handlers.clone(), -// id, -// } -// }) -// } + WryEventHandler { + handlers: desktop.shared.event_handlers.clone(), + id, + } + }) +} // /// Provide a callback to handle asset loading yourself. // /// diff --git a/packages/desktop/src/launch.rs b/packages/desktop/src/launch.rs index c1594917c..62c3ceb1b 100644 --- a/packages/desktop/src/launch.rs +++ b/packages/desktop/src/launch.rs @@ -23,8 +23,8 @@ use tao::event::{Event, StartCause, WindowEvent}; /// }) /// } /// ``` -pub fn launch(root: fn() -> Element) { - launch_with_props(|root| root(), root, Config::default()) +pub fn launch(app: fn() -> Element) { + launch_with_props(|root| root(), app, Config::default()) } /// Launch the WebView and run the event loop, with configuration. @@ -47,8 +47,8 @@ pub fn launch(root: fn() -> Element) { /// }) /// } /// ``` -pub fn launch_cfg(root: Component, config_builder: Config) { - launch_with_props(root, (), config_builder) +pub fn launch_cfg(app: fn() -> Element, config_builder: Config) { + launch_with_props(|props| props(), app, config_builder) } /// Launch the WebView and run the event loop, with configuration and root props. diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index d6b648876..af605366f 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -46,3 +46,5 @@ pub use desktop_context::{ // pub use hooks::{use_asset_handler, use_global_shortcut, use_window, use_wry_event_handler}; pub use shortcut::{ShortcutHandle, ShortcutId, ShortcutRegistryError}; pub use wry::RequestAsyncResponder; + +pub use hooks::*; diff --git a/packages/signals/src/signal.rs b/packages/signals/src/signal.rs index 33e497728..185ffea16 100644 --- a/packages/signals/src/signal.rs +++ b/packages/signals/src/signal.rs @@ -7,8 +7,11 @@ use std::{ }; use dioxus_core::{ - prelude::{current_scope_id, has_context, provide_context, schedule_update_any}, - use_hook, ScopeId, + prelude::{ + current_scope_id, has_context, provide_context, schedule_update_any, use_hook, + IntoAttributeValue, + }, + ScopeId, }; use generational_box::{GenerationalRef, GenerationalRefMut}; @@ -306,6 +309,15 @@ impl Signal { } } +impl IntoAttributeValue for Signal +where + T: Clone + IntoAttributeValue, +{ + fn into_value(self) -> dioxus_core::AttributeValue { + self.with(|f| f.clone().into_value()) + } +} + impl Signal { /// Get the current value of the signal. This will subscribe the current scope to the signal. /// If the signal has been dropped, this will panic.