1use crate::{
8 webview::{DetachedWebview, PendingWebview},
9 Icon, Runtime, UserEvent, WindowDispatch,
10};
11
12use dpi::PixelUnit;
13use serde::{Deserialize, Deserializer, Serialize};
14use tauri_utils::{
15 config::{Color, WindowConfig},
16 Theme,
17};
18#[cfg(windows)]
19use windows::Win32::Foundation::HWND;
20
21use std::{
22 hash::{Hash, Hasher},
23 marker::PhantomData,
24 path::PathBuf,
25 sync::mpsc::Sender,
26};
27
28#[derive(Debug, Clone)]
30pub enum WindowEvent {
31 Resized(dpi::PhysicalSize<u32>),
33 Moved(dpi::PhysicalPosition<i32>),
35 CloseRequested {
37 signal_tx: Sender<bool>,
39 },
40 Destroyed,
42 Focused(bool),
46 ScaleFactorChanged {
54 scale_factor: f64,
56 new_inner_size: dpi::PhysicalSize<u32>,
58 },
59 DragDrop(DragDropEvent),
61 ThemeChanged(Theme),
65}
66
67#[derive(Debug, Clone)]
69pub enum WebviewEvent {
70 DragDrop(DragDropEvent),
72}
73
74#[derive(Debug, Clone)]
76#[non_exhaustive]
77pub enum DragDropEvent {
78 Enter {
80 paths: Vec<PathBuf>,
82 position: dpi::PhysicalPosition<f64>,
84 },
85 Over {
87 position: dpi::PhysicalPosition<f64>,
89 },
90 Drop {
92 paths: Vec<PathBuf>,
94 position: dpi::PhysicalPosition<f64>,
96 },
97 Leave,
99}
100
101#[non_exhaustive]
103#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
104pub enum CursorIcon {
105 #[default]
107 Default,
108 Crosshair,
110 Hand,
112 Arrow,
114 Move,
116 Text,
118 Wait,
120 Help,
122 Progress,
126
127 NotAllowed,
129 ContextMenu,
130 Cell,
131 VerticalText,
132 Alias,
133 Copy,
134 NoDrop,
135 Grab,
137 Grabbing,
139 AllScroll,
140 ZoomIn,
141 ZoomOut,
142
143 EResize,
146 NResize,
147 NeResize,
148 NwResize,
149 SResize,
150 SeResize,
151 SwResize,
152 WResize,
153 EwResize,
154 NsResize,
155 NeswResize,
156 NwseResize,
157 ColResize,
158 RowResize,
159}
160
161impl<'de> Deserialize<'de> for CursorIcon {
162 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
163 where
164 D: Deserializer<'de>,
165 {
166 let s = String::deserialize(deserializer)?;
167 Ok(match s.to_lowercase().as_str() {
168 "default" => CursorIcon::Default,
169 "crosshair" => CursorIcon::Crosshair,
170 "hand" => CursorIcon::Hand,
171 "arrow" => CursorIcon::Arrow,
172 "move" => CursorIcon::Move,
173 "text" => CursorIcon::Text,
174 "wait" => CursorIcon::Wait,
175 "help" => CursorIcon::Help,
176 "progress" => CursorIcon::Progress,
177 "notallowed" => CursorIcon::NotAllowed,
178 "contextmenu" => CursorIcon::ContextMenu,
179 "cell" => CursorIcon::Cell,
180 "verticaltext" => CursorIcon::VerticalText,
181 "alias" => CursorIcon::Alias,
182 "copy" => CursorIcon::Copy,
183 "nodrop" => CursorIcon::NoDrop,
184 "grab" => CursorIcon::Grab,
185 "grabbing" => CursorIcon::Grabbing,
186 "allscroll" => CursorIcon::AllScroll,
187 "zoomin" => CursorIcon::ZoomIn,
188 "zoomout" => CursorIcon::ZoomOut,
189 "eresize" => CursorIcon::EResize,
190 "nresize" => CursorIcon::NResize,
191 "neresize" => CursorIcon::NeResize,
192 "nwresize" => CursorIcon::NwResize,
193 "sresize" => CursorIcon::SResize,
194 "seresize" => CursorIcon::SeResize,
195 "swresize" => CursorIcon::SwResize,
196 "wresize" => CursorIcon::WResize,
197 "ewresize" => CursorIcon::EwResize,
198 "nsresize" => CursorIcon::NsResize,
199 "neswresize" => CursorIcon::NeswResize,
200 "nwseresize" => CursorIcon::NwseResize,
201 "colresize" => CursorIcon::ColResize,
202 "rowresize" => CursorIcon::RowResize,
203 _ => CursorIcon::Default,
204 })
205 }
206}
207
208#[derive(Clone, Copy, PartialEq, Debug, Default, Serialize, Deserialize)]
210#[serde(rename_all = "camelCase")]
211pub struct WindowSizeConstraints {
212 pub min_width: Option<PixelUnit>,
216 pub min_height: Option<PixelUnit>,
220 pub max_width: Option<PixelUnit>,
224 pub max_height: Option<PixelUnit>,
228}
229
230pub trait WindowBuilderBase: std::fmt::Debug + Clone + Sized {}
234
235pub trait WindowBuilder: WindowBuilderBase {
240 fn new() -> Self;
242
243 fn with_config(config: &WindowConfig) -> Self;
245
246 #[must_use]
248 fn center(self) -> Self;
249
250 #[must_use]
252 fn position(self, x: f64, y: f64) -> Self;
253
254 #[must_use]
256 fn inner_size(self, width: f64, height: f64) -> Self;
257
258 #[must_use]
260 fn min_inner_size(self, min_width: f64, min_height: f64) -> Self;
261
262 #[must_use]
264 fn max_inner_size(self, max_width: f64, max_height: f64) -> Self;
265
266 #[must_use]
268 fn inner_size_constraints(self, constraints: WindowSizeConstraints) -> Self;
269
270 #[must_use]
273 fn resizable(self, resizable: bool) -> Self;
274
275 #[must_use]
283 fn maximizable(self, maximizable: bool) -> Self;
284
285 #[must_use]
291 fn minimizable(self, minimizable: bool) -> Self;
292
293 #[must_use]
301 fn closable(self, closable: bool) -> Self;
302
303 #[must_use]
305 fn title<S: Into<String>>(self, title: S) -> Self;
306
307 #[must_use]
309 fn fullscreen(self, fullscreen: bool) -> Self;
310
311 #[must_use]
313 fn focused(self, focused: bool) -> Self;
314
315 #[must_use]
317 fn maximized(self, maximized: bool) -> Self;
318
319 #[must_use]
321 fn visible(self, visible: bool) -> Self;
322
323 #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
326 #[cfg_attr(
327 docsrs,
328 doc(cfg(any(not(target_os = "macos"), feature = "macos-private-api")))
329 )]
330 #[must_use]
331 fn transparent(self, transparent: bool) -> Self;
332
333 #[must_use]
335 fn decorations(self, decorations: bool) -> Self;
336
337 #[must_use]
339 fn always_on_bottom(self, always_on_bottom: bool) -> Self;
340
341 #[must_use]
343 fn always_on_top(self, always_on_top: bool) -> Self;
344
345 #[must_use]
347 fn visible_on_all_workspaces(self, visible_on_all_workspaces: bool) -> Self;
348
349 #[must_use]
351 fn content_protected(self, protected: bool) -> Self;
352
353 fn icon(self, icon: Icon) -> crate::Result<Self>;
355
356 #[must_use]
358 fn skip_taskbar(self, skip: bool) -> Self;
359
360 #[must_use]
362 fn background_color(self, color: Color) -> Self;
363
364 #[must_use]
374 fn shadow(self, enable: bool) -> Self;
375
376 #[cfg(windows)]
385 #[must_use]
386 fn owner(self, owner: HWND) -> Self;
387
388 #[cfg(windows)]
394 #[must_use]
395 fn parent(self, parent: HWND) -> Self;
396
397 #[cfg(target_os = "macos")]
401 #[must_use]
402 fn parent(self, parent: *mut std::ffi::c_void) -> Self;
403
404 #[cfg(any(
408 target_os = "linux",
409 target_os = "dragonfly",
410 target_os = "freebsd",
411 target_os = "netbsd",
412 target_os = "openbsd"
413 ))]
414 fn transient_for(self, parent: &impl gtk::glib::IsA<gtk::Window>) -> Self;
415
416 #[cfg(windows)]
418 #[must_use]
419 fn drag_and_drop(self, enabled: bool) -> Self;
420
421 #[cfg(target_os = "macos")]
423 #[must_use]
424 fn title_bar_style(self, style: tauri_utils::TitleBarStyle) -> Self;
425
426 #[cfg(target_os = "macos")]
430 #[must_use]
431 fn traffic_light_position<P: Into<dpi::Position>>(self, position: P) -> Self;
432
433 #[cfg(target_os = "macos")]
435 #[must_use]
436 fn hidden_title(self, hidden: bool) -> Self;
437
438 #[cfg(target_os = "macos")]
445 #[must_use]
446 fn tabbing_identifier(self, identifier: &str) -> Self;
447
448 fn theme(self, theme: Option<Theme>) -> Self;
450
451 fn has_icon(&self) -> bool;
453
454 fn get_theme(&self) -> Option<Theme>;
455
456 #[must_use]
458 fn window_classname<S: Into<String>>(self, window_classname: S) -> Self;
459}
460
461pub struct PendingWindow<T: UserEvent, R: Runtime<T>> {
463 pub label: String,
465
466 pub window_builder: <R::WindowDispatcher as WindowDispatch<T>>::WindowBuilder,
468
469 pub webview: Option<PendingWebview<T, R>>,
471}
472
473pub fn is_label_valid(label: &str) -> bool {
474 label
475 .chars()
476 .all(|c| char::is_alphanumeric(c) || c == '-' || c == '/' || c == ':' || c == '_')
477}
478
479pub fn assert_label_is_valid(label: &str) {
480 assert!(
481 is_label_valid(label),
482 "Window label must include only alphanumeric characters, `-`, `/`, `:` and `_`."
483 );
484}
485
486impl<T: UserEvent, R: Runtime<T>> PendingWindow<T, R> {
487 pub fn new(
489 window_builder: <R::WindowDispatcher as WindowDispatch<T>>::WindowBuilder,
490 label: impl Into<String>,
491 ) -> crate::Result<Self> {
492 let label = label.into();
493 if !is_label_valid(&label) {
494 Err(crate::Error::InvalidWindowLabel)
495 } else {
496 Ok(Self {
497 window_builder,
498 label,
499 webview: None,
500 })
501 }
502 }
503
504 pub fn set_webview(&mut self, webview: PendingWebview<T, R>) -> &mut Self {
506 self.webview.replace(webview);
507 self
508 }
509}
510
511#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
513pub struct WindowId(u32);
514
515impl From<u32> for WindowId {
516 fn from(value: u32) -> Self {
517 Self(value)
518 }
519}
520
521#[derive(Debug)]
523pub struct DetachedWindow<T: UserEvent, R: Runtime<T>> {
524 pub id: WindowId,
526 pub label: String,
528
529 pub dispatcher: R::WindowDispatcher,
531
532 pub webview: Option<DetachedWindowWebview<T, R>>,
534}
535
536#[derive(Debug)]
538pub struct DetachedWindowWebview<T: UserEvent, R: Runtime<T>> {
539 pub webview: DetachedWebview<T, R>,
540 pub use_https_scheme: bool,
541}
542
543impl<T: UserEvent, R: Runtime<T>> Clone for DetachedWindowWebview<T, R> {
544 fn clone(&self) -> Self {
545 Self {
546 webview: self.webview.clone(),
547 use_https_scheme: self.use_https_scheme,
548 }
549 }
550}
551
552impl<T: UserEvent, R: Runtime<T>> Clone for DetachedWindow<T, R> {
553 fn clone(&self) -> Self {
554 Self {
555 id: self.id,
556 label: self.label.clone(),
557 dispatcher: self.dispatcher.clone(),
558 webview: self.webview.clone(),
559 }
560 }
561}
562
563impl<T: UserEvent, R: Runtime<T>> Hash for DetachedWindow<T, R> {
564 fn hash<H: Hasher>(&self, state: &mut H) {
566 self.label.hash(state)
567 }
568}
569
570impl<T: UserEvent, R: Runtime<T>> Eq for DetachedWindow<T, R> {}
571impl<T: UserEvent, R: Runtime<T>> PartialEq for DetachedWindow<T, R> {
572 fn eq(&self, other: &Self) -> bool {
574 self.label.eq(&other.label)
575 }
576}
577
578pub struct RawWindow<'a> {
582 #[cfg(windows)]
583 pub hwnd: isize,
584 #[cfg(any(
585 target_os = "linux",
586 target_os = "dragonfly",
587 target_os = "freebsd",
588 target_os = "netbsd",
589 target_os = "openbsd"
590 ))]
591 pub gtk_window: &'a gtk::ApplicationWindow,
592 #[cfg(any(
593 target_os = "linux",
594 target_os = "dragonfly",
595 target_os = "freebsd",
596 target_os = "netbsd",
597 target_os = "openbsd"
598 ))]
599 pub default_vbox: Option<&'a gtk::Box>,
600 pub _marker: &'a PhantomData<()>,
601}