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
71 #[allow(clippy::type_complexity)]
72 pub(crate) on_window: Option<Box<dyn FnMut(Arc<Window>, &mut VirtualDom) + 'static>>,
73}
74
75impl LaunchConfig for Config {}
76
77pub(crate) type WryProtocol = (
78 String,
79 Box<dyn Fn(WebViewId, HttpRequest<Vec<u8>>) -> HttpResponse<Cow<'static, [u8]>> + 'static>,
80);
81
82pub(crate) type AsyncWryProtocol = (
83 String,
84 Box<dyn Fn(WebViewId, HttpRequest<Vec<u8>>, RequestAsyncResponder) + 'static>,
85);
86
87impl Config {
88 #[inline]
90 pub fn new() -> Self {
91 let mut window: WindowBuilder = WindowBuilder::new()
92 .with_title(dioxus_cli_config::app_title().unwrap_or_else(|| "Dioxus App".to_string()));
93
94 let always_on_top = dioxus_cli_config::always_on_top().unwrap_or(true);
96
97 if cfg!(debug_assertions) {
98 window = window.with_always_on_top(always_on_top);
99 }
100
101 Self {
102 window,
103 as_child_window: false,
104 event_loop: None,
105 menu: MenuBuilderState::Unset,
106 protocols: Vec::new(),
107 asynchronous_protocols: Vec::new(),
108 pre_rendered: None,
109 disable_context_menu: !cfg!(debug_assertions),
110 resource_dir: None,
111 data_dir: None,
112 custom_head: None,
113 custom_index: None,
114 root_name: "main".to_string(),
115 background_color: None,
116 exit_on_last_window_close: true,
117 window_close_behavior: WindowCloseBehaviour::WindowCloses,
118 custom_event_handler: None,
119 disable_file_drop_handler: false,
120 on_window: None,
121 }
122 }
123
124 pub fn with_resource_directory(mut self, path: impl Into<PathBuf>) -> Self {
126 self.resource_dir = Some(path.into());
127 self
128 }
129
130 pub fn with_data_directory(mut self, path: impl Into<PathBuf>) -> Self {
134 self.data_dir = Some(path.into());
135 self
136 }
137
138 pub fn with_disable_context_menu(mut self, disable: bool) -> Self {
140 self.disable_context_menu = disable;
141 self
142 }
143
144 pub fn with_disable_drag_drop_handler(mut self, disable: bool) -> Self {
147 self.disable_file_drop_handler = disable;
148 self
149 }
150
151 pub fn with_prerendered(mut self, content: String) -> Self {
153 self.pre_rendered = Some(content);
154 self
155 }
156
157 pub fn with_event_loop(mut self, event_loop: EventLoop<UserWindowEvent>) -> Self {
159 self.event_loop = Some(event_loop);
160 self
161 }
162
163 pub fn with_window(mut self, window: WindowBuilder) -> Self {
165 self.window = window;
167 if !self.window.window.decorations && matches!(self.menu, MenuBuilderState::Unset) {
169 self.menu = MenuBuilderState::Set(None);
170 }
171 self
172 }
173
174 pub fn with_as_child_window(mut self) -> Self {
176 self.as_child_window = true;
177 self
178 }
179
180 pub fn with_exits_when_last_window_closes(mut self, exit: bool) -> Self {
186 self.exit_on_last_window_close = exit;
187 self
188 }
189
190 pub fn with_close_behaviour(mut self, behaviour: WindowCloseBehaviour) -> Self {
192 self.window_close_behavior = behaviour;
193 self
194 }
195
196 pub fn with_custom_event_handler(
198 mut self,
199 f: impl FnMut(&tao::event::Event<'_, UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>)
200 + 'static,
201 ) -> Self {
202 self.custom_event_handler = Some(Box::new(f));
203 self
204 }
205
206 pub fn with_custom_protocol<F>(mut self, name: impl ToString, handler: F) -> Self
208 where
209 F: Fn(WebViewId, HttpRequest<Vec<u8>>) -> HttpResponse<Cow<'static, [u8]>> + 'static,
210 {
211 self.protocols.push((name.to_string(), Box::new(handler)));
212 self
213 }
214
215 pub fn with_asynchronous_custom_protocol<F>(mut self, name: impl ToString, handler: F) -> Self
242 where
243 F: Fn(WebViewId, HttpRequest<Vec<u8>>, RequestAsyncResponder) + 'static,
244 {
245 self.asynchronous_protocols
246 .push((name.to_string(), Box::new(handler)));
247 self
248 }
249
250 pub fn with_icon(mut self, icon: Icon) -> Self {
252 self.window.window.window_icon = Some(icon);
253 self
254 }
255
256 pub fn with_custom_head(mut self, head: String) -> Self {
260 self.custom_head = Some(head);
261 self
262 }
263
264 pub fn with_custom_index(mut self, index: String) -> Self {
271 self.custom_index = Some(index);
272 self
273 }
274
275 pub fn with_root_name(mut self, name: impl Into<String>) -> Self {
279 self.root_name = name.into();
280 self
281 }
282
283 pub fn with_background_color(mut self, color: (u8, u8, u8, u8)) -> Self {
287 self.background_color = Some(color);
288 self
289 }
290
291 #[allow(unused)]
297 pub fn with_menu(mut self, menu: impl Into<Option<DioxusMenu>>) -> Self {
298 #[cfg(not(any(target_os = "ios", target_os = "android")))]
299 {
300 if self.window.window.decorations {
301 self.menu = MenuBuilderState::Set(menu.into())
302 }
303 }
304 self
305 }
306
307 pub fn with_on_window(mut self, f: impl FnMut(Arc<Window>, &mut VirtualDom) + 'static) -> Self {
312 self.on_window = Some(Box::new(f));
313 self
314 }
315}
316
317impl Default for Config {
318 fn default() -> Self {
319 Self::new()
320 }
321}
322
323