diff --git a/output/src/lib.rs b/output/src/lib.rs index 65c7580..40d31f0 100644 --- a/output/src/lib.rs +++ b/output/src/lib.rs @@ -16,7 +16,7 @@ pub(crate) use std::sync::{Arc, RwLock, atomic::{AtomicUsize, Ordering::Relaxed} pub(crate) use std::marker::PhantomData; pub(crate) use dizzle::*; -// Define macros first, so that private macros are available in private modules. +// Define macros first, so that private macros are available in private modules: /// Clear a pre-allocated buffer, then write into it. #[macro_export] macro_rules! rewrite { @@ -117,8 +117,8 @@ macro_rules! layout_op_xy ( macro_rules! push_pull(($T:ident: $method: ident)=>{ layout_op_xy!(1: $T); impl> Layout for $T { - fn x (&self, area: O::Area) -> O::Unit { area.x().$method(self.dx()) } - fn y (&self, area: O::Area) -> O::Unit { area.y().$method(self.dy()) } + fn layout_x (&self, area: XYWH) -> O::Unit { area.x().$method(self.dx()) } + fn layout_y (&self, area: XYWH) -> O::Unit { area.y().$method(self.dy()) } } }); diff --git a/output/src/out_impls.rs b/output/src/out_impls.rs index 99dfdc6..787d752 100644 --- a/output/src/out_impls.rs +++ b/output/src/out_impls.rs @@ -49,37 +49,62 @@ impl WH { } } impl XYWH { - fn with_w (&self, w: N) -> XYWH { Self(self.x(), self.y(), w, self.h()) } - fn with_h (&self, h: N) -> XYWH { Self(self.x(), self.y(), self.w(), h) } - fn lrtb (&self) -> XYWH { Self(self.x(), self.x2(), self.y(), self.y2()) } - fn clipped_w (&self, w: N) -> XYWH { Self(self.x(), self.y(), self.w().min(w), self.h()) } - fn clipped_h (&self, h: N) -> XYWH { Self(self.x(), self.y(), self.w(), self.h().min(h)) } - fn clipped (&self, wh: impl HasWH) -> XYWH { Self(self.x(), self.y(), wh.w(), wh.h()) } + fn zero () -> Self { + Self(0.into(), 0.into(), 0.into(), 0.into()) + } + fn x2 (&self) -> N { + self.x().plus(self.w()) + } + fn y2 (&self) -> N { + self.y().plus(self.h()) + } + fn with_w (&self, w: N) -> XYWH { + Self(self.x(), self.y(), w, self.h()) + } + fn with_h (&self, h: N) -> XYWH { + Self(self.x(), self.y(), self.w(), h) + } + fn lrtb (&self) -> XYWH { + Self(self.x(), self.x2(), self.y(), self.y2()) + } + fn clipped_w (&self, w: N) -> XYWH { + Self(self.x(), self.y(), self.w().min(w), self.h()) + } + fn clipped_h (&self, h: N) -> XYWH { + Self(self.x(), self.y(), self.w(), self.h().min(h)) + } + fn clipped (&self, wh: WH) -> XYWH { + Self(self.x(), self.y(), wh.w(), wh.h()) + } /// Iterate over every covered X coordinate. fn iter_x (&self) -> impl Iterator where N: std::iter::Step { - self.x()..(self.x()+self.w()) + let Self(x, _, w, _) = *self; + x..(x+w) } /// Iterate over every covered Y coordinate. fn iter_y (&self) -> impl Iterator where N: std::iter::Step { - self.y()..(self.y()+self.h()) + let Self(_, y, _, h) = *self; + y..(y+h) } fn center (&self) -> XY { + let Self(x, y, w, h) = self; XY(self.x().plus(self.w()/2.into()), self.y().plus(self.h()/2.into())) } fn centered (&self) -> XY { - XY(self.x().minus(self.w()/2.into()), self.y().minus(self.h()/2.into())) + let Self(x, y, w, h) = *self; + XY(x.minus(w/2.into()), y.minus(h/2.into())) } fn centered_x (&self, n: N) -> XYWH { - let [x, y, w, h] = self.xywh(); - Self((x.plus(w / 2.into())).minus(n / 2.into()), y.plus(h / 2.into()), n, 1.into()) + let Self(x, y, w, h) = *self; + XYWH((x.plus(w / 2.into())).minus(n / 2.into()), y.plus(h / 2.into()), n, 1.into()) } fn centered_y (&self, n: N) -> XYWH { - let [x, y, w, h] = self.xywh(); - Self(x.plus(w / 2.into()), (y.plus(h / 2.into())).minus(n / 2.into()), 1.into(), n) + let Self(x, y, w, h) = *self; + XYWH(x.plus(w / 2.into()), (y.plus(h / 2.into())).minus(n / 2.into()), 1.into(), n) } fn centered_xy (&self, [n, m]: [N;2]) -> XYWH { - let [x, y, w, h] = self.xywh(); - Self((x.plus(w / 2.into())).minus(n / 2.into()), (y.plus(h / 2.into())).minus(m / 2.into()), n, m) + let Self(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) } } @@ -167,20 +192,20 @@ impl Memo { } impl Direction { - pub fn split_fixed (self, area: impl HasXYWH, a: N) -> ([N;4],[N;4]) { - let XYWH(x, y, w, h) = area.xywh(); + pub fn split_fixed (self, area: XYWH, a: N) -> (XYWH, XYWH) { + let XYWH(x, y, w, h) = area; 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()) + North => (XYWH(x, y.plus(h).minus(a), w, a), XYWH(x, y, w, h.minus(a))), + South => (XYWH(x, y, w, a), XYWH(x, y.plus(a), w, h.minus(a))), + East => (XYWH(x, y, a, h), XYWH(x.plus(a), y, w.minus(a), h)), + West => (XYWH(x.plus(w).minus(a), y, a, h), XYWH(x, y, w.minus(a), h)), + Above | Below => (area, area) } } } -impl>> HasSize for T { - fn size (&self) -> &Measure { +impl>> Measured for T { + fn measure (&self) -> &Measure { self.get() } } @@ -221,97 +246,104 @@ impl Measure { 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) } pub fn new (x: O::Unit, y: O::Unit) -> Self { - Self { __: PhantomData::default(), x: Arc::new(x.into()), y: Arc::new(y.into()), } + Self { __: PhantomData::default(), x: Arc::new(x.atomic()), y: Arc::new(y.atomic()), } } } -impl From<[O::Unit; 2]> for Measure { - fn from ([x, y]: [O::Unit; 2]) -> Self { Self::new(x, y) } +/// FIXME don't convert to u16 specifically +impl HasWH for Measure { + fn w (&self) -> O::Unit { (self.x.load(Relaxed) as u16).into() } + fn h (&self) -> O::Unit { (self.y.load(Relaxed) as u16).into() } +} +impl From> for Measure { + fn from (WH(x, y): WH) -> Self { Self::new(x, y) } } impl Layout for () { - fn x (&self, a: O::Area) -> O::Unit { a.x() } - fn y (&self, a: O::Area) -> O::Unit { a.y() } - fn w (&self, _: O::Area) -> O::Unit { 0.into() } - fn w_min (&self, _: O::Area) -> O::Unit { 0.into() } - fn w_max (&self, _: O::Area) -> O::Unit { 0.into() } - fn h (&self, _: O::Area) -> O::Unit { 0.into() } - fn h_min (&self, _: O::Area) -> O::Unit { 0.into() } - fn h_max (&self, _: O::Area) -> O::Unit { 0.into() } - fn layout (&self, a: O::Area) -> O::Area { [a.x(), a.y(), 0.into(), 0.into()].into() } + fn layout_x (&self, a: XYWH) -> O::Unit { a.x() } + fn layout_y (&self, a: XYWH) -> O::Unit { a.y() } + fn layout_w (&self, _: XYWH) -> O::Unit { 0.into() } + fn layout_w_min (&self, _: XYWH) -> O::Unit { 0.into() } + fn layout_w_max (&self, _: XYWH) -> O::Unit { 0.into() } + fn layout_h (&self, _: XYWH) -> O::Unit { 0.into() } + fn layout_h_min (&self, _: XYWH) -> O::Unit { 0.into() } + fn layout_h_max (&self, _: XYWH) -> O::Unit { 0.into() } + fn layout (&self, a: XYWH) -> XYWH { XYWH(a.x(), a.y(), 0.into(), 0.into()) } } impl> Layout for &L { - fn x (&self, a: O::Area) -> O::Unit { (*self).x(a) } - fn y (&self, a: O::Area) -> O::Unit { (*self).y(a) } - fn w (&self, a: O::Area) -> O::Unit { (*self).w(a) } - fn w_min (&self, a: O::Area) -> O::Unit { (*self).w_min(a) } - fn w_max (&self, a: O::Area) -> O::Unit { (*self).w_max(a) } - fn h (&self, a: O::Area) -> O::Unit { (*self).h(a) } - fn h_min (&self, a: O::Area) -> O::Unit { (*self).h_min(a) } - fn h_max (&self, a: O::Area) -> O::Unit { (*self).h_max(a) } - fn layout (&self, a: O::Area) -> O::Area { (*self).layout(a) } + fn layout_x (&self, a: XYWH) -> O::Unit { (*self).layout_x(a) } + fn layout_y (&self, a: XYWH) -> O::Unit { (*self).layout_y(a) } + fn layout_w (&self, a: XYWH) -> O::Unit { (*self).layout_w(a) } + fn layout_w_min (&self, a: XYWH) -> O::Unit { (*self).layout_w_min(a) } + fn layout_w_max (&self, a: XYWH) -> O::Unit { (*self).layout_w_max(a) } + fn layout_h (&self, a: XYWH) -> O::Unit { (*self).layout_h(a) } + fn layout_h_min (&self, a: XYWH) -> O::Unit { (*self).layout_h_min(a) } + fn layout_h_max (&self, a: XYWH) -> O::Unit { (*self).layout_h_max(a) } + fn layout (&self, a: XYWH) -> XYWH { (*self).layout(a) } } impl> Layout for &mut L { - fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) } - fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) } - fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) } - fn w_min (&self, a: O::Area) -> O::Unit { (**self).w_min(a) } - fn w_max (&self, a: O::Area) -> O::Unit { (**self).w_max(a) } - fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) } - fn h_min (&self, a: O::Area) -> O::Unit { (**self).h_min(a) } - fn h_max (&self, a: O::Area) -> O::Unit { (**self).h_max(a) } - fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) } + fn layout_x (&self, a: XYWH) -> O::Unit { (**self).layout_x(a) } + fn layout_y (&self, a: XYWH) -> O::Unit { (**self).layout_y(a) } + fn layout_w (&self, a: XYWH) -> O::Unit { (**self).layout_w(a) } + fn layout_w_min (&self, a: XYWH) -> O::Unit { (**self).layout_w_min(a) } + fn layout_w_max (&self, a: XYWH) -> O::Unit { (**self).layout_w_max(a) } + fn layout_h (&self, a: XYWH) -> O::Unit { (**self).layout_h(a) } + fn layout_h_min (&self, a: XYWH) -> O::Unit { (**self).layout_h_min(a) } + fn layout_h_max (&self, a: XYWH) -> O::Unit { (**self).layout_h_max(a) } + fn layout (&self, a: XYWH) -> XYWH { (**self).layout(a) } } impl> Layout for Arc { - fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) } - fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) } - fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) } - fn w_min (&self, a: O::Area) -> O::Unit { (**self).w_min(a) } - fn w_max (&self, a: O::Area) -> O::Unit { (**self).w_max(a) } - fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) } - fn h_min (&self, a: O::Area) -> O::Unit { (**self).h_min(a) } - fn h_max (&self, a: O::Area) -> O::Unit { (**self).h_max(a) } - fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) } + fn layout_x (&self, a: XYWH) -> O::Unit { (**self).layout_x(a) } + fn layout_y (&self, a: XYWH) -> O::Unit { (**self).layout_y(a) } + fn layout_w (&self, a: XYWH) -> O::Unit { (**self).layout_w(a) } + fn layout_w_min (&self, a: XYWH) -> O::Unit { (**self).layout_w_min(a) } + fn layout_w_max (&self, a: XYWH) -> O::Unit { (**self).layout_w_max(a) } + fn layout_h (&self, a: XYWH) -> O::Unit { (**self).layout_h(a) } + fn layout_h_min (&self, a: XYWH) -> O::Unit { (**self).layout_h_min(a) } + fn layout_h_max (&self, a: XYWH) -> O::Unit { (**self).layout_h_max(a) } + fn layout (&self, a: XYWH) -> XYWH { (**self).layout(a) } } impl Layout for Box> { - fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) } - fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) } - fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) } - fn w_min (&self, a: O::Area) -> O::Unit { (**self).w_min(a) } - fn w_max (&self, a: O::Area) -> O::Unit { (**self).w_max(a) } - fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) } - fn h_min (&self, a: O::Area) -> O::Unit { (**self).h_min(a) } - fn h_max (&self, a: O::Area) -> O::Unit { (**self).h_max(a) } - fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) } + fn layout_x (&self, a: XYWH) -> O::Unit { (**self).layout_x(a) } + fn layout_y (&self, a: XYWH) -> O::Unit { (**self).layout_y(a) } + fn layout_w (&self, a: XYWH) -> O::Unit { (**self).layout_w(a) } + fn layout_w_min (&self, a: XYWH) -> O::Unit { (**self).layout_w_min(a) } + fn layout_w_max (&self, a: XYWH) -> O::Unit { (**self).layout_w_max(a) } + fn layout_h (&self, a: XYWH) -> O::Unit { (**self).layout_h(a) } + fn layout_h_min (&self, a: XYWH) -> O::Unit { (**self).layout_h_min(a) } + fn layout_h_max (&self, a: XYWH) -> O::Unit { (**self).layout_h_max(a) } + fn layout (&self, a: XYWH) -> XYWH { (**self).layout(a) } } impl> Layout for RwLock { - fn x (&self, a: O::Area) -> O::Unit { self.read().unwrap().x(a) } - fn y (&self, a: O::Area) -> O::Unit { self.read().unwrap().y(a) } - fn w (&self, a: O::Area) -> O::Unit { self.read().unwrap().w(a) } - fn w_min (&self, a: O::Area) -> O::Unit { self.read().unwrap().w_min(a) } - fn w_max (&self, a: O::Area) -> O::Unit { self.read().unwrap().w_max(a) } - fn h (&self, a: O::Area) -> O::Unit { self.read().unwrap().h(a) } - fn h_min (&self, a: O::Area) -> O::Unit { self.read().unwrap().h_min(a) } - fn h_max (&self, a: O::Area) -> O::Unit { self.read().unwrap().h_max(a) } - fn layout (&self, a: O::Area) -> O::Area { self.read().unwrap().layout(a) } + fn layout_x (&self, a: XYWH) -> O::Unit { self.read().unwrap().layout_x(a) } + fn layout_y (&self, a: XYWH) -> O::Unit { self.read().unwrap().layout_y(a) } + fn layout_w (&self, a: XYWH) -> O::Unit { self.read().unwrap().layout_w(a) } + fn layout_w_min (&self, a: XYWH) -> O::Unit { self.read().unwrap().layout_w_min(a) } + fn layout_w_max (&self, a: XYWH) -> O::Unit { self.read().unwrap().layout_w_max(a) } + fn layout_h (&self, a: XYWH) -> O::Unit { self.read().unwrap().layout_h(a) } + fn layout_h_min (&self, a: XYWH) -> O::Unit { self.read().unwrap().layout_h_min(a) } + fn layout_h_max (&self, a: XYWH) -> O::Unit { self.read().unwrap().layout_h_max(a) } + fn layout (&self, a: XYWH) -> XYWH { self.read().unwrap().layout(a) } } impl> Layout for Option { - fn x (&self, to: O::Area) -> O::Unit { self.as_ref().map(|c|c.x(to)).unwrap_or(to.x()) } - fn y (&self, to: O::Area) -> O::Unit { self.as_ref().map(|c|c.y(to)).unwrap_or(to.y()) } - fn w_min (&self, to: O::Area) -> O::Unit { self.as_ref().map(|c|c.w_min(to)).unwrap_or(0.into()) } - fn w_max (&self, to: O::Area) -> O::Unit { self.as_ref().map(|c|c.w_max(to)).unwrap_or(0.into()) } - fn w (&self, to: O::Area) -> O::Unit { self.as_ref().map(|c|c.w(to)).unwrap_or(0.into()) } - fn h_min (&self, to: O::Area) -> O::Unit { self.as_ref().map(|c|c.h_min(to)).unwrap_or(0.into()) } - fn h_max (&self, to: O::Area) -> O::Unit { self.as_ref().map(|c|c.h_max(to)).unwrap_or(0.into()) } - fn h (&self, to: O::Area) -> O::Unit { self.as_ref().map(|c|c.h(to)).unwrap_or(0.into()) } - fn layout (&self, to: O::Area) -> O::Area { self.as_ref().map(|c|c.layout([self.x(to), self.y(to), self.w(to), self.h(to)].into())) - .unwrap_or([to.x(), to.y(), 0.into(), 0.into()].into()) } + fn layout_x (&self, to: XYWH) -> O::Unit { self.as_ref().map(|c|c.layout_x(to)).unwrap_or(to.x()) } + fn layout_y (&self, to: XYWH) -> O::Unit { self.as_ref().map(|c|c.layout_y(to)).unwrap_or(to.y()) } + fn layout_w_min (&self, to: XYWH) -> O::Unit { self.as_ref().map(|c|c.layout_w_min(to)).unwrap_or(0.into()) } + fn layout_w_max (&self, to: XYWH) -> O::Unit { self.as_ref().map(|c|c.layout_w_max(to)).unwrap_or(0.into()) } + fn layout_w (&self, to: XYWH) -> O::Unit { self.as_ref().map(|c|c.layout_w(to)).unwrap_or(0.into()) } + fn layout_h_min (&self, to: XYWH) -> O::Unit { self.as_ref().map(|c|c.layout_h_min(to)).unwrap_or(0.into()) } + fn layout_h_max (&self, to: XYWH) -> O::Unit { self.as_ref().map(|c|c.layout_h_max(to)).unwrap_or(0.into()) } + fn layout_h (&self, to: XYWH) -> O::Unit { self.as_ref().map(|c|c.layout_h(to)).unwrap_or(0.into()) } + fn layout (&self, to: XYWH) -> XYWH { + let xywh = XYWH(self.layout_x(to), self.layout_y(to), self.layout_w(to), self.layout_h(to)); + self.as_ref().map(|c|c.layout(xywh)).unwrap_or(XYWH(to.x(), to.y(), 0.into(), 0.into())) + } } impl> HasContent for Bounded { @@ -335,9 +367,9 @@ impl> When { } impl> Layout for When { - fn layout (&self, to: O::Area) -> O::Area { + fn layout (&self, to: XYWH) -> XYWH { let Self(cond, item, ..) = self; - if *cond { item.layout(to) } else { O::Area::zero().into() } + if *cond { item.layout(to) } else { XYWH::::zero().into() } } } @@ -356,7 +388,7 @@ impl, B: Content> Either { } impl, B: Layout> Layout for Either { - fn layout (&self, to: E::Area) -> E::Area { + fn layout (&self, to: XYWH) -> XYWH { let Self(cond, a, b, ..) = self; if *cond { a.layout(to) } else { b.layout(to) } } @@ -370,77 +402,88 @@ impl, B: Content> Draw for Either { } } - push_pull!(Push: plus); +push_pull!(Push: plus); - push_pull!(Pull: minus); +push_pull!(Pull: minus); - layout_op_xy!(0: Fill); - impl> Layout for Fill { - fn x (&self, area: O::Area) -> O::Unit { if self.dx() { area.x() } else { self.inner().x(area) } } - fn y (&self, area: O::Area) -> O::Unit { if self.dy() { area.y() } else { self.inner().y(area) } } - fn w (&self, area: O::Area) -> O::Unit { if self.dx() { area.w() } else { self.inner().w(area) } } - fn w_min (&self, area: O::Area) -> O::Unit { if self.dx() { area.w() } else { self.inner().w_min(area) } } - fn w_max (&self, area: O::Area) -> O::Unit { if self.dx() { area.w() } else { self.inner().w_max(area) } } - fn h (&self, area: O::Area) -> O::Unit { if self.dy() { area.h() } else { self.inner().h(area) } } - fn h_min (&self, area: O::Area) -> O::Unit { if self.dy() { area.h() } else { self.inner().h_min(area) } } - fn h_max (&self, area: O::Area) -> O::Unit { if self.dy() { area.h() } else { self.inner().h_max(area) } } - } - impl Fill { - #[inline] pub const fn dx (&self) -> bool { matches!(self, Self::X(_) | Self::XY(_)) } - #[inline] pub const fn dy (&self) -> bool { matches!(self, Self::Y(_) | Self::XY(_)) } - } +layout_op_xy!(0: Fill); - layout_op_xy!(1 opt: Fixed); - impl> Layout for Fixed { - fn w (&self, area: O::Area) -> O::Unit { self.dx().unwrap_or(self.inner().w(area)) } - fn w_min (&self, area: O::Area) -> O::Unit { self.dx().unwrap_or(self.inner().w_min(area)) } - fn w_max (&self, area: O::Area) -> O::Unit { self.dx().unwrap_or(self.inner().w_max(area)) } - fn h (&self, area: O::Area) -> O::Unit { self.dy().unwrap_or(self.inner().h(area)) } - fn h_min (&self, area: O::Area) -> O::Unit { self.dy().unwrap_or(self.inner().h_min(area)) } - fn h_max (&self, area: O::Area) -> O::Unit { self.dy().unwrap_or(self.inner().h_max(area)) } - } +impl> Layout for Fill { + fn layout_x (&self, area: XYWH) -> O::Unit { if self.dx() { area.x() } else { self.inner().layout_x(area) } } + fn layout_y (&self, area: XYWH) -> O::Unit { if self.dy() { area.y() } else { self.inner().layout_y(area) } } + fn layout_w (&self, area: XYWH) -> O::Unit { if self.dx() { area.w() } else { self.inner().layout_w(area) } } + fn layout_w_min (&self, area: XYWH) -> O::Unit { if self.dx() { area.w() } else { self.inner().layout_w_min(area) } } + fn layout_w_max (&self, area: XYWH) -> O::Unit { if self.dx() { area.w() } else { self.inner().layout_w_max(area) } } + fn layout_h (&self, area: XYWH) -> O::Unit { if self.dy() { area.h() } else { self.inner().layout_h(area) } } + fn layout_h_min (&self, area: XYWH) -> O::Unit { if self.dy() { area.h() } else { self.inner().layout_h_min(area) } } + fn layout_h_max (&self, area: XYWH) -> O::Unit { if self.dy() { area.h() } else { self.inner().layout_h_max(area) } } +} - layout_op_xy!(1 opt: Max); - impl> Layout for Max { - fn layout (&self, area: E::Area) -> E::Area { - let [x, y, w, h] = self.inner().layout(area).xywh(); - match self { - Self::X(mw, _) => [x, y, w.min(*mw), h ], - Self::Y(mh, _) => [x, y, w, h.min(*mh)], - Self::XY(mw, mh, _) => [x, y, w.min(*mw), h.min(*mh)], - }.into() +impl Fill { + #[inline] pub const fn dx (&self) -> bool { matches!(self, Self::X(_) | Self::XY(_)) } + #[inline] pub const fn dy (&self) -> bool { matches!(self, Self::Y(_) | Self::XY(_)) } +} + +layout_op_xy!(1 opt: Fixed); + +impl> Layout for Fixed { + fn layout_w (&self, area: XYWH) -> O::Unit { self.dx().unwrap_or(self.inner().layout_w(area)) } + fn layout_w_min (&self, area: XYWH) -> O::Unit { self.dx().unwrap_or(self.inner().layout_w_min(area)) } + fn layout_w_max (&self, area: XYWH) -> O::Unit { self.dx().unwrap_or(self.inner().layout_w_max(area)) } + fn layout_h (&self, area: XYWH) -> O::Unit { self.dy().unwrap_or(self.inner().layout_h(area)) } + fn layout_h_min (&self, area: XYWH) -> O::Unit { self.dy().unwrap_or(self.inner().layout_h_min(area)) } + fn layout_h_max (&self, area: XYWH) -> O::Unit { self.dy().unwrap_or(self.inner().layout_h_max(area)) } +} + +layout_op_xy!(1 opt: Max); + +impl> Layout for Max { + fn layout (&self, area: XYWH) -> XYWH { + let XYWH(x, y, w, h) = self.inner().layout(area); + match self { + Self::X(mw, _) => XYWH(x, y, w.min(*mw), h ), + Self::Y(mh, _) => XYWH(x, y, w, h.min(*mh)), + Self::XY(mw, mh, _) => XYWH(x, y, w.min(*mw), h.min(*mh)), } } +} - layout_op_xy!(1 opt: Min); - impl> Layout for Min { - fn layout (&self, area: E::Area) -> E::Area { - let [x, y, w, h] = self.inner().layout(area).xywh(); - match self { - Self::X(mw, _) => [x, y, w.max(*mw), h], - Self::Y(mh, _) => [x, y, w, h.max(*mh)], - Self::XY(mw, mh, _) => [x, y, w.max(*mw), h.max(*mh)], - }.into() +layout_op_xy!(1 opt: Min); + +impl> Layout for Min { + fn layout (&self, area: XYWH) -> XYWH { + let XYWH(x, y, w, h) = self.inner().layout(area); + match self { + Self::X(mw, _) => XYWH(x, y, w.max(*mw), h), + Self::Y(mh, _) => XYWH(x, y, w, h.max(*mh)), + Self::XY(mw, mh, _) => XYWH(x, y, w.max(*mw), h.max(*mh)), } } +} - layout_op_xy!(1 opt: Expand); - impl> Layout for Expand { - fn w (&self, to: O::Area) -> O::Unit { self.inner().w(to).plus(self.dx().unwrap_or_default()) } - fn h (&self, to: O::Area) -> O::Unit { self.inner().w(to).plus(self.dy().unwrap_or_default()) } +layout_op_xy!(1 opt: Expand); + +impl> Layout for Expand { + fn layout_w (&self, to: XYWH) -> O::Unit { + self.inner().layout_w(to).plus(self.dx().unwrap_or_default()) } - - // FIXME: why they differ? - - layout_op_xy!(1 opt: Shrink); - impl> Layout for Shrink { - fn layout (&self, to: E::Area) -> E::Area { - let area = self.inner().layout(to); - let dx = self.dx().unwrap_or_default(); - let dy = self.dy().unwrap_or_default(); - [area.x(), area.y(), area.w().minus(dx), area.h().minus(dy)].into() - } + fn layout_h (&self, to: XYWH) -> O::Unit { + self.inner().layout_w(to).plus(self.dy().unwrap_or_default()) } +} + +// FIXME: why they differ? + +layout_op_xy!(1 opt: Shrink); + +impl> Layout for Shrink { + fn layout (&self, to: XYWH) -> XYWH { + let area = self.inner().layout(to); + let dx = self.dx().unwrap_or_default(); + let dy = self.dy().unwrap_or_default(); + XYWH(area.x(), area.y(), area.w().minus(dx), area.h().minus(dy)) + } +} impl Align { #[inline] pub const fn c (a: T) -> Self { Self(Alignment::Center, a) } @@ -461,19 +504,19 @@ impl> Draw for Align { } impl> Layout for Align { - fn x (&self, to: O::Area) -> O::Unit { + fn layout_x (&self, to: XYWH) -> O::Unit { match self.0 { NW | W | SW => to.x(), - N | Center | S => to.x().plus(to.w() / 2.into()).minus(self.1.w(to) / 2.into()), - NE | E | SE => to.x().plus(to.w()).minus(self.1.w(to)), + N | Center | S => to.x().plus(to.w() / 2.into()).minus(self.1.layout_w(to) / 2.into()), + NE | E | SE => to.x().plus(to.w()).minus(self.1.layout_w(to)), _ => todo!(), } } - fn y (&self, to: O::Area) -> O::Unit { + fn layout_y (&self, to: XYWH) -> O::Unit { match self.0 { NW | N | NE => to.y(), - W | Center | E => to.y().plus(to.h() / 2.into()).minus(self.1.h(to) / 2.into()), - SW | S | SE => to.y().plus(to.h()).minus(self.1.h(to)), + W | Center | E => to.y().plus(to.h() / 2.into()).minus(self.1.layout_h(to) / 2.into()), + SW | S | SE => to.y().plus(to.h()).minus(self.1.layout_h(to)), _ => todo!(), } } @@ -485,6 +528,7 @@ impl Pad { match self { X(_, c) | Y(_, c) | XY(_, _, c) => c, } } } + impl Pad { #[inline] pub fn dx (&self) -> U { use Pad::*; @@ -495,15 +539,18 @@ impl Pad { match self { X(_, _) => 0.into(), Y(y, _) => *y, XY(_, y, _) => *y, } } } + impl> Draw for Pad { fn draw (&self, to: &mut O) { Bounded(self.layout(to.area()), self.inner()).draw(to) } } + impl> Layout for Pad { - fn x (&self, area: O::Area) -> O::Unit { area.x().plus(self.dx()) } - fn y (&self, area: O::Area) -> O::Unit { area.x().plus(self.dx()) } - fn w (&self, area: O::Area) -> O::Unit { area.w().minus(self.dx() * 2.into()) } - fn h (&self, area: O::Area) -> O::Unit { area.h().minus(self.dy() * 2.into()) } + fn layout_x (&self, area: XYWH) -> O::Unit { area.x().plus(self.dx()) } + fn layout_y (&self, area: XYWH) -> O::Unit { area.x().plus(self.dx()) } + fn layout_w (&self, area: XYWH) -> O::Unit { area.w().minus(self.dx() * 2.into()) } + fn layout_h (&self, area: XYWH) -> O::Unit { area.h().minus(self.dy() * 2.into()) } } + impl Bsp { #[inline] pub const fn n (a: Head, b: Tail) -> Self { Self(North, a, b) } #[inline] pub const fn s (a: Head, b: Tail) -> Self { Self(South, a, b) } @@ -519,12 +566,10 @@ impl, Tail: Content> Draw for Bsp { South => { //panic!("{}", self.1.h(to.area())); let area_1 = self.1.layout(to.area()); - let area_2 = self.2.layout([ - to.area().x(), - to.area().y().plus(area_1.h()), - to.area().w(), - to.area().h().minus(area_1.h()) - ].into()); + let area_2 = self.2.layout(XYWH( + to.area().x(), to.area().y().plus(area_1.h()), + to.area().w(), to.area().h().minus(area_1.h()) + )); //panic!("{area_1:?} {area_2:?}"); to.place_at(area_1, &self.1); to.place_at(area_2, &self.2); @@ -542,70 +587,95 @@ impl, Tail: Content> Draw for Bsp { //} } } + impl, Tail: Layout> Layout for Bsp { - fn w (&self, area: O::Area) -> O::Unit { - match self.0 { Above | Below | North | South => self.1.w(area).max(self.2.w(area)), East | West => self.1.w_min(area).plus(self.2.w(area)), } + fn layout_w (&self, area: XYWH) -> O::Unit { + match self.0 { + Above | Below | North | South => self.1.layout_w(area).max(self.2.layout_w(area)), + East | West => self.1.layout_w_min(area).plus(self.2.layout_w(area)), + } } - fn w_min (&self, area: O::Area) -> O::Unit { - match self.0 { Above | Below | North | South => self.1.w_min(area).max(self.2.w_min(area)), East | West => self.1.w_min(area).plus(self.2.w_min(area)), } + fn layout_w_min (&self, area: XYWH) -> O::Unit { + match self.0 { + Above | Below | North | South => self.1.layout_w_min(area).max(self.2.layout_w_min(area)), + East | West => self.1.layout_w_min(area).plus(self.2.layout_w_min(area)), + } } - fn w_max (&self, area: O::Area) -> O::Unit { - match self.0 { Above | Below | North | South => self.1.w_max(area).max(self.2.w_max(area)), East | West => self.1.w_max(area).plus(self.2.w_max(area)), } + fn layout_w_max (&self, area: XYWH) -> O::Unit { + match self.0 { + Above | Below | North | South => self.1.layout_w_max(area).max(self.2.layout_w_max(area)), + East | West => self.1.layout_w_max(area).plus(self.2.layout_w_max(area)), + } } - fn h (&self, area: O::Area) -> O::Unit { - match self.0 { Above | Below | East | West => self.1.h(area).max(self.2.h(area)), North | South => self.1.h(area).plus(self.2.h(area)), } + fn layout_h (&self, area: XYWH) -> O::Unit { + match self.0 { + Above | Below | East | West => self.1.layout_h(area).max(self.2.layout_h(area)), + North | South => self.1.layout_h(area).plus(self.2.layout_h(area)), + } } - fn h_min (&self, area: O::Area) -> O::Unit { - match self.0 { Above | Below | East | West => self.1.h_min(area).max(self.2.h_min(area)), North | South => self.1.h_min(area).plus(self.2.h_min(area)), } + fn layout_h_min (&self, area: XYWH) -> O::Unit { + match self.0 { + Above | Below | East | West => self.1.layout_h_min(area).max(self.2.layout_h_min(area)), + North | South => self.1.layout_h_min(area).plus(self.2.layout_h_min(area)), + } } - fn h_max (&self, area: O::Area) -> O::Unit { - match self.0 { Above | Below | North | South => self.1.h_max(area).max(self.2.h_max(area)), East | West => self.1.h_max(area).plus(self.2.h_max(area)), } + fn layout_h_max (&self, area: XYWH) -> O::Unit { + match self.0 { + Above | Below | North | South => self.1.layout_h_max(area).max(self.2.layout_h_max(area)), + East | West => self.1.layout_h_max(area).plus(self.2.layout_h_max(area)), + } } - fn layout (&self, area: O::Area) -> O::Area { + fn layout (&self, area: XYWH) -> XYWH { bsp_areas(area, self.0, &self.1, &self.2)[2] } } -fn bsp_areas , B: Layout> (area: O::Area, direction: Direction, a: &A, b: &B,) -> [O::Area;3] { - let [x, y, w, h] = area.xywh(); - let [aw, ah] = a.layout(area).wh(); - let [bw, bh] = b.layout(match direction { - Above | Below => area, - South => [x, y + ah, w, h.minus(ah)].into(), - North => [x, y, w, h.minus(ah)].into(), - East => [x + aw, y, w.minus(aw), h].into(), - West => [x, y, w.minus(aw), h].into(), +fn bsp_areas , B: Layout> ( + area: XYWH, + direction: Direction, + a: &A, + b: &B, +) -> [XYWH;3] { + let XYWH(x, y, w, h) = area; + let WH(aw, ah) = a.layout(area).wh(); + let WH(bw, bh) = b.layout(match direction { + South => XYWH(x, y + ah, w, h.minus(ah)), + North => XYWH(x, y, w, h.minus(ah)), + East => XYWH(x + aw, y, w.minus(aw), h), + West => XYWH(x, y, w.minus(aw), h), + Above => area, + Below => area, }).wh(); match direction { Above | Below => { - let [x, y, w, h] = area.center_xy([aw.max(bw), ah.max(bh)]); - let a = [(x + w/2.into()).minus(aw/2.into()), (y + h/2.into()).minus(ah/2.into()), aw, ah]; - let b = [(x + w/2.into()).minus(bw/2.into()), (y + h/2.into()).minus(bh/2.into()), bw, bh]; - [a.into(), b.into(), [x, y, w, h].into()] + let XYWH(x, y, w, h) = area.centered_xy([aw.max(bw), ah.max(bh)]); + let a = XYWH((x + w/2.into()).minus(aw/2.into()), (y + h/2.into()).minus(ah/2.into()), aw, ah); + let b = XYWH((x + w/2.into()).minus(bw/2.into()), (y + h/2.into()).minus(bh/2.into()), bw, bh); + [a.into(), b.into(), XYWH(x, y, w, h)] }, South => { - let [x, y, w, h] = area.center_xy([aw.max(bw), ah + bh]); - let a = [(x + w/2.into()).minus(aw/2.into()), y, aw, ah]; - let b = [(x + w/2.into()).minus(bw/2.into()), y + ah, bw, bh]; - [a.into(), b.into(), [x, y, w, h].into()] + let XYWH(x, y, w, h) = area.centered_xy([aw.max(bw), ah + bh]); + let a = XYWH((x + w/2.into()).minus(aw/2.into()), y, aw, ah); + let b = XYWH((x + w/2.into()).minus(bw/2.into()), y + ah, bw, bh); + [a.into(), b.into(), XYWH(x, y, w, h)] }, North => { - let [x, y, w, h] = area.center_xy([aw.max(bw), ah + bh]); - let a = [(x + (w/2.into())).minus(aw/2.into()), y + bh, aw, ah]; - let b = [(x + (w/2.into())).minus(bw/2.into()), y, bw, bh]; - [a.into(), b.into(), [x, y, w, h].into()] + let XYWH(x, y, w, h) = area.centered_xy([aw.max(bw), ah + bh]); + let a = XYWH((x + (w/2.into())).minus(aw/2.into()), y + bh, aw, ah); + let b = XYWH((x + (w/2.into())).minus(bw/2.into()), y, bw, bh); + [a.into(), b.into(), XYWH(x, y, w, h)] }, East => { - let [x, y, w, h] = area.center_xy([aw + bw, ah.max(bh)]); - let a = [x, (y + h/2.into()).minus(ah/2.into()), aw, ah]; - let b = [x + aw, (y + h/2.into()).minus(bh/2.into()), bw, bh]; - [a.into(), b.into(), [x, y, w, h].into()] + let XYWH(x, y, w, h) = area.centered_xy([aw + bw, ah.max(bh)]); + let a = XYWH(x, (y + h/2.into()).minus(ah/2.into()), aw, ah); + let b = XYWH(x + aw, (y + h/2.into()).minus(bh/2.into()), bw, bh); + [a.into(), b.into(), XYWH(x, y, w, h)] }, West => { - let [x, y, w, h] = area.center_xy([aw + bw, ah.max(bh)]); - let a = [x + bw, (y + h/2.into()).minus(ah/2.into()), aw, ah]; - let b = [x, (y + h/2.into()).minus(bh/2.into()), bw, bh]; - [a.into(), b.into(), [x, y, w, h].into()] + let XYWH(x, y, w, h) = area.centered_xy([aw + bw, ah.max(bh)]); + let a = XYWH(x + bw, (y + h/2.into()).minus(ah/2.into()), aw, ah); + let b = XYWH(x, (y + h/2.into()).minus(bh/2.into()), bw, bh); + [a.into(), b.into(), XYWH(x, y, w, h)] }, } } @@ -635,13 +705,13 @@ impl<'a, O, A, B, I, F, G> Layout for Map where F: Fn() -> I + Send + Sync + 'a, G: Fn(A, usize)->B + Send + Sync { - fn layout (&self, area: O::Area) -> O::Area { + fn layout (&self, area: XYWH) -> XYWH { let Self { get_iter, get_item, .. } = self; let mut index = 0; - let [mut min_x, mut min_y] = area.center(); - let [mut max_x, mut max_y] = area.center(); + let XY(mut min_x, mut min_y) = area.centered(); + let XY(mut max_x, mut max_y) = area.center(); for item in get_iter() { - let [x,y,w,h] = get_item(item, index).layout(area).xywh(); + let XYWH(x, y, w, h) = get_item(item, index).layout(area); min_x = min_x.min(x); min_y = min_y.min(y); max_x = max_x.max(x + w); @@ -651,9 +721,10 @@ impl<'a, O, A, B, I, F, G> Layout for Map where let w = max_x - min_x; let h = max_y - min_y; //[min_x.into(), min_y.into(), w.into(), h.into()].into() - area.center_xy([w.into(), h.into()]).into() + area.centered_xy([w.into(), h.into()]) } } + impl<'a, O, A, B, I, F, G> Draw for Map where O: Out, B: Content, @@ -720,15 +791,11 @@ impl Tryptich { } impl> Layout for Foreground { - fn layout (&self, to: O::Area) -> O::Area { - self.1.layout(to) - } + fn layout (&self, to: XYWH) -> XYWH { self.1.layout(to) } } impl> Layout for Background { - fn layout (&self, to: O::Area) -> O::Area { - self.1.layout(to) - } + fn layout (&self, to: XYWH) -> XYWH { self.1.layout(to) } } impl, V: Content> HasContent for FieldH { @@ -736,7 +803,7 @@ impl, V: Content> HasContent for FieldH } impl, V: Content> Layout for FieldH { - fn layout (&self, to: O::Area) -> O::Area { self.content().layout(to) } + fn layout (&self, to: XYWH) -> XYWH { self.content().layout(to) } } impl, V: Content> Draw for FieldH { @@ -748,17 +815,19 @@ impl, V: Content> HasContent for FieldV } impl, V: Content> Layout for FieldV { - fn layout (&self, to: O::Area) -> O::Area { self.content().layout(to) } + fn layout (&self, to: XYWH) -> XYWH { self.content().layout(to) } } impl, V: Content> Draw for FieldV { fn draw (&self, to: &mut O) { self.content().draw(to) } } + impl> Layout for Border { - fn layout (&self, area: O::Area) -> O::Area { + fn layout (&self, area: XYWH) -> XYWH { self.1.layout(area) } } + impl Field { pub fn new (direction: Direction) -> Field { Field:: { diff --git a/output/src/out_structs.rs b/output/src/out_structs.rs index 22b6465..c267a29 100644 --- a/output/src/out_structs.rs +++ b/output/src/out_structs.rs @@ -5,14 +5,16 @@ use crate::*; /// ``` /// let xy: XY = XY(0, 0); /// ``` -pub struct XY(pub C, pub C); +#[cfg_attr(test, derive(Arbitrary))] +#[derive(Copy, Clone, Default)] pub struct XY(pub C, pub C); /// A size (Width, Height). /// /// ``` /// let wh: WH = WH(0, 0); /// ``` -pub struct WH(pub C, pub C); +#[cfg_attr(test, derive(Arbitrary))] +#[derive(Copy, Clone, Default)] pub struct WH(pub C, pub C); /// Point with size. /// @@ -22,15 +24,16 @@ pub struct WH(pub C, pub C); /// /// * [ ] TODO: anchor field (determines at which corner/side is X0 Y0) /// -pub struct XYWH(pub C, pub C, pub C, pub C); +#[cfg_attr(test, derive(Arbitrary))] +#[derive(Copy, Clone, Default)] pub struct XYWH(pub C, pub C, pub C, pub C); /// A cardinal direction. /// /// ``` /// let direction = Direction::Above; /// ``` -#[derive(Copy, Clone, PartialEq, Debug)] #[cfg_attr(test, derive(Arbitrary))] -pub enum Direction { +#[cfg_attr(test, derive(Arbitrary))] +#[derive(Copy, Clone, PartialEq, Debug)] pub enum Direction { North, South, East, West, Above, Below } @@ -39,8 +42,8 @@ pub enum Direction { /// ``` /// let alignment = Align::Center; /// ``` -#[derive(Debug, Copy, Clone, Default)] -pub enum Alignment { +#[cfg_attr(test, derive(Arbitrary))] +#[derive(Debug, Copy, Clone, Default)] pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W } @@ -50,7 +53,7 @@ pub enum Alignment { /// let measure = Measure::default(); /// ``` #[derive(Default)] pub struct Measure { - __: PhantomData, + pub __: PhantomData, pub x: Arc, pub y: Arc, } @@ -130,8 +133,8 @@ pub enum Expand { X(U, A), Y(U, A), XY(U, U, A), } /// /// ``` /// use ::tengri::{output::*, tui::*}; -/// let area: [u16;4] = [10, 10, 20, 20]; -/// fn test (area: [u16;4], item: &impl Draw, expected: [u16;4]) { +/// let area = XYWH(10u16, 10, 20, 20); +/// fn test (area: XYWH<16>, item: &impl Draw, expected: [u16;4]) { /// assert_eq!(Content::layout(item, area), expected); /// assert_eq!(Draw::layout(item, area), expected); /// }; @@ -166,7 +169,7 @@ pub enum Pad { X(U, A), Y(U, A), XY(U, U, A), } /// ``` /// let bounded = Bounded(XYWH(0, 0, 0, 0), ""); /// ``` -pub struct Bounded(pub O::Area, pub D); +pub struct Bounded(pub XYWH, pub D); /// Draws items from an iterator. /// diff --git a/output/src/out_traits.rs b/output/src/out_traits.rs index 10196cb..127d1ae 100644 --- a/output/src/out_traits.rs +++ b/output/src/out_traits.rs @@ -15,7 +15,7 @@ use crate::*; /// fn area_mut (&mut self) -> &mut [u16;4] { /// &mut self.0 /// } -/// fn place_at + ?Sized> (&mut self, area: [u16;4], _: &T) { +/// fn place_at + ?Sized> (&mut self, area: XYWH, _: &T) { /// println!("place_at: {area:?}"); /// () /// } @@ -29,24 +29,44 @@ use crate::*; 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; + fn area (&self) -> XYWH; /// Mutable pointer to area. - fn area_mut (&mut self) -> &mut Self::Area; + fn area_mut (&mut self) -> &mut XYWH; /// Render drawable in area specified by `area` - fn place_at <'t, T: Draw + ?Sized> (&mut self, area: Self::Area, content: &'t T); + fn place_at <'t, T: Draw + ?Sized> (&mut self, area: XYWH, 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 - ) { + #[inline] fn place <'t, T: Content + ?Sized> (&mut self, content: &'t T) { self.place_at(content.layout(self.area()), content) } } +/// 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 plus (self, other: Self) -> Self; + fn minus (self, other: Self) -> Self { + if self >= other { self - other } else { 0.into() } + } + fn atomic (self) -> AtomicUsize { + AtomicUsize::new(self.into()) + } + fn zero () -> Self { + 0.into() + } +} + /// Drawable with dynamic dispatch. pub trait Draw { fn draw (&self, to: &mut O); @@ -79,15 +99,17 @@ 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() } + fn layout_x (&self, to: XYWH) -> O::Unit { to.x() } + fn layout_y (&self, to: XYWH) -> O::Unit { to.y() } + fn layout_w_min (&self, _t: XYWH) -> O::Unit { 0.into() } + fn layout_w_max (&self, to: XYWH) -> O::Unit { to.w() } + fn layout_w (&self, to: XYWH) -> O::Unit { to.w().max(self.layout_w_min(to)).min(self.layout_w_max(to)) } + fn layout_h_min (&self, _t: XYWH) -> O::Unit { 0.into() } + fn layout_h_max (&self, to: XYWH) -> O::Unit { to.h() } + fn layout_h (&self, to: XYWH) -> O::Unit { to.h().max(self.layout_h_min(to)).min(self.layout_h_max(to)) } + fn layout (&self, to: XYWH) -> XYWH { + XYWH(self.layout_x(to), self.layout_y(to), self.layout_w(to), self.layout_h(to)) + } } pub trait HasContent { @@ -97,25 +119,6 @@ pub trait HasContent { // 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; @@ -131,6 +134,8 @@ pub trait HasWH { } // Something that has a 2D bounding box (X, Y, W, H). +// +// FIXME: The other way around? pub trait HasXYWH: HasXY + HasWH { fn x2 (&self) -> N { self.x().plus(self.w()) } fn y2 (&self) -> N { self.y().plus(self.h()) } @@ -144,9 +149,9 @@ pub trait HasXYWH: HasXY + HasWH { } } -// TODO DOCUMENTME -pub trait HasSize { - fn size (&self) -> &Measure; - fn width (&self) -> O::Unit { self.size().w() } - fn height (&self) -> O::Unit { self.size().h() } +// Something that has a [Measure] of its rendered size. +pub trait Measured { + fn measure (&self) -> &Measure; + fn measure_width (&self) -> O::Unit { self.measure().w() } + fn measure_height (&self) -> O::Unit { self.measure().h() } } diff --git a/tui/src/tui_content/_tui_focus.rs b/tui/src/.scratch.rs similarity index 68% rename from tui/src/tui_content/_tui_focus.rs rename to tui/src/.scratch.rs index 41b0f33..a6ee28e 100644 --- a/tui/src/tui_content/_tui_focus.rs +++ b/tui/src/.scratch.rs @@ -304,3 +304,131 @@ pub fn to_focus_command (input: &TuiIn) -> Option> { + pub menus: Vec>, + pub index: usize, +} +impl> MenuBar { + pub fn new () -> Self { Self { menus: vec![], index: 0 } } + pub fn add (mut self, menu: Menu) -> Self { + self.menus.push(menu); + self + } +} +pub struct Menu> { + pub title: Arc, + pub items: Vec>, + pub index: Option, +} +impl> Menu { + pub fn new (title: impl AsRef) -> Self { + Self { + title: title.as_ref().to_string(), + items: vec![], + index: None, + } + } + pub fn add (mut self, item: MenuItem) -> Self { + self.items.push(item); + self + } + pub fn sep (mut self) -> Self { + self.items.push(MenuItem::sep()); + self + } + pub fn cmd (mut self, hotkey: &'static str, text: &'static str, command: C) -> Self { + self.items.push(MenuItem::cmd(hotkey, text, command)); + self + } + pub fn off (mut self, hotkey: &'static str, text: &'static str) -> Self { + self.items.push(MenuItem::off(hotkey, text)); + self + } +} +pub enum MenuItem> { + /// Unused. + __(PhantomData, PhantomData), + /// A separator. Skip it. + Separator, + /// A menu item with command, description and hotkey. + Command(&'static str, &'static str, C), + /// A menu item that can't be activated but has description and hotkey + Disabled(&'static str, &'static str) +} +impl> MenuItem { + pub fn sep () -> Self { + Self::Separator + } + pub fn cmd (hotkey: &'static str, text: &'static str, command: C) -> Self { + Self::Command(hotkey, text, command) + } + pub fn off (hotkey: &'static str, text: &'static str) -> Self { + Self::Disabled(hotkey, text) + } +} + +//impl> Content for Result> { + //fn content (&self) -> impl Draw + '_ { + //Bsp::a(self.as_ref().ok(), self.as_ref().err().map( + //|e|Tui::fg_bg(Color::Rgb(255,255,255), Color::Rgb(32,32,32), e.to_string()) + //)) + //} +//} + +//impl> Draw for Result> { + //fn layout (&self, to: [u16;4]) -> [u16;4] { + //match self { + //Ok(content) => content.layout(to), + //Err(e) => [0, 0, to.w(), to.h()] + //} + //} + //fn draw (&self, to: &mut TuiOut) { + //match self { + //Ok(content) => content.draw(to), + //Err(e) => to.blit(&e.to_string(), 0, 0, Some(Style::default() + //.bg(Color::Rgb(32,32,32)) + //.fg(Color::Rgb(255,255,255)))) + //} + //} +//} + + //let token = token.as_ref(); + //if token.len() < 2 { + //Self { valid: false, key: None, mods: KeyModifiers::NONE } + //} else if token.chars().next() != Some('@') { + //Self { valid: false, key: None, mods: KeyModifiers::NONE } + //} else { + //Self { valid: true, key: None, mods: KeyModifiers::NONE }.next(&token[1..]) + //} + //} + //pub fn build (self) -> Option { + //if self.valid && self.key.is_some() { + //Some(Event::Key(KeyEvent::new(self.key.unwrap(), self.mods))) + //} else { + //None + //} + //} + //fn next (mut self, token: &str) -> Self { + //let mut tokens = token.split('-').peekable(); + //while let Some(token) = tokens.next() { + //if tokens.peek().is_some() { + //match token { + //"ctrl" | "Ctrl" | "c" | "C" => self.mods |= KeyModifiers::CONTROL, + //"alt" | "Alt" | "m" | "M" => self.mods |= KeyModifiers::ALT, + //"shift" | "Shift" | "s" | "S" => { + //self.mods |= KeyModifiers::SHIFT; + //// + TODO normalize character case, BackTab, etc. + //}, + //_ => panic!("unknown modifier {token}"), + //} + //} else { + //self.key = if token.len() == 1 { + //Some(KeyCode::Char(token.chars().next().unwrap())) + //} else { + //Some(Self::named_key(token).unwrap_or_else(||panic!("unknown character {token}"))) + //} + //} + //} + //self + //} diff --git a/tui/src/lib.rs b/tui/src/lib.rs index d9e16c6..976d230 100644 --- a/tui/src/lib.rs +++ b/tui/src/lib.rs @@ -1,4 +1,9 @@ #![feature(type_changing_struct_update, trait_alias)] + +use std::{time::Duration, thread::{spawn, JoinHandle}}; + +use unicode_width::*; + pub use ::{ dizzle, tengri_input, @@ -8,6 +13,7 @@ pub use ::{ palette, better_panic, }; + pub(crate) use ::{ dizzle::*, tengri_input::*, @@ -26,11 +32,10 @@ pub(crate) use ::{ crossterm::{ ExecutableCommand, terminal::{EnterAlternateScreen, LeaveAlternateScreen, enable_raw_mode, disable_raw_mode}, - event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState}, + event::{poll, read, Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState}, } }; -mod tui_engine; pub use self::tui_engine::*; -mod tui_content; pub use self::tui_content::*; + #[macro_export] macro_rules! tui_main { ($expr:expr) => { fn main () -> Usually<()> { @@ -41,6 +46,256 @@ mod tui_content; pub use self::tui_content::*; }; } +#[macro_export] macro_rules! has_color { + (|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => { + impl $(<$($L),*$($T $(: $U)?),*>)? HasColor for $Struct $(<$($L),*$($T),*>)? { + fn color (&$self) -> ItemColor { $cb } + } + } +} + +macro_rules! border { + ($($T:ident { + $nw:literal $n:literal $ne:literal $w:literal $e:literal $sw:literal $s:literal $se:literal + $($x:tt)* + }),+) => {$( + impl BorderStyle for $T { + const NW: &'static str = $nw; + const N: &'static str = $n; + const NE: &'static str = $ne; + const W: &'static str = $w; + const E: &'static str = $e; + const SW: &'static str = $sw; + const S: &'static str = $s; + const SE: &'static str = $se; + $($x)* + fn enabled (&self) -> bool { self.0 } + } + #[derive(Copy, Clone)] pub struct $T(pub bool, pub Style); + impl Layout for $T {} + impl Draw for $T { + fn draw (&self, to: &mut TuiOut) { + if self.enabled() { let _ = BorderStyle::draw(self, to); } + } + } + )+} +} + +mod tui_structs; pub use self::tui_structs::*; +mod tui_traits; pub use self::tui_traits::*; +mod tui_impls; pub use self::tui_impls::*; + +#[cfg(feature = "dsl")] +pub fn evaluate_output_expression_tui <'a, S> ( + state: &S, output: &mut TuiOut, expr: impl Expression + 'a +) -> Usually where + S: View + + for<'b>Namespace<'b, bool> + + for<'b>Namespace<'b, u16> + + for<'b>Namespace<'b, Color> +{ + // See `tengri_output::evaluate_output_expression` + let head = expr.head()?; + let mut frags = head.src()?.unwrap_or_default().split("/"); + let args = expr.tail(); + let arg0 = args.head(); + let tail0 = args.tail(); + let arg1 = tail0.head(); + let tail1 = tail0.tail(); + let _arg2 = tail1.head(); + match frags.next() { + + Some("text") => if let Some(src) = args?.src()? { output.place(&src) }, + + Some("fg") => { + let arg0 = arg0?.expect("fg: expected arg 0 (color)"); + output.place(&Tui::fg( + Namespace::::resolve(state, arg0)?.unwrap_or_else(||panic!("fg: {arg0:?}: not a color")), + Thunk::new(move|output: &mut TuiOut|state.view(output, &arg1).unwrap()), + )) + }, + + Some("bg") => { + let arg0 = arg0?.expect("bg: expected arg 0 (color)"); + output.place(&Tui::bg( + Namespace::::resolve(state, arg0)?.unwrap_or_else(||panic!("bg: {arg0:?}: not a color")), + Thunk::new(move|output: &mut TuiOut|state.view(output, &arg1).unwrap()), + )) + }, + + _ => return Ok(false) + + }; + Ok(true) +} + +pub fn named_key (token: &str) -> Option { + use KeyCode::*; + Some(match token { + "up" => Up, + "down" => Down, + "left" => Left, + "right" => Right, + "esc" | "escape" => Esc, + "enter" | "return" => Enter, + "delete" | "del" => Delete, + "backspace" => Backspace, + "tab" => Tab, + "space" => Char(' '), + "comma" => Char(','), + "period" => Char('.'), + "plus" => Char('+'), + "minus" | "dash" => Char('-'), + "equal" | "equals" => Char('='), + "underscore" => Char('_'), + "backtick" => Char('`'), + "lt" => Char('<'), + "gt" => Char('>'), + "cbopen" | "openbrace" => Char('{'), + "cbclose" | "closebrace" => Char('}'), + "bropen" | "openbracket" => Char('['), + "brclose" | "closebracket" => Char(']'), + "pgup" | "pageup" => PageUp, + "pgdn" | "pagedown" => PageDown, + "f1" => F(1), + "f2" => F(2), + "f3" => F(3), + "f4" => F(4), + "f5" => F(5), + "f6" => F(6), + "f7" => F(7), + "f8" => F(8), + "f9" => F(9), + "f10" => F(10), + "f11" => F(11), + "f12" => F(12), + _ => return None, + }) +} + +pub fn button_2 <'a> (key: impl Content, label: impl Content, editing: bool) -> impl Content { + Tui::bold(true, Bsp::e( + Tui::fg_bg(Tui::orange(), Tui::g(0), Bsp::e(Tui::fg(Tui::g(0), &"▐"), Bsp::e(key, Tui::fg(Tui::g(96), &"▐")))), + When::new(!editing, Tui::fg_bg(Tui::g(255), Tui::g(96), label)))) +} + +pub fn button_3 <'a> ( + key: impl Content, label: impl Content, value: impl Content, editing: bool, +) -> impl Content { + Tui::bold(true, Bsp::e( + Tui::fg_bg(Tui::orange(), Tui::g(0), + Bsp::e(Tui::fg(Tui::g(0), &"▐"), Bsp::e(key, Tui::fg(if editing { Tui::g(128) } else { Tui::g(96) }, "▐")))), + Bsp::e( + When::new(!editing, Bsp::e(Tui::fg_bg(Tui::g(255), Tui::g(96), label), Tui::fg_bg(Tui::g(128), Tui::g(96), &"▐"),)), + Bsp::e(Tui::fg_bg(Tui::g(224), Tui::g(128), value), Tui::fg_bg(Tui::g(128), Reset, &"▌"), )))) +} + +border! { + Square { + "┌" "─" "┐" + "│" "│" + "└" "─" "┘" fn style (&self) -> Option