blitz_shell/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3//! Event loop, windowing and system integration.
4//!
5//! ## Feature flags
6//!  - `default`: Enables the features listed below.
7//!  - `accessibility`: Enables [`accesskit`] accessibility support.
8//!  - `hot-reload`: Enables hot-reloading of Dioxus RSX.
9//!  - `tracing`: Enables tracing support.
10
11mod application;
12mod convert_events;
13mod event;
14mod window;
15
16#[cfg(feature = "accessibility")]
17mod accessibility;
18
19pub use crate::application::BlitzApplication;
20pub use crate::event::BlitzShellEvent;
21pub use crate::window::{View, WindowConfig};
22
23use blitz_dom::net::Resource;
24use blitz_traits::net::NetCallback;
25use blitz_traits::shell::ShellProvider;
26use std::sync::Arc;
27pub use winit::event_loop::{ControlFlow, EventLoop, EventLoopProxy};
28pub use winit::window::{CursorIcon, Window};
29
30#[derive(Default)]
31pub struct Config {
32    pub stylesheets: Vec<String>,
33    pub base_url: Option<String>,
34}
35
36/// Build an event loop for the application
37pub fn create_default_event_loop<Event>() -> EventLoop<Event> {
38    let mut ev_builder = EventLoop::<Event>::with_user_event();
39    #[cfg(target_os = "android")]
40    {
41        use winit::platform::android::EventLoopBuilderExtAndroid;
42        ev_builder.with_android_app(current_android_app());
43    }
44
45    let event_loop = ev_builder.build().unwrap();
46    event_loop.set_control_flow(ControlFlow::Wait);
47
48    event_loop
49}
50
51#[cfg(target_os = "android")]
52static ANDROID_APP: std::sync::OnceLock<android_activity::AndroidApp> = std::sync::OnceLock::new();
53
54#[cfg(target_os = "android")]
55#[cfg_attr(docsrs, doc(cfg(target_os = "android")))]
56/// Set the current [`AndroidApp`](android_activity::AndroidApp).
57pub fn set_android_app(app: android_activity::AndroidApp) {
58    ANDROID_APP.set(app).unwrap()
59}
60
61#[cfg(target_os = "android")]
62#[cfg_attr(docsrs, doc(cfg(target_os = "android")))]
63/// Get the current [`AndroidApp`](android_activity::AndroidApp).
64/// This will panic if the android activity has not been setup with [`set_android_app`].
65pub fn current_android_app() -> android_activity::AndroidApp {
66    ANDROID_APP.get().unwrap().clone()
67}
68
69/// A NetCallback that injects the fetched Resource into our winit event loop
70pub struct BlitzShellNetCallback(EventLoopProxy<BlitzShellEvent>);
71
72impl BlitzShellNetCallback {
73    pub fn new(proxy: EventLoopProxy<BlitzShellEvent>) -> Self {
74        Self(proxy)
75    }
76
77    pub fn shared(proxy: EventLoopProxy<BlitzShellEvent>) -> Arc<dyn NetCallback<Resource>> {
78        Arc::new(Self(proxy))
79    }
80}
81impl NetCallback<Resource> for BlitzShellNetCallback {
82    fn call(&self, doc_id: usize, result: Result<Resource, Option<String>>) {
83        // TODO: handle error case
84        if let Ok(data) = result {
85            self.0
86                .send_event(BlitzShellEvent::ResourceLoad { doc_id, data })
87                .unwrap()
88        }
89    }
90}
91
92pub struct BlitzShellProvider {
93    window: Arc<Window>,
94}
95impl BlitzShellProvider {
96    pub fn new(window: Arc<Window>) -> Self {
97        Self { window }
98    }
99}
100
101impl ShellProvider for BlitzShellProvider {
102    fn request_redraw(&self) {
103        self.window.request_redraw();
104    }
105    fn set_cursor(&self, icon: CursorIcon) {
106        self.window.set_cursor(icon);
107    }
108    fn set_window_title(&self, title: String) {
109        self.window.set_title(&title);
110    }
111
112    #[cfg(all(
113        feature = "clipboard",
114        any(
115            target_os = "windows",
116            target_os = "macos",
117            target_os = "linux",
118            target_os = "dragonfly",
119            target_os = "freebsd",
120            target_os = "netbsd",
121            target_os = "openbsd"
122        )
123    ))]
124    fn get_clipboard_text(&self) -> Result<String, blitz_traits::shell::ClipboardError> {
125        let mut cb = arboard::Clipboard::new().unwrap();
126        cb.get_text()
127            .map_err(|_| blitz_traits::shell::ClipboardError)
128    }
129
130    #[cfg(all(
131        feature = "clipboard",
132        any(
133            target_os = "windows",
134            target_os = "macos",
135            target_os = "linux",
136            target_os = "dragonfly",
137            target_os = "freebsd",
138            target_os = "netbsd",
139            target_os = "openbsd"
140        )
141    ))]
142    fn set_clipboard_text(&self, text: String) -> Result<(), blitz_traits::shell::ClipboardError> {
143        let mut cb = arboard::Clipboard::new().unwrap();
144        cb.set_text(text.to_owned())
145            .map_err(|_| blitz_traits::shell::ClipboardError)
146    }
147}