// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 //! Pupup window handling helpers use crate::lengths::{LogicalPoint, LogicalRect, LogicalSize}; /// A collection of data that might influence the palcement of a `Popup`. pub enum Placement { /// Request a fixed position Fixed(LogicalRect), } /// Find a placement for the `Popup`, using the provided `Placement`. /// When a `clip_region` is provided, then the `Popup` will stay within those bounds. /// The `clip_region` typically is the window or the screen the window is on. pub fn place_popup(placement: Placement, clip_region: &Option) -> LogicalRect { match placement { Placement::Fixed(rect) => { let clip = clip_region.unwrap_or(rect); if clip.contains_rect(&rect) { rect } else { let size = LogicalSize::new( crate::Coord::min(rect.size.width, clip.size.width), crate::Coord::min(rect.size.height, clip.size.height), ); let origin = LogicalPoint::new( rect.origin .x .clamp(clip.origin.x, clip.origin.x + clip.size.width - size.width), rect.origin .y .clamp(clip.origin.y, clip.origin.y + clip.size.height - size.height), ); LogicalRect::new(origin, size) } } } } #[cfg(test)] fn r(x: i32, y: i32, w: i32, h: i32) -> LogicalRect { LogicalRect::new(LogicalPoint::new(x as f32, y as f32), LogicalSize::new(w as f32, h as f32)) } #[cfg(test)] #[track_caller] fn fixed_placement(input: LogicalRect, expected: LogicalRect, clip: Option) { std::eprintln!("fixed: {input:?}, clip({clip:?}) => {expected:?}"); let result = place_popup(Placement::Fixed(input), &clip); if let Some(clip) = clip { clip.contains_rect(&result); } assert_eq!(result, expected); } #[test] fn test_place_popup_fixed_unclipped() { let data = r(5, 5, 100, 100); fixed_placement(data, data, None); let data = r(5, -20, 100, 100); fixed_placement(data, data, None); let data = r(2000, -20, 100, 100); fixed_placement(data, data, None); let data = r(2000, 5, 100, 100); fixed_placement(data, data, None); let data = r(2000, 2000, 100, 100); fixed_placement(data, data, None); let data = r(5, 2000, 100, 100); fixed_placement(data, data, None); let data = r(-20, 2000, 100, 100); fixed_placement(data, data, None); let data = r(-20, 5, 100, 100); fixed_placement(data, data, None); let data = r(-20, -20, 100, 100); fixed_placement(data, data, None); let data = r(-20, -20, 2000, 2000); fixed_placement(data, data, None); } #[test] fn test_place_popup_fixed_clipped() { for (clip_offset_x, clip_offset_y) in [ (-200, -200), (-200, 0), (-200, 200), (0, -200), (0, 0), (0, 200), (200, -200), (200, 0), (200, 200), ] { for (clip_width, clip_height) in [(110, 110), (800, 600)] { let clip = r(clip_offset_x, clip_offset_y, clip_width, clip_height); let x_w = clip_offset_x - 10; let x_c = clip_offset_x + 5; let x_e = clip_offset_x + clip_width - 80; let y_n = clip_offset_y - 10; let y_c = clip_offset_y + 5; let y_s = clip_offset_y + clip_height - 80; let x_min = clip_offset_x; let x_max = clip_offset_x + clip_width; let y_min = clip_offset_y; let y_max = clip_offset_y + clip_height; assert!(clip_width > 105 && clip_width < 1000); assert!(clip_height > 105 && clip_height < 1000); // smaller, inside fixed_placement(r(x_c, y_c, 100, 100), r(x_c, y_c, 100, 100), Some(clip)); // smaller, partial outside fixed_placement(r(x_c, y_n, 100, 100), r(x_c, y_min, 100, 100), Some(clip)); fixed_placement(r(x_e, y_n, 100, 100), r(x_max - 100, y_min, 100, 100), Some(clip)); fixed_placement(r(x_e, y_c, 100, 100), r(x_max - 100, y_c, 100, 100), Some(clip)); fixed_placement( r(x_e, y_s, 100, 100), r(x_max - 100, y_max - 100, 100, 100), Some(clip), ); fixed_placement(r(x_c, y_s, 100, 100), r(x_c, y_max - 100, 100, 100), Some(clip)); fixed_placement(r(x_c, y_s, 100, 100), r(x_c, y_max - 100, 100, 100), Some(clip)); fixed_placement(r(x_w, y_s, 100, 100), r(x_min, y_max - 100, 100, 100), Some(clip)); fixed_placement(r(x_w, y_c, 100, 100), r(x_min, y_c, 100, 100), Some(clip)); fixed_placement(r(x_w, y_n, 100, 100), r(x_min, y_min, 100, 100), Some(clip)); // smaller, totally outside fixed_placement(r(x_c, -2000, 100, 100), r(x_c, y_min, 100, 100), Some(clip)); fixed_placement(r(2000, -2000, 100, 100), r(x_max - 100, y_min, 100, 100), Some(clip)); fixed_placement(r(2000, y_c, 100, 100), r(x_max - 100, y_c, 100, 100), Some(clip)); fixed_placement( r(2000, 2000, 100, 100), r(x_max - 100, y_max - 100, 100, 100), Some(clip), ); fixed_placement(r(x_c, 2000, 100, 100), r(x_c, y_max - 100, 100, 100), Some(clip)); fixed_placement(r(-2000, 2000, 100, 100), r(x_min, y_max - 100, 100, 100), Some(clip)); fixed_placement(r(-2000, y_c, 100, 100), r(x_min, y_c, 100, 100), Some(clip)); fixed_placement(r(-2000, -2000, 100, 100), r(x_min, y_min, 100, 100), Some(clip)); // matching size, covering fixed_placement( r(x_min, y_min, clip_width, clip_height), r(x_min, y_min, clip_width, clip_height), Some(clip), ); // matching size, overlapping fixed_placement( r(x_c, y_c, clip_width, clip_height), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(x_c, y_n, clip_width, clip_height), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(x_e, y_n, clip_width, clip_height), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(x_e, y_c, clip_width, clip_height), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(x_e, y_s, clip_width, clip_height), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(x_c, y_s, clip_width, clip_height), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(x_w, y_s, clip_width, clip_height), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(x_w, y_c, clip_width, clip_height), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(x_w, y_n, clip_width, clip_height), r(x_min, y_min, clip_width, clip_height), Some(clip), ); // too big, overlapping fixed_placement( r(x_c, y_c, clip_width + 5, clip_height + 5), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(x_c, y_n, clip_width + 5, clip_height + 5), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(x_e, y_n, clip_width + 5, clip_height + 5), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(x_e, y_c, clip_width + 5, clip_height + 5), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(x_e, y_s, clip_width + 5, clip_height + 5), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(x_c, y_s, clip_width + 5, clip_height + 5), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(x_w, y_s, clip_width + 5, clip_height + 5), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(x_w, y_c, clip_width + 5, clip_height + 5), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(x_w, y_n, clip_width + 5, clip_height + 5), r(x_min, y_min, clip_width, clip_height), Some(clip), ); // too big, outside fixed_placement( r(x_c, -3000, clip_width + 5, clip_height + 5), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(3000, -3000, clip_width + 5, clip_height + 5), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(3000, y_c, clip_width + 5, clip_height + 5), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(3000, 3000, clip_width + 5, clip_height + 5), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(x_c, 3000, clip_width + 5, clip_height + 5), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(-3000, 3000, clip_width + 5, clip_height + 5), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(-3000, y_c, clip_width + 5, clip_height + 5), r(x_min, y_min, clip_width, clip_height), Some(clip), ); fixed_placement( r(-3000, -3000, clip_width + 5, clip_height + 5), r(x_min, y_min, clip_width, clip_height), Some(clip), ); } } }