1use peniko::kurbo::{Point, Size};
2#[cfg(windows)]
3pub use winit::platform::windows::IconExtWindows;
4pub use winit::window::Fullscreen;
5pub use winit::window::Icon;
6pub use winit::window::ResizeDirection;
7pub use winit::window::Theme;
8pub use winit::window::WindowButtons;
9pub use winit::window::WindowId;
10pub use winit::window::WindowLevel;
11
12use crate::app::{add_app_update_event, AppUpdateEvent};
13use crate::view::IntoView;
14use crate::AnyView;
15
16pub struct WindowCreation {
17 pub(crate) view_fn: Box<dyn FnOnce(WindowId) -> AnyView>,
18 pub(crate) config: Option<WindowConfig>,
19}
20
21#[derive(Debug)]
23pub struct WindowConfig {
24 pub(crate) size: Option<Size>,
25 pub(crate) min_size: Option<Size>,
26 pub(crate) max_size: Option<Size>,
27 pub(crate) position: Option<Point>,
28 pub(crate) show_titlebar: bool,
29 pub(crate) transparent: bool,
30 pub(crate) fullscreen: Option<Fullscreen>,
31 pub(crate) window_icon: Option<Icon>,
32 pub(crate) title: String,
33 pub(crate) enabled_buttons: WindowButtons,
34 pub(crate) resizable: bool,
35 pub(crate) undecorated: bool,
36 pub(crate) undecorated_shadow: bool,
37 pub(crate) window_level: WindowLevel,
38 pub(crate) apply_default_theme: bool,
39 pub(crate) font_embolden: f32,
40 #[allow(dead_code)]
41 pub(crate) mac_os_config: Option<MacOSWindowConfig>,
42 pub(crate) web_config: Option<WebWindowConfig>,
43}
44
45impl Default for WindowConfig {
46 fn default() -> Self {
47 Self {
48 size: None,
49 min_size: None,
50 max_size: None,
51 position: None,
52 show_titlebar: true,
53 transparent: false,
54 fullscreen: None,
55 window_icon: None,
56 title: std::env::current_exe()
57 .ok()
58 .and_then(|p| p.file_name().map(|f| f.to_string_lossy().into_owned()))
59 .unwrap_or("Floem Window".to_string()),
60 enabled_buttons: WindowButtons::all(),
61 resizable: true,
62 undecorated: false,
63 undecorated_shadow: false,
64 window_level: WindowLevel::Normal,
65 apply_default_theme: true,
66 font_embolden: if cfg!(target_os = "macos") { 0.2 } else { 0. },
67 mac_os_config: None,
68 web_config: None,
69 }
70 }
71}
72
73impl WindowConfig {
74 #[inline]
78 pub fn size(mut self, size: impl Into<Size>) -> Self {
79 self.size = Some(size.into());
80 self
81 }
82
83 #[inline]
85 pub fn min_size(mut self, size: impl Into<Size>) -> Self {
86 self.min_size = Some(size.into());
87 self
88 }
89
90 #[inline]
92 pub fn max_size(mut self, size: impl Into<Size>) -> Self {
93 self.max_size = Some(size.into());
94 self
95 }
96
97 #[inline]
101 pub fn position(mut self, position: Point) -> Self {
102 self.position = Some(position);
103 self
104 }
105
106 #[inline]
110 pub fn show_titlebar(mut self, show_titlebar: bool) -> Self {
111 self.show_titlebar = show_titlebar;
112 self
113 }
114
115 #[inline]
119 pub fn undecorated(mut self, undecorated: bool) -> Self {
120 self.undecorated = undecorated;
121 self
122 }
123
124 #[inline]
128 pub fn undecorated_shadow(mut self, undecorated_shadow: bool) -> Self {
129 self.undecorated_shadow = undecorated_shadow;
130 self
131 }
132
133 #[inline]
137 pub fn with_transparent(mut self, transparent: bool) -> Self {
138 self.transparent = transparent;
139 self
140 }
141
142 #[inline]
146 pub fn fullscreen(mut self, fullscreen: Fullscreen) -> Self {
147 self.fullscreen = Some(fullscreen);
148 self
149 }
150
151 #[inline]
155 pub fn window_icon(mut self, window_icon: Icon) -> Self {
156 self.window_icon = Some(window_icon);
157 self
158 }
159
160 #[inline]
164 pub fn title(mut self, title: impl Into<String>) -> Self {
165 self.title = title.into();
166 self
167 }
168
169 #[inline]
173 pub fn enabled_buttons(mut self, enabled_buttons: WindowButtons) -> Self {
174 self.enabled_buttons = enabled_buttons;
175 self
176 }
177
178 #[inline]
182 pub fn resizable(mut self, resizable: bool) -> Self {
183 self.resizable = resizable;
184 self
185 }
186
187 #[inline]
193 pub fn window_level(mut self, window_level: WindowLevel) -> Self {
194 self.window_level = window_level;
195 self
196 }
197
198 #[inline]
202 pub fn apply_default_theme(mut self, apply_default_theme: bool) -> Self {
203 self.apply_default_theme = apply_default_theme;
204 self
205 }
206
207 #[inline]
211 pub fn font_embolden(mut self, font_embolden: f32) -> Self {
212 self.font_embolden = font_embolden;
213 self
214 }
215
216 #[allow(unused_variables, unused_mut)] pub fn with_mac_os_config(
220 mut self,
221 mut f: impl FnMut(MacOSWindowConfig) -> MacOSWindowConfig,
222 ) -> Self {
223 #[cfg(target_os = "macos")]
224 if let Some(existing_config) = self.mac_os_config {
225 self.mac_os_config = Some(f(existing_config))
226 } else {
227 let new_config = f(MacOSWindowConfig::default());
228 self.mac_os_config = Some(new_config);
229 }
230 self
231 }
232
233 #[allow(unused_variables, unused_mut)] pub fn with_web_config(mut self, f: impl FnOnce(WebWindowConfig) -> WebWindowConfig) -> Self {
237 #[cfg(target_arch = "wasm32")]
238 if let Some(existing_config) = self.web_config {
239 self.web_config = Some(f(existing_config))
240 } else {
241 let new_config = f(WebWindowConfig {
242 canvas_id: String::new(),
243 });
244 self.web_config = Some(new_config);
245 }
246 self
247 }
248}
249
250#[derive(Default, Debug, Clone)]
255pub struct MacOSWindowConfig {
256 pub(crate) movable_by_window_background: Option<bool>,
257 pub(crate) titlebar_transparent: Option<bool>,
258 pub(crate) titlebar_hidden: Option<bool>,
259 pub(crate) title_hidden: Option<bool>,
260 pub(crate) titlebar_buttons_hidden: Option<bool>,
261 pub(crate) full_size_content_view: Option<bool>,
262 pub(crate) unified_titlebar: Option<bool>,
263 pub(crate) movable: Option<bool>,
264 pub(crate) traffic_lights_offset: Option<(f64, f64)>,
265 pub(crate) accepts_first_mouse: Option<bool>,
266 pub(crate) tabbing_identifier: Option<String>,
267 pub(crate) option_as_alt: Option<MacOsOptionAsAlt>,
268 pub(crate) has_shadow: Option<bool>,
269 pub(crate) disallow_high_dpi: Option<bool>,
270 pub(crate) panel: Option<bool>,
271}
272
273impl MacOSWindowConfig {
274 pub fn movable_by_window_background(mut self, val: bool) -> Self {
277 self.movable_by_window_background = Some(val);
278 self
279 }
280
281 pub fn transparent_title_bar(mut self, val: bool) -> Self {
283 self.titlebar_transparent = Some(val);
284 self
285 }
286
287 pub fn hide_titlebar(mut self, val: bool) -> Self {
289 self.titlebar_hidden = Some(val);
290 self
291 }
292
293 pub fn hide_title(mut self, val: bool) -> Self {
295 self.title_hidden = Some(val);
296 self
297 }
298
299 pub fn hide_titlebar_buttons(mut self, val: bool) -> Self {
301 self.titlebar_buttons_hidden = Some(val);
302 self
303 }
304
305 pub fn full_size_content_view(mut self, val: bool) -> Self {
307 self.full_size_content_view = Some(val);
308 self
309 }
310
311 pub fn unified_titlebar(mut self, val: bool) -> Self {
313 self.unified_titlebar = Some(val);
314 self
315 }
316
317 pub fn movable(mut self, val: bool) -> Self {
319 self.movable = Some(val);
320 self
321 }
322
323 pub fn traffic_lights_offset(mut self, x_y_offset: (f64, f64)) -> Self {
326 self.traffic_lights_offset = Some(x_y_offset);
327 self
328 }
329
330 pub fn accept_first_mouse(mut self, val: bool) -> Self {
335 self.accepts_first_mouse = Some(val);
336 self
337 }
338
339 pub fn tabbing_identifier(mut self, val: impl Into<String>) -> Self {
341 self.tabbing_identifier = Some(val.into());
342 self
343 }
344
345 pub fn interpret_option_as_alt(mut self, val: MacOsOptionAsAlt) -> Self {
348 self.option_as_alt = Some(val);
349 self
350 }
351
352 pub fn enable_shadow(mut self, val: bool) -> Self {
354 self.has_shadow = Some(val);
355 self
356 }
357
358 pub fn disallow_high_dpi(mut self, val: bool) -> Self {
361 self.disallow_high_dpi = Some(val);
362 self
363 }
364
365 pub fn panel(mut self, val: bool) -> Self {
367 self.panel = Some(val);
368 self
369 }
370}
371
372#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
378pub enum MacOsOptionAsAlt {
379 OnlyLeft,
380 OnlyRight,
381 Both,
382 #[default]
383 None,
384}
385
386#[cfg(target_os = "macos")]
387impl From<MacOsOptionAsAlt> for winit::platform::macos::OptionAsAlt {
388 fn from(opts: MacOsOptionAsAlt) -> winit::platform::macos::OptionAsAlt {
389 match opts {
390 MacOsOptionAsAlt::OnlyLeft => winit::platform::macos::OptionAsAlt::OnlyLeft,
391 MacOsOptionAsAlt::OnlyRight => winit::platform::macos::OptionAsAlt::OnlyRight,
392 MacOsOptionAsAlt::Both => winit::platform::macos::OptionAsAlt::Both,
393 MacOsOptionAsAlt::None => winit::platform::macos::OptionAsAlt::None,
394 }
395 }
396}
397
398#[derive(Default, Debug, Clone)]
400pub struct WebWindowConfig {
401 pub(crate) canvas_id: String,
403}
404
405impl WebWindowConfig {
406 pub fn canvas_id(mut self, val: impl Into<String>) -> Self {
408 self.canvas_id = val.into();
409 self
410 }
411}
412
413pub fn new_window<V: IntoView + 'static>(
416 app_view: impl FnOnce(WindowId) -> V + 'static,
417 config: Option<WindowConfig>,
418) {
419 add_app_update_event(AppUpdateEvent::NewWindow {
420 window_creation: WindowCreation {
421 view_fn: Box::new(|window_id| app_view(window_id).into_any()),
422 config,
423 },
424 });
425}
426
427pub fn close_window(window_id: WindowId) {
429 add_app_update_event(AppUpdateEvent::CloseWindow { window_id });
430}