1#[cfg(feature = "tray-icon")]
7use crate::Tray;
8use serde_repr::Deserialize_repr;
9#[cfg(feature = "tray-icon")]
10use tauri::Manager;
11#[cfg(feature = "tray-icon")]
12use tauri::Monitor;
13use tauri::{PhysicalPosition, PhysicalSize, Result, Runtime, WebviewWindow, Window};
14
15#[derive(Debug, Deserialize_repr)]
17#[repr(u16)]
18pub enum Position {
19 TopLeft = 0,
20 TopRight,
21 BottomLeft,
22 BottomRight,
23 TopCenter,
24 BottomCenter,
25 LeftCenter,
26 RightCenter,
27 Center,
28 #[cfg(feature = "tray-icon")]
29 TrayLeft,
30 #[cfg(feature = "tray-icon")]
31 TrayBottomLeft,
32 #[cfg(feature = "tray-icon")]
33 TrayRight,
34 #[cfg(feature = "tray-icon")]
35 TrayBottomRight,
36 #[cfg(feature = "tray-icon")]
37 TrayCenter,
38 #[cfg(feature = "tray-icon")]
39 TrayBottomCenter,
40}
41
42pub trait WindowExt {
44 fn move_window(&self, position: Position) -> Result<()>;
48 #[cfg(feature = "tray-icon")]
49 fn move_window_constrained(&self, position: Position) -> Result<()>;
56}
57
58impl<R: Runtime> WindowExt for WebviewWindow<R> {
59 fn move_window(&self, pos: Position) -> Result<()> {
60 self.as_ref().window().move_window(pos)
61 }
62
63 #[cfg(feature = "tray-icon")]
64 fn move_window_constrained(&self, position: Position) -> Result<()> {
65 self.as_ref().window().move_window_constrained(position)
66 }
67}
68
69impl<R: Runtime> WindowExt for Window<R> {
70 #[cfg(feature = "tray-icon")]
71 fn move_window_constrained(&self, position: Position) -> Result<()> {
72 if !matches!(
74 position,
75 Position::TrayLeft
76 | Position::TrayBottomLeft
77 | Position::TrayRight
78 | Position::TrayBottomRight
79 | Position::TrayCenter
80 | Position::TrayBottomCenter
81 ) {
82 return self.move_window(position);
83 }
84
85 let window_position = calculate_position(self, position)?;
86 let monitor = get_monitor_for_tray_icon(self)?;
87 if let Some(monitor) = monitor {
88 let monitor_size = monitor.size();
89 let monitor_position = monitor.position();
90 let window_size = self.outer_size()?;
91
92 let right_border_monitor = monitor_position.x as f64 + monitor_size.width as f64;
93 let left_border_monitor = monitor_position.x as f64;
94 let right_border_window = window_position.x as f64 + window_size.width as f64;
95 let left_border_window = window_position.x as f64;
96
97 let constrained_x = if left_border_window < left_border_monitor {
98 left_border_monitor
99 } else if right_border_window > right_border_monitor {
100 right_border_monitor - window_size.width as f64
101 } else {
102 window_position.x as f64
103 };
104
105 let bottom_border_monitor = monitor_position.y as f64 + monitor_size.height as f64;
106 let top_border_monitor = monitor_position.y as f64;
107 let bottom_border_window = window_position.y as f64 + window_size.height as f64;
108 let top_border_window = window_position.y as f64;
109
110 let constrained_y = if top_border_window < top_border_monitor {
111 top_border_monitor
112 } else if bottom_border_window > bottom_border_monitor {
113 bottom_border_monitor - window_size.height as f64
114 } else {
115 window_position.y as f64
116 };
117
118 self.set_position(PhysicalPosition::new(constrained_x, constrained_y))?;
119 } else {
120 self.set_position(window_position)?;
122 }
123
124 Ok(())
125 }
126
127 fn move_window(&self, pos: Position) -> Result<()> {
128 let position = calculate_position(self, pos)?;
129 self.set_position(position)
130 }
131}
132
133#[cfg(feature = "tray-icon")]
134fn get_monitor_for_tray_icon<R: Runtime>(window: &Window<R>) -> Result<Option<Monitor>> {
136 let tray_position = window
137 .state::<Tray>()
138 .0
139 .lock()
140 .unwrap()
141 .map(|(pos, _)| pos)
142 .unwrap_or_default();
143
144 window.monitor_from_point(tray_position.x, tray_position.y)
145}
146
147fn calculate_position<R: Runtime>(
150 window: &Window<R>,
151 pos: Position,
152) -> Result<PhysicalPosition<i32>> {
153 use Position::*;
154
155 let screen = window.current_monitor()?.unwrap();
156 let screen_position = screen.position();
159 let screen_size = PhysicalSize::<i32> {
160 width: screen.size().width as i32,
161 height: screen.size().height as i32,
162 };
163 let window_size = PhysicalSize::<i32> {
164 width: window.outer_size()?.width as i32,
165 height: window.outer_size()?.height as i32,
166 };
167 #[cfg(feature = "tray-icon")]
168 let (tray_position, tray_size) = window
169 .state::<Tray>()
170 .0
171 .lock()
172 .unwrap()
173 .map(|(pos, size)| {
174 (
175 Some((pos.x as i32, pos.y as i32)),
176 Some((size.width as i32, size.height as i32)),
177 )
178 })
179 .unwrap_or_default();
180
181 let physical_pos = match pos {
182 TopLeft => *screen_position,
183 TopRight => PhysicalPosition {
184 x: screen_position.x + (screen_size.width - window_size.width),
185 y: screen_position.y,
186 },
187 BottomLeft => PhysicalPosition {
188 x: screen_position.x,
189 y: screen_size.height - (window_size.height - screen_position.y),
190 },
191 BottomRight => PhysicalPosition {
192 x: screen_position.x + (screen_size.width - window_size.width),
193 y: screen_size.height - (window_size.height - screen_position.y),
194 },
195 TopCenter => PhysicalPosition {
196 x: screen_position.x + ((screen_size.width / 2) - (window_size.width / 2)),
197 y: screen_position.y,
198 },
199 BottomCenter => PhysicalPosition {
200 x: screen_position.x + ((screen_size.width / 2) - (window_size.width / 2)),
201 y: screen_size.height - (window_size.height - screen_position.y),
202 },
203 LeftCenter => PhysicalPosition {
204 x: screen_position.x,
205 y: screen_position.y + (screen_size.height / 2) - (window_size.height / 2),
206 },
207 RightCenter => PhysicalPosition {
208 x: screen_position.x + (screen_size.width - window_size.width),
209 y: screen_position.y + (screen_size.height / 2) - (window_size.height / 2),
210 },
211 Center => PhysicalPosition {
212 x: screen_position.x + ((screen_size.width / 2) - (window_size.width / 2)),
213 y: screen_position.y + (screen_size.height / 2) - (window_size.height / 2),
214 },
215 #[cfg(feature = "tray-icon")]
216 TrayLeft => {
217 if let (Some((tray_x, tray_y)), Some((_, _tray_height))) = (tray_position, tray_size) {
218 let y = tray_y - window_size.height;
219 #[cfg(target_os = "windows")]
221 let y = if y < 0 { tray_y + _tray_height } else { y };
222
223 #[cfg(target_os = "macos")]
224 let y = if y < 0 { tray_y } else { y };
225
226 PhysicalPosition { x: tray_x, y }
227 } else {
228 panic!("Tray position not set");
229 }
230 }
231 #[cfg(feature = "tray-icon")]
232 TrayBottomLeft => {
233 if let Some((tray_x, tray_y)) = tray_position {
234 PhysicalPosition {
235 x: tray_x,
236 y: tray_y,
237 }
238 } else {
239 panic!("Tray position not set");
240 }
241 }
242 #[cfg(feature = "tray-icon")]
243 TrayRight => {
244 if let (Some((tray_x, tray_y)), Some((tray_width, _tray_height))) =
245 (tray_position, tray_size)
246 {
247 let y = tray_y - window_size.height;
248 #[cfg(target_os = "windows")]
250 let y = if y < 0 { tray_y + _tray_height } else { y };
251
252 #[cfg(target_os = "macos")]
253 let y = if y < 0 { tray_y } else { y };
254
255 PhysicalPosition {
256 x: tray_x + tray_width,
257 y,
258 }
259 } else {
260 panic!("Tray position not set");
261 }
262 }
263 #[cfg(feature = "tray-icon")]
264 TrayBottomRight => {
265 if let (Some((tray_x, tray_y)), Some((tray_width, _))) = (tray_position, tray_size) {
266 PhysicalPosition {
267 x: tray_x + tray_width,
268 y: tray_y,
269 }
270 } else {
271 panic!("Tray position not set");
272 }
273 }
274 #[cfg(feature = "tray-icon")]
275 TrayCenter => {
276 if let (Some((tray_x, tray_y)), Some((tray_width, _tray_height))) =
277 (tray_position, tray_size)
278 {
279 let x = tray_x + tray_width / 2 - window_size.width / 2;
280 let y = tray_y - window_size.height;
281 #[cfg(target_os = "windows")]
283 let y = if y < 0 { tray_y + _tray_height } else { y };
284
285 #[cfg(target_os = "macos")]
286 let y = if y < 0 { tray_y } else { y };
287
288 PhysicalPosition { x, y }
289 } else {
290 panic!("Tray position not set");
291 }
292 }
293 #[cfg(feature = "tray-icon")]
294 TrayBottomCenter => {
295 if let (Some((tray_x, tray_y)), Some((tray_width, _))) = (tray_position, tray_size) {
296 PhysicalPosition {
297 x: tray_x + (tray_width / 2) - (window_size.width / 2),
298 y: tray_y,
299 }
300 } else {
301 panic!("Tray position not set");
302 }
303 }
304 };
305
306 Ok(physical_pos)
307}