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