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 | |
6 | use crate::lengths::{LogicalPoint, LogicalRect, LogicalSize}; |
7 | |
8 | /// A collection of data that might influence the palcement of a `Popup`. |
9 | pub 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. |
17 | pub 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)] |
43 | fn 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 ] |
49 | fn 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 ] |
59 | fn 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 ] |
85 | fn 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 | |