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