egui_winit/
window_settings.rs
1use egui::ViewportBuilder;
2
3#[derive(Clone, Copy, Debug, Default)]
5#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
6#[cfg_attr(feature = "serde", serde(default))]
7pub struct WindowSettings {
8 inner_position_pixels: Option<egui::Pos2>,
10
11 outer_position_pixels: Option<egui::Pos2>,
13
14 fullscreen: bool,
15
16 inner_size_points: Option<egui::Vec2>,
18}
19
20impl WindowSettings {
21 pub fn from_window(egui_zoom_factor: f32, window: &winit::window::Window) -> Self {
22 let inner_size_points = window
23 .inner_size()
24 .to_logical::<f32>(egui_zoom_factor as f64 * window.scale_factor());
25
26 let inner_position_pixels = window
27 .inner_position()
28 .ok()
29 .map(|p| egui::pos2(p.x as f32, p.y as f32));
30
31 let outer_position_pixels = window
32 .outer_position()
33 .ok()
34 .map(|p| egui::pos2(p.x as f32, p.y as f32));
35
36 Self {
37 inner_position_pixels,
38 outer_position_pixels,
39
40 fullscreen: window.fullscreen().is_some(),
41
42 inner_size_points: Some(egui::vec2(
43 inner_size_points.width,
44 inner_size_points.height,
45 )),
46 }
47 }
48
49 pub fn inner_size_points(&self) -> Option<egui::Vec2> {
50 self.inner_size_points
51 }
52
53 pub fn initialize_viewport_builder(
54 &self,
55 egui_zoom_factor: f32,
56 event_loop: &winit::event_loop::ActiveEventLoop,
57 mut viewport_builder: ViewportBuilder,
58 ) -> ViewportBuilder {
59 profiling::function_scope!();
60
61 let pos_px = if cfg!(target_os = "macos") {
64 self.inner_position_pixels
65 } else {
66 self.outer_position_pixels
67 };
68 if let Some(pos) = pos_px {
69 let monitor_scale_factor = if let Some(inner_size_points) = self.inner_size_points {
70 find_active_monitor(egui_zoom_factor, event_loop, inner_size_points, &pos)
71 .map_or(1.0, |monitor| monitor.scale_factor() as f32)
72 } else {
73 1.0
74 };
75
76 let scaled_pos = pos / (egui_zoom_factor * monitor_scale_factor);
77 viewport_builder = viewport_builder.with_position(scaled_pos);
78 }
79
80 if let Some(inner_size_points) = self.inner_size_points {
81 viewport_builder = viewport_builder
82 .with_inner_size(inner_size_points)
83 .with_fullscreen(self.fullscreen);
84 }
85
86 viewport_builder
87 }
88
89 pub fn initialize_window(&self, window: &winit::window::Window) {
90 if cfg!(target_os = "macos") {
91 if let Some(pos) = self.outer_position_pixels {
94 window.set_outer_position(winit::dpi::PhysicalPosition { x: pos.x, y: pos.y });
95 }
96 }
97 }
98
99 pub fn clamp_size_to_sane_values(&mut self, largest_monitor_size_points: egui::Vec2) {
100 use egui::NumExt as _;
101
102 if let Some(size) = &mut self.inner_size_points {
103 let min_size = egui::Vec2::splat(64.0);
105 *size = size.at_least(min_size);
106
107 *size = size.at_most(largest_monitor_size_points);
110 }
111 }
112
113 pub fn clamp_position_to_monitors(
114 &mut self,
115 egui_zoom_factor: f32,
116 event_loop: &winit::event_loop::ActiveEventLoop,
117 ) {
118 if !cfg!(target_os = "windows") {
124 return;
125 }
126
127 let Some(inner_size_points) = self.inner_size_points else {
128 return;
129 };
130
131 if let Some(pos_px) = &mut self.inner_position_pixels {
132 clamp_pos_to_monitors(egui_zoom_factor, event_loop, inner_size_points, pos_px);
133 }
134 if let Some(pos_px) = &mut self.outer_position_pixels {
135 clamp_pos_to_monitors(egui_zoom_factor, event_loop, inner_size_points, pos_px);
136 }
137 }
138}
139
140fn find_active_monitor(
141 egui_zoom_factor: f32,
142 event_loop: &winit::event_loop::ActiveEventLoop,
143 window_size_pts: egui::Vec2,
144 position_px: &egui::Pos2,
145) -> Option<winit::monitor::MonitorHandle> {
146 profiling::function_scope!();
147 let monitors = event_loop.available_monitors();
148
149 let Some(mut active_monitor) = event_loop
151 .primary_monitor()
152 .or_else(|| event_loop.available_monitors().next())
153 else {
154 return None; };
156
157 for monitor in monitors {
158 let window_size_px = window_size_pts * (egui_zoom_factor * monitor.scale_factor() as f32);
159 let monitor_x_range = (monitor.position().x - window_size_px.x as i32)
160 ..(monitor.position().x + monitor.size().width as i32);
161 let monitor_y_range = (monitor.position().y - window_size_px.y as i32)
162 ..(monitor.position().y + monitor.size().height as i32);
163
164 if monitor_x_range.contains(&(position_px.x as i32))
165 && monitor_y_range.contains(&(position_px.y as i32))
166 {
167 active_monitor = monitor;
168 }
169 }
170
171 Some(active_monitor)
172}
173
174fn clamp_pos_to_monitors(
175 egui_zoom_factor: f32,
176 event_loop: &winit::event_loop::ActiveEventLoop,
177 window_size_pts: egui::Vec2,
178 position_px: &mut egui::Pos2,
179) {
180 profiling::function_scope!();
181
182 let Some(active_monitor) =
183 find_active_monitor(egui_zoom_factor, event_loop, window_size_pts, position_px)
184 else {
185 return; };
187
188 let mut window_size_px =
189 window_size_pts * (egui_zoom_factor * active_monitor.scale_factor() as f32);
190 if cfg!(target_os = "windows") {
192 window_size_px += egui::Vec2::new(
193 0.0,
194 32.0 * egui_zoom_factor * active_monitor.scale_factor() as f32,
195 );
196 }
197 let monitor_position = egui::Pos2::new(
198 active_monitor.position().x as f32,
199 active_monitor.position().y as f32,
200 );
201 let monitor_size_px = egui::Vec2::new(
202 active_monitor.size().width as f32,
203 active_monitor.size().height as f32,
204 );
205
206 let window_size = (monitor_size_px - window_size_px).max(egui::Vec2::ZERO);
208 *position_px = position_px.clamp(monitor_position, monitor_position + window_size);
212}