use crate::{*, draw::*}; #[cfg(test)] use proptest_derive::Arbitrary; mod xywh; pub use self::xywh::*; mod origin; pub use self::origin::*; mod split; pub use self::split::*; /// A numeric type that can be used as coordinate. /// /// FIXME: Replace with `num` crate? /// FIXME: Use AsRef/AsMut? /// /// ``` /// use tengri::draw::Coord; /// let a: u16 = Coord::zero(); /// let b: u16 = a.plus(1); /// let c: u16 = a.minus(2); /// let d = a.atomic(); /// ``` pub trait Coord: Send + Sync + Copy + Add + Sub + Mul + Div + Ord + PartialEq + Eq + Debug + Display + Default + From + Into + Into + Into + std::iter::Step { /// Zero in own type. fn zero () -> Self { 0.into() } /// Addition. fn plus (self, other: Self) -> Self; /// Saturating subtraction. fn minus (self, other: Self) -> Self { if self >= other { self - other } else { 0.into() } } /// Convert to [AtomicUsize]. fn atomic (self) -> AtomicUsize { AtomicUsize::new(self.into()) } } /// Horizontal axis. pub trait X { fn x (&self) -> N; fn w (&self) -> N { N::zero() } fn w_min (&self) -> N { self.w() } fn w_max (&self) -> N { self.w() } fn iter_x (&self) -> impl Iterator where Self: HasOrigin { self.x_west()..self.x_east() } fn x_west (&self) -> N where Self: HasOrigin { use Origin::*; let w = self.w(); let a = self.origin(); let d = match a { NW|W|SW => 0.into(), N|X|C|Y|S => w/2.into(), NE|E|SE => w }; self.x().minus(d) } fn x_east (&self) -> N where Self: HasOrigin { use Origin::*; let w = self.w(); let a = self.origin(); let d = match a { NW|W|SW => w, N|X|C|Y|S => w/2.into(), NE|E|SE => 0.into() }; self.x().plus(d) } fn x_center (&self) -> N where Self: HasOrigin { todo!() } } pub const fn x_push (x: T::Unit, a: impl Draw) -> impl Draw { a } pub const fn x_pull (x: T::Unit, a: impl Draw) -> impl Draw { a } pub const fn w_max (w: Option, a: impl Draw) -> impl Draw { wh_max(w, None, a) } pub const fn w_min (w: Option, a: impl Draw) -> impl Draw { wh_min(w, None, a) } pub const fn w_exact (w: T::Unit, c: impl Draw) -> impl Draw { wh_exact(Some(w), None, c) } /// Shrink drawing area symmetrically. /// /// ``` /// let padded = tengri::W(3).pad("Hello"); /// ``` pub const fn w_pad (x: T::Unit, draw: impl Draw) -> impl Draw { thunk(move|to: &mut T|draw.draw(todo!())) } pub trait Y { fn y (&self) -> N; fn h (&self) -> N { N::zero() } fn h_min (&self) -> N { self.h() } fn h_max (&self) -> N { self.h() } fn iter_y (&self) -> impl Iterator where Self: HasOrigin { self.y_north()..self.y_south() } fn y_north (&self) -> N where Self: HasOrigin { let a = self.origin(); let h = self.h(); use Origin::*; let d = match a { NW|N|NE => 0.into(), W|X|C|Y|E => h/2.into(), SW|S|SE => h }; self.y().minus(d) } fn y_south (&self) -> N where Self: HasOrigin { let a = self.origin(); let h = self.h(); use Origin::*; let d = match a { NW|N|NE => h, W|X|C|Y|E => h/2.into(), SW|S|SE => 0.into() }; self.y().plus(d) } fn y_center (&self) -> N where Self: HasOrigin { todo!() } } pub const fn y_push (x: T::Unit, a: impl Draw) -> impl Draw { a } pub const fn y_pull (x: T::Unit, a: impl Draw) -> impl Draw { a } pub const fn h_max (h: Option, a: impl Draw) -> impl Draw { wh_max(None, h, a) } pub const fn h_min (h: Option, a: impl Draw) -> impl Draw { wh_min(None, h, a) } pub const fn h_exact (h: T::Unit, c: impl Draw) -> impl Draw { wh_exact(None, Some(h), c) } /// Shrink drawing area symmetrically. /// /// ``` /// let padded = tengri::W::pad(3, "Hello"); /// ``` pub const fn h_pad (x: T::Unit, draw: impl Draw) -> impl Draw { thunk(move|to: &mut T|draw.draw(todo!())) } pub trait Space: X + Y { fn xywh (&self) -> XYWH { XYWH(self.x(), self.y(), self.w(), self.h()) } // FIXME: factor origin fn lrtb (&self) -> [N;4] { [self.x(), self.y(), self.x()+self.w(), self.y()+self.h()] } } impl + Y> Space for T {} pub const fn xy_push (x: T::Unit, y: T::Unit, a: impl Draw) -> impl Draw { a } pub const fn xy_pull (x: T::Unit, y: T::Unit, a: impl Draw) -> impl Draw { a } /// Shrink drawing area symmetrically. /// /// ``` /// let padded = tengri::WH(3, 5).pad("Hello"); /// ``` pub const fn wh_pad (w: T::Unit, h: T::Unit, draw: impl Draw) -> impl Draw { thunk(move|to: &mut T|draw.draw(todo!())) } /// Only draw content if area is above a certain size. /// /// ``` /// let min = tengri::wh_min(3, 5, "Hello"); // 5x5 /// ``` pub const fn wh_min (w: Option, h: Option, draw: impl Draw) -> impl Draw { thunk(move|to: &mut T|draw.draw(todo!())) } /// Set the maximum width and/or height of the content. /// /// ``` /// let max = tengri::wh_max(Some(3), Some(5), "Hello"); /// ``` pub const fn wh_max (w: Option, h: Option, draw: impl Draw) -> impl Draw { thunk(move|to: &mut T|draw.draw(todo!())) } /// Set the maximum width and/or height of the content. /// /// ``` /// let exact = tengri::wh_exact(Some(3), Some(5), "Hello"); /// ``` pub const fn wh_exact (w: Option, h: Option, draw: impl Draw) -> impl Draw { thunk(move|to: &mut T|draw.draw(todo!())) } /// Limit size of drawing area /// ``` /// let clipped = tengri::wh_clip(Some(3), Some(5), "Hello"); /// ``` pub const fn wh_clip ( w: Option, h: Option, draw: impl Draw ) -> impl Draw { thunk(move|to: &mut T|draw.draw(todo!())) } pub const fn wh_full (a: impl Draw) -> impl Draw { a } pub const fn w_full (a: impl Draw) -> impl Draw { a } pub const fn h_full (a: impl Draw) -> impl Draw { a } /// Iterate over a collection of renderables: /// /// ``` /// use tengri::draw::{Origin::*, Split::*}; /// let _ = Below.iter([ /// NW.align(W(15).max(W(10).min("Leftbar"))), /// NE.align(W(12).max(W(10).min("Rightbar"))), /// Center.align(W(40).max(H(20.max("Center")))) /// ].iter(), |x|x); /// ``` pub fn iter < S: Screen, D: Draw, V: Fn()->I, I: Iterator, F: Fn(&D)->dyn Draw, > (_items: V, _cb: F) -> impl Draw { thunk(move|_to: &mut S|{ todo!() }) } pub fn iter_north <'a, S: Screen, D: 'a, I: Iterator, U: Draw> ( _iter: impl Fn()->I, _draw: impl Fn(D)->U, ) -> impl Draw { thunk(move|_to: &mut S|{ todo!() }) } pub fn iter_east <'a, S: Screen, D: 'a, I: Iterator, U: Draw> ( _iter: impl Fn()->I, _draw: impl Fn(D)->U, ) -> impl Draw { thunk(move|_to: &mut S|{ todo!() }) } pub fn iter_south <'a, S: Screen, D: 'a, I: Iterator, U: Draw> ( _iter: impl Fn()->I, _draw: impl Fn(D)->U, ) -> impl Draw { thunk(move|_to: &mut S|{ todo!() }) } pub fn iter_west <'a, S: Screen, D: 'a, I: Iterator, U: Draw> ( _iter: impl Fn()->I, _draw: impl Fn(D)->U, ) -> impl Draw { thunk(move|_to: &mut S|{ todo!() }) } #[derive(Default, Debug)] pub struct Size(AtomicUsize, AtomicUsize); impl X for Size { fn x (&self) -> u16 { 0 } fn w (&self) -> u16 { self.0.load(Relaxed) as u16 } } impl Y for Size { fn y (&self) -> u16 { 0 } fn h (&self) -> u16 { self.1.load(Relaxed) as u16 } } impl Size { pub const fn of (&self, of: impl Draw) -> impl Draw { thunk(move|to: &mut T|{ let area = of.draw(to)?; self.0.store(area.w().into(), Relaxed); self.1.store(area.h().into(), Relaxed); Ok(area) }) } }