diff --git a/Cargo.lock b/Cargo.lock index be0a39ba..e433e4ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -235,7 +235,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi 0.1.19", "libc", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -610,7 +610,7 @@ checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" dependencies = [ "atty", "lazy_static", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -2458,7 +2458,7 @@ checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" dependencies = [ "autocfg", "bitflags", - "cfg-if 1.0.0", + "cfg-if", "libc", "memoffset", "pin-utils", @@ -3032,7 +3032,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22122d5ec4f9fe1b3916419b76be1e80bcb93f618d071d2edf841b137b2a2bd6" dependencies = [ "autocfg", - "cfg-if 1.0.0", + "cfg-if", "libc", "log", "wepoll-ffi", @@ -4435,6 +4435,7 @@ name = "tauri-plugin-window-state" version = "0.1.0" dependencies = [ "bincode", + "bitflags", "log", "serde", "serde_json", @@ -4869,7 +4870,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d" dependencies = [ "tempfile", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -5598,7 +5599,7 @@ dependencies = [ "static_assertions", "tracing", "uds_windows", - "winapi 0.3.9", + "winapi", "zbus_macros", "zbus_names", "zvariant", diff --git a/plugins/window-state/Cargo.toml b/plugins/window-state/Cargo.toml index 00d15f97..16535398 100644 --- a/plugins/window-state/Cargo.toml +++ b/plugins/window-state/Cargo.toml @@ -15,4 +15,5 @@ serde_json.workspace = true tauri.workspace = true log.workspace = true thiserror.workspace = true -bincode = "1.3" \ No newline at end of file +bincode = "1.3" +bitflags = "1" \ No newline at end of file diff --git a/plugins/window-state/src/lib.rs b/plugins/window-state/src/lib.rs index 05157317..d9775f7a 100644 --- a/plugins/window-state/src/lib.rs +++ b/plugins/window-state/src/lib.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use bitflags::bitflags; use serde::{Deserialize, Serialize}; use tauri::{ plugin::{Builder as PluginBuilder, TauriPlugin}, @@ -30,25 +31,25 @@ pub enum Error { Bincode(#[from] Box), } -/// Defines how the window visibility should be restored. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum ShowMode { - /// The window will always be shown, regardless of what the last stored state was. - Always, - /// The window will be automatically shown if the last stored state for visibility was `true`. - LastSaved, - /// The window will not be automatically shown by this plugin. - Never, +pub type Result = std::result::Result; + +bitflags! { + pub struct StateFlags: u32 { + const SIZE = 1 << 0; + const POSITION = 1 << 1; + const MAXIMIZED = 1 << 2; + const VISIBLE = 1 << 3; + const DECORATIONS = 1 << 4; + const FULLSCREEN = 1 << 5; + } } -impl Default for ShowMode { +impl Default for StateFlags { fn default() -> Self { - Self::LastSaved + Self::all() } } -pub type Result = std::result::Result; - #[derive(Debug, Default, Deserialize, Serialize)] struct WindowMetadata { width: f64, @@ -86,50 +87,86 @@ impl AppHandleExt for tauri::AppHandle { } pub trait WindowExt { - fn restore_state(&self, show_mode: ShowMode) -> tauri::Result<()>; + fn restore_state(&self, flags: StateFlags) -> tauri::Result<()>; } impl WindowExt for Window { - fn restore_state(&self, show_mode: ShowMode) -> tauri::Result<()> { + fn restore_state(&self, flags: StateFlags) -> tauri::Result<()> { let cache = self.state::(); let mut c = cache.0.lock().unwrap(); let mut should_show = true; + if let Some(state) = c.get(self.label()) { - self.set_decorations(state.decorated)?; - - self.set_size(LogicalSize { - width: state.width, - height: state.height, - })?; - - // restore position to saved value if saved monitor exists - // otherwise, let the OS decide where to place the window - for m in self.available_monitors()? { - if m.contains((state.x, state.y).into()) { - self.set_position(PhysicalPosition { - x: state.x, - y: state.y, - })?; + if flags.contains(StateFlags::DECORATIONS) { + self.set_decorations(state.decorated)?; + } + + if flags.contains(StateFlags::SIZE) { + self.set_size(LogicalSize { + width: state.width, + height: state.height, + })?; + } + + if flags.contains(StateFlags::POSITION) { + // restore position to saved value if saved monitor exists + // otherwise, let the OS decide where to place the window + for m in self.available_monitors()? { + if m.contains((state.x, state.y).into()) { + self.set_position(PhysicalPosition { + x: state.x, + y: state.y, + })?; + } } } - if state.maximized { + if flags.contains(StateFlags::MAXIMIZED) && state.maximized { self.maximize()?; } - self.set_fullscreen(state.fullscreen)?; + + if flags.contains(StateFlags::FULLSCREEN) { + self.set_fullscreen(state.fullscreen)?; + } should_show = state.visible; } else { - let scale_factor = self - .current_monitor()? - .map(|m| m.scale_factor()) - .unwrap_or(1.); - let LogicalSize { width, height } = self.inner_size()?.to_logical(scale_factor); - let PhysicalPosition { x, y } = self.outer_position()?; - let maximized = self.is_maximized().unwrap_or(false); - let visible = self.is_visible().unwrap_or(true); - let decorated = self.is_decorated().unwrap_or(true); - let fullscreen = self.is_fullscreen().unwrap_or(false); + let LogicalSize { width, height } = match flags.contains(StateFlags::SIZE) { + true => { + let scale_factor = self + .current_monitor()? + .map(|m| m.scale_factor()) + .unwrap_or(1.); + self.inner_size()?.to_logical(scale_factor) + } + false => (0, 0).into(), + }; + + let PhysicalPosition { x, y } = match flags.contains(StateFlags::POSITION) { + true => self.outer_position()?, + false => (0, 0).into(), + }; + + let maximized = match flags.contains(StateFlags::MAXIMIZED) { + true => self.is_maximized().unwrap_or(false), + false => false, + }; + + let visible = match flags.contains(StateFlags::VISIBLE) { + true => self.is_visible().unwrap_or(true), + false => true, + }; + + let decorated = match flags.contains(StateFlags::DECORATIONS) { + true => self.is_visible().unwrap_or(true), + false => true, + }; + + let fullscreen = match flags.contains(StateFlags::FULLSCREEN) { + true => self.is_fullscreen().unwrap_or(false), + false => false, + }; + c.insert( self.label().into(), WindowMetadata { @@ -145,7 +182,7 @@ impl WindowExt for Window { ); } - if show_mode == ShowMode::Always || (show_mode == ShowMode::LastSaved && should_show) { + if flags.contains(StateFlags::VISIBLE) && should_show { self.show()?; self.set_focus()?; } @@ -156,17 +193,15 @@ impl WindowExt for Window { #[derive(Default)] pub struct Builder { - show_mode: ShowMode, denylist: HashSet, skip_initial_state: HashSet, + state_flags: StateFlags, } impl Builder { - /// Sets how the window visibility should be restored. - /// - /// The default is [`ShowMode::LastSaved`] - pub fn with_show_mode(mut self, show_mode: ShowMode) -> Self { - self.show_mode = show_mode; + /// Sets the state flags to control what state gets restored and saved. + pub fn with_state_flags(mut self, flags: StateFlags) -> Self { + self.state_flags = flags; self } @@ -212,59 +247,84 @@ impl Builder { } if !self.skip_initial_state.contains(window.label()) { - let _ = window.restore_state(self.show_mode); + let _ = window.restore_state(self.state_flags); } let cache = window.state::(); let cache = cache.0.clone(); let label = window.label().to_string(); let window_clone = window.clone(); + let flags = self.state_flags.clone(); window.on_window_event(move |e| match e { WindowEvent::Moved(position) => { let mut c = cache.lock().unwrap(); if let Some(state) = c.get_mut(&label) { - let is_maximized = window_clone.is_maximized().unwrap_or(false); - state.maximized = is_maximized; - - if let Some(monitor) = window_clone.current_monitor().unwrap() { - let monitor_position = monitor.position(); - // save only window positions that are inside the current monitor - if position.x > monitor_position.x - && position.y > monitor_position.y - && !is_maximized - { - state.x = position.x; - state.y = position.y; - }; - }; + if flags.intersects(StateFlags::MAXIMIZED | StateFlags::POSITION) { + let is_maximized = window_clone.is_maximized().unwrap_or(false); + + if flags.contains(StateFlags::MAXIMIZED) { + state.maximized = is_maximized; + } + + if flags.contains(StateFlags::POSITION) { + if let Some(monitor) = window_clone.current_monitor().unwrap() { + let monitor_position = monitor.position(); + // save only window positions that are inside the current monitor + if position.x > monitor_position.x + && position.y > monitor_position.y + && !is_maximized + { + state.x = position.x; + state.y = position.y; + } + } + } + } } } WindowEvent::Resized(size) => { - let scale_factor = window_clone - .current_monitor() - .ok() - .map(|m| m.map(|m| m.scale_factor()).unwrap_or(1.)) - .unwrap_or(1.); - let size = size.to_logical(scale_factor); let mut c = cache.lock().unwrap(); if let Some(state) = c.get_mut(&label) { - let is_maximized = window_clone.is_maximized().unwrap_or(false); - let is_fullscreen = window_clone.is_fullscreen().unwrap_or(false); - state.decorated = window_clone.is_decorated().unwrap_or(true); - state.maximized = is_maximized; - state.fullscreen = is_fullscreen; - - // It doesn't make sense to save a window with 0 height or width - if size.width > 0. && size.height > 0. && !is_maximized { - state.width = size.width; - state.height = size.height; + let is_maximized = + match flags.intersects(StateFlags::MAXIMIZED | StateFlags::SIZE) { + true => window_clone.is_maximized().unwrap_or(false), + false => false, + }; + + if flags.contains(StateFlags::MAXIMIZED) { + state.maximized = is_maximized; + } + + if flags.contains(StateFlags::FULLSCREEN) { + state.fullscreen = window_clone.is_fullscreen().unwrap_or(false); + } + + if flags.contains(StateFlags::DECORATIONS) { + state.decorated = window_clone.is_decorated().unwrap_or(true); + } + + if flags.contains(StateFlags::SIZE) { + let scale_factor = window_clone + .current_monitor() + .ok() + .map(|m| m.map(|m| m.scale_factor()).unwrap_or(1.)) + .unwrap_or(1.); + let size = size.to_logical(scale_factor); + + // It doesn't make sense to save a window with 0 height or width + if size.width > 0. && size.height > 0. && !is_maximized { + state.width = size.width; + state.height = size.height; + } } } } WindowEvent::CloseRequested { .. } => { let mut c = cache.lock().unwrap(); if let Some(state) = c.get_mut(&label) { - state.visible = window_clone.is_visible().unwrap_or(true); + if flags.contains(StateFlags::VISIBLE) { + state.visible = window_clone.is_visible().unwrap_or(true); + } } } _ => {}