소스 검색

implement launch builder for the desktop and web platforms

Evan Almloff 1 년 전
부모
커밋
7e4d2debe0
70개의 변경된 파일448개의 추가작업 그리고 431개의 파일을 삭제
  1. 1 1
      Cargo.toml
  2. 1 1
      examples/all_css.rs
  3. 1 1
      examples/all_events.rs
  4. 34 14
      examples/calculator.rs
  5. 1 1
      examples/clock.rs
  6. 1 1
      examples/compose.rs
  7. 1 1
      examples/control_focus.rs
  8. 1 1
      examples/counter.rs
  9. 1 1
      examples/crm.rs
  10. 1 1
      examples/custom_assets.rs
  11. 3 7
      examples/custom_html.rs
  12. 1 1
      examples/disabled.rs
  13. 1 1
      examples/dog_app.rs
  14. 1 1
      examples/dynamic_asset.rs
  15. 1 1
      examples/error_handle.rs
  16. 1 1
      examples/eval.rs
  17. 3 3
      examples/file_explorer.rs
  18. 1 1
      examples/file_upload.rs
  19. 5 7
      examples/filedragdrop.rs
  20. 22 16
      examples/flat_router.rs
  21. 1 1
      examples/form.rs
  22. 1 1
      examples/generic_component.rs
  23. 1 1
      examples/hello_world.rs
  24. 4 4
      examples/hydration.rs
  25. 1 1
      examples/inputs.rs
  26. 1 1
      examples/link.rs
  27. 1 1
      examples/login_form.rs
  28. 9 5
      examples/mobile_demo/src/lib.rs
  29. 1 1
      examples/multiwindow.rs
  30. 1 1
      examples/nested_listeners.rs
  31. 1 1
      examples/optional_props.rs
  32. 1 1
      examples/overlay.rs
  33. 1 1
      examples/pattern_model.rs
  34. 1 1
      examples/pattern_reducer.rs
  35. 2 6
      examples/read_size.rs
  36. 1 1
      examples/readme.rs
  37. 1 1
      examples/router.rs
  38. 1 1
      examples/rsx_usage.rs
  39. 1 1
      examples/scroll_to_top.rs
  40. 1 1
      examples/shortcut.rs
  41. 1 1
      examples/shorthand.rs
  42. 1 1
      examples/signals.rs
  43. 1 1
      examples/simple_desktop.rs
  44. 1 1
      examples/simple_list.rs
  45. 1 1
      examples/simple_router.rs
  46. 1 1
      examples/streams.rs
  47. 12 19
      examples/suspense.rs
  48. 1 1
      examples/svg.rs
  49. 1 1
      examples/svg_basic.rs
  50. 1 1
      examples/tailwind/src/main.rs
  51. 1 1
      examples/tasks.rs
  52. 1 1
      examples/textarea.rs
  53. 1 1
      examples/todomvc.rs
  54. 1 1
      examples/video_stream.rs
  55. 1 1
      examples/web_component.rs
  56. 18 13
      examples/window_event.rs
  57. 4 9
      examples/window_focus.rs
  58. 1 1
      examples/window_zoom.rs
  59. 1 1
      examples/xss_safety.rs
  60. 7 5
      packages/core/src/lib.rs
  61. 106 0
      packages/core/src/platform.rs
  62. 1 2
      packages/core/src/scope_context.rs
  63. 10 4
      packages/core/src/virtual_dom.rs
  64. 24 14
      packages/desktop/src/app.rs
  65. 0 14
      packages/desktop/src/config.rs
  66. 33 86
      packages/desktop/src/launch.rs
  67. 68 62
      packages/dioxus/src/launch.rs
  68. 3 2
      packages/dioxus/src/lib.rs
  69. 13 91
      packages/web/src/lib.rs
  70. 19 0
      packages/web/src/platform.rs

+ 1 - 1
Cargo.toml

@@ -125,7 +125,7 @@ rust-version = "1.60.0"
 publish = false
 
 [dev-dependencies]
-dioxus = { workspace = true }
+dioxus = { workspace = true, features = ["desktop"] }
 dioxus-desktop = { workspace = true, features = ["transparent"] }
 dioxus-ssr = { workspace = true }
 dioxus-router = { workspace = true }

+ 1 - 1
examples/all_css.rs

@@ -1,7 +1,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/all_events.rs

@@ -1,7 +1,7 @@
 use dioxus::{events::*, html::MouseEvent, prelude::*};
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 #[derive(Debug)]

+ 34 - 14
examples/calculator.rs

@@ -15,7 +15,7 @@ fn main() {
             .with_inner_size(LogicalSize::new(300.0, 500.0)),
     );
 
