Skip to main content

tessera_ui/
window_insets.rs

1//! Window safe areas and on-screen keyboard occlusion.
2//!
3//! Use [`WindowInsets`] with inset layout modifiers. Platform integrations publish
4//! changes through the session's [`WindowInsetsController`].
5
6use crate::{Dp, State, StateOwner};
7
8pub(crate) mod platform;
9
10/// Insets values in density-independent pixels.
11#[derive(Clone, Copy, Debug, Default, PartialEq)]
12pub struct Insets {
13    /// Left insets.
14    pub left: Dp,
15    /// Top insets.
16    pub top: Dp,
17    /// Right insets.
18    pub right: Dp,
19    /// Bottom insets.
20    pub bottom: Dp,
21}
22
23impl Insets {
24    /// Creates insets with explicit edges.
25    pub const fn new(left: Dp, top: Dp, right: Dp, bottom: Dp) -> Self {
26        Self {
27            left,
28            top,
29            right,
30            bottom,
31        }
32    }
33
34    /// Creates symmetric insets on all edges.
35    pub const fn all(value: Dp) -> Self {
36        Self {
37            left: value,
38            top: value,
39            right: value,
40            bottom: value,
41        }
42    }
43
44    /// Creates symmetric insets for horizontal and vertical edges.
45    pub const fn symmetric(horizontal: Dp, vertical: Dp) -> Self {
46        Self {
47            left: horizontal,
48            top: vertical,
49            right: horizontal,
50            bottom: vertical,
51        }
52    }
53
54    /// Creates insets with only horizontal edges.
55    pub const fn horizontal(value: Dp) -> Self {
56        Self::symmetric(value, Dp(0.0))
57    }
58
59    /// Creates insets with only vertical edges.
60    pub const fn vertical(value: Dp) -> Self {
61        Self::symmetric(Dp(0.0), value)
62    }
63
64    /// Creates insets with only the left edge.
65    pub const fn left(value: Dp) -> Self {
66        Self {
67            left: value,
68            top: Dp(0.0),
69            right: Dp(0.0),
70            bottom: Dp(0.0),
71        }
72    }
73
74    /// Creates insets with only the top edge.
75    pub const fn top(value: Dp) -> Self {
76        Self {
77            left: Dp(0.0),
78            top: value,
79            right: Dp(0.0),
80            bottom: Dp(0.0),
81        }
82    }
83
84    /// Creates insets with only the right edge.
85    pub const fn right(value: Dp) -> Self {
86        Self {
87            left: Dp(0.0),
88            top: Dp(0.0),
89            right: value,
90            bottom: Dp(0.0),
91        }
92    }
93
94    /// Creates insets with only the bottom edge.
95    pub const fn bottom(value: Dp) -> Self {
96        Self {
97            left: Dp(0.0),
98            top: Dp(0.0),
99            right: Dp(0.0),
100            bottom: value,
101        }
102    }
103}
104
105impl Insets {
106    /// Combines edge lengths by taking the larger value on each edge.
107    pub fn union(self, other: Self) -> Self {
108        Self::new(
109            Dp(self.left.0.max(other.left.0)),
110            Dp(self.top.0.max(other.top.0)),
111            Dp(self.right.0.max(other.right.0)),
112            Dp(self.bottom.0.max(other.bottom.0)),
113        )
114    }
115
116    /// Subtracts occupied edges, clamping each remaining length to zero.
117    pub fn exclude(self, consumed: Self) -> Self {
118        Self::new(
119            Dp((self.left.0 - consumed.left.0).max(0.0)),
120            Dp((self.top.0 - consumed.top.0).max(0.0)),
121            Dp((self.right.0 - consumed.right.0).max(0.0)),
122            Dp((self.bottom.0 - consumed.bottom.0).max(0.0)),
123        )
124    }
125}
126
127/// A live window inset source, read during measurement by inset modifiers.
128#[derive(Clone, Copy, Debug, PartialEq)]
129pub enum WindowInsets {
130    /// Status bars, navigation bars, cutouts and other window safe areas.
131    SystemBars,
132    /// The docked on-screen keyboard occluding the drawable window.
133    Ime,
134    /// The union of system bars and keyboard occlusion.
135    SafeDrawing,
136    /// Explicit edge lengths, including custom parent-consumed space.
137    Fixed(Insets),
138}
139
140impl WindowInsets {
141    /// Reads current lengths and tracks the active build or layout dependency.
142    pub fn get(self) -> Insets {
143        if let Self::Fixed(insets) = self {
144            return insets;
145        }
146        let (system, ime) = WindowInsetsController::current().0.get();
147        match self {
148            Self::SystemBars => system,
149            Self::Ime => ime,
150            _ => system.union(ime),
151        }
152    }
153}
154
155/// A copyable controller for one application session's window insets.
156#[derive(Clone, Copy, PartialEq)]
157pub struct WindowInsetsController(State<(Insets, Insets)>);
158
159struct WindowInsetsState {
160    _owner: StateOwner,
161    controller: WindowInsetsController,
162}
163
164impl Default for WindowInsetsState {
165    fn default() -> Self {
166        let owner = StateOwner::new();
167        let controller = WindowInsetsController(owner.state_with_key((), Default::default));
168        Self {
169            _owner: owner,
170            controller,
171        }
172    }
173}
174
175impl WindowInsetsController {
176    /// Returns the active application's window inset controller.
177    pub fn current() -> Self {
178        crate::local_runtime_resource::<WindowInsetsState>().controller
179    }
180
181    /// Publishes safe-area and keyboard lengths in density-independent units.
182    /// Unchanged values preserve cached builds and measurements.
183    pub fn set(self, system_bars: Insets, ime: Insets) {
184        self.0.set_if_changed((system_bars, ime));
185    }
186}