dioxus_native/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3//! A native renderer for Dioxus.
4//!
5//! ## Feature flags
6//!  - `default`: Enables the features listed below.
7//!  - `accessibility`: Enables [`accesskit`](https://docs.rs/accesskit/latest/accesskit/) accessibility support.
8//!  - `hot-reload`: Enables hot-reloading of Dioxus RSX.
9//!  - `menu`: Enables the [`muda`](https://docs.rs/muda/latest/muda/) menubar.
10//!  - `tracing`: Enables tracing support.
11
12mod assets;
13mod config;
14mod contexts;
15mod dioxus_application;
16mod dioxus_renderer;
17mod link_handler;
18
19#[cfg(feature = "prelude")]
20pub mod prelude;
21
22#[doc(inline)]
23pub use dioxus_native_dom::*;
24
25pub use anyrender_vello::{CustomPaintCtx, CustomPaintSource, DeviceHandle, TextureHandle};
26use assets::DioxusNativeNetProvider;
27pub use dioxus_application::{DioxusNativeApplication, DioxusNativeEvent};
28pub use dioxus_renderer::{DioxusNativeWindowRenderer, Features, Limits};
29
30#[cfg(not(all(target_os = "ios", target_abi = "sim")))]
31pub use dioxus_renderer::use_wgpu;
32
33pub use config::Config;
34pub use winit::dpi::{LogicalSize, PhysicalSize};
35pub use winit::window::WindowAttributes;
36
37use blitz_shell::{create_default_event_loop, BlitzShellEvent, WindowConfig};
38use dioxus_core::{ComponentFunction, Element, VirtualDom};
39use link_handler::DioxusNativeNavigationProvider;
40use std::any::Any;
41use std::sync::Arc;
42
43/// Launch an interactive HTML/CSS renderer driven by the Dioxus virtualdom
44pub fn launch(app: fn() -> Element) {
45    launch_cfg(app, vec![], vec![])
46}
47
48pub fn launch_cfg(
49    app: fn() -> Element,
50    contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
51    cfg: Vec<Box<dyn Any>>,
52) {
53    launch_cfg_with_props(app, (), contexts, cfg)
54}
55
56// todo: props shouldn't have the clone bound - should try and match dioxus-desktop behavior
57pub fn launch_cfg_with_props<P: Clone + 'static, M: 'static>(
58    app: impl ComponentFunction<P, M>,
59    props: P,
60    contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
61    configs: Vec<Box<dyn Any>>,
62) {
63    // Macro to attempt to downcast a type out of a Box<dyn Any>
64    macro_rules! try_read_config {
65        ($input:ident, $store:ident, $kind:ty) => {
66            // Try to downcast the Box<dyn Any> to type $kind
67            match $input.downcast::<$kind>() {
68                // If the type matches then write downcast value to variable $store
69                Ok(value) => {
70                    $store = Some(*value);
71                    continue;
72                }
73                // Else extract the original Box<dyn Any> value out of the error type
74                // and return it so that we can try again with a different type.
75                Err(cfg) => cfg,
76            }
77        };
78    }
79
80    // Read config values
81    let mut features = None;
82    let mut limits = None;
83    let mut window_attributes = None;
84    let mut config = None;
85    for mut cfg in configs {
86        cfg = try_read_config!(cfg, features, Features);
87        cfg = try_read_config!(cfg, limits, Limits);
88        cfg = try_read_config!(cfg, window_attributes, WindowAttributes);
89        cfg = try_read_config!(cfg, config, Config);
90        let _ = cfg;
91    }
92
93    let mut config = config.unwrap_or_default();
94    if let Some(window_attributes) = window_attributes {
95        config.window_attributes = window_attributes;
96    }
97    let event_loop = create_default_event_loop::<BlitzShellEvent>();
98
99    // Turn on the runtime and enter it
100    #[cfg(feature = "net")]
101    let rt = tokio::runtime::Builder::new_multi_thread()
102        .enable_all()
103        .build()
104        .unwrap();
105    #[cfg(feature = "net")]
106    let _guard = rt.enter();
107
108    // Setup hot-reloading if enabled.
109    #[cfg(all(
110        feature = "hot-reload",
111        debug_assertions,
112        not(target_os = "android"),
113        not(target_os = "ios")
114    ))]
115    {
116        let proxy = event_loop.create_proxy();
117        dioxus_devtools::connect(move |event| {
118            let dxn_event = DioxusNativeEvent::DevserverEvent(event);
119            let _ = proxy.send_event(BlitzShellEvent::embedder_event(dxn_event));
120        })
121    }
122
123    // Spin up the virtualdom
124    // We're going to need to hit it with a special waker
125    // Note that we are delaying the initialization of window-specific contexts (net provider, document, etc)
126    let mut vdom = VirtualDom::new_with_props(app, props);
127
128    // Add contexts
129    for context in contexts {
130        vdom.insert_any_root_context(context());
131    }
132
133    let net_provider = Some(DioxusNativeNetProvider::shared(event_loop.create_proxy()));
134
135    #[cfg(feature = "html")]
136    let html_parser_provider = Some(Arc::new(blitz_html::HtmlProvider) as _);
137    #[cfg(not(feature = "html"))]
138    let html_parser_provider = None;
139
140    let navigation_provider = Some(Arc::new(DioxusNativeNavigationProvider) as _);
141
142    // Create document + window from the baked virtualdom
143    let doc = DioxusDocument::new(
144        vdom,
145        DocumentConfig {
146            net_provider,
147            html_parser_provider,
148            navigation_provider,
149            ..Default::default()
150        },
151    );
152    #[cfg(not(all(target_os = "ios", target_abi = "sim")))]
153    let renderer = DioxusNativeWindowRenderer::with_features_and_limits(features, limits);
154    #[cfg(all(target_os = "ios", target_abi = "sim"))]
155    let renderer = DioxusNativeWindowRenderer::new();
156    let config = WindowConfig::with_attributes(
157        Box::new(doc) as _,
158        renderer.clone(),
159        config.window_attributes,
160    );
161
162    // Create application
163    let mut application = DioxusNativeApplication::new(event_loop.create_proxy(), config);
164
165    // Run event loop
166    event_loop.run_app(&mut application).unwrap();
167}