1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3mod 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, BlitzShellProxy};
22pub use crate::window::{View, WindowConfig};
23
24#[cfg(feature = "data-uri")]
25pub use crate::net::DataUriNetProvider;
26
27#[cfg(all(
28 feature = "file_dialog",
29 any(
30 target_os = "windows",
31 target_os = "macos",
32 target_os = "linux",
33 target_os = "dragonfly",
34 target_os = "freebsd",
35 target_os = "netbsd",
36 target_os = "openbsd"
37 )
38))]
39use blitz_traits::shell::FileDialogFilter;
40use blitz_traits::shell::ShellProvider;
41use std::sync::Arc;
42use winit::cursor::{Cursor, CursorIcon};
43use winit::dpi::{LogicalPosition, LogicalSize};
44pub use winit::event_loop::{ControlFlow, EventLoop, EventLoopProxy};
45pub use winit::window::Window;
46use winit::window::{ImeCapabilities, ImeEnableRequest, ImeRequest, ImeRequestData};
47
48#[derive(Default)]
49pub struct Config {
50 pub stylesheets: Vec<String>,
51 pub base_url: Option<String>,
52}
53
54pub fn create_default_event_loop() -> EventLoop {
56 let mut ev_builder = EventLoop::builder();
57 #[cfg(target_os = "android")]
58 {
59 use winit::platform::android::EventLoopBuilderExtAndroid;
60 ev_builder.with_android_app(current_android_app());
61 }
62
63 let event_loop = ev_builder.build().unwrap();
64 event_loop.set_control_flow(ControlFlow::Wait);
65
66 event_loop
67}
68
69#[cfg(target_os = "android")]
70static ANDROID_APP: std::sync::OnceLock<android_activity::AndroidApp> = std::sync::OnceLock::new();
71
72#[cfg(target_os = "android")]
73#[cfg_attr(docsrs, doc(cfg(target_os = "android")))]
74pub fn set_android_app(app: android_activity::AndroidApp) {
76 ANDROID_APP.set(app).unwrap()
77}
78
79#[cfg(target_os = "android")]
80#[cfg_attr(docsrs, doc(cfg(target_os = "android")))]
81pub fn current_android_app() -> android_activity::AndroidApp {
84 ANDROID_APP.get().unwrap().clone()
85}
86
87pub struct BlitzShellProvider {
88 window: Arc<dyn Window>,
89}
90impl BlitzShellProvider {
91 pub fn new(window: Arc<dyn Window>) -> Self {
92 Self { window }
93 }
94}
95
96impl ShellProvider for BlitzShellProvider {
97 fn request_redraw(&self) {
98 self.window.request_redraw();
99 }
100 fn set_cursor(&self, icon: CursorIcon) {
101 self.window.set_cursor(Cursor::Icon(icon));
102 }
103 fn set_window_title(&self, title: String) {
104 self.window.set_title(&title);
105 }
106 fn set_ime_enabled(&self, is_enabled: bool) {
107 if is_enabled {
108 let _ = self.window.request_ime_update(ImeRequest::Enable(
109 ImeEnableRequest::new(ImeCapabilities::new(), ImeRequestData::default()).unwrap(),
110 ));
111 } else {
112 let _ = self.window.request_ime_update(ImeRequest::Disable);
113 }
114 }
115 fn set_ime_cursor_area(&self, x: f32, y: f32, width: f32, height: f32) {
116 let _ = self.window.request_ime_update(ImeRequest::Update(
117 ImeRequestData::default().with_cursor_area(
118 LogicalPosition::new(x, y).into(),
119 LogicalSize::new(width, height).into(),
120 ),
121 ));
122 }
123
124 #[cfg(all(
125 feature = "clipboard",
126 any(
127 target_os = "windows",
128 target_os = "macos",
129 target_os = "linux",
130 target_os = "dragonfly",
131 target_os = "freebsd",
132 target_os = "netbsd",
133 target_os = "openbsd"
134 )
135 ))]
136 fn get_clipboard_text(&self) -> Result<String, blitz_traits::shell::ClipboardError> {
137 let mut cb = arboard::Clipboard::new().unwrap();
138 cb.get_text()
139 .map_err(|_| blitz_traits::shell::ClipboardError)
140 }
141
142 #[cfg(all(
143 feature = "clipboard",
144 any(
145 target_os = "windows",
146 target_os = "macos",
147 target_os = "linux",
148 target_os = "dragonfly",
149 target_os = "freebsd",
150 target_os = "netbsd",
151 target_os = "openbsd"
152 )
153 ))]
154 fn set_clipboard_text(&self, text: String) -> Result<(), blitz_traits::shell::ClipboardError> {
155 let mut cb = arboard::Clipboard::new().unwrap();
156 cb.set_text(text.to_owned())
157 .map_err(|_| blitz_traits::shell::ClipboardError)
158 }
159
160 #[cfg(all(
161 feature = "file_dialog",
162 any(
163 target_os = "windows",
164 target_os = "macos",
165 target_os = "linux",
166 target_os = "dragonfly",
167 target_os = "freebsd",
168 target_os = "netbsd",
169 target_os = "openbsd"
170 )
171 ))]
172 fn open_file_dialog(
173 &self,
174 multiple: bool,
175 filter: Option<FileDialogFilter>,
176 ) -> Vec<std::path::PathBuf> {
177 let mut dialog = rfd::FileDialog::new();
178 if let Some(FileDialogFilter { name, extensions }) = filter {
179 dialog = dialog.add_filter(&name, &extensions);
180 }
181 let files = if multiple {
182 dialog.pick_files()
183 } else {
184 dialog.pick_file().map(|file| vec![file])
185 };
186 files.unwrap_or_default()
187 }
188}