1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4//! Pupup window handling helpers
5
6use crate::lengths::{LogicalPoint, LogicalRect, LogicalSize};
7
8/// A collection of data that might influence the palcement of a `Popup`.
9pub enum Placement {
10 /// Request a fixed position
11 Fixed(LogicalRect),
12}
13
14/// Find a placement for the `Popup`, using the provided `Placement`.
15/// When a `clip_region` is provided, then the `Popup` will stay within those bounds.
16/// The `clip_region` typically is the window or the screen the window is on.
17pub fn place_popup(placement: Placement, clip_region: &Option<LogicalRect>) -> LogicalRect {
18 match placement {
19 Placement::Fixed(rect: Rect) => {
20 let clip: Rect = clip_region.unwrap_or(default:rect);
21 if clip.contains_rect(&rect) {
22 rect
23 } else {
24 let size: Size2D = LogicalSize::new(
25 width:crate::Coord::min(rect.size.width, clip.size.width),
26 height:crate::Coord::min(self:rect.size.height, other:clip.size.height),
27 );
28 let origin: Point2D = LogicalPoint::new(
29 x:rect.origin
30 .x
31 .clamp(clip.origin.x, clip.origin.x + clip.size.width - size.width),
32 y:rect.origin
33 .y
34 .clamp(min:clip.origin.y, max:clip.origin.y + clip.size.height - size.height),
35 );
36 LogicalRect::new(origin, size)
37 }
38 }
39 }
40}
41
42#[cfg(test)]
43fn r(x: i32, y: i32, w: i32, h: i32) -> LogicalRect {
44 LogicalRect::new(LogicalPoint::new(x as f32, y as f32), LogicalSize::new(w as f32, h as f32))
45}
46
47#[cfg(test)]
48#[track_caller]
49fn fixed_placement(input: LogicalRect, expected: LogicalRect, clip: Option<LogicalRect>) {
50 std::eprintln!("fixed: {input:?}, clip({clip:?}) => {expected:?}");
51 let result = place_popup(Placement::Fixed(input), &clip);
52 if let Some(clip) = clip {
53 clip.contains_rect(&result);
54 }
55 assert_eq!(result, expected);
56}
57
58#[test]
59fn test_place_popup_fixed_unclipped() {
60 let data = r(5, 5, 100, 100);
61 fixed_placement(data, data, None);
62
63 let data = r(5, -20, 100, 100);
64 fixed_placement(data, data, None);
65 let data = r(2000, -20, 100, 100);
66 fixed_placement(data, data, None);
67 let data = r(2000, 5, 100, 100);
68 fixed_placement(data, data, None);
69 let data = r(2000, 2000, 100, 100);
70 fixed_placement(data, data, None);
71 let data = r(5, 2000, 100, 100);
72 fixed_placement(data, data, None);
73 let data = r(-20, 2000, 100, 100);
74 fixed_placement(data, data, None);
75 let data = r(-20, 5, 100, 100);
76 fixed_placement(data, data, None);
77 let data = r(-20, -20, 100, 100);
78 fixed_placement(data, data, None);
79
80 let data = r(-20, -20, 2000, 2000);
81 fixed_placement(data, data, None);
82}
83
84#[test]
85fn test_place_popup_fixed_clipped() {
86 for (clip_offset_x, clip_offset_y) in [
87 (-200, -200),
88 (-200, 0),
89 (-200, 200),
90 (0, -200),
91 (0, 0),
92 (0, 200),
93 (200, -200),
94 (200, 0),
95 (200, 200),
96 ] {
97 for (clip_width, clip_height) in [(110, 110), (800, 600)] {
98 let clip = r(clip_offset_x, clip_offset_y, clip_width, clip_height);
99
100 let x_w = clip_offset_x - 10;
101 let x_c = clip_offset_x + 5;
102 let x_e = clip_offset_x + clip_width - 80;
103
104 let y_n = clip_offset_y - 10;
105 let y_c = clip_offset_y + 5;
106 let y_s = clip_offset_y + clip_height - 80;
107
108 let x_min = clip_offset_x;
109 let x_max = clip_offset_x + clip_width;
110 let y_min = clip_offset_y;
111 let y_max = clip_offset_y + clip_height;
112
113 assert!(clip_width > 105 && clip_width < 1000);
114 assert!(clip_height > 105 && clip_height < 1000);
115
116 // smaller, inside
117 fixed_placement(r(x_c, y_c, 100, 100), r(x_c, y_c, 100, 100), Some(clip));
118
119 // smaller, partial outside
120 fixed_placement(r(x_c, y_n, 100, 100), r(x_c, y_min, 100, 100), Some(clip));
121 fixed_placement(r(x_e, y_n, 100, 100), r(x_max - 100, y_min, 100, 100), Some(clip));
122 fixed_placement(r(x_e, y_c, 100, 100), r(x_max - 100, y_c, 100, 100), Some(clip));
123 fixed_placement(
124 r(x_e, y_s, 100, 100),
125 r(x_max - 100, y_max - 100, 100, 100),
126 Some(clip),
127 );
128 fixed_placement(r(x_c, y_s, 100, 100), r(x_c, y_max - 100, 100, 100), Some(clip));
129 fixed_placement(r(x_c, y_s, 100, 100), r(x_c, y_max - 100, 100, 100), Some(clip));
130 fixed_placement(r(x_w, y_s, 100, 100), r(x_min, y_max - 100, 100, 100), Some(clip));
131 fixed_placement(r(x_w, y_c, 100, 100), r(x_min, y_c, 100, 100), Some(clip));
132 fixed_placement(r(x_w, y_n, 100, 100), r(x_min, y_min, 100, 100), Some(clip));
133
134 // smaller, totally outside
135 fixed_placement(r(x_c, -2000, 100, 100), r(x_c, y_min, 100, 100), Some(clip));
136 fixed_placement(r(2000, -2000, 100, 100), r(x_max - 100, y_min, 100, 100), Some(clip));
137 fixed_placement(r(2000, y_c, 100, 100), r(x_max - 100, y_c, 100, 100), Some(clip));
138 fixed_placement(
139 r(2000, 2000, 100, 100),
140 r(x_max - 100, y_max - 100, 100, 100),
141 Some(clip),
142 );
143 fixed_placement(r(x_c, 2000, 100, 100), r(x_c, y_max - 100, 100, 100), Some(clip));
144 fixed_placement(r(-2000, 2000, 100, 100), r(x_min, y_max - 100, 100, 100), Some(clip));
145 fixed_placement(r(-2000, y_c, 100, 100), r(x_min, y_c, 100, 100), Some(clip));
146 fixed_placement(r(-2000, -2000, 100, 100), r(x_min, y_min, 100, 100), Some(clip));
147
148 // matching size, covering
149 fixed_placement(
150 r(x_min, y_min, clip_width, clip_height),
151 r(x_min, y_min, clip_width, clip_height),
152 Some(clip),
153 );
154
155 // matching size, overlapping
156 fixed_placement(
157 r(x_c, y_c, clip_width, clip_height),
158 r(x_min, y_min, clip_width, clip_height),
159 Some(clip),
160 );
161 fixed_placement(
162 r(x_c, y_n, clip_width, clip_height),
163 r(x_min, y_min, clip_width, clip_height),
164 Some(clip),
165 );
166
167 fixed_placement(
168 r(x_e, y_n, clip_width, clip_height),
169 r(x_min, y_min, clip_width, clip_height),
170 Some(clip),
171 );
172 fixed_placement(
173 r(x_e, y_c, clip_width, clip_height),
174 r(x_min, y_min, clip_width, clip_height),
175 Some(clip),
176 );
177 fixed_placement(
178 r(x_e, y_s, clip_width, clip_height),
179 r(x_min, y_min, clip_width, clip_height),
180 Some(clip),
181 );
182 fixed_placement(
183 r(x_c, y_s, clip_width, clip_height),
184 r(x_min, y_min, clip_width, clip_height),
185 Some(clip),
186 );
187 fixed_placement(
188 r(x_w, y_s, clip_width, clip_height),
189 r(x_min, y_min, clip_width, clip_height),
190 Some(clip),
191 );
192 fixed_placement(
193 r(x_w, y_c, clip_width, clip_height),
194 r(x_min, y_min, clip_width, clip_height),
195 Some(clip),
196 );
197 fixed_placement(
198 r(x_w, y_n, clip_width, clip_height),
199 r(x_min, y_min, clip_width, clip_height),
200 Some(clip),
201 );
202
203 // too big, overlapping
204 fixed_placement(
205 r(x_c, y_c, clip_width + 5, clip_height + 5),
206 r(x_min, y_min, clip_width, clip_height),
207 Some(clip),
208 );
209 fixed_placement(
210 r(x_c, y_n, clip_width + 5, clip_height + 5),
211 r(x_min, y_min, clip_width, clip_height),
212 Some(clip),
213 );
214 fixed_placement(
215 r(x_e, y_n, clip_width + 5, clip_height + 5),
216 r(x_min, y_min, clip_width, clip_height),
217 Some(clip),
218 );
219 fixed_placement(
220 r(x_e, y_c, clip_width + 5, clip_height + 5),
221 r(x_min, y_min, clip_width, clip_height),
222 Some(clip),
223 );
224 fixed_placement(
225 r(x_e, y_s, clip_width + 5, clip_height + 5),
226 r(x_min, y_min, clip_width, clip_height),
227 Some(clip),
228 );
229 fixed_placement(
230 r(x_c, y_s, clip_width + 5, clip_height + 5),
231 r(x_min, y_min, clip_width, clip_height),
232 Some(clip),
233 );
234 fixed_placement(
235 r(x_w, y_s, clip_width + 5, clip_height + 5),
236 r(x_min, y_min, clip_width, clip_height),
237 Some(clip),
238 );
239 fixed_placement(
240 r(x_w, y_c, clip_width + 5, clip_height + 5),
241 r(x_min, y_min, clip_width, clip_height),
242 Some(clip),
243 );
244 fixed_placement(
245 r(x_w, y_n, clip_width + 5, clip_height + 5),
246 r(x_min, y_min, clip_width, clip_height),
247 Some(clip),
248 );
249
250 // too big, outside
251 fixed_placement(
252 r(x_c, -3000, clip_width + 5, clip_height + 5),
253 r(x_min, y_min, clip_width, clip_height),
254 Some(clip),
255 );
256 fixed_placement(
257 r(3000, -3000, clip_width + 5, clip_height + 5),
258 r(x_min, y_min, clip_width, clip_height),
259 Some(clip),
260 );
261 fixed_placement(
262 r(3000, y_c, clip_width + 5, clip_height + 5),
263 r(x_min, y_min, clip_width, clip_height),
264 Some(clip),
265 );
266 fixed_placement(
267 r(3000, 3000, clip_width + 5, clip_height + 5),
268 r(x_min, y_min, clip_width, clip_height),
269 Some(clip),
270 );
271 fixed_placement(
272 r(x_c, 3000, clip_width + 5, clip_height + 5),
273 r(x_min, y_min, clip_width, clip_height),
274 Some(clip),
275 );
276 fixed_placement(
277 r(-3000, 3000, clip_width + 5, clip_height + 5),
278 r(x_min, y_min, clip_width, clip_height),
279 Some(clip),
280 );
281 fixed_placement(
282 r(-3000, y_c, clip_width + 5, clip_height + 5),
283 r(x_min, y_min, clip_width, clip_height),
284 Some(clip),
285 );
286 fixed_placement(
287 r(-3000, -3000, clip_width + 5, clip_height + 5),
288 r(x_min, y_min, clip_width, clip_height),
289 Some(clip),
290 );
291 }
292 }
293}
294