1#![allow(unused_variables)]
2
3use tauri::{LogicalPosition, LogicalSize};
4use tauri_runtime::{
5 Error, Icon, ProgressBarState, Result, UserAttentionType, UserEvent, WindowDispatch,
6 WindowEventId,
7 dpi::{PhysicalPosition, PhysicalSize, Position, Size},
8 monitor::Monitor,
9 webview::{DetachedWebview, PendingWebview},
10 window::{
11 CursorIcon, DetachedWindow, PendingWindow, RawWindow, WindowBuilder, WindowBuilderBase,
12 WindowEvent, WindowId,
13 },
14};
15use tauri_utils::{Theme, config::WindowConfig};
16use verso::{VersoBuilder, VersoviewController};
17#[cfg(windows)]
18use windows::Win32::Foundation::HWND;
19
20use std::{
21 collections::HashMap,
22 fmt::{self, Debug},
23 sync::{Arc, Mutex},
24};
25
26use crate::{
27 RuntimeContext, VersoRuntime,
28 event_loop_ext::TaoEventLoopWindowTargetExt,
29 get_verso_devtools_port, get_verso_resource_directory,
30 runtime::Message,
31 utils::{from_verso_theme, to_verso_theme},
32};
33
34pub(crate) struct Window {
35 pub(crate) label: String,
36 pub(crate) webview: Arc<Mutex<VersoviewController>>,
37 pub(crate) on_window_event_listeners: WindowEventListeners,
38}
39
40#[derive(Debug, Clone)]
41pub struct VersoWindowBuilder {
42 pub verso_builder: VersoBuilder,
43 pub has_icon: bool,
44 pub theme: Option<Theme>,
45}
46
47impl Default for VersoWindowBuilder {
48 fn default() -> Self {
49 let mut verso_builder = VersoBuilder::new();
50 if let Some(resource_directory) = get_verso_resource_directory() {
51 verso_builder = verso_builder.resources_directory(resource_directory);
52 }
53 if let Some(devtools_port) = get_verso_devtools_port() {
54 verso_builder = verso_builder.devtools_port(devtools_port);
55 }
56 verso_builder = verso_builder.decorated(true);
58 verso_builder = verso_builder.transparent(false);
60 Self {
61 verso_builder,
62 has_icon: false,
63 theme: None,
64 }
65 }
66}
67
68impl WindowBuilderBase for VersoWindowBuilder {}
69
70impl WindowBuilder for VersoWindowBuilder {
71 fn new() -> Self {
72 Self::default()
73 }
74
75 fn with_config(config: &WindowConfig) -> Self {
76 let builder = Self::default();
77 let mut verso_builder = builder.verso_builder;
78 verso_builder = verso_builder
79 .focused(config.focus)
80 .fullscreen(config.fullscreen)
81 .maximized(config.maximized)
82 .visible(config.visible)
83 .inner_size(LogicalSize::new(config.width, config.height))
84 .title(config.title.clone())
85 .decorated(config.decorations)
86 .transparent(config.transparent);
87
88 if let (Some(x), Some(y)) = (config.x, config.y) {
89 verso_builder = verso_builder.position(LogicalPosition::new(x, y));
90 };
91
92 if let Some(theme) = config.theme {
93 verso_builder = verso_builder.theme(to_verso_theme(theme));
94 }
95
96 Self {
97 verso_builder,
98 has_icon: false,
99 theme: None,
100 }
101 }
102
103 fn center(self) -> Self {
105 self
106 }
107
108 fn position(mut self, x: f64, y: f64) -> Self {
110 self.verso_builder = self.verso_builder.position(LogicalPosition::new(x, y));
111 self
112 }
113
114 fn inner_size(mut self, width: f64, height: f64) -> Self {
116 self.verso_builder = self
117 .verso_builder
118 .inner_size(LogicalSize::new(width, height));
119 self
120 }
121
122 fn min_inner_size(self, min_width: f64, min_height: f64) -> Self {
124 self
125 }
126
127 fn max_inner_size(self, max_width: f64, max_height: f64) -> Self {
129 self
130 }
131
132 fn inner_size_constraints(
134 self,
135 constraints: tauri_runtime::window::WindowSizeConstraints,
136 ) -> Self {
137 self
138 }
139
140 fn resizable(self, resizable: bool) -> Self {
142 self
143 }
144
145 fn maximizable(self, resizable: bool) -> Self {
147 self
148 }
149
150 fn minimizable(self, resizable: bool) -> Self {
152 self
153 }
154
155 fn closable(self, resizable: bool) -> Self {
157 self
158 }
159
160 fn title<S: Into<String>>(mut self, title: S) -> Self {
161 self.verso_builder = self.verso_builder.title(title);
162 self
163 }
164
165 fn fullscreen(mut self, fullscreen: bool) -> Self {
166 self.verso_builder = self.verso_builder.fullscreen(fullscreen);
167 self
168 }
169
170 fn focused(mut self, focused: bool) -> Self {
171 self.verso_builder = self.verso_builder.focused(focused);
172 self
173 }
174
175 fn maximized(mut self, maximized: bool) -> Self {
176 self.verso_builder = self.verso_builder.maximized(maximized);
177 self
178 }
179
180 fn visible(mut self, visible: bool) -> Self {
181 self.verso_builder = self.verso_builder.visible(visible);
182 self
183 }
184
185 fn decorations(mut self, decorations: bool) -> Self {
186 self.verso_builder = self.verso_builder.decorated(decorations);
187 self
188 }
189
190 fn always_on_bottom(mut self, always_on_bottom: bool) -> Self {
191 self.verso_builder = self.verso_builder.window_level(if always_on_bottom {
192 verso::WindowLevel::AlwaysOnTop
193 } else {
194 verso::WindowLevel::Normal
195 });
196 self
197 }
198
199 fn always_on_top(mut self, always_on_top: bool) -> Self {
200 self.verso_builder = self.verso_builder.window_level(if always_on_top {
201 verso::WindowLevel::AlwaysOnTop
202 } else {
203 verso::WindowLevel::Normal
204 });
205 self
206 }
207
208 fn visible_on_all_workspaces(self, visible_on_all_workspaces: bool) -> Self {
210 self
211 }
212
213 fn content_protected(self, protected: bool) -> Self {
215 self
216 }
217
218 fn icon(mut self, icon: Icon<'_>) -> Result<Self> {
219 self.verso_builder = self.verso_builder.icon(verso::Icon {
220 rgba: icon.rgba.to_vec(),
221 width: icon.width,
222 height: icon.height,
223 });
224 self.has_icon = true;
225 Ok(self)
226 }
227
228 fn skip_taskbar(self, skip: bool) -> Self {
230 self
231 }
232
233 fn window_classname<S: Into<String>>(self, classname: S) -> Self {
235 self
236 }
237
238 fn shadow(self, enable: bool) -> Self {
240 self
241 }
242
243 #[cfg(target_os = "macos")]
245 fn parent(self, parent: *mut std::ffi::c_void) -> Self {
246 self
247 }
248
249 #[cfg(any(
251 target_os = "linux",
252 target_os = "dragonfly",
253 target_os = "freebsd",
254 target_os = "netbsd",
255 target_os = "openbsd"
256 ))]
257 fn transient_for(self, parent: &impl gtk::glib::IsA<gtk::Window>) -> Self {
258 self
259 }
260
261 #[cfg(windows)]
263 fn drag_and_drop(self, enabled: bool) -> Self {
264 self
265 }
266
267 #[cfg(target_os = "macos")]
269 fn title_bar_style(self, style: tauri_utils::TitleBarStyle) -> Self {
270 self
271 }
272
273 #[cfg(target_os = "macos")]
275 fn hidden_title(self, transparent: bool) -> Self {
276 self
277 }
278
279 #[cfg(target_os = "macos")]
281 fn tabbing_identifier(self, identifier: &str) -> Self {
282 self
283 }
284
285 #[cfg(target_os = "macos")]
287 fn traffic_light_position<P: Into<Position>>(self, position: P) -> Self {
288 self
289 }
290
291 fn theme(mut self, theme: Option<Theme>) -> Self {
292 if let Some(theme) = theme {
293 self.verso_builder = self.verso_builder.theme(to_verso_theme(theme));
294 self.theme = Some(theme);
295 }
296 self
297 }
298
299 fn has_icon(&self) -> bool {
300 self.has_icon
301 }
302
303 fn get_theme(&self) -> Option<Theme> {
304 self.theme
305 }
306
307 fn background_color(self, _color: tauri_utils::config::Color) -> Self {
309 self
310 }
311
312 #[cfg(windows)]
314 fn owner(self, owner: HWND) -> Self {
315 self
316 }
317
318 #[cfg(windows)]
320 fn parent(self, parent: HWND) -> Self {
321 self
322 }
323
324 #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
325 #[cfg_attr(
326 docsrs,
327 doc(cfg(any(not(target_os = "macos"), feature = "macos-private-api")))
328 )]
329 fn transparent(mut self, transparent: bool) -> Self {
330 self.verso_builder = self.verso_builder.transparent(transparent);
331 self
332 }
333
334 fn prevent_overflow(self) -> Self {
336 self
337 }
338
339 fn prevent_overflow_with_margin(self, margin: tauri_runtime::dpi::Size) -> Self {
341 self
342 }
343}
344
345pub type WindowEventHandler = Box<dyn Fn(&WindowEvent) + Send>;
346pub type WindowEventListeners = Arc<Mutex<HashMap<WindowEventId, WindowEventHandler>>>;
347
348#[derive(Clone)]
350pub struct VersoWindowDispatcher<T: UserEvent> {
351 pub(crate) id: WindowId,
352 pub(crate) context: RuntimeContext<T>,
353 pub(crate) webview: Arc<Mutex<VersoviewController>>,
354 pub(crate) on_window_event_listeners: WindowEventListeners,
355}
356
357impl<T: UserEvent> Debug for VersoWindowDispatcher<T> {
358 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
359 f.debug_struct("VersoWebviewDispatcher")
360 .field("id", &self.id)
361 .field("context", &self.context)
362 .field("webview", &"VersoviewController")
363 .finish()
364 }
365}
366
367impl<T: UserEvent> WindowDispatch<T> for VersoWindowDispatcher<T> {
368 type Runtime = VersoRuntime<T>;
369
370 type WindowBuilder = VersoWindowBuilder;
371
372 fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
373 self.context.run_on_main_thread(f)
374 }
375
376 fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) -> WindowEventId {
378 let id = self.context.next_window_event_id();
379 self.on_window_event_listeners
380 .lock()
381 .unwrap()
382 .insert(id, Box::new(f));
383 id
384 }
385
386 fn scale_factor(&self) -> Result<f64> {
387 self.webview
388 .lock()
389 .unwrap()
390 .get_scale_factor()
391 .map_err(|_| Error::FailedToSendMessage)
392 }
393
394 fn inner_position(&self) -> Result<PhysicalPosition<i32>> {
400 Ok(self
401 .webview
402 .lock()
403 .unwrap()
404 .get_inner_position()
405 .map_err(|_| Error::FailedToSendMessage)?
406 .unwrap_or_default())
407 }
408
409 fn outer_position(&self) -> Result<PhysicalPosition<i32>> {
415 Ok(self
416 .webview
417 .lock()
418 .unwrap()
419 .get_outer_position()
420 .map_err(|_| Error::FailedToSendMessage)?
421 .unwrap_or_default())
422 }
423
424 fn inner_size(&self) -> Result<PhysicalSize<u32>> {
425 self.webview
426 .lock()
427 .unwrap()
428 .get_inner_size()
429 .map_err(|_| Error::FailedToSendMessage)
430 }
431
432 fn outer_size(&self) -> Result<PhysicalSize<u32>> {
433 self.webview
434 .lock()
435 .unwrap()
436 .get_outer_size()
437 .map_err(|_| Error::FailedToSendMessage)
438 }
439
440 fn is_fullscreen(&self) -> Result<bool> {
441 self.webview
442 .lock()
443 .unwrap()
444 .is_fullscreen()
445 .map_err(|_| Error::FailedToSendMessage)
446 }
447
448 fn is_minimized(&self) -> Result<bool> {
449 self.webview
450 .lock()
451 .unwrap()
452 .is_minimized()
453 .map_err(|_| Error::FailedToSendMessage)
454 }
455
456 fn is_maximized(&self) -> Result<bool> {
457 self.webview
458 .lock()
459 .unwrap()
460 .is_maximized()
461 .map_err(|_| Error::FailedToSendMessage)
462 }
463
464 fn is_focused(&self) -> Result<bool> {
466 Ok(false)
467 }
468
469 fn is_decorated(&self) -> Result<bool> {
471 Ok(false)
472 }
473
474 fn is_resizable(&self) -> Result<bool> {
476 Ok(true)
477 }
478
479 fn is_maximizable(&self) -> Result<bool> {
481 Ok(true)
482 }
483
484 fn is_minimizable(&self) -> Result<bool> {
486 Ok(true)
487 }
488
489 fn is_closable(&self) -> Result<bool> {
491 Ok(true)
492 }
493
494 fn is_visible(&self) -> Result<bool> {
495 self.webview
496 .lock()
497 .unwrap()
498 .is_visible()
499 .map_err(|_| Error::FailedToSendMessage)
500 }
501
502 fn title(&self) -> Result<String> {
503 self.webview
504 .lock()
505 .unwrap()
506 .get_title()
507 .map_err(|_| Error::FailedToSendMessage)
508 }
509
510 fn current_monitor(&self) -> Result<Option<Monitor>> {
512 Ok(None)
513 }
514
515 fn primary_monitor(&self) -> Result<Option<Monitor>> {
516 self.context
517 .run_on_main_thread_with_event_loop(|e| e.tauri_primary_monitor())
518 }
519
520 fn monitor_from_point(&self, x: f64, y: f64) -> Result<Option<Monitor>> {
521 self.context
522 .run_on_main_thread_with_event_loop(move |e| e.tauri_monitor_from_point(x, y))
523 }
524
525 fn available_monitors(&self) -> Result<Vec<Monitor>> {
526 self.context
527 .run_on_main_thread_with_event_loop(|e| e.tauri_available_monitors())
528 }
529
530 fn theme(&self) -> Result<Theme> {
531 let theme = self
532 .webview
533 .lock()
534 .unwrap()
535 .get_theme()
536 .map_err(|_| Error::FailedToSendMessage)?;
537 Ok(from_verso_theme(theme))
538 }
539
540 #[cfg(any(
542 target_os = "linux",
543 target_os = "dragonfly",
544 target_os = "freebsd",
545 target_os = "netbsd",
546 target_os = "openbsd"
547 ))]
548 fn gtk_window(&self) -> Result<gtk::ApplicationWindow> {
549 unimplemented!()
550 }
551
552 #[cfg(any(
554 target_os = "linux",
555 target_os = "dragonfly",
556 target_os = "freebsd",
557 target_os = "netbsd",
558 target_os = "openbsd"
559 ))]
560 fn default_vbox(&self) -> Result<gtk::Box> {
561 unimplemented!()
562 }
563
564 fn center(&self) -> Result<()> {
566 Ok(())
567 }
568
569 fn request_user_attention(&self, request_type: Option<UserAttentionType>) -> Result<()> {
571 Ok(())
572 }
573
574 fn create_window<F: Fn(RawWindow<'_>) + Send + 'static>(
579 &mut self,
580 pending: PendingWindow<T, Self::Runtime>,
581 after_window_creation: Option<F>,
582 ) -> Result<DetachedWindow<T, Self::Runtime>> {
583 self.context.create_window(pending, after_window_creation)
584 }
585
586 fn create_webview(
588 &mut self,
589 pending: PendingWebview<T, Self::Runtime>,
590 ) -> Result<DetachedWebview<T, Self::Runtime>> {
591 Err(tauri_runtime::Error::CreateWindow)
592 }
593
594 fn set_resizable(&self, resizable: bool) -> Result<()> {
596 Ok(())
597 }
598
599 fn set_maximizable(&self, maximizable: bool) -> Result<()> {
601 Ok(())
602 }
603
604 fn set_minimizable(&self, minimizable: bool) -> Result<()> {
606 Ok(())
607 }
608
609 fn set_closable(&self, closable: bool) -> Result<()> {
611 Ok(())
612 }
613
614 fn set_title<S: Into<String>>(&self, title: S) -> Result<()> {
615 self.webview
616 .lock()
617 .unwrap()
618 .set_title(title)
619 .map_err(|_| Error::FailedToSendMessage)?;
620 Ok(())
621 }
622
623 fn maximize(&self) -> Result<()> {
624 self.webview
625 .lock()
626 .unwrap()
627 .set_maximized(true)
628 .map_err(|_| Error::FailedToSendMessage)?;
629 Ok(())
630 }
631
632 fn unmaximize(&self) -> Result<()> {
633 self.webview
634 .lock()
635 .unwrap()
636 .set_maximized(false)
637 .map_err(|_| Error::FailedToSendMessage)?;
638 Ok(())
639 }
640
641 fn minimize(&self) -> Result<()> {
642 self.webview
643 .lock()
644 .unwrap()
645 .set_minimized(true)
646 .map_err(|_| Error::FailedToSendMessage)?;
647 Ok(())
648 }
649
650 fn unminimize(&self) -> Result<()> {
651 self.webview
652 .lock()
653 .unwrap()
654 .set_minimized(false)
655 .map_err(|_| Error::FailedToSendMessage)?;
656 Ok(())
657 }
658
659 fn show(&self) -> Result<()> {
660 self.webview
661 .lock()
662 .unwrap()
663 .set_visible(true)
664 .map_err(|_| Error::FailedToSendMessage)?;
665 Ok(())
666 }
667
668 fn hide(&self) -> Result<()> {
669 self.webview
670 .lock()
671 .unwrap()
672 .set_visible(false)
673 .map_err(|_| Error::FailedToSendMessage)?;
674 Ok(())
675 }
676
677 fn close(&self) -> Result<()> {
678 self.context.send_message(Message::CloseWindow(self.id))?;
679 Ok(())
680 }
681
682 fn destroy(&self) -> Result<()> {
683 self.context.send_message(Message::DestroyWindow(self.id))?;
684 Ok(())
685 }
686
687 fn set_decorations(&self, decorations: bool) -> Result<()> {
689 Ok(())
690 }
691
692 fn set_shadow(&self, shadow: bool) -> Result<()> {
694 Ok(())
695 }
696
697 fn set_always_on_bottom(&self, always_on_bottom: bool) -> Result<()> {
698 self.webview
699 .lock()
700 .unwrap()
701 .set_window_level(if always_on_bottom {
702 verso::WindowLevel::AlwaysOnBottom
703 } else {
704 verso::WindowLevel::Normal
705 })
706 .map_err(|_| Error::FailedToSendMessage)?;
707 Ok(())
708 }
709
710 fn set_always_on_top(&self, always_on_top: bool) -> Result<()> {
711 self.webview
712 .lock()
713 .unwrap()
714 .set_window_level(if always_on_top {
715 verso::WindowLevel::AlwaysOnTop
716 } else {
717 verso::WindowLevel::Normal
718 })
719 .map_err(|_| Error::FailedToSendMessage)?;
720 Ok(())
721 }
722
723 fn set_visible_on_all_workspaces(&self, visible_on_all_workspaces: bool) -> Result<()> {
725 Ok(())
726 }
727
728 fn set_content_protected(&self, protected: bool) -> Result<()> {
730 Ok(())
731 }
732
733 fn set_size(&self, size: Size) -> Result<()> {
734 self.webview
735 .lock()
736 .unwrap()
737 .set_size(size)
738 .map_err(|_| Error::FailedToSendMessage)?;
739 Ok(())
740 }
741
742 fn set_min_size(&self, size: Option<Size>) -> Result<()> {
744 Ok(())
745 }
746
747 fn set_max_size(&self, size: Option<Size>) -> Result<()> {
749 Ok(())
750 }
751
752 fn set_position(&self, position: Position) -> Result<()> {
753 self.webview
754 .lock()
755 .unwrap()
756 .set_position(position)
757 .map_err(|_| Error::FailedToSendMessage)?;
758 Ok(())
759 }
760
761 fn set_fullscreen(&self, fullscreen: bool) -> Result<()> {
762 self.webview
763 .lock()
764 .unwrap()
765 .set_fullscreen(fullscreen)
766 .map_err(|_| Error::FailedToSendMessage)?;
767 Ok(())
768 }
769
770 fn set_focus(&self) -> Result<()> {
771 self.webview
772 .lock()
773 .unwrap()
774 .focus()
775 .map_err(|_| Error::FailedToSendMessage)?;
776 Ok(())
777 }
778
779 fn set_icon(&self, icon: Icon<'_>) -> Result<()> {
781 Ok(())
782 }
783
784 fn set_skip_taskbar(&self, skip: bool) -> Result<()> {
786 Ok(())
787 }
788
789 fn set_cursor_grab(&self, grab: bool) -> Result<()> {
791 Ok(())
792 }
793
794 fn set_cursor_visible(&self, visible: bool) -> Result<()> {
796 Ok(())
797 }
798
799 fn set_cursor_icon(&self, icon: CursorIcon) -> Result<()> {
801 Ok(())
802 }
803
804 fn set_cursor_position<Pos: Into<Position>>(&self, position: Pos) -> Result<()> {
806 Ok(())
807 }
808
809 fn set_ignore_cursor_events(&self, ignore: bool) -> Result<()> {
811 Ok(())
812 }
813
814 fn start_dragging(&self) -> Result<()> {
815 self.webview
816 .lock()
817 .unwrap()
818 .start_dragging()
819 .map_err(|_| Error::FailedToSendMessage)?;
820 Ok(())
821 }
822
823 fn start_resize_dragging(&self, direction: tauri_runtime::ResizeDirection) -> Result<()> {
825 Ok(())
826 }
827
828 fn set_progress_bar(&self, progress_state: ProgressBarState) -> Result<()> {
830 Ok(())
831 }
832
833 fn set_badge_count(&self, count: Option<i64>, desktop_filename: Option<String>) -> Result<()> {
835 Ok(())
836 }
837
838 fn set_badge_label(&self, label: Option<String>) -> Result<()> {
840 Ok(())
841 }
842
843 fn set_overlay_icon(&self, icon: Option<Icon<'_>>) -> Result<()> {
845 Ok(())
846 }
847
848 fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> Result<()> {
850 Ok(())
851 }
852
853 fn set_size_constraints(
855 &self,
856 constraints: tauri_runtime::window::WindowSizeConstraints,
857 ) -> Result<()> {
858 Ok(())
859 }
860
861 fn set_theme(&self, theme: Option<Theme>) -> Result<()> {
862 self.webview
863 .lock()
864 .unwrap()
865 .set_theme(theme.map(to_verso_theme))
866 .map_err(|_| Error::FailedToSendMessage)?;
867 Ok(())
868 }
869
870 fn set_enabled(&self, enabled: bool) -> Result<()> {
872 Ok(())
873 }
874
875 fn is_enabled(&self) -> Result<bool> {
877 Ok(true)
878 }
879
880 fn set_background_color(&self, color: Option<tauri_utils::config::Color>) -> Result<()> {
882 Ok(())
883 }
884
885 fn window_handle(
887 &self,
888 ) -> std::result::Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError>
889 {
890 Err(raw_window_handle::HandleError::NotSupported)
891 }
892
893 fn is_always_on_top(&self) -> Result<bool> {
895 Ok(false)
896 }
897
898 fn set_traffic_light_position(&self, position: Position) -> Result<()> {
900 Ok(())
901 }
902}