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 net;
15mod window;
16
17#[cfg(feature = "accessibility")]
18mod accessibility;
19
20pub use crate::application::BlitzApplication;
21pub use crate::event::BlitzShellEvent;
22pub use crate::net::BlitzShellNetCallback;
23pub use crate::window::{View, WindowConfig};
24
25#[cfg(feature = "data-uri")]
26pub use crate::net::DataUriNetProvider;
27
28#[cfg(all(
29    feature = "file_dialog",
30    any(
31        target_os = "windows",
32        target_os = "macos",
33        target_os = "linux",
34        target_os = "dragonfly",
35        target_os = "freebsd",
36        target_os = "netbsd",
37        target_os = "openbsd"
38    )
39))]
40use blitz_traits::shell::FileDialogFilter;
41use blitz_traits::shell::ShellProvider;
42use std::sync::Arc;
43pub use winit::event_loop::{ControlFlow, EventLoop, EventLoopProxy};
44pub use winit::window::{CursorIcon, Window};
45
46#[derive(Default)]
47pub struct Config {
48    pub stylesheets: Vec<String>,
49    pub base_url: Option<String>,
50}
51
52/// Build an event loop for the application
53pub fn create_default_event_loop<Event>() -> EventLoop<Event> {
54    let mut ev_builder = EventLoop::<Event>::with_user_event();
55    #[cfg(target_os = "android")]
56    {
57        use winit::platform::android::EventLoopBuilderExtAndroid;
58        ev_builder.with_android_app(current_android_app());
59    }
60
61    let event_loop = ev_builder.build().unwrap();
62    event_loop.set_control_flow(ControlFlow::Wait);
63
64    event_loop
65}
66
67#[cfg(target_os = "android")]
68static ANDROID_APP: std::sync::OnceLock<android_activity::AndroidApp> = std::sync::OnceLock::new();
69
70#[cfg(target_os = "android")]
71#[cfg_attr(docsrs, doc(cfg(target_os = "android")))]
72/// Set the current [`AndroidApp`](android_activity::AndroidApp).
73pub fn set_android_app(app: android_activity::AndroidApp) {
74    ANDROID_APP.set(app).unwrap()
75}
76
77#[cfg(target_os = "android")]
78#[cfg_attr(docsrs, doc(cfg(target_os = "android")))]
79/// Get the current [`AndroidApp`](android_activity::AndroidApp).
80/// This will panic if the android activity has not been setup with [`set_android_app`].
81pub fn current_android_app() -> android_activity::AndroidApp {
82    ANDROID_APP.get().unwrap().clone()
83}
84
85pub struct BlitzShellProvider {
86    window: Arc<Window>,
87}
88impl BlitzShellProvider {
89    pub fn new(window: Arc<Window>) -> Self {
90        Self { window }
91    }
92}
93
94impl ShellProvider for BlitzShellProvider {
95    fn request_redraw(&self) {
96        self.window.request_redraw();
97    }
98    fn set_cursor(&self, icon: CursorIcon) {
99        self.window.set_cursor(icon);
100    }
101    fn set_window_title(&self, title: String) {
102        self.window.set_title(&title);
103    }
104
105    #[cfg(all(
106        feature = "clipboard",
107        any(
108            target_os = "windows",
109            target_os = "macos",
110            target_os = "linux",
111            target_os = "dragonfly",
112            target_os = "freebsd",
113            target_os = "netbsd",
114            target_os = "openbsd"
115        )
116    ))]
117    fn get_clipboard_text(&self) -> Result<String, blitz_traits::shell::ClipboardError> {
118        let mut cb = arboard::Clipboard::new().unwrap();
119        cb.get_text()
120            .map_err(|_| blitz_traits::shell::ClipboardError)
121    }
122
123    #[cfg(all(
124        feature = "clipboard",
125        any(
126            target_os = "windows",
127            target_os = "macos",
128            target_os = "linux",
129            target_os = "dragonfly",
130            target_os = "freebsd",
131            target_os = "netbsd",
132            target_os = "openbsd"
133        )
134    ))]
135    fn set_clipboard_text(&self, text: String) -> Result<(), blitz_traits::shell::ClipboardError> {
136        let mut cb = arboard::Clipboard::new().unwrap();
137        cb.set_text(text.to_owned())
138            .map_err(|_| blitz_traits::shell::ClipboardError)
139    }
140
141    #[cfg(all(
142        feature = "file_dialog",
143        any(
144            target_os = "windows",
145            target_os = "macos",
146            target_os = "linux",
147            target_os = "dragonfly",
148            target_os = "freebsd",
149            target_os = "netbsd",
150            target_os = "openbsd"
151        )
152    ))]
153    fn open_file_dialog(
154        &self,
155        multiple: bool,
156        filter: Option<FileDialogFilter>,
157    ) -> Vec<std::path::PathBuf> {
158        let mut dialog = rfd::FileDialog::new();
159        if let Some(FileDialogFilter { name, extensions }) = filter {
160            dialog = dialog.add_filter(&name, &extensions);
161        }
162        let files = if multiple {
163            dialog.pick_files()
164        } else {
165            dialog.pick_file().map(|file| vec![file])
166        };
167        files.unwrap_or_default()
168    }
169}