mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Merge pull request #3660 from nrq6993-a11y/stream/vc-issue-3635-linux-maximize
Restore and remember maximized window state
This commit is contained in:
@@ -70,6 +70,12 @@ struct App {
|
||||
frames: u64,
|
||||
/// Keeps the custom title bar's window borderless (Windows, #1758).
|
||||
window_frame: Option<window_frame::WindowFrameFn>,
|
||||
/// The main window; used to read and set maximized state the UI layer does not see on Linux (#3635).
|
||||
winit_window: Option<std::sync::Arc<winit::window::Window>>,
|
||||
/// A saved or fitted geometry asked for maximize once the window is mapped (Linux).
|
||||
pending_maximize: bool,
|
||||
/// Frames left to retry [`window::apply_pending_maximize`].
|
||||
maximize_tries: u8,
|
||||
}
|
||||
|
||||
impl eframe::App for App {
|
||||
@@ -147,7 +153,15 @@ impl App {
|
||||
open_files(&mut self.app, open_documents::take());
|
||||
}
|
||||
self.app.logic(ctx);
|
||||
window::track(ctx, &mut self.app.ui.window);
|
||||
if self.pending_maximize {
|
||||
if let Some(w) = &self.winit_window {
|
||||
self.pending_maximize = window::apply_pending_maximize(w, true, &mut self.maximize_tries);
|
||||
} else {
|
||||
self.pending_maximize = false;
|
||||
}
|
||||
}
|
||||
let host_maximized = self.winit_window.as_ref().map(|w| w.is_maximized());
|
||||
window::track(ctx, &mut self.app.ui.window, host_maximized);
|
||||
if self.app.ui.status == "quit" {
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
||||
}
|
||||
@@ -953,11 +967,15 @@ fn main() -> std::process::ExitCode {
|
||||
// which takes over from the copy the preferences file used to hold.
|
||||
prefs_dir::attach_pdf_preset_store(&mut app.session);
|
||||
// Fit the window to its monitor, or put it back where it was (still hidden).
|
||||
if let Some(w) = cc.winit_window() {
|
||||
app.ui.window = Some(window::restore(w, saved_window));
|
||||
let winit_window = cc.winit_window().cloned();
|
||||
if let Some(w) = &winit_window {
|
||||
let g = window::restore(w, saved_window);
|
||||
app.ui.window = Some(g);
|
||||
#[cfg(windows)]
|
||||
set_taskbar_icon(w);
|
||||
}
|
||||
// Linux may not map the window before the first frames, so a saved maximize is retried then (#3635).
|
||||
let pending_maximize = cfg!(target_os = "linux") && app.ui.window.as_ref().is_some_and(|g| g.maximized);
|
||||
// User Defined swatch and graphic style libraries live next to the preferences.
|
||||
let swatches = prefs_path().and_then(|p| Some(p.parent()?.join("Swatches").to_string_lossy().to_string()));
|
||||
app.session.swatch_libraries.set_user_dir(swatches);
|
||||
@@ -1038,7 +1056,16 @@ fn main() -> std::process::ExitCode {
|
||||
created.ready();
|
||||
// The content and the pointer stay aligned on Windows (#1758).
|
||||
let window_frame = window_frame::service(cc, custom_titlebar);
|
||||
Ok(Box::new(App { app, graphics_loss, graphics_lost: false, frames: 0, window_frame }))
|
||||
Ok(Box::new(App {
|
||||
app,
|
||||
graphics_loss,
|
||||
graphics_lost: false,
|
||||
frames: 0,
|
||||
window_frame,
|
||||
winit_window,
|
||||
pending_maximize,
|
||||
maximize_tries: 8,
|
||||
}))
|
||||
}),
|
||||
)
|
||||
});
|
||||
|
||||
@@ -110,7 +110,8 @@ fn fit(size: [f32; 2], room: [f32; 2]) -> [f32; 2] {
|
||||
}
|
||||
|
||||
/// Size and place the new window (eframe shows it after drawing the first frame) and return its
|
||||
/// geometry.
|
||||
/// geometry. On Linux the window may not be mapped yet, so [`apply_pending_maximize`] must run
|
||||
/// until the host reports maximized (fixes #3635).
|
||||
pub fn restore(window: &winit::window::Window, saved: Option<WindowGeometry>) -> WindowGeometry {
|
||||
let handles: Vec<_> = window.available_monitors().collect();
|
||||
let monitors: Vec<Monitor> = handles.iter().map(|m| Monitor::new(m.position(), m.size(), m.scale_factor())).collect();
|
||||
@@ -121,22 +122,41 @@ pub fn restore(window: &winit::window::Window, saved: Option<WindowGeometry>) ->
|
||||
}
|
||||
// Returns the new size when it applies at once; either way the window gets it.
|
||||
let _ = window.request_inner_size(LogicalSize::new(g.size[0], g.size[1]));
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
if g.maximized {
|
||||
window.set_maximized(true);
|
||||
}
|
||||
g
|
||||
}
|
||||
|
||||
/// Keep asking the host to maximize until it sticks, or give up after a few frames.
|
||||
pub fn apply_pending_maximize(window: &winit::window::Window, want: bool, tries: &mut u8) -> bool {
|
||||
if !want {
|
||||
return false;
|
||||
}
|
||||
if window.is_maximized() {
|
||||
return false;
|
||||
}
|
||||
if *tries == 0 {
|
||||
return false;
|
||||
}
|
||||
*tries -= 1;
|
||||
window.set_maximized(true);
|
||||
true
|
||||
}
|
||||
|
||||
/// Per frame: remember the window's geometry for the next launch. Minimized or full screen keeps
|
||||
/// the last one; maximized only sets its flag, so un-maximizing next time restores the last size.
|
||||
pub fn track(ctx: &egui::Context, geometry: &mut Option<WindowGeometry>) {
|
||||
/// `host_maximized` is the window manager's idea of maximized when egui does not report it (#3635).
|
||||
pub fn track(ctx: &egui::Context, geometry: &mut Option<WindowGeometry>, host_maximized: Option<bool>) {
|
||||
let zoom = ctx.zoom_factor();
|
||||
ctx.input(|i| {
|
||||
let v = i.viewport();
|
||||
if v.minimized == Some(true) || v.fullscreen == Some(true) {
|
||||
return;
|
||||
}
|
||||
if v.maximized == Some(true) {
|
||||
let maximized = v.maximized == Some(true) || host_maximized == Some(true);
|
||||
if maximized {
|
||||
if let Some(g) = geometry {
|
||||
g.maximized = true;
|
||||
}
|
||||
@@ -222,7 +242,7 @@ mod tests {
|
||||
let mut frame = |info: egui::ViewportInfo| {
|
||||
let mut input = egui::RawInput::default();
|
||||
input.viewports.insert(egui::ViewportId::ROOT, info);
|
||||
ctx.run_ui(input, |ui| track(ui.ctx(), &mut geometry)).textures_delta.clear();
|
||||
ctx.run_ui(input, |ui| track(ui.ctx(), &mut geometry, None)).textures_delta.clear();
|
||||
geometry
|
||||
};
|
||||
let rect = egui::Rect::from_min_size(egui::pos2(20.0, 30.0), egui::vec2(1000.0, 600.0));
|
||||
@@ -253,4 +273,19 @@ mod tests {
|
||||
let back: vectorcraft_ui_egui::UiState = serde_json::from_value(json).unwrap();
|
||||
assert_eq!(back.window, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_host_maximized_flag_is_remembered_when_egui_does_not_report_it() {
|
||||
let ctx = egui::Context::default();
|
||||
let mut geometry = Some(WindowGeometry { pos: Some([10, 20]), size: [800.0, 600.0], maximized: false });
|
||||
let rect = egui::Rect::from_min_size(egui::pos2(0.0, 0.0), egui::vec2(1920.0, 1080.0));
|
||||
let mut input = egui::RawInput::default();
|
||||
input.viewports.insert(
|
||||
egui::ViewportId::ROOT,
|
||||
egui::ViewportInfo { inner_rect: Some(rect), outer_rect: Some(rect), maximized: None, ..Default::default() },
|
||||
);
|
||||
let mut out = ctx.run_ui(input, |ui| track(ui.ctx(), &mut geometry, Some(true)));
|
||||
out.textures_delta.clear();
|
||||
assert_eq!(geometry, Some(WindowGeometry { pos: Some([10, 20]), size: [800.0, 600.0], maximized: true }));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user