1pub(crate) mod handle;
2pub(crate) mod id;
3pub mod mock;
4pub(crate) mod state;
5pub(crate) mod tracking;
6
7pub use id::{Urgency, WindowIdExt};
8pub use mock::MockWindow;
9pub use state::WindowState;
10
11use peniko::Color;
12use peniko::kurbo::{Point, Size};
13pub use winit::icon::{Icon, RgbaIcon};
14pub use winit::monitor::Fullscreen;
15pub use winit::window::ResizeDirection;
16pub use winit::window::Theme;
17pub use winit::window::WindowButtons;
18pub use winit::window::WindowId;
19pub use winit::window::WindowLevel;
20
21use crate::AnyView;
22use crate::app::{AppUpdateEvent, add_app_update_event};
23use crate::view::IntoView;
24
25pub struct WindowCreation {
26 pub(crate) view_fn: Box<dyn FnOnce(WindowId) -> AnyView>,
27 pub(crate) config: Option<WindowConfig>,
28}
29
30pub struct WindowConfig {
32 pub(crate) size: Option<Size>,
33 pub(crate) min_size: Option<Size>,
34 pub(crate) max_size: Option<Size>,
35 pub(crate) position: Option<Point>,
36 pub(crate) show_titlebar: bool,
37 pub(crate) transparent: bool,
38 pub(crate) fullscreen: Option<Fullscreen>,
39 pub(crate) window_icon: Option<Icon>,
40 pub(crate) title: String,
41 pub(crate) enabled_buttons: WindowButtons,
42 pub(crate) resizable: bool,
43 pub(crate) undecorated: bool,
44 pub(crate) undecorated_shadow: bool,
45 pub(crate) window_level: WindowLevel,
46 pub(crate) theme_override: Option<Theme>,
48 pub(crate) apply_default_theme: bool,
49 pub(crate) font_embolden: f32,
50 #[allow(dead_code)]
51 pub(crate) mac_os_config: Option<MacOSWindowConfig>,
52 pub(crate) win_os_config: Option<WinOSWindowConfig>,
53 pub(crate) web_config: Option<WebWindowConfig>,
54}
55
56impl Default for WindowConfig {
57 fn default() -> Self {
58 Self {
59 size: None,
60 min_size: None,
61 max_size: None,
62 position: None,
63 show_titlebar: true,
64 transparent: false,
65 fullscreen: None,
66 window_icon: None,
67 title: std::env::current_exe()
68 .ok()
69 .and_then(|p| p.file_name().map(|f| f.to_string_lossy().into_owned()))
70 .unwrap_or("Floem Window".to_string()),
71 enabled_buttons: WindowButtons::all(),
72 resizable: true,
73 undecorated: false,
74 undecorated_shadow: false,
75 window_level: WindowLevel::Normal,
76 theme_override: None,
77 apply_default_theme: true,
78 font_embolden: if cfg!(target_os = "macos") { 0.1 } else { 0. },
79 mac_os_config: None,
80 win_os_config: None,
81 web_config: None,
82 }
83 }
84}
85
86impl WindowConfig {
87 #[inline]
91 pub fn size(mut self, size: impl Into<Size>) -> Self {
92 self.size = Some(size.into());
93 self
94 }
95
96 #[inline]
98 pub fn min_size(mut self, size: impl Into<Size>) -> Self {
99 self.min_size = Some(size.into());
100 self
101 }
102
103 #[inline]
105 pub fn max_size(mut self, size: impl Into<Size>) -> Self {
106 self.max_size = Some(size.into());
107 self
108 }
109
110 #[inline]
114 pub fn position(mut self, position: Point) -> Self {
115 self.position = Some(position);
116 self
117 }
118
119 #[inline]
123 pub fn show_titlebar(mut self, show_titlebar: bool) -> Self {
124 self.show_titlebar = show_titlebar;
125 self
126 }
127
128 #[inline]
132 pub fn undecorated(mut self, undecorated: bool) -> Self {
133 self.undecorated = undecorated;
134 self
135 }
136
137 #[inline]
141 pub fn undecorated_shadow(mut self, undecorated_shadow: bool) -> Self {
142 self.undecorated_shadow = undecorated_shadow;
143 self
144 }
145
146 #[inline]
150 pub fn with_transparent(mut self, transparent: bool) -> Self {
151 self.transparent = transparent;
152 self
153 }
154
155 #[inline]
159 pub fn fullscreen(mut self, fullscreen: Fullscreen) -> Self {
160 self.fullscreen = Some(fullscreen);
161 self
162 }
163
164 #[inline]
168 pub fn window_icon(mut self, window_icon: Icon) -> Self {
169 self.window_icon = Some(window_icon);
170 self
171 }
172
173 #[inline]
177 pub fn title(mut self, title: impl Into<String>) -> Self {
178 self.title = title.into();
179 self
180 }
181
182 #[inline]
186 pub fn enabled_buttons(mut self, enabled_buttons: WindowButtons) -> Self {
187 self.enabled_buttons = enabled_buttons;
188 self
189 }
190
191 #[inline]
195 pub fn resizable(mut self, resizable: bool) -> Self {
196 self.resizable = resizable;
197 self
198 }
199
200 #[inline]
206 pub fn window_level(mut self, window_level: WindowLevel) -> Self {
207 self.window_level = window_level;
208 self
209 }
210
211 #[inline]
215 pub fn theme_override(mut self, theme_override: Theme) -> Self {
216 self.theme_override = Some(theme_override);
217 self
218 }
219
220 #[inline]
222 pub fn apply_default_theme(mut self, apply: bool) -> Self {
223 self.apply_default_theme = apply;
224 self
225 }
226
227 #[inline]
231 pub fn font_embolden(mut self, font_embolden: f32) -> Self {
232 self.font_embolden = font_embolden;
233 self
234 }
235
236 #[allow(unused_variables, unused_mut)] pub fn with_mac_os_config(
240 mut self,
241 mut f: impl FnMut(MacOSWindowConfig) -> MacOSWindowConfig,
242 ) -> Self {
243 #[cfg(target_os = "macos")]
244 if let Some(existing_config) = self.mac_os_config {
245 self.mac_os_config = Some(f(existing_config))
246 } else {
247 let new_config = f(MacOSWindowConfig::default());
248 self.mac_os_config = Some(new_config);
249 }
250 self
251 }
252
253 #[allow(unused_variables, unused_mut)] pub fn with_win_os_config(
257 mut self,
258 mut f: impl FnMut(WinOSWindowConfig) -> WinOSWindowConfig,
259 ) -> Self {
260 #[cfg(target_os = "windows")]
261 if let Some(existing_config) = self.win_os_config {
262 self.win_os_config = Some(f(existing_config))
263 } else {
264 let new_config = f(WinOSWindowConfig::default());
265 self.win_os_config = Some(new_config);
266 }
267 self
268 }
269
270 #[allow(unused_variables, unused_mut)] pub fn with_web_config(mut self, f: impl FnOnce(WebWindowConfig) -> WebWindowConfig) -> Self {
274 #[cfg(target_arch = "wasm32")]
275 if let Some(existing_config) = self.web_config {
276 self.web_config = Some(f(existing_config))
277 } else {
278 let new_config = f(WebWindowConfig {
279 canvas_id: String::new(),
280 });
281 self.web_config = Some(new_config);
282 }
283 self
284 }
285}
286
287#[derive(Default, Debug, Clone)]
292pub struct MacOSWindowConfig {
293 pub(crate) movable_by_window_background: Option<bool>,
294 pub(crate) titlebar_transparent: Option<bool>,
295 pub(crate) titlebar_hidden: Option<bool>,
296 pub(crate) title_hidden: Option<bool>,
297 pub(crate) titlebar_buttons_hidden: Option<bool>,
298 pub(crate) full_size_content_view: Option<bool>,
299 pub(crate) unified_titlebar: Option<bool>,
300 pub(crate) movable: Option<bool>,
301 pub(crate) traffic_lights_offset: Option<(f64, f64)>,
302 pub(crate) accepts_first_mouse: Option<bool>,
303 pub(crate) tabbing_identifier: Option<String>,
304 pub(crate) option_as_alt: Option<MacOsOptionAsAlt>,
305 pub(crate) has_shadow: Option<bool>,
306 pub(crate) disallow_high_dpi: Option<bool>,
307 pub(crate) panel: Option<bool>,
308}
309
310impl MacOSWindowConfig {
311 pub fn movable_by_window_background(mut self, val: bool) -> Self {
314 self.movable_by_window_background = Some(val);
315 self
316 }
317
318 pub fn transparent_title_bar(mut self, val: bool) -> Self {
320 self.titlebar_transparent = Some(val);
321 self
322 }
323
324 pub fn hide_titlebar(mut self, val: bool) -> Self {
326 self.titlebar_hidden = Some(val);
327 self
328 }
329
330 pub fn hide_title(mut self, val: bool) -> Self {
332 self.title_hidden = Some(val);
333 self
334 }
335
336 pub fn hide_titlebar_buttons(mut self, val: bool) -> Self {
338 self.titlebar_buttons_hidden = Some(val);
339 self
340 }
341
342 pub fn full_size_content_view(mut self, val: bool) -> Self {
344 self.full_size_content_view = Some(val);
345 self
346 }
347
348 pub fn unified_titlebar(mut self, val: bool) -> Self {
350 self.unified_titlebar = Some(val);
351 self
352 }
353
354 pub fn movable(mut self, val: bool) -> Self {
356 self.movable = Some(val);
357 self
358 }
359
360 pub fn traffic_lights_offset(mut self, x_y_offset: (f64, f64)) -> Self {
363 self.traffic_lights_offset = Some(x_y_offset);
364 self
365 }
366
367 pub fn accept_first_mouse(mut self, val: bool) -> Self {
372 self.accepts_first_mouse = Some(val);
373 self
374 }
375
376 pub fn tabbing_identifier(mut self, val: impl Into<String>) -> Self {
378 self.tabbing_identifier = Some(val.into());
379 self
380 }
381
382 pub fn interpret_option_as_alt(mut self, val: MacOsOptionAsAlt) -> Self {
385 self.option_as_alt = Some(val);
386 self
387 }
388
389 pub fn enable_shadow(mut self, val: bool) -> Self {
391 self.has_shadow = Some(val);
392 self
393 }
394
395 pub fn disallow_high_dpi(mut self, val: bool) -> Self {
398 self.disallow_high_dpi = Some(val);
399 self
400 }
401
402 pub fn panel(mut self, val: bool) -> Self {
404 self.panel = Some(val);
405 self
406 }
407}
408
409#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
415pub enum MacOsOptionAsAlt {
416 OnlyLeft,
417 OnlyRight,
418 Both,
419 #[default]
420 None,
421}
422
423#[cfg(target_os = "macos")]
424impl From<MacOsOptionAsAlt> for winit::platform::macos::OptionAsAlt {
425 fn from(opts: MacOsOptionAsAlt) -> winit::platform::macos::OptionAsAlt {
426 match opts {
427 MacOsOptionAsAlt::OnlyLeft => winit::platform::macos::OptionAsAlt::OnlyLeft,
428 MacOsOptionAsAlt::OnlyRight => winit::platform::macos::OptionAsAlt::OnlyRight,
429 MacOsOptionAsAlt::Both => winit::platform::macos::OptionAsAlt::Both,
430 MacOsOptionAsAlt::None => winit::platform::macos::OptionAsAlt::None,
431 }
432 }
433}
434
435#[derive(Debug, Clone)]
437pub struct WinOSWindowConfig {
438 pub(crate) corner_preference: WinOsCornerPreference,
440 pub(crate) set_enable: bool,
441 pub(crate) set_skip_taskbar: bool,
442 pub(crate) set_system_backdrop: WinOsBackdropType,
447 pub(crate) set_border_color: Option<Color>,
448 pub(crate) set_title_background_color: Option<Color>,
449 pub(crate) set_title_text_color: Option<Color>,
450}
451
452impl Default for WinOSWindowConfig {
453 fn default() -> Self {
454 Self {
455 corner_preference: WinOsCornerPreference::Default,
457 set_enable: true,
458 set_skip_taskbar: false,
459 set_system_backdrop: WinOsBackdropType::Auto,
460 set_border_color: None,
461 set_title_background_color: None,
462 set_title_text_color: None,
463 }
464 }
465}
466
467impl WinOSWindowConfig {
468 pub fn corner_preference(mut self, corner_preference: WinOsCornerPreference) -> Self {
480 self.corner_preference = corner_preference;
481 self
482 }
483
484 pub fn set_enable(mut self, set_enable: bool) -> Self {
498 self.set_enable = set_enable;
499 self
500 }
501
502 pub fn set_skip_taskbar(mut self, set_skip_taskbar: bool) -> Self {
504 self.set_skip_taskbar = set_skip_taskbar;
505 self
506 }
507
508 pub fn set_system_backdrop(mut self, set_system_backdrop: WinOsBackdropType) -> Self {
512 self.set_system_backdrop = set_system_backdrop;
513 self
514 }
515
516 pub fn set_border_color(mut self, set_border_color: Color) -> Self {
520 self.set_border_color = Some(set_border_color);
521 self
522 }
523
524 pub fn set_title_background_color(mut self, set_title_background_color: Color) -> Self {
528 self.set_title_background_color = Some(set_title_background_color);
529 self
530 }
531
532 pub fn set_title_text_color(mut self, set_title_text_color: Color) -> Self {
536 self.set_title_text_color = Some(set_title_text_color);
537 self
538 }
539}
540
541#[derive(Debug, Clone)]
547pub enum WinOsCornerPreference {
548 Default,
552
553 DoNotRound,
557
558 Round,
562
563 RoundSmall,
567}
568
569#[cfg(target_os = "windows")]
570impl From<WinOsCornerPreference> for winit::platform::windows::CornerPreference {
571 fn from(value: WinOsCornerPreference) -> Self {
572 use winit::platform::windows::CornerPreference;
573 match value {
574 WinOsCornerPreference::Default => CornerPreference::Default,
575 WinOsCornerPreference::DoNotRound => CornerPreference::DoNotRound,
576 WinOsCornerPreference::Round => CornerPreference::Round,
577 WinOsCornerPreference::RoundSmall => CornerPreference::RoundSmall,
578 }
579 }
580}
581
582#[derive(Debug, Clone)]
588pub enum WinOsBackdropType {
589 Auto,
593
594 None,
596
597 MainWindow,
601
602 TransientWindow,
606
607 TabbedWindow,
611}
612
613#[cfg(target_os = "windows")]
614impl From<WinOsBackdropType> for winit::platform::windows::BackdropType {
615 fn from(value: WinOsBackdropType) -> Self {
616 use winit::platform::windows::BackdropType;
617 match value {
618 WinOsBackdropType::Auto => BackdropType::Auto,
619 WinOsBackdropType::None => BackdropType::None,
620 WinOsBackdropType::MainWindow => BackdropType::MainWindow,
621 WinOsBackdropType::TransientWindow => BackdropType::TransientWindow,
622 WinOsBackdropType::TabbedWindow => BackdropType::TabbedWindow,
623 }
624 }
625}
626
627#[cfg(target_os = "windows")]
628pub(super) fn convert_to_win(c: Option<peniko::Color>) -> Option<winit::platform::windows::Color> {
629 c.map(|c| {
630 let c = c.to_rgba8();
631 winit::platform::windows::Color::from_rgb(c.r, c.g, c.b)
632 })
633}
634
635#[derive(Default, Debug, Clone)]
638pub struct WebWindowConfig {
639 pub(crate) canvas_id: String,
641}
642
643impl WebWindowConfig {
644 pub fn canvas_id(mut self, val: impl Into<String>) -> Self {
646 self.canvas_id = val.into();
647 self
648 }
649}
650
651pub fn new_window<V: IntoView + 'static>(
654 app_view: impl FnOnce(WindowId) -> V + 'static,
655 config: Option<WindowConfig>,
656) {
657 add_app_update_event(AppUpdateEvent::NewWindow {
658 window_creation: WindowCreation {
659 view_fn: Box::new(|window_id| app_view(window_id).into_any()),
660 config,
661 },
662 });
663}
664
665pub fn close_window(window_id: WindowId) {
675 add_app_update_event(AppUpdateEvent::CloseWindow { window_id });
676}
677
678pub fn request_close_window(window_id: WindowId) {
688 add_app_update_event(AppUpdateEvent::RequestCloseWindow { window_id });
689}