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