fix(ui): remember window size across launches

The window always opened at the hardcoded 900x760 because the size was never
persisted: run_gui hardcoded it, AppConfig had no size fields, and the Resized
handler only kept the size in memory (for divider clamping) while
exit_on_close_request:true quit before anything could save.

- AppConfig gains window_width/window_height (serde-default 900/760).
- run_gui restores them as the initial window size.
- The Resized handler mirrors the live size into config (guarded against bogus
  tiny sizes); divider positions on load now clamp against the restored size
  rather than a hardcoded default.
- exit_on_close_request:false + a CloseRequested handler writes the final size
  once, then iced::exit() — no per-resize disk thrash.

Verified empirically that KWin/Wayland honors a client-requested initial size
(requested 1150x680 -> window reported 1150x680). Window *position* is not
restored: xdg-shell gives Wayland clients no way to set their own position.

+1 config test (default + round-trip).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-06 17:42:59 -04:00
co-authored by Claude Opus 4.8
parent 1a377a2323
commit 88905e5173
2 changed files with 69 additions and 11 deletions
+37
View File
@@ -92,6 +92,14 @@ fn default_chat_drawer_width() -> f32 {
320.0
}
fn default_window_width() -> f32 {
900.0
}
fn default_window_height() -> f32 {
760.0
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AppConfig {
/// Last nickname used to join/create a room; pre-filled on the launch screen.
@@ -149,6 +157,12 @@ pub struct AppConfig {
/// Empty / unset = look it up on `$PATH`. Hand-editable; no Settings UI yet.
#[serde(default)]
pub pixelpass_path: Option<String>,
/// Last window size (px), restored as the initial size on next launch.
/// Saved on close. (Window *position* can't be restored on Wayland.)
#[serde(default = "default_window_width")]
pub window_width: f32,
#[serde(default = "default_window_height")]
pub window_height: f32,
}
impl Default for AppConfig {
@@ -177,6 +191,8 @@ impl Default for AppConfig {
custom_sound_mic_toggle: None,
custom_sound_reconnect_failed: None,
pixelpass_path: None,
window_width: default_window_width(),
window_height: default_window_height(),
}
}
}
@@ -251,6 +267,27 @@ mod tests {
assert!(deserialized.custom_sound_self_leave.is_none());
assert!(deserialized.custom_sound_mic_toggle.is_none());
assert!(deserialized.custom_sound_reconnect_failed.is_none());
// Configs predating the remembered window size load the default size.
assert_eq!(deserialized.window_width, 900.0);
assert_eq!(deserialized.window_height, 760.0);
}
#[test]
fn test_window_size_fields() {
// Default impl is the standard launch size.
let def = AppConfig::default();
assert_eq!(def.window_width, 900.0);
assert_eq!(def.window_height, 760.0);
// A saved size round-trips.
let cfg = AppConfig {
window_width: 1280.0,
window_height: 720.0,
..AppConfig::default()
};
let json = serde_json::to_string(&cfg).unwrap();
let back: AppConfig = serde_json::from_str(&json).unwrap();
assert_eq!(back.window_width, 1280.0);
assert_eq!(back.window_height, 720.0);
}
#[test]