Skip to main content

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 event_handlers;
18mod hooks;
19mod link_handler;
20
21#[cfg(feature = "prelude")]
22pub mod prelude;
23
24#[cfg(all(feature = "net", not(target_arch = "wasm32")))]
25use blitz_traits::net::NetProvider;
26#[doc(inline)]
27pub use dioxus_native_dom::*;
28
29use assets::DioxusNativeNetProvider;
30pub use dioxus_application::{DioxusNativeApplication, DioxusNativeEvent};
31pub use dioxus_renderer::DioxusNativeWindowRenderer;
32
33#[cfg(target_os = "android")]
34#[cfg_attr(docsrs, doc(cfg(target_os = "android")))]
35/// Set the current [`AndroidApp`](android_activity::AndroidApp).
36pub fn set_android_app(app: android_activity::AndroidApp) {
37    blitz_shell::set_android_app(app);
38}
39
40#[cfg(target_os = "android")]
41#[cfg_attr(docsrs, doc(cfg(target_os = "android")))]
42/// Get the current [`AndroidApp`](android_activity::AndroidApp).
43/// This will panic if the android activity has not been setup with [`set_android_app`].
44pub fn current_android_app() -> android_activity::AndroidApp {
45    blitz_shell::current_android_app()
46}
47
48#[cfg(target_os = "android")]
49#[cfg_attr(docsrs, doc(cfg(target_os = "android")))]
50pub use android_activity::AndroidApp;
51
52#[cfg(any(feature = "vello", feature = "vello-hybrid"))]
53pub use {
54    dioxus_renderer::{Features, Limits},
55    wgpu_context::DeviceHandle,
56};
57
58pub use blitz_dom::{FontContext, Widget, build_single_font_ctx};
59pub use config::Config;
60pub use event_handlers::WinitEventHandlerId;
61pub use hooks::{use_back_button, use_window_event};
62pub use winit;
63pub use winit::dpi::{LogicalSize, PhysicalSize};
64pub use winit::window::WindowAttributes;
65
66use blitz_shell::{BlitzShellEvent, BlitzShellProxy, WindowConfig, create_default_event_loop};
67use dioxus_core::{ComponentFunction, Element, VirtualDom, consume_context, use_hook};
68use link_handler::DioxusNativeNavigationProvider;
69use std::any::Any;
70use std::sync::Arc;
71use winit::{
72    raw_window_handle::{HasWindowHandle as _, RawWindowHandle},
73    window::Window,
74};
75
76pub fn use_window() -> Arc<dyn Window> {
77    use_hook(consume_context::<Arc<dyn Window>>)
78}
79
80pub fn use_raw_window_handle() -> RawWindowHandle {
81    use_hook(|| {
82        consume_context::<Arc<dyn Window>>()
83            .window_handle()
84            .unwrap()
85            .as_raw()
86    })
87}
88
89/// Launch an interactive HTML/CSS renderer driven by the Dioxus virtualdom
90pub fn launch(app: fn() -> Element) {
91    launch_cfg(app, vec![], vec![])
92}
93
94pub fn launch_cfg(
95    app: fn() -> Element,
96    contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
97    cfg: Vec<Box<dyn Any>>,
98) {
99    launch_cfg_with_props(app, (), contexts, cfg)
100}
101
102// todo: props shouldn't have the clone bound - should try and match dioxus-desktop behavior
103pub fn launch_cfg_with_props<P: Clone + 'static, M: 'static>(
104    app: impl ComponentFunction<P, M>,
105    props: P,
106    contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
107    configs: Vec<Box<dyn Any>>,
108) {
109    // Macro to attempt to downcast a type out of a Box<dyn Any>
110    macro_rules! try_read_config {
111        ($input:ident, $store:ident, $kind:ty) => {
112            // Try to downcast the Box<dyn Any> to type $kind
113            match $input.downcast::<$kind>() {
114                // If the type matches then write downcast value to variable $store
115                Ok(value) => {
116                    $store = Some(*value);
117                    continue;
118                }
119                // Else extract the original Box<dyn Any> value out of the error type
120                // and return it so that we can try again with a different type.
121                Err(cfg) => cfg,
122            }
123        };
124    }
125
126    // Read config values
127    #[cfg(any(feature = "vello", feature = "vello-hybrid"))]
128    let (mut features, mut limits) = (None, None);
129    let mut window_attributes = None;
130    let mut config = None;
131    for mut cfg in configs {
132        #[cfg(any(feature = "vello", feature = "vello-hybrid"))]
133        {
134            cfg = try_read_config!(cfg, features, Features);
135            cfg = try_read_config!(cfg, limits, Limits);
136        }
137        cfg = try_read_config!(cfg, window_attributes, WindowAttributes);
138        cfg = try_read_config!(cfg, config, Config);
139        let _ = cfg;
140    }
141
142    let mut config = config.unwrap_or_default();
143    if let Some(window_attributes) = window_attributes {
144        config.window_attributes = window_attributes;
145    }
146    let event_loop = create_default_event_loop();
147    let winit_proxy = event_loop.create_proxy();
148    let (proxy, event_queue) = BlitzShellProxy::new(winit_proxy);
149
150    // Turn on the runtime and enter it
151    #[cfg(feature = "net")]
152    #[cfg(not(target_arch = "wasm32"))]
153    let rt = tokio::runtime::Builder::new_multi_thread()
154        .enable_all()
155        .build()
156        .unwrap();
157    #[cfg(feature = "net")]
158    #[cfg(not(target_arch = "wasm32"))]
159    let _guard = rt.enter();
160
161    // Setup hot-reloading if enabled.
162    #[cfg(all(feature = "hot-reload", debug_assertions))]
163    #[cfg(not(target_arch = "wasm32"))]
164    {
165        let proxy = proxy.clone();
166        dioxus_devtools::connect(move |event| {
167            let dxn_event = DioxusNativeEvent::DevserverEvent(event);
168            proxy.send_event(BlitzShellEvent::embedder_event(dxn_event));
169        })
170    }
171
172    // Build the vdom first; the net provider, document, and other window-bound
173    // contexts are attached below once the event-loop proxy exists.
174    let mut vdom = VirtualDom::new_with_props(app, props);
175
176    for context in contexts {
177        vdom.insert_any_root_context(context());
178    }
179
180    #[cfg(all(feature = "net", not(target_arch = "wasm32")))]
181    let net_provider = {
182        let net_waker = Some(Arc::new(proxy.clone()) as _);
183        let inner_net_provider = Arc::new(blitz_net::Provider::new(net_waker));
184        vdom.provide_root_context(Arc::clone(&inner_net_provider));
185
186        Arc::new(DioxusNativeNetProvider::with_inner(
187            proxy.clone(),
188            inner_net_provider as _,
189        )) as Arc<dyn NetProvider>
190    };
191
192    #[cfg(any(not(feature = "net"), target_arch = "wasm32"))]
193    let net_provider = DioxusNativeNetProvider::shared(proxy.clone());
194
195    vdom.provide_root_context(Arc::clone(&net_provider));
196
197    #[cfg(feature = "html")]
198    let html_parser_provider = {
199        let html_parser = Arc::new(blitz_html::HtmlProvider) as _;
200        vdom.provide_root_context(Arc::clone(&html_parser));
201        Some(html_parser)
202    };
203    #[cfg(not(feature = "html"))]
204    let html_parser_provider = None;
205
206    let navigation_provider = Some(Arc::new(DioxusNativeNavigationProvider) as _);
207
208    // Create document + window from the baked virtualdom
209    let doc = DioxusDocument::new(
210        vdom,
211        DocumentConfig {
212            net_provider: Some(net_provider),
213            html_parser_provider,
214            navigation_provider,
215            font_ctx: config.font_ctx,
216            ..Default::default()
217        },
218    );
219    #[cfg(any(feature = "vello", feature = "vello-hybrid"))]
220    let renderer = DioxusNativeWindowRenderer::with_features_and_limits(features, limits);
221    #[cfg(not(any(feature = "vello", feature = "vello-hybrid")))]
222    let renderer = DioxusNativeWindowRenderer::new();
223    let config = WindowConfig::with_attributes(
224        Box::new(doc) as _,
225        renderer.clone(),
226        config.window_attributes,
227    );
228
229    // Create application
230    let application = DioxusNativeApplication::new(proxy, event_queue, config);
231
232    // Run event loop
233    event_loop.run_app(application).unwrap();
234}