i_slint_core/window/
popup.rs

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 placement 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) => {
20            let clip = clip_region.unwrap_or(rect);
21            if clip.contains_rect(&rect) {
22                rect
23            } else {
24                let size = LogicalSize::new(
25                    crate::Coord::min(rect.size.width, clip.size.width),
26                    crate::Coord::min(rect.size.height, clip.size.height),
27                );
28                let origin = LogicalPoint::new(
29                    rect.origin
30                        .x
31                        .clamp(clip.origin.x, clip.origin.x + clip.size.width - size.width),
32                    rect.origin
33                        .y
34                        .clamp(clip.origin.y, 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}