diff --git a/Cargo.lock b/Cargo.lock index a1cde1cd..404aba1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2546,6 +2546,15 @@ dependencies = [ "tek_proc", ] +[[package]] +name = "tek_test" +version = "0.1.0" +dependencies = [ + "tek_core", + "tek_mixer", + "tek_sequencer", +] + [[package]] name = "thiserror" version = "1.0.61" diff --git a/Cargo.toml b/Cargo.toml index 51f234d1..ecdaa97c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,5 @@ members = [ "crates/tek_core", "crates/tek_mixer", "crates/tek_sequencer", + "crates/tek_test", ] diff --git a/crates/tek_core/src/audio.rs b/crates/tek_core/src/audio.rs index f96090c8..a3d23759 100644 --- a/crates/tek_core/src/audio.rs +++ b/crates/tek_core/src/audio.rs @@ -18,7 +18,7 @@ pub trait Device: Component + Process { /// All things that implement the required traits can be treated as `Device`. impl Device for D where D: Component + Process {} -impl Render for Box> { +impl<'a, E: Engine> Render for Box + 'a> { fn render (&self, to: &mut E) -> Perhaps { (**self).render(to) } diff --git a/crates/tek_core/src/engine.rs b/crates/tek_core/src/engine.rs index 8c76bec5..ec13a12b 100644 --- a/crates/tek_core/src/engine.rs +++ b/crates/tek_core/src/engine.rs @@ -13,7 +13,7 @@ pub trait Engine: Sized { /// Unit of distance. type Unit: Number; - type Area: Area + From<[Self::Unit;4]>; + type Area: Area + From<[Self::Unit;4]> + Debug; type HandleInput; type Handled; diff --git a/crates/tek_core/src/engine/collect.rs b/crates/tek_core/src/engine/collect.rs index 4c95aee7..51054e85 100644 --- a/crates/tek_core/src/engine/collect.rs +++ b/crates/tek_core/src/engine/collect.rs @@ -53,7 +53,7 @@ impl<'a, E: Engine> Collect<'a, E> for Collection<'a, E> { } } -pub struct Layers<'a, E: Engine>(pub &'a [&'a dyn Render]); +pub struct Layers<'a, E: Engine>(pub &'a [&'a dyn Layout]); // this actually works, except for the type inference //pub struct Layers<'a, E: Engine + 'a, I: std::iter::IntoIterator>>( diff --git a/crates/tek_core/src/engine/component.rs b/crates/tek_core/src/engine/component.rs index cbd99c42..f2e498b5 100644 --- a/crates/tek_core/src/engine/component.rs +++ b/crates/tek_core/src/engine/component.rs @@ -15,3 +15,97 @@ pub trait ExitableComponent: Exit + Component where E: Engine { } impl + Exit> ExitableComponent for C {} + +pub trait Widget { + type Engine: Engine; + fn layout (&self, to: <::Engine as Engine>::Area) -> + Perhaps<<::Engine as Engine>::Area>; + fn render (&self, to: &mut Self::Engine) -> + Perhaps<<::Engine as Engine>::Area>; +} +impl Widget for Box> { + type Engine = E; + fn layout (&self, to: E::Area) -> Perhaps { + (**self).layout(to) + } + fn render (&self, to: &mut E) -> Perhaps { + (**self).render(to) + } +} +impl Widget for &dyn Widget { + type Engine = E; + fn layout (&self, to: E::Area) -> Perhaps { + (*self).layout(to) + } + fn render (&self, to: &mut E) -> Perhaps { + (*self).render(to) + } +} +impl Widget for &mut dyn Widget { + type Engine = E; + fn layout (&self, to: E::Area) -> Perhaps { + (**self).layout(to) + } + fn render (&self, to: &mut E) -> Perhaps { + (**self).render(to) + } +} +impl> Widget for Arc { + type Engine = E; + fn layout (&self, to: E::Area) -> Perhaps { + self.as_ref().layout(to) + } + fn render (&self, to: &mut E) -> Perhaps { + self.as_ref().render(to) + } +} +impl> Widget for Mutex { + type Engine = E; + fn layout (&self, to: E::Area) -> Perhaps { + self.lock().unwrap().layout(to) + } + fn render (&self, to: &mut E) -> Perhaps { + self.lock().unwrap().render(to) + } +} +impl> Widget for RwLock { + type Engine = E; + fn layout (&self, to: E::Area) -> Perhaps { + self.read().unwrap().layout(to) + } + fn render (&self, to: &mut E) -> Perhaps { + self.read().unwrap().render(to) + } +} +impl> Widget for Option { + type Engine = E; + fn layout (&self, to: E::Area) -> Perhaps { + Ok(self.as_ref().map(|widget|widget.layout(to)).transpose()?.flatten()) + } + fn render (&self, to: &mut E) -> Perhaps { + Ok(self.as_ref().map(|widget|widget.render(to)).transpose()?.flatten()) + } +} + +pub trait Content { + type Engine: Engine; + fn content (&self) -> impl Widget::Engine>; +} +//impl Content for () where E: Engine { + //fn content (&self) -> impl Widget { + //() + //} +//} +impl Widget for W where W: Content { + type Engine = ::Engine; + fn layout (&self, to: <::Engine as Engine>::Area) + -> Perhaps<<::Engine as Engine>::Area> + { + self.content().layout(to) + } + fn render (&self, to: &mut ::Engine) + -> Perhaps<<::Engine as Engine>::Area> + { + self.content().render(to) + } +} diff --git a/crates/tek_core/src/engine/layout.rs b/crates/tek_core/src/engine/layout.rs index d647db5d..5acf498a 100644 --- a/crates/tek_core/src/engine/layout.rs +++ b/crates/tek_core/src/engine/layout.rs @@ -14,11 +14,26 @@ pub fn center_box (area: [u16;4], w: u16, h: u16) -> [u16;4] { pub trait Layout: Render { fn layout (&self, area: E::Area) -> Perhaps; } +impl<'a, E: Engine> Layout for Box + 'a> { + fn layout (&self, area: E::Area) -> Perhaps { + (**self).layout(area) + } +} +impl<'a, E: Engine> Layout for Box + 'a> { + fn layout (&self, area: E::Area) -> Perhaps { + (**self).layout(area) + } +} impl> Layout for &T { fn layout (&self, area: E::Area) -> Perhaps { (*self).layout(area) } } +impl> Layout for &mut T { + fn layout (&self, area: E::Area) -> Perhaps { + (**self).layout(area) + } +} impl> Layout for Option { fn layout (&self, area: E::Area) -> Perhaps { match self { @@ -127,6 +142,7 @@ 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 { diff --git a/crates/tek_core/src/engine/render.rs b/crates/tek_core/src/engine/render.rs index 4ad9aaff..29c330de 100644 --- a/crates/tek_core/src/engine/render.rs +++ b/crates/tek_core/src/engine/render.rs @@ -27,6 +27,16 @@ impl<'a, E: Engine> Render for Box + 'a> { (**self).render(to) } } +impl<'a, E: Engine> Render for Box + 'a> { + fn render (&self, to: &mut E) -> Perhaps { + (**self).render(to) + } +} +impl<'a, E: Engine> Render for Box + 'a> { + fn render (&self, to: &mut E) -> Perhaps { + (**self).render(to) + } +} /// Immutable references can be rendered. impl Render for &R where R: Render { diff --git a/crates/tek_core/src/tui.rs b/crates/tek_core/src/tui.rs index 28ad2ab8..55d2c084 100644 --- a/crates/tek_core/src/tui.rs +++ b/crates/tek_core/src/tui.rs @@ -79,7 +79,7 @@ impl Tui { let _input_thread = { let engine = engine.clone(); let state = state.clone(); - let poll = Duration::from_millis(100); + let poll = Duration::from_millis(20); spawn(move || loop { if ::crossterm::event::poll(poll).is_ok() { let event = TuiEvent::Input(::crossterm::event::read().unwrap()); diff --git a/crates/tek_core/src/tui/tui_border.rs b/crates/tek_core/src/tui/tui_border.rs index 78c97859..ce99c926 100644 --- a/crates/tek_core/src/tui/tui_border.rs +++ b/crates/tek_core/src/tui/tui_border.rs @@ -98,6 +98,11 @@ macro_rules! border { const SE: &'static str = $se; $($x)* } + impl Layout for $T { + fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { + Ok(Some(area)) + } + } impl Render for $T { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { self.draw(to) diff --git a/crates/tek_core/src/tui/tui_colors.rs b/crates/tek_core/src/tui/tui_colors.rs index 467b9d8d..cd3a2454 100644 --- a/crates/tek_core/src/tui/tui_colors.rs +++ b/crates/tek_core/src/tui/tui_colors.rs @@ -33,9 +33,14 @@ impl Render for Styled<&str> { pub struct FillBg(pub Color); +impl Layout for FillBg { + fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { + Ok(Some(area)) + } +} impl Render for FillBg { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { - to.fill_bg(to.area, self.0); + to.fill_bg(to.area(), self.0); Ok(Some(to.area)) } } diff --git a/crates/tek_core/src/tui/tui_layout.rs b/crates/tek_core/src/tui/tui_layout.rs index ba11ee85..34069775 100644 --- a/crates/tek_core/src/tui/tui_layout.rs +++ b/crates/tek_core/src/tui/tui_layout.rs @@ -1,12 +1,29 @@ use crate::*; +impl<'a> Layout for Layers<'a, Tui> { + fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { + let [x, y, ..] = area; + let mut w = 0; + let mut h = 0; + for layer in self.0.iter() { + if let Some(layer_area) = layer.layout(area)? { + w = w.max(layer_area.w()); + h = h.max(layer_area.h()); + } + } + Ok(Some([x, y, w, h])) + } +} impl<'a> Render for Layers<'a, Tui> { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { - let area = to.area(); - for layer in self.0.iter() { - layer.render(to)?; - } - Ok(Some(area)) + self.layout(to.area())? + .map(|area| { + for layer in self.0.iter() { + layer.render(to.with_rect(area))?; + } + Ok(area) + }) + .transpose() } } @@ -20,18 +37,16 @@ impl<'a> Render for Layered<'a, Tui> { } } -impl<'a> Render for Split<'a, Tui> { - fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { - Ok(Some(self.render_areas(to)?.0)) - } -} - impl<'a> Layout for Split<'a, Tui> { fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { todo!() } } - +impl<'a> Render for Split<'a, Tui> { + fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { + Ok(Some(self.render_areas(to)?.0)) + } +} // TODO impl<'a> Split<'a, Tui> { pub fn render_areas (&self, to: &mut Tui) -> Usually<([u16;4], Vec>)> { @@ -73,7 +88,7 @@ impl<'a> Split<'a, Tui> { } } -impl> Layout for Align where Self: Render { +impl + Layout> Layout for Align { fn layout (&self, outer_area: [u16;4]) -> Perhaps<[u16;4]> { Ok(match self { Self::Center(inner) => inner, @@ -106,7 +121,7 @@ impl> Layout for Align where Self: Render { } } -impl + Layout> Render for Align { +impl + Layout> Render for Align { fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { self.layout(to.area())? .map(|area|to.with_area(area.x(), area.y(), area.w(), area.h())) diff --git a/crates/tek_test/src/main.rs b/crates/tek_test/src/main.rs index cd210778..67cdc6af 100644 --- a/crates/tek_test/src/main.rs +++ b/crates/tek_test/src/main.rs @@ -1,23 +1,53 @@ use tek_core::*; +use tek_core::jack::*; pub fn main () -> Usually<()> { Tui::run(Arc::new(RwLock::new(Demo::new())))?; Ok(()) } -pub struct Demo { +pub struct Demo { index: usize, - items: Vec>> + items: Vec>> } -impl Demo { +impl Demo { fn new () -> Self { - let items = vec![]; + let mut items: Vec>> = vec![]; + items.push(Box::new(tek_sequencer::TransportPlayPauseButton { + value: Some(TransportState::Stopped), + focused: true + })); + items.push(Box::new(tek_sequencer::TransportPlayPauseButton { + value: Some(TransportState::Rolling), + focused: false + })); Self { index: 0, items } } + fn content <'a> (&'a self) -> impl Layout + 'a { + Align::Center(Layers(&[ + &Outset::WH(2, 1, FillBg(Color::Rgb(0,128,128))), + &Layers(&[ + &"---------", + &Align::Center("...") + ]) + ])) + } } -impl Handle for Demo { +impl Layout for Demo { + fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { + self.content().layout(area) + } +} + +impl Render for Demo { + fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { + self.content().render(to) + } +} + +impl Handle for Demo { fn handle (&mut self, from: &Tui) -> Perhaps { match from.event() { key!(KeyCode::PageUp) => { @@ -36,15 +66,3 @@ impl Handle for Demo { } } } - -impl Layout for Demo { - fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> { - Align::Center(self.items[self.index]).layout(area) - } -} - -impl Render for Demo { - fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> { - Align::Center(self.items[self.index]).render(to) - } -}