1use dioxus_core::LaunchConfig;
2use std::borrow::Cow;
3use std::path::PathBuf;
4use tao::event_loop::{EventLoop, EventLoopWindowTarget};
5use tao::window::{Icon, WindowBuilder};
6use wry::http::{Request as HttpRequest, Response as HttpResponse};
7use wry::RequestAsyncResponder;
8
9use crate::ipc::UserWindowEvent;
10use crate::menubar::{default_menu_bar, DioxusMenu};
11
12type CustomEventHandler = Box<
13 dyn 'static
14 + for<'a> FnMut(
15 &tao::event::Event<'a, UserWindowEvent>,
16 &EventLoopWindowTarget<UserWindowEvent>,
17 ),
18>;
19
20#[derive(Copy, Clone, Eq, PartialEq)]
22#[non_exhaustive]
23pub enum WindowCloseBehaviour {
24 LastWindowExitsApp,
26 LastWindowHides,
28 CloseWindow,
30}
31
32pub(crate) enum MenuBuilderState {
35 Unset,
36 Set(Option<DioxusMenu>),
37}
38
39impl From<MenuBuilderState> for Option<DioxusMenu> {
40 fn from(val: MenuBuilderState) -> Self {
41 match val {
42 MenuBuilderState::Unset => Some(default_menu_bar()),
43 MenuBuilderState::Set(menu) => menu,
44 }
45 }
46}
47
48pub struct Config {
50 pub(crate) event_loop: Option<EventLoop<UserWindowEvent>>,
51 pub(crate) window: WindowBuilder,
52 pub(crate) as_child_window: bool,
53 pub(crate) menu: MenuBuilderState,
54 pub(crate) protocols: Vec<WryProtocol>,
55 pub(crate) asynchronous_protocols: Vec<AsyncWryProtocol>,
56 pub(crate) pre_rendered: Option<String>,
57 pub(crate) disable_context_menu: bool,
58 pub(crate) resource_dir: Option<PathBuf>,
59 pub(crate) data_dir: Option<PathBuf>,
60 pub(crate) custom_head: Option<String>,
61 pub(crate) custom_index: Option<String>,
62 pub(crate) root_name: String,
63 pub(crate) background_color: Option<(u8, u8, u8, u8)>,
64 pub(crate) last_window_close_behavior: WindowCloseBehaviour,
65 pub(crate) custom_event_handler: Option<CustomEventHandler>,
66 pub(crate) disable_file_drop_handler: bool,
67}
68
69impl LaunchConfig for Config {}
70
71pub(crate) type WryProtocol = (
72 String,
73 Box<dyn Fn(HttpRequest<Vec<u8>>) -> HttpResponse<Cow<'static, [u8]>> + 'static>,
74);
75
76pub(crate) type AsyncWryProtocol = (
77 String,
78 Box<dyn Fn(HttpRequest<Vec<u8>>, RequestAsyncResponder) + 'static>,
79);
80
81impl Config {
82 #[inline]
84 pub fn new() -> Self {
85 let mut window: WindowBuilder = WindowBuilder::new()
86 .with_title(dioxus_cli_config::app_title().unwrap_or_else(|| "Dioxus App".to_string()));
87
88 let always_on_top = dioxus_cli_config::always_on_top().unwrap_or(true);
90
91 if cfg!(debug_assertions) {
92 window = window.with_always_on_top(always_on_top);
93 }
94
95 Self {
96 window,
97 as_child_window: false,
98 event_loop: None,
99 menu: MenuBuilderState::Unset,
100 protocols: Vec::new(),
101 asynchronous_protocols: Vec::new(),
102 pre_rendered: None,
103 disable_context_menu: !cfg!(debug_assertions),
104 resource_dir: None,
105 data_dir: None,
106 custom_head: None,
107 custom_index: None,
108 root_name: "main".to_string(),
109 background_color: None,
110 last_window_close_behavior: WindowCloseBehaviour::LastWindowExitsApp,
111 custom_event_handler: None,
112 disable_file_drop_handler: false,
113 }
114 }
115
116 pub fn with_resource_directory(mut self, path: impl Into<PathBuf>) -> Self {
118 self.resource_dir = Some(path.into());
119 self
120 }
121
122 pub fn with_data_directory(mut self, path: impl Into<PathBuf>) -> Self {
126 self.data_dir = Some(path.into());
127 self
128 }
129
130 pub fn with_disable_context_menu(mut self, disable: bool) -> Self {
132 self.disable_context_menu = disable;
133 self
134 }
135
136 pub fn with_disable_drag_drop_handler(mut self, disable: bool) -> Self {
139 self.disable_file_drop_handler = disable;
140 self
141 }
142
143 pub fn with_prerendered(mut self, content: String) -> Self {
145 self.pre_rendered = Some(content);
146 self
147 }
148
149 pub fn with_event_loop(mut self, event_loop: EventLoop<UserWindowEvent>) -> Self {
151 self.event_loop = Some(event_loop);
152 self
153 }
154
155 pub fn with_window(mut self, window: WindowBuilder) -> Self {
157 self.window = window;
159 if !self.window.window.decorations && matches!(self.menu, MenuBuilderState::Unset) {
161 self.menu = MenuBuilderState::Set(None);
162 }
163 self
164 }
165
166 pub fn with_as_child_window(mut self) -> Self {
168 self.as_child_window = true;
169 self
170 }
171
172 pub fn with_close_behaviour(mut self, behaviour: WindowCloseBehaviour) -> Self {
174 self.last_window_close_behavior = behaviour;
175 self
176 }
177
178 pub fn with_custom_event_handler(
180 mut self,
181 f: impl FnMut(&tao::event::Event<'_, UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>)
182 + 'static,
183 ) -> Self {
184 self.custom_event_handler = Some(Box::new(f));
185 self
186 }
187
188 pub fn with_custom_protocol<F>(mut self, name: impl ToString, handler: F) -> Self
190 where
191 F: Fn(HttpRequest<Vec<u8>>) -> HttpResponse<Cow<'static, [u8]>> + 'static,
192 {
193 self.protocols.push((name.to_string(), Box::new(handler)));
194 self
195 }
196
197 pub fn with_asynchronous_custom_protocol<F>(mut self, name: impl ToString, handler: F) -> Self
224 where
225 F: Fn(HttpRequest<Vec<u8>>, RequestAsyncResponder) + 'static,
226 {
227 self.asynchronous_protocols
228 .push((name.to_string(), Box::new(handler)));
229 self
230 }
231
232 pub fn with_icon(mut self, icon: Icon) -> Self {
234 self.window.window.window_icon = Some(icon);
235 self
236 }
237
238 pub fn with_custom_head(mut self, head: String) -> Self {
242 self.custom_head = Some(head);
243 self
244 }
245
246 pub fn with_custom_index(mut self, index: String) -> Self {
253 self.custom_index = Some(index);
254 self
255 }
256
257 pub fn with_root_name(mut self, name: impl Into<String>) -> Self {
261 self.root_name = name.into();
262 self
263 }
264
265 pub fn with_background_color(mut self, color: (u8, u8, u8, u8)) -> Self {
269 self.background_color = Some(color);
270 self
271 }
272
273 #[allow(unused)]
279 pub fn with_menu(mut self, menu: impl Into<Option<DioxusMenu>>) -> Self {
280 #[cfg(not(any(target_os = "ios", target_os = "android")))]
281 {
282 if self.window.window.decorations {
283 self.menu = MenuBuilderState::Set(menu.into())
284 }
285 }
286 self
287 }
288}
289
290impl Default for Config {
291 fn default() -> Self {
292 Self::new()
293 }
294}
295
296