use crate::{
webview::{DetachedWebview, PendingWebview},
Icon, Runtime, UserEvent, WindowDispatch,
};
use dpi::PixelUnit;
use serde::{Deserialize, Deserializer, Serialize};
use tauri_utils::{config::WindowConfig, Theme};
#[cfg(windows)]
use windows::Win32::Foundation::HWND;
use std::{
hash::{Hash, Hasher},
marker::PhantomData,
path::PathBuf,
sync::mpsc::Sender,
};
#[derive(Debug, Clone)]
pub enum WindowEvent {
Resized(dpi::PhysicalSize<u32>),
Moved(dpi::PhysicalPosition<i32>),
CloseRequested {
signal_tx: Sender<bool>,
},
Destroyed,
Focused(bool),
ScaleFactorChanged {
scale_factor: f64,
new_inner_size: dpi::PhysicalSize<u32>,
},
DragDrop(DragDropEvent),
ThemeChanged(Theme),
}
#[derive(Debug, Clone)]
pub enum WebviewEvent {
DragDrop(DragDropEvent),
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum DragDropEvent {
Enter {
paths: Vec<PathBuf>,
position: dpi::PhysicalPosition<f64>,
},
Over {
position: dpi::PhysicalPosition<f64>,
},
Drop {
paths: Vec<PathBuf>,
position: dpi::PhysicalPosition<f64>,
},
Leave,
}
#[non_exhaustive]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
pub enum CursorIcon {
#[default]
Default,
Crosshair,
Hand,
Arrow,
Move,
Text,
Wait,
Help,
Progress,
NotAllowed,
ContextMenu,
Cell,
VerticalText,
Alias,
Copy,
NoDrop,
Grab,
Grabbing,
AllScroll,
ZoomIn,
ZoomOut,
EResize,
NResize,
NeResize,
NwResize,
SResize,
SeResize,
SwResize,
WResize,
EwResize,
NsResize,
NeswResize,
NwseResize,
ColResize,
RowResize,
}
impl<'de> Deserialize<'de> for CursorIcon {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(match s.to_lowercase().as_str() {
"default" => CursorIcon::Default,
"crosshair" => CursorIcon::Crosshair,
"hand" => CursorIcon::Hand,
"arrow" => CursorIcon::Arrow,
"move" => CursorIcon::Move,
"text" => CursorIcon::Text,
"wait" => CursorIcon::Wait,
"help" => CursorIcon::Help,
"progress" => CursorIcon::Progress,
"notallowed" => CursorIcon::NotAllowed,
"contextmenu" => CursorIcon::ContextMenu,
"cell" => CursorIcon::Cell,
"verticaltext" => CursorIcon::VerticalText,
"alias" => CursorIcon::Alias,
"copy" => CursorIcon::Copy,
"nodrop" => CursorIcon::NoDrop,
"grab" => CursorIcon::Grab,
"grabbing" => CursorIcon::Grabbing,
"allscroll" => CursorIcon::AllScroll,
"zoomin" => CursorIcon::ZoomIn,
"zoomout" => CursorIcon::ZoomOut,
"eresize" => CursorIcon::EResize,
"nresize" => CursorIcon::NResize,
"neresize" => CursorIcon::NeResize,
"nwresize" => CursorIcon::NwResize,
"sresize" => CursorIcon::SResize,
"seresize" => CursorIcon::SeResize,
"swresize" => CursorIcon::SwResize,
"wresize" => CursorIcon::WResize,
"ewresize" => CursorIcon::EwResize,
"nsresize" => CursorIcon::NsResize,
"neswresize" => CursorIcon::NeswResize,
"nwseresize" => CursorIcon::NwseResize,
"colresize" => CursorIcon::ColResize,
"rowresize" => CursorIcon::RowResize,
_ => CursorIcon::Default,
})
}
}
#[derive(Clone, Copy, PartialEq, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WindowSizeConstraints {
pub min_width: Option<PixelUnit>,
pub min_height: Option<PixelUnit>,
pub max_width: Option<PixelUnit>,
pub max_height: Option<PixelUnit>,
}
pub trait WindowBuilderBase: std::fmt::Debug + Clone + Sized {}
pub trait WindowBuilder: WindowBuilderBase {
fn new() -> Self;
fn with_config(config: &WindowConfig) -> Self;
#[must_use]
fn center(self) -> Self;
#[must_use]
fn position(self, x: f64, y: f64) -> Self;
#[must_use]
fn inner_size(self, width: f64, height: f64) -> Self;
#[must_use]
fn min_inner_size(self, min_width: f64, min_height: f64) -> Self;
#[must_use]
fn max_inner_size(self, max_width: f64, max_height: f64) -> Self;
#[must_use]
fn inner_size_constraints(self, constraints: WindowSizeConstraints) -> Self;
#[must_use]
fn resizable(self, resizable: bool) -> Self;
#[must_use]
fn maximizable(self, maximizable: bool) -> Self;
#[must_use]
fn minimizable(self, minimizable: bool) -> Self;
#[must_use]
fn closable(self, closable: bool) -> Self;
#[must_use]
fn title<S: Into<String>>(self, title: S) -> Self;
#[must_use]
fn fullscreen(self, fullscreen: bool) -> Self;
#[must_use]
fn focused(self, focused: bool) -> Self;
#[must_use]
fn maximized(self, maximized: bool) -> Self;
#[must_use]
fn visible(self, visible: bool) -> Self;
#[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
#[cfg_attr(
docsrs,
doc(cfg(any(not(target_os = "macos"), feature = "macos-private-api")))
)]
#[must_use]
fn transparent(self, transparent: bool) -> Self;
#[must_use]
fn decorations(self, decorations: bool) -> Self;
#[must_use]
fn always_on_bottom(self, always_on_bottom: bool) -> Self;
#[must_use]
fn always_on_top(self, always_on_top: bool) -> Self;
#[must_use]
fn visible_on_all_workspaces(self, visible_on_all_workspaces: bool) -> Self;
#[must_use]
fn content_protected(self, protected: bool) -> Self;
fn icon(self, icon: Icon) -> crate::Result<Self>;
#[must_use]
fn skip_taskbar(self, skip: bool) -> Self;
#[must_use]
fn shadow(self, enable: bool) -> Self;
#[cfg(windows)]
#[must_use]
fn owner(self, owner: HWND) -> Self;
#[cfg(windows)]
#[must_use]
fn parent(self, parent: HWND) -> Self;
#[cfg(target_os = "macos")]
#[must_use]
fn parent(self, parent: *mut std::ffi::c_void) -> Self;
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
fn transient_for(self, parent: &impl gtk::glib::IsA<gtk::Window>) -> Self;
#[cfg(windows)]
#[must_use]
fn drag_and_drop(self, enabled: bool) -> Self;
#[cfg(target_os = "macos")]
#[must_use]
fn title_bar_style(self, style: tauri_utils::TitleBarStyle) -> Self;
#[cfg(target_os = "macos")]
#[must_use]
fn hidden_title(self, hidden: bool) -> Self;
#[cfg(target_os = "macos")]
#[must_use]
fn tabbing_identifier(self, identifier: &str) -> Self;
fn theme(self, theme: Option<Theme>) -> Self;
fn has_icon(&self) -> bool;
fn get_theme(&self) -> Option<Theme>;
}
pub struct PendingWindow<T: UserEvent, R: Runtime<T>> {
pub label: String,
pub window_builder: <R::WindowDispatcher as WindowDispatch<T>>::WindowBuilder,
pub webview: Option<PendingWebview<T, R>>,
}
pub fn is_label_valid(label: &str) -> bool {
label
.chars()
.all(|c| char::is_alphanumeric(c) || c == '-' || c == '/' || c == ':' || c == '_')
}
pub fn assert_label_is_valid(label: &str) {
assert!(
is_label_valid(label),
"Window label must include only alphanumeric characters, `-`, `/`, `:` and `_`."
);
}
impl<T: UserEvent, R: Runtime<T>> PendingWindow<T, R> {
pub fn new(
window_builder: <R::WindowDispatcher as WindowDispatch<T>>::WindowBuilder,
label: impl Into<String>,
) -> crate::Result<Self> {
let label = label.into();
if !is_label_valid(&label) {
Err(crate::Error::InvalidWindowLabel)
} else {
Ok(Self {
window_builder,
label,
webview: None,
})
}
}
pub fn set_webview(&mut self, webview: PendingWebview<T, R>) -> &mut Self {
self.webview.replace(webview);
self
}
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct WindowId(u32);
impl From<u32> for WindowId {
fn from(value: u32) -> Self {
Self(value)
}
}
#[derive(Debug)]
pub struct DetachedWindow<T: UserEvent, R: Runtime<T>> {
pub id: WindowId,
pub label: String,
pub dispatcher: R::WindowDispatcher,
pub webview: Option<DetachedWebview<T, R>>,
}
impl<T: UserEvent, R: Runtime<T>> Clone for DetachedWindow<T, R> {
fn clone(&self) -> Self {
Self {
id: self.id,
label: self.label.clone(),
dispatcher: self.dispatcher.clone(),
webview: self.webview.clone(),
}
}
}
impl<T: UserEvent, R: Runtime<T>> Hash for DetachedWindow<T, R> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.label.hash(state)
}
}
impl<T: UserEvent, R: Runtime<T>> Eq for DetachedWindow<T, R> {}
impl<T: UserEvent, R: Runtime<T>> PartialEq for DetachedWindow<T, R> {
fn eq(&self, other: &Self) -> bool {
self.label.eq(&other.label)
}
}
pub struct RawWindow<'a> {
#[cfg(windows)]
pub hwnd: isize,
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
pub gtk_window: &'a gtk::ApplicationWindow,
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
pub default_vbox: Option<&'a gtk::Box>,
pub _marker: &'a PhantomData<()>,
}