wip: Content and Widget traits

This commit is contained in:
🪞👃🪞 2024-09-08 23:48:30 +03:00
parent 5fc7da3aca
commit b944dd5f9e
13 changed files with 209 additions and 36 deletions

9
Cargo.lock generated
View file

@ -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"

View file

@ -4,4 +4,5 @@ members = [
"crates/tek_core",
"crates/tek_mixer",
"crates/tek_sequencer",
"crates/tek_test",
]

View file

@ -18,7 +18,7 @@ pub trait Device<E: Engine>: Component<E> + Process {
/// All things that implement the required traits can be treated as `Device`.
impl<D, E: Engine> Device<E> for D where D: Component<E> + Process {}
impl<E: Engine> Render<E> for Box<dyn Device<E>> {
impl<'a, E: Engine> Render<E> for Box<dyn Device<E> + 'a> {
fn render (&self, to: &mut E) -> Perhaps<E::Rendered> {
(**self).render(to)
}

View file

@ -13,7 +13,7 @@ pub trait Engine: Sized {
/// Unit of distance.
type Unit: Number;
type Area: Area<Self::Unit> + From<[Self::Unit;4]>;
type Area: Area<Self::Unit> + From<[Self::Unit;4]> + Debug;
type HandleInput;
type Handled;

View file

@ -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<E>]);
pub struct Layers<'a, E: Engine>(pub &'a [&'a dyn Layout<E>]);
// this actually works, except for the type inference
//pub struct Layers<'a, E: Engine + 'a, I: std::iter::IntoIterator<Item = &'a dyn Render<E>>>(

View file

@ -15,3 +15,97 @@ pub trait ExitableComponent<E>: Exit + Component<E> where E: Engine {
}
impl<E: Engine, C: Component<E> + Exit> ExitableComponent<E> for C {}
pub trait Widget {
type Engine: Engine;
fn layout (&self, to: <<Self as Widget>::Engine as Engine>::Area) ->
Perhaps<<<Self as Widget>::Engine as Engine>::Area>;
fn render (&self, to: &mut Self::Engine) ->
Perhaps<<<Self as Widget>::Engine as Engine>::Area>;
}
impl<E: Engine> Widget for Box<dyn Widget<Engine = E>> {
type Engine = E;
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
(**self).layout(to)
}
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
(**self).render(to)
}
}
impl<E: Engine> Widget for &dyn Widget<Engine = E> {
type Engine = E;
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
(*self).layout(to)
}
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
(*self).render(to)
}
}
impl<E: Engine> Widget for &mut dyn Widget<Engine = E> {
type Engine = E;
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
(**self).layout(to)
}
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
(**self).render(to)
}
}
impl<E: Engine, W: Widget<Engine = E>> Widget for Arc<W> {
type Engine = E;
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
self.as_ref().layout(to)
}
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
self.as_ref().render(to)
}
}
impl<E: Engine, W: Widget<Engine = E>> Widget for Mutex<W> {
type Engine = E;
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
self.lock().unwrap().layout(to)
}
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
self.lock().unwrap().render(to)
}
}
impl<E: Engine, W: Widget<Engine = E>> Widget for RwLock<W> {
type Engine = E;
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
self.read().unwrap().layout(to)
}
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
self.read().unwrap().render(to)
}
}
impl<E: Engine, W: Widget<Engine = E>> Widget for Option<W> {
type Engine = E;
fn layout (&self, to: E::Area) -> Perhaps<E::Area> {
Ok(self.as_ref().map(|widget|widget.layout(to)).transpose()?.flatten())
}
fn render (&self, to: &mut E) -> Perhaps<E::Area> {
Ok(self.as_ref().map(|widget|widget.render(to)).transpose()?.flatten())
}
}
pub trait Content {
type Engine: Engine;
fn content (&self) -> impl Widget<Engine = <Self as Content>::Engine>;
}
//impl<E> Content<E> for () where E: Engine {
//fn content (&self) -> impl Widget<E> {
//()
//}
//}
impl<W> Widget for W where W: Content {
type Engine = <Self as Content>::Engine;
fn layout (&self, to: <<Self as Content>::Engine as Engine>::Area)
-> Perhaps<<<Self as Content>::Engine as Engine>::Area>
{
self.content().layout(to)
}
fn render (&self, to: &mut <Self as Content>::Engine)
-> Perhaps<<<Self as Content>::Engine as Engine>::Area>
{
self.content().render(to)
}
}

View file