-    dioxus_desktop::launch_cfg(app, config);
+    LaunchBuilder::new(app).cfg(config);
 }
 
 fn app() -> Element {
@@ -61,9 +61,7 @@ fn app() -> Element {
         style { {include_str!("./assets/calculator.css")} }
         div { id: "wrapper",
             div { class: "app",
-                div { class: "calculator",
-                    tabindex: "0",
-                    onkeydown: handle_key_down_event,
+                div { class: "calculator", tabindex: "0", onkeydown: handle_key_down_event,
                     div { class: "calculator-display", "{val}" }
                     div { class: "calculator-keypad",
                         div { class: "input-keys",
@@ -72,7 +70,7 @@ fn app() -> Element {
                                     class: "calculator-key key-clear",
                                     onclick: move |_| {
                                         val.set(String::new());
-                                        if !val.cloned().is_empty(){
+                                        if !val.cloned().is_empty() {
                                             val.set("0".into());
                                         }
                                     },
@@ -93,16 +91,22 @@ fn app() -> Element {
                                 button {
                                     class: "calculator-key key-percent",
                                     onclick: move |_| {
-                                        val.set(
-                                            format!("{}", calc_val(val.cloned().as_str()) / 100.0)
-                                        );
+                                        val.set(format!("{}", calc_val(val.cloned().as_str()) / 100.0));
                                     },
                                     "%"
                                 }
                             }
                             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.write().push('.'), "●" }
+                                button {
+                                    class: "calculator-key key-0",
+                                    onclick: move |_| input_digit(0),
+                                    "0"
+                                }
+                                button {
+                                    class: "calculator-key key-dot",
+                                    onclick: move |_| val.write().push('.'),
+                                    "●"
+                                }
                                 for k in 1..10 {
                                     button {
                                         class: "calculator-key {k}",
@@ -114,10 +118,26 @@ fn app() -> Element {
                             }
                         }
                         div { class: "operator-keys",
-                            button { class: "calculator-key key-divide", onclick: move |_| input_operator("/"), "÷" }
-                            button { class: "calculator-key key-multiply", onclick: move |_| input_operator("*"), "×" }
-                            button { class: "calculator-key key-subtract", onclick: move |_| input_operator("-"), "−" }
-                            button { class: "calculator-key key-add", onclick: move |_| input_operator("+"), "+" }
+                            button {
+                                class: "calculator-key key-divide",
+                                onclick: move |_| input_operator("/"),
+                                "÷"
+                            }
+                            button {
+                                class: "calculator-key key-multiply",
+                                onclick: move |_| input_operator("*"),
+                                "×"
+                            }
+                            button {
+                                class: "calculator-key key-subtract",
+                                onclick: move |_| input_operator("-"),
+                                "−"
+                            }
+                            button {
+                                class: "calculator-key key-add",
+                                onclick: move |_| input_operator("+"),
+                                "+"
+                            }
                             button {
                                 class: "calculator-key key-equals",
                                 onclick: move |_| val.set(format!("{}", calc_val(val.cloned().as_str()))),

+ 1 - 1
examples/clock.rs

@@ -2,7 +2,7 @@ use dioxus::prelude::*;
 use dioxus_signals::use_signal;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/compose.rs

@@ -6,7 +6,7 @@ use dioxus::prelude::*;
 use futures_util::StreamExt;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/control_focus.rs

@@ -3,7 +3,7 @@ use std::rc::Rc;
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/counter.rs

@@ -4,7 +4,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/crm.rs

@@ -3,7 +3,7 @@ use dioxus::prelude::*;
 use dioxus_router::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 /// A type alias that reprsents a shared context between components

+ 1 - 1
examples/custom_assets.rs

@@ -1,7 +1,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 3 - 7
examples/custom_html.rs

@@ -5,13 +5,11 @@ use dioxus::prelude::*;
 use dioxus_desktop::Config;
 
 fn main() {
-    dioxus_desktop::launch_cfg(
-        app,
+    LaunchBuilder::new(app).cfg(
         Config::new().with_custom_head("<style>body { background-color: red; }</style>".into()),
     );
 
-    dioxus_desktop::launch_cfg(
-        app,
+    LaunchBuilder::new(app).cfg(
         Config::new().with_custom_index(
             r#"
 <!DOCTYPE html>
@@ -33,8 +31,6 @@ fn main() {
 
 fn app() -> Element {
     rsx! {
-        div {
-            h1 {"hello world!"}
-        }
+        div { h1 { "hello world!" } }
     }
 }

+ 1 - 1
examples/disabled.rs

@@ -1,7 +1,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/dog_app.rs

@@ -2,7 +2,7 @@ use dioxus::prelude::*;
 use std::collections::HashMap;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/dynamic_asset.rs

@@ -2,7 +2,7 @@ use dioxus::prelude::*;
 use dioxus_desktop::{use_asset_handler, wry::http::Response};
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/error_handle.rs

@@ -1,7 +1,7 @@
 use dioxus::{dioxus_core::CapturedError, prelude::*};
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/eval.rs

@@ -1,7 +1,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 3 - 3
examples/file_explorer.rs

@@ -12,9 +12,9 @@ use dioxus::prelude::*;
 use dioxus_desktop::{Config, WindowBuilder};
 
 fn main() {
-    Config::new()
-        .with_window(WindowBuilder::new().with_resizable(true))
-        .launch(app)
+    LaunchBuilder::new(app)
+        .cfg(Config::new().with_window(WindowBuilder::new().with_resizable(true)))
+        .launch()
 }
 
 const _STYLE: &str = manganis::mg!(file("./examples/assets/fileexplorer.css"));

+ 1 - 1
examples/file_upload.rs

@@ -4,7 +4,7 @@ use dioxus::prelude::*;
 use tokio::time::sleep;
 
 fn main() {
-    dioxus_desktop::launch(App);
+    launch(App);
 }
 
 fn App() -> Element {

+ 5 - 7
examples/filedragdrop.rs

@@ -2,18 +2,16 @@ use dioxus::prelude::*;
 use dioxus_desktop::Config;
 
 fn main() {
-    Config::new()
-        .with_file_drop_handler(|_w, e| {
+    LaunchBuilder::new(app)
+        .cfg(Config::new().with_file_drop_handler(|_w, e| {
             println!("{e:?}");
             true
-        })
-        .launch(app)
+        }))
+        .launch()
 }
 
 fn app() -> Element {
     rsx!(
-        div {
-            h1 { "drag a file here and check your console" }
-        }
+        div { h1 { "drag a file here and check your console" } }
     )
 }

+ 22 - 16
examples/flat_router.rs

@@ -5,17 +5,17 @@ use dioxus_router::prelude::*;
 fn main() {
     env_logger::init();
 
-    Config::new()
-        .with_window(
+    LaunchBuilder::new(|| {
+        render! { Router::<Route> {} }
+    })
+    .cfg(
+        Config::new().with_window(
             WindowBuilder::new()
                 .with_inner_size(LogicalSize::new(600, 1000))
                 .with_resizable(false),
-        )
-        .launch(|| {
-            render! {
-                Router::<Route> {}
-            }
-        });
+        ),
+    )
+    .launch()
 }
 
 #[derive(Routable, Clone)]
@@ -36,18 +36,24 @@ enum Route {
 fn Footer() -> Element {
     render! {
         div {
-            Outlet::<Route> { }
+            Outlet::<Route> {}
 
-            p {
-                "----"
-            }
+            p { "----" }
 
             nav {
                 ul {
-                    li { Link { to: Route::Home {}, "Home" } }
-                    li { Link { to: Route::Games {}, "Games" } }
-                    li { Link { to: Route::Play {}, "Play" } }
-                    li { Link { to: Route::Settings {}, "Settings" } }
+                    li {
+                        Link { to: Route::Home {}, "Home" }
+                    }
+                    li {
+                        Link { to: Route::Games {}, "Games" }
+                    }
+                    li {
+                        Link { to: Route::Play {}, "Play" }
+                    }
+                    li {
+                        Link { to: Route::Settings {}, "Settings" }
+                    }
                 }
             }
         }

+ 1 - 1
examples/form.rs

@@ -6,7 +6,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/generic_component.rs

@@ -3,7 +3,7 @@ use std::fmt::Display;
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/hello_world.rs

@@ -1,7 +1,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 4 - 4
examples/hydration.rs

@@ -13,15 +13,15 @@ use dioxus::prelude::*;
 use dioxus_desktop::Config;
 
 fn main() {
-    Config::new()
-        .with_prerendered({
+    LaunchBuilder::new(app)
+        .cfg(Config::new().with_prerendered({
             // We build the dom a first time, then pre-render it to HTML
             let pre_rendered_dom = VirtualDom::prebuilt(app);
 
             // We then launch the app with the pre-rendered HTML
             dioxus_ssr::pre_render(&pre_rendered_dom)
-        })
-        .launch(app);
+        }))
+        .launch();
 }
 
 fn app() -> Element {

+ 1 - 1
examples/inputs.rs

@@ -5,7 +5,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 const FIELDS: &[(&str, &str)] = &[

+ 1 - 1
examples/link.rs

@@ -2,7 +2,7 @@ use dioxus::prelude::*;
 use dioxus_router::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(App);
+    launch(App);
 }
 
 #[component]

+ 1 - 1
examples/login_form.rs

@@ -4,7 +4,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 9 - 5
examples/mobile_demo/src/lib.rs

@@ -49,8 +49,7 @@ pub fn main() -> Result<()> {
 
     // Right now we're going through dioxus-desktop but we'd like to go through dioxus-mobile
     // That will seed the index.html with some fixes that prevent the page from scrolling/zooming etc
-    dioxus_desktop::launch_cfg(
-        app,
+    LaunchBuilder::new(app).cfg(
         // Note that we have to disable the viewport goofiness of the browser.
         // Dioxus_mobile should do this for us
         Config::default().with_custom_index(include_str!("index.html").to_string()),
@@ -66,10 +65,15 @@ fn app() -> Element {
 
     render! {
         div {
-            h1 { "Hello, Mobile"}
-            div { margin_left: "auto", margin_right: "auto", width: "200px", padding: "10px", border: "1px solid black",
+            h1 { "Hello, Mobile" }
+            div {
+                margin_left: "auto",
+                margin_right: "auto",
+                width: "200px",
+                padding: "10px",
+                border: "1px solid black",
                 button {
-                    onclick: move|_| {
+                    onclick: move |_| {
                         println!("Clicked!");
                         items.push(items.len());
                         cx.needs_update_any(ScopeId::ROOT);

+ 1 - 1
examples/multiwindow.rs

@@ -1,7 +1,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/nested_listeners.rs

@@ -7,7 +7,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/optional_props.rs

@@ -7,7 +7,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/overlay.rs

@@ -2,7 +2,7 @@ use dioxus::prelude::*;
 use dioxus_desktop::{tao::dpi::PhysicalPosition, LogicalSize, WindowBuilder};
 
 fn main() {
-    dioxus_desktop::launch_cfg(app, make_config());
+    LaunchBuilder::new(app).cfg(make_config());
 }
 
 fn app() -> Element {

+ 1 - 1
examples/pattern_model.rs

@@ -32,7 +32,7 @@ fn main() {
             .with_inner_size(LogicalSize::new(320.0, 530.0)),
     );
 
-    dioxus_desktop::launch_cfg(app, cfg);
+    LaunchBuilder::new(app).cfg(cfg);
 }
 
 const STYLE: &str = include_str!("./assets/calculator.css");

+ 1 - 1
examples/pattern_reducer.rs

@@ -8,7 +8,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 2 - 6
examples/read_size.rs

@@ -4,8 +4,7 @@ use std::rc::Rc;
 use dioxus::{html::geometry::euclid::Rect, prelude::*};
 
 fn main() {
-    dioxus_desktop::launch_cfg(
-        app,
+    LaunchBuilder::new(app).cfg(
         dioxus_desktop::Config::default().with_custom_head(
             r#"
 <style type="text/css">
@@ -48,9 +47,6 @@ fn app() -> Element {
             "This element is {dimensions():?}"
         }
 
-        button {
-            onclick: read_dims,
-            "Read dimensions"
-        }
+        button { onclick: read_dims, "Read dimensions" }
     )
 }

+ 1 - 1
examples/readme.rs

@@ -5,7 +5,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/router.rs

@@ -5,7 +5,7 @@ fn main() {
     #[cfg(target_arch = "wasm32")]
     dioxus_web::launch(App);
     #[cfg(not(target_arch = "wasm32"))]
-    dioxus_desktop::launch(App);
+    launch(App);
 }
 
 // ANCHOR: router

+ 1 - 1
examples/rsx_usage.rs

@@ -40,7 +40,7 @@
 
 fn main() {
     todo!()
-    // dioxus_desktop::launch(App);
+    // launch(App);
 }
 
 // use core::{fmt, str::FromStr};

+ 1 - 1
examples/scroll_to_top.rs

@@ -1,7 +1,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/shortcut.rs

@@ -2,7 +2,7 @@ use dioxus::prelude::*;
 use dioxus_desktop::use_global_shortcut;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/shorthand.rs

@@ -1,7 +1,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/signals.rs

@@ -2,7 +2,7 @@ use dioxus::prelude::*;
 use std::time::Duration;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/simple_desktop.rs

@@ -9,7 +9,7 @@ fn main() {
         .with_module_level("dioxus", log::LevelFilter::Trace)
         .init()
         .unwrap();
-    dioxus_desktop::launch(App);
+    launch(App);
 }
 
 #[component]

+ 1 - 1
examples/simple_list.rs

@@ -1,7 +1,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/simple_router.rs

@@ -38,5 +38,5 @@ fn Nav() -> Element {
 }
 
 fn main() {
-    dioxus_desktop::launch(|| render!(Router::<Route> {}));
+    launch(|| render!(Router::<Route> {}));
 }

+ 1 - 1
examples/streams.rs

@@ -4,7 +4,7 @@ use futures_util::{future, stream, Stream, StreamExt};
 use std::time::Duration;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 12 - 19
examples/suspense.rs

@@ -17,19 +17,21 @@ use dioxus::prelude::*;
 use dioxus_desktop::{Config, LogicalSize, WindowBuilder};
 
 fn main() {
-    Config::new()
-        .with_window(
-            WindowBuilder::new()
-                .with_title("Doggo Fetcher")
-                .with_inner_size(LogicalSize::new(600.0, 800.0)),
+    LaunchBuilder::new(app)
+        .cfg(
+            Config::new().with_window(
+                WindowBuilder::new()
+                    .with_title("Doggo Fetcher")
+                    .with_inner_size(LogicalSize::new(600.0, 800.0)),
+            ),
         )
-        .launch(app)
+        .launch()
 }
 
 fn app() -> Element {
     rsx! {
         div {
-            h1 {"Dogs are very important"}
+            h1 { "Dogs are very important" }
             p {
                 "The dog or domestic dog (Canis familiaris[4][5] or Canis lupus familiaris[5])"
                 "is a domesticated descendant of the wolf which is characterized by an upturning tail."
@@ -39,7 +41,7 @@ fn app() -> Element {
             }
 
             h3 { "Illustrious Dog Photo" }
-            Doggo { }
+            Doggo {}
         }
     }
 }
@@ -63,17 +65,8 @@ fn Doggo() -> Element {
 
     match fut.value().read().as_ref() {
         Some(Ok(resp)) => rsx! {
-            button {
-                onclick: move |_| fut.restart(),
-                "Click to fetch another doggo"
-            }
-            div {
-                img {
-                    max_width: "500px",
-                    max_height: "500px",
-                    src: "{resp.message}",
-                }
-            }
+            button { onclick: move |_| fut.restart(), "Click to fetch another doggo" }
+            div { img { max_width: "500px", max_height: "500px", src: "{resp.message}" } }
         },
         Some(Err(_)) => rsx! { div { "loading dogs failed" } },
         None => rsx! { div { "loading dogs..." } },

+ 1 - 1
examples/svg.rs

@@ -4,7 +4,7 @@ use dioxus::prelude::*;
 use rand::{thread_rng, Rng};
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/svg_basic.rs

@@ -79,5 +79,5 @@ fn app() -> Element {
 }
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }

+ 1 - 1
examples/tailwind/src/main.rs

@@ -6,7 +6,7 @@ const _STYLE: &str = manganis::mg!(file("./public/tailwind.css"));
 
 fn main() {
     #[cfg(not(target_arch = "wasm32"))]
-    dioxus_desktop::launch(app);
+    launch(app);
     #[cfg(target_arch = "wasm32")]
     dioxus_web::launch(app);
 }

+ 1 - 1
examples/tasks.rs

@@ -6,7 +6,7 @@ use dioxus::prelude::*;
 use std::time::Duration;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/textarea.rs

@@ -3,7 +3,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/todomvc.rs

@@ -4,7 +4,7 @@ use dioxus_elements::input_data::keyboard_types::Key;
 use std::collections::HashMap;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 #[derive(PartialEq, Eq, Clone, Copy)]

+ 1 - 1
examples/video_stream.rs

@@ -26,7 +26,7 @@ fn main() {
                 }
             });
     }
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/web_component.rs

@@ -1,7 +1,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 18 - 13
examples/window_event.rs

@@ -2,13 +2,15 @@ use dioxus::prelude::*;
 use dioxus_desktop::{window, Config, WindowBuilder};
 
 fn main() {
-    Config::new()
-        .with_window(
-            WindowBuilder::new()
-                .with_title("Borderless Window")
-                .with_decorations(false),
+    LaunchBuilder::new(app)
+        .cfg(
+            Config::new().with_window(
+                WindowBuilder::new()
+                    .with_title("Borderless Window")
+                    .with_decorations(false),
+            ),
         )
-        .launch(app)
+        .launch()
 }
 
 fn app() -> Element {
@@ -17,11 +19,16 @@ fn app() -> Element {
     let mut 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(),
+        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",
                 a { class: "flex title-font font-medium items-center text-white mb-4 md:mb-0",
-                    span { class: "ml-3 text-xl", "Dioxus"}
+                    span { class: "ml-3 text-xl", "Dioxus" }
                 }
                 nav { class: "md:ml-auto flex flex-wrap items-center text-base justify-center" }
                 button {
@@ -49,10 +56,8 @@ fn app() -> Element {
             }
         }
         br {}
-        div {
-            class: "container mx-auto",
-            div {
-                class: "grid grid-cols-5",
+        div { class: "container mx-auto",
+            div { class: "grid grid-cols-5",
                 div {
                     button {
                         class: "inline-flex items-center text-white bg-green-500 border-0 py-1 px-3 hover:bg-green-700 rounded",

+ 4 - 9
examples/window_focus.rs

@@ -5,9 +5,9 @@ use dioxus_desktop::use_wry_event_handler;
 use dioxus_desktop::{Config, WindowCloseBehaviour};
 
 fn main() {
-    Config::new()
-        .with_close_behaviour(WindowCloseBehaviour::CloseWindow)
-        .launch(app)
+    LaunchBuilder::new(app)
+        .cfg(Config::new().with_close_behaviour(WindowCloseBehaviour::CloseWindow))
+        .launch()
 }
 
 fn app() -> Element {
@@ -22,12 +22,7 @@ fn app() -> Element {
     });
 
     rsx! {
-        div {
-            width: "100%",
-            height: "100%",
-            display: "flex",
-            flex_direction: "column",
-            align_items: "center",
+        div { width: "100%", height: "100%", display: "flex", flex_direction: "column", align_items: "center",
             if focused() {
                 "This window is focused!"
             } else {

+ 1 - 1
examples/window_zoom.rs

@@ -1,7 +1,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 1 - 1
examples/xss_safety.rs

@@ -5,7 +5,7 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus_desktop::launch(app);
+    launch(app);
 }
 
 fn app() -> Element {

+ 7 - 5
packages/core/src/lib.rs

@@ -13,6 +13,7 @@ mod fragment;
 mod global_context;
 mod mutations;
 mod nodes;
+mod platform;
 mod properties;
 mod runtime;
 mod scheduler;
@@ -30,6 +31,7 @@ pub(crate) mod innerlude {
     pub use crate::global_context::*;
     pub use crate::mutations::*;
     pub use crate::nodes::*;
+    pub use crate::platform::*;
     pub use crate::properties::*;
     pub use crate::runtime::{Runtime, RuntimeGuard};
     pub use crate::scheduler::*;
@@ -73,11 +75,11 @@ pub(crate) mod innerlude {
 
 pub use crate::innerlude::{
     fc_to_builder, generation, schedule_update, schedule_update_any, use_hook, vdom_is_rendering,
-    AnyValue, Attribute, AttributeValue, CapturedError, Component, ComponentFunction, DynamicNode,
-    Element, ElementId, Event, Fragment, HasAttributes, IntoDynNode, Mutation, Mutations,
-    NoOpMutations, Properties, RenderReturn, ScopeId, ScopeState, Task, Template,
-    TemplateAttribute, TemplateNode, VComponent, VNode, VNodeInner, VPlaceholder, VText,
-    VirtualDom, WriteMutations,
+    AnyValue, Attribute, AttributeValue, BoxedContext, CapturedError, Component, ComponentFunction,
+    CrossPlatformConfig, DynamicNode, Element, ElementId, Event, Fragment, HasAttributes,
+    IntoDynNode, Mutation, Mutations, NoOpMutations, PlatformBuilder, Properties, RenderReturn,
+    ScopeId, ScopeState, Task, Template, TemplateAttribute, TemplateNode, VComponent, VNode,
+    VNodeInner, VPlaceholder, VText, VirtualDom, WriteMutations,
 };
 
 /// The purpose of this module is to alleviate imports of many common types

+ 106 - 0
packages/core/src/platform.rs

@@ -0,0 +1,106 @@
+use std::{any::Any, marker::PhantomData};
+
+use crate::{ComponentFunction, VirtualDom};
+
+/// A boxed object that can be injected into a component's context.
+pub struct BoxedContext(Box<dyn ClonableAny>);
+
+impl BoxedContext {
+    /// Create a new boxed context.
+    pub fn new(value: impl Any + Clone + 'static) -> Self {
+        Self(Box::new(value))
+    }
+
+    /// Unwrap the boxed context into its inner value.
+    pub fn into_inner(self) -> Box<dyn Any> {
+        self.0.into_inner()
+    }
+}
+
+impl Clone for BoxedContext {
+    fn clone(&self) -> Self {
+        Self(self.0.clone_box())
+    }
+}
+
+trait ClonableAny: Any {
+    fn clone_box(&self) -> Box<dyn ClonableAny>;
+
+    fn into_inner(self: Box<Self>) -> Box<dyn Any>;
+}
+
+impl<T: Any + Clone> ClonableAny for T {
+    fn clone_box(&self) -> Box<dyn ClonableAny> {
+        Box::new(self.clone())
+    }
+
+    fn into_inner(self: Box<Self>) -> Box<dyn Any> {
+        self
+    }
+}
+
+/// The platform-independent part of the config needed to launch an application.
+pub struct CrossPlatformConfig<
+    Component: ComponentFunction<Phantom, Props = Props>,
+    Props: Clone + 'static,
+    Phantom: 'static,
+> {
+    /// The root component function.
+    pub component: Component,
+    /// The props for the root component.
+    pub props: Props,
+    /// The contexts to provide to the root component.
+    pub root_contexts: Vec<BoxedContext>,
+    _phantom: PhantomData<Phantom>,
+}
+
+impl<
+        Component: ComponentFunction<Phantom, Props = Props>,
+        Props: Clone + 'static,
+        Phantom: 'static,
+    > CrossPlatformConfig<Component, Props, Phantom>
+{
+    /// Create a new cross-platform config.
+    pub fn new(component: Component, props: Props, root_contexts: Vec<BoxedContext>) -> Self {
+        Self {
+            component,
+            props,
+            root_contexts,
+            _phantom: PhantomData,
+        }
+    }
+
+    /// Build a virtual dom from the config.
+    pub fn build_vdom(self) -> VirtualDom {
+        let mut vdom = VirtualDom::new_with_props(self.component, self.props);
+
+        for context in self.root_contexts {
+            vdom.insert_boxed_root_context(context);
+        }
+
+        vdom
+    }
+}
+
+/// A builder to launch a specific platform.
+pub trait PlatformBuilder<Props: Clone + 'static> {
+    /// The platform-specific config needed to launch an application.
+    type Config: Default;
+
+    /// Launch the app.
+    fn launch<Component: ComponentFunction<Phantom, Props = Props>, Phantom: 'static>(
+        config: CrossPlatformConfig<Component, Props, Phantom>,
+        platform_config: Self::Config,
+    );
+}
+
+impl<Props: Clone + 'static> PlatformBuilder<Props> for () {
+    type Config = ();
+
+    fn launch<Component: ComponentFunction<Phantom, Props = Props>, Phantom: 'static>(
+        _: CrossPlatformConfig<Component, Props, Phantom>,
+        _: Self::Config,
+    ) {
+        panic!("No platform is currently enabled. Please enable a platform feature for the dioxus crate.");
+    }
+}

+ 1 - 2
packages/core/src/scope_context.rs

@@ -309,12 +309,11 @@ impl ScopeContext {
 impl Drop for ScopeContext {
     fn drop(&mut self) {
         // Drop all spawned tasks
-        with_runtime(|rt| {
+        _ = with_runtime(|rt| {
             for id in self.spawned_tasks.borrow().iter() {
                 rt.remove_task(*id);
             }
         })
-        .expect("Runtime to exist")
     }
 }
 

+ 10 - 4
packages/core/src/virtual_dom.rs

@@ -227,7 +227,7 @@ impl VirtualDom {
     ///
     /// Note: the VirtualDom is not progressed, you must either "run_with_deadline" or use "rebuild" to progress it.
     pub fn new(app: fn() -> Element) -> Self {
-        Self::new_with_props(|app| app(), app)
+        Self::new_with_props(app, ())
     }
 
     /// Create a new virtualdom and build it immediately
@@ -267,7 +267,14 @@ impl VirtualDom {
     /// let mut dom = VirtualDom::new_with_props(Example, SomeProps { name: "jane" });
     /// let mutations = dom.rebuild();
     /// ```
-    pub fn new_with_props<P: Clone + 'static>(root: fn(P) -> Element, root_props: P) -> Self {
+    pub fn new_with_props<
+        F: crate::ComponentFunction<Phantom, Props = P>,
+        P: Clone + 'static,
+        Phantom: 'static,
+    >(
+        root: F,
+        root_props: P,
+    ) -> Self {
         let (tx, rx) = futures_channel::mpsc::unbounded();
 
         let mut dom = Self {
@@ -328,11 +335,10 @@ impl VirtualDom {
     /// Build the virtualdom with a global context inserted into the base scope
     ///
     /// This method is useful for when you want to provide a context in your app without knowing its type
-    pub fn with_boxed_root_context(self, context: BoxedContext) -> Self {
+    pub fn insert_boxed_root_context(&mut self, context: BoxedContext) {
         self.base_scope()
             .context()
             .provide_any_context(context.into_inner());
-        self
     }
 
     /// Manually mark a scope as requiring a re-render

+ 24 - 14
packages/desktop/src/app.rs

@@ -10,7 +10,7 @@ use crate::{
     webview::WebviewInstance,
 };
 use crossbeam_channel::Receiver;
-use dioxus_core::{Component, ElementId, VirtualDom};
+use dioxus_core::{ComponentFunction, CrossPlatformConfig, ElementId};
 use dioxus_html::{
     native_bind::NativeFileEngine, FileEngine, HasFileData, HasFormData, HtmlEvent,
     PlatformEventData,
@@ -18,6 +18,7 @@ use dioxus_html::{
 use std::{
     cell::{Cell, RefCell},
     collections::HashMap,
+    marker::PhantomData,
     rc::Rc,
     sync::Arc,
 };
@@ -28,14 +29,17 @@ use tao::{
 };
 
 /// The single top-level object that manages all the running windows, assets, shortcuts, etc
-pub(crate) struct App<P> {
+pub(crate) struct App<
+    Component: ComponentFunction<Phantom, Props = Props>,
+    Props: Clone + 'static,
+    Phantom: 'static,
+> {
     // move the props into a cell so we can pop it out later to create the first window
     // iOS panics if we create a window before the event loop is started, so we toss them into a cell
-    pub(crate) props: Cell<Option<P>>,
+    pub(crate) dioxus_config: Cell<Option<CrossPlatformConfig<Component, Props, Phantom>>>,
     pub(crate) cfg: Cell<Option<Config>>,
 
     // Stuff we need mutable access to
-    pub(crate) root: Component<P>,
     pub(crate) control_flow: ControlFlow,
     pub(crate) is_visible_before_start: bool,
     pub(crate) window_behavior: WindowCloseBehaviour,
@@ -45,6 +49,8 @@ pub(crate) struct App<P> {
     ///
     /// This includes stuff like the event handlers, shortcuts, etc as well as ways to modify *other* windows
     pub(crate) shared: Rc<SharedContext>,
+
+    phantom: PhantomData<Phantom>,
 }
 
 /// A bundle of state shared between all the windows, providing a way for us to communicate with running webview.
@@ -59,17 +65,24 @@ pub struct SharedContext {
     pub(crate) target: EventLoopWindowTarget<UserWindowEvent>,
 }
 
-impl<P: 'static + Clone> App<P> {
-    pub fn new(cfg: Config, props: P, root: Component<P>) -> (EventLoop<UserWindowEvent>, Self) {
+impl<
+        Component: ComponentFunction<Phantom, Props = Props>,
+        Props: Clone + 'static,
+        Phantom: 'static,
+    > App<Component, Props, Phantom>
+{
+    pub fn new(
+        cfg: Config,
+        dioxus_config: CrossPlatformConfig<Component, Props, Phantom>,
+    ) -> (EventLoop<UserWindowEvent>, Self) {
         let event_loop = EventLoopBuilder::<UserWindowEvent>::with_user_event().build();
 
         let app = Self {
-            root,
             window_behavior: cfg.last_window_close_behaviour,
             is_visible_before_start: true,
             webviews: HashMap::new(),
             control_flow: ControlFlow::Wait,
-            props: Cell::new(Some(props)),
+            dioxus_config: Cell::new(Some(dioxus_config)),
             cfg: Cell::new(Some(cfg)),
             shared: Rc::new(SharedContext {
                 event_handlers: WindowEventHandlers::default(),
@@ -79,6 +92,7 @@ impl<P: 'static + Clone> App<P> {
                 proxy: event_loop.create_proxy(),
                 target: event_loop.clone(),
             }),
+            phantom: PhantomData,
         };
 
         // Set the event converter
@@ -164,16 +178,12 @@ impl<P: 'static + Clone> App<P> {
     }
 
     pub fn handle_start_cause_init(&mut self) {
-        let props = self.props.take().unwrap();
+        let dioxus_config = self.dioxus_config.take().unwrap();
         let cfg = self.cfg.take().unwrap();
 
         self.is_visible_before_start = cfg.window.window.visible;
 
-        let webview = WebviewInstance::new(
-            cfg,
-            VirtualDom::new_with_props(self.root, props),
-            self.shared.clone(),
-        );
+        let webview = WebviewInstance::new(cfg, dioxus_config.build_vdom(), self.shared.clone());
 
         let id = webview.desktop_context.window.id();
         self.webviews.insert(id, webview);

+ 0 - 14
packages/desktop/src/config.rs

@@ -71,20 +71,6 @@ 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, app: fn() -> Element) {
-        self.launch_with_props(|props| props(), app)
-    }
-
-    /// Launch a Dioxus app using the given component, config, and props
-    ///
-    /// See the [`crate::launch::launch_with_props`] function for more details.
-    pub fn launch_with_props<P: 'static + Clone>(self, root: Component<P>, props: P) {
-        crate::launch::launch_with_props(root, props, self)
-    }
-
     /// Set whether the default menu bar should be enabled.
     ///
     /// > Note: `enable` is `true` by default. To disable the default menu bar pass `false`.

+ 33 - 86
packages/desktop/src/launch.rs

@@ -6,96 +6,19 @@ use crate::{
 use dioxus_core::*;
 use tao::event::{Event, StartCause, WindowEvent};
 
-/// Launch the WebView and run the event loop.
-///
-/// This function will start a multithreaded Tokio runtime as well the WebView event loop.
-///
-/// ```rust, no_run
-/// use dioxus::prelude::*;
-///
-/// fn main() {
-///     dioxus_desktop::launch(app);
-/// }
-///
-/// fn app() -> Element {
-///     rsx!{
-///         h1 {"hello world!"}
-///     })
-/// }
-/// ```
-pub fn launch(app: fn() -> Element) {
-    launch_with_props(|root| root(), app, Config::default())
-}
-
-/// Launch the WebView and run the event loop, with configuration.
-///
-/// This function will start a multithreaded Tokio runtime as well the WebView event loop.
-///
-/// You can configure the WebView window with a configuration closure
-///
-/// ```rust, no_run
-/// use dioxus::prelude::*;
-/// use dioxus_desktop::*;
-///
-/// fn main() {
-///     dioxus_desktop::launch_cfg(app, Config::default().with_window(WindowBuilder::new().with_title("My App")));
-/// }
-///
-/// fn app() -> Element {
-///     rsx!{
-///         h1 {"hello world!"}
-///     })
-/// }
-/// ```
-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.
-///
-/// If the [`tokio`] feature is enabled, this will also startup and block a tokio runtime using the unconstrained task.
-/// This function will start a multithreaded Tokio runtime as well the WebView event loop. This will block the current thread.
-///
-/// You can configure the WebView window with a configuration closure
-///
-/// ```rust, no_run
-/// use dioxus::prelude::*;
-/// use dioxus_desktop::Config;
-///
-/// fn main() {
-///     dioxus_desktop::launch_with_props(app, AppProps { name: "asd" }, Config::default());
-/// }
-///
-/// struct AppProps {
-///     name: &'static str
-/// }
-///
-/// fn app(cx: Scope<AppProps>) -> Element {
-///     rsx!{
-///         h1 {"hello {cx.props.name}!"}
-///     })
-/// }
-/// ```
-pub fn launch_with_props<P: 'static + Clone>(root: Component<P>, props: P, cfg: Config) {
-    #[cfg(feature = "tokio")]
-    tokio::runtime::Builder::new_multi_thread()
-        .enable_all()
-        .build()
-        .unwrap()
-        .block_on(tokio::task::unconstrained(async move {
-            launch_with_props_blocking(root, props, cfg);
-        }));
-
-    #[cfg(not(feature = "tokio"))]
-    launch_with_props_blocking(root, props, cfg);
-}
-
 /// Launch the WebView and run the event loop, with configuration and root props.
 ///
 /// This will block the main thread, and *must* be spawned on the main thread. This function does not assume any runtime
 /// and is equivalent to calling launch_with_props with the tokio feature disabled.
-pub fn launch_with_props_blocking<P: 'static + Clone>(root: Component<P>, props: P, cfg: Config) {
-    let (event_loop, mut app) = App::new(cfg, props, root);
+pub fn launch_with_props_blocking<
+    Component: ComponentFunction<Phantom, Props = Props>,
+    Props: Clone + 'static,
+    Phantom: 'static,
+>(
+    dioxus_cfg: CrossPlatformConfig<Component, Props, Phantom>,
+    desktop_config: Config,
+) {
+    let (event_loop, mut app) = App::new(desktop_config, dioxus_cfg);
 
     event_loop.run(move |window_event, _, control_flow| {
         app.tick(&window_event);
@@ -130,3 +53,27 @@ pub fn launch_with_props_blocking<P: 'static + Clone>(root: Component<P>, props:
         *control_flow = app.control_flow;
     })
 }
+
+/// The desktop renderer platform
+pub struct DesktopPlatform;
+
+impl<Props: Clone + 'static> PlatformBuilder<Props> for DesktopPlatform {
+    type Config = Config;
+
+    fn launch<Component: ComponentFunction<Phantom, Props = Props>, Phantom: 'static>(
+        config: dioxus_core::CrossPlatformConfig<Component, Props, Phantom>,
+        platform_config: Self::Config,
+    ) {
+        #[cfg(feature = "tokio")]
+        tokio::runtime::Builder::new_multi_thread()
+            .enable_all()
+            .build()
+            .unwrap()
+            .block_on(tokio::task::unconstrained(async move {
+                launch_with_props_blocking(config, platform_config)
+            }));
+
+        #[cfg(not(feature = "tokio"))]
+        launch_with_props_blocking(config, platform_config)
+    }
+}

+ 68 - 62
packages/dioxus/src/launch.rs

@@ -5,98 +5,104 @@ use std::any::Any;
 use crate::prelude::*;
 use dioxus_core::prelude::*;
 use dioxus_core::ComponentFunction;
-
-pub trait ClonableAny: Any {
-    fn clone_box(&self) -> Box<dyn ClonableAny>;
-}
-
-impl<T: Any + Clone> ClonableAny for T {
-    fn clone_box(&self) -> Box<dyn ClonableAny> {
-        Box::new(self.clone())
-    }
-}
-
-/// The platform-independent part of the config needed to launch an application.
-pub struct CrossPlatformConfig<F: ComponentFunction<P>, P> {
-    /// The root component function.
-    pub component: F,
-    /// The props for the root component.
-    pub props: P,
-    /// The contexts to provide to the root component.
-    pub root_contexts: Vec<Box<dyn ClonableAny>>,
-}
-
-pub trait PlatformBuilder<P> {
-    type Config;
-
-    /// Launch the app.
-    fn launch<F: ComponentFunction<P>>(config: CrossPlatformConfig<F, P>, config: Self::Config);
-}
-
-impl<P> PlatformBuilder<P> for () {
-    type Config = ();
-
-    fn launch<F: ComponentFunction<P>>(config: CrossPlatformConfig<F, P>, _: ()) {}
-}
+use dioxus_core::{BoxedContext, CrossPlatformConfig, PlatformBuilder};
 
 /// A builder for a fullstack app.
-pub struct LaunchBuilder<F: ComponentFunction<P>, P> {
-    cross_platform_config: CrossPlatformConfig<F, P>,
+pub struct LaunchBuilder<
+    Component: ComponentFunction<Phantom, Props = Props>,
+    Props: Clone + 'static,
+    Phantom: 'static,
+> {
+    cross_platform_config: CrossPlatformConfig<Component, Props, Phantom>,
+    platform_config: Option<<CurrentPlatform as PlatformBuilder<Props>>::Config>,
 }
 
-impl<F: ComponentFunction<P>, P> LaunchBuilder<F, P> {
+impl<
+        Component: ComponentFunction<Phantom, Props = Props>,
+        Props: Clone + 'static,
+        Phantom: 'static,
+    > LaunchBuilder<Component, Props, Phantom>
+{
     /// Create a new builder for your application.
-    pub fn new(component: F) -> Self
+    pub fn new(component: Component) -> Self
     where
-        P: Default,
+        Props: Default,
     {
         Self {
-            cross_platform_config: CrossPlatformConfig {
+            cross_platform_config: CrossPlatformConfig::new(
                 component,
-                props: Default::default(),
-                root_contexts: vec![],
-            },
+                Default::default(),
+                Default::default(),
+            ),
+            platform_config: None,
         }
     }
 
     /// Pass some props to your application.
-    pub fn props(mut self, props: P) -> Self {
+    pub fn props(mut self, props: Props) -> Self {
         self.cross_platform_config.props = props;
         self
     }
 
     /// Inject state into the root component's context.
-    pub fn context(mut self, state: impl ClonableAny + 'static) -> Self {
+    pub fn context(mut self, state: impl Any + Clone + 'static) -> Self {
         self.cross_platform_config
             .root_contexts
-            .push(Box::new(state));
+            .push(BoxedContext::new(state));
         self
     }
 
     /// Provide a platform-specific config to the builder.
-    pub fn platform_config(
-        self,
-        config: Option<<CurrentPlatform as PlatformBuilder<P>>::Config>,
+    pub fn cfg(
+        mut self,
+        config: impl Into<Option<<CurrentPlatform as PlatformBuilder<Props>>::Config>>,
     ) -> Self {
+        if let Some(config) = config.into() {
+            self.platform_config = Some(config);
+        }
         self
     }
 
+    #[allow(clippy::unit_arg)]
     /// Launch the app.
-    pub fn launch(self) {}
-}
-
-#[cfg(feature = "router")]
-impl<R: Routable> LaunchBuilder<crate::router::FullstackRouterConfig<R>>
-where
-    <R as std::str::FromStr>::Err: std::fmt::Display,
-    R: Clone + serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static,
-{
-    /// Create a new launch builder for the given router.
-    pub fn router() -> Self {
-        let component = crate::router::RouteWithCfg::<R>;
-        let props = crate::router::FullstackRouterConfig::default();
-        Self::new_with_props(component, props)
+    pub fn launch(self) {
+        CurrentPlatform::launch(
+            self.cross_platform_config,
+            self.platform_config.unwrap_or_default(),
+        );
     }
 }
 
+// #[cfg(feature = "router")]
+// impl<R: Routable> LaunchBuilder<crate::router::FullstackRouterConfig<R>>
+// where
+//     <R as std::str::FromStr>::Err: std::fmt::Display,
+//     R: Clone + serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static,
+// {
+//     /// Create a new launch builder for the given router.
+//     pub fn router() -> Self {
+//         let component = crate::router::RouteWithCfg::<R>;
+//         let props = crate::router::FullstackRouterConfig::default();
+//         Self::new_with_props(component, props)
+//     }
+// }
+
+#[cfg(feature = "desktop")]
+type CurrentPlatform = dioxus_desktop::DesktopPlatform;
+#[cfg(feature = "web")]
+type CurrentPlatform = dioxus_web::WebPlatform;
+#[cfg(not(any(feature = "desktop", feature = "web")))]
 type CurrentPlatform = ();
+
+/// Launch your application without any additional configuration. See [`LaunchBuilder`] for more options.
+pub fn launch<
+    Component: ComponentFunction<Phantom, Props = Props>,
+    Props: Clone + 'static,
+    Phantom: 'static,
+>(
+    component: Component,
+) where
+    Props: Default,
+{
+    LaunchBuilder::new(component).launch()
+}

+ 3 - 2
packages/dioxus/src/lib.rs

@@ -6,8 +6,6 @@ pub use dioxus_core;
 
 #[cfg(feature = "launch")]
 mod launch;
-#[cfg(feature = "launch")]
-pub use launch::*;
 
 #[cfg(feature = "hooks")]
 pub use dioxus_hooks as hooks;
@@ -30,6 +28,9 @@ pub use dioxus_rsx as rsx;
 pub use dioxus_core_macro as core_macro;
 
 pub mod prelude {
+    #[cfg(feature = "launch")]
+    pub use crate::launch::*;
+
     #[cfg(feature = "hooks")]
     pub use crate::hooks::*;
 

+ 13 - 91
packages/web/src/lib.rs

@@ -60,7 +60,7 @@ use std::rc::Rc;
 pub use crate::cfg::Config;
 #[cfg(feature = "file_engine")]
 pub use crate::file_engine::WebFileEngineExt;
-use dioxus_core::{Element, VirtualDom};
+use dioxus_core::{ComponentFunction, CrossPlatformConfig, VirtualDom};
 use futures_util::{
     future::{select, Either},
     pin_mut, FutureExt, StreamExt,
@@ -72,7 +72,9 @@ mod dom;
 mod eval;
 mod event;
 mod mutations;
+mod platform;
 pub use event::*;
+pub use platform::*;
 #[cfg(feature = "file_engine")]
 mod file_engine;
 #[cfg(all(feature = "hot_reload", debug_assertions))]
@@ -85,89 +87,6 @@ mod rehydrate;
 // mod ric_raf;
 // mod rehydrate;
 
-/// Launch the VirtualDOM given a root component and a configuration.
-///
-/// This function expects the root component to not have root props. To launch the root component with root props, use
-/// `launch_with_props` instead.
-///
-/// This method will block the thread with `spawn_local` from wasm_bindgen_futures.
-///
-/// If you need to run the VirtualDOM in its own thread, use `run_with_props` instead and await the future.
-///
-/// # Example
-///
-/// ```rust, ignore
-/// fn main() {
-///     dioxus_web::launch(App);
-/// }
-///
-/// static App: Component = |cx| {
-///     render!(div {"hello world"})
-/// }
-/// ```
-pub fn launch(root_component: fn() -> Element) {
-    launch_with_props(
-        |root_component| root_component(),
-        root_component,
-        Config::default(),
-    );
-}
-
-/// Launch your app and run the event loop, with configuration.
-///
-/// This function will start your web app on the main web thread.
-///
-/// You can configure the WebView window with a configuration closure
-///
-/// ```rust, ignore
-/// use dioxus::prelude::*;
-///
-/// fn main() {
-///     dioxus_web::launch_with_props(App, Config::new().pre_render(true));
-/// }
-///
-/// fn app() -> Element {
-///     rsx!{
-///         h1 {"hello world!"}
-///     })
-/// }
-/// ```
-pub fn launch_cfg(root: fn(()) -> Element, config: Config) {
-    launch_with_props(root, (), config)
-}
-
-/// Launches the VirtualDOM from the specified component function and props.
-///
-/// This method will block the thread with `spawn_local`
-///
-/// # Example
-///
-/// ```rust, ignore
-/// fn main() {
-///     dioxus_web::launch_with_props(
-///         App,
-///         RootProps { name: String::from("joe") },
-///         Config::new()
-///     );
-/// }
-///
-/// #[derive(ParitalEq, Props)]
-/// struct RootProps {
-///     name: String
-/// }
-///
-/// static App: Component<RootProps> = |cx| {
-///     render!(div {"hello {cx.props.name}"})
-/// }
-/// ```
-pub fn launch_with_props<T: Clone + 'static>(
-    root_component: fn(T) -> Element,
-    root_properties: T,
-    config: Config,
-) {
-    wasm_bindgen_futures::spawn_local(run_with_props(root_component, root_properties, config));
-}
-
 /// Runs the app as a future that can be scheduled around the main thread.
 ///
 /// Polls futures internal to the VirtualDOM, hence the async nature of this function.
@@ -180,14 +99,17 @@ pub fn launch_with_props<T: Clone + 'static>(
 ///     wasm_bindgen_futures::spawn_local(app_fut);
 /// }
 /// ```
-pub async fn run_with_props<T: Clone + 'static>(
-    root: fn(T) -> Element,
-    root_props: T,
-    cfg: Config,
+pub async fn run_with_props<
+    Component: ComponentFunction<Phantom, Props = Props>,
+    Props: Clone + 'static,
+    Phantom: 'static,
+>(
+    dioxus_config: CrossPlatformConfig<Component, Props, Phantom>,
+    web_config: Config,
 ) {
     tracing::info!("Starting up");
 
-    let mut dom = VirtualDom::new_with_props(root, root_props);
+    let mut dom = dioxus_config.build_vdom();
 
     #[cfg(feature = "eval")]
     {
@@ -198,7 +120,7 @@ pub async fn run_with_props<T: Clone + 'static>(
     }
 
     #[cfg(feature = "panic_hook")]
-    if cfg.default_panic_hook {
+    if web_config.default_panic_hook {
         console_error_panic_hook::set_once();
     }
 
@@ -212,7 +134,7 @@ pub async fn run_with_props<T: Clone + 'static>(
     #[cfg(not(feature = "hydrate"))]
     let should_hydrate = false;
 
-    let mut websys_dom = dom::WebsysDom::new(cfg, tx);
+    let mut websys_dom = dom::WebsysDom::new(web_config, tx);
 
     tracing::info!("rebuilding app");
 

+ 19 - 0
packages/web/src/platform.rs

@@ -0,0 +1,19 @@
+use dioxus_core::*;
+
+use crate::Config;
+
+/// The web renderer platform
+pub struct WebPlatform;
+
+impl<Props: Clone + 'static> PlatformBuilder<Props> for WebPlatform {
+    type Config = Config;
+
+    fn launch<Component: ComponentFunction<Phantom, Props = Props>, Phantom: 'static>(
+        config: CrossPlatformConfig<Component, Props, Phantom>,
+        platform_config: Self::Config,
+    ) {
+        wasm_bindgen_futures::spawn_local(async move {
+            crate::run_with_props(config, platform_config).await;
+        });
+    }
+}