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 dpi::{Position, Size},
25 monitor::Monitor as RuntimeMonitor,
26 window::{DetachedWindow, PendingWindow, WindowBuilder as _},
27 RuntimeHandle, WindowDispatch,
28 },
29 sealed::{ManagerBase, RuntimeOrDispatch},
30 utils::config::{WindowConfig, WindowEffectsConfig},
31 webview::WebviewBuilder,
32 Emitter, EventLoopMessage, EventName, Listener, Manager, ResourceTable, Runtime, Theme, Webview,
33 WindowEvent,
34};
35#[cfg(desktop)]
36use crate::{
37 image::Image,
38 menu::{ContextMenu, Menu, MenuId},
39 runtime::UserAttentionType,
40 CursorIcon,
41};
42
43use serde::Serialize;
44#[cfg(windows)]
45use windows::Win32::Foundation::HWND;
46
47use tauri_macros::default_runtime;
48
49use std::{
50 fmt,
51 hash::{Hash, Hasher},
52 sync::{Arc, Mutex, MutexGuard},
53};
54
55#[derive(Debug, Clone, Serialize)]
57#[serde(rename_all = "camelCase")]
58pub struct Monitor {
59 pub(crate) name: Option<String>,
60 pub(crate) size: PhysicalSize<u32>,
61 pub(crate) position: PhysicalPosition<i32>,
62 pub(crate) work_area: PhysicalRect<i32, u32>,
63 pub(crate) scale_factor: f64,
64}
65
66impl From<RuntimeMonitor> for Monitor {
67 fn from(monitor: RuntimeMonitor) -> Self {
68 Self {
69 name: monitor.name,
70 size: monitor.size,
71 position: monitor.position,
72 work_area: monitor.work_area,
73 scale_factor: monitor.scale_factor,
74 }
75 }
76}
77
78impl Monitor {
79 pub fn name(&self) -> Option<&String> {
82 self.name.as_ref()
83 }
84
85 pub fn size(&self) -> &PhysicalSize<u32> {
87 &self.size
88 }
89
90 pub fn position(&self) -> &PhysicalPosition<i32> {
92 &self.position
93 }
94
95 pub fn work_area(&self) -> &PhysicalRect<i32, u32> {
97 &self.work_area
98 }
99
100 pub fn scale_factor(&self) -> f64 {
102 self.scale_factor
103 }
104}
105
106macro_rules! unstable_struct {
107 (#[doc = $doc:expr] $($tokens:tt)*) => {
108 #[cfg(feature = "unstable")]
109 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
110 #[doc = $doc]
111 pub $($tokens)*
112
113 #[cfg(not(feature = "unstable"))]
114 pub(crate) $($tokens)*
115 }
116}
117
118unstable_struct!(
119 #[doc = "A builder for a window managed by Tauri."]
120 struct WindowBuilder<'a, R: Runtime, M: Manager<R>> {
121 manager: &'a M,
122 pub(crate) label: String,
123 pub(crate) window_builder:
124 <R::WindowDispatcher as WindowDispatch<EventLoopMessage>>::WindowBuilder,
125 #[cfg(desktop)]
126 pub(crate) menu: Option<Menu<R>>,
127 #[cfg(desktop)]
128 on_menu_event: Option<crate::app::GlobalMenuEventListener<Window<R>>>,
129 window_effects: Option<WindowEffectsConfig>,
130 #[cfg(target_os = "android")]
131 created_by_activity_name_set: bool,
132 #[cfg(target_os = "ios")]
133 requested_by_scene_identifier_set: bool,
134 }
135);
136
137impl<R: Runtime, M: Manager<R>> fmt::Debug for WindowBuilder<'_, R, M> {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 f.debug_struct("WindowBuilder")
140 .field("label", &self.label)
141 .field("window_builder", &self.window_builder)
142 .finish()
143 }
144}
145
146#[cfg_attr(not(feature = "unstable"), allow(dead_code))]
147impl<'a, R: Runtime, M: Manager<R>> WindowBuilder<'a, R, M> {
148 #[cfg_attr(
160 feature = "unstable",
161 doc = r####"
162```
163tauri::Builder::default()
164 .setup(|app| {
165 let window = tauri::window::WindowBuilder::new(app, "label")
166 .build()?;
167 Ok(())
168 });
169```
170 "####
171 )]
172 #[cfg_attr(
175 feature = "unstable",
176 doc = r####"
177```
178tauri::Builder::default()
179 .setup(|app| {
180 let handle = app.handle().clone();
181 std::thread::spawn(move || {
182 let window = tauri::window::WindowBuilder::new(&handle, "label")
183 .build()
184 .unwrap();
185 });
186 Ok(())
187 });
188```
189 "####
190 )]
191 #[cfg_attr(
195 feature = "unstable",
196 doc = r####"
197```
198#[tauri::command]
199async fn create_window(app: tauri::AppHandle) {
200 let window = tauri::window::WindowBuilder::new(&app, "label")
201 .build()
202 .unwrap();
203}
204```
205 "####
206 )]
207 pub fn new<L: Into<String>>(manager: &'a M, label: L) -> Self {
210 Self {
211 manager,
212 label: label.into(),
213 window_builder: <R::WindowDispatcher as WindowDispatch<EventLoopMessage>>::WindowBuilder::new(
214 ),
215 #[cfg(desktop)]
216 menu: None,
217 #[cfg(desktop)]
218 on_menu_event: None,
219 window_effects: None,
220 #[cfg(target_os = "android")]
221 created_by_activity_name_set: false,
222 #[cfg(target_os = "ios")]
223 requested_by_scene_identifier_set: false,
224 }
225 }
226
227 #[cfg_attr(
241 feature = "unstable",
242 doc = r####"
243```
244#[tauri::command]
245async fn reopen_window(app: tauri::AppHandle) {
246 let window = tauri::window::WindowBuilder::from_config(&app, &app.config().app.windows.get(0).unwrap().clone())
247 .unwrap()
248 .build()
249 .unwrap();
250}
251```
252 "####
253 )]
254 pub fn from_config(manager: &'a M, config: &WindowConfig) -> crate::Result<Self> {
257 #[cfg_attr(not(windows), allow(unused_mut))]
258 let mut builder = Self {
259 #[cfg(target_os = "android")]
260 created_by_activity_name_set: config.created_by_activity_name.is_some(),
261 #[cfg(target_os = "ios")]
262 requested_by_scene_identifier_set: config.requested_by_scene_identifier.is_some(),
263 manager,
264 label: config.label.clone(),
265 window_effects: config.window_effects.clone(),
266 window_builder:
267 <R::WindowDispatcher as WindowDispatch<EventLoopMessage>>::WindowBuilder::with_config(
268 config,
269 ),
270 #[cfg(desktop)]
271 menu: None,
272 #[cfg(desktop)]
273 on_menu_event: None,
274 };
275
276 #[cfg(desktop)]
277 if let Some(parent) = &config.parent {
278 let window = manager
279 .manager()
280 .get_window(parent)
281 .ok_or(crate::Error::WindowNotFound)?;
282 builder = builder.parent(&window)?;
283 }
284
285 Ok(builder)
286 }
287
288 #[cfg_attr(
298 feature = "unstable",
299 doc = r####"
300```
301use tauri::menu::{Menu, Submenu, MenuItem};
302tauri::Builder::default()
303 .setup(|app| {
304 let handle = app.handle();
305 let save_menu_item = MenuItem::new(handle, "Save", true, None::<&str>)?;
306 let menu = Menu::with_items(handle, &[
307 &Submenu::with_items(handle, "File", true, &[
308 &save_menu_item,
309 ])?,
310 ])?;
311 let window = tauri::window::WindowBuilder::new(app, "editor")
312 .menu(menu)
313 .on_menu_event(move |window, event| {
314 if event.id == save_menu_item.id() {
315 // save menu item
316 }
317 })
318 .build()
319 .unwrap();
320 ///
321 Ok(())
322 });
323```"####
324 )]
325 #[cfg(desktop)]
326 pub fn on_menu_event<F: Fn(&Window<R>, crate::menu::MenuEvent) + Send + Sync + 'static>(
327 mut self,
328 f: F,
329 ) -> Self {
330 self.on_menu_event.replace(Box::new(f));
331 self
332 }
333
334 #[cfg_attr(
336 feature = "tracing",
337 tracing::instrument(name = "webview::create", skip_all)
338 )]
339 pub(crate) fn with_webview(
340 self,
341 webview: WebviewBuilder<R>,
342 ) -> crate::Result<(Window<R>, Webview<R>)> {
343 let pending_webview = webview.into_pending_webview(self.manager, &self.label)?;
344 let window = self.build_internal(Some(pending_webview))?;
345
346 let webview = window.webviews().first().unwrap().clone();
347
348 Ok((window, webview))
349 }
350
351 pub fn build(self) -> crate::Result<Window<R>> {
353 self.build_internal(None)
354 }
355
356 fn build_internal(
358 #[allow(unused_mut)] mut self,
360 webview: Option<PendingWebview<EventLoopMessage, R>>,
361 ) -> crate::Result<Window<R>> {
362 #[cfg(desktop)]
363 let theme = self.window_builder.get_theme();
364
365 #[cfg(target_os = "android")]
366 if !self.created_by_activity_name_set {
367 if let Some(manager_window_activity_name) = self.manager.activity_name() {
368 self.window_builder = self
369 .window_builder
370 .created_by_activity_name(manager_window_activity_name?);
371 }
372 }
373
374 #[cfg(target_os = "ios")]
375 if !self.requested_by_scene_identifier_set {
376 if let Some(manager_window_scene_identifier) = self.manager.scene_identifier() {
377 self.window_builder = self
378 .window_builder
379 .requested_by_scene_identifier(manager_window_scene_identifier?);
380 }
381 }
382
383 let mut pending = PendingWindow::new(self.window_builder, self.label)?;
384 if let Some(webview) = webview {
385 pending.set_webview(webview);
386 }
387
388 let app_manager = self.manager.manager();
389
390 let pending = app_manager.window.prepare_window(pending)?;
391
392 #[cfg(desktop)]
393 let window_menu = {
394 let is_app_wide = self.menu.is_none();
395 self
396 .menu
397 .or_else(|| self.manager.app_handle().menu())
398 .map(|menu| WindowMenu { is_app_wide, menu })
399 };
400
401 #[cfg(desktop)]
402 let handler = app_manager
403 .menu
404 .prepare_window_menu_creation_handler(window_menu.as_ref(), theme);
405 #[cfg(not(desktop))]
406 #[allow(clippy::type_complexity)]
407 let handler: Option<Box<dyn Fn(tauri_runtime::window::RawWindow<'_>) + Send>> = None;
408
409 let window = match &mut self.manager.runtime() {
410 RuntimeOrDispatch::Runtime(runtime) => runtime.create_window(pending, handler),
411 RuntimeOrDispatch::RuntimeHandle(handle) => handle.create_window(pending, handler),
412 RuntimeOrDispatch::Dispatch(dispatcher) => dispatcher.create_window(pending, handler),
413 }
414 .map(|detached_window| {
415 let window = app_manager.window.attach_window(
416 self.manager.app_handle().clone(),
417 detached_window.clone(),
418 #[cfg(desktop)]
419 window_menu,
420 );
421
422 if let Some(webview) = detached_window.webview {
423 app_manager.webview.attach_webview(
424 window.clone(),
425 webview.webview,
426 webview.use_https_scheme,
427 );
428 }
429
430 window
431 })?;
432
433 #[cfg(desktop)]
434 if let Some(handler) = self.on_menu_event {
435 window.on_menu_event(handler);
436 }
437
438 let app_manager = self.manager.manager_owned();
439 let window_label = window.label().to_string();
440 let window_ = window.clone();
441 let _ = window.run_on_main_thread(move || {
443 if let Some(effects) = self.window_effects {
444 _ = crate::vibrancy::set_window_effects(&window_, Some(effects));
445 }
446 let event = crate::EventName::from_str("tauri://window-created");
447 let payload = Some(crate::webview::CreatedEvent {
448 label: window_label,
449 });
450 let _ = app_manager.emit(event, EmitPayload::Serialize(&payload));
451 });
452
453 Ok(window)
454 }
455}
456
457#[cfg(desktop)]
459#[cfg_attr(not(feature = "unstable"), allow(dead_code))]
460impl<'a, R: Runtime, M: Manager<R>> WindowBuilder<'a, R, M> {
461 #[must_use]
463 pub fn menu(mut self, menu: Menu<R>) -> Self {
464 self.menu.replace(menu);
465 self
466 }
467
468 #[must_use]
470 pub fn center(mut self) -> Self {
471 self.window_builder = self.window_builder.center();
472 self
473 }
474
475 #[must_use]
484 pub fn prevent_overflow(mut self) -> Self {
485 self.window_builder = self.window_builder.prevent_overflow();
486 self
487 }
488
489 #[must_use]
498 pub fn prevent_overflow_with_margin(mut self, margin: impl Into<Size>) -> Self {
499 self.window_builder = self
500 .window_builder
501 .prevent_overflow_with_margin(margin.into());
502 self
503 }
504
505 #[must_use]
513 pub fn maximizable(mut self, maximizable: bool) -> Self {
514 self.window_builder = self.window_builder.maximizable(maximizable);
515 self
516 }
517
518 #[must_use]
524 pub fn minimizable(mut self, minimizable: bool) -> Self {
525 self.window_builder = self.window_builder.minimizable(minimizable);
526 self
527 }
528
529 #[must_use]
537 pub fn closable(mut self, closable: bool) -> Self {
538 self.window_builder = self.window_builder.closable(closable);
539 self
540 }
541
542 #[must_use]
544 pub fn fullscreen(mut self, fullscreen: bool) -> Self {
545 self.window_builder = self.window_builder.fullscreen(fullscreen);
546 self
547 }
548
549 #[must_use]
551 pub fn maximized(mut self, maximized: bool) -> Self {
552 self.window_builder = self.window_builder.maximized(maximized);
553 self
554 }
555
556 #[must_use]
558 pub fn decorations(mut self, decorations: bool) -> Self {
559 self.window_builder = self.window_builder.decorations(decorations);
560 self
561 }
562
563 #[must_use]
565 pub fn always_on_bottom(mut self, always_on_bottom: bool) -> Self {
566 self.window_builder = self.window_builder.always_on_bottom(always_on_bottom);
567 self
568 }
569
570 #[must_use]
572 pub fn always_on_top(mut self, always_on_top: bool) -> Self {
573 self.window_builder = self.window_builder.always_on_top(always_on_top);
574 self
575 }
576
577 #[must_use]
583 pub fn visible_on_all_workspaces(mut self, visible_on_all_workspaces: bool) -> Self {
584 self.window_builder = self
585 .window_builder
586 .visible_on_all_workspaces(visible_on_all_workspaces);
587 self
588 }
589
590 pub fn icon(mut self, icon: Image<'a>) -> crate::Result<Self> {
592 self.window_builder = self.window_builder.icon(icon.into())?;
593 Ok(self)
594 }
595
596 #[must_use]
602 pub fn skip_taskbar(mut self, skip: bool) -> Self {
603 self.window_builder = self.window_builder.skip_taskbar(skip);
604 self
605 }
606
607 #[must_use]
609 pub fn window_classname<S: Into<String>>(mut self, classname: S) -> Self {
610 self.window_builder = self.window_builder.window_classname(classname);
611 self
612 }
613
614 #[must_use]
624 pub fn shadow(mut self, enable: bool) -> Self {
625 self.window_builder = self.window_builder.shadow(enable);
626 self
627 }
628
629 pub fn parent(mut self, parent: &Window<R>) -> crate::Result<Self> {
641 #[cfg(windows)]
642 {
643 self.window_builder = self.window_builder.owner(parent.hwnd()?);
644 }
645
646 #[cfg(any(
647 target_os = "linux",
648 target_os = "dragonfly",
649 target_os = "freebsd",
650 target_os = "netbsd",
651 target_os = "openbsd"
652 ))]
653 {
654 self.window_builder = self.window_builder.transient_for(&parent.gtk_window()?);
655 }
656
657 #[cfg(target_os = "macos")]
658 {
659 self.window_builder = self.window_builder.parent(parent.ns_window()?);
660 }
661
662 Ok(self)
663 }
664
665 #[cfg(windows)]
674 pub fn owner(mut self, owner: &Window<R>) -> crate::Result<Self> {
675 self.window_builder = self.window_builder.owner(owner.hwnd()?);
676 Ok(self)
677 }
678
679 #[cfg(windows)]
690 #[must_use]
691 pub fn owner_raw(mut self, owner: HWND) -> Self {
692 self.window_builder = self.window_builder.owner(owner);
693 self
694 }
695
696 #[cfg(windows)]
704 #[must_use]
705 pub fn parent_raw(mut self, parent: HWND) -> Self {
706 self.window_builder = self.window_builder.parent(parent);
707 self
708 }
709
710 #[cfg(target_os = "macos")]
716 #[must_use]
717 pub fn parent_raw(mut self, parent: *mut std::ffi::c_void) -> Self {
718 self.window_builder = self.window_builder.parent(parent);
719 self
720 }
721
722 #[cfg(any(
728 target_os = "linux",
729 target_os = "dragonfly",
730 target_os = "freebsd",
731 target_os = "netbsd",
732 target_os = "openbsd"
733 ))]
734 pub fn transient_for(mut self, parent: &Window<R>) -> crate::Result<Self> {
735 self.window_builder = self.window_builder.transient_for(&parent.gtk_window()?);
736 Ok(self)
737 }
738
739 #[cfg(any(
745 target_os = "linux",
746 target_os = "dragonfly",
747 target_os = "freebsd",
748 target_os = "netbsd",
749 target_os = "openbsd"
750 ))]
751 #[must_use]
752 pub fn transient_for_raw(mut self, parent: &impl gtk::glib::IsA<gtk::Window>) -> Self {
753 self.window_builder = self.window_builder.transient_for(parent);
754 self
755 }
756
757 #[cfg(windows)]
759 #[must_use]
760 pub fn drag_and_drop(mut self, enabled: bool) -> Self {
761 self.window_builder = self.window_builder.drag_and_drop(enabled);
762 self
763 }
764
765 #[cfg(target_os = "macos")]
767 #[must_use]
768 pub fn title_bar_style(mut self, style: crate::TitleBarStyle) -> Self {
769 self.window_builder = self.window_builder.title_bar_style(style);
770 self
771 }
772
773 #[cfg(target_os = "macos")]
775 #[must_use]
776 pub fn hidden_title(mut self, hidden: bool) -> Self {
777 self.window_builder = self.window_builder.hidden_title(hidden);
778 self
779 }
780
781 #[cfg(target_os = "macos")]
788 #[must_use]
789 pub fn tabbing_identifier(mut self, identifier: &str) -> Self {
790 self.window_builder = self.window_builder.tabbing_identifier(identifier);
791 self
792 }
793
794 pub fn effects(mut self, effects: WindowEffectsConfig) -> Self {
803 self.window_effects.replace(effects);
804 self
805 }
806}
807
808#[cfg_attr(not(feature = "unstable"), allow(dead_code))]
810impl<'a, R: Runtime, M: Manager<R>> WindowBuilder<'a, R, M> {
811 #[must_use]
813 pub fn position(mut self, x: f64, y: f64) -> Self {
814 self.window_builder = self.window_builder.position(x, y);
815 self
816 }
817
818 #[must_use]
820 pub fn inner_size(mut self, width: f64, height: f64) -> Self {
821 self.window_builder = self.window_builder.inner_size(width, height);
822 self
823 }
824
825 #[must_use]
827 pub fn min_inner_size(mut self, min_width: f64, min_height: f64) -> Self {
828 self.window_builder = self.window_builder.min_inner_size(min_width, min_height);
829 self
830 }
831
832 #[must_use]
834 pub fn max_inner_size(mut self, max_width: f64, max_height: f64) -> Self {
835 self.window_builder = self.window_builder.max_inner_size(max_width, max_height);
836 self
837 }
838
839 #[must_use]
841 pub fn inner_size_constraints(
842 mut self,
843 constraints: tauri_runtime::window::WindowSizeConstraints,
844 ) -> Self {
845 self.window_builder = self.window_builder.inner_size_constraints(constraints);
846 self
847 }
848
849 #[must_use]
852 pub fn resizable(mut self, resizable: bool) -> Self {
853 self.window_builder = self.window_builder.resizable(resizable);
854 self
855 }
856
857 #[must_use]
859 pub fn title<S: Into<String>>(mut self, title: S) -> Self {
860 self.window_builder = self.window_builder.title(title);
861 self
862 }
863
864 #[must_use]
866 #[deprecated(
867 since = "1.2.0",
868 note = "The window is automatically focused by default. This function Will be removed in 3.0.0. Use `focused` instead."
869 )]
870 pub fn focus(mut self) -> Self {
871 self.window_builder = self.window_builder.focused(true);
872 self
873 }
874
875 #[must_use]
877 pub fn focused(mut self, focused: bool) -> Self {
878 self.window_builder = self.window_builder.focused(focused);
879 self
880 }
881
882 #[must_use]
884 pub fn focusable(mut self, focusable: bool) -> Self {
885 self.window_builder = self.window_builder.focusable(focusable);
886 self
887 }
888
889 #[must_use]
891 pub fn visible(mut self, visible: bool) -> Self {
892 self.window_builder = self.window_builder.visible(visible);
893 self
894 }
895
896 #[must_use]
902 pub fn theme(mut self, theme: Option<Theme>) -> Self {
903 self.window_builder = self.window_builder.theme(theme);
904 self
905 }
906
907 #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
910 #[cfg_attr(
911 docsrs,
912 doc(cfg(any(not(target_os = "macos"), feature = "macos-private-api")))
913 )]
914 #[must_use]
915 pub fn transparent(mut self, transparent: bool) -> Self {
916 self.window_builder = self.window_builder.transparent(transparent);
917 self
918 }
919
920 #[must_use]
922 pub fn content_protected(mut self, protected: bool) -> Self {
923 self.window_builder = self.window_builder.content_protected(protected);
924 self
925 }
926
927 #[must_use]
933 pub fn background_color(mut self, color: Color) -> Self {
934 self.window_builder = self.window_builder.background_color(color);
935 self
936 }
937}
938
939#[cfg(target_os = "android")]
940impl<R: Runtime, M: Manager<R>> WindowBuilder<'_, R, M> {
941 pub fn activity_name<S: Into<String>>(mut self, class_name: S) -> Self {
943 self.window_builder = self.window_builder.activity_name(class_name);
944 self
945 }
946
947 pub fn created_by_activity_name<S: Into<String>>(mut self, class_name: S) -> Self {
951 self.created_by_activity_name_set = true;
952 self.window_builder = self.window_builder.created_by_activity_name(class_name);
953 self
954 }
955}
956
957#[cfg(target_os = "ios")]
959impl<R: Runtime, M: Manager<R>> WindowBuilder<'_, R, M> {
960 #[cfg(target_os = "ios")]
965 pub fn requested_by_scene_identifier(mut self, identifier: String) -> Self {
966 self.requested_by_scene_identifier_set = true;
967 self.window_builder = self
968 .window_builder
969 .requested_by_scene_identifier(identifier);
970 self
971 }
972}
973
974#[cfg(desktop)]
977pub(crate) struct WindowMenu<R: Runtime> {
978 pub(crate) is_app_wide: bool,
979 pub(crate) menu: Menu<R>,
980}
981
982#[default_runtime(crate::Wry, wry)]
988pub struct Window<R: Runtime> {
989 pub(crate) window: DetachedWindow<EventLoopMessage, R>,
991 pub(crate) manager: Arc<AppManager<R>>,
993 pub(crate) app_handle: AppHandle<R>,
994 #[cfg(desktop)]
996 pub(crate) menu: Arc<Mutex<Option<WindowMenu<R>>>>,
997 pub(crate) resources_table: Arc<Mutex<ResourceTable>>,
998}
999
1000impl<R: Runtime> std::fmt::Debug for Window<R> {
1001 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1002 f.debug_struct("Window")
1003 .field("window", &self.window)
1004 .field("manager", &self.manager)
1005 .field("app_handle", &self.app_handle)
1006 .finish()
1007 }
1008}
1009
1010impl<R: Runtime> raw_window_handle::HasWindowHandle for Window<R> {
1011 fn window_handle(
1012 &self,
1013 ) -> std::result::Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
1014 self.window.dispatcher.window_handle()
1015 }
1016}
1017
1018impl<R: Runtime> raw_window_handle::HasDisplayHandle for Window<R> {
1019 fn display_handle(
1020 &self,
1021 ) -> std::result::Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
1022 self.app_handle.display_handle()
1023 }
1024}
1025
1026impl<R: Runtime> Clone for Window<R> {
1027 fn clone(&self) -> Self {
1028 Self {
1029 window: self.window.clone(),
1030 manager: self.manager.clone(),
1031 app_handle: self.app_handle.clone(),
1032 #[cfg(desktop)]
1033 menu: self.menu.clone(),
1034 resources_table: self.resources_table.clone(),
1035 }
1036 }
1037}
1038
1039impl<R: Runtime> Hash for Window<R> {
1040 fn hash<H: Hasher>(&self, state: &mut H) {
1042 self.window.label.hash(state)
1043 }
1044}
1045
1046impl<R: Runtime> Eq for Window<R> {}
1047impl<R: Runtime> PartialEq for Window<R> {
1048 fn eq(&self, other: &Self) -> bool {
1050 self.window.label.eq(&other.window.label)
1051 }
1052}
1053
1054impl<R: Runtime> Manager<R> for Window<R> {
1055 fn resources_table(&self) -> MutexGuard<'_, ResourceTable> {
1056 self
1057 .resources_table
1058 .lock()
1059 .expect("poisoned window resources table")
1060 }
1061}
1062
1063impl<R: Runtime> ManagerBase<R> for Window<R> {
1064 fn manager(&self) -> &AppManager<R> {
1065 &self.manager
1066 }
1067
1068 fn manager_owned(&self) -> Arc<AppManager<R>> {
1069 self.manager.clone()
1070 }
1071
1072 fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
1073 RuntimeOrDispatch::Dispatch(self.window.dispatcher.clone())
1074 }
1075
1076 fn managed_app_handle(&self) -> &AppHandle<R> {
1077 &self.app_handle
1078 }
1079
1080 #[cfg(target_os = "android")]
1081 fn activity_name(&self) -> Option<crate::Result<String>> {
1082 Some(self.activity_name())
1083 }
1084
1085 #[cfg(target_os = "ios")]
1086 fn scene_identifier(&self) -> Option<crate::Result<String>> {
1087 Some(self.scene_identifier())
1088 }
1089}
1090
1091impl<'de, R: Runtime> CommandArg<'de, R> for Window<R> {
1092 fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError> {
1094 Ok(command.message.webview().window())
1095 }
1096}
1097
1098impl<R: Runtime> Window<R> {
1100 pub(crate) fn new(
1102 manager: Arc<AppManager<R>>,
1103 window: DetachedWindow<EventLoopMessage, R>,
1104 app_handle: AppHandle<R>,
1105 #[cfg(desktop)] menu: Option<WindowMenu<R>>,
1106 ) -> Self {
1107 Self {
1108 window,
1109 manager,
1110 app_handle,
1111 #[cfg(desktop)]
1112 menu: Arc::new(std::sync::Mutex::new(menu)),
1113 resources_table: Default::default(),
1114 }
1115 }
1116
1117 #[cfg(feature = "unstable")]
1121 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
1122 pub fn builder<M: Manager<R>, L: Into<String>>(manager: &M, label: L) -> WindowBuilder<'_, R, M> {
1123 WindowBuilder::new(manager, label.into())
1124 }
1125
1126 #[cfg(any(test, all(desktop, feature = "unstable")))]
1128 #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "unstable"))))]
1129 pub fn add_child<P: Into<Position>, S: Into<Size>>(
1130 &self,
1131 webview_builder: WebviewBuilder<R>,
1132 position: P,
1133 size: S,
1134 ) -> crate::Result<Webview<R>> {
1135 use std::sync::mpsc::channel;
1136
1137 let (tx, rx) = channel();
1138 let position = position.into();
1139 let size = size.into();
1140 let window_ = self.clone();
1141 self.run_on_main_thread(move || {
1142 let res = webview_builder.build(window_, position, size);
1143 tx.send(res).unwrap();
1144 })?;
1145 rx.recv().unwrap()
1146 }
1147
1148 pub fn webviews(&self) -> Vec<Webview<R>> {
1150 self
1151 .manager
1152 .webview
1153 .webviews_lock()
1154 .values()
1155 .filter(|w| w.window_label() == self.label())
1156 .cloned()
1157 .collect()
1158 }
1159
1160 pub(crate) fn is_webview_window(&self) -> bool {
1161 self.webviews().iter().all(|w| w.label() == self.label())
1162 }
1163
1164 pub fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> crate::Result<()> {
1166 self
1167 .window
1168 .dispatcher
1169 .run_on_main_thread(f)
1170 .map_err(Into::into)
1171 }
1172
1173 pub fn label(&self) -> &str {
1175 &self.window.label
1176 }
1177
1178 pub fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) {
1180 self
1181 .window
1182 .dispatcher
1183 .on_window_event(move |event| f(&event.clone().into()));
1184 }
1185}
1186
1187#[cfg(desktop)]
1189impl<R: Runtime> Window<R> {
1190 #[cfg_attr(
1200 feature = "unstable",
1201 doc = r####"
1202```
1203use tauri::menu::{Menu, Submenu, MenuItem};
1204tauri::Builder::default()
1205 .setup(|app| {
1206 let handle = app.handle();
1207 let save_menu_item = MenuItem::new(handle, "Save", true, None::<&str>)?;
1208 let menu = Menu::with_items(handle, &[
1209 &Submenu::with_items(handle, "File", true, &[
1210 &save_menu_item,
1211 ])?,
1212 ])?;
1213 let window = tauri::window::WindowBuilder::new(app, "editor")
1214 .menu(menu)
1215 .build()
1216 .unwrap();
1217
1218 window.on_menu_event(move |window, event| {
1219 if event.id == save_menu_item.id() {
1220 // save menu item
1221 }
1222 });
1223
1224 Ok(())
1225 });
1226```
1227 "####
1228 )]
1229 pub fn on_menu_event<F: Fn(&Window<R>, crate::menu::MenuEvent) + Send + Sync + 'static>(
1230 &self,
1231 f: F,
1232 ) {
1233 self
1234 .manager
1235 .menu
1236 .event_listeners
1237 .lock()
1238 .unwrap()
1239 .insert(self.label().to_string(), Box::new(f));
1240 }
1241
1242 pub(crate) fn menu_lock(&self) -> std::sync::MutexGuard<'_, Option<WindowMenu<R>>> {
1243 self.menu.lock().expect("poisoned window")
1244 }
1245
1246 #[cfg_attr(target_os = "macos", allow(dead_code))]
1247 pub(crate) fn has_app_wide_menu(&self) -> bool {
1248 self
1249 .menu_lock()
1250 .as_ref()
1251 .map(|m| m.is_app_wide)
1252 .unwrap_or(false)
1253 }
1254
1255 #[cfg_attr(target_os = "macos", allow(dead_code))]
1256 pub(crate) fn is_menu_in_use<I: PartialEq<MenuId>>(&self, id: &I) -> bool {
1257 self
1258 .menu_lock()
1259 .as_ref()
1260 .map(|m| id.eq(m.menu.id()))
1261 .unwrap_or(false)
1262 }
1263
1264 pub fn menu(&self) -> Option<Menu<R>> {
1266 self.menu_lock().as_ref().map(|m| m.menu.clone())
1267 }
1268
1269 #[cfg_attr(target_os = "macos", allow(unused_variables))]
1276 pub fn set_menu(&self, menu: Menu<R>) -> crate::Result<Option<Menu<R>>> {
1277 let prev_menu = self.remove_menu()?;
1278
1279 self.manager.menu.insert_menu_into_stash(&menu);
1280
1281 let window = self.clone();
1282 let menu_ = menu.clone();
1283 self.run_on_main_thread(move || {
1284 #[cfg(windows)]
1285 if let Ok(hwnd) = window.hwnd() {
1286 let theme = window
1287 .theme()
1288 .map(crate::menu::map_to_menu_theme)
1289 .unwrap_or(muda::MenuTheme::Auto);
1290
1291 let _ = unsafe { menu_.inner().init_for_hwnd_with_theme(hwnd.0 as _, theme) };
1292 }
1293 #[cfg(any(
1294 target_os = "linux",
1295 target_os = "dragonfly",
1296 target_os = "freebsd",
1297 target_os = "netbsd",
1298 target_os = "openbsd"
1299 ))]
1300 if let (Ok(gtk_window), Ok(gtk_box)) = (window.gtk_window(), window.default_vbox()) {
1301 let _ = menu_
1302 .inner()
1303 .init_for_gtk_window(>k_window, Some(>k_box));
1304 }
1305 })?;
1306
1307 self.menu_lock().replace(WindowMenu {
1308 is_app_wide: false,
1309 menu,
1310 });
1311
1312 Ok(prev_menu)
1313 }
1314
1315 pub fn remove_menu(&self) -> crate::Result<Option<Menu<R>>> {
1322 let prev_menu = self.menu_lock().take().map(|m| m.menu);
1323
1324 #[cfg_attr(target_os = "macos", allow(unused_variables))]
1326 if let Some(menu) = &prev_menu {
1327 let window = self.clone();
1328 let menu = menu.clone();
1329 self.run_on_main_thread(move || {
1330 #[cfg(windows)]
1331 if let Ok(hwnd) = window.hwnd() {
1332 let _ = unsafe { menu.inner().remove_for_hwnd(hwnd.0 as _) };
1333 }
1334 #[cfg(any(
1335 target_os = "linux",
1336 target_os = "dragonfly",
1337 target_os = "freebsd",
1338 target_os = "netbsd",
1339 target_os = "openbsd"
1340 ))]
1341 if let Ok(gtk_window) = window.gtk_window() {
1342 let _ = menu.inner().remove_for_gtk_window(>k_window);
1343 }
1344 })?;
1345 }
1346
1347 self
1348 .manager
1349 .remove_menu_from_stash_by_id(prev_menu.as_ref().map(|m| m.id()));
1350
1351 Ok(prev_menu)
1352 }
1353
1354 pub fn hide_menu(&self) -> crate::Result<()> {
1356 #[cfg_attr(target_os = "macos", allow(unused_variables))]
1358 if let Some(window_menu) = &*self.menu_lock() {
1359 let window = self.clone();
1360 let menu_ = window_menu.menu.clone();
1361 self.run_on_main_thread(move || {
1362 #[cfg(windows)]
1363 if let Ok(hwnd) = window.hwnd() {
1364 let _ = unsafe { menu_.inner().hide_for_hwnd(hwnd.0 as _) };
1365 }
1366 #[cfg(any(
1367 target_os = "linux",
1368 target_os = "dragonfly",
1369 target_os = "freebsd",
1370 target_os = "netbsd",
1371 target_os = "openbsd"
1372 ))]
1373 if let Ok(gtk_window) = window.gtk_window() {
1374 let _ = menu_.inner().hide_for_gtk_window(>k_window);
1375 }
1376 })?;
1377 }
1378
1379 Ok(())
1380 }
1381
1382 pub fn show_menu(&self) -> crate::Result<()> {
1384 #[cfg_attr(target_os = "macos", allow(unused_variables))]
1386 if let Some(window_menu) = &*self.menu_lock() {
1387 let window = self.clone();
1388 let menu_ = window_menu.menu.clone();
1389 self.run_on_main_thread(move || {
1390 #[cfg(windows)]
1391 if let Ok(hwnd) = window.hwnd() {
1392 let _ = unsafe { menu_.inner().show_for_hwnd(hwnd.0 as _) };
1393 }
1394 #[cfg(any(
1395 target_os = "linux",
1396 target_os = "dragonfly",
1397 target_os = "freebsd",
1398 target_os = "netbsd",
1399 target_os = "openbsd"
1400 ))]
1401 if let Ok(gtk_window) = window.gtk_window() {
1402 let _ = menu_.inner().show_for_gtk_window(>k_window);
1403 }
1404 })?;
1405 }
1406
1407 Ok(())
1408 }
1409
1410 pub fn is_menu_visible(&self) -> crate::Result<bool> {
1412 #[cfg_attr(target_os = "macos", allow(unused_variables))]
1414 if let Some(window_menu) = &*self.menu_lock() {
1415 let (tx, rx) = std::sync::mpsc::channel();
1416 let window = self.clone();
1417 let menu_ = window_menu.menu.clone();
1418 self.run_on_main_thread(move || {
1419 #[cfg(windows)]
1420 if let Ok(hwnd) = window.hwnd() {
1421 let _ = tx.send(unsafe { menu_.inner().is_visible_on_hwnd(hwnd.0 as _) });
1422 }
1423 #[cfg(any(
1424 target_os = "linux",
1425 target_os = "dragonfly",
1426 target_os = "freebsd",
1427 target_os = "netbsd",
1428 target_os = "openbsd"
1429 ))]
1430 if let Ok(gtk_window) = window.gtk_window() {
1431 let _ = tx.send(menu_.inner().is_visible_on_gtk_window(>k_window));
1432 }
1433 })?;
1434
1435 return Ok(rx.recv().unwrap_or(false));
1436 }
1437
1438 Ok(false)
1439 }
1440
1441 pub fn popup_menu<M: ContextMenu>(&self, menu: &M) -> crate::Result<()> {
1443 menu.popup(self.clone())
1444 }
1445
1446 pub fn popup_menu_at<M: ContextMenu, P: Into<Position>>(
1450 &self,
1451 menu: &M,
1452 position: P,
1453 ) -> crate::Result<()> {
1454 menu.popup_at(self.clone(), position)
1455 }
1456}
1457
1458impl<R: Runtime> Window<R> {
1460 pub fn scale_factor(&self) -> crate::Result<f64> {
1462 self.window.dispatcher.scale_factor().map_err(Into::into)
1463 }
1464
1465 pub fn inner_position(&self) -> crate::Result<PhysicalPosition<i32>> {
1467 self.window.dispatcher.inner_position().map_err(Into::into)
1468 }
1469
1470 pub fn outer_position(&self) -> crate::Result<PhysicalPosition<i32>> {
1472 self.window.dispatcher.outer_position().map_err(Into::into)
1473 }
1474
1475 pub fn inner_size(&self) -> crate::Result<PhysicalSize<u32>> {
1479 self.window.dispatcher.inner_size().map_err(Into::into)
1480 }
1481
1482 pub fn outer_size(&self) -> crate::Result<PhysicalSize<u32>> {
1486 self.window.dispatcher.outer_size().map_err(Into::into)
1487 }
1488
1489 pub fn is_fullscreen(&self) -> crate::Result<bool> {
1491 self.window.dispatcher.is_fullscreen().map_err(Into::into)
1492 }
1493
1494 pub fn is_minimized(&self) -> crate::Result<bool> {
1496 self.window.dispatcher.is_minimized().map_err(Into::into)
1497 }
1498
1499 pub fn is_maximized(&self) -> crate::Result<bool> {
1501 self.window.dispatcher.is_maximized().map_err(Into::into)
1502 }
1503
1504 pub fn is_focused(&self) -> crate::Result<bool> {
1506 self.window.dispatcher.is_focused().map_err(Into::into)
1507 }
1508
1509 pub fn is_decorated(&self) -> crate::Result<bool> {
1511 self.window.dispatcher.is_decorated().map_err(Into::into)
1512 }
1513
1514 pub fn is_resizable(&self) -> crate::Result<bool> {
1516 self.window.dispatcher.is_resizable().map_err(Into::into)
1517 }
1518
1519 pub fn is_enabled(&self) -> crate::Result<bool> {
1521 self.window.dispatcher.is_enabled().map_err(Into::into)
1522 }
1523
1524 pub fn is_always_on_top(&self) -> crate::Result<bool> {
1530 self
1531 .window
1532 .dispatcher
1533 .is_always_on_top()
1534 .map_err(Into::into)
1535 }
1536
1537 pub fn is_maximizable(&self) -> crate::Result<bool> {
1543 self.window.dispatcher.is_maximizable().map_err(Into::into)
1544 }
1545
1546 pub fn is_minimizable(&self) -> crate::Result<bool> {
1552 self.window.dispatcher.is_minimizable().map_err(Into::into)
1553 }
1554
1555 pub fn is_closable(&self) -> crate::Result<bool> {
1561 self.window.dispatcher.is_closable().map_err(Into::into)
1562 }
1563
1564 pub fn is_visible(&self) -> crate::Result<bool> {
1566 self.window.dispatcher.is_visible().map_err(Into::into)
1567 }
1568
1569 pub fn title(&self) -> crate::Result<String> {
1571 self.window.dispatcher.title().map_err(Into::into)
1572 }
1573
1574 pub fn current_monitor(&self) -> crate::Result<Option<Monitor>> {
1578 self
1579 .window
1580 .dispatcher
1581 .current_monitor()
1582 .map(|m| m.map(Into::into))
1583 .map_err(Into::into)
1584 }
1585
1586 pub fn monitor_from_point(&self, x: f64, y: f64) -> crate::Result<Option<Monitor>> {
1588 self
1589 .window
1590 .dispatcher
1591 .monitor_from_point(x, y)
1592 .map(|m| m.map(Into::into))
1593 .map_err(Into::into)
1594 }
1595
1596 pub fn primary_monitor(&self) -> crate::Result<Option<Monitor>> {
1600 self
1601 .window
1602 .dispatcher
1603 .primary_monitor()
1604 .map(|m| m.map(Into::into))
1605 .map_err(Into::into)
1606 }
1607
1608 pub fn available_monitors(&self) -> crate::Result<Vec<Monitor>> {
1610 self
1611 .window
1612 .dispatcher
1613 .available_monitors()
1614 .map(|m| m.into_iter().map(Into::into).collect())
1615 .map_err(Into::into)
1616 }
1617
1618 #[cfg(target_os = "macos")]
1620 pub fn ns_window(&self) -> crate::Result<*mut std::ffi::c_void> {
1621 self
1622 .window
1623 .dispatcher
1624 .window_handle()
1625 .map_err(Into::into)
1626 .and_then(|handle| {
1627 if let raw_window_handle::RawWindowHandle::AppKit(h) = handle.as_raw() {
1628 let view: &objc2_app_kit::NSView = unsafe { h.ns_view.cast().as_ref() };
1629 let ns_window = view.window().expect("view to be installed in window");
1630 Ok(objc2::rc::Retained::autorelease_ptr(ns_window).cast())
1631 } else {
1632 Err(crate::Error::InvalidWindowHandle)
1633 }
1634 })
1635 }
1636
1637 #[cfg(target_os = "macos")]
1639 pub fn ns_view(&self) -> crate::Result<*mut std::ffi::c_void> {
1640 self
1641 .window
1642 .dispatcher
1643 .window_handle()
1644 .map_err(Into::into)
1645 .and_then(|handle| {
1646 if let raw_window_handle::RawWindowHandle::AppKit(h) = handle.as_raw() {
1647 Ok(h.ns_view.as_ptr())
1648 } else {
1649 Err(crate::Error::InvalidWindowHandle)
1650 }
1651 })
1652 }
1653
1654 #[cfg(windows)]
1656 pub fn hwnd(&self) -> crate::Result<HWND> {
1657 self
1658 .window
1659 .dispatcher
1660 .window_handle()
1661 .map_err(Into::into)
1662 .and_then(|handle| {
1663 if let raw_window_handle::RawWindowHandle::Win32(h) = handle.as_raw() {
1664 Ok(HWND(h.hwnd.get() as _))
1665 } else {
1666 Err(crate::Error::InvalidWindowHandle)
1667 }
1668 })
1669 }
1670
1671 #[cfg(any(
1675 target_os = "linux",
1676 target_os = "dragonfly",
1677 target_os = "freebsd",
1678 target_os = "netbsd",
1679 target_os = "openbsd"
1680 ))]
1681 pub fn gtk_window(&self) -> crate::Result<gtk::ApplicationWindow> {
1682 self.window.dispatcher.gtk_window().map_err(Into::into)
1683 }
1684
1685 #[cfg(any(
1689 target_os = "linux",
1690 target_os = "dragonfly",
1691 target_os = "freebsd",
1692 target_os = "netbsd",
1693 target_os = "openbsd"
1694 ))]
1695 pub fn default_vbox(&self) -> crate::Result<gtk::Box> {
1696 self.window.dispatcher.default_vbox().map_err(Into::into)
1697 }
1698
1699 #[cfg(target_os = "android")]
1701 pub fn activity_name(&self) -> crate::Result<String> {
1702 self.window.dispatcher.activity_name().map_err(Into::into)
1703 }
1704
1705 #[cfg(target_os = "ios")]
1707 pub fn scene_identifier(&self) -> crate::Result<String> {
1708 self
1709 .window
1710 .dispatcher
1711 .scene_identifier()
1712 .map_err(Into::into)
1713 }
1714
1715 pub fn theme(&self) -> crate::Result<Theme> {
1721 self.window.dispatcher.theme().map_err(Into::into)
1722 }
1723}
1724
1725#[cfg(desktop)]
1727impl<R: Runtime> Window<R> {
1728 pub fn cursor_position(&self) -> crate::Result<PhysicalPosition<f64>> {
1737 self.app_handle.cursor_position()
1738 }
1739}
1740
1741impl<R: Runtime> Window<R> {
1743 pub fn set_resizable(&self, resizable: bool) -> crate::Result<()> {
1746 self
1747 .window
1748 .dispatcher
1749 .set_resizable(resizable)
1750 .map_err(Into::into)
1751 }
1752
1753 pub fn set_title(&self, title: &str) -> crate::Result<()> {
1755 self
1756 .window
1757 .dispatcher
1758 .set_title(title.to_string())
1759 .map_err(Into::into)
1760 }
1761
1762 pub fn set_enabled(&self, enabled: bool) -> crate::Result<()> {
1764 self
1765 .window
1766 .dispatcher
1767 .set_enabled(enabled)
1768 .map_err(Into::into)
1769 }
1770
1771 pub fn show(&self) -> crate::Result<()> {
1773 self.window.dispatcher.show().map_err(Into::into)
1774 }
1775
1776 pub fn hide(&self) -> crate::Result<()> {
1778 self.window.dispatcher.hide().map_err(Into::into)
1779 }
1780
1781 pub fn close(&self) -> crate::Result<()> {
1783 self.window.dispatcher.close().map_err(Into::into)
1784 }
1785
1786 pub fn destroy(&self) -> crate::Result<()> {
1788 self.window.dispatcher.destroy().map_err(Into::into)
1789 }
1790
1791 pub fn set_background_color(&self, color: Option<Color>) -> crate::Result<()> {
1798 self
1799 .window
1800 .dispatcher
1801 .set_background_color(color)
1802 .map_err(Into::into)
1803 }
1804
1805 pub fn set_content_protected(&self, protected: bool) -> crate::Result<()> {
1807 self
1808 .window
1809 .dispatcher
1810 .set_content_protected(protected)
1811 .map_err(Into::into)
1812 }
1813
1814 pub fn set_size<S: Into<Size>>(&self, size: S) -> crate::Result<()> {
1816 self
1817 .window
1818 .dispatcher
1819 .set_size(size.into())
1820 .map_err(Into::into)
1821 }
1822
1823 pub fn set_min_size<S: Into<Size>>(&self, size: Option<S>) -> crate::Result<()> {
1825 self
1826 .window
1827 .dispatcher
1828 .set_min_size(size.map(|s| s.into()))
1829 .map_err(Into::into)
1830 }
1831
1832 pub fn set_max_size<S: Into<Size>>(&self, size: Option<S>) -> crate::Result<()> {
1834 self
1835 .window
1836 .dispatcher
1837 .set_max_size(size.map(|s| s.into()))
1838 .map_err(Into::into)
1839 }
1840
1841 pub fn set_size_constraints(
1843 &self,
1844 constraints: tauri_runtime::window::WindowSizeConstraints,
1845 ) -> crate::Result<()> {
1846 self
1847 .window
1848 .dispatcher
1849 .set_size_constraints(constraints)
1850 .map_err(Into::into)
1851 }
1852
1853 pub fn set_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
1855 self
1856 .window
1857 .dispatcher
1858 .set_position(position.into())
1859 .map_err(Into::into)
1860 }
1861
1862 pub fn set_focus(&self) -> crate::Result<()> {
1864 self.window.dispatcher.set_focus().map_err(Into::into)
1865 }
1866
1867 pub fn set_focusable(&self, focusable: bool) -> crate::Result<()> {
1874 self
1875 .window
1876 .dispatcher
1877 .set_focusable(focusable)
1878 .map_err(Into::into)
1879 }
1880
1881 pub fn set_theme(&self, theme: Option<Theme>) -> crate::Result<()> {
1888 self
1889 .window
1890 .dispatcher
1891 .set_theme(theme)
1892 .map_err(Into::<crate::Error>::into)?;
1893 #[cfg(windows)]
1894 if let (Some(menu), Ok(hwnd)) = (self.menu(), self.hwnd()) {
1895 let raw_hwnd = hwnd.0 as isize;
1896 self.run_on_main_thread(move || {
1897 let _ = unsafe {
1898 menu.inner().set_theme_for_hwnd(
1899 raw_hwnd,
1900 theme
1901 .map(crate::menu::map_to_menu_theme)
1902 .unwrap_or(muda::MenuTheme::Auto),
1903 )
1904 };
1905 })?;
1906 };
1907 Ok(())
1908 }
1909}
1910
1911#[cfg(desktop)]
1913impl<R: Runtime> Window<R> {
1914 pub fn center(&self) -> crate::Result<()> {
1916 self.window.dispatcher.center().map_err(Into::into)
1917 }
1918
1919 pub fn request_user_attention(
1931 &self,
1932 request_type: Option<UserAttentionType>,
1933 ) -> crate::Result<()> {
1934 self
1935 .window
1936 .dispatcher
1937 .request_user_attention(request_type)
1938 .map_err(Into::into)
1939 }
1940
1941 pub fn set_maximizable(&self, maximizable: bool) -> crate::Result<()> {
1949 self
1950 .window
1951 .dispatcher
1952 .set_maximizable(maximizable)
1953 .map_err(Into::into)
1954 }
1955
1956 pub fn set_minimizable(&self, minimizable: bool) -> crate::Result<()> {
1962 self
1963 .window
1964 .dispatcher
1965 .set_minimizable(minimizable)
1966 .map_err(Into::into)
1967 }
1968
1969 pub fn set_closable(&self, closable: bool) -> crate::Result<()> {
1977 self
1978 .window
1979 .dispatcher
1980 .set_closable(closable)
1981 .map_err(Into::into)
1982 }
1983
1984 pub fn maximize(&self) -> crate::Result<()> {
1986 self.window.dispatcher.maximize().map_err(Into::into)
1987 }
1988
1989 pub fn unmaximize(&self) -> crate::Result<()> {
1991 self.window.dispatcher.unmaximize().map_err(Into::into)
1992 }
1993
1994 pub fn minimize(&self) -> crate::Result<()> {
1996 self.window.dispatcher.minimize().map_err(Into::into)
1997 }
1998
1999 pub fn unminimize(&self) -> crate::Result<()> {
2001 self.window.dispatcher.unminimize().map_err(Into::into)
2002 }
2003
2004 pub fn set_decorations(&self, decorations: bool) -> crate::Result<()> {
2008 self
2009 .window
2010 .dispatcher
2011 .set_decorations(decorations)
2012 .map_err(Into::into)
2013 }
2014
2015 pub fn set_shadow(&self, enable: bool) -> crate::Result<()> {
2025 self
2026 .window
2027 .dispatcher
2028 .set_shadow(enable)
2029 .map_err(Into::into)
2030 }
2031
2032 #[cfg_attr(
2039 feature = "unstable",
2040 doc = r####"
2041```rust,no_run
2042use tauri::{Manager, window::{Color, Effect, EffectState, EffectsBuilder}};
2043tauri::Builder::default()
2044 .setup(|app| {
2045 let window = app.get_window("main").unwrap();
2046 window.set_effects(
2047 EffectsBuilder::new()
2048 .effect(Effect::Popover)
2049 .state(EffectState::Active)
2050 .radius(5.)
2051 .color(Color(0, 0, 0, 255))
2052 .build(),
2053 )?;
2054 Ok(())
2055 });
2056```
2057 "####
2058 )]
2059 pub fn set_effects<E: Into<Option<WindowEffectsConfig>>>(&self, effects: E) -> crate::Result<()> {
2065 let effects = effects.into();
2066 let window = self.clone();
2067 self.run_on_main_thread(move || {
2068 let _ = crate::vibrancy::set_window_effects(&window, effects);
2069 })
2070 }
2071
2072 pub fn set_always_on_bottom(&self, always_on_bottom: bool) -> crate::Result<()> {
2074 self
2075 .window
2076 .dispatcher
2077 .set_always_on_bottom(always_on_bottom)
2078 .map_err(Into::into)
2079 }
2080
2081 pub fn set_always_on_top(&self, always_on_top: bool) -> crate::Result<()> {
2083 self
2084 .window
2085 .dispatcher
2086 .set_always_on_top(always_on_top)
2087 .map_err(Into::into)
2088 }
2089
2090 pub fn set_visible_on_all_workspaces(
2096 &self,
2097 visible_on_all_workspaces: bool,
2098 ) -> crate::Result<()> {
2099 self
2100 .window
2101 .dispatcher
2102 .set_visible_on_all_workspaces(visible_on_all_workspaces)
2103 .map_err(Into::into)
2104 }
2105
2106 pub fn set_fullscreen(&self, fullscreen: bool) -> crate::Result<()> {
2108 self
2109 .window
2110 .dispatcher
2111 .set_fullscreen(fullscreen)
2112 .map_err(Into::into)
2113 }
2114
2115 pub fn set_simple_fullscreen(&self, enable: bool) -> crate::Result<()> {
2126 #[cfg(target_os = "macos")]
2127 {
2128 self
2129 .window
2130 .dispatcher
2131 .set_simple_fullscreen(enable)
2132 .map_err(Into::into)
2133 }
2134 #[cfg(not(target_os = "macos"))]
2135 self.set_fullscreen(enable)
2136 }
2137
2138 pub fn set_icon(&self, icon: Image<'_>) -> crate::Result<()> {
2140 self
2141 .window
2142 .dispatcher
2143 .set_icon(icon.into())
2144 .map_err(Into::into)
2145 }
2146
2147 pub fn set_skip_taskbar(&self, skip: bool) -> crate::Result<()> {
2153 self
2154 .window
2155 .dispatcher
2156 .set_skip_taskbar(skip)
2157 .map_err(Into::into)
2158 }
2159
2160 pub fn set_cursor_grab(&self, grab: bool) -> crate::Result<()> {
2170 self
2171 .window
2172 .dispatcher
2173 .set_cursor_grab(grab)
2174 .map_err(Into::into)
2175 }
2176
2177 pub fn set_cursor_visible(&self, visible: bool) -> crate::Result<()> {
2187 self
2188 .window
2189 .dispatcher
2190 .set_cursor_visible(visible)
2191 .map_err(Into::into)
2192 }
2193
2194 pub fn set_cursor_icon(&self, icon: CursorIcon) -> crate::Result<()> {
2196 self
2197 .window
2198 .dispatcher
2199 .set_cursor_icon(icon)
2200 .map_err(Into::into)
2201 }
2202
2203 pub fn set_cursor_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
2205 self
2206 .window
2207 .dispatcher
2208 .set_cursor_position(position)
2209 .map_err(Into::into)
2210 }
2211
2212 pub fn set_ignore_cursor_events(&self, ignore: bool) -> crate::Result<()> {
2214 self
2215 .window
2216 .dispatcher
2217 .set_ignore_cursor_events(ignore)
2218 .map_err(Into::into)
2219 }
2220
2221 pub fn start_dragging(&self) -> crate::Result<()> {
2223 self.window.dispatcher.start_dragging().map_err(Into::into)
2224 }
2225
2226 pub fn start_resize_dragging(
2228 &self,
2229 direction: tauri_runtime::ResizeDirection,
2230 ) -> crate::Result<()> {
2231 self
2232 .window
2233 .dispatcher
2234 .start_resize_dragging(direction)
2235 .map_err(Into::into)
2236 }
2237
2238 #[cfg(target_os = "windows")]
2242 #[cfg_attr(docsrs, doc(cfg(target_os = "windows")))]
2243 pub fn set_overlay_icon(&self, icon: Option<Image<'_>>) -> crate::Result<()> {
2244 self
2245 .window
2246 .dispatcher
2247 .set_overlay_icon(icon.map(|x| x.into()))
2248 .map_err(Into::into)
2249 }
2250
2251 pub fn set_badge_count(&self, count: Option<i64>) -> crate::Result<()> {
2258 self
2259 .window
2260 .dispatcher
2261 .set_badge_count(count, Some(format!("{}.desktop", self.package_info().name)))
2262 .map_err(Into::into)
2263 }
2264
2265 #[cfg(target_os = "macos")]
2267 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
2268 pub fn set_badge_label(&self, label: Option<String>) -> crate::Result<()> {
2269 self
2270 .window
2271 .dispatcher
2272 .set_badge_label(label)
2273 .map_err(Into::into)
2274 }
2275
2276 pub fn set_progress_bar(&self, progress_state: ProgressBarState) -> crate::Result<()> {
2284 self
2285 .window
2286 .dispatcher
2287 .set_progress_bar(crate::runtime::ProgressBarState {
2288 status: progress_state.status,
2289 progress: progress_state.progress,
2290 desktop_filename: Some(format!("{}.desktop", self.package_info().name)),
2291 })
2292 .map_err(Into::into)
2293 }
2294
2295 pub fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> crate::Result<()> {
2297 self
2298 .window
2299 .dispatcher
2300 .set_title_bar_style(style)
2301 .map_err(Into::into)
2302 }
2303}
2304
2305#[cfg(desktop)]
2307#[cfg_attr(
2308 docsrs,
2309 doc(cfg(any(target_os = "macos", target_os = "linux", windows)))
2310)]
2311#[derive(serde::Deserialize, Debug)]
2312pub struct ProgressBarState {
2313 pub status: Option<ProgressBarStatus>,
2315 pub progress: Option<u64>,
2317}
2318
2319impl<R: Runtime> Listener<R> for Window<R> {
2320 #[cfg_attr(
2324 feature = "unstable",
2325 doc = r####"
2326```
2327use tauri::{Manager, Listener};
2328
2329tauri::Builder::default()
2330 .setup(|app| {
2331 let window = app.get_window("main").unwrap();
2332 window.listen("component-loaded", move |event| {
2333 println!("window just loaded a component");
2334 });
2335
2336 Ok(())
2337 });
2338```
2339 "####
2340 )]
2341 fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
2342 where
2343 F: Fn(Event) + Send + 'static,
2344 {
2345 let event = EventName::new(event.into()).unwrap();
2346 self.manager.listen(
2347 event,
2348 EventTarget::Window {
2349 label: self.label().to_string(),
2350 },
2351 handler,
2352 )
2353 }
2354
2355 fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
2359 where
2360 F: FnOnce(Event) + Send + 'static,
2361 {
2362 let event = EventName::new(event.into()).unwrap();
2363 self.manager.once(
2364 event,
2365 EventTarget::Window {
2366 label: self.label().to_string(),
2367 },
2368 handler,
2369 )
2370 }
2371
2372 #[cfg_attr(
2376 feature = "unstable",
2377 doc = r####"
2378```
2379use tauri::{Manager, Listener};
2380
2381tauri::Builder::default()
2382 .setup(|app| {
2383 let window = app.get_window("main").unwrap();
2384 let window_ = window.clone();
2385 let handler = window.listen("component-loaded", move |event| {
2386 println!("window just loaded a component");
2387
2388 // we no longer need to listen to the event
2389 // we also could have used `window.once` instead
2390 window_.unlisten(event.id());
2391 });
2392
2393 // stop listening to the event when you do not need it anymore
2394 window.unlisten(handler);
2395
2396 Ok(())
2397 });
2398```
2399 "####
2400 )]
2401 fn unlisten(&self, id: EventId) {
2402 self.manager.unlisten(id)
2403 }
2404}
2405
2406impl<R: Runtime> Emitter<R> for Window<R> {}
2407
2408#[derive(Default)]
2410pub struct EffectsBuilder(WindowEffectsConfig);
2411impl EffectsBuilder {
2412 pub fn new() -> Self {
2414 Self(WindowEffectsConfig::default())
2415 }
2416
2417 pub fn effect(mut self, effect: Effect) -> Self {
2419 self.0.effects.push(effect);
2420 self
2421 }
2422
2423 pub fn effects<I: IntoIterator<Item = Effect>>(mut self, effects: I) -> Self {
2425 self.0.effects.extend(effects);
2426 self
2427 }
2428
2429 pub fn clear_effects(mut self) -> Self {
2431 self.0.effects.clear();
2432 self
2433 }
2434
2435 pub fn state(mut self, state: EffectState) -> Self {
2437 self.0.state = Some(state);
2438 self
2439 }
2440 pub fn radius(mut self, radius: f64) -> Self {
2442 self.0.radius = Some(radius);
2443 self
2444 }
2445 pub fn color(mut self, color: Color) -> Self {
2447 self.0.color = Some(color);
2448 self
2449 }
2450
2451 pub fn build(self) -> WindowEffectsConfig {
2453 self.0
2454 }
2455}
2456
2457impl From<WindowEffectsConfig> for EffectsBuilder {
2458 fn from(value: WindowEffectsConfig) -> Self {
2459 Self(value)
2460 }
2461}
2462
2463#[cfg(test)]
2464mod tests {
2465 #[test]
2466 fn window_is_send_sync() {
2467 crate::test_utils::assert_send::<super::Window>();
2468 crate::test_utils::assert_sync::<super::Window>();
2469 }
2470}