use nami::{Computed, SignalExt, signal::IntoComputed};
use waterui_core::{Environment, metadata::MetadataKey};
use super::padding::EdgeInsets;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[allow(clippy::struct_excessive_bools)]
pub struct EdgeSet {
pub top: bool,
pub leading: bool,
pub bottom: bool,
pub trailing: bool,
}
impl EdgeSet {
pub const ALL: Self = Self {
top: true,
leading: true,
bottom: true,
trailing: true,
};
pub const NONE: Self = Self {
top: false,
leading: false,
bottom: false,
trailing: false,
};
pub const HORIZONTAL: Self = Self {
top: false,
leading: true,
bottom: false,
trailing: true,
};
pub const VERTICAL: Self = Self {
top: true,
leading: false,
bottom: true,
trailing: false,
};
pub const TOP: Self = Self {
top: true,
leading: false,
bottom: false,
trailing: false,
};
pub const BOTTOM: Self = Self {
top: false,
leading: false,
bottom: true,
trailing: false,
};
#[must_use]
#[allow(clippy::fn_params_excessive_bools)]
pub const fn new(top: bool, leading: bool, bottom: bool, trailing: bool) -> Self {
Self {
top,
leading,
bottom,
trailing,
}
}
#[must_use]
pub const fn any(&self) -> bool {
self.top || self.leading || self.bottom || self.trailing
}
#[must_use]
pub const fn all(&self) -> bool {
self.top && self.leading && self.bottom && self.trailing
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IgnoreSafeArea {
pub edges: EdgeSet,
}
impl MetadataKey for IgnoreSafeArea {}
impl IgnoreSafeArea {
#[must_use]
pub const fn new(edges: EdgeSet) -> Self {
Self { edges }
}
#[must_use]
pub const fn all() -> Self {
Self {
edges: EdgeSet::ALL,
}
}
#[must_use]
pub const fn vertical() -> Self {
Self {
edges: EdgeSet::VERTICAL,
}
}
#[must_use]
pub const fn horizontal() -> Self {
Self {
edges: EdgeSet::HORIZONTAL,
}
}
}
#[derive(Debug, Clone)]
pub struct SafeAreaInsets(Computed<EdgeInsets>);
impl SafeAreaInsets {
#[must_use]
pub fn new(insets: impl IntoComputed<EdgeInsets>) -> Self {
Self(insets.into_computed())
}
#[must_use]
pub const fn signal(&self) -> &Computed<EdgeInsets> {
&self.0
}
#[must_use]
pub fn resolve(env: &Environment) -> Computed<EdgeInsets> {
env.get::<Self>().map_or_else(
|| EdgeInsets::default().into_computed(),
|scope| scope.0.clone(),
)
}
#[must_use]
pub fn resolve_with_margin(env: &Environment, margin: EdgeInsets) -> Computed<EdgeInsets> {
Self::resolve(env)
.map(move |insets| insets + margin.clone())
.into_computed()
}
pub fn install(env: &mut Environment, insets: impl IntoComputed<EdgeInsets>) {
env.insert(Self::new(insets));
}
}
#[cfg(test)]
mod tests {
use super::{EdgeInsets, SafeAreaInsets};
use nami::{Signal, binding};
use waterui_core::Environment;
#[test]
fn resolves_to_zero_when_no_backend_published_insets() {
let env = Environment::new();
assert_eq!(SafeAreaInsets::resolve(&env).get(), EdgeInsets::default());
}
#[test]
fn resolved_insets_track_the_published_signal() {
let published = binding(EdgeInsets::new(59.0, 34.0, 0.0, 0.0));
let mut env = Environment::new();
SafeAreaInsets::install(&mut env, published.clone());
let resolved = SafeAreaInsets::resolve(&env);
assert_eq!(resolved.get().top(), 59.0);
published.set(EdgeInsets::new(0.0, 21.0, 59.0, 59.0));
assert_eq!(resolved.get().top(), 0.0);
assert_eq!(resolved.get().leading(), 59.0);
}
#[test]
fn margin_stacks_on_top_of_the_published_insets() {
let mut env = Environment::new();
SafeAreaInsets::install(&mut env, EdgeInsets::new(59.0, 34.0, 0.0, 0.0));
let padded = SafeAreaInsets::resolve_with_margin(&env, EdgeInsets::all(16.0));
assert_eq!(padded.get(), EdgeInsets::new(75.0, 50.0, 16.0, 16.0));
}
#[test]
fn margin_alone_applies_without_a_backend() {
let env = Environment::new();
let padded = SafeAreaInsets::resolve_with_margin(&env, EdgeInsets::all(16.0));
assert_eq!(padded.get(), EdgeInsets::all(16.0));
}
}