output: more big refactors

This commit is contained in:
🪞👃🪞 2025-09-07 12:34:30 +03:00
parent 194f2f9874
commit 5e6338fad8
68 changed files with 1604 additions and 1016 deletions

View file

@ -5,7 +5,8 @@ version = { workspace = true }
edition = { workspace = true }
[features]
dsl = [ "tengri_dsl", "tengri_output/dsl" ]
dsl = [ "dep:tengri_dsl", "tengri_output/dsl" ]
bumpalo = [ "dep:bumpalo" ]
[dependencies]
tengri_core = { workspace = true }
@ -13,14 +14,15 @@ tengri_input = { workspace = true }
tengri_output = { workspace = true }
tengri_dsl = { workspace = true, optional = true }
palette = { workspace = true }
rand = { workspace = true }
crossterm = { workspace = true }
ratatui = { workspace = true }
better-panic = { workspace = true }
konst = { workspace = true }
atomic_float = { workspace = true }
better-panic = { workspace = true }
bumpalo = { workspace = true, optional = true }
crossterm = { workspace = true }
konst = { workspace = true }
palette = { workspace = true }
quanta = { workspace = true }
rand = { workspace = true }
ratatui = { workspace = true }
unicode-width = { workspace = true }
[dev-dependencies]

View file

@ -3,7 +3,7 @@
(bg/behind :bg2 (border/around :border2 (margin/xy 4 2 :label2)))
(bg/behind :bg3 (border/around :border3 (margin/xy 6 3 :label3)))))))
fn content (&self) -> dyn Render<Engine = Tui> {
fn content (&self) -> dyn Draw<Engine = Tui> {
let border_style = Style::default().fg(Color::Rgb(0,0,0));
Align::Center(Layers::new(move|add|{

View file

@ -76,25 +76,25 @@ content!(TuiOut: |self: Example|{
//#[tengri_proc::view(TuiOut)]
//impl Example {
//pub fn title (&self) -> impl Render<TuiOut> + use<'_> {
//pub fn title (&self) -> impl Draw<TuiOut> + use<'_> {
//Tui::bg(Color::Rgb(60, 10, 10), Push::y(1, Align::n(format!("Example {}/{}:", self.0 + 1, EXAMPLES.len())))).boxed()
//}
//pub fn code (&self) -> impl Render<TuiOut> + use<'_> {
//pub fn code (&self) -> impl Draw<TuiOut> + use<'_> {
//Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", EXAMPLES[self.0])))).boxed()
//}
//pub fn hello (&self) -> impl Render<TuiOut> + use<'_> {
//pub fn hello (&self) -> impl Draw<TuiOut> + use<'_> {
//Tui::bg(Color::Rgb(10, 100, 10), "Hello").boxed()
//}
//pub fn world (&self) -> impl Render<TuiOut> + use<'_> {
//pub fn world (&self) -> impl Draw<TuiOut> + use<'_> {
//Tui::bg(Color::Rgb(100, 10, 10), "world").boxed()
//}
//pub fn hello_world (&self) -> impl Render<TuiOut> + use<'_> {
//pub fn hello_world (&self) -> impl Draw<TuiOut> + use<'_> {
//"Hello world!".boxed()
//}
//pub fn map_e (&self) -> impl Render<TuiOut> + use<'_> {
//pub fn map_e (&self) -> impl Draw<TuiOut> + use<'_> {
//Map::east(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed()
//}
//pub fn map_s (&self) -> impl Render<TuiOut> + use<'_> {
//pub fn map_s (&self) -> impl Draw<TuiOut> + use<'_> {
//Map::south(5u16, ||0..5u16, |n, _i|format!("{n}")).boxed()
//}
//}

View file

@ -27,7 +27,7 @@ pub(crate) use std::io::{stdout, Stdout};
//use std::sync::{Arc, RwLock};
struct TestComponent(String);
impl Content<TuiOut> for TestComponent {
fn content (&self) -> impl Render<TuiOut> {
fn content (&self) -> impl Draw<TuiOut> {
Some(self.0.as_str())
}
}

View file

@ -1,56 +1,128 @@
use crate::*;
#[allow(unused)] use crate::*;
macro_rules! impl_content_layout_render {
($Output:ty: |$self:ident: $Struct:ty, $to:ident|
layout = $layout:expr;
render = $render:expr) =>
{
impl Render<$Output> for $Struct {
fn layout (&$self, $to: [u16;4]) -> [u16;4] { $layout }
fn render (&$self, $to: &mut $Output) { $render }
impl Tui {
pub const fn fg <T> (color: Color, w: T) -> TuiForeground<T> {
TuiForeground(Foreground(color, w))
}
pub const fn bg <T> (color: Color, w: T) -> TuiBackground<T> {
TuiBackground(Background(color, w))
}
pub const fn fg_bg <T> (fg: Color, bg: Color, w: T) -> TuiBackground<TuiForeground<T>> {
TuiBackground(Background(bg, TuiForeground(Foreground(fg, w))))
}
pub const fn modify <T> (enable: bool, modifier: Modifier, w: T) -> Modify<T> {
Modify(enable, modifier, w)
}
pub const fn bold <T> (enable: bool, w: T) -> Modify<T> {
Self::modify(enable, Modifier::BOLD, w)
}
pub const fn border <S, T> (enable: bool, style: S, w: T) -> Bordered<S, T> {
Bordered(enable, style, w)
}
}
#[macro_export] macro_rules! tui_layout ((|$self:ident:$Self:ty, $to:ident|$expr:expr)=>{
impl Layout<TuiOut> for $Self {
fn layout (&$self, $to: [u16;4]) -> [u16;4] {
$expr
}
}
}
});
#[macro_export] macro_rules! tui_draw ((|$self:ident:$Self:ty, $to:ident|$expr:expr)=>{
impl Draw<TuiOut> for $Self {
fn draw (&$self, $to: &mut TuiOut) {
$expr
}
}
});
mod tui_border; pub use self::tui_border::*;
mod tui_button; pub use self::tui_button::*;
mod tui_color; pub use self::tui_color::*;
mod tui_error; pub use self::tui_error::*;
mod tui_field; pub use self::tui_field::*;
mod tui_phat; pub use self::tui_phat::*;
mod tui_repeat; pub use self::tui_repeat::*;
mod tui_scroll; pub use self::tui_scroll::*;
mod tui_string; pub use self::tui_string::*;
mod tui_border; pub use self::tui_border::*;
mod tui_button; //pub use self::tui_button::*;
mod tui_color; pub use self::tui_color::*;
mod tui_error; pub use self::tui_error::*;
mod tui_field; pub use self::tui_field::*;
mod tui_number; //pub use self::tui_number::*;
mod tui_phat; pub use self::tui_phat::*;
mod tui_repeat; pub use self::tui_repeat::*;
mod tui_scroll; pub use self::tui_scroll::*;
mod tui_string; pub use self::tui_string::*;
mod tui_style; pub use self::tui_style::*;
mod tui_tryptich; pub use self::tui_tryptich::*;
mod tui_tryptich; //pub use self::tui_tryptich::*;
impl<T: Render<TuiOut>> Render<TuiOut> for std::sync::Arc<T> {
pub struct TuiForeground<T>(pub(crate) Foreground<Color, T>);
pub struct TuiBackground<T>(pub(crate) Background<Color, T>);
pub struct Modify<T>(pub bool, pub Modifier, pub T);
pub struct Styled<T>(pub Option<Style>, pub T);
impl<T: Layout<TuiOut>> Layout<TuiOut> for TuiForeground<T> {
fn layout (&self, to: [u16;4]) -> [u16;4] {
Render::<TuiOut>::layout(&**self, to)
self.0.layout(to)
}
fn render (&self, to: &mut TuiOut) {
Render::<TuiOut>::render(&**self, to)
}
impl<T: Layout<TuiOut> + Draw<TuiOut>> Draw<TuiOut> for TuiForeground<T> {
fn draw (&self, to: &mut TuiOut) {
let area = self.layout(to.area());
to.fill_bg(area, self.0.0);
to.place_at(area, &self.0.1);
}
}
impl<T: Render<TuiOut>> Content<TuiOut> for Result<T, Box<dyn std::error::Error>> {
fn content (&self) -> Option<impl Render<TuiOut> + '_> {
Some(Bsp::a(self.as_ref().ok(), self.as_ref().err()
.map(|e|Tui::fg_bg(Color::Rgb(255,255,255), Color::Rgb(32,32,32), e.to_string()))))
impl<T: Layout<TuiOut>> Layout<TuiOut> for TuiBackground<T> {
fn layout (&self, to: [u16;4]) -> [u16;4] {
self.0.layout(to)
}
}
impl<T: Layout<TuiOut> + Draw<TuiOut>> Draw<TuiOut> for TuiBackground<T> {
fn draw (&self, to: &mut TuiOut) {
let area = self.layout(to.area());
to.fill_bg(area, self.0.0);
to.place_at(area, &self.0.1);
}
}
//impl<T: Render<TuiOut>> Render<TuiOut> for Result<T, Box<dyn std::error::Error>> {
impl<T: Layout<TuiOut>> Layout<TuiOut> for Modify<T> {
fn layout (&self, to: [u16;4]) -> [u16;4] {
self.2.layout(to)
}
}
impl<T: Draw<TuiOut>> Draw<TuiOut> for Modify<T> {
fn draw (&self, to: &mut TuiOut) {
to.fill_mod(to.area(), self.0, self.1);
self.2.draw(to)
}
}
impl<T: Layout<TuiOut>> Layout<TuiOut> for Styled<T> {
fn layout (&self, to: [u16;4]) -> [u16;4] {
self.1.layout(to)
}
}
impl<T: Layout<TuiOut> + Draw<TuiOut>> Draw<TuiOut> for Styled<T> {
fn draw (&self, to: &mut TuiOut) {
to.place(&self.1);
// TODO write style over area
}
}
//impl<T: Draw<TuiOut> + Layout<TuiOut>> Content<TuiOut> for Result<T, Box<dyn std::error::Error>> {
//fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
//Bsp::a(self.as_ref().ok(), self.as_ref().err().map(
//|e|Tui::fg_bg(Color::Rgb(255,255,255), Color::Rgb(32,32,32), e.to_string())
//))
//}
//}
//impl<T: Draw<TuiOut>> Draw<TuiOut> for Result<T, Box<dyn std::error::Error>> {
//fn layout (&self, to: [u16;4]) -> [u16;4] {
//match self {
//Ok(content) => content.layout(to),
//Err(e) => [0, 0, to.w(), to.h()]
//}
//}
//fn render (&self, to: &mut TuiOut) {
//fn draw (&self, to: &mut TuiOut) {
//match self {
//Ok(content) => content.render(to),
//Ok(content) => content.draw(to),
//Err(e) => to.blit(&e.to_string(), 0, 0, Some(Style::default()
//.bg(Color::Rgb(32,32,32))
//.fg(Color::Rgb(255,255,255))))

View file

@ -243,7 +243,7 @@ impl<T: FocusGrid + HasEnter> FocusOrder for T {
}
pub trait FocusWrap<T> {
fn wrap <W: Content<TuiOut>> (self, focus: T, content: &'_ W) -> impl Render<TuiOut> + '_;
fn wrap <W: Content<TuiOut>> (self, focus: T, content: &'_ W) -> impl Draw<TuiOut> + '_;
}
pub fn to_focus_command <T: Send + Sync> (input: &TuiIn) -> Option<FocusCommand<T>> {

View file

@ -1,20 +1,16 @@
use crate::*;
pub struct Bordered<S, W>(pub bool, pub S, pub W);
impl<S: BorderStyle, W: Render<TuiOut>> Content<TuiOut> for Bordered<S, W> {
fn content (&self) -> Option<impl Render<TuiOut> + '_> {
Some(Fill::xy(
lay!(When::new(self.0, Border(self.0, self.1)), Padding::xy(1, 1, &self.2))
impl<S: BorderStyle, W: Draw<TuiOut> + Layout<TuiOut>> Content<TuiOut> for Bordered<S, W> {
fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
Fill::xy(lay!(
When::new(self.0, Border(self.0, self.1)),
Padding::xy(1, 1, &self.2)
))
}
}
pub struct Border<S: BorderStyle>(pub bool, pub S);
impl<S: BorderStyle> Render<TuiOut> for Border<S> {
fn layout (&self, area: [u16;4]) -> [u16;4] {
self.1.layout(area)
}
fn render (&self, to: &mut TuiOut) {
impl<S: BorderStyle> Draw<TuiOut> for Border<S> {
fn draw (&self, to: &mut TuiOut) {
if self.0 {
let area = to.area();
if area.w() > 0 && area.y() > 0 {
@ -35,15 +31,15 @@ impl<S: BorderStyle> Render<TuiOut> for Border<S> {
}
}
pub trait BorderStyle: Render<TuiOut> + Copy {
pub trait BorderStyle: Draw<TuiOut> + Layout<TuiOut> + Copy {
fn enabled (&self) -> bool;
fn enclose <W: Render<TuiOut>> (self, w: W) -> impl Render<TuiOut> {
fn enclose (self, w: impl Draw<TuiOut> + Layout<TuiOut>) -> impl Draw<TuiOut> + Layout<TuiOut> {
Bsp::b(Fill::xy(Border(self.enabled(), self)), w)
}
fn enclose2 <W: Render<TuiOut>> (self, w: W) -> impl Render<TuiOut> {
fn enclose2 (self, w: impl Draw<TuiOut> + Layout<TuiOut>) -> impl Draw<TuiOut> + Layout<TuiOut> {
Bsp::b(Margin::xy(1, 1, Fill::xy(Border(self.enabled(), self))), w)
}
fn enclose_bg <W: Render<TuiOut>> (self, w: W) -> impl Render<TuiOut> {
fn enclose_bg (self, w: impl Draw<TuiOut> + Layout<TuiOut>) -> impl Draw<TuiOut> + Layout<TuiOut> {
Tui::bg(self.style().unwrap().bg.unwrap_or(Color::Reset),
Bsp::b(Fill::xy(Border(self.enabled(), self)), w))
}
@ -147,9 +143,10 @@ macro_rules! border {
fn enabled (&self) -> bool { self.0 }
}
#[derive(Copy, Clone)] pub struct $T(pub bool, pub Style);
impl Render<TuiOut> for $T {
fn render (&self, to: &mut TuiOut) {
if self.enabled() { let _ = self.draw(to); }
impl Layout<TuiOut> for $T {}
impl Draw<TuiOut> for $T {
fn draw (&self, to: &mut TuiOut) {
if self.enabled() { let _ = BorderStyle::draw(self, to); }
}
}
)+}
@ -260,11 +257,3 @@ border! {
fn style (&self) -> Option<Style> { Some(self.1) }
}
}
//impl<S: BorderStyle, R: Content<TuiOut>> Content<TuiOut> for Bordered<S, R> {
//fn content (&self) -> impl Render<TuiOut> {
//let content: &dyn Content<TuiOut> = &self.1;
//lay! { content.padding_xy(1, 1), Border(self.0) }.fill_xy()
//}
//}

View file

@ -1 +1,38 @@
//use crate::{*, Color::*};
use crate::{*, Color::*};
pub fn button_2 <'a> (
key: impl Draw<TuiOut> + Layout<TuiOut> + 'a,
label: impl Draw<TuiOut> + Layout<TuiOut> + 'a,
editing: bool,
) -> impl Draw<TuiOut> + Layout<TuiOut> + 'a {
let key = Tui::fg_bg(Tui::orange(), Tui::g(0), Bsp::e(
Tui::fg(Tui::g(0), ""),
Bsp::e(key, Tui::fg(Tui::g(96), ""))
));
let label = When::new(!editing, Tui::fg_bg(Tui::g(255), Tui::g(96), label));
Tui::bold(true, Bsp::e(key, label))
}
pub fn button_3 <'a> (
key: impl Draw<TuiOut> + Layout<TuiOut> + 'a,
label: impl Draw<TuiOut> + Layout<TuiOut> + 'a,
value: impl Draw<TuiOut> + Layout<TuiOut> + 'a,
editing: bool,
) -> impl Draw<TuiOut> + Layout<TuiOut> + 'a {
let key = Tui::fg_bg(Tui::orange(), Tui::g(0),
Bsp::e(Tui::fg(Tui::g(0), ""), Bsp::e(key, Tui::fg(if editing {
Tui::g(128)
} else {
Tui::g(96)
}, ""))));
let label = Bsp::e(
When::new(!editing, Bsp::e(
Tui::fg_bg(Tui::g(255), Tui::g(96), label),
Tui::fg_bg(Tui::g(128), Tui::g(96), ""),
)),
Bsp::e(
Tui::fg_bg(Tui::g(224), Tui::g(128), value),
Tui::fg_bg(Tui::g(128), Reset, ""),
));
Tui::bold(true, Bsp::e(key, label))
}

View file

@ -2,26 +2,28 @@ use crate::*;
use ratatui::style::Stylize;
// Thunks can be natural error boundaries!
pub struct ErrorBoundary<O: Output, T: Render<O>>(
pub struct ErrorBoundary<O: Out, T: Draw<O>>(
std::marker::PhantomData<O>, Perhaps<T>
);
impl<O: Output, T: Render<O>> ErrorBoundary<O, T> {
impl<O: Out, T: Draw<O>> ErrorBoundary<O, T> {
pub fn new (content: Perhaps<T>) -> Self {
Self(Default::default(), content)
}
}
impl<T: Render<TuiOut>> Render<TuiOut> for ErrorBoundary<TuiOut, T> {
fn render (&self, to: &mut TuiOut) {
impl<T: Draw<TuiOut>> Draw<TuiOut> for ErrorBoundary<TuiOut, T> {
fn draw (&self, to: &mut TuiOut) {
match self.1.as_ref() {
Ok(Some(content)) => content.render(to),
Ok(Some(content)) => content.draw(to),
Ok(None) => to.blit(&"empty?", 0, 0, Some(Style::default().yellow())),
Err(e) => Tui::fg_bg(
Color::Rgb(255,224,244), Color::Rgb(96,24,24), Bsp::s(
Bsp::e(Tui::bold(true, "oops. "), "rendering failed."),
Bsp::e("\"why?\" ", Tui::bold(true, &format!("{e}"))))
).render(to)
Err(e) => {
let err_fg = Color::Rgb(255,224,244);
let err_bg = Color::Rgb(96,24,24);
let title = Bsp::e(Tui::bold(true, "oops. "), "rendering failed.");
let error = Bsp::e("\"why?\" ", Tui::bold(true, format!("{e}")));
to.place(&Tui::fg_bg(err_fg, err_bg, Bsp::s(title, error)))
}
}
}
}

View file

@ -1,30 +1,34 @@
use crate::*;
pub struct FieldH<T, U>(pub ItemTheme, pub T, pub U);
impl<T: Render<TuiOut>, U: Render<TuiOut>> Content<TuiOut> for FieldH<T, U> {
fn content (&self) -> Option<impl Render<TuiOut> + '_> {
impl<
Label: Draw<TuiOut> + Layout<TuiOut>,
Value: Draw<TuiOut> + Layout<TuiOut>
> Content<TuiOut> for FieldH<ItemTheme, Label, Value> {
fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
let Self(ItemTheme { darkest, dark, lightest, .. }, title, value) = self;
Some(row!(
row!(
Tui::fg_bg(dark.rgb, darkest.rgb, ""),
Tui::fg_bg(lightest.rgb, dark.rgb, title),
Tui::fg_bg(dark.rgb, darkest.rgb, ""),
Tui::fg_bg(lightest.rgb, darkest.rgb, Tui::bold(true, value)),
))
)
}
}
pub struct FieldV<T, U>(pub ItemTheme, pub T, pub U);
impl<T: Render<TuiOut>, U: Render<TuiOut>> Content<TuiOut> for FieldV<T, U> {
fn content (&self) -> Option<impl Render<TuiOut> + '_> {
impl<
Label: Draw<TuiOut> + Layout<TuiOut>,
Value: Draw<TuiOut> + Layout<TuiOut>
> Content<TuiOut> for FieldV<ItemTheme, Label, Value> {
fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
let Self(ItemTheme { darkest, dark, lightest, .. }, title, value) = self;
Some(Bsp::n(
Bsp::n(
Align::w(Tui::bg(darkest.rgb, Tui::fg(lightest.rgb, Tui::bold(true, value)))),
Fill::x(Align::w(row!(
Tui::bg(darkest.rgb, Tui::fg(dark.rgb, "")),
Tui::bg(dark.rgb, Tui::fg(lightest.rgb, title)),
Tui::bg(darkest.rgb, Tui::fg(dark.rgb, "")),
)))
))
)
}
}
@ -40,9 +44,9 @@ pub struct Field<T, U> {
pub value_bg: Option<ItemColor>,
pub value_align: Option<Direction>,
}
impl<T: Render<TuiOut>, U: Render<TuiOut>> Content<TuiOut> for Field<T, U> {
fn content (&self) -> Option<impl Render<TuiOut> + '_> {
Some("TODO")
impl<T: Draw<TuiOut>, U: Draw<TuiOut>> Content<TuiOut> for Field<T, U> {
fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
"TODO"
}
}

View file

@ -1,13 +1,13 @@
use crate::*;
impl Render<TuiOut> for u64 {
fn render (&self, _to: &mut TuiOut) {
impl Draw<TuiOut> for u64 {
fn draw (&self, _to: &mut TuiOut) {
todo!()
}
}
impl Render<TuiOut> for f64 {
fn render (&self, _to: &mut TuiOut) {
impl Draw<TuiOut> for f64 {
fn draw (&self, _to: &mut TuiOut) {
todo!()
}
}

View file

@ -8,28 +8,26 @@ pub struct Phat<T> {
pub content: T,
pub colors: [Color;4],
}
impl<T> Phat<T> {
pub const LO: &'static str = "";
pub const HI: &'static str = "";
/// A phat line
pub fn lo (fg: Color, bg: Color) -> impl Render<TuiOut> {
pub fn lo (fg: Color, bg: Color) -> impl Draw<TuiOut> + Layout<TuiOut> {
Fixed::y(1, Tui::fg_bg(fg, bg, RepeatH(Self::LO)))
}
/// A phat line
pub fn hi (fg: Color, bg: Color) -> impl Render<TuiOut> {
pub fn hi (fg: Color, bg: Color) -> impl Draw<TuiOut> + Layout<TuiOut> {
Fixed::y(1, Tui::fg_bg(fg, bg, RepeatH(Self::HI)))
}
}
impl<T: Render<TuiOut>> Content<TuiOut> for Phat<T> {
fn content (&self) -> Option<impl Render<TuiOut> + '_> {
impl<T: Layout<TuiOut> + Draw<TuiOut>> Content<TuiOut> for Phat<T> {
fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
let [fg, bg, hi, lo] = self.colors;
let top = Fixed::y(1, Self::lo(bg, hi));
let low = Fixed::y(1, Self::hi(bg, lo));
let content = Tui::fg_bg(fg, bg, &self.content);
Some(Min::xy(
self.width,
self.height,
Bsp::s(top, Bsp::n(low, Fill::xy(content)))
))
Min::xy(self.width, self.height, Bsp::s(top, Bsp::n(low, Fill::xy(content))))
}
}

View file

@ -2,11 +2,13 @@ use crate::*;
use ratatui::prelude::Position;
pub struct Repeat<'a>(pub &'a str);
impl Render<TuiOut> for Repeat<'_> {
impl Layout<TuiOut> for Repeat<'_> {
fn layout (&self, to: [u16;4]) -> [u16;4] {
to
}
fn render (&self, to: &mut TuiOut) {
}
impl Draw<TuiOut> for Repeat<'_> {
fn draw (&self, to: &mut TuiOut) {
let [x, y, w, h] = to.area().xywh();
let a = self.0.len();
for (_v, y) in (y..y+h).enumerate() {
@ -21,11 +23,13 @@ impl Render<TuiOut> for Repeat<'_> {
}
pub struct RepeatV<'a>(pub &'a str);
impl Render<TuiOut> for RepeatV<'_> {
impl Layout<TuiOut> for RepeatV<'_> {
fn layout (&self, to: [u16;4]) -> [u16;4] {
to
}
fn render (&self, to: &mut TuiOut) {
}
impl Draw<TuiOut> for RepeatV<'_> {
fn draw (&self, to: &mut TuiOut) {
let [x, y, _w, h] = to.area().xywh();
for y in y..y+h {
if let Some(cell) = to.buffer.cell_mut(Position::from((x, y))) {
@ -36,11 +40,13 @@ impl Render<TuiOut> for RepeatV<'_> {
}
pub struct RepeatH<'a>(pub &'a str);
impl Render<TuiOut> for RepeatH<'_> {
impl Layout<TuiOut> for RepeatH<'_> {
fn layout (&self, to: [u16;4]) -> [u16;4] {
to
}
fn render (&self, to: &mut TuiOut) {
}
impl Draw<TuiOut> for RepeatH<'_> {
fn draw (&self, to: &mut TuiOut) {
let [x, y, w, _h] = to.area().xywh();
for x in x..x+w {
if let Some(cell) = to.buffer.cell_mut(Position::from((x, y))) {

View file

@ -23,8 +23,8 @@ impl ScrollbarH {
const ICON_INC: &[char] = &[' ', '🞂', ' '];
}
impl Render<TuiOut> for ScrollbarV {
fn render (&self, to: &mut TuiOut) {
impl Draw<TuiOut> for ScrollbarV {
fn draw (&self, to: &mut TuiOut) {
let [x, y1, _w, h] = to.area().xywh();
let y2 = y1 + h;
for (i, y) in (y1..=y2).enumerate() {
@ -51,8 +51,8 @@ impl Render<TuiOut> for ScrollbarV {
}
}
impl Render<TuiOut> for ScrollbarH {
fn render (&self, to: &mut TuiOut) {
impl Draw<TuiOut> for ScrollbarH {
fn draw (&self, to: &mut TuiOut) {
let [x1, y, w, _h] = to.area().xywh();
let x2 = x1 + w;
for (i, x) in (x1..=x2).enumerate() {

View file

@ -2,26 +2,17 @@ use crate::*;
use crate::ratatui::prelude::Position;
use unicode_width::{UnicodeWidthStr, UnicodeWidthChar};
impl_content_layout_render!(TuiOut: |self: &str, to|
layout = to.center_xy([width_chars_max(to.w(), self), 1]);
render = {let [x, y, w, ..] = Render::layout(self, to.area());
to.text(self, x, y, w)});
tui_layout!(|self: &str, to|to.center_xy([width_chars_max(to.w(), self), 1]));
tui_draw!(|self: &str, to|{
let [x, y, w, ..] = self.layout(to.area());
to.text(&self, x, y, w)
});
impl_content_layout_render!(TuiOut: |self: String, to|
layout = Render::<TuiOut>::layout(&self.as_str(), to);
render = Render::<TuiOut>::render(&self.as_str(), to));
tui_layout!(|self: Arc<str>, to|self.as_ref().layout(to));
tui_draw!(|self: Arc<str>, to|self.as_ref().draw(to));
impl_content_layout_render!(TuiOut: |self: Arc<str>, to|
layout = Render::<TuiOut>::layout(&self.as_ref(), to);
render = Render::<TuiOut>::render(&self.as_ref(), to));
impl_content_layout_render!(TuiOut: |self: std::sync::RwLock<String>, to|
layout = Render::<TuiOut>::layout(&self.read().unwrap(), to);
render = Render::<TuiOut>::render(&self.read().unwrap(), to));
impl_content_layout_render!(TuiOut: |self: std::sync::RwLockReadGuard<'_, String>, to|
layout = Render::<TuiOut>::layout(&**self, to);
render = Render::<TuiOut>::render(&**self, to));
tui_layout!(|self: String, to|self.as_str().layout(to));
tui_draw!(|self: String, to|self.as_str().draw(to));
fn width_chars_max (max: u16, text: impl AsRef<str>) -> u16 {
let mut width: u16 = 0;
@ -61,12 +52,14 @@ impl<'a, T: AsRef<str>> TrimString<T> {
TrimStringRef(self.0, &self.1)
}
}
impl<'a, T: AsRef<str>> Render<TuiOut> for TrimString<T> {
impl<'a, T: AsRef<str>> Layout<TuiOut> for TrimString<T> {
fn layout (&self, to: [u16; 4]) -> [u16;4] {
Render::layout(&self.as_ref(), to)
Layout::layout(&self.as_ref(), to)
}
fn render (&self, to: &mut TuiOut) {
Render::render(&self.as_ref(), to)
}
impl<'a, T: AsRef<str>> Draw<TuiOut> for TrimString<T> {
fn draw (&self, to: &mut TuiOut) {
Draw::draw(&self.as_ref(), to)
}
}
@ -75,11 +68,13 @@ impl<'a, T: AsRef<str>> Render<TuiOut> for TrimString<T> {
/// Width is computed using [unicode_width].
pub struct TrimStringRef<'a, T: AsRef<str>>(pub u16, pub &'a T);
impl<T: AsRef<str>> Render<TuiOut> for TrimStringRef<'_, T> {
impl<T: AsRef<str>> Layout<TuiOut> for TrimStringRef<'_, T> {
fn layout (&self, to: [u16; 4]) -> [u16;4] {
[to.x(), to.y(), to.w().min(self.0).min(self.1.as_ref().width() as u16), to.h()]
}
fn render (&self, target: &mut TuiOut) {
}
impl<T: AsRef<str>> Draw<TuiOut> for TrimStringRef<'_, T> {
fn draw (&self, target: &mut TuiOut) {
let area = target.area();
let mut width: u16 = 1;
let mut chars = self.1.as_ref().chars();

View file

@ -1,70 +1 @@
use crate::*;
pub trait TuiStyle {
fn fg <R: Render<TuiOut>> (color: Color, w: R) -> Foreground<R> {
Foreground(color, w)
}
fn bg <R: Render<TuiOut>> (color: Color, w: R) -> Background<R> {
Background(color, w)
}
fn fg_bg <R: Render<TuiOut>> (fg: Color, bg: Color, w: R) -> Background<Foreground<R>> {
Background(bg, Foreground(fg, w))
}
fn modify <R: Render<TuiOut>> (enable: bool, modifier: Modifier, w: R) -> Modify<R> {
Modify(enable, modifier, w)
}
fn bold <R: Render<TuiOut>> (enable: bool, w: R) -> Modify<R> {
Self::modify(enable, Modifier::BOLD, w)
}
fn border <R: Render<TuiOut>, S: BorderStyle> (enable: bool, style: S, w: R) -> Bordered<S, R> {
Bordered(enable, style, w)
}
}
impl TuiStyle for Tui {}
pub struct Foreground<R>(pub Color, pub R);
impl<R: Render<TuiOut>> Render<TuiOut> for Foreground<R> {
fn layout (&self, to: [u16;4]) -> [u16;4] {
self.1.layout(to)
}
fn render (&self, to: &mut TuiOut) {
let area = self.layout(to.area());
to.fill_fg(area, self.0);
to.place(area, &self.1);
}
}
pub struct Background<R>(pub Color, pub R);
impl<R: Render<TuiOut>> Render<TuiOut> for Background<R> {
fn layout (&self, to: [u16;4]) -> [u16;4] {
self.1.layout(to)
}
fn render (&self, to: &mut TuiOut) {
let area = self.layout(to.area());
to.fill_bg(area, self.0);
to.place(area, &self.1);
}
}
pub struct Modify<R: Render<TuiOut>>(pub bool, pub Modifier, pub R);
impl<R: Render<TuiOut>> Render<TuiOut> for Modify<R> {
fn layout (&self, to: [u16;4]) -> [u16;4] {
self.2.layout(to)
}
fn render (&self, to: &mut TuiOut) {
to.fill_mod(to.area(), self.0, self.1);
self.2.render(to)
}
}
pub struct Styled<R: Render<TuiOut>>(pub Option<Style>, pub R);
impl<R: Render<TuiOut>> Render<TuiOut> for Styled<R> {
fn layout (&self, to: [u16;4]) -> [u16;4] {
self.1.layout(to)
}
fn render (&self, to: &mut TuiOut) {
to.place(self.1.layout(to.area()), &self.1);
// TODO write style over area
}
}

View file

@ -1,40 +1,13 @@
use crate::*;
/// A three-column layout.
pub struct Tryptich<A, B, C> {
pub top: bool,
pub h: u16,
pub left: (u16, A),
pub middle: (u16, B),
pub right: (u16, C),
}
impl Tryptich<(), (), ()> {
pub fn center (h: u16) -> Self {
Self { h, top: false, left: (0, ()), middle: (0, ()), right: (0, ()) }
}
pub fn top (h: u16) -> Self {
Self { h, top: true, left: (0, ()), middle: (0, ()), right: (0, ()) }
}
}
impl<A, B, C> Tryptich<A, B, C> {
pub fn left <D> (self, w: u16, content: D) -> Tryptich<D, B, C> {
Tryptich { left: (w, content), ..self }
}
pub fn middle <D> (self, w: u16, content: D) -> Tryptich<A, D, C> {
Tryptich { middle: (w, content), ..self }
}
pub fn right <D> (self, w: u16, content: D) -> Tryptich<A, B, D> {
Tryptich { right: (w, content), ..self }
}
}
impl<A, B, C> Content<TuiOut> for Tryptich<A, B, C>
where A: Render<TuiOut>, B: Render<TuiOut>, C: Render<TuiOut> {
fn content (&self) -> Option<impl Render<TuiOut> + '_> {
impl<
A: Draw<TuiOut> + Layout<TuiOut>,
B: Draw<TuiOut> + Layout<TuiOut>,
C: Draw<TuiOut> + Layout<TuiOut>,
> Content<TuiOut> for Tryptich<A, B, C> {
fn content (&self) -> impl Draw<TuiOut> + Layout<TuiOut> + '_ {
let Self { top, h, left: (w_a, ref a), middle: (w_b, ref b), right: (w_c, ref c) } = *self;
Some(Fixed::y(h, if top {
Fixed::y(h, if top {
Bsp::a(
Fill::x(Align::n(Fixed::x(w_b, Align::x(Tui::bg(Color::Reset, b))))),
Bsp::a(
@ -50,7 +23,7 @@ where A: Render<TuiOut>, B: Render<TuiOut>, C: Render<TuiOut> {
Fill::xy(Align::e(Fixed::x(w_c, Tui::bg(Color::Reset, c)))),
),
)
}))
})
}
}

View file

@ -8,7 +8,7 @@ mod tui_output; pub use self::tui_output::*;
mod tui_perf; pub use self::tui_perf::*;
// The `Tui` struct (the *engine*) implements the
// `tengri_input::Input` and `tengri_output::Output` traits.
// `tengri_input::Input` and `tengri_output::Out` traits.
// At launch, the `Tui` engine spawns two threads, the render thread and the input thread.
// the application may further spawn other threads. All threads communicate using shared ownership:
@ -73,12 +73,12 @@ impl Tui {
}
}
pub trait TuiRun<R: Render<TuiOut> + Handle<TuiIn> + 'static> {
pub trait TuiRun<R: Draw<TuiOut> + Handle<TuiIn> + 'static> {
/// Run an app in the main loop.
fn run (&self, state: &Arc<RwLock<R>>) -> Usually<()>;
}
impl<T: Render<TuiOut> + Handle<TuiIn> + Send + Sync + 'static> TuiRun<T> for Arc<RwLock<Tui>> {
impl<T: Draw<TuiOut> + Handle<TuiIn> + Send + Sync + 'static> TuiRun<T> for Arc<RwLock<Tui>> {
fn run (&self, state: &Arc<RwLock<T>>) -> Usually<()> {
let _input_thread = TuiIn::run_input(self, state, Duration::from_millis(100));
self.write().unwrap().setup()?;
@ -90,7 +90,7 @@ impl<T: Render<TuiOut> + Handle<TuiIn> + Send + Sync + 'static> TuiRun<T> for Ar
},
Err(error) => {
self.write().unwrap().teardown()?;
panic!("\n\rRender thread failed: {error:?}.\n\r")
panic!("\n\rDraw thread failed: {error:?}.\n\r")
},
}
Ok(())

View file

@ -2,12 +2,14 @@ use crate::*;
use std::time::Duration;
use std::thread::{spawn, JoinHandle};
use unicode_width::*;
#[derive(Default)]
pub struct TuiOut {
pub buffer: Buffer,
pub area: [u16;4]
}
impl Output for TuiOut {
impl Out for TuiOut {
type Unit = u16;
type Size = [Self::Unit;2];
type Area = [Self::Unit;4];
@ -17,16 +19,20 @@ impl Output for TuiOut {
#[inline] fn area_mut (&mut self) -> &mut [u16;4] {
&mut self.area
}
#[inline] fn place <'t, T: Render<Self> + ?Sized> (&mut self, area: [u16;4], content: &'t T) {
#[inline] fn place_at <'t, T: Draw<Self> + ?Sized> (&mut self, area: [u16;4], content: &'t T) {
let last = self.area();
*self.area_mut() = area;
content.render(self);
content.draw(self);
*self.area_mut() = last;
}
}
impl Layout<TuiOut> for fn(&mut TuiOut) {}
impl TuiOut {
/// Spawn the output thread.
pub fn run_output <T: Render<TuiOut> + Send + Sync + 'static> (
pub fn run_output <T: Draw<TuiOut> + Send + Sync + 'static> (
engine: &Arc<RwLock<Tui>>,
state: &Arc<RwLock<T>>,
timer: Duration
@ -52,7 +58,7 @@ impl TuiOut {
buffer.reset();
}
let mut output = TuiOut { buffer, area: [0, 0, width, height] };
state.render(&mut output);
state.draw(&mut output);
buffer = engine.write().unwrap().flip(output.buffer, size);
}
//let t1 = engine.read().unwrap().perf.get_t1(t0).unwrap();