Skip to main content

argui_core/
insets.rs

1use crate::Rect;
2
3/// Distances from a window edge to the region kept clear for system UI.
4///
5/// Values use logical pixels, matching Argui layout coordinates. The platform
6/// adapter decides which system regions to include.
7#[derive(Clone, Copy, Debug, Default, PartialEq)]
8pub struct Insets {
9    pub top: f32,
10    pub right: f32,
11    pub bottom: f32,
12    pub left: f32,
13}
14
15impl Insets {
16    pub const ZERO: Self = Self {
17        top: 0.0,
18        right: 0.0,
19        bottom: 0.0,
20        left: 0.0,
21    };
22
23    #[must_use]
24    pub const fn new(top: f32, right: f32, bottom: f32, left: f32) -> Self {
25        Self {
26            top,
27            right,
28            bottom,
29            left,
30        }
31    }
32
33    /// Derive insets when both physical rectangles describe non-empty areas.
34    ///
35    /// Platform APIs can report an empty safe rectangle while a native window
36    /// is being created. Treat that as unavailable until a later update rather
37    /// than interpreting it as the whole window being obscured.
38    #[must_use]
39    pub fn try_from_physical_rects(window: Rect, safe: Rect, scale_factor: f32) -> Option<Self> {
40        let coordinates = [
41            window.origin.x,
42            window.origin.y,
43            window.size.width,
44            window.size.height,
45            safe.origin.x,
46            safe.origin.y,
47            safe.size.width,
48            safe.size.height,
49        ];
50        if coordinates.iter().any(|value| !value.is_finite())
51            || window.size.width <= 0.0
52            || window.size.height <= 0.0
53            || safe.size.width <= 0.0
54            || safe.size.height <= 0.0
55        {
56            return None;
57        }
58
59        let safe = window.intersection(safe)?;
60        Some(Self::from_physical_rects(window, safe, scale_factor))
61    }
62
63    /// Derive logical insets from a physical window rectangle and its safe
64    /// subrectangle. Coordinates in both rectangles use the same screen space.
65    #[must_use]
66    pub fn from_physical_rects(window: Rect, safe: Rect, scale_factor: f32) -> Self {
67        let scale_factor = if scale_factor.is_finite() && scale_factor > 0.0 {
68            scale_factor
69        } else {
70            1.0
71        };
72        let width = window.size.width.max(0.0);
73        let height = window.size.height.max(0.0);
74        let safe_width = safe.size.width.max(0.0);
75        let safe_height = safe.size.height.max(0.0);
76        let left = (safe.origin.x - window.origin.x).clamp(0.0, width);
77        let right = (safe.origin.x + safe_width - window.origin.x).clamp(left, width);
78        let top = (safe.origin.y - window.origin.y).clamp(0.0, height);
79        let bottom = (safe.origin.y + safe_height - window.origin.y).clamp(top, height);
80        Self::new(
81            top / scale_factor,
82            (width - right) / scale_factor,
83            (height - bottom) / scale_factor,
84            left / scale_factor,
85        )
86    }
87
88    #[must_use]
89    pub const fn horizontal(self) -> f32 {
90        self.left + self.right
91    }
92
93    #[must_use]
94    pub const fn vertical(self) -> f32 {
95        self.top + self.bottom
96    }
97}