Compare commits

..

2 commits

Author SHA1 Message Date
41fa55fa6c fix: impl Layout for Measure
Some checks failed
/ build (push) Has been cancelled
2025-09-09 20:59:07 +03:00
90fc869e14 uuugh 2025-09-09 20:39:08 +03:00
13 changed files with 86 additions and 91 deletions

3
.editorconfig Normal file
View file

@ -0,0 +1,3 @@
root = true
[*]
max_line_length = 132

View file

@ -35,3 +35,11 @@ pub trait Eval<Args, RetVal> {
}
};
}
pub fn wrap_inc (index: usize, count: usize) -> usize {
if count > 0 { (index + 1) % count } else { 0 }
}
pub fn wrap_dec (index: usize, count: usize) -> usize {
if count > 0 { index.overflowing_sub(1).0.min(count.saturating_sub(1)) } else { 0 }
}

View file

@ -11,6 +11,7 @@ impl<O: Out> Draw<O> for fn(&mut O) { fn draw (&self, to: &mut O) { (*self)(to)
impl<O: Out> Draw<O> for Box<dyn Draw<O>> { fn draw (&self, to: &mut O) { (**self).draw(to) } }
impl<O: Out, D: Draw<O>> Draw<O> for &D { fn draw (&self, to: &mut O) { (*self).draw(to) } }
impl<O: Out, D: Draw<O>> Draw<O> for &mut D { fn draw (&self, to: &mut O) { (**self).draw(to) } }
impl<O: Out, D: Draw<O>> Draw<O> for Option<D> { fn draw (&self, to: &mut O) { if let Some(draw) = self { draw.draw(to) } } }
/// Drawable area of display.
pub trait Layout<O: Out> {
fn x (&self, to: O::Area) -> O::Unit { to.x() }

View file

@ -2,28 +2,30 @@ use crate::*;
/// A widget that tracks its render width and height
#[derive(Default)]
pub struct Measure<E: Out> {
_engine: PhantomData<E>,
pub struct Measure<O: Out> {
_engine: PhantomData<O>,
pub x: Arc<AtomicUsize>,
pub y: Arc<AtomicUsize>,
}
impl<E: Out> PartialEq for Measure<E> {
impl<O: Out> PartialEq for Measure<O> {
fn eq (&self, other: &Self) -> bool {
self.x.load(Relaxed) == other.x.load(Relaxed) &&
self.y.load(Relaxed) == other.y.load(Relaxed)
}
}
impl<O: Out> Layout<O> for Measure<O> {}
// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small
impl<E: Out> Draw<E> for Measure<E> {
fn draw (&self, to: &mut E) {
impl<O: Out> Draw<O> for Measure<O> {
fn draw (&self, to: &mut O) {
self.x.store(to.area().w().into(), Relaxed);
self.y.store(to.area().h().into(), Relaxed);
}
}
impl<E: Out> Clone for Measure<E> {
impl<O: Out> Clone for Measure<O> {
fn clone (&self) -> Self {
Self {
_engine: Default::default(),
@ -33,7 +35,7 @@ impl<E: Out> Clone for Measure<E> {
}
}
impl<E: Out> std::fmt::Debug for Measure<E> {
impl<O: Out> std::fmt::Debug for Measure<O> {
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
f.debug_struct("Measure")
.field("width", &self.x)
@ -42,7 +44,7 @@ impl<E: Out> std::fmt::Debug for Measure<E> {
}
}
impl<E: Out> Measure<E> {
impl<O: Out> Measure<O> {
pub fn new () -> Self {
Self {
_engine: PhantomData::default(),
@ -75,7 +77,7 @@ impl<E: Out> Measure<E> {
pub fn format (&self) -> Arc<str> {
format!("{}x{}", self.w(), self.h()).into()
}
pub fn of <T: Draw<E>> (&self, item: T) -> Bsp<Fill<&Self>, T> {
pub fn of <T: Draw<O>> (&self, item: T) -> Bsp<Fill<&Self>, T> {
Bsp::b(Fill::XY(self), item)
}
}

View file

@ -106,7 +106,7 @@ pub(crate) fn write_quote_to (out: &mut TokenStream2, quote: TokenStream2) {
//#[tengri_proc::view(SomeOut)]
//impl SomeView {
//#[tengri::view(":view-1")]
//fn view_1 (&self) -> impl Draw<SomeOut> + use<'_> {
//fn view_1 (&self) -> impl Content<SomeOut> + use<'_> {
//"view-1"
//}
//}
@ -114,13 +114,13 @@ pub(crate) fn write_quote_to (out: &mut TokenStream2, quote: TokenStream2) {
//let written = quote! { #parsed };
//assert_eq!(format!("{written}"), format!("{}", quote! {
//impl SomeView {
//fn view_1 (&self) -> impl Draw<SomeOut> + use<'_> {
//fn view_1 (&self) -> impl Content<SomeOut> + use<'_> {
//"view-1"
//}
//}
///// Generated by [tengri_proc].
//impl ::tengri::output::Content<SomeOut> for SomeView {
//fn content (&self) -> impl Draw<SomeOut> {
//fn content (&self) -> impl Content<SomeOut> {
//self.size.of(::tengri::output::View(self, self.config.view))
//}
//}

View file

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

View file

@ -18,31 +18,31 @@ mod tui_scroll; pub use self::tui_scroll::*;
mod tui_string; pub use self::tui_string::*;
mod tui_number; //pub use self::tui_number::*;
mod tui_tryptich; //pub use self::tui_tryptich::*;
impl<T: Content<TuiOut>> Draw<TuiOut> for Foreground<Color, T> {
fn draw (&self, to: &mut TuiOut) {
let area = self.layout(to.area());
to.fill_fg(area, self.0);
to.place_at(area, &self.1);
}
}
impl<T: Content<TuiOut>> Draw<TuiOut> for Background<Color, T> {
fn draw (&self, to: &mut TuiOut) {
let area = self.layout(to.area());
to.fill_bg(area, self.0);
to.place_at(area, &self.1);
}
}
pub struct Modify<T>(pub bool, pub Modifier, pub T);
pub struct Styled<T>(pub Option<Style>, pub T);
impl<T: Draw<TuiOut>> Draw<TuiOut> for Foreground<Color, T> {
fn draw (&self, to: &mut TuiOut) {
let area = self.layout(to.area());
to.fill_fg(area, self.0.0);
to.place_at(area, &self.0.1);
}
}
impl<T: Draw<TuiOut>> Draw<TuiOut> for Background<Color, 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: Draw<TuiOut>> Layout<TuiOut> for Modify<T> {}
impl<T: Draw<TuiOut>> Draw<TuiOut> for Modify<T> {
impl<T: Content<TuiOut>> Layout<TuiOut> for Modify<T> {}
impl<T: Content<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: Draw<TuiOut>> Layout<TuiOut> for Styled<T> {}
impl<T: Draw<TuiOut>> Draw<TuiOut> for Styled<T> {
pub struct Styled<T>(pub Option<Style>, pub T);
impl<T: Content<TuiOut>> Layout<TuiOut> for Styled<T> {}
impl<T: Content<TuiOut>> Draw<TuiOut> for Styled<T> {
fn draw (&self, to: &mut TuiOut) {
to.place(&self.1);
// TODO write style over area

View file

@ -8,8 +8,8 @@ impl<S: BorderStyle, W: Content<TuiOut>> HasContent<TuiOut> for Bordered<S, W> {
impl<S: BorderStyle> Draw<TuiOut> for Border<S> {
fn draw (&self, to: &mut TuiOut) {
let Border(enabled, style) = self.0;
if enabled {
let Border(enabled, style) = self;
if *enabled {
let area = to.area();
if area.w() > 0 && area.y() > 0 {
to.blit(&style.border_nw(), area.x(), area.y(), style.style());
@ -31,13 +31,13 @@ impl<S: BorderStyle> Draw<TuiOut> for Border<S> {
pub trait BorderStyle: Content<TuiOut> + Copy {
fn enabled (&self) -> bool;
fn enclose (self, w: impl Draw<TuiOut>) -> impl Draw<TuiOut> {
fn enclose (self, w: impl Content<TuiOut>) -> impl Content<TuiOut> {
Bsp::b(Fill::XY(Border(self.enabled(), self)), w)
}
fn enclose2 (self, w: impl Draw<TuiOut>) -> impl Draw<TuiOut> {
fn enclose2 (self, w: impl Content<TuiOut>) -> impl Content<TuiOut> {
Bsp::b(Pad::XY(1, 1, Fill::XY(Border(self.enabled(), self))), w)
}
fn enclose_bg (self, w: impl Draw<TuiOut>) -> impl Draw<TuiOut> {
fn enclose_bg (self, w: impl Content<TuiOut>) -> impl Content<TuiOut> {
Tui::bg(self.style().unwrap().bg.unwrap_or(Color::Reset),
Bsp::b(Fill::XY(Border(self.enabled(), self)), w))
}
@ -141,6 +141,7 @@ macro_rules! border {
fn enabled (&self) -> bool { self.0 }
}
#[derive(Copy, Clone)] pub struct $T(pub bool, pub Style);
impl Layout<TuiOut> for $T {}
impl Draw<TuiOut> for $T {
fn draw (&self, to: &mut TuiOut) {
if self.enabled() { let _ = BorderStyle::draw(self, to); }

View file

@ -1,38 +1,18 @@
use crate::{*, Color::*};
pub fn button_2 <'a> (
key: impl Draw<TuiOut> + 'a,
label: impl Draw<TuiOut> + 'a,
editing: bool,
) -> impl Draw<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_2 <'a> (key: impl Content<TuiOut>, label: impl Content<TuiOut>, editing: bool) -> impl Content<TuiOut> {
Tui::bold(true, Bsp::e(
Tui::fg_bg(Tui::orange(), Tui::g(0), Bsp::e(Tui::fg(Tui::g(0), &""), Bsp::e(key, Tui::fg(Tui::g(96), &"")))),
When::new(!editing, Tui::fg_bg(Tui::g(255), Tui::g(96), label))))
}
pub fn button_3 <'a> (
key: impl Draw<TuiOut> + 'a,
label: impl Draw<TuiOut> + 'a,
value: impl Draw<TuiOut> + 'a,
editing: bool,
) -> impl Draw<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), &""),
)),
key: impl Content<TuiOut>, label: impl Content<TuiOut>, value: impl Content<TuiOut>, editing: bool,
) -> impl Content<TuiOut> {
Tui::bold(true, Bsp::e(
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) }, "")))),
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))
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, &""), ))))
}

View file

@ -13,20 +13,16 @@ 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 Content<TuiOut> {
Fixed::Y(1, Tui::fg_bg(fg, bg, RepeatH(Self::LO)))
}
pub fn lo (fg: Color, bg: Color) -> impl Content<TuiOut> { Fixed::Y(1, Tui::fg_bg(fg, bg, RepeatH(Self::LO))) }
/// A phat line
pub fn hi (fg: Color, bg: Color) -> impl Content<TuiOut> {
Fixed::Y(1, Tui::fg_bg(fg, bg, RepeatH(Self::HI)))
}
pub fn hi (fg: Color, bg: Color) -> impl Content<TuiOut> { Fixed::Y(1, Tui::fg_bg(fg, bg, RepeatH(Self::HI))) }
}
impl<T: Content<TuiOut>> HasContent<TuiOut> for Phat<T> {
fn content (&self) -> impl Content<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 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);
Min::XY(self.width, self.height, Bsp::s(top, Bsp::n(low, Fill::XY(content))))
}

View file

@ -18,6 +18,7 @@ impl Draw<TuiOut> for Repeat<'_> {
}
pub struct RepeatV<'a>(pub &'a str);
impl Layout<TuiOut> for RepeatV<'_> {}
impl Draw<TuiOut> for RepeatV<'_> {
fn draw (&self, to: &mut TuiOut) {
let [x, y, _w, h] = to.area().xywh();
@ -30,6 +31,7 @@ impl Draw<TuiOut> for RepeatV<'_> {
}
pub struct RepeatH<'a>(pub &'a str);
impl Layout<TuiOut> for RepeatH<'_> {}
impl Draw<TuiOut> for RepeatH<'_> {
fn draw (&self, to: &mut TuiOut) {
let [x, y, w, _h] = to.area().xywh();

View file

@ -5,6 +5,8 @@ impl Layout<TuiOut> for &str { fn layout (&self, to: [u16;4]) -> [u16;4] { to.
impl Draw<TuiOut> for &str { fn draw (&self, to: &mut TuiOut) { let [x, y, w, ..] = self.layout(to.area()); to.text(&self, x, y, w) } }
impl Layout<TuiOut> for String { fn layout (&self, to: [u16;4]) -> [u16;4] { self.as_str().layout(to) } }
impl Draw<TuiOut> for String { fn draw (&self, to: &mut TuiOut) { self.as_str().draw(to) } }
impl Layout<TuiOut> for Arc<str> { fn layout (&self, to: [u16;4]) -> [u16;4] { self.as_ref().layout(to) } }
impl Draw<TuiOut> for Arc<str> { fn draw (&self, to: &mut TuiOut) { self.as_ref().draw(to) } }
fn width_chars_max (max: u16, text: impl AsRef<str>) -> u16 {
let mut width: u16 = 0;

View file

@ -7,20 +7,20 @@ impl<
> HasContent<TuiOut> for Tryptich<A, B, C> {
fn content (&self) -> impl Content<TuiOut> {
let Self { top, h, left: (w_a, ref a), middle: (w_b, ref b), right: (w_c, ref c) } = *self;
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))))),
Fill::X(Align::n(Fixed::X(w_b, Align::x(Tui::bg(Color::Reset, b))))),
Bsp::a(
Fill::x(Align::nw(Fixed::x(w_a, Tui::bg(Color::Reset, a)))),
Fill::x(Align::ne(Fixed::x(w_c, Tui::bg(Color::Reset, c)))),
Fill::X(Align::nw(Fixed::X(w_a, Tui::bg(Color::Reset, a)))),
Fill::X(Align::ne(Fixed::X(w_c, Tui::bg(Color::Reset, c)))),
),
)
} else {
Bsp::a(
Fill::XY(Align::c(Fixed::x(w_b, Align::x(Tui::bg(Color::Reset, b))))),
Fill::XY(Align::c(Fixed::X(w_b, Align::x(Tui::bg(Color::Reset, b))))),
Bsp::a(
Fill::XY(Align::w(Fixed::x(w_a, Tui::bg(Color::Reset, a)))),
Fill::XY(Align::e(Fixed::x(w_c, Tui::bg(Color::Reset, c)))),
Fill::XY(Align::w(Fixed::X(w_a, Tui::bg(Color::Reset, a)))),
Fill::XY(Align::e(Fixed::X(w_c, Tui::bg(Color::Reset, c)))),
),
)
})