use crate::*; /// Drawing target. /// /// ``` /// use crate::*; /// struct TestOut([u16;4]); /// impl Out for TestOut { /// type Unit = u16; /// type Size = [u16;2]; /// type Area = [u16;4]; /// fn area (&self) -> [u16;4] { /// self.0 /// } /// fn area_mut (&mut self) -> &mut [u16;4] { /// &mut self.0 /// } /// fn place_at + ?Sized> (&mut self, area: [u16;4], _: &T) { /// println!("place_at: {area:?}"); /// () /// } /// } /// impl Draw for String { /// fn draw (&self, to: &mut TestOut) { /// to.area_mut().set_w(self.len() as u16); /// } /// } /// ``` pub trait Out: Send + Sync + Sized { /// Unit of length type Unit: Coord; /// Rectangle without offset type Size: HasWH; /// Rectangle with offset type Area: HasXYWH; /// Current output area fn area (&self) -> Self::Area; /// Mutable pointer to area. fn area_mut (&mut self) -> &mut Self::Area; /// Render drawable in area specified by `area` fn place_at <'t, T: Draw + ?Sized> (&mut self, area: Self::Area, content: &'t T); /// Render drawable in area specified by `T::layout(self.area())` #[inline] fn place <'t, T: Content + ?Sized> ( &mut self, content: &'t T ) { self.place_at(content.layout(self.area()), content) } } /// Drawable with dynamic dispatch. pub trait Draw { fn draw (&self, to: &mut O); } /// FIXME: This is a general implementation: should be called `Eval` and be part of [dizzle]. /// Matches [Language] expressions to renderings for a given [Output] target. pub trait View { fn view_expr <'a> (&'a self, _output: &mut O, expr: &'a impl Expression) -> Usually { Err(format!("View::view_expr: no exprs defined: {expr:?}").into()) } fn view_word <'a> (&'a self, _output: &mut O, word: &'a impl Symbol) -> Usually { Err(format!("View::view_word: no words defined: {word:?}").into()) } fn view <'a> (&'a self, output: &mut O, dsl: &'a impl Language) -> Usually { let is_expr = dsl.expr(); let is_word = dsl.word(); match (dsl.expr(), dsl.word()) { (Ok(Some(e)), _ ) => self.view_expr(output, &e), (_, Ok(Some(w))) => self.view_word(output, &w), (Err(e), _ ) => Err(format!("invalid view expr:\n{dsl:?}\n{e}").into()), (_, Err(w) ) => Err(format!("invalid view word:\n{dsl:?}\n{w}").into()), (Ok(None), Ok(None) ) => Err(format!("empty view:\n{dsl:?}").into()), } } } /// Outputs combinator. pub trait Lay: Sized {} /// Drawable area of display. pub trait Layout { fn x (&self, to: O::Area) -> O::Unit { to.x() } fn y (&self, to: O::Area) -> O::Unit { to.y() } fn w_min (&self, _t: O::Area) -> O::Unit { 0.into() } fn w_max (&self, to: O::Area) -> O::Unit { to.w() } fn w (&self, to: O::Area) -> O::Unit { to.w().max(self.w_min(to)).min(self.w_max(to)) } fn h_min (&self, _t: O::Area) -> O::Unit { 0.into() } fn h_max (&self, to: O::Area) -> O::Unit { to.h() } fn h (&self, to: O::Area) -> O::Unit { to.h().max(self.h_min(to)).min(self.h_max(to)) } fn layout (&self, to: O::Area) -> O::Area { [self.x(to), self.y(to), self.w(to), self.h(to)].into() } } // TODO DOCUMENTME pub trait Content: Draw + Layout {} /// A numeric type that can be used as coordinate. /// /// FIXME: Replace this ad-hoc trait with `num` crate. pub trait Coord: 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() } } } // Something that has an origin point (X, Y). pub trait HasXY { fn x (&self) -> N; fn y (&self) -> N; fn xy (&self) -> XY { XY(self.x(), self.y()) } } // Something that has a size (W, H). pub trait HasWH { fn w (&self) -> N; fn h (&self) -> N; fn wh (&self) -> WH { WH(self.w(), self.h()) } } // Something that has a 2D bounding box (X, Y, W, H). pub trait HasXYWH: HasXY + HasWH { fn x2 (&self) -> N { self.x().plus(self.w()) } fn y2 (&self) -> N { self.y().plus(self.h()) } fn xywh (&self) -> XYWH { XYWH(self.x(), self.y(), self.w(), self.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) } } } // TODO DOCUMENTME pub trait HasSize { fn size (&self) -> &Measure; fn width (&self) -> usize { self.size().w() } fn height (&self) -> usize { self.size().h() } }