diff --git a/crates/tek_core/examples/demo.rs b/crates/tek_core/examples/demo.rs index d0711e9c..7123e6c3 100644 --- a/crates/tek_core/examples/demo.rs +++ b/crates/tek_core/examples/demo.rs @@ -123,3 +123,22 @@ impl Handle for Demo { Ok(Some(true)) } } + +//lisp!(CONTENT Demo (LET + //(BORDER-STYLE (STYLE (FG (RGB 0 0 0)))) + //(BG-COLOR-0 (RGB 0 128 128)) + //(BG-COLOR-1 (RGB 128 96 0)) + //(BG-COLOR-2 (RGB 128 64 0)) + //(BG-COLOR-3 (RGB 96 64 0)) + //(CENTER (LAYERS + //(BACKGROUND BG-COLOR-0) + //(OUTSET-XY 1 1 (SPLIT-DOWN + //(LAYERS (BACKGROUND BG-COLOR-1) + //(BORDER SQUARE BORDER-STYLE) + //(OUTSET-XY 2 1 "...")) + //(LAYERS (BACKGROUND BG-COLOR-2) + //(BORDER LOZENGE BORDER-STYLE) + //(OUTSET-XY 4 2 "---")) + //(LAYERS (BACKGROUND BG-COLOR-3) + //(BORDER SQUARE-BOLD BORDER-STYLE) + //(OUTSET-XY 2 1 "~~~")))))))) diff --git a/crates/tek_core/src/audio.rs b/crates/tek_core/src/audio.rs index 50a94923..6cb9934f 100644 --- a/crates/tek_core/src/audio.rs +++ b/crates/tek_core/src/audio.rs @@ -307,7 +307,7 @@ impl std::fmt::Debug for JackDevice { impl Widget for JackDevice { type Engine = E; - fn layout (&self, to: E::Area) -> Perhaps { + fn layout (&self, to: E::Size) -> Perhaps { self.state.read().unwrap().layout(to) } fn render (&self, to: &mut E) -> Perhaps { diff --git a/crates/tek_core/src/engine.rs b/crates/tek_core/src/engine.rs index c668acfd..3fa5e7ca 100644 --- a/crates/tek_core/src/engine.rs +++ b/crates/tek_core/src/engine.rs @@ -34,8 +34,8 @@ pub trait Engine: Send + Sync + Sized { pub trait Widget: Send + Sync { type Engine: Engine; - fn layout (&self, to: <::Engine as Engine>::Area) -> - Perhaps<<::Engine as Engine>::Area> + fn layout (&self, to: <::Engine as Engine>::Size) -> + Perhaps<<::Engine as Engine>::Size> { Ok(Some(to)) } @@ -44,7 +44,7 @@ pub trait Widget: Send + Sync { } impl<'a, E: Engine> Widget for Box + 'a> { type Engine = E; - fn layout (&self, to: E::Area) -> Perhaps { + fn layout (&self, to: E::Size) -> Perhaps { (**self).layout(to) } fn render (&self, to: &mut E) -> Perhaps { @@ -53,7 +53,7 @@ impl<'a, E: Engine> Widget for Box + 'a> { } impl Widget for &dyn Widget { type Engine = E; - fn layout (&self, to: E::Area) -> Perhaps { + fn layout (&self, to: E::Size) -> Perhaps { (*self).layout(to) } fn render (&self, to: &mut E) -> Perhaps { @@ -62,7 +62,7 @@ impl Widget for &dyn Widget { } impl Widget for &mut dyn Widget { type Engine = E; - fn layout (&self, to: E::Area) -> Perhaps { + fn layout (&self, to: E::Size) -> Perhaps { (**self).layout(to) } fn render (&self, to: &mut E) -> Perhaps { @@ -71,7 +71,7 @@ impl Widget for &mut dyn Widget { } impl> Widget for Arc { type Engine = E; - fn layout (&self, to: E::Area) -> Perhaps { + fn layout (&self, to: E::Size) -> Perhaps { self.as_ref().layout(to) } fn render (&self, to: &mut E) -> Perhaps { @@ -80,7 +80,7 @@ impl> Widget for Arc { } impl> Widget for Mutex { type Engine = E; - fn layout (&self, to: E::Area) -> Perhaps { + fn layout (&self, to: E::Size) -> Perhaps { self.lock().unwrap().layout(to) } fn render (&self, to: &mut E) -> Perhaps { @@ -89,7 +89,7 @@ impl> Widget for Mutex { } impl> Widget for RwLock { type Engine = E; - fn layout (&self, to: E::Area) -> Perhaps { + fn layout (&self, to: E::Size) -> Perhaps { self.read().unwrap().layout(to) } fn render (&self, to: &mut E) -> Perhaps { @@ -98,7 +98,7 @@ impl> Widget for RwLock { } impl> Widget for Option { type Engine = E; - fn layout (&self, to: E::Area) -> Perhaps { + fn layout (&self, to: E::Size) -> Perhaps { Ok(self.as_ref().map(|widget|widget.layout(to)).transpose()?.flatten()) } fn render (&self, to: &mut E) -> Perhaps { @@ -117,12 +117,12 @@ pub trait Content: Send + Sync { //} impl> Widget for W { type Engine = E; - fn layout (&self, to: E::Area) -> Perhaps { + fn layout (&self, to: E::Size) -> Perhaps { self.content().layout(to) } fn render (&self, to: &mut E) -> Perhaps { - match self.layout(to.area())? { - Some(area) => to.render_in(area, &self.content()), + match self.layout(to.area().wh().into())? { + Some(wh) => to.render_in(to.area().clip(wh).into(), &self.content()), None => Ok(None) } } @@ -394,45 +394,74 @@ impl Align { } } -impl> Widget for Align { - type Engine = E; - fn layout (&self, outer_area: E::Area) -> Perhaps { - Ok(match self { - Self::Center(_) => self.inner().layout(outer_area)?.map(|inner_area|{ - let [_, _, w, h] = inner_area.xywh(); - let offset_x = (outer_area.w() / 2.into()) - (w / 2.into()); - let offset_y = (outer_area.h() / 2.into()) - (h / 2.into()); - let result = [outer_area.x() + offset_x, outer_area.y() + offset_y, w, h]; - result.into() - }), - Self::X(_) => self.inner().layout(outer_area)?.map(|inner_area|{ - let [_, y, w, h] = inner_area.xywh(); - let offset_x = (outer_area.w() - w) / 2.into(); - let result = [outer_area.x() + offset_x, y, w, h]; - result.into() - }), - Self::Y(_) => self.inner().layout(outer_area)?.map(|inner_area|{ - let [x, _, w, h] = inner_area.xywh(); - let offset_y = (outer_area.h() / 2.into()) - (h / 2.into()); - let result = [x, outer_area.y() + offset_y, w, h]; - result.into() - }), - Self::NW(_) => { todo!() }, - Self::N(_) => { todo!() }, - Self::NE(_) => { todo!() }, - Self::W(_) => { todo!() }, - Self::E(_) => { todo!() }, - Self::SW(_) => { todo!() }, - Self::S(_) => { todo!() }, - Self::SE(_) => { todo!() }, +fn align + From<[N;4]>> (align: &Align, outer: R, inner: R) -> Option { + if outer.w() < inner.w() || outer.h() < inner.h() { + None + } else { + let [ox, oy, ow, oh] = outer.xywh(); + let [ix, iy, iw, ih] = inner.xywh(); + Some(match align { + Align::Center(_) => [ox + (ow - iw) / 2.into(), oy + (oh - ih) / 2.into(), iw, ih,].into(), + Align::X(_) => [ox + (ow - iw) / 2.into(), iy, iw, ih,].into(), + Align::Y(_) => [ix, oy + (oh - ih) / 2.into(), iw, ih,].into(), + Align::NW(_) => [ox, oy, iw, ih,].into(), + Align::N(_) => [ox + (ow - iw) / 2.into(), oy, iw, ih,].into(), + Align::NE(_) => [ox + ow - iw, oy, iw, ih,].into(), + Align::W(_) => [ox, oy + (oh - ih) / 2.into(), iw, ih,].into(), + Align::E(_) => [ox + ow - iw, oy + (oh - ih) / 2.into(), iw, ih,].into(), + Align::SW(_) => [ox, oy + oh - ih, iw, ih,].into(), + Align::S(_) => [ox + (ow - iw) / 2.into(), oy + oh - ih, iw, ih,].into(), + Align::SE(_) => [ox + ow - iw, oy + oh - ih, iw, ih,].into(), }) } +} + +impl> Widget for Align { + type Engine = E; + fn layout (&self, outer_area: E::Size) -> Perhaps { + self.inner().layout(outer_area) + //Ok(match self { + //Self::Center(_) => self.inner().layout(outer_area)?.map(|inner_area|{ + //let [_, _, w, h] = inner_area.xywh(); + //let offset_x = (outer_area.w() / 2.into()) - (w / 2.into()); + //let offset_y = (outer_area.h() / 2.into()) - (h / 2.into()); + //let result = [outer_area.x() + offset_x, outer_area.y() + offset_y, w, h]; + //result.into() + //}), + //Self::X(_) => self.inner().layout(outer_area)?.map(|inner_area|{ + //let [_, y, w, h] = inner_area.xywh(); + //let offset_x = (outer_area.w() - w) / 2.into(); + //let result = [outer_area.x() + offset_x, y, w, h]; + //result.into( + //}), + //Self::Y(_) => self.inner().layout(outer_area)?.map(|inner_area|{ + //let [x, _, w, h] = inner_area.xywh(); + //let offset_y = (outer_area.h() / 2.into()) - (h / 2.into()); + //let result = [x, outer_area.y() + offset_y, w, h]; + //result.into() + //}), + //Self::NW(_) => { todo!() }, + //Self::N(_) => { todo!() }, + //Self::NE(_) => { todo!() }, + //Self::W(_) => { todo!() }, + //Self::E(_) => { todo!() }, + //Self::SW(_) => { todo!() }, + //Self::S(_) => { todo!() }, + //Self::SE(_) => { todo!() }, + //}) + } fn render (&self, to: &mut E) -> Perhaps { - if let Some(area) = self.layout(to.area())? { - to.render_in(area, self.inner()) + let outer_area = to.area(); + Ok(if let Some(inner_size) = self.layout(outer_area.wh().into())? { + let inner_area = outer_area.clip(inner_size); + if let Some(aligned) = align(&self, outer_area.into(), inner_area.into()) { + to.render_in(aligned, self.inner())? + } else { + None + } } else { - Ok(None) - } + None + }) } } @@ -468,16 +497,18 @@ impl Min { } impl> Widget for Min { type Engine = E; - fn layout (&self, to: E::Area) -> Perhaps { + fn layout (&self, to: E::Size) -> Perhaps { Ok(self.inner().layout(to)?.map(|to|match *self { - Self::X(w, _) => [to.x(), to.y(), to.w().max(w), to.h()], - Self::Y(h, _) => [to.x(), to.y(), to.w(), to.h().max(h)], - Self::XY(w, h, _) => [to.x(), to.y(), to.w().max(w), to.h().max(h)], + Self::X(w, _) => [to.w().max(w), to.h()], + Self::Y(h, _) => [to.w(), to.h().max(h)], + Self::XY(w, h, _) => [to.w().max(w), to.h().max(h)], }.into())) } // TODO: 🡘 🡙 ←🡙→ fn render (&self, to: &mut E) -> Perhaps { - Ok(self.layout(to.area())?.map(|a|to.render_in(a, self.inner())).transpose()?.flatten()) + Ok(self.layout(to.area().wh().into())? + .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) + .transpose()?.flatten()) } } @@ -499,15 +530,17 @@ impl Max { impl> Widget for Max { type Engine = E; - fn layout (&self, to: E::Area) -> Perhaps { + fn layout (&self, to: E::Size) -> Perhaps { Ok(self.inner().layout(to)?.map(|to|match *self { - Self::X(w, _) => [to.x(), to.y(), to.w().min(w), to.h()], - Self::Y(h, _) => [to.x(), to.y(), to.w(), to.h().min(h)], - Self::XY(w, h, _) => [to.x(), to.y(), to.w().min(w), to.h().min(h)], + Self::X(w, _) => [to.w().min(w), to.h()], + Self::Y(h, _) => [to.w(), to.h().min(h)], + Self::XY(w, h, _) => [to.w().min(w), to.h().min(h)], }.into())) } fn render (&self, to: &mut E) -> Perhaps { - Ok(self.layout(to.area())?.map(|a|to.render_in(a, self.inner())).transpose()?.flatten()) + Ok(self.layout(to.area().wh().into())? + .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) + .transpose()?.flatten()) } } @@ -529,15 +562,17 @@ impl Grow { impl> Widget for Grow { type Engine = E; - fn layout (&self, to: E::Area) -> Perhaps { + fn layout (&self, to: E::Size) -> Perhaps { Ok(self.inner().layout(to)?.map(|to|match *self { - Self::X(w, _) => [to.x(), to.y(), to.w() + w, to.h()], - Self::Y(h, _) => [to.x(), to.y(), to.w(), to.h() + h], - Self::XY(w, h, _) => [to.x(), to.y(), to.w() + w, to.h() + h], + Self::X(w, _) => [to.w() + w, to.h()], + Self::Y(h, _) => [to.w(), to.h() + h], + Self::XY(w, h, _) => [to.w() + w, to.h() + h], }.into())) } fn render (&self, to: &mut E) -> Perhaps { - Ok(self.layout(to.area())?.map(|a|to.render_in(a, self.inner())).transpose()?.flatten()) + Ok(self.layout(to.area().wh().into())? + .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) + .transpose()?.flatten()) } } @@ -559,15 +594,17 @@ impl Shrink { impl> Widget for Shrink { type Engine = E; - fn layout (&self, to: E::Area) -> Perhaps { + fn layout (&self, to: E::Size) -> Perhaps { Ok(self.inner().layout(to)?.map(|to|match *self { - Self::X(w, _) => [to.x(), to.y(), to.w() - w, to.h()], - Self::Y(h, _) => [to.x(), to.y(), to.w(), to.h() - h], - Self::XY(w, h, _) => [to.x(), to.y(), to.w() - w, to.h() - h] + Self::X(w, _) => [to.w() - w, to.h()], + Self::Y(h, _) => [to.w(), to.h() - h], + Self::XY(w, h, _) => [to.w() - w, to.h() - h] }.into())) } fn render (&self, to: &mut E) -> Perhaps { - Ok(self.layout(to.area())?.map(|a|to.render_in(a, self.inner())).transpose()?.flatten()) + Ok(self.layout(to.area().wh().into())? + .map(|size|to.render_in(to.area().clip(size).into(), self.inner())) + .transpose()?.flatten()) } } @@ -605,7 +642,7 @@ impl Outset { impl> Widget for Inset { type Engine = E; - fn layout (&self, to: E::Area) -> Perhaps { + fn layout (&self, to: E::Size) -> Perhaps { match *self { Self::X(x, ref inner) => Shrink::X(x + x, inner as &dyn Widget), Self::Y(y, ref inner) => Shrink::Y(y + y, inner as &dyn Widget), @@ -623,7 +660,7 @@ impl> Widget for Inset { impl> Widget for Outset { type Engine = E; - fn layout (&self, to: E::Area) -> Perhaps { + fn layout (&self, to: E::Size) -> Perhaps { match *self { Self::X(x, ref inner) => Grow::X(x + x, inner as &dyn Widget), Self::Y(y, ref inner) => Grow::Y(y + y, inner as &dyn Widget), @@ -663,15 +700,17 @@ impl Plus { impl> Widget for Plus { type Engine = E; - fn layout (&self, to: E::Area) -> Perhaps { - Ok(self.inner().layout(to)?.map(|to|match *self { - Self::X(x, _) => [to.x() + x, to.y(), to.w(), to.h()], - Self::Y(y, _) => [to.x(), to.y() + y, to.w(), to.h()], - Self::XY(x, y, _) => [to.x() + x, to.y() + y, to.w(), to.h()] - }.into())) + fn layout (&self, to: E::Size) -> Perhaps { + self.inner().layout(to) } fn render (&self, to: &mut E) -> Perhaps { - Ok(self.layout(to.area())?.map(|a|to.render_in(a, self.inner())).transpose()?.flatten()) + let area = to.area(); + Ok(self.layout(area.wh().into())? + .map(|size|to.render_in(match *self { + Self::X(x, _) => [area.x() + x, area.y(), size.w(), size.h()], + Self::Y(y, _) => [area.x(), area.y() + y, size.w(), size.h()], + Self::XY(x, y, _) => [area.x() + x, area.y() + y, size.w(), size.h()], + }.into(), self.inner())).transpose()?.flatten()) } } @@ -699,15 +738,17 @@ impl Minus { impl> Widget for Minus { type Engine = E; - fn layout (&self, to: E::Area) -> Perhaps { - Ok(self.inner().layout(to)?.map(|to|match *self { - Self::X(x, _) => [to.x().minus(x), to.y(), to.w(), to.h()], - Self::Y(y, _) => [to.x(), to.y().minus(y), to.w(), to.h()], - Self::XY(x, y, _) => [to.x().minus(x), to.y().minus(y), to.w(), to.h()] - }.into())) + fn layout (&self, to: E::Size) -> Perhaps { + self.inner().layout(to) } fn render (&self, to: &mut E) -> Perhaps { - Ok(self.layout(to.area())?.map(|a|to.render_in(a, self.inner())).transpose()?.flatten()) + let area = to.area(); + Ok(self.layout(area.wh().into())? + .map(|size|to.render_in(match *self { + Self::X(x, _) => [area.x().minus(x), area.y(), size.w(), size.h()], + Self::Y(y, _) => [area.x(), area.y().minus(y), size.w(), size.h()], + Self::XY(x, y, _) => [area.x().minus(x), area.y().minus(y), size.w(), size.h()], + }.into(), self.inner())).transpose()?.flatten()) } } diff --git a/crates/tek_core/src/space.rs b/crates/tek_core/src/space.rs index bb1b8dbc..93a957e4 100644 --- a/crates/tek_core/src/space.rs +++ b/crates/tek_core/src/space.rs @@ -1,10 +1,20 @@ use crate::*; +// TODO: return impl Point and impl Size instead of [N;x] +// to disambiguate between usage of 2-"tuple"s + pub trait Size { fn x (&self) -> N; fn y (&self) -> N; - fn w (&self) -> N { self.x() } - fn h (&self) -> N { self.y() } + #[inline] fn w (&self) -> N { self.x() } + #[inline] fn h (&self) -> N { self.y() } + #[inline] 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) + } + } } impl Size for (N, N) { @@ -24,39 +34,39 @@ pub trait Area: Copy { fn h (&self) -> N; fn x2 (&self) -> N { self.x() + self.w() } fn y2 (&self) -> N { self.y() + self.h() } - fn wh (&self) -> [N;2] { + #[inline] fn wh (&self) -> [N;2] { [self.w(), self.h()] } - fn xywh (&self) -> [N;4] { + #[inline] fn xywh (&self) -> [N;4] { [self.x(), self.y(), self.w(), self.h()] } - fn lrtb (&self) -> [N;4] { + #[inline] fn lrtb (&self) -> [N;4] { [self.x(), self.x2(), self.y(), self.y2()] } - fn expect_min (&self, w: N, h: N) -> Usually<&Self> { + #[inline] 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 clip (&self, wh: impl Size) -> [N;4] { + #[inline] fn clip (&self, wh: impl Size) -> [N;4] { [self.x(), self.y(), wh.w(), wh.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 } + #[inline] fn x (&self) -> N { self.0 } + #[inline] fn y (&self) -> N { self.1 } + #[inline] fn w (&self) -> N { self.2 } + #[inline] 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] } + #[inline] fn x (&self) -> N { self[0] } + #[inline] fn y (&self) -> N { self[1] } + #[inline] fn w (&self) -> N { self[2] } + #[inline] fn h (&self) -> N { self[3] } } macro_rules! impl_axis_common { ($A:ident $T:ty) => { diff --git a/crates/tek_core/src/test.rs b/crates/tek_core/src/test.rs index 86f239d6..928a34e2 100644 --- a/crates/tek_core/src/test.rs +++ b/crates/tek_core/src/test.rs @@ -24,7 +24,7 @@ struct TestArea(u16, u16); impl Widget for TestArea { type Engine = TestEngine; - fn layout (&self, to: [u16;4]) -> Perhaps<[u16;4]> { + fn layout (&self, to: [u16;2]) -> Perhaps<[u16;2]> { Ok(Some([to[0], to[1], self.0, self.1])) } fn render (&self, to: &mut Self::Engine) -> Perhaps<[u16;4]> { diff --git a/crates/tek_core/src/tui.rs b/crates/tek_core/src/tui.rs index d442c8e8..353c2f5d 100644 --- a/crates/tek_core/src/tui.rs +++ b/crates/tek_core/src/tui.rs @@ -246,15 +246,15 @@ pub fn buffer_update (buf: &mut Buffer, area: [u16;4], callback: &impl Fn(&mut C impl Widget for &str { type Engine = Tui; - fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { - let [x, y, ..] = area; + fn layout (&self, area: [u16;2]) -> Perhaps<[u16;2]> { // TODO: line breaks - Ok(Some([x, y, self.len() as u16, 1])) + Ok(Some([self.len() as u16, 1])) } fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { - let area = self.layout(to.area())?.unwrap(); - to.blit(&self, area.x(), area.y(), None)?; - Ok(Some(area)) + let [x, y, ..] = to.area(); + let [w, h] = self.layout(to.area().wh())?.unwrap(); + to.blit(&self, x, y, None)?; + Ok(Some([x, y, w, h])) } } @@ -262,12 +262,12 @@ pub struct Styled>(pub Option