1pub(crate) mod plugin;
8
9use tauri_runtime::{
10 dpi::{PhysicalPosition, PhysicalRect, PhysicalSize},
11 webview::PendingWebview,
12};
13pub use tauri_utils::{config::Color, WindowEffect as Effect, WindowEffectState as EffectState};
14
15#[cfg(desktop)]
16pub use crate::runtime::ProgressBarStatus;
17
18use crate::{
19 app::AppHandle,
20 event::{Event, EventId, EventTarget},
21 ipc::{CommandArg, CommandItem, InvokeError},
22 manager::{AppManager, EmitPayload},
23 runtime::{
24 monitor::Monitor as RuntimeMonitor,
25 window::{DetachedWindow, PendingWindow, WindowBuilder as _},
26 RuntimeHandle, WindowDispatch,
27 },
28 sealed::{ManagerBase, RuntimeOrDispatch},
29 utils::config::{WindowConfig, WindowEffectsConfig},
30 webview::WebviewBuilder,
31 Emitter, EventLoopMessage, EventName, Listener, Manager, ResourceTable, Runtime, Theme, Webview,
32 WindowEvent,
33};
34#[cfg(desktop)]
35use crate::{
36 image::Image,
37 menu::{ContextMenu, Menu, MenuId},
38 runtime::{
39 dpi::{Position, Size},
40 UserAttentionType,
41 },
42 CursorIcon,
43};
44
45use serde::Serialize;
46#[cfg(windows)]
47use windows::Win32::Foundation::HWND;
48
49use tauri_macros::default_runtime;
50
51use std::{
52 fmt,
53 hash::{Hash, Hasher},
54 sync::{Arc, Mutex, MutexGuard},
55};
56
57#[derive(Debug, Clone, Serialize)]
59#[serde(rename_all = "camelCase")]
60pub struct Monitor {
61 pub(crate) name: Option<String>,
62 pub(crate) size: PhysicalSize<u32>,
63 pub(crate) position: PhysicalPosition<i32>,
64 pub(crate) work_area: PhysicalRect<i32, u32>,
65 pub(crate) scale_factor: f64,
66}
67
68impl From<RuntimeMonitor> for Monitor {
69 fn from(monitor: RuntimeMonitor) -> Self {
70 Self {
71 name: monitor.name,
72 size: monitor.size,
73 position: monitor.position,
74 work_area: monitor.work_area,
75 scale_factor: monitor.scale_factor,
76 }
77 }
78}
79
80impl Monitor {
81 pub fn name(&self) -> Option<&String> {
84 self.name.as_ref()
85 }
86
87 pub fn size(&self) -> &PhysicalSize<u32> {
89 &self.size
90 }
91
92 pub fn position(&self) -> &PhysicalPosition<i32> {
94 &self.position
95 }
96
97 pub fn work_area(&self) -> &PhysicalRect<i32, u32> {
99 &self.work_area
100 }
101
102 pub fn scale_factor(&self) -> f64 {
104 self.scale_factor
105 }
106}
107
108macro_rules! unstable_struct {
109 (#[doc = $doc:expr] $($tokens:tt)*) => {
110 #[cfg(feature = "unstable")]
111 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
112 #[doc = $doc]
113 pub $($tokens)*
114
115 #[cfg(not(feature = "unstable"))]
116 pub(crate) $($tokens)*
117 }
118}
119
120unstable_struct!(
121 #[doc = "A builder for a window managed by Tauri."]
122 struct WindowBuilder<'a, R: Runtime, M: Manager<R>> {
123 manager: &'a M,
124 pub(crate) label: String,
125 pub(crate) window_builder:
126 <R::WindowDispatcher as WindowDispatch<EventLoopMessage>>::WindowBuilder,
127 #[cfg(desktop)]
128 pub(crate) menu: Option<Menu<R>>,
129 #[cfg(desktop)]
130 on_menu_event: Option<crate::app::GlobalMenuEventListener<Window<R>>>,
131 window_effects: Option<WindowEffectsConfig>,
132 }
133);
134
135impl<R: Runtime, M: Manager<R>> fmt::Debug for WindowBuilder<'_, R, M> {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 f.debug_struct("WindowBuilder")
138 .field("label", &self.label)
139 .field("window_builder", &self.window_builder)
140 .finish()
141 }
142}
143
144#[cfg_attr(not(feature = "unstable"), allow(dead_code))]
145impl<'a, R: Runtime, M: Manager<R>> WindowBuilder<'a, R, M> {
146 #[cfg_attr(
158 feature = "unstable",
159 doc = r####"
160```
161tauri::Builder::default()
162 .setup(|app| {
163 let window = tauri::window::WindowBuilder::new(app, "label")
164 .build()?;
165 Ok(())
166 });
167```
168 "####
169 )]
170 #[cfg_attr(
173 feature = "unstable",
174 doc = r####"
175```
176tauri::Builder::default()
177 .setup(|app| {
178 let handle = app.handle().clone();
179 std::thread::spawn(move || {
180 let window = tauri::window::WindowBuilder::new(&handle, "label")
181 .build()
182 .unwrap();
183 });
184 Ok(())
185 });
186```
187 "####
188 )]
189 #[cfg_attr(
193 feature = "unstable",
194 doc = r####"
195```
196#[tauri::command]
197async fn create_window(app: tauri::AppHandle) {
198 let window = tauri::window::WindowBuilder::new(&app, "label")
199 .build()
200 .unwrap();
201}
202```
203 "####
204 )]
205 pub fn new<L: Into<String>>(manager: &'a M, label: L) -> Self {
208 Self {
209 manager,
210 label: label.into(),
211 window_builder: <R::WindowDispatcher as WindowDispatch<EventLoopMessage>>::WindowBuilder::new(
212 ),
213 #[cfg(desktop)]
214 menu: None,
215 #[cfg(desktop)]
216 on_menu_event: None,
217 window_effects: None,
218 }
219 }
220
221 #[cfg_attr(
235 feature = "unstable",
236 doc = r####"
237```
238#[tauri::command]
239async fn reopen_window(app: tauri::AppHandle) {
240 let window = tauri::window::WindowBuilder::from_config(&app, &app.config().app.windows.get(0).unwrap().clone())
241 .unwrap()
242 .build()
243 .unwrap();
244}
245```
246 "####
247 )]
248 pub fn from_config(manager: &'a M, config: &WindowConfig) -> crate::Result<Self> {
251 #[cfg_attr(not(windows), allow(unused_mut))]
252 let mut builder = Self {
253 manager,
254 label: config.label.clone(),
255 window_effects: config.window_effects.clone(),
256 window_builder:
257 <R::WindowDispatcher as WindowDispatch<EventLoopMessage>>::WindowBuilder::with_config(
258 config,
259 ),
260 #[cfg(desktop)]
261 menu: None,
262 #[cfg(desktop)]
263 on_menu_event: None,
264 };
265
266 #[cfg(desktop)]
267 if let Some(parent) = &config.parent {
268 let window = manager
269 .manager()
270 .get_window(parent)
271 .ok_or(crate::Error::WindowNotFound)?;
272 builder = builder.parent(&window)?;
273 }
274
275 Ok(builder)
276 }
277
278 #[cfg_attr(
288 feature = "unstable",
289 doc = r####"
290```
291use tauri::menu::{Menu, Submenu, MenuItem};
292tauri::Builder::default()
293 .setup(|app| {
294 let handle = app.handle();
295 let save_menu_item = MenuItem::new(handle, "Save", true, None::<&str>)?;
296 let menu = Menu::with_items(handle, &[
297 &Submenu::with_items(handle, "File", true, &[
298 &save_menu_item,
299 ])?,
300 ])?;
301 let window = tauri::window::WindowBuilder::new(app, "editor")
302 .menu(menu)
303 .on_menu_event(move |window, event| {
304 if event.id == save_menu_item.id() {
305 // save menu item
306 }
307 })
308 .build()
309 .unwrap();
310 ///
311 Ok(())
312 });
313```"####
314 )]
315 #[cfg(desktop)]
316 pub fn on_menu_event<F: Fn(&Window<R>, crate::menu::MenuEvent) + Send + Sync + 'static>(
317 mut self,
318 f: F,
319 ) -> Self {
320 self.on_menu_event.replace(Box::new(f));
321 self
322 }
323
324 #[cfg_attr(
326 feature = "tracing",
327 tracing::instrument(name = "webview::create", skip_all)
328 )]
329 pub(crate) fn with_webview(
330 self,
331 webview: WebviewBuilder<R>,
332 ) -> crate::Result<(Window<R>, Webview<R>)> {
333 let pending_webview = webview.into_pending_webview(self.manager, &self.label)?;
334 let window = self.build_internal(Some(pending_webview))?;
335
336 let webview = window.webviews().first().unwrap().clone();
337
338 Ok((window, webview))
339 }
340
341 pub fn build(self) -> crate::Result<Window<R>> {
343 self.build_internal(None)
344 }
345
346 fn build_internal(
348 self,
349 webview: Option<PendingWebview<EventLoopMessage, R>>,
350 ) -> crate::Result<Window<R>> {
351 #[cfg(desktop)]
352 let theme = self.window_builder.get_theme();
353
354 let mut pending = PendingWindow::new(self.window_builder, self.label)?;
355 if let Some(webview) = webview {
356 pending.set_webview(webview);
357 }
358
359 let app_manager = self.manager.manager();
360
361 let pending = app_manager.window.prepare_window(pending)?;
362
363 #[cfg(desktop)]
364 let window_menu = {
365 let is_app_wide = self.menu.is_none();
366 self
367 .menu
368 .or_else(|| self.manager.app_handle().menu())
369 .map(|menu| WindowMenu { is_app_wide, menu })
370 };
371
372 #[cfg(desktop)]
373 let handler = app_manager
374 .menu
375 .prepare_window_menu_creation_handler(window_menu.as_ref(), theme);
376 #[cfg(not(desktop))]
377 #[allow(clippy::type_complexity)]
378 let handler: Option<Box<dyn Fn(tauri_runtime::window::RawWindow<'_>) + Send>> = None;
379
380 let window = match &mut self.manager.runtime() {
381 RuntimeOrDispatch::Runtime(runtime) => runtime.create_window(pending, handler),
382 RuntimeOrDispatch::RuntimeHandle(handle) => handle.create_window(pending, handler),
383 RuntimeOrDispatch::Dispatch(dispatcher) => dispatcher.create_window(pending, handler),
384 }
385 .map(|detached_window| {
386 let window = app_manager.window.attach_window(
387 self.manager.app_handle().clone(),
388 detached_window.clone(),
389 #[cfg(desktop)]
390 window_menu,
391 );
392
393 if let Some(webview) = detached_window.webview {
394 app_manager.webview.attach_webview(
395 window.clone(),
396 webview.webview,
397 webview.use_https_scheme,
398 );
399 }
400
401 window
402 })?;
403
404 #[cfg(desktop)]
405 if let Some(handler) = self.on_menu_event {
406 window.on_menu_event(handler);
407 }
408
409 let app_manager = self.manager.manager_owned();
410 let window_label = window.label().to_string();
411 let window_ = window.clone();
412 let _ = window.run_on_main_thread(move || {
414 if let Some(effects) = self.window_effects {
415 _ = crate::vibrancy::set_window_effects(&window_, Some(effects));
416 }
417 let event = crate::EventName::from_str("tauri://window-created");
418 let payload = Some(crate::webview::CreatedEvent {
419 label: window_label,
420 });
421 let _ = app_manager.emit(event, EmitPayload::Serialize(&payload));
422 });
423
424 Ok(window)
425 }
426}
427
428#[cfg(desktop)]
430#[cfg_attr(not(feature = "unstable"), allow(dead_code))]
431impl<'a, R: Runtime, M: Manager<R>> WindowBuilder<'a, R, M> {
432 #[must_use]
434 pub fn menu(mut self, menu: Menu<R>) -> Self {
435 self.menu.replace(menu);
436 self
437 }
438
439 #[must_use]
441 pub fn center(mut self) -> Self {
442 self.window_builder = self.window_builder.center();
443 self
444 }
445
446 #[must_use]
448 pub fn position(mut self, x: f64, y: f64) -> Self {
449 self.window_builder = self.window_builder.position(x, y);
450 self
451 }
452
453 #[must_use]
455 pub fn inner_size(mut self, width: f64, height: f64) -> Self {
456 self.window_builder = self.window_builder.inner_size(width, height);
457 self
458 }
459
460 #[must_use]
462 pub fn min_inner_size(mut self, min_width: f64, min_height: f64) -> Self {
463 self.window_builder = self.window_builder.min_inner_size(min_width, min_height);
464 self
465 }
466
467 #[must_use]
469 pub fn max_inner_size(mut self, max_width: f64, max_height: f64) -> Self {
470 self.window_builder = self.window_builder.max_inner_size(max_width, max_height);
471 self
472 }
473
474 #[must_use]
476 pub fn inner_size_constraints(
477 mut self,
478 constraints: tauri_runtime::window::WindowSizeConstraints,
479 ) -> Self {
480 self.window_builder = self.window_builder.inner_size_constraints(constraints);
481 self
482 }
483
484 #[must_use]
493 pub fn prevent_overflow(mut self) -> Self {
494 self.window_builder = self.window_builder.prevent_overflow();
495 self
496 }
497
498 #[must_use]
507 pub fn prevent_overflow_with_margin(mut self, margin: impl Into<Size>) -> Self {
508 self.window_builder = self
509 .window_builder
510 .prevent_overflow_with_margin(margin.into());
511 self
512 }
513
514 #[must_use]
517 pub fn resizable(mut self, resizable: bool) -> Self {
518 self.window_builder = self.window_builder.resizable(resizable);
519 self
520 }
521
522 #[must_use]
530 pub fn maximizable(mut self, maximizable: bool) -> Self {
531 self.window_builder = self.window_builder.maximizable(maximizable);
532 self
533 }
534
535 #[must_use]
541 pub fn minimizable(mut self, minimizable: bool) -> Self {
542 self.window_builder = self.window_builder.minimizable(minimizable);
543 self
544 }
545
546 #[must_use]
554 pub fn closable(mut self, closable: bool) -> Self {
555 self.window_builder = self.window_builder.closable(closable);
556 self
557 }
558
559 #[must_use]
561 pub fn title<S: Into<String>>(mut self, title: S) -> Self {
562 self.window_builder = self.window_builder.title(title);
563 self
564 }
565
566 #[must_use]
568 pub fn fullscreen(mut self, fullscreen: bool) -> Self {
569 self.window_builder = self.window_builder.fullscreen(fullscreen);
570 self
571 }
572
573 #[must_use]
575 #[deprecated(
576 since = "1.2.0",
577 note = "The window is automatically focused by default. This function Will be removed in 3.0.0. Use `focused` instead."
578 )]
579 pub fn focus(mut self) -> Self {
580 self.window_builder = self.window_builder.focused(true);
581 self
582 }
583
584 #[must_use]
586 pub fn focused(mut self, focused: bool) -> Self {
587 self.window_builder = self.window_builder.focused(focused);
588 self
589 }
590
591 #[must_use]
593 pub fn focusable(mut self, focusable: bool) -> Self {
594 self.window_builder = self.window_builder.focusable(focusable);
595 self
596 }
597
598 #[must_use]
600 pub fn maximized(mut self, maximized: bool) -> Self {
601 self.window_builder = self.window_builder.maximized(maximized);
602 self
603 }
604
605 #[must_use]
607 pub fn visible(mut self, visible: bool) -> Self {
608 self.window_builder = self.window_builder.visible(visible);
609 self
610 }
611
612 #[must_use]
618 pub fn theme(mut self, theme: Option<Theme>) -> Self {
619 self.window_builder = self.window_builder.theme(theme);
620 self
621 }
622
623 #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
626 #[cfg_attr(
627 docsrs,
628 doc(cfg(any(not(target_os = "macos"), feature = "macos-private-api")))
629 )]
630 #[must_use]
631 pub fn transparent(mut self, transparent: bool) -> Self {
632 self.window_builder = self.window_builder.transparent(transparent);
633 self
634 }
635
636 #[must_use]
638 pub fn decorations(mut self, decorations: bool) -> Self {
639 self.window_builder = self.window_builder.decorations(decorations);
640 self
641 }
642
643 #[must_use]
645 pub fn always_on_bottom(mut self, always_on_bottom: bool) -> Self {
646 self.window_builder = self.window_builder.always_on_bottom(always_on_bottom);
647 self
648 }
649
650 #[must_use]
652 pub fn always_on_top(mut self, always_on_top: bool) -> Self {
653 self.window_builder = self.window_builder.always_on_top(always_on_top);
654 self
655 }
656
657 #[must_use]
663 pub fn visible_on_all_workspaces(mut self, visible_on_all_workspaces: bool) -> Self {
664 self.window_builder = self
665 .window_builder
666 .visible_on_all_workspaces(visible_on_all_workspaces);
667 self
668 }
669
670 #[must_use]
672 pub fn content_protected(mut self, protected: bool) -> Self {
673 self.window_builder = self.window_builder.content_protected(protected);
674 self
675 }
676
677 pub fn icon(mut self, icon: Image<'a>) -> crate::Result<Self> {
679 self.window_builder = self.window_builder.icon(icon.into())?;
680 Ok(self)
681 }
682
683 #[must_use]
689 pub fn skip_taskbar(mut self, skip: bool) -> Self {
690 self.window_builder = self.window_builder.skip_taskbar(skip);
691 self
692 }
693
694 #[must_use]
696 pub fn window_classname<S: Into<String>>(mut self, classname: S) -> Self {
697 self.window_builder = self.window_builder.window_classname(classname);
698 self
699 }
700
701 #[must_use]
711 pub fn shadow(mut self, enable: bool) -> Self {
712 self.window_builder = self.window_builder.shadow(enable);
713 self
714 }
715
716 pub fn parent(mut self, parent: &Window<R>) -> crate::Result<Self> {
728 #[cfg(windows)]
729 {
730 self.window_builder = self.window_builder.owner(parent.hwnd()?);
731 }
732
733 #[cfg(any(
734 target_os = "linux",
735 target_os = "dragonfly",
736 target_os = "freebsd",
737 target_os = "netbsd",
738 target_os = "openbsd"
739 ))]
740 {
741 self.window_builder = self.window_builder.transient_for(&parent.gtk_window()?);
742 }
743
744 #[cfg(target_os = "macos")]
745 {
746 self.window_builder = self.window_builder.parent(parent.ns_window()?);
747 }
748
749 Ok(self)
750 }
751
752 #[cfg(windows)]
761 pub fn owner(mut self, owner: &Window<R>) -> crate::Result<Self> {
762 self.window_builder = self.window_builder.owner(owner.hwnd()?);
763 Ok(self)
764 }
765
766 #[cfg(windows)]
777 #[must_use]
778 pub fn owner_raw(mut self, owner: HWND) -> Self {
779 self.window_builder = self.window_builder.owner(owner);
780 self
781 }
782
783 #[cfg(windows)]
791 #[must_use]
792 pub fn parent_raw(mut self, parent: HWND) -> Self {
793 self.window_builder = self.window_builder.parent(parent);
794 self
795 }
796
797 #[cfg(target_os = "macos")]
803 #[must_use]
804 pub fn parent_raw(mut self, parent: *mut std::ffi::c_void) -> Self {
805 self.window_builder = self.window_builder.parent(parent);
806 self
807 }
808
809 #[cfg(any(
815 target_os = "linux",
816 target_os = "dragonfly",
817 target_os = "freebsd",
818 target_os = "netbsd",
819 target_os = "openbsd"
820 ))]
821 pub fn transient_for(mut self, parent: &Window<R>) -> crate::Result<Self> {
822 self.window_builder = self.window_builder.transient_for(&parent.gtk_window()?);
823 Ok(self)
824 }
825
826 #[cfg(any(
832 target_os = "linux",
833 target_os = "dragonfly",
834 target_os = "freebsd",
835 target_os = "netbsd",
836 target_os = "openbsd"
837 ))]
838 #[must_use]
839 pub fn transient_for_raw(mut self, parent: &impl gtk::glib::IsA<gtk::Window>) -> Self {
840 self.window_builder = self.window_builder.transient_for(parent);
841 self
842 }
843
844 #[cfg(windows)]
846 #[must_use]
847 pub fn drag_and_drop(mut self, enabled: bool) -> Self {
848 self.window_builder = self.window_builder.drag_and_drop(enabled);
849 self
850 }
851
852 #[cfg(target_os = "macos")]
854 #[must_use]
855 pub fn title_bar_style(mut self, style: crate::TitleBarStyle) -> Self {
856 self.window_builder = self.window_builder.title_bar_style(style);
857 self
858 }
859
860 #[cfg(target_os = "macos")]
862 #[must_use]
863 pub fn hidden_title(mut self, hidden: bool) -> Self {
864 self.window_builder = self.window_builder.hidden_title(hidden);
865 self
866 }
867
868 #[cfg(target_os = "macos")]
875 #[must_use]
876 pub fn tabbing_identifier(mut self, identifier: &str) -> Self {
877 self.window_builder = self.window_builder.tabbing_identifier(identifier);
878 self
879 }
880
881 pub fn effects(mut self, effects: WindowEffectsConfig) -> Self {
890 self.window_effects.replace(effects);
891 self
892 }
893}
894
895impl<R: Runtime, M: Manager<R>> WindowBuilder<'_, R, M> {
896 #[must_use]
902 pub fn background_color(mut self, color: Color) -> Self {
903 self.window_builder = self.window_builder.background_color(color);
904 self
905 }
906}
907#[cfg(desktop)]
910pub(crate) struct WindowMenu<R: Runtime> {
911 pub(crate) is_app_wide: bool,
912 pub(crate) menu: Menu<R>,
913}
914
915#[default_runtime(crate::Wry, wry)]
921pub struct Window<R: Runtime> {
922 pub(crate) window: DetachedWindow<EventLoopMessage, R>,
924 pub(crate) manager: Arc<AppManager<R>>,
926 pub(crate) app_handle: AppHandle<R>,
927 #[cfg(desktop)]
929 pub(crate) menu: Arc<Mutex<Option<WindowMenu<R>>>>,
930 pub(crate) resources_table: Arc<Mutex<ResourceTable>>,
931}
932
933impl<R: Runtime> std::fmt::Debug for Window<R> {
934 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
935 f.debug_struct("Window")
936 .field("window", &self.window)
937 .field("manager", &self.manager)
938 .field("app_handle", &self.app_handle)
939 .finish()
940 }
941}
942
943impl<R: Runtime> raw_window_handle::HasWindowHandle for Window<R> {
944 fn window_handle(
945 &self,
946 ) -> std::result::Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
947 self.window.dispatcher.window_handle()
948 }
949}
950
951impl<R: Runtime> raw_window_handle::HasDisplayHandle for Window<R> {
952 fn display_handle(
953 &self,
954 ) -> std::result::Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
955 self.app_handle.display_handle()
956 }
957}
958
959impl<R: Runtime> Clone for Window<R> {
960 fn clone(&self) -> Self {
961 Self {
962 window: self.window.clone(),
963 manager: self.manager.clone(),
964 app_handle: self.app_handle.clone(),
965 #[cfg(desktop)]
966 menu: self.menu.clone(),
967 resources_table: self.resources_table.clone(),
968 }
969 }
970}
971
972impl<R: Runtime> Hash for Window<R> {
973 fn hash<H: Hasher>(&self, state: &mut H) {
975 self.window.label.hash(state)
976 }
977}
978
979impl<R: Runtime> Eq for Window<R> {}
980impl<R: Runtime> PartialEq for Window<R> {
981 fn eq(&self, other: &Self) -> bool {
983 self.window.label.eq(&other.window.label)
984 }
985}
986
987impl<R: Runtime> Manager<R> for Window<R> {
988 fn resources_table(&self) -> MutexGuard<'_, ResourceTable> {
989 self
990 .resources_table
991 .lock()
992 .expect("poisoned window resources table")
993 }
994}
995
996impl<R: Runtime> ManagerBase<R> for Window<R> {
997 fn manager(&self) -> &AppManager<R> {
998 &self.manager
999 }
1000
1001 fn manager_owned(&self) -> Arc<AppManager<R>> {
1002 self.manager.clone()
1003 }
1004
1005 fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
1006 RuntimeOrDispatch::Dispatch(self.window.dispatcher.clone())
1007 }
1008
1009 fn managed_app_handle(&self) -> &AppHandle<R> {
1010 &self.app_handle
1011 }
1012}
1013
1014impl<'de, R: Runtime> CommandArg<'de, R> for Window<R> {
1015 fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError> {
1017 Ok(command.message.webview().window())
1018 }
1019}
1020
1021impl<R: Runtime> Window<R> {
1023 pub(crate) fn new(
1025 manager: Arc<AppManager<R>>,
1026 window: DetachedWindow<EventLoopMessage, R>,
1027 app_handle: AppHandle<R>,
1028 #[cfg(desktop)] menu: Option<WindowMenu<R>>,
1029 ) -> Self {
1030 Self {
1031 window,
1032 manager,
1033 app_handle,
1034 #[cfg(desktop)]
1035 menu: Arc::new(std::sync::Mutex::new(menu)),
1036 resources_table: Default::default(),
1037 }
1038 }
1039
1040 #[cfg(feature = "unstable")]
1044 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
1045 pub fn builder<M: Manager<R>, L: Into<String>>(manager: &M, label: L) -> WindowBuilder<'_, R, M> {
1046 WindowBuilder::new(manager, label.into())
1047 }
1048
1049 #[cfg(any(test, all(desktop, feature = "unstable")))]
1051 #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "unstable"))))]
1052 pub fn add_child<P: Into<Position>, S: Into<Size>>(
1053 &self,
1054 webview_builder: WebviewBuilder<R>,
1055 position: P,
1056 size: S,
1057 ) -> crate::Result<Webview<R>> {
1058 use std::sync::mpsc::channel;
1059
1060 let (tx, rx) = channel();
1061 let position = position.into();
1062 let size = size.into();
1063 let window_ = self.clone();
1064 self.run_on_main_thread(move || {
1065 let res = webview_builder.build(window_, position, size);
1066 tx.send(res).unwrap();
1067 })?;
1068 rx.recv().unwrap()
1069 }
1070
1071 pub fn webviews(&self) -> Vec<Webview<R>> {
1073 self
1074 .manager
1075 .webview
1076 .webviews_lock()
1077 .values()
1078 .filter(|w| w.window_label() == self.label())
1079 .cloned()
1080 .collect()
1081 }
1082
1083 pub(crate) fn is_webview_window(&self) -> bool {
1084 self.webviews().iter().all(|w| w.label() == self.label())
1085 }
1086
1087 pub fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> crate::Result<()> {
1089 self
1090 .window
1091 .dispatcher
1092 .run_on_main_thread(f)
1093 .map_err(Into::into)
1094 }
1095
1096 pub fn label(&self) -> &str {
1098 &self.window.label
1099 }
1100
1101 pub fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) {
1103 self
1104 .window
1105 .dispatcher
1106 .on_window_event(move |event| f(&event.clone().into()));
1107 }
1108}
1109
1110#[cfg(desktop)]
1112impl<R: Runtime> Window<R> {
1113 #[cfg_attr(
1123 feature = "unstable",
1124 doc = r####"
1125```
1126use tauri::menu::{Menu, Submenu, MenuItem};
1127tauri::Builder::default()
1128 .setup(|app| {
1129 let handle = app.handle();
1130 let save_menu_item = MenuItem::new(handle, "Save", true, None::<&str>)?;
1131 let menu = Menu::with_items(handle, &[
1132 &Submenu::with_items(handle, "File", true, &[
1133 &save_menu_item,
1134 ])?,
1135 ])?;
1136 let window = tauri::window::WindowBuilder::new(app, "editor")
1137 .menu(menu)
1138 .build()
1139 .unwrap();
1140
1141 window.on_menu_event(move |window, event| {
1142 if event.id == save_menu_item.id() {
1143 // save menu item
1144 }
1145 });
1146
1147 Ok(())
1148 });
1149```
1150 "####
1151 )]
1152 pub fn on_menu_event<F: Fn(&Window<R>, crate::menu::MenuEvent) + Send + Sync + 'static>(
1153 &self,
1154 f: F,
1155 ) {
1156 self
1157 .manager
1158 .menu
1159 .event_listeners
1160 .lock()
1161 .unwrap()
1162 .insert(self.label().to_string(), Box::new(f));
1163 }
1164
1165 pub(crate) fn menu_lock(&self) -> std::sync::MutexGuard<'_, Option<WindowMenu<R>>> {
1166 self.menu.lock().expect("poisoned window")
1167 }
1168
1169 #[cfg_attr(target_os = "macos", allow(dead_code))]
1170 pub(crate) fn has_app_wide_menu(&self) -> bool {
1171 self
1172 .menu_lock()
1173 .as_ref()
1174 .map(|m| m.is_app_wide)
1175 .unwrap_or(false)
1176 }
1177
1178 #[cfg_attr(target_os = "macos", allow(dead_code))]
1179 pub(crate) fn is_menu_in_use<I: PartialEq<MenuId>>(&self, id: &I) -> bool {
1180 self
1181 .menu_lock()
1182 .as_ref()
1183 .map(|m| id.eq(m.menu.id()))
1184 .unwrap_or(false)
1185 }
1186
1187 pub fn menu(&self) -> Option<Menu<R>> {
1189 self.menu_lock().as_ref().map(|m| m.menu.clone())
1190 }
1191
1192 #[cfg_attr(target_os = "macos", allow(unused_variables))]
1199 pub fn set_menu(&self, menu: Menu<R>) -> crate::Result<Option<Menu<R>>> {
1200 let prev_menu = self.remove_menu()?;
1201
1202 self.manager.menu.insert_menu_into_stash(&menu);
1203
1204 let window = self.clone();
1205 let menu_ = menu.clone();
1206 self.run_on_main_thread(move || {
1207 #[cfg(windows)]
1208 if let Ok(hwnd) = window.hwnd() {
1209 let theme = window
1210 .theme()
1211 .map(crate::menu::map_to_menu_theme)
1212 .unwrap_or(muda::MenuTheme::Auto);
1213
1214 let _ = unsafe { menu_.inner().init_for_hwnd_with_theme(hwnd.0 as _, theme) };
1215 }
1216 #[cfg(any(
1217 target_os = "linux",
1218 target_os = "dragonfly",
1219 target_os = "freebsd",
1220 target_os = "netbsd",
1221 target_os = "openbsd"
1222 ))]
1223 if let (Ok(gtk_window), Ok(gtk_box)) = (window.gtk_window(), window.default_vbox()) {
1224 let _ = menu_
1225 .inner()
1226 .init_for_gtk_window(>k_window, Some(>k_box));
1227 }
1228 })?;
1229
1230 self.menu_lock().replace(WindowMenu {
1231 is_app_wide: false,
1232 menu,
1233 });
1234
1235 Ok(prev_menu)
1236 }
1237
1238 pub fn remove_menu(&self) -> crate::Result<Option<Menu<R>>> {
1245 let prev_menu = self.menu_lock().take().map(|m| m.menu);
1246
1247 #[cfg_attr(target_os = "macos", allow(unused_variables))]
1249 if let Some(menu) = &prev_menu {
1250 let window = self.clone();
1251 let menu = menu.clone();
1252 self.run_on_main_thread(move || {
1253 #[cfg(windows)]
1254 if let Ok(hwnd) = window.hwnd() {
1255 let _ = unsafe { menu.inner().remove_for_hwnd(hwnd.0 as _) };
1256 }
1257 #[cfg(any(
1258 target_os = "linux",
1259 target_os = "dragonfly",
1260 target_os = "freebsd",
1261 target_os = "netbsd",
1262 target_os = "openbsd"
1263 ))]
1264 if let Ok(gtk_window) = window.gtk_window() {
1265 let _ = menu.inner().remove_for_gtk_window(>k_window);
1266 }
1267 })?;
1268 }
1269
1270 self
1271 .manager
1272 .remove_menu_from_stash_by_id(prev_menu.as_ref().map(|m| m.id()));
1273
1274 Ok(prev_menu)
1275 }
1276
1277 pub fn hide_menu(&self) -> crate::Result<()> {
1279 #[cfg_attr(target_os = "macos", allow(unused_variables))]
1281 if let Some(window_menu) = &*self.menu_lock() {
1282 let window = self.clone();
1283 let menu_ = window_menu.menu.clone();
1284 self.run_on_main_thread(move || {
1285 #[cfg(windows)]
1286 if let Ok(hwnd) = window.hwnd() {
1287 let _ = unsafe { menu_.inner().hide_for_hwnd(hwnd.0 as _) };
1288 }
1289 #[cfg(any(
1290 target_os = "linux",
1291 target_os = "dragonfly",
1292 target_os = "freebsd",
1293 target_os = "netbsd",
1294 target_os = "openbsd"
1295 ))]
1296 if let Ok(gtk_window) = window.gtk_window() {
1297 let _ = menu_.inner().hide_for_gtk_window(>k_window);
1298 }
1299 })?;
1300 }
1301
1302 Ok(())
1303 }
1304
1305 pub fn show_menu(&self) -> crate::Result<()> {
1307 #[cfg_attr(target_os = "macos", allow(unused_variables))]
1309 if let Some(window_menu) = &*self.menu_lock() {
1310 let window = self.clone();
1311 let menu_ = window_menu.menu.clone();
1312 self.run_on_main_thread(move || {
1313 #[cfg(windows)]
1314 if let Ok(hwnd) = window.hwnd() {
1315 let _ = unsafe { menu_.inner().show_for_hwnd(hwnd.0 as _) };
1316 }
1317 #[cfg(any(
1318 target_os = "linux",
1319 target_os = "dragonfly",
1320 target_os = "freebsd",
1321 target_os = "netbsd",
1322 target_os = "openbsd"
1323 ))]
1324 if let Ok(gtk_window) = window.gtk_window() {
1325 let _ = menu_.inner().show_for_gtk_window(>k_window);
1326 }
1327 })?;
1328 }
1329
1330 Ok(())
1331 }
1332
1333 pub fn is_menu_visible(&self) -> crate::Result<bool> {
1335 #[cfg_attr(target_os = "macos", allow(unused_variables))]
1337 if let Some(window_menu) = &*self.menu_lock() {
1338 let (tx, rx) = std::sync::mpsc::channel();
1339 let window = self.clone();
1340 let menu_ = window_menu.menu.clone();
1341 self.run_on_main_thread(move || {
1342 #[cfg(windows)]
1343 if let Ok(hwnd) = window.hwnd() {
1344 let _ = tx.send(unsafe { menu_.inner().is_visible_on_hwnd(hwnd.0 as _) });
1345 }
1346 #[cfg(any(
1347 target_os = "linux",
1348 target_os = "dragonfly",
1349 target_os = "freebsd",
1350 target_os = "netbsd",
1351 target_os = "openbsd"
1352 ))]
1353 if let Ok(gtk_window) = window.gtk_window() {
1354 let _ = tx.send(menu_.inner().is_visible_on_gtk_window(>k_window));
1355 }
1356 })?;
1357
1358 return Ok(rx.recv().unwrap_or(false));
1359 }
1360
1361 Ok(false)
1362 }
1363
1364 pub fn popup_menu<M: ContextMenu>(&self, menu: &M) -> crate::Result<()> {
1366 menu.popup(self.clone())
1367 }
1368
1369 pub fn popup_menu_at<M: ContextMenu, P: Into<Position>>(
1373 &self,
1374 menu: &M,
1375 position: P,
1376 ) -> crate::Result<()> {
1377 menu.popup_at(self.clone(), position)
1378 }
1379}
1380
1381impl<R: Runtime> Window<R> {
1383 pub fn scale_factor(&self) -> crate::Result<f64> {
1385 self.window.dispatcher.scale_factor().map_err(Into::into)
1386 }
1387
1388 pub fn inner_position(&self) -> crate::Result<PhysicalPosition<i32>> {
1390 self.window.dispatcher.inner_position().map_err(Into::into)
1391 }
1392
1393 pub fn outer_position(&self) -> crate::Result<PhysicalPosition<i32>> {
1395 self.window.dispatcher.outer_position().map_err(Into::into)
1396 }
1397
1398 pub fn inner_size(&self) -> crate::Result<PhysicalSize<u32>> {
1402 self.window.dispatcher.inner_size().map_err(Into::into)
1403 }
1404
1405 pub fn outer_size(&self) -> crate::Result<PhysicalSize<u32>> {
1409 self.window.dispatcher.outer_size().map_err(Into::into)
1410 }
1411
1412 pub fn is_fullscreen(&self) -> crate::Result<bool> {
1414 self.window.dispatcher.is_fullscreen().map_err(Into::into)
1415 }
1416
1417 pub fn is_minimized(&self) -> crate::Result<bool> {
1419 self.window.dispatcher.is_minimized().map_err(Into::into)
1420 }
1421
1422 pub fn is_maximized(&self) -> crate::Result<bool> {
1424 self.window.dispatcher.is_maximized().map_err(Into::into)
1425 }
1426
1427 pub fn is_focused(&self) -> crate::Result<bool> {
1429 self.window.dispatcher.is_focused().map_err(Into::into)
1430 }
1431
1432 pub fn is_decorated(&self) -> crate::Result<bool> {
1434 self.window.dispatcher.is_decorated().map_err(Into::into)
1435 }
1436
1437 pub fn is_resizable(&self) -> crate::Result<bool> {
1439 self.window.dispatcher.is_resizable().map_err(Into::into)
1440 }
1441
1442 pub fn is_enabled(&self) -> crate::Result<bool> {
1444 self.window.dispatcher.is_enabled().map_err(Into::into)
1445 }
1446
1447 pub fn is_always_on_top(&self) -> crate::Result<bool> {
1453 self
1454 .window
1455 .dispatcher
1456 .is_always_on_top()
1457 .map_err(Into::into)
1458 }
1459
1460 pub fn is_maximizable(&self) -> crate::Result<bool> {
1466 self.window.dispatcher.is_maximizable().map_err(Into::into)
1467 }
1468
1469 pub fn is_minimizable(&self) -> crate::Result<bool> {
1475 self.window.dispatcher.is_minimizable().map_err(Into::into)
1476 }
1477
1478 pub fn is_closable(&self) -> crate::Result<bool> {
1484 self.window.dispatcher.is_closable().map_err(Into::into)
1485 }
1486
1487 pub fn is_visible(&self) -> crate::Result<bool> {
1489 self.window.dispatcher.is_visible().map_err(Into::into)
1490 }
1491
1492 pub fn title(&self) -> crate::Result<String> {
1494 self.window.dispatcher.title().map_err(Into::into)
1495 }
1496
1497 pub fn current_monitor(&self) -> crate::Result<Option<Monitor>> {
1501 self
1502 .window
1503 .dispatcher
1504 .current_monitor()
1505 .map(|m| m.map(Into::into))
1506 .map_err(Into::into)
1507 }
1508
1509 pub fn monitor_from_point(&self, x: f64, y: f64) -> crate::Result<Option<Monitor>> {
1511 self
1512 .window
1513 .dispatcher
1514 .monitor_from_point(x, y)
1515 .map(|m| m.map(Into::into))
1516 .map_err(Into::into)
1517 }
1518
1519 pub fn primary_monitor(&self) -> crate::Result<Option<Monitor>> {
1523 self
1524 .window
1525 .dispatcher
1526 .primary_monitor()
1527 .map(|m| m.map(Into::into))
1528 .map_err(Into::into)
1529 }
1530
1531 pub fn available_monitors(&self) -> crate::Result<Vec<Monitor>> {
1533 self
1534 .window
1535 .dispatcher
1536 .available_monitors()
1537 .map(|m| m.into_iter().map(Into::into).collect())
1538 .map_err(Into::into)
1539 }
1540
1541 #[cfg(target_os = "macos")]
1543 pub fn ns_window(&self) -> crate::Result<*mut std::ffi::c_void> {
1544 self
1545 .window
1546 .dispatcher
1547 .window_handle()
1548 .map_err(Into::into)
1549 .and_then(|handle| {
1550 if let raw_window_handle::RawWindowHandle::AppKit(h) = handle.as_raw() {
1551 let view: &objc2_app_kit::NSView = unsafe { h.ns_view.cast().as_ref() };
1552 let ns_window = view.window().expect("view to be installed in window");
1553 Ok(objc2::rc::Retained::autorelease_ptr(ns_window).cast())
1554 } else {
1555 Err(crate::Error::InvalidWindowHandle)
1556 }
1557 })
1558 }
1559
1560 #[cfg(target_os = "macos")]
1562 pub fn ns_view(&self) -> crate::Result<*mut std::ffi::c_void> {
1563 self
1564 .window
1565 .dispatcher
1566 .window_handle()
1567 .map_err(Into::into)
1568 .and_then(|handle| {
1569 if let raw_window_handle::RawWindowHandle::AppKit(h) = handle.as_raw() {
1570 Ok(h.ns_view.as_ptr())
1571 } else {
1572 Err(crate::Error::InvalidWindowHandle)
1573 }
1574 })
1575 }
1576
1577 #[cfg(windows)]
1579 pub fn hwnd(&self) -> crate::Result<HWND> {
1580 self
1581 .window
1582 .dispatcher
1583 .window_handle()
1584 .map_err(Into::into)
1585 .and_then(|handle| {
1586 if let raw_window_handle::RawWindowHandle::Win32(h) = handle.as_raw() {
1587 Ok(HWND(h.hwnd.get() as _))
1588 } else {
1589 Err(crate::Error::InvalidWindowHandle)
1590 }
1591 })
1592 }
1593
1594 #[cfg(any(
1598 target_os = "linux",
1599 target_os = "dragonfly",
1600 target_os = "freebsd",
1601 target_os = "netbsd",
1602 target_os = "openbsd"
1603 ))]
1604 pub fn gtk_window(&self) -> crate::Result<gtk::ApplicationWindow> {
1605 self.window.dispatcher.gtk_window().map_err(Into::into)
1606 }
1607
1608 #[cfg(any(
1612 target_os = "linux",
1613 target_os = "dragonfly",
1614 target_os = "freebsd",
1615 target_os = "netbsd",
1616 target_os = "openbsd"
1617 ))]
1618 pub fn default_vbox(&self) -> crate::Result<gtk::Box> {
1619 self.window.dispatcher.default_vbox().map_err(Into::into)
1620 }
1621
1622 pub fn theme(&self) -> crate::Result<Theme> {
1628 self.window.dispatcher.theme().map_err(Into::into)
1629 }
1630}
1631
1632#[cfg(desktop)]
1634impl<R: Runtime> Window<R> {
1635 pub fn cursor_position(&self) -> crate::Result<PhysicalPosition<f64>> {
1644 self.app_handle.cursor_position()
1645 }
1646}
1647
1648#[cfg(desktop)]
1650impl<R: Runtime> Window<R> {
1651 pub fn center(&self) -> crate::Result<()> {
1653 self.window.dispatcher.center().map_err(Into::into)
1654 }
1655
1656 pub fn request_user_attention(
1668 &self,
1669 request_type: Option<UserAttentionType>,
1670 ) -> crate::Result<()> {
1671 self
1672 .window
1673 .dispatcher
1674 .request_user_attention(request_type)
1675 .map_err(Into::into)
1676 }
1677
1678 pub fn set_resizable(&self, resizable: bool) -> crate::Result<()> {
1681 self
1682 .window
1683 .dispatcher
1684 .set_resizable(resizable)
1685 .map_err(Into::into)
1686 }
1687
1688 pub fn set_maximizable(&self, maximizable: bool) -> crate::Result<()> {
1696 self
1697 .window
1698 .dispatcher
1699 .set_maximizable(maximizable)
1700 .map_err(Into::into)
1701 }
1702
1703 pub fn set_minimizable(&self, minimizable: bool) -> crate::Result<()> {
1709 self
1710 .window
1711 .dispatcher
1712 .set_minimizable(minimizable)
1713 .map_err(Into::into)
1714 }
1715
1716 pub fn set_closable(&self, closable: bool) -> crate::Result<()> {
1724 self
1725 .window
1726 .dispatcher
1727 .set_closable(closable)
1728 .map_err(Into::into)
1729 }
1730
1731 pub fn set_title(&self, title: &str) -> crate::Result<()> {
1733 self
1734 .window
1735 .dispatcher
1736 .set_title(title.to_string())
1737 .map_err(Into::into)
1738 }
1739
1740 pub fn set_enabled(&self, enabled: bool) -> crate::Result<()> {
1742 self
1743 .window
1744 .dispatcher
1745 .set_enabled(enabled)
1746 .map_err(Into::into)
1747 }
1748
1749 pub fn maximize(&self) -> crate::Result<()> {
1751 self.window.dispatcher.maximize().map_err(Into::into)
1752 }
1753
1754 pub fn unmaximize(&self) -> crate::Result<()> {
1756 self.window.dispatcher.unmaximize().map_err(Into::into)
1757 }
1758
1759 pub fn minimize(&self) -> crate::Result<()> {
1761 self.window.dispatcher.minimize().map_err(Into::into)
1762 }
1763
1764 pub fn unminimize(&self) -> crate::Result<()> {
1766 self.window.dispatcher.unminimize().map_err(Into::into)
1767 }
1768
1769 pub fn show(&self) -> crate::Result<()> {
1771 self.window.dispatcher.show().map_err(Into::into)
1772 }
1773
1774 pub fn hide(&self) -> crate::Result<()> {
1776 self.window.dispatcher.hide().map_err(Into::into)
1777 }
1778
1779 pub fn close(&self) -> crate::Result<()> {
1781 self.window.dispatcher.close().map_err(Into::into)
1782 }
1783
1784 pub fn destroy(&self) -> crate::Result<()> {
1786 self.window.dispatcher.destroy().map_err(Into::into)
1787 }
1788
1789 pub fn set_decorations(&self, decorations: bool) -> crate::Result<()> {
1793 self
1794 .window
1795 .dispatcher
1796 .set_decorations(decorations)
1797 .map_err(Into::into)
1798 }
1799
1800 pub fn set_shadow(&self, enable: bool) -> crate::Result<()> {
1810 self
1811 .window
1812 .dispatcher
1813 .set_shadow(enable)
1814 .map_err(Into::into)
1815 }
1816
1817 #[cfg_attr(
1824 feature = "unstable",
1825 doc = r####"
1826```rust,no_run
1827use tauri::{Manager, window::{Color, Effect, EffectState, EffectsBuilder}};
1828tauri::Builder::default()
1829 .setup(|app| {
1830 let window = app.get_window("main").unwrap();
1831 window.set_effects(
1832 EffectsBuilder::new()
1833 .effect(Effect::Popover)
1834 .state(EffectState::Active)
1835 .radius(5.)
1836 .color(Color(0, 0, 0, 255))
1837 .build(),
1838 )?;
1839 Ok(())
1840 });
1841```
1842 "####
1843 )]
1844 pub fn set_effects<E: Into<Option<WindowEffectsConfig>>>(&self, effects: E) -> crate::Result<()> {
1850 let effects = effects.into();
1851 let window = self.clone();
1852 self.run_on_main_thread(move || {
1853 let _ = crate::vibrancy::set_window_effects(&window, effects);
1854 })
1855 }
1856
1857 pub fn set_always_on_bottom(&self, always_on_bottom: bool) -> crate::Result<()> {
1859 self
1860 .window
1861 .dispatcher
1862 .set_always_on_bottom(always_on_bottom)
1863 .map_err(Into::into)
1864 }
1865
1866 pub fn set_always_on_top(&self, always_on_top: bool) -> crate::Result<()> {
1868 self
1869 .window
1870 .dispatcher
1871 .set_always_on_top(always_on_top)
1872 .map_err(Into::into)
1873 }
1874
1875 pub fn set_visible_on_all_workspaces(
1881 &self,
1882 visible_on_all_workspaces: bool,
1883 ) -> crate::Result<()> {
1884 self
1885 .window
1886 .dispatcher
1887 .set_visible_on_all_workspaces(visible_on_all_workspaces)
1888 .map_err(Into::into)
1889 }
1890
1891 pub fn set_background_color(&self, color: Option<Color>) -> crate::Result<()> {
1898 self
1899 .window
1900 .dispatcher
1901 .set_background_color(color)
1902 .map_err(Into::into)
1903 }
1904
1905 pub fn set_content_protected(&self, protected: bool) -> crate::Result<()> {
1907 self
1908 .window
1909 .dispatcher
1910 .set_content_protected(protected)
1911 .map_err(Into::into)
1912 }
1913
1914 pub fn set_size<S: Into<Size>>(&self, size: S) -> crate::Result<()> {
1916 self
1917 .window
1918 .dispatcher
1919 .set_size(size.into())
1920 .map_err(Into::into)
1921 }
1922
1923 pub fn set_min_size<S: Into<Size>>(&self, size: Option<S>) -> crate::Result<()> {
1925 self
1926 .window
1927 .dispatcher
1928 .set_min_size(size.map(|s| s.into()))
1929 .map_err(Into::into)
1930 }
1931
1932 pub fn set_max_size<S: Into<Size>>(&self, size: Option<S>) -> crate::Result<()> {
1934 self
1935 .window
1936 .dispatcher
1937 .set_max_size(size.map(|s| s.into()))
1938 .map_err(Into::into)
1939 }
1940
1941 pub fn set_size_constraints(
1943 &self,
1944 constriants: tauri_runtime::window::WindowSizeConstraints,
1945 ) -> crate::Result<()> {
1946 self
1947 .window
1948 .dispatcher
1949 .set_size_constraints(constriants)
1950 .map_err(Into::into)
1951 }
1952
1953 pub fn set_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
1955 self
1956 .window
1957 .dispatcher
1958 .set_position(position.into())
1959 .map_err(Into::into)
1960 }
1961
1962 pub fn set_fullscreen(&self, fullscreen: bool) -> crate::Result<()> {
1964 self
1965 .window
1966 .dispatcher
1967 .set_fullscreen(fullscreen)
1968 .map_err(Into::into)
1969 }
1970
1971 #[cfg(target_os = "macos")]
1975 pub fn set_simple_fullscreen(&self, enable: bool) -> crate::Result<()> {
1976 self
1977 .window
1978 .dispatcher
1979 .set_simple_fullscreen(enable)
1980 .map_err(Into::into)
1981 }
1982
1983 #[cfg(not(target_os = "macos"))]
1988 pub fn set_simple_fullscreen(&self, fullscreen: bool) -> crate::Result<()> {
1989 self.set_fullscreen(fullscreen)
1990 }
1991
1992 pub fn set_focus(&self) -> crate::Result<()> {
1994 self.window.dispatcher.set_focus().map_err(Into::into)
1995 }
1996
1997 pub fn set_focusable(&self, focusable: bool) -> crate::Result<()> {
2004 self
2005 .window
2006 .dispatcher
2007 .set_focusable(focusable)
2008 .map_err(Into::into)
2009 }
2010
2011 pub fn set_icon(&self, icon: Image<'_>) -> crate::Result<()> {
2013 self
2014 .window
2015 .dispatcher
2016 .set_icon(icon.into())
2017 .map_err(Into::into)
2018 }
2019
2020 pub fn set_skip_taskbar(&self, skip: bool) -> crate::Result<()> {
2026 self
2027 .window
2028 .dispatcher
2029 .set_skip_taskbar(skip)
2030 .map_err(Into::into)
2031 }
2032
2033 pub fn set_cursor_grab(&self, grab: bool) -> crate::Result<()> {
2043 self
2044 .window
2045 .dispatcher
2046 .set_cursor_grab(grab)
2047 .map_err(Into::into)
2048 }
2049
2050 pub fn set_cursor_visible(&self, visible: bool) -> crate::Result<()> {
2060 self
2061 .window
2062 .dispatcher
2063 .set_cursor_visible(visible)
2064 .map_err(Into::into)
2065 }
2066
2067 pub fn set_cursor_icon(&self, icon: CursorIcon) -> crate::Result<()> {
2069 self
2070 .window
2071 .dispatcher
2072 .set_cursor_icon(icon)
2073 .map_err(Into::into)
2074 }
2075
2076 pub fn set_cursor_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
2078 self
2079 .window
2080 .dispatcher
2081 .set_cursor_position(position)
2082 .map_err(Into::into)
2083 }
2084
2085 pub fn set_ignore_cursor_events(&self, ignore: bool) -> crate::Result<()> {
2087 self
2088 .window
2089 .dispatcher
2090 .set_ignore_cursor_events(ignore)
2091 .map_err(Into::into)
2092 }
2093
2094 pub fn start_dragging(&self) -> crate::Result<()> {
2096 self.window.dispatcher.start_dragging().map_err(Into::into)
2097 }
2098
2099 pub fn start_resize_dragging(
2101 &self,
2102 direction: tauri_runtime::ResizeDirection,
2103 ) -> crate::Result<()> {
2104 self
2105 .window
2106 .dispatcher
2107 .start_resize_dragging(direction)
2108 .map_err(Into::into)
2109 }
2110
2111 #[cfg(target_os = "windows")]
2115 #[cfg_attr(docsrs, doc(cfg(target_os = "windows")))]
2116 pub fn set_overlay_icon(&self, icon: Option<Image<'_>>) -> crate::Result<()> {
2117 self
2118 .window
2119 .dispatcher
2120 .set_overlay_icon(icon.map(|x| x.into()))
2121 .map_err(Into::into)
2122 }
2123
2124 pub fn set_badge_count(&self, count: Option<i64>) -> crate::Result<()> {
2131 self
2132 .window
2133 .dispatcher
2134 .set_badge_count(count, Some(format!("{}.desktop", self.package_info().name)))
2135 .map_err(Into::into)
2136 }
2137
2138 #[cfg(target_os = "macos")]
2140 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
2141 pub fn set_badge_label(&self, label: Option<String>) -> crate::Result<()> {
2142 self
2143 .window
2144 .dispatcher
2145 .set_badge_label(label)
2146 .map_err(Into::into)
2147 }
2148
2149 pub fn set_progress_bar(&self, progress_state: ProgressBarState) -> crate::Result<()> {
2157 self
2158 .window
2159 .dispatcher
2160 .set_progress_bar(crate::runtime::ProgressBarState {
2161 status: progress_state.status,
2162 progress: progress_state.progress,
2163 desktop_filename: Some(format!("{}.desktop", self.package_info().name)),
2164 })
2165 .map_err(Into::into)
2166 }
2167
2168 pub fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> crate::Result<()> {
2170 self
2171 .window
2172 .dispatcher
2173 .set_title_bar_style(style)
2174 .map_err(Into::into)
2175 }
2176
2177 pub fn set_theme(&self, theme: Option<Theme>) -> crate::Result<()> {
2184 self
2185 .window
2186 .dispatcher
2187 .set_theme(theme)
2188 .map_err(Into::<crate::Error>::into)?;
2189 #[cfg(windows)]
2190 if let (Some(menu), Ok(hwnd)) = (self.menu(), self.hwnd()) {
2191 let raw_hwnd = hwnd.0 as isize;
2192 self.run_on_main_thread(move || {
2193 let _ = unsafe {
2194 menu.inner().set_theme_for_hwnd(
2195 raw_hwnd,
2196 theme
2197 .map(crate::menu::map_to_menu_theme)
2198 .unwrap_or(muda::MenuTheme::Auto),
2199 )
2200 };
2201 })?;
2202 };
2203 Ok(())
2204 }
2205}
2206
2207#[cfg(desktop)]
2209#[cfg_attr(
2210 docsrs,
2211 doc(cfg(any(target_os = "macos", target_os = "linux", windows)))
2212)]
2213#[derive(serde::Deserialize, Debug)]
2214pub struct ProgressBarState {
2215 pub status: Option<ProgressBarStatus>,
2217 pub progress: Option<u64>,
2219}
2220
2221impl<R: Runtime> Listener<R> for Window<R> {
2222 #[cfg_attr(
2226 feature = "unstable",
2227 doc = r####"
2228```
2229use tauri::{Manager, Listener};
2230
2231tauri::Builder::default()
2232 .setup(|app| {
2233 let window = app.get_window("main").unwrap();
2234 window.listen("component-loaded", move |event| {
2235 println!("window just loaded a component");
2236 });
2237
2238 Ok(())
2239 });
2240```
2241 "####
2242 )]
2243 fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
2244 where
2245 F: Fn(Event) + Send + 'static,
2246 {
2247 let event = EventName::new(event.into()).unwrap();
2248 self.manager.listen(
2249 event,
2250 EventTarget::Window {
2251 label: self.label().to_string(),
2252 },
2253 handler,
2254 )
2255 }
2256
2257 fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
2261 where
2262 F: FnOnce(Event) + Send + 'static,
2263 {
2264 let event = EventName::new(event.into()).unwrap();
2265 self.manager.once(
2266 event,
2267 EventTarget::Window {
2268 label: self.label().to_string(),
2269 },
2270 handler,
2271 )
2272 }
2273
2274 #[cfg_attr(
2278 feature = "unstable",
2279 doc = r####"
2280```
2281use tauri::{Manager, Listener};
2282
2283tauri::Builder::default()
2284 .setup(|app| {
2285 let window = app.get_window("main").unwrap();
2286 let window_ = window.clone();
2287 let handler = window.listen("component-loaded", move |event| {
2288 println!("window just loaded a component");
2289
2290 // we no longer need to listen to the event
2291 // we also could have used `window.once` instead
2292 window_.unlisten(event.id());
2293 });
2294
2295 // stop listening to the event when you do not need it anymore
2296 window.unlisten(handler);
2297
2298 Ok(())
2299 });
2300```
2301 "####
2302 )]
2303 fn unlisten(&self, id: EventId) {
2304 self.manager.unlisten(id)
2305 }
2306}
2307
2308impl<R: Runtime> Emitter<R> for Window<R> {}
2309
2310#[derive(Default)]
2312pub struct EffectsBuilder(WindowEffectsConfig);
2313impl EffectsBuilder {
2314 pub fn new() -> Self {
2316 Self(WindowEffectsConfig::default())
2317 }
2318
2319 pub fn effect(mut self, effect: Effect) -> Self {
2321 self.0.effects.push(effect);
2322 self
2323 }
2324
2325 pub fn effects<I: IntoIterator<Item = Effect>>(mut self, effects: I) -> Self {
2327 self.0.effects.extend(effects);
2328 self
2329 }
2330
2331 pub fn clear_effects(mut self) -> Self {
2333 self.0.effects.clear();
2334 self
2335 }
2336
2337 pub fn state(mut self, state: EffectState) -> Self {
2339 self.0.state = Some(state);
2340 self
2341 }
2342 pub fn radius(mut self, radius: f64) -> Self {
2344 self.0.radius = Some(radius);
2345 self
2346 }
2347 pub fn color(mut self, color: Color) -> Self {
2349 self.0.color = Some(color);
2350 self
2351 }
2352
2353 pub fn build(self) -> WindowEffectsConfig {
2355 self.0
2356 }
2357}
2358
2359impl From<WindowEffectsConfig> for EffectsBuilder {
2360 fn from(value: WindowEffectsConfig) -> Self {
2361 Self(value)
2362 }
2363}
2364
2365#[cfg(test)]
2366mod tests {
2367 #[test]
2368 fn window_is_send_sync() {
2369 crate::test_utils::assert_send::<super::Window>();
2370 crate::test_utils::assert_sync::<super::Window>();
2371 }
2372}