@ -14,11 +14,26 @@ pub fn center_box (area: [u16;4], w: u16, h: u16) -> [u16;4] {
pub trait Layout<E: Engine>: Render<E> {
fn layout (&self, area: E::Area) -> Perhaps<E::Area>;
}
impl<'a, E: Engine> Layout<E> for Box<dyn Layout<E> + 'a> {
fn layout (&self, area: E::Area) -> Perhaps<E::Area> {
(**self).layout(area)
}
}
impl<'a, E: Engine> Layout<E> for Box<dyn Component<E> + 'a> {
fn layout (&self, area: E::Area) -> Perhaps<E::Area> {
(**self).layout(area)
}
}
impl<E: Engine, T: Layout<E>> Layout<E> for &T {
fn layout (&self, area: E::Area) -> Perhaps<E::Area> {
(*self).layout(area)
}
}
impl<E: Engine, T: Layout<E>> Layout<E> for &mut T {
fn layout (&self, area: E::Area) -> Perhaps<E::Area> {
(**self).layout(area)
}
}
impl<E: Engine, T: Layout<E>> Layout<E> for Option<T> {
fn layout (&self, area: E::Area) -> Perhaps<E::Area> {
match self {
@ -127,6 +142,7 @@ impl<E: Engine, L: Layout<E>> Layout<E> for Offset<E::Unit, L> {
impl<E: Engine, R: Render<E> + Layout<E>> Render<E> for Min<E::Unit, R> {
fn render (&self, to: &mut E) -> Perhaps<E::Rendered> {
// 🡘 🡙 ←🡙→
self.layout(to.area())?
.map(|area|to.with_area(area.x(), area.y(), area.w(), area.h()))
.map(|to|match self {

View file

@ -27,6 +27,16 @@ impl<'a, E: Engine> Render<E> for Box<dyn Render<E> + 'a> {
(**self).render(to)
}
}
impl<'a, E: Engine> Render<E> for Box<dyn Layout<E> + 'a> {
fn render (&self, to: &mut E) -> Perhaps<E::Rendered> {
(**self).render(to)
}
}
impl<'a, E: Engine> Render<E> for Box<dyn Component<E> + 'a> {
fn render (&self, to: &mut E) -> Perhaps<E::Rendered> {
(**self).render(to)
}
}
/// Immutable references can be rendered.
impl<R, E: Engine> Render<E> for &R where R: Render<E> {

View file

@ -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());

View file

@ -98,6 +98,11 @@ macro_rules! border {
const SE: &'static str = $se;
$($x)*
}
impl Layout<Tui> for $T {
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
Ok(Some(area))
}
}
impl Render<Tui> for $T {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
self.draw(to)

View file

@ -33,9 +33,14 @@ impl Render<Tui> for Styled<&str> {
pub struct FillBg(pub Color);
impl Layout<Tui> for FillBg {
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
Ok(Some(area))
}
}
impl Render<Tui> 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))
}
}

View file

@ -1,12 +1,29 @@
use crate::*;
impl<'a> Layout<Tui> 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<Tui> 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<Tui> for Layered<'a, Tui> {
}
}
impl<'a> Render<Tui> for Split<'a, Tui> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
Ok(Some(self.render_areas(to)?.0))
}
}
impl<'a> Layout<Tui> for Split<'a, Tui> {
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
todo!()
}
}
impl<'a> Render<Tui> 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<Option<[u16;4]>>)> {
@ -73,7 +88,7 @@ impl<'a> Split<'a, Tui> {
}
}
impl<L: Layout<Tui>> Layout<Tui> for Align<L> where Self: Render<Tui> {
impl<T: Render<Tui> + Layout<Tui>> Layout<Tui> for Align<T> {
fn layout (&self, outer_area: [u16;4]) -> Perhaps<[u16;4]> {
Ok(match self {
Self::Center(inner) => inner,
@ -106,7 +121,7 @@ impl<L: Layout<Tui>> Layout<Tui> for Align<L> where Self: Render<Tui> {
}
}
impl<R: Render<Tui> + Layout<Tui>> Render<Tui> for Align<R> {
impl<T: Render<Tui> + Layout<Tui>> Render<Tui> for Align<T> {
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()))

View file

@ -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<E: Engine> {
index: usize,
items: Vec<Box<dyn Component<Tui>>>
items: Vec<Box<dyn Component<E>>>
}
impl Demo {
impl Demo<Tui> {
fn new () -> Self {
let items = vec![];
let mut items: Vec<Box<dyn Component<Tui>>> = 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<Tui> + 'a {
Align::Center(Layers(&[
&Outset::WH(2, 1, FillBg(Color::Rgb(0,128,128))),
&Layers(&[
&"---------",
&Align::Center("...")
])
]))
}
}
impl Handle<Tui> for Demo {
impl Layout<Tui> for Demo<Tui> {
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
self.content().layout(area)
}
}
impl Render<Tui> for Demo<Tui> {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
self.content().render(to)
}
}
impl Handle<Tui> for Demo<Tui> {
fn handle (&mut self, from: &Tui) -> Perhaps<bool> {
match from.event() {
key!(KeyCode::PageUp) => {
@ -36,15 +66,3 @@ impl Handle<Tui> for Demo {
}
}
}
impl Layout<Tui> for Demo {
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
Align::Center(self.items[self.index]).layout(area)
}
}
impl Render<Tui> for Demo {
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
Align::Center(self.items[self.index]).render(to)
}
}