1use dioxus_core::{LaunchConfig, VirtualDom};
2use std::path::PathBuf;
3use std::{borrow::Cow, sync::Arc};
4use tao::window::{Icon, WindowBuilder};
5use tao::{
6 event_loop::{EventLoop, EventLoopWindowTarget},
7 window::Window,
8};
9use wry::http::{Request as HttpRequest, Response as HttpResponse};
10use wry::{RequestAsyncResponder, WebViewId};
11
12use crate::ipc::UserWindowEvent;
13use crate::menubar::{default_menu_bar, DioxusMenu};
14
15type CustomEventHandler = Box<
16 dyn 'static
17 + for<'a> FnMut(
18 &tao::event::Event<'a, UserWindowEvent>,
19 &EventLoopWindowTarget<UserWindowEvent>,
20 ),
21>;
22
23#[derive(Debug, Copy, Clone, Eq, PartialEq)]
25#[non_exhaustive]
26pub enum WindowCloseBehaviour {
27 WindowHides,
29
30 WindowCloses,
32}
33
34pub(crate) enum MenuBuilderState {
37 Unset,
38 Set(Option<DioxusMenu>),
39}
40
41impl From<MenuBuilderState> for Option<DioxusMenu> {
42 fn from(val: MenuBuilderState) -> Self {
43 match val {
44 MenuBuilderState::Unset => Some(default_menu_bar()),
45 MenuBuilderState::Set(menu) => menu,
46 }
47 }
48}
49
50pub struct Config {
52 pub(crate) event_loop: Option<EventLoop<UserWindowEvent>>,
53 pub(crate) window: WindowBuilder,
54 pub(crate) as_child_window: bool,
55 pub(crate) menu: MenuBuilderState,
56 pub(crate) protocols: Vec<WryProtocol>,
57 pub(crate) asynchronous_protocols: Vec<AsyncWryProtocol>,
58 pub(crate) pre_rendered: Option<String>,
59 pub(crate) disable_context_menu: bool,
60 pub(crate) resource_dir: Option<PathBuf>,
61 pub(crate) data_dir: Option<PathBuf>,
62 pub(crate) custom_head: Option<String>,
63 pub(crate) custom_index: Option<String>,
64 pub(crate) root_name: String,
65 pub(crate) background_color: Option<(u8, u8, u8, u8)>,
66 pub(crate) exit_on_last_window_close: bool,
67 pub(crate) window_close_behavior: WindowCloseBehaviour,
68 pub(crate) custom_event_handler: Option<CustomEventHandler>,
69 pub(crate) disable_file_drop_handler: bool,
70 pub(crate) disable_dma_buf_on_wayland: bool,
71 pub(crate) additional_windows_args: Option<String>,
72
73 #[allow(clippy::type_complexity)]
74 pub(crate) on_window: Option<Box<dyn FnMut(Arc<Window>, &mut VirtualDom) + 'static>>,
75}
76
77impl LaunchConfig for Config {}
78
79pub(crate) type WryProtocol = (
80 String,
81 Box<dyn Fn(WebViewId, HttpRequest<Vec<u8>>) -> HttpResponse<Cow<'static, [u8]>> + 'static>,
82);
83
84pub(crate) type AsyncWryProtocol = (
85 String,
86 Box<dyn Fn(WebViewId, HttpRequest<Vec<u8>>, RequestAsyncResponder) + 'static>,
87);
88
89impl Config {
90 #[inline]
92 pub fn new() -> Self {
93 let mut window: WindowBuilder = WindowBuilder::new()
94 .with_title(dioxus_cli_config::app_title().unwrap_or_else(|| "Dioxus App".to_string()));
95
96 let always_on_top = dioxus_cli_config::always_on_top().unwrap_or(true);
98
99 if cfg!(debug_assertions) {
100 window = window.with_always_on_top(always_on_top);
101 }
102
103 Self {
104 window,
105 as_child_window: false,
106 event_loop: None,
107 menu: MenuBuilderState::Unset,
108 protocols: Vec::new(),
109 asynchronous_protocols: Vec::new(),
110 pre_rendered: None,
111 disable_context_menu: !cfg!(debug_assertions),
112 resource_dir: None,
113 data_dir: None,
114 custom_head: None,
115 custom_index: None,
116 root_name: "main".to_string(),
117 background_color: None,
118 exit_on_last_window_close: true,
119 window_close_behavior: WindowCloseBehaviour::WindowCloses,
120 custom_event_handler: None,
121 disable_file_drop_handler: false,
122 disable_dma_buf_on_wayland: true,
123 on_window: None,
124 additional_windows_args: None,
125 }
126 }
127
128 pub fn with_resource_directory(mut self, path: impl Into<PathBuf>) -> Self {
130 self.resource_dir = Some(path.into());
131 self
132 }
133
134 pub fn with_data_directory(mut self, path: impl Into<PathBuf>) -> Self {
138 self.data_dir = Some(path.into());
139 self
140 }
141
142 pub fn with_disable_context_menu(mut self, disable: bool) -> Self {
144 self.disable_context_menu = disable;
145 self
146 }
147
148 pub fn with_disable_drag_drop_handler(mut self, disable: bool) -> Self {
151 self.disable_file_drop_handler = disable;
152 self
153 }
154
155 pub fn with_prerendered(mut self, content: String) -> Self {
157 self.pre_rendered = Some(content);
158 self
159 }
160
161 pub fn with_event_loop(mut self, event_loop: EventLoop<UserWindowEvent>) -> Self {
163 self.event_loop = Some(event_loop);
164 self
165 }
166
167 pub fn with_window(mut self, window: WindowBuilder) -> Self {
169 self.window = window;
171 if !self.window.window.decorations && matches!(self.menu, MenuBuilderState::Unset) {
173 self.menu = MenuBuilderState::Set(None);
174 }
175 self
176 }
177
178 pub fn with_as_child_window(mut self) -> Self {
180 self.as_child_window = true;
181 self
182 }
183
184 pub fn with_exits_when_last_window_closes(mut self, exit: bool) -> Self {
190 self.exit_on_last_window_close = exit;
191 self
192 }
193
194 pub fn with_close_behaviour(mut self, behaviour: WindowCloseBehaviour) -> Self {
196 self.window_close_behavior = behaviour;
197 self
198 }
199
200 pub fn with_custom_event_handler(
202 mut self,
203 f: impl FnMut(&tao::event::Event<'_, UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>)
204 + 'static,
205 ) -> Self {
206 self.custom_event_handler = Some(Box::new(f));
207 self
208 }
209
210 pub fn with_custom_protocol<F>(mut self, name: impl ToString, handler: F) -> Self
212 where
213 F: Fn(WebViewId, HttpRequest<Vec<u8>>) -> HttpResponse<Cow<'static, [u8]>> + 'static,
214 {
215 self.protocols.push((name.to_string(), Box::new(handler)));
216 self
217 }
218
219 pub fn with_asynchronous_custom_protocol<F>(mut self, name: impl ToString, handler: F) -> Self
246 where
247 F: Fn(WebViewId, HttpRequest<Vec<u8>>, RequestAsyncResponder) + 'static,
248 {
249 self.asynchronous_protocols
250 .push((name.to_string(), Box::new(handler)));
251 self
252 }
253
254 pub fn with_icon(mut self, icon: Icon) -> Self {
256 self.window.window.window_icon = Some(icon);
257 self
258 }
259
260 pub fn with_custom_head(mut self, head: String) -> Self {
264 self.custom_head = Some(head);
265 self
266 }
267
268 pub fn with_custom_index(mut self, index: String) -> Self {
275 self.custom_index = Some(index);
276 self
277 }
278
279 pub fn with_root_name(mut self, name: impl Into<String>) -> Self {
283 self.root_name = name.into();
284 self
285 }
286
287 pub fn with_background_color(mut self, color: (u8, u8, u8, u8)) -> Self {
291 self.background_color = Some(color);
292 self
293 }
294
295 #[allow(unused)]
301 pub fn with_menu(mut self, menu: impl Into<Option<DioxusMenu>>) -> Self {
302 #[cfg(not(any(target_os = "ios", target_os = "android")))]
303 {
304 if self.window.window.decorations {
305 self.menu = MenuBuilderState::Set(menu.into())
306 }
307 }
308 self
309 }
310
311 pub fn with_on_window(mut self, f: impl FnMut(Arc<Window>, &mut VirtualDom) + 'static) -> Self {
316 self.on_window = Some(Box::new(f));
317 self
318 }
319
320 pub fn with_disable_dma_buf_on_wayland(mut self, disable: bool) -> Self {
325 self.disable_dma_buf_on_wayland = disable;
326 self
327 }
328
329 pub fn with_windows_browser_args(mut self, additional_args: impl ToString) -> Self {
331 self.additional_windows_args = Some(additional_args.to_string());
332 self
333 }
334}
335
336impl Default for Config {
337 fn default() -> Self {
338 Self::new()
339 }
340}
341
342