unify edn_view entrypoint

This commit is contained in:
🪞👃🪞 2025-01-14 15:39:28 +01:00
parent df50bb9f47
commit 9cd6e9f195
16 changed files with 603 additions and 541 deletions

View file

@ -1,7 +1,8 @@
use tek::*;
use tek_tui::{*, tek_input::*, tek_output::*};
use tek_edn::*;
use std::sync::{Arc, RwLock};
use crossterm::event::{*, KeyCode::*};
use crate::ratatui::style::Color;
const EDN: &'static [&'static str] = &[
include_str!("edn01.edn"),
@ -25,26 +26,15 @@ fn main () -> Usually<()> {
Ok(())
}
pub struct Example(usize, Measure<TuiOut>);
#[derive(Debug)] pub struct Example(usize, Measure<TuiOut>);
impl EdnViewData<TuiOut> for &Example {
fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> {
use EdnItem::*;
match item {
Nil => Box::new(()),
Exp(items) => Box::new(EdnView::from_items(*self, items.as_slice())),
//Sym(name) => self.get_sym(name), TODO
Sym(":title") => Tui::bg(Color::Rgb(60,10,10), Push::y(1,
Align::n(format!("Example {}/{}:", self.0 + 1, EDN.len())))).boxed(),
Sym(":code") => Tui::bg(Color::Rgb(10,60,10), Push::y(2,
Align::n(format!("{}", EDN[self.0])))).boxed(),
Sym(":hello-world") => "Hello world!".boxed(),
Sym(":hello") => Tui::bg(Color::Rgb(10, 100, 10), "Hello").boxed(),
Sym(":world") => Tui::bg(Color::Rgb(100, 10, 10), "world").boxed(),
_ => panic!("no content for {item:?}")
}
}
}
edn_provide_content!(|self: Example|{
":title" => Tui::bg(Color::Rgb(60,10,10), Push::y(1, Align::n(format!("Example {}/{}:", self.0 + 1, EDN.len())))).boxed(),
":code" => Tui::bg(Color::Rgb(10,60,10), Push::y(2, Align::n(format!("{}", EDN[self.0])))).boxed(),
":hello-world" => "Hello world!".boxed(),
":hello" => Tui::bg(Color::Rgb(10, 100, 10), "Hello").boxed(),
":world" => Tui::bg(Color::Rgb(100, 10, 10), "world").boxed(),
});
impl Content<TuiOut> for Example {
fn content (&self) -> impl Render<TuiOut> {

View file

@ -1,11 +1,42 @@
use crate::*;
use std::marker::PhantomData;
use EdnItem::*;
#[macro_export] macro_rules! edn_view {
($Output:ty: |$self:ident: $App:ty| $content:expr; {
$( $type:ty { $($sym:literal => $value:expr),* } );*
}) => {
impl Content<$Output> for $App {
fn content(&$self) -> impl Render<$Output> { $content }
}
$(
impl<'a> EdnProvide<'a, $type> for $App {
fn get <S: AsRef<str>> (&'a $self, edn: &'a EdnItem<S>) -> Option<$type> {
Some(match edn.to_ref() {
$(EdnItem::Sym($sym) => $value,)*
_ => return None
})
}
}
)*
}
}
/// Implements `EdnProvide` for content components and expressions
#[macro_export] macro_rules! edn_provide_content {
(|$self:ident:$Stat:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
impl<'a> EdnProvide<'a, Box<dyn Render<TuiOut> + 'a>> for $State {
fn get <S: AsRef<str>> (&'a $self, edn: &'a EdnItem<S>) -> Option<Box<dyn Render<TuiOut> + 'a>> {
(|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
impl<'a, E: Output> EdnProvide<'a, Box<dyn Render<E> + 'a>> for $State {
fn get <S: AsRef<str>> (&'a $self, edn: &'a EdnItem<S>) -> Option<Box<dyn Render<E> + 'a>> {
Some(match edn.to_ref() {
$(EdnItem::Sym($pat) => $expr),*,
_ => return None
})
}
}
};
($Output:ty: |$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => {
impl<'a> EdnProvide<'a, Box<dyn Render<$Output> + 'a>> for $State {
fn get <S: AsRef<str>> (&'a $self, edn: &'a EdnItem<S>) -> Option<Box<dyn Render<$Output> + 'a>> {
Some(match edn.to_ref() {
$(EdnItem::Sym($pat) => $expr),*,
_ => return None
@ -15,21 +46,23 @@ use EdnItem::*;
}
}
/// Renders from EDN source and context.
#[derive(Default)]
pub enum EdnView<'a, E: Output, T: EdnViewData<'a, E> + std::fmt::Debug> {
#[default]
Inert,
_Unused(PhantomData<&'a E>),
#[derive(Default)] pub enum EdnView<'a, E: Output, T: EdnViewData<'a, E> + std::fmt::Debug> {
#[default] Inert,
Ok(T, EdnItem<&'a str>),
//render: Box<dyn Fn(&'a T)->Box<dyn Render<E> + Send + Sync + 'a> + Send + Sync + 'a>
Err(String)
Err(String),
_Unused(PhantomData<&'a E>),
}
impl<'a, E: Output, T: EdnViewData<'a, E> + std::fmt::Debug> std::fmt::Debug for EdnView<'a, E, T> {
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
Self::Inert | Self::_Unused(_) => write!(f, "EdnView::Inert"),
Self::Ok(state, view) => write!(f, "EdnView::Ok(state={state:?} view={view:?}"),
Self::Err(error) => write!(f, "EdnView::Err({error})"),
Self::Inert | Self::_Unused(_) =>
write!(f, "EdnView::Inert"),
Self::Ok(state, view) =>
write!(f, "EdnView::Ok(state={state:?} view={view:?}"),
Self::Err(error) =>
write!(f, "EdnView::Err({error})"),
_ => unreachable!()
}
}
}

View file

@ -66,42 +66,35 @@ pub type Perhaps<T> = Result<Option<T>, Box<dyn Error>>;
#[cfg(test)] #[test] fn test_layout () -> Usually<()> {
use ::tek_tui::{*, tek_output::*};
let area: [u16;4] = [10, 10, 20, 20];
let unit = ();
assert_eq!(Content::<TuiOut>::layout(&unit, area), [20, 20, 0, 0]);
assert_eq!(Content::<TuiOut>::layout(&Fill::<()>::x(unit), area), [10, 20, 20, 0]);
assert_eq!(Render::<TuiOut>::layout(&Fill::<()>::x(unit), area), [10, 20, 20, 0]);
assert_eq!(Fill::<()>::y(unit).layout(area), [20, 10, 0, 20]);
assert_eq!(Fill::<()>::xy(unit).layout(area), area);
assert_eq!(Fixed::<TuiOut, u16>::x(4, unit).layout(area), [18, 20, 4, 0]);
assert_eq!(Fixed::<TuiOut, u16>::y(4, unit).layout(area), [20, 18, 0, 4]);
assert_eq!(Fixed::<TuiOut, u16>::xy(4, 4, unit).layout(area), [18, 18, 4, 4]);
let four = ||Fixed::<TuiOut, _>::xy(4, 4, unit);
assert_eq!(Align::nw(four()).layout(area), [10, 10, 4, 4]);
assert_eq!(Align::n(four()).layout(area), [18, 10, 4, 4]);
assert_eq!(Align::ne(four()).layout(area), [26, 10, 4, 4]);
assert_eq!(Align::e(four()).layout(area), [26, 18, 4, 4]);
assert_eq!(Align::se(four()).layout(area), [26, 26, 4, 4]);
assert_eq!(Align::s(four()).layout(area), [18, 26, 4, 4]);
assert_eq!(Align::sw(four()).layout(area), [10, 26, 4, 4]);
assert_eq!(Align::w(four()).layout(area), [10, 18, 4, 4]);
let two_by_four = ||Fixed::<TuiOut, _>::xy(4, 2, unit);
assert_eq!(Align::nw(two_by_four()).layout(area), [10, 10, 4, 2]);
assert_eq!(Align::n(two_by_four()).layout(area), [18, 10, 4, 2]);
assert_eq!(Align::ne(two_by_four()).layout(area), [26, 10, 4, 2]);
assert_eq!(Align::e(two_by_four()).layout(area), [26, 19, 4, 2]);
assert_eq!(Align::se(two_by_four()).layout(area), [26, 28, 4, 2]);
assert_eq!(Align::s(two_by_four()).layout(area), [18, 28, 4, 2]);
assert_eq!(Align::sw(two_by_four()).layout(area), [10, 28, 4, 2]);
assert_eq!(Align::w(two_by_four()).layout(area), [10, 19, 4, 2]);
fn test (area: [u16;4], item: &impl Content<TuiOut>, expected: [u16;4]) {
assert_eq!(Content::layout(item, area), expected);
assert_eq!(Render::layout(item, area), expected);
};
test(area, &(), [20, 20, 0, 0]);
test(area, &Fill::xy(()), area);
test(area, &Fill::x(()), [10, 20, 20, 0]);
test(area, &Fill::y(()), [20, 10, 0, 20]);
test(area, &Fixed::x(4, unit), [18, 20, 4, 0]);
test(area, &Fixed::y(4, unit), [20, 18, 0, 4]);
test(area, &Fixed::xy(4, 4, unit), [18, 18, 4, 4]);
let four = ||Fixed::<TuiOut, _, _>::xy(4, 4, unit);
test(area, &Align::nw(four()), [10, 10, 4, 4]);
test(area, &Align::n(four()), [18, 10, 4, 4]);
test(area, &Align::ne(four()), [26, 10, 4, 4]);
test(area, &Align::e(four()), [26, 18, 4, 4]);
test(area, &Align::se(four()), [26, 26, 4, 4]);
test(area, &Align::s(four()), [18, 26, 4, 4]);
test(area, &Align::sw(four()), [10, 26, 4, 4]);
test(area, &Align::w(four()), [10, 18, 4, 4]);
let two_by_four = ||Fixed::<TuiOut, _, _>::xy(4, 2, unit);
test(area, &Align::nw(two_by_four()), [10, 10, 4, 2]);
test(area, &Align::n(two_by_four()), [18, 10, 4, 2]);
test(area, &Align::ne(two_by_four()), [26, 10, 4, 2]);
test(area, &Align::e(two_by_four()), [26, 19, 4, 2]);
test(area, &Align::se(two_by_four()), [26, 28, 4, 2]);
test(area, &Align::s(two_by_four()), [18, 28, 4, 2]);
test(area, &Align::sw(two_by_four()), [10, 28, 4, 2]);
test(area, &Align::w(two_by_four()), [10, 19, 4, 2]);
Ok(())
}