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