use std::cell::RefCell;
use crate::Modifier;
use cranpose_core::{compositionLocalOf, CompositionLocal};
use cranpose_ui_graphics::EdgeInsets;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct WindowInsets {
pub safe_area: EdgeInsets,
pub ime: EdgeInsets,
}
impl WindowInsets {
pub fn combined(self) -> EdgeInsets {
EdgeInsets::from_components(
self.safe_area.left.max(self.ime.left),
self.safe_area.top.max(self.ime.top),
self.safe_area.right.max(self.ime.right),
self.safe_area.bottom.max(self.ime.bottom),
)
}
}
pub fn local_safe_area_insets() -> CompositionLocal<EdgeInsets> {
thread_local! {
static LOCAL: RefCell<Option<CompositionLocal<EdgeInsets>>> = const { RefCell::new(None) };
}
LOCAL.with(|cell| {
cell.borrow_mut()
.get_or_insert_with(|| compositionLocalOf(EdgeInsets::default))
.clone()
})
}
pub fn local_ime_insets() -> CompositionLocal<EdgeInsets> {
thread_local! {
static LOCAL: RefCell<Option<CompositionLocal<EdgeInsets>>> = const { RefCell::new(None) };
}
LOCAL.with(|cell| {
cell.borrow_mut()
.get_or_insert_with(|| compositionLocalOf(EdgeInsets::default))
.clone()
})
}
pub fn window_insets() -> WindowInsets {
WindowInsets {
safe_area: local_safe_area_insets().current(),
ime: local_ime_insets().current(),
}
}
impl Modifier {
pub fn window_insets_padding(self, insets: EdgeInsets) -> Self {
self.padding_each(insets.left, insets.top, insets.right, insets.bottom)
}
pub fn safe_area_padding(self) -> Self {
self.window_insets_padding(local_safe_area_insets().current())
}
}
#[cfg(test)]
mod tests {
use super::{local_ime_insets, local_safe_area_insets, WindowInsets};
use cranpose_ui_graphics::EdgeInsets;
#[test]
fn defaults_to_zero_insets() {
assert_eq!(
local_safe_area_insets().default_value(),
EdgeInsets::default()
);
}
#[test]
fn ime_insets_default_to_zero() {
assert_eq!(local_ime_insets().default_value(), EdgeInsets::default());
}
#[test]
fn returns_one_shared_local_per_thread() {
assert!(local_safe_area_insets() == local_safe_area_insets());
assert!(local_ime_insets() == local_ime_insets());
assert!(local_ime_insets() != local_safe_area_insets());
}
#[test]
fn combined_insets_do_not_double_count_overlapping_edges() {
let combined = WindowInsets {
safe_area: EdgeInsets::from_components(2.0, 8.0, 4.0, 20.0),
ime: EdgeInsets::from_components(0.0, 0.0, 6.0, 100.0),
}
.combined();
assert_eq!(combined, EdgeInsets::from_components(2.0, 8.0, 6.0, 100.0));
}
#[test]
fn explicit_window_insets_become_padding() {
use crate::modifier::ModifierChainHandle;
use crate::Modifier;
let _app_context = crate::render_state::app_context_test_scope();
let insets = EdgeInsets::from_components(1.0, 2.0, 3.0, 4.0);
let mut handle = ModifierChainHandle::new();
let _ = handle.update(&Modifier::empty().window_insets_padding(insets));
assert_eq!(handle.resolved_modifiers().padding(), insets);
}
}