Files
VitosUpdater/src-tauri/src/settings/mod.rs
T

74 lines
2.4 KiB
Rust

//! Persistenter Zustand unter `%APPDATA%\VitosUpdater` (Phase 1: Fensterposition und -größe).
use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
/// Gespeicherte Fenstergeometrie in physischen Pixeln.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct WindowState {
pub x: i32,
pub y: i32,
pub width: u32,
pub height: u32,
pub maximized: bool,
}
fn file(dir: &Path) -> PathBuf {
dir.join("window.json")
}
/// Lädt die gespeicherte Fenstergeometrie. Fehlt die Datei oder ist sie defekt, ist das Ergebnis `None`.
pub fn load_window_state(dir: &Path) -> Option<WindowState> {
let text = std::fs::read_to_string(file(dir)).ok()?;
serde_json::from_str(&text).ok()
}
/// Speichert die Fenstergeometrie atomar (Schreiben in temporäre Datei, dann Umbenennen).
pub fn save_window_state(dir: &Path, state: &WindowState) -> std::io::Result<()> {
std::fs::create_dir_all(dir)?;
let tmp = dir.join("window.json.tmp");
std::fs::write(&tmp, serde_json::to_string_pretty(state)?)?;
std::fs::rename(tmp, file(dir))
}
/// Prüft, ob ein Fensterrechteck ein Monitorrechteck `(x, y, breite, höhe)` ausreichend überlappt.
pub fn is_visible_on(state: &WindowState, monitors: &[(i32, i32, u32, u32)]) -> bool {
const MIN_VISIBLE: i64 = 120;
monitors.iter().any(|&(mx, my, mw, mh)| {
let left = i64::from(state.x.max(mx));
let top = i64::from(state.y.max(my));
let right = (i64::from(state.x) + i64::from(state.width)).min(i64::from(mx) + i64::from(mw));
let bottom = (i64::from(state.y) + i64::from(state.height)).min(i64::from(my) + i64::from(mh));
right - left >= MIN_VISIBLE && bottom - top >= MIN_VISIBLE
})
}
#[cfg(test)]
mod tests {
use super::*;
fn state(x: i32, y: i32) -> WindowState {
WindowState { x, y, width: 1280, height: 800, maximized: false }
}
#[test]
fn visible_on_primary_monitor() {
assert!(is_visible_on(&state(100, 100), &[(0, 0, 1920, 1080)]));
}
#[test]
fn offscreen_after_monitor_removed() {
assert!(!is_visible_on(&state(2500, 100), &[(0, 0, 1920, 1080)]));
}
#[test]
fn roundtrip() {
let dir = std::env::temp_dir().join("vitosupdater-test-window");
let s = state(10, 20);
save_window_state(&dir, &s).unwrap();
assert_eq!(load_window_state(&dir), Some(s));
let _ = std::fs::remove_dir_all(dir);
}
}