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(not(target_os = "macos"))]
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<()> {
1360 #[cfg(not(target_os = "macos"))]
1362 if let Some(window_menu) = &*self.menu_lock() {
1363 let window = self.clone();
1364 let menu_ = window_menu.menu.clone();
1365 self.run_on_main_thread(move || {
1366 #[cfg(windows)]
1367 if let Ok(hwnd) = window.hwnd() {
1368 let _ = unsafe { menu_.inner().hide_for_hwnd(hwnd.0 as _) };
1369 }
1370 #[cfg(any(
1371 target_os = "linux",
1372 target_os = "dragonfly",
1373 target_os = "freebsd",
1374 target_os = "netbsd",
1375 target_os = "openbsd"
1376 ))]
1377 if let Ok(gtk_window) = window.gtk_window() {
1378 let _ = menu_.inner().hide_for_gtk_window(>k_window);
1379 }
1380 })?;
1381 }
1382
1383 Ok(())
1384 }
1385
1386 pub fn show_menu(&self) -> crate::Result<()> {
1392 #[cfg(not(target_os = "macos"))]
1394 if let Some(window_menu) = &*self.menu_lock() {
1395 let window = self.clone();
1396 let menu_ = window_menu.menu.clone();
1397 self.run_on_main_thread(move || {
1398 #[cfg(windows)]
1399 if let Ok(hwnd) = window.hwnd() {
1400 let _ = unsafe { menu_.inner().show_for_hwnd(hwnd.0 as _) };
1401 }
1402 #[cfg(any(
1403 target_os = "linux",
1404 target_os = "dragonfly",
1405 target_os = "freebsd",
1406 target_os = "netbsd",
1407 target_os = "openbsd"
1408 ))]
1409 if let Ok(gtk_window) = window.gtk_window() {
1410 let _ = menu_.inner().show_for_gtk_window(>k_window);
1411 }
1412 })?;
1413 }
1414
1415 Ok(())
1416 }
1417
1418 pub fn is_menu_visible(&self) -> crate::Result<bool> {
1424 #[cfg(not(target_os = "macos"))]
1426 if let Some(window_menu) = &*self.menu_lock() {
1427 let (tx, rx) = std::sync::mpsc::channel();
1428 let window = self.clone();
1429 let menu_ = window_menu.menu.clone();
1430 self.run_on_main_thread(move || {
1431 #[cfg(windows)]
1432 if let Ok(hwnd) = window.hwnd() {
1433 let _ = tx.send(unsafe { menu_.inner().is_visible_on_hwnd(hwnd.0 as _) });
1434 }
1435 #[cfg(any(
1436 target_os = "linux",
1437 target_os = "dragonfly",
1438 target_os = "freebsd",
1439 target_os = "netbsd",
1440 target_os = "openbsd"
1441 ))]
1442 if let Ok(gtk_window) = window.gtk_window() {
1443 let _ = tx.send(menu_.inner().is_visible_on_gtk_window(>k_window));
1444 }
1445 })?;
1446
1447 return Ok(rx.recv().unwrap_or(false));
1448 }
1449
1450 Ok(false)
1451 }
1452
1453 pub fn popup_menu<M: ContextMenu>(&self, menu: &M) -> crate::Result<()> {
1455 menu.popup(self.clone())
1456 }
1457
1458 pub fn popup_menu_at<M: ContextMenu, P: Into<Position>>(
1462 &self,
1463 menu: &M,
1464 position: P,
1465 ) -> crate::Result<()> {
1466 menu.popup_at(self.clone(), position)
1467 }
1468}
1469
1470impl<R: Runtime> Window<R> {
1472 pub fn scale_factor(&self) -> crate::Result<f64> {
1474 self.window.dispatcher.scale_factor().map_err(Into::into)
1475 }
1476
1477 pub fn inner_position(&self) -> crate::Result<PhysicalPosition<i32>> {
1479 self.window.dispatcher.inner_position().map_err(Into::into)
1480 }
1481
1482 pub fn outer_position(&self) -> crate::Result<PhysicalPosition<i32>> {
1484 self.window.dispatcher.outer_position().map_err(Into::into)
1485 }
1486
1487 pub fn inner_size(&self) -> crate::Result<PhysicalSize<u32>> {
1491 self.window.dispatcher.inner_size().map_err(Into::into)
1492 }
1493
1494 pub fn outer_size(&self) -> crate::Result<PhysicalSize<u32>> {
1498 self.window.dispatcher.outer_size().map_err(Into::into)
1499 }
1500
1501 pub fn is_fullscreen(&self) -> crate::Result<bool> {
1503 self.window.dispatcher.is_fullscreen().map_err(Into::into)
1504 }
1505
1506 pub fn is_minimized(&self) -> crate::Result<bool> {
1508 self.window.dispatcher.is_minimized().map_err(Into::into)
1509 }
1510
1511 pub fn is_maximized(&self) -> crate::Result<bool> {
1513 self.window.dispatcher.is_maximized().map_err(Into::into)
1514 }
1515
1516 pub fn is_focused(&self) -> crate::Result<bool> {
1518 self.window.dispatcher.is_focused().map_err(Into::into)
1519 }
1520
1521 pub fn is_decorated(&self) -> crate::Result<bool> {
1523 self.window.dispatcher.is_decorated().map_err(Into::into)
1524 }
1525
1526 pub fn is_resizable(&self) -> crate::Result<bool> {
1528 self.window.dispatcher.is_resizable().map_err(Into::into)
1529 }
1530
1531 pub fn is_enabled(&self) -> crate::Result<bool> {
1533 self.window.dispatcher.is_enabled().map_err(Into::into)
1534 }
1535
1536 pub fn is_always_on_top(&self) -> crate::Result<bool> {
1542 self
1543 .window
1544 .dispatcher
1545 .is_always_on_top()
1546 .map_err(Into::into)
1547 }
1548
1549 pub fn is_maximizable(&self) -> crate::Result<bool> {
1555 self.window.dispatcher.is_maximizable().map_err(Into::into)
1556 }
1557
1558 pub fn is_minimizable(&self) -> crate::Result<bool> {
1564 self.window.dispatcher.is_minimizable().map_err(Into::into)
1565 }
1566
1567 pub fn is_closable(&self) -> crate::Result<bool> {
1573 self.window.dispatcher.is_closable().map_err(Into::into)
1574 }
1575
1576 pub fn is_visible(&self) -> crate::Result<bool> {
1578 self.window.dispatcher.is_visible().map_err(Into::into)
1579 }
1580
1581 pub fn title(&self) -> crate::Result<String> {
1583 self.window.dispatcher.title().map_err(Into::into)
1584 }
1585
1586 pub fn current_monitor(&self) -> crate::Result<Option<Monitor>> {
1590 self
1591 .window
1592 .dispatcher
1593 .current_monitor()
1594 .map(|m| m.map(Into::into))
1595 .map_err(Into::into)
1596 }
1597
1598 pub fn monitor_from_point(&self, x: f64, y: f64) -> crate::Result<Option<Monitor>> {
1600 self
1601 .window
1602 .dispatcher
1603 .monitor_from_point(x, y)
1604 .map(|m| m.map(Into::into))
1605 .map_err(Into::into)
1606 }
1607
1608 pub fn primary_monitor(&self) -> crate::Result<Option<Monitor>> {
1612 self
1613 .window
1614 .dispatcher
1615 .primary_monitor()
1616 .map(|m| m.map(Into::into))
1617 .map_err(Into::into)
1618 }
1619
1620 pub fn available_monitors(&self) -> crate::Result<Vec<Monitor>> {
1622 self
1623 .window
1624 .dispatcher
1625 .available_monitors()
1626 .map(|m| m.into_iter().map(Into::into).collect())
1627 .map_err(Into::into)
1628 }
1629
1630 #[cfg(target_os = "macos")]
1632 pub fn ns_window(&self) -> crate::Result<*mut std::ffi::c_void> {
1633 self
1634 .window
1635 .dispatcher
1636 .window_handle()
1637 .map_err(Into::into)
1638 .and_then(|handle| {
1639 if let raw_window_handle::RawWindowHandle::AppKit(h) = handle.as_raw() {
1640 let view: &objc2_app_kit::NSView = unsafe { h.ns_view.cast().as_ref() };
1641 let ns_window = view.window().expect("view to be installed in window");
1642 Ok(objc2::rc::Retained::autorelease_ptr(ns_window).cast())
1643 } else {
1644 Err(crate::Error::InvalidWindowHandle)
1645 }
1646 })
1647 }
1648
1649 #[cfg(target_os = "macos")]
1651 pub fn ns_view(&self) -> crate::Result<*mut std::ffi::c_void> {
1652 self
1653 .window
1654 .dispatcher
1655 .window_handle()
1656 .map_err(Into::into)
1657 .and_then(|handle| {
1658 if let raw_window_handle::RawWindowHandle::AppKit(h) = handle.as_raw() {
1659 Ok(h.ns_view.as_ptr())
1660 } else {
1661 Err(crate::Error::InvalidWindowHandle)
1662 }
1663 })
1664 }
1665
1666 #[cfg(windows)]
1668 pub fn hwnd(&self) -> crate::Result<HWND> {
1669 self
1670 .window
1671 .dispatcher
1672 .window_handle()
1673 .map_err(Into::into)
1674 .and_then(|handle| {
1675 if let raw_window_handle::RawWindowHandle::Win32(h) = handle.as_raw() {
1676 Ok(HWND(h.hwnd.get() as _))
1677 } else {
1678 Err(crate::Error::InvalidWindowHandle)
1679 }
1680 })
1681 }
1682
1683 #[cfg(any(
1687 target_os = "linux",
1688 target_os = "dragonfly",
1689 target_os = "freebsd",
1690 target_os = "netbsd",
1691 target_os = "openbsd"
1692 ))]
1693 pub fn gtk_window(&self) -> crate::Result<gtk::ApplicationWindow> {
1694 self.window.dispatcher.gtk_window().map_err(Into::into)
1695 }
1696
1697 #[cfg(any(
1701 target_os = "linux",
1702 target_os = "dragonfly",
1703 target_os = "freebsd",
1704 target_os = "netbsd",
1705 target_os = "openbsd"
1706 ))]
1707 pub fn default_vbox(&self) -> crate::Result<gtk::Box> {
1708 self.window.dispatcher.default_vbox().map_err(Into::into)
1709 }
1710
1711 #[cfg(target_os = "android")]
1713 pub fn activity_name(&self) -> crate::Result<String> {
1714 self.window.dispatcher.activity_name().map_err(Into::into)
1715 }
1716
1717 #[cfg(target_os = "ios")]
1719 pub fn scene_identifier(&self) -> crate::Result<String> {
1720 self
1721 .window
1722 .dispatcher
1723 .scene_identifier()
1724 .map_err(Into::into)
1725 }
1726
1727 pub fn theme(&self) -> crate::Result<Theme> {
1733 self.window.dispatcher.theme().map_err(Into::into)
1734 }
1735}
1736
1737#[cfg(desktop)]
1739impl<R: Runtime> Window<R> {
1740 pub fn cursor_position(&self) -> crate::Result<PhysicalPosition<f64>> {
1749 self.app_handle.cursor_position()
1750 }
1751}
1752
1753impl<R: Runtime> Window<R> {
1755 pub fn set_resizable(&self, resizable: bool) -> crate::Result<()> {
1758 self
1759 .window
1760 .dispatcher
1761 .set_resizable(resizable)
1762 .map_err(Into::into)
1763 }
1764
1765 pub fn set_title(&self, title: &str) -> crate::Result<()> {
1767 self
1768 .window
1769 .dispatcher
1770 .set_title(title.to_string())
1771 .map_err(Into::into)
1772 }
1773
1774 pub fn set_enabled(&self, enabled: bool) -> crate::Result<()> {
1776 self
1777 .window
1778 .dispatcher
1779 .set_enabled(enabled)
1780 .map_err(Into::into)
1781 }
1782
1783 pub fn show(&self) -> crate::Result<()> {
1785 self.window.dispatcher.show().map_err(Into::into)
1786 }
1787
1788 pub fn hide(&self) -> crate::Result<()> {
1790 self.window.dispatcher.hide().map_err(Into::into)
1791 }
1792
1793 pub fn close(&self) -> crate::Result<()> {
1795 self.window.dispatcher.close().map_err(Into::into)
1796 }
1797
1798 pub fn destroy(&self) -> crate::Result<()> {
1800 self.window.dispatcher.destroy().map_err(Into::into)
1801 }
1802
1803 pub fn set_background_color(&self, color: Option<Color>) -> crate::Result<()> {
1810 self
1811 .window
1812 .dispatcher
1813 .set_background_color(color)
1814 .map_err(Into::into)
1815 }
1816
1817 pub fn set_content_protected(&self, protected: bool) -> crate::Result<()> {
1819 self
1820 .window
1821 .dispatcher
1822 .set_content_protected(protected)
1823 .map_err(Into::into)
1824 }
1825
1826 pub fn set_size<S: Into<Size>>(&self, size: S) -> crate::Result<()> {
1828 self
1829 .window
1830 .dispatcher
1831 .set_size(size.into())
1832 .map_err(Into::into)
1833 }
1834
1835 pub fn set_min_size<S: Into<Size>>(&self, size: Option<S>) -> crate::Result<()> {
1837 self
1838 .window
1839 .dispatcher
1840 .set_min_size(size.map(|s| s.into()))
1841 .map_err(Into::into)
1842 }
1843
1844 pub fn set_max_size<S: Into<Size>>(&self, size: Option<S>) -> crate::Result<()> {
1846 self
1847 .window
1848 .dispatcher
1849 .set_max_size(size.map(|s| s.into()))
1850 .map_err(Into::into)
1851 }
1852
1853 pub fn set_size_constraints(
1855 &self,
1856 constraints: tauri_runtime::window::WindowSizeConstraints,
1857 ) -> crate::Result<()> {
1858 self
1859 .window
1860 .dispatcher
1861 .set_size_constraints(constraints)
1862 .map_err(Into::into)
1863 }
1864
1865 pub fn set_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
1867 self
1868 .window
1869 .dispatcher
1870 .set_position(position.into())
1871 .map_err(Into::into)
1872 }
1873
1874 pub fn set_focus(&self) -> crate::Result<()> {
1876 self.window.dispatcher.set_focus().map_err(Into::into)
1877 }
1878
1879 pub fn set_focusable(&self, focusable: bool) -> crate::Result<()> {
1886 self
1887 .window
1888 .dispatcher
1889 .set_focusable(focusable)
1890 .map_err(Into::into)
1891 }
1892
1893 pub fn set_theme(&self, theme: Option<Theme>) -> crate::Result<()> {
1900 self
1901 .window
1902 .dispatcher
1903 .set_theme(theme)
1904 .map_err(Into::<crate::Error>::into)?;
1905 #[cfg(windows)]
1906 if let (Some(menu), Ok(hwnd)) = (self.menu(), self.hwnd()) {
1907 let raw_hwnd = hwnd.0 as isize;
1908 self.run_on_main_thread(move || {
1909 let _ = unsafe {
1910 menu.inner().set_theme_for_hwnd(
1911 raw_hwnd,
1912 theme
1913 .map(crate::menu::map_to_menu_theme)
1914 .unwrap_or(muda::MenuTheme::Auto),
1915 )
1916 };
1917 })?;
1918 };
1919 Ok(())
1920 }
1921}
1922
1923#[cfg(desktop)]
1925impl<R: Runtime> Window<R> {
1926 pub fn center(&self) -> crate::Result<()> {
1928 self.window.dispatcher.center().map_err(Into::into)
1929 }
1930
1931 pub fn request_user_attention(
1943 &self,
1944 request_type: Option<UserAttentionType>,
1945 ) -> crate::Result<()> {
1946 self
1947 .window
1948 .dispatcher
1949 .request_user_attention(request_type)
1950 .map_err(Into::into)
1951 }
1952
1953 pub fn set_maximizable(&self, maximizable: bool) -> crate::Result<()> {
1961 self
1962 .window
1963 .dispatcher
1964 .set_maximizable(maximizable)
1965 .map_err(Into::into)
1966 }
1967
1968 pub fn set_minimizable(&self, minimizable: bool) -> crate::Result<()> {
1974 self
1975 .window
1976 .dispatcher
1977 .set_minimizable(minimizable)
1978 .map_err(Into::into)
1979 }
1980
1981 pub fn set_closable(&self, closable: bool) -> crate::Result<()> {
1989 self
1990 .window
1991 .dispatcher
1992 .set_closable(closable)
1993 .map_err(Into::into)
1994 }
1995
1996 pub fn maximize(&self) -> crate::Result<()> {
1998 self.window.dispatcher.maximize().map_err(Into::into)
1999 }
2000
2001 pub fn unmaximize(&self) -> crate::Result<()> {
2003 self.window.dispatcher.unmaximize().map_err(Into::into)
2004 }
2005
2006 pub fn minimize(&self) -> crate::Result<()> {
2008 self.window.dispatcher.minimize().map_err(Into::into)
2009 }
2010
2011 pub fn unminimize(&self) -> crate::Result<()> {
2013 self.window.dispatcher.unminimize().map_err(Into::into)
2014 }
2015
2016 pub fn set_decorations(&self, decorations: bool) -> crate::Result<()> {
2020 self
2021 .window
2022 .dispatcher
2023 .set_decorations(decorations)
2024 .map_err(Into::into)
2025 }
2026
2027 pub fn set_shadow(&self, enable: bool) -> crate::Result<()> {
2037 self
2038 .window
2039 .dispatcher
2040 .set_shadow(enable)
2041 .map_err(Into::into)
2042 }
2043
2044 #[cfg_attr(
2051 feature = "unstable",
2052 doc = r####"
2053```rust,no_run
2054use tauri::{Manager, window::{Color, Effect, EffectState, EffectsBuilder}};
2055tauri::Builder::default()
2056 .setup(|app| {
2057 let window = app.get_window("main").unwrap();
2058 window.set_effects(
2059 EffectsBuilder::new()
2060 .effect(Effect::Popover)
2061 .state(EffectState::Active)
2062 .radius(5.)
2063 .color(Color(0, 0, 0, 255))
2064 .build(),
2065 )?;
2066 Ok(())
2067 });
2068```
2069 "####
2070 )]
2071 pub fn set_effects<E: Into<Option<WindowEffectsConfig>>>(&self, effects: E) -> crate::Result<()> {
2077 let effects = effects.into();
2078 let window = self.clone();
2079 self.run_on_main_thread(move || {
2080 let _ = crate::vibrancy::set_window_effects(&window, effects);
2081 })
2082 }
2083
2084 pub fn set_always_on_bottom(&self, always_on_bottom: bool) -> crate::Result<()> {
2086 self
2087 .window
2088 .dispatcher
2089 .set_always_on_bottom(always_on_bottom)
2090 .map_err(Into::into)
2091 }
2092
2093 pub fn set_always_on_top(&self, always_on_top: bool) -> crate::Result<()> {
2095 self
2096 .window
2097 .dispatcher
2098 .set_always_on_top(always_on_top)
2099 .map_err(Into::into)
2100 }
2101
2102 pub fn set_visible_on_all_workspaces(
2108 &self,
2109 visible_on_all_workspaces: bool,
2110 ) -> crate::Result<()> {
2111 self
2112 .window
2113 .dispatcher
2114 .set_visible_on_all_workspaces(visible_on_all_workspaces)
2115 .map_err(Into::into)
2116 }
2117
2118 pub fn set_fullscreen(&self, fullscreen: bool) -> crate::Result<()> {
2120 self
2121 .window
2122 .dispatcher
2123 .set_fullscreen(fullscreen)
2124 .map_err(Into::into)
2125 }
2126
2127 pub fn set_simple_fullscreen(&self, enable: bool) -> crate::Result<()> {
2138 #[cfg(target_os = "macos")]
2139 {
2140 self
2141 .window
2142 .dispatcher
2143 .set_simple_fullscreen(enable)
2144 .map_err(Into::into)
2145 }
2146 #[cfg(not(target_os = "macos"))]
2147 self.set_fullscreen(enable)
2148 }
2149
2150 pub fn set_icon(&self, icon: Image<'_>) -> crate::Result<()> {
2152 self
2153 .window
2154 .dispatcher
2155 .set_icon(icon.into())
2156 .map_err(Into::into)
2157 }
2158
2159 pub fn set_skip_taskbar(&self, skip: bool) -> crate::Result<()> {
2165 self
2166 .window
2167 .dispatcher
2168 .set_skip_taskbar(skip)
2169 .map_err(Into::into)
2170 }
2171
2172 pub fn set_cursor_grab(&self, grab: bool) -> crate::Result<()> {
2182 self
2183 .window
2184 .dispatcher
2185 .set_cursor_grab(grab)
2186 .map_err(Into::into)
2187 }
2188
2189 pub fn set_cursor_visible(&self, visible: bool) -> crate::Result<()> {
2199 self
2200 .window
2201 .dispatcher
2202 .set_cursor_visible(visible)
2203 .map_err(Into::into)
2204 }
2205
2206 pub fn set_cursor_icon(&self, icon: CursorIcon) -> crate::Result<()> {
2208 self
2209 .window
2210 .dispatcher
2211 .set_cursor_icon(icon)
2212 .map_err(Into::into)
2213 }
2214
2215 pub fn set_cursor_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
2217 self
2218 .window
2219 .dispatcher
2220 .set_cursor_position(position)
2221 .map_err(Into::into)
2222 }
2223
2224 pub fn set_ignore_cursor_events(&self, ignore: bool) -> crate::Result<()> {
2226 self
2227 .window
2228 .dispatcher
2229 .set_ignore_cursor_events(ignore)
2230 .map_err(Into::into)
2231 }
2232
2233 pub fn start_dragging(&self) -> crate::Result<()> {
2235 self.window.dispatcher.start_dragging().map_err(Into::into)
2236 }
2237
2238 pub fn start_resize_dragging(
2240 &self,
2241 direction: tauri_runtime::ResizeDirection,
2242 ) -> crate::Result<()> {
2243 self
2244 .window
2245 .dispatcher
2246 .start_resize_dragging(direction)
2247 .map_err(Into::into)
2248 }
2249
2250 #[cfg(target_os = "windows")]
2254 #[cfg_attr(docsrs, doc(cfg(target_os = "windows")))]
2255 pub fn set_overlay_icon(&self, icon: Option<Image<'_>>) -> crate::Result<()> {
2256 self
2257 .window
2258 .dispatcher
2259 .set_overlay_icon(icon.map(|x| x.into()))
2260 .map_err(Into::into)
2261 }
2262
2263 pub fn set_badge_count(&self, count: Option<i64>) -> crate::Result<()> {
2270 self
2271 .window
2272 .dispatcher
2273 .set_badge_count(count, Some(format!("{}.desktop", self.package_info().name)))
2274 .map_err(Into::into)
2275 }
2276
2277 #[cfg(target_os = "macos")]
2279 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
2280 pub fn set_badge_label(&self, label: Option<String>) -> crate::Result<()> {
2281 self
2282 .window
2283 .dispatcher
2284 .set_badge_label(label)
2285 .map_err(Into::into)
2286 }
2287
2288 pub fn set_progress_bar(&self, progress_state: ProgressBarState) -> crate::Result<()> {
2296 self
2297 .window
2298 .dispatcher
2299 .set_progress_bar(crate::runtime::ProgressBarState {
2300 status: progress_state.status,
2301 progress: progress_state.progress,
2302 desktop_filename: Some(format!("{}.desktop", self.package_info().name)),
2303 })
2304 .map_err(Into::into)
2305 }
2306
2307 pub fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> crate::Result<()> {
2309 self
2310 .window
2311 .dispatcher
2312 .set_title_bar_style(style)
2313 .map_err(Into::into)
2314 }
2315}
2316
2317#[cfg(desktop)]
2319#[cfg_attr(
2320 docsrs,
2321 doc(cfg(any(target_os = "macos", target_os = "linux", windows)))
2322)]
2323#[derive(serde::Deserialize, Debug)]
2324pub struct ProgressBarState {
2325 pub status: Option<ProgressBarStatus>,
2327 pub progress: Option<u64>,
2329}
2330
2331impl<R: Runtime> Listener<R> for Window<R> {
2332 #[cfg_attr(
2336 feature = "unstable",
2337 doc = r####"
2338```
2339use tauri::{Manager, Listener};
2340
2341tauri::Builder::default()
2342 .setup(|app| {
2343 let window = app.get_window("main").unwrap();
2344 window.listen("component-loaded", move |event| {
2345 println!("window just loaded a component");
2346 });
2347
2348 Ok(())
2349 });
2350```
2351 "####
2352 )]
2353 fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
2354 where
2355 F: Fn(Event) + Send + 'static,
2356 {
2357 let event = EventName::new(event.into()).unwrap();
2358 self.manager.listen(
2359 event,
2360 EventTarget::Window {
2361 label: self.label().to_string(),
2362 },
2363 handler,
2364 )
2365 }
2366
2367 fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
2371 where
2372 F: FnOnce(Event) + Send + 'static,
2373 {
2374 let event = EventName::new(event.into()).unwrap();
2375 self.manager.once(
2376 event,
2377 EventTarget::Window {
2378 label: self.label().to_string(),
2379 },
2380 handler,
2381 )
2382 }
2383
2384 #[cfg_attr(
2388 feature = "unstable",
2389 doc = r####"
2390```
2391use tauri::{Manager, Listener};
2392
2393tauri::Builder::default()
2394 .setup(|app| {
2395 let window = app.get_window("main").unwrap();
2396 let window_ = window.clone();
2397 let handler = window.listen("component-loaded", move |event| {
2398 println!("window just loaded a component");
2399
2400 // we no longer need to listen to the event
2401 // we also could have used `window.once` instead
2402 window_.unlisten(event.id());
2403 });
2404
2405 // stop listening to the event when you do not need it anymore
2406 window.unlisten(handler);
2407
2408 Ok(())
2409 });
2410```
2411 "####
2412 )]
2413 fn unlisten(&self, id: EventId) {
2414 self.manager.unlisten(id)
2415 }
2416}
2417
2418impl<R: Runtime> Emitter<R> for Window<R> {}
2419
2420#[derive(Default)]
2422pub struct EffectsBuilder(WindowEffectsConfig);
2423impl EffectsBuilder {
2424 pub fn new() -> Self {
2426 Self(WindowEffectsConfig::default())
2427 }
2428
2429 pub fn effect(mut self, effect: Effect) -> Self {
2431 self.0.effects.push(effect);
2432 self
2433 }
2434
2435 pub fn effects<I: IntoIterator<Item = Effect>>(mut self, effects: I) -> Self {
2437 self.0.effects.extend(effects);
2438 self
2439 }
2440
2441 pub fn clear_effects(mut self) -> Self {
2443 self.0.effects.clear();
2444 self
2445 }
2446
2447 pub fn state(mut self, state: EffectState) -> Self {
2449 self.0.state = Some(state);
2450 self
2451 }
2452 pub fn radius(mut self, radius: f64) -> Self {
2454 self.0.radius = Some(radius);
2455 self
2456 }
2457 pub fn color(mut self, color: Color) -> Self {
2459 self.0.color = Some(color);
2460 self
2461 }
2462
2463 pub fn build(self) -> WindowEffectsConfig {
2465 self.0
2466 }
2467}
2468
2469impl From<WindowEffectsConfig> for EffectsBuilder {
2470 fn from(value: WindowEffectsConfig) -> Self {
2471 Self(value)
2472 }
2473}
2474
2475#[cfg(test)]
2476mod tests {
2477 #[test]
2478 fn window_is_send_sync() {
2479 crate::test_utils::assert_send::<super::Window>();
2480 crate::test_utils::assert_sync::<super::Window>();
2481 }
2482}