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