1
0
mirror of https://github.com/emilk/egui.git synced 2026-06-26 14:49:06 -04:00

Choose restored window monitor by overlap (#8191)

This changes the monitor selection used when restoring a persisted
window position.

Currently, `egui-winit` picks a monitor by checking whether the saved
window position fits inside a loose monitor range. This can choose the
wrong monitor when a saved window rectangle slightly overlaps another
monitor.

My failure case was on Windows with two monitors:
- primary monitor on the right
- secondary monitor on the left
- window maximized on the primary monitor
- persisted outer position was slightly negative, e.g. `x = -8`, because
of the invisible window border

That position matched both monitor ranges, so the restored maximized
window could
open on the secondary monitor instead of the primary one.

This PR picks the monitor with the largest overlap with the saved window
rectangle instead.

Related note: probably the best solution would be to save the normal
window position when maximized, so that when unmaximizing, the window
would get restored to the previous state. It's mentioned in this comment
https://github.com/emilk/egui/issues/3494#issuecomment-1986985211. I
tried doing that in
[fix-windows-maximized-restore-placement](https://github.com/YelovSK/egui/tree/fix-windows-maximized-restore-placement),
and it works, but it requires adding windows-sys as a dependency to call
a relevant winapi, so that's probably not the right solution. Winit
doesn't seem to provide an API that would return this information.

* [x] I have followed the instructions in the PR template

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
Patrik Hampel
2026-05-25 12:10:07 +02:00
committed by GitHub
parent 4a897168a6
commit f57291bac0

View File

@@ -158,23 +158,30 @@ fn find_active_monitor(
return None; // no monitors 🤷
};
let mut active_monitor_overlap = 0.0;
for monitor in monitors {
let window_size_px = window_size_pts * (egui_zoom_factor * monitor.scale_factor() as f32);
let monitor_x_range = (monitor.position().x - window_size_px.x as i32)
..(monitor.position().x + monitor.size().width as i32);
let monitor_y_range = (monitor.position().y - window_size_px.y as i32)
..(monitor.position().y + monitor.size().height as i32);
let window_rect = egui::Rect::from_min_size(*position_px, window_size_px);
let overlap = window_rect.intersect(monitor_rect_px(&monitor)).area();
if monitor_x_range.contains(&(position_px.x as i32))
&& monitor_y_range.contains(&(position_px.y as i32))
{
if active_monitor_overlap < overlap {
active_monitor = monitor;
active_monitor_overlap = overlap;
}
}
Some(active_monitor)
}
fn monitor_rect_px(monitor: &winit::monitor::MonitorHandle) -> egui::Rect {
let pos = monitor.position();
let size = monitor.size();
egui::Rect::from_min_size(
egui::pos2(pos.x as f32, pos.y as f32),
egui::vec2(size.width as f32, size.height as f32),
)
}
fn clamp_pos_to_monitors(
egui_zoom_factor: f32,
event_loop: &winit::event_loop::ActiveEventLoop,
@@ -198,19 +205,12 @@ fn clamp_pos_to_monitors(
32.0 * egui_zoom_factor * active_monitor.scale_factor() as f32,
);
}
let monitor_position = egui::Pos2::new(
active_monitor.position().x as f32,
active_monitor.position().y as f32,
);
let monitor_size_px = egui::Vec2::new(
active_monitor.size().width as f32,
active_monitor.size().height as f32,
);
let monitor_rect = monitor_rect_px(&active_monitor);
// Window size cannot be negative or the subsequent `clamp` will panic.
let window_size = (monitor_size_px - window_size_px).max(egui::Vec2::ZERO);
let window_size = (monitor_rect.size() - window_size_px).max(egui::Vec2::ZERO);
// To get the maximum position, we get the rightmost corner of the display, then
// subtract the size of the window to get the bottom right most value window.position
// can have.
*position_px = position_px.clamp(monitor_position, monitor_position + window_size);
*position_px = position_px.clamp(monitor_rect.min, monitor_rect.min + window_size);
}