fix(properties): retain window size per app session

This commit is contained in:
NimBold
2026-08-07 14:14:21 +03:30
parent 757b849e4e
commit 27da8b1c44
2 changed files with 144 additions and 10 deletions
+43 -8
View File
@@ -14581,15 +14581,50 @@ pub fn run() {
.plugin(tauri_plugin_notification::init())
.plugin(tauri_plugin_clipboard_manager::init())
.on_window_event(|window, event| {
if matches!(event, tauri::WindowEvent::Destroyed)
&& properties_window::is_properties_window_label(window.label())
{
if let Some(registry) = window.app_handle().try_state::<properties_window::PropertiesWindowRegistry>() {
let _ = registry.remove_window(window.label());
if properties_window::is_properties_window_label(window.label()) {
let resize = match event {
tauri::WindowEvent::Resized(size) => {
window.scale_factor().ok().map(|scale_factor| {
(size.width, size.height, scale_factor)
})
}
tauri::WindowEvent::ScaleFactorChanged {
scale_factor,
new_inner_size,
..
} => Some((new_inner_size.width, new_inner_size.height, *scale_factor)),
tauri::WindowEvent::CloseRequested { .. } => window
.inner_size()
.ok()
.and_then(|size| {
window
.scale_factor()
.ok()
.map(|scale_factor| (size.width, size.height, scale_factor))
}),
_ => None,
};
if let Some((width, height, scale_factor)) = resize {
if let Some(registry) = window
.app_handle()
.try_state::<properties_window::PropertiesWindowRegistry>()
{
let _ = registry.remember_size(
window.label(),
width,
height,
scale_factor,
);
}
}
if matches!(event, tauri::WindowEvent::Destroyed) {
if let Some(registry) = window.app_handle().try_state::<properties_window::PropertiesWindowRegistry>() {
let _ = registry.remove_window(window.label());
}
let _ = window
.app_handle()
.emit_to("main", "properties-window-closed", window.label().to_string());
}
let _ = window
.app_handle()
.emit_to("main", "properties-window-closed", window.label().to_string());
}
if window.label() == "main" {
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
+101 -2
View File
@@ -9,6 +9,10 @@ use uuid::Uuid;
const MAIN_WINDOW_LABEL: &str = "main";
const PROPERTIES_LABEL_PREFIX: &str = "properties-";
const PROPERTIES_WINDOW_TITLE: &str = "Properties - Firelink";
const PROPERTIES_DEFAULT_WIDTH: f64 = 960.0;
const PROPERTIES_DEFAULT_HEIGHT: f64 = 640.0;
const PROPERTIES_MIN_WIDTH: f64 = 680.0;
const PROPERTIES_MIN_HEIGHT: f64 = 500.0;
const PROPERTIES_WINDOW_READY_EVENT: &str = "properties-window-ready";
const PROPERTIES_WINDOW_ACTION_REQUEST_EVENT: &str = "properties-window-action-request";
const MAX_PROPERTIES_ACTION_PAYLOAD_BYTES: usize = 64 * 1024;
@@ -30,6 +34,13 @@ struct RegistryState {
ready_windows: HashSet<String>,
sessions_by_window: HashMap<String, String>,
retired_sessions_by_window: HashMap<String, HashSet<String>>,
remembered_size: Option<PropertiesWindowSize>,
}
#[derive(Clone, Copy)]
struct PropertiesWindowSize {
width: f64,
height: f64,
}
#[derive(Clone, Serialize)]
@@ -52,6 +63,47 @@ struct PropertiesWindowActionEvent {
}
impl PropertiesWindowRegistry {
pub(crate) fn remember_size(
&self,
window_label: &str,
physical_width: u32,
physical_height: u32,
scale_factor: f64,
) -> Result<(), String> {
if !scale_factor.is_finite() || scale_factor <= 0.0 {
return Ok(());
}
let width = (f64::from(physical_width) / scale_factor).round();
let height = (f64::from(physical_height) / scale_factor).round();
if !width.is_finite() || !height.is_finite() || width <= 0.0 || height <= 0.0 {
return Ok(());
}
let mut state = self
.state
.lock()
.map_err(|_| "Properties window registry is unavailable".to_string())?;
if !state.by_window.contains_key(window_label) {
return Ok(());
}
state.remembered_size = Some(PropertiesWindowSize {
width: width.max(PROPERTIES_MIN_WIDTH),
height: height.max(PROPERTIES_MIN_HEIGHT),
});
Ok(())
}
fn remembered_size(&self) -> Result<Option<(f64, f64)>, String> {
Ok(self
.state
.lock()
.map_err(|_| "Properties window registry is unavailable".to_string())?
.remembered_size
.map(|size| (size.width, size.height)))
}
pub fn allocate(&self, download_id: &str) -> Result<String, String> {
let mut state = self
.state
@@ -352,10 +404,13 @@ pub fn open_download_properties_window(
// If the native window disappeared without delivering Destroyed, discard
// the old readiness bit before constructing a fresh hidden webview.
registry.clear_ready(&label)?;
let (initial_width, initial_height) = registry
.remembered_size()?
.unwrap_or((PROPERTIES_DEFAULT_WIDTH, PROPERTIES_DEFAULT_HEIGHT));
let builder = WebviewWindowBuilder::new(&app, &label, WebviewUrl::App("index.html".into()))
.title(PROPERTIES_WINDOW_TITLE)
.inner_size(960.0, 540.0)
.min_inner_size(680.0, 500.0)
.inner_size(initial_width, initial_height)
.min_inner_size(PROPERTIES_MIN_WIDTH, PROPERTIES_MIN_HEIGHT)
.resizable(true)
.always_on_top(false)
// Let the child renderer paint its rounded loading shell before the
@@ -586,6 +641,50 @@ mod tests {
assert_ne!(registry.allocate("download-a").unwrap(), first);
}
#[test]
fn remembered_size_uses_logical_units_and_survives_window_cleanup() {
let registry = PropertiesWindowRegistry::default();
let label = registry.allocate("download-a").unwrap();
registry.remember_size(&label, 1920, 1280, 2.0).unwrap();
assert_eq!(registry.remembered_size().unwrap(), Some((960.0, 640.0)));
registry.remove_window(&label).unwrap();
assert_eq!(registry.remembered_size().unwrap(), Some((960.0, 640.0)));
}
#[test]
fn remembered_size_clamps_below_minimum_and_ignores_invalid_scale() {
let registry = PropertiesWindowRegistry::default();
let label = registry.allocate("download-a").unwrap();
registry.remember_size(&label, 1, 1, 1.0).unwrap();
assert_eq!(
registry.remembered_size().unwrap(),
Some((PROPERTIES_MIN_WIDTH, PROPERTIES_MIN_HEIGHT))
);
registry.remember_size(&label, 2000, 1600, 0.0).unwrap();
assert_eq!(
registry.remembered_size().unwrap(),
Some((PROPERTIES_MIN_WIDTH, PROPERTIES_MIN_HEIGHT))
);
}
#[test]
fn late_resize_from_unregistered_window_cannot_overwrite_session_size() {
let registry = PropertiesWindowRegistry::default();
let label = registry.allocate("download-a").unwrap();
registry.remember_size(&label, 1920, 1280, 2.0).unwrap();
registry.remove_window(&label).unwrap();
registry
.remember_size(&label, 2560, 1600, 2.0)
.unwrap();
assert_eq!(registry.remembered_size().unwrap(), Some((960.0, 640.0)));
}
#[test]
fn invalid_ids_are_rejected() {
assert!(validate_download_id("").is_err());