use crate::*; use Direction::*; /// A cardinal direction. #[derive(Copy, Clone, PartialEq, Debug)] #[cfg_attr(test, derive(Arbitrary))] pub enum Direction { North, South, East, West, Above, Below } impl Direction { pub fn split_fixed (self, area: impl Area, a: N) -> ([N;4],[N;4]) { let [x, y, w, h] = area.xywh(); match self { North => ([x, y.plus(h).minus(a), w, a], [x, y, w, h.minus(a)]), South => ([x, y, w, a], [x, y.plus(a), w, h.minus(a)]), East => ([x, y, a, h], [x.plus(a), y, w.minus(a), h]), West => ([x.plus(w).minus(a), y, a, h], [x, y, w.minus(a), h]), Above | Below => (area.xywh(), area.xywh()) } } } /// A linear coordinate. pub trait Coordinate: Send + Sync + Copy + Add + Sub + Mul + Div + Ord + PartialEq + Eq + Debug + Display + Default + From + Into + Into + Into { fn zero () -> Self { 0.into() } fn plus (self, other: Self) -> Self; fn minus (self, other: Self) -> Self { if self >= other { self - other } else { 0.into() } } } impl Coordinate for u16 { fn plus (self, other: Self) -> Self { self.saturating_add(other) } } pub trait Area: From<[N;4]> + Debug + Copy { fn x (&self) -> N; fn y (&self) -> N; fn w (&self) -> N; fn h (&self) -> N; fn zero () -> [N;4] { [N::zero(), N::zero(), N::zero(), N::zero()] } fn from_position (pos: impl Size) -> [N;4] { let [x, y] = pos.wh(); [x, y, 0.into(), 0.into()] } fn from_size (size: impl Size) -> [N;4] { let [w, h] = size.wh(); [0.into(), 0.into(), w, h] } fn expect_min (&self, w: N, h: N) -> Usually<&Self> { if self.w() < w || self.h() < h { Err(format!("min {w}x{h}").into()) } else { Ok(self) } } fn xy (&self) -> [N;2] { [self.x(), self.y()] } fn wh (&self) -> [N;2] { [self.w(), self.h()] } fn xywh (&self) -> [N;4] { [self.x(), self.y(), self.w(), self.h()] } fn clip_h (&self, h: N) -> [N;4] { [self.x(), self.y(), self.w(), self.h().min(h)] } fn clip_w (&self, w: N) -> [N;4] { [self.x(), self.y(), self.w().min(w), self.h()] } fn clip (&self, wh: impl Size) -> [N;4] { [self.x(), self.y(), wh.w(), wh.h()] } fn set_w (&self, w: N) -> [N;4] { [self.x(), self.y(), w, self.h()] } fn set_h (&self, h: N) -> [N;4] { [self.x(), self.y(), self.w(), h] } fn x2 (&self) -> N { self.x().plus(self.w()) } fn y2 (&self) -> N { self.y().plus(self.h()) } fn lrtb (&self) -> [N;4] { [self.x(), self.x2(), self.y(), self.y2()] } fn center (&self) -> [N;2] { [self.x().plus(self.w()/2.into()), self.y().plus(self.h()/2.into())] } fn center_x (&self, n: N) -> [N;4] { let [x, y, w, h] = self.xywh(); [(x.plus(w / 2.into())).minus(n / 2.into()), y.plus(h / 2.into()), n, 1.into()] } fn center_y (&self, n: N) -> [N;4] { let [x, y, w, h] = self.xywh(); [x.plus(w / 2.into()), (y.plus(h / 2.into())).minus(n / 2.into()), 1.into(), n] } fn center_xy (&self, [n, m]: [N;2]) -> [N;4] { let [x, y, w, h] = self.xywh(); [(x.plus(w / 2.into())).minus(n / 2.into()), (y.plus(h / 2.into())).minus(m / 2.into()), n, m] } fn centered (&self) -> [N;2] { [self.x().minus(self.w()/2.into()), self.y().minus(self.h()/2.into())] } fn iter_x (&self) -> impl Iterator where N: std::iter::Step { self.x()..(self.x()+self.w()) } fn iter_y (&self) -> impl Iterator where N: std::iter::Step { self.y()..(self.y()+self.h()) } } impl Area for (N, N, N, N) { fn x (&self) -> N { self.0 } fn y (&self) -> N { self.1 } fn w (&self) -> N { self.2 } fn h (&self) -> N { self.3 } } impl Area for [N;4] { fn x (&self) -> N { self[0] } fn y (&self) -> N { self[1] } fn w (&self) -> N { self[2] } fn h (&self) -> N { self[3] } } pub trait Size: From<[N;2]> + Debug + Copy { fn x (&self) -> N; fn y (&self) -> N; fn w (&self) -> N { self.x() } fn h (&self) -> N { self.y() } fn wh (&self) -> [N;2] { [self.x(), self.y()] } fn clip_w (&self, w: N) -> [N;2] { [self.w().min(w), self.h()] } fn clip_h (&self, h: N) -> [N;2] { [self.w(), self.h().min(h)] } fn expect_min (&self, w: N, h: N) -> Usually<&Self> { if self.w() < w || self.h() < h { Err(format!("min {w}x{h}").into()) } else { Ok(self) } } fn zero () -> [N;2] { [N::zero(), N::zero()] } fn to_area_pos (&self) -> [N;4] { let [x, y] = self.wh(); [x, y, 0.into(), 0.into()] } fn to_area_size (&self) -> [N;4] { let [w, h] = self.wh(); [0.into(), 0.into(), w, h] } } impl Size for (N, N) { fn x (&self) -> N { self.0 } fn y (&self) -> N { self.1 } } impl Size for [N;2] { fn x (&self) -> N { self[0] } fn y (&self) -> N { self[1] } } pub trait HasSize { fn size (&self) -> &Measure; fn width (&self) -> usize { self.size().w() } fn height (&self) -> usize { self.size().h() } } impl>> HasSize for T { fn size (&self) -> &Measure { self.get() } } /// A widget that tracks its render width and height #[derive(Default)] pub struct Measure { _engine: PhantomData, pub x: Arc, pub y: Arc, } // TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small impl Content for Measure { fn render (&self, to: &mut E) { self.x.store(to.area().w().into(), Relaxed); self.y.store(to.area().h().into(), Relaxed); } } impl Clone for Measure { fn clone (&self) -> Self { Self { _engine: Default::default(), x: self.x.clone(), y: self.y.clone(), } } } impl std::fmt::Debug for Measure { fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { f.debug_struct("Measure") .field("width", &self.x) .field("height", &self.y) .finish() } } impl Measure { pub fn new () -> Self { Self { _engine: PhantomData::default(), x: Arc::new(0.into()), y: Arc::new(0.into()), } } pub fn set_w (&self, w: impl Into) -> &Self { self.x.store(w.into(), Relaxed); self } pub fn set_h (&self, h: impl Into) -> &Self { self.y.store(h.into(), Relaxed); self } pub fn set_wh (&self, w: impl Into, h: impl Into) -> &Self { self.set_w(w); self.set_h(h); self } pub fn w (&self) -> usize { self.x.load(Relaxed) } pub fn h (&self) -> usize { self.y.load(Relaxed) } pub fn wh (&self) -> [usize;2] { [self.w(), self.h()] } pub fn format (&self) -> Arc { format!("{}x{}", self.w(), self.h()).into() } pub fn of > (&self, item: T) -> Bsp, T> { Bsp::b(Fill::xy(self), item) } }