eframe applies `NativeOptions::centered` after restoring the saved window geometry, so the window moved back to the centre of the primary monitor on every launch. Centre only when ui.ron holds no saved position. Claude-Session: https://claude.ai/code/session_01KrteAUDjpXgQZJ7659DUro Co-authored-by: Leel Wasanatha <260480244+kavishkashehan726-sudo@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
663863e62b
commit
fec0573e71
|
|
@ -75,7 +75,8 @@ fn custom_titlebar(prefs_file: Option<&std::path::Path>) -> bool {
|
|||
/// The main window: 1440 × 900 (shrunk to fit the monitor, and maximized on the first frame
|
||||
/// when it still doesn't fit, `work_area::fit_window`), centred on the main monitor. Without
|
||||
/// `centered`, Windows cascades each new window from the top-left corner, so it opened at a
|
||||
/// different offset every launch (#419). Wayland compositors place windows themselves.
|
||||
/// different offset every launch (#419). A later launch restores the saved position instead
|
||||
/// (`main` turns `centered` off, #4826). Wayland compositors place windows themselves.
|
||||
fn native_options(custom_titlebar: bool) -> eframe::NativeOptions {
|
||||
eframe::NativeOptions {
|
||||
viewport: egui::ViewportBuilder::default()
|
||||
|
|
@ -330,6 +331,9 @@ fn main() -> eframe::Result {
|
|||
}
|
||||
// eframe restores the saved window layout before our code runs; drop values that would crash it.
|
||||
ui_state::sanitize(options.persistence_path.as_deref());
|
||||
// Centre only the first window: eframe centres after restoring the saved position, so a
|
||||
// window closed on another monitor reopened in the middle of the main one (#4826).
|
||||
options.centered = !ui_state::has_saved_position(options.persistence_path.as_deref());
|
||||
// Crash-safe GPU startup (#4): pick the backend (a marker left by a start that died in the
|
||||
// driver moves to a safer one), and lock this start's marker until the first frames render.
|
||||
let t_sentinel = std::time::Instant::now();
|
||||
|
|
|
|||
|
|
@ -27,6 +27,23 @@ pub fn sanitize(path: Option<&Path>) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Whether `path` holds a window position from an earlier session, which eframe restores. Then
|
||||
/// `NativeOptions::centered` must be off: eframe applies it after restoring the saved geometry,
|
||||
/// so it moved the window back to the centre of the main monitor on every launch (#4826).
|
||||
pub fn has_saved_position(path: Option<&Path>) -> bool {
|
||||
let Some(text) = path.and_then(|p| std::fs::read_to_string(p).ok()) else { return false };
|
||||
saved_position(&text)
|
||||
}
|
||||
|
||||
fn saved_position(text: &str) -> bool {
|
||||
let Ok(kv) = ron::from_str::<std::collections::HashMap<String, String>>(text) else { return false };
|
||||
let Some(window) = kv.get("window").and_then(|w| ron::from_str::<ron::Value>(w).ok()) else { return false };
|
||||
// eframe restores the inner position on macOS and the outer position elsewhere
|
||||
// (`egui_winit::WindowSettings::initialize_viewport_builder`).
|
||||
let key = if cfg!(target_os = "macos") { "inner_position_pixels" } else { "outer_position_pixels" };
|
||||
matches!(field(&window, key), Some(ron::Value::Option(Some(_))))
|
||||
}
|
||||
|
||||
/// `Err` names the first value that would crash startup.
|
||||
fn check(text: &str) -> Result<(), String> {
|
||||
// eframe stores a map of key → RON string; a file that doesn't parse is ignored by eframe.
|
||||
|
|
@ -78,7 +95,7 @@ fn field<'a>(v: &'a ron::Value, name: &str) -> Option<&'a ron::Value> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::check;
|
||||
use super::{check, saved_position};
|
||||
|
||||
fn file(window: &str, zoom: &str) -> String {
|
||||
let egui = format!("(options:(zoom_factor:{zoom},max_passes:2),data:([(18446744073709551615,(ron:\"(x:1.0)\"))]))");
|
||||
|
|
@ -105,6 +122,21 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_saved_window_position_is_detected() {
|
||||
// #4826: the position from the report (Linux Mint, window moved to the third monitor).
|
||||
let window = "(inner_position_pixels:Some((x:2048.0,y:1265.0)),outer_position_pixels:Some((x:2048.0,y:1228.0)),fullscreen:false,maximized:false,inner_size_points:Some((x:880.0,y:480.0)))";
|
||||
assert!(saved_position(&file(window, "1.0")));
|
||||
let no_position = "(inner_position_pixels:None,outer_position_pixels:None,fullscreen:false,maximized:false,inner_size_points:Some((x:880.0,y:480.0)))";
|
||||
assert!(!saved_position(&file(no_position, "1.0")));
|
||||
assert!(!saved_position(&format!("{{\"egui\":{:?}}}", "()")), "no window entry");
|
||||
for text in ["", "not ron {{{", "{\"window\":\"(((\"}"] {
|
||||
assert!(!saved_position(text), "{text:?}");
|
||||
}
|
||||
assert!(!super::has_saved_position(None));
|
||||
assert!(!super::has_saved_position(Some(&std::env::temp_dir().join("photocraft-missing-ui.ron"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unreadable_files_are_left_to_eframe() {
|
||||
// eframe ignores files and entries it can't parse, so they can't crash it.
|
||||
|
|
|
|||
Loading…
Reference in New Issue