From 4855609a7d4c692a176876111c5edaf503aae1dd Mon Sep 17 00:00:00 2001 From: unspeaker Date: Fri, 6 Sep 2024 23:32:13 +0300 Subject: [PATCH] genericize layout rendering --- crates/tek_core/src/audio.rs | 2 +- crates/tek_core/src/audio/device.rs | 2 +- crates/tek_core/src/engine.rs | 41 +++++---- crates/tek_core/src/engine/collect.rs | 2 +- crates/tek_core/src/engine/layout.rs | 72 +++++++++++++++- crates/tek_core/src/engine/render.rs | 20 ++--- crates/tek_core/src/tui.rs | 26 ++++-- crates/tek_core/src/tui/tui_layout.rs | 120 +++++++++----------------- 8 files changed, 167 insertions(+), 118 deletions(-) diff --git a/crates/tek_core/src/audio.rs b/crates/tek_core/src/audio.rs index fd0f8207..f96090c8 100644 --- a/crates/tek_core/src/audio.rs +++ b/crates/tek_core/src/audio.rs @@ -19,7 +19,7 @@ pub trait Device: Component + Process { impl Device for D where D: Component + Process {} impl Render for Box> { - fn render (&self, to: &mut E::RenderInput) -> Perhaps { + fn render (&self, to: &mut E) -> Perhaps { (**self).render(to) } } diff --git a/crates/tek_core/src/audio/device.rs b/crates/tek_core/src/audio/device.rs index 984acb84..c05da5f9 100644 --- a/crates/tek_core/src/audio/device.rs +++ b/crates/tek_core/src/audio/device.rs @@ -17,7 +17,7 @@ impl std::fmt::Debug for JackDevice { } } impl Render for JackDevice { - fn render (&self, to: &mut E::RenderInput) -> Perhaps { + fn render (&self, to: &mut E) -> Perhaps { self.state.read().unwrap().render(to) } } diff --git a/crates/tek_core/src/engine.rs b/crates/tek_core/src/engine.rs index 79f65174..354f19cf 100644 --- a/crates/tek_core/src/engine.rs +++ b/crates/tek_core/src/engine.rs @@ -7,6 +7,28 @@ pub trait App { fn run (self, context: T) -> Usually; } +/// Platform backend. +pub trait Engine: Sized { + fn setup (&mut self) -> Usually<()> { Ok(()) } + fn exited (&self) -> bool; + fn teardown (&mut self) -> Usually<()> { Ok(()) } + + /// Unit of distance. + type Unit: Number; + type Area: Rectangle; + + type HandleInput; + type Handled; + type RenderInput; + type Rendered; + + // FIXME + fn area (&self) -> Self::Area; + // FIXME + fn with_area (&mut self, x: Self::Unit, y: Self::Unit, w: Self::Unit, h: Self::Unit) + -> &mut Self; +} + pub trait Number: Send + Sync + Copy + Add + Sub @@ -19,25 +41,6 @@ impl Number for T where + Ord + PartialEq + Eq {} -/// Platform backend. -pub trait Engine: Sized { - fn setup (&mut self) -> Usually<()> { Ok(()) } - fn exited (&self) -> bool; - fn teardown (&mut self) -> Usually<()> { Ok(()) } - - /// Unit of distance. - type Unit: Number; - - type HandleInput; - type Handled; - type RenderInput; - type Rendered; -} - -pub trait HandleContext {} - -pub trait RenderContext {} - submod! { collect component diff --git a/crates/tek_core/src/engine/collect.rs b/crates/tek_core/src/engine/collect.rs index f44ca701..048999a6 100644 --- a/crates/tek_core/src/engine/collect.rs +++ b/crates/tek_core/src/engine/collect.rs @@ -6,7 +6,7 @@ pub enum Collected<'a, E: Engine> { } impl<'a, E: Engine> Render for Collected<'a, E> { - fn render (&self, to: &mut E::RenderInput) -> Perhaps { + fn render (&self, to: &mut E) -> Perhaps { match self { Self::Box(item) => (*item).render(to), Self::Ref(item) => (*item).render(to), diff --git a/crates/tek_core/src/engine/layout.rs b/crates/tek_core/src/engine/layout.rs index c8dad2e5..8239e667 100644 --- a/crates/tek_core/src/engine/layout.rs +++ b/crates/tek_core/src/engine/layout.rs @@ -1,7 +1,7 @@ use crate::*; /// Compute drawing area before rendering -pub trait Layout { +pub trait Layout: Render { fn layout (&self, area: impl Rectangle) -> Perhaps>; } /// Enforce minimum size of drawing area @@ -98,3 +98,73 @@ impl> Layout for Offset { } } } + +impl + Layout> Render for Min { + fn render (&self, to: &mut E) -> Perhaps { + self.layout(to.area())? + .map(|area|to.with_area(area.x(), area.y(), area.w(), area.h())) + .map(|to|match self { + Self::W(_, inner) => inner, + Self::H(_, inner) => inner, + Self::WH(_, _, inner) => inner, + }.render(to)) + .transpose() + .map(|x|x.flatten()) + } +} + +impl + Layout> Render for Max { + fn render (&self, to: &mut E) -> Perhaps { + self.layout(to.area())? + .map(|area|to.with_area(area.x(), area.y(), area.w(), area.h())) + .map(|to|match self { + Self::W(_, inner) => inner, + Self::H(_, inner) => inner, + Self::WH(_, _, inner) => inner, + }.render(to)) + .transpose() + .map(|x|x.flatten()) + } +} + +impl + Layout> Render for Inset { + fn render (&self, to: &mut E) -> Perhaps { + self.layout(to.area())? + .map(|area|to.with_area(area.x(), area.y(), area.w(), area.h())) + .map(|to|match self { + Self::W(_, inner) => inner, + Self::H(_, inner) => inner, + Self::WH(_, _, inner) => inner, + }.render(to)) + .transpose() + .map(|x|x.flatten()) + } +} + +impl + Layout> Render for Outset { + fn render (&self, to: &mut E) -> Perhaps { + self.layout(to.area())? + .map(|area|to.with_area(area.x(), area.y(), area.w(), area.h())) + .map(|to|match self { + Self::W(_, inner) => inner, + Self::H(_, inner) => inner, + Self::WH(_, _, inner) => inner, + }.render(to)) + .transpose() + .map(|x|x.flatten()) + } +} + +impl + Layout> Render for Offset { + fn render (&self, to: &mut E) -> Perhaps { + self.layout(to.area())? + .map(|area|to.with_area(area.x(), area.y(), area.w(), area.h())) + .map(|to|match self { + Self::X(_, inner) => inner, + Self::Y(_, inner) => inner, + Self::XY(_, _, inner) => inner, + }.render(to)) + .transpose() + .map(|x|x.flatten()) + } +} diff --git a/crates/tek_core/src/engine/render.rs b/crates/tek_core/src/engine/render.rs index 7c9aaaf1..e0c54f6a 100644 --- a/crates/tek_core/src/engine/render.rs +++ b/crates/tek_core/src/engine/render.rs @@ -2,12 +2,12 @@ use crate::*; /// Render to output. pub trait Render: Send + Sync { - fn render (&self, to: &mut E::RenderInput) -> Perhaps; + fn render (&self, to: &mut E) -> Perhaps; } /// Options can be rendered optionally. impl Render for Option where R: Render { - fn render (&self, to: &mut E::RenderInput) -> Perhaps { + fn render (&self, to: &mut E) -> Perhaps { match self { Some(component) => component.render(to), None => Ok(None) @@ -17,42 +17,42 @@ impl Render for Option where R: Render { /// Boxed references can be rendered. impl<'a, E: Engine> Render for Box + 'a> { - fn render (&self, to: &mut E::RenderInput) -> Perhaps { + fn render (&self, to: &mut E) -> Perhaps { (**self).render(to) } } /// Immutable references can be rendered. impl Render for &R where R: Render { - fn render (&self, to: &mut E::RenderInput) -> Perhaps { + fn render (&self, to: &mut E) -> Perhaps { (*self).render(to) } } /// Mutable references can be rendered. impl Render for &mut R where R: Render { - fn render (&self, to: &mut E::RenderInput) -> Perhaps { + fn render (&self, to: &mut E) -> Perhaps { (**self).render(to) } } /// Counted references can be rendered. impl Render for Arc where R: Render { - fn render (&self, to: &mut E::RenderInput) -> Perhaps { + fn render (&self, to: &mut E) -> Perhaps { self.as_ref().render(to) } } /// References behind a [Mutex] can be rendered. impl Render for Mutex where R: Render { - fn render (&self, to: &mut E::RenderInput) -> Perhaps { + fn render (&self, to: &mut E) -> Perhaps { self.lock().unwrap().render(to) } } /// References behind a [RwLock] can be rendered. impl Render for RwLock where R: Render { - fn render (&self, to: &mut E::RenderInput) -> Perhaps { + fn render (&self, to: &mut E) -> Perhaps { self.read().unwrap().render(to) } } @@ -62,8 +62,8 @@ impl Render for RwLock where R: Render { /// Rendering unboxed closures should also be possible; /// but in practice implementing the trait for an unboxed /// `Fn` closure causes an impl conflict. -impl<'a, E: Engine> Render for Box Perhaps + Send + Sync + 'a> { - fn render (&self, to: &mut E::RenderInput) -> Perhaps { +impl<'a, E: Engine> Render for Box Perhaps + Send + Sync + 'a> { + fn render (&self, to: &mut E) -> Perhaps { (*self)(to) } } diff --git a/crates/tek_core/src/tui.rs b/crates/tek_core/src/tui.rs index 1ffba0de..b6751946 100644 --- a/crates/tek_core/src/tui.rs +++ b/crates/tek_core/src/tui.rs @@ -26,6 +26,7 @@ pub struct Tui { } impl Engine for Tui { type Unit = u16; + type Area = Rect; type HandleInput = Self; type Handled = bool; type RenderInput = Self; @@ -47,6 +48,15 @@ impl Engine for Tui { stdout().execute(LeaveAlternateScreen)?; disable_raw_mode().map_err(Into::into) } + // FIXME + fn area (&self) -> Self::Area { + self.area + } + #[inline] + fn with_area (&mut self, x: u16, y: u16, w: u16, h: u16) -> &mut Self { + self.with_rect(Rect { x, y, width: w, height: h }); + self + } } impl Tui { /// Run the main loop. @@ -113,9 +123,6 @@ impl Tui { pub fn event (&self) -> TuiEvent { self.event.read().unwrap().clone().unwrap() } - pub fn area (&self) -> Rect { - self.area - } pub fn buffer (&mut self) -> &mut Buffer { &mut self.buffers[self.buffer] } @@ -172,11 +179,6 @@ impl Tui { self.with_area(x, y, width, height) } #[inline] - pub fn with_area (&mut self, x: u16, y: u16, w: u16, h: u16) -> &mut Self { - self.with_rect(Rect { x, y, width: w, height: h }); - self - } - #[inline] pub fn with_rect (&mut self, area: Rect) -> &mut Self { self.area = area; self @@ -221,3 +223,11 @@ pub fn half_block (lower: bool, upper: bool) -> Option { _ => None } } + +impl Rectangle for Rect { + fn x (&self) -> u16 { self.x } + fn y (&self) -> u16 { self.y } + fn w (&self) -> u16 { self.width } + fn h (&self) -> u16 { self.height } +} + diff --git a/crates/tek_core/src/tui/tui_layout.rs b/crates/tek_core/src/tui/tui_layout.rs index e1a3cf9c..f82ce793 100644 --- a/crates/tek_core/src/tui/tui_layout.rs +++ b/crates/tek_core/src/tui/tui_layout.rs @@ -1,82 +1,5 @@ use crate::*; -impl Rectangle for Rect { - fn x (&self) -> u16 { self.x } - fn y (&self) -> u16 { self.y } - fn w (&self) -> u16 { self.width } - fn h (&self) -> u16 { self.height } -} - -impl Render for Min<::Unit, R> where R: Render + Layout { - fn render (&self, to: &mut Tui) -> Perhaps { - self.layout(to.area())? - .map(|area|to.with_area(area.x(), area.y(), area.w(), area.h())) - .map(|to|match self { - Self::W(_, inner) => inner, - Self::H(_, inner) => inner, - Self::WH(_, _, inner) => inner, - }.render(to)) - .transpose() - .map(|x|x.flatten()) - } -} - -impl Render for Max<::Unit, R> where R: Render + Layout { - fn render (&self, to: &mut Tui) -> Perhaps { - self.layout(to.area())? - .map(|area|to.with_area(area.x(), area.y(), area.w(), area.h())) - .map(|to|match self { - Self::W(_, inner) => inner, - Self::H(_, inner) => inner, - Self::WH(_, _, inner) => inner, - }.render(to)) - .transpose() - .map(|x|x.flatten()) - } -} - -impl Render for Inset<::Unit, R> where R: Render + Layout { - fn render (&self, to: &mut Tui) -> Perhaps { - self.layout(to.area())? - .map(|area|to.with_area(area.x(), area.y(), area.w(), area.h())) - .map(|to|match self { - Self::W(_, inner) => inner, - Self::H(_, inner) => inner, - Self::WH(_, _, inner) => inner, - }.render(to)) - .transpose() - .map(|x|x.flatten()) - } -} - -impl Render for Outset<::Unit, R> where R: Render + Layout { - fn render (&self, to: &mut Tui) -> Perhaps { - self.layout(to.area())? - .map(|area|to.with_area(area.x(), area.y(), area.w(), area.h())) - .map(|to|match self { - Self::W(_, inner) => inner, - Self::H(_, inner) => inner, - Self::WH(_, _, inner) => inner, - }.render(to)) - .transpose() - .map(|x|x.flatten()) - } -} - -impl Render for Offset<::Unit, R> where R: Render + Layout { - fn render (&self, to: &mut Tui) -> Perhaps { - self.layout(to.area())? - .map(|area|to.with_area(area.x(), area.y(), area.w(), area.h())) - .map(|to|match self { - Self::X(_, inner) => inner, - Self::Y(_, inner) => inner, - Self::XY(_, _, inner) => inner, - }.render(to)) - .transpose() - .map(|x|x.flatten()) - } -} - impl<'a> Render for Layered<'a, Tui> { fn render (&self, to: &mut Tui) -> Perhaps { let area = to.area(); @@ -138,3 +61,46 @@ impl<'a> Split<'a, Tui> { Ok((Rect { x, y, width, height }, areas)) } } + +// TODO +//impl<'a> Split<'a, Tui> { + //pub fn render_areas (&self, to: &mut Tui) + //-> Usually<(Rectangle, Vec>>)> + //{ + //let area = to.area(); + //let mut w = 0u16; + //let mut h = 0u16; + //let mut areas = vec![]; + //Ok((match self.direction { + //Direction::Down => { + //for component in self.items.0.iter() { + //if h >= area.h() { + //break + //} + //let result = Offset::Y(h, component).render(to)?; + //areas.push(result); + //if let Some(Rect { width, height, .. }) = result { + //h += height; + //w = w.max(width) + //} + //} + //(area.x(), area.y(), w, h) + //}, + //Direction::Right => { + //for component in self.items.0.iter() { + //if w >= area.x() { + //break + //} + //let result = Offset::X(w, component).render(to)?; + //areas.push(result); + //if let Some(Rect { width, height, .. }) = result { + //w += width; + //h = h.max(height) + //} + //} + //(area.x(), area.y(), w, h) + //}, + //_ => todo!() + //}, areas)) + //} +//}