From 501782f8fe84a12d6340860f2ff1c5ea5452684d Mon Sep 17 00:00:00 2001 From: same mf who else Date: Wed, 4 Feb 2026 19:46:54 +0200 Subject: [PATCH] wip: refactor(output): simplify layout traits --- bacon.toml | 12 +- output/src/layout.rs | 324 ++++++++++--------- output/src/layout/layout_align.rs | 69 ----- output/src/layout/layout_bsp.rs | 152 --------- output/src/lib.rs | 122 +------- output/src/output.rs | 26 ++ output/src/space.rs | 446 ++++++++++++++++++++++++++- output/src/space/space_area.rs | 97 ------ output/src/space/space_coordinate.rs | 30 -- output/src/space/space_direction.rs | 21 -- output/src/space/space_measure.rs | 83 ----- output/src/space/space_size.rs | 55 ---- output/src/view.rs | 36 ++- tui/Cargo.toml | 2 +- tui/examples/tui_00.rs | 235 +++++++------- tui/src/lib.rs | 24 +- tui/src/tui_content/tui_color.rs | 13 +- tui/src/tui_engine/tui_buffer.rs | 2 +- 18 files changed, 841 insertions(+), 908 deletions(-) delete mode 100644 output/src/layout/layout_align.rs delete mode 100644 output/src/layout/layout_bsp.rs delete mode 100644 output/src/space/space_area.rs delete mode 100644 output/src/space/space_coordinate.rs delete mode 100644 output/src/space/space_direction.rs delete mode 100644 output/src/space/space_measure.rs delete mode 100644 output/src/space/space_size.rs diff --git a/bacon.toml b/bacon.toml index 8a5516f..1fb14f2 100644 --- a/bacon.toml +++ b/bacon.toml @@ -36,9 +36,9 @@ need_stdout = false on_success = "back" # so that we don't open the browser at each change [skin] -status_fg = 251 -status_bg = 200 -key_fg = 11 -status_key_fg = 11 -project_name_badge_fg = 11 -project_name_badge_bg = 69 +status_fg = 15 +status_bg = 16 +key_fg = 11 +status_key_fg = 11 +project_name_badge_fg = 42 +project_name_badge_bg = 16 diff --git a/output/src/layout.rs b/output/src/layout.rs index 3130385..50ddec8 100644 --- a/output/src/layout.rs +++ b/output/src/layout.rs @@ -1,7 +1,3 @@ -use crate::*; - -mod layout_align; pub use self::layout_align::*; -mod layout_bsp; pub use self::layout_bsp::*; mod layout_cond; pub use self::layout_cond::*; mod layout_map; pub use self::layout_map::*; mod layout_pad; pub use self::layout_pad::*; @@ -9,153 +5,195 @@ mod layout_move; pub use self::layout_move::*; mod layout_size; pub use self::layout_size::*; mod layout_stack; //pub use self::layout_stack::*; -/// Drawable area of display. -pub trait Layout { - fn x (&self, to: O::Area) -> O::Unit { - to.x() +/// Stack things on top of each other, +#[macro_export] macro_rules! lay (($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::b(bsp, $expr);)*; bsp }}); +/// Stack southward. +#[macro_export] macro_rules! col (($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::s(bsp, $expr);)*; bsp }}); +/// Stack northward. +#[macro_export] macro_rules! col_up (($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::n(bsp, $expr);)*; bsp }}); +/// Stack eastward. +#[macro_export] macro_rules! row (($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::e(bsp, $expr);)*; bsp }}); + +pub use self::align::*; mod align { + use crate::*; + use Alignment::*; + + /// 9th of area to place. + #[derive(Debug, Copy, Clone, Default)] + pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W } + + /// Align position of inner area to middle, side, or corner of outer area. + /// + /// + /// ``` + /// use ::tengri::{output::*, tui::*}; + /// let area: [u16;4] = [10, 10, 20, 20]; + /// fn test (area: [u16;4], item: &impl Draw, expected: [u16;4]) { + /// assert_eq!(Content::layout(item, area), expected); + /// assert_eq!(Draw::layout(item, area), expected); + /// }; + /// + /// let four = ||Fixed::XY(4, 4, ""); + /// 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::XY(4, 2, ""); + /// 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]); + /// ``` + pub struct Align(Alignment, T); + impl Align { + #[inline] pub const fn c (a: T) -> Self { Self(Alignment::Center, a) } + #[inline] pub const fn x (a: T) -> Self { Self(Alignment::X, a) } + #[inline] pub const fn y (a: T) -> Self { Self(Alignment::Y, a) } + #[inline] pub const fn n (a: T) -> Self { Self(Alignment::N, a) } + #[inline] pub const fn s (a: T) -> Self { Self(Alignment::S, a) } + #[inline] pub const fn e (a: T) -> Self { Self(Alignment::E, a) } + #[inline] pub const fn w (a: T) -> Self { Self(Alignment::W, a) } + #[inline] pub const fn nw (a: T) -> Self { Self(Alignment::NW, a) } + #[inline] pub const fn sw (a: T) -> Self { Self(Alignment::SW, a) } + #[inline] pub const fn ne (a: T) -> Self { Self(Alignment::NE, a) } + #[inline] pub const fn se (a: T) -> Self { Self(Alignment::SE, a) } } - fn y (&self, to: O::Area) -> O::Unit { - to.y() + + impl> Draw for Align { + fn draw (&self, to: &mut O) { Bounded(self.layout(to.area()), &self.1).draw(to) } } - fn min_w (&self, _to: O::Area) -> O::Unit { - 0.into() - } - fn max_w (&self, to: O::Area) -> O::Unit { - to.w() - } - fn w (&self, to: O::Area) -> O::Unit { - to.w().max(self.min_w(to)).min(self.max_w(to)) - } - fn min_h (&self, _to: O::Area) -> O::Unit { - 0.into() - } - fn max_h (&self, to: O::Area) -> O::Unit { - to.h() - } - fn h (&self, to: O::Area) -> O::Unit { - to.h().max(self.min_h(to)).min(self.max_h(to)) - } - fn layout (&self, to: O::Area) -> O::Area { - [self.x(to), self.y(to), self.w(to), self.h(to)].into() + + impl> Layout for Align { + fn x (&self, to: O::Area) -> O::Unit { + match self.0 { + NW | W | SW => to.x(), + N | Center | S => to.x().plus(to.w() / 2.into()).minus(self.1.w(to) / 2.into()), + NE | E | SE => to.x().plus(to.w()).minus(self.1.w(to)), + _ => todo!(), + } + } + fn y (&self, to: O::Area) -> O::Unit { + match self.0 { + NW | N | NE => to.y(), + W | Center | E => to.y().plus(to.h() / 2.into()).minus(self.1.h(to) / 2.into()), + SW | S | SE => to.y().plus(to.h()).minus(self.1.h(to)), + _ => todo!(), + } + } } + } -impl Layout for () { - fn x (&self, a: O::Area) -> O::Unit { a.x() } - fn y (&self, a: O::Area) -> O::Unit { a.y() } - fn w (&self, _: O::Area) -> O::Unit { 0.into() } - fn min_w (&self, _: O::Area) -> O::Unit { 0.into() } - fn max_w (&self, _: O::Area) -> O::Unit { 0.into() } - fn h (&self, _: O::Area) -> O::Unit { 0.into() } - fn min_h (&self, _: O::Area) -> O::Unit { 0.into() } - fn max_h (&self, _: O::Area) -> O::Unit { 0.into() } - fn layout (&self, a: O::Area) -> O::Area { [a.x(), a.y(), 0.into(), 0.into()].into() } -} +pub use self::bsp::*; mod bsp { + use crate::*; -impl> Layout for &L { - fn x (&self, a: O::Area) -> O::Unit { (*self).x(a) } - fn y (&self, a: O::Area) -> O::Unit { (*self).y(a) } - fn w (&self, a: O::Area) -> O::Unit { (*self).w(a) } - fn min_w (&self, a: O::Area) -> O::Unit { (*self).min_w(a) } - fn max_w (&self, a: O::Area) -> O::Unit { (*self).max_w(a) } - fn h (&self, a: O::Area) -> O::Unit { (*self).h(a) } - fn min_h (&self, a: O::Area) -> O::Unit { (*self).min_h(a) } - fn max_h (&self, a: O::Area) -> O::Unit { (*self).max_h(a) } - fn layout (&self, a: O::Area) -> O::Area { (*self).layout(a) } -} + /// A binary split or layer. + pub struct Bsp( + pub(crate) Direction, + /// First element. + pub(crate) Head, + /// Second element. + pub(crate) Tail, + ); -impl> Layout for &mut L { - fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) } - fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) } - fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) } - fn min_w (&self, a: O::Area) -> O::Unit { (**self).min_w(a) } - fn max_w (&self, a: O::Area) -> O::Unit { (**self).max_w(a) } - fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) } - fn min_h (&self, a: O::Area) -> O::Unit { (**self).min_h(a) } - fn max_h (&self, a: O::Area) -> O::Unit { (**self).max_h(a) } - fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) } -} + impl Bsp { + #[inline] pub const fn n (a: Head, b: Tail) -> Self { Self(North, a, b) } + #[inline] pub const fn s (a: Head, b: Tail) -> Self { Self(South, a, b) } + #[inline] pub const fn e (a: Head, b: Tail) -> Self { Self(East, a, b) } + #[inline] pub const fn w (a: Head, b: Tail) -> Self { Self(West, a, b) } + #[inline] pub const fn a (a: Head, b: Tail) -> Self { Self(Above, a, b) } + #[inline] pub const fn b (a: Head, b: Tail) -> Self { Self(Below, a, b) } + } -impl> Layout for Arc { - fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) } - fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) } - fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) } - fn min_w (&self, a: O::Area) -> O::Unit { (**self).min_w(a) } - fn max_w (&self, a: O::Area) -> O::Unit { (**self).max_w(a) } - fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) } - fn min_h (&self, a: O::Area) -> O::Unit { (**self).min_h(a) } - fn max_h (&self, a: O::Area) -> O::Unit { (**self).max_h(a) } - fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) } -} + impl, Tail: Content> Draw for Bsp { + fn draw (&self, to: &mut O) { + match self.0 { + South => { + //panic!("{}", self.1.h(to.area())); + let area_1 = self.1.layout(to.area()); + let area_2 = self.2.layout([ + to.area().x(), + to.area().y().plus(area_1.h()), + to.area().w(), + to.area().h().minus(area_1.h()) + ].into()); + //panic!("{area_1:?} {area_2:?}"); + to.place_at(area_1, &self.1); + to.place_at(area_2, &self.2); + }, + _ => todo!("{:?}", self.0) + } + //let [a, b, _] = bsp_areas(to.area(), self.0, &self.1, &self.2); + //panic!("{a:?} {b:?}"); + //if self.0 == Below { + //to.place_at(a, &self.1); + //to.place_at(b, &self.2); + //} else { + //to.place_at(b, &self.2); + //to.place_at(a, &self.1); + //} + } + } + impl, Tail: Layout> Layout for Bsp { + fn w (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | North | South => self.1.w(area).max(self.2.w(area)), East | West => self.1.min_w(area).plus(self.2.w(area)), } } + fn min_w (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | North | South => self.1.min_w(area).max(self.2.min_w(area)), East | West => self.1.min_w(area).plus(self.2.min_w(area)), } } + fn max_w (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | North | South => self.1.max_w(area).max(self.2.max_w(area)), East | West => self.1.max_w(area).plus(self.2.max_w(area)), } } + fn h (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | East | West => self.1.h(area).max(self.2.h(area)), North | South => self.1.h(area).plus(self.2.h(area)), } } + fn min_h (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | East | West => self.1.min_h(area).max(self.2.min_h(area)), North | South => self.1.min_h(area).plus(self.2.min_h(area)), } } + fn max_h (&self, area: O::Area) -> O::Unit { match self.0 { Above | Below | North | South => self.1.max_h(area).max(self.2.max_h(area)), East | West => self.1.max_h(area).plus(self.2.max_h(area)), } } + fn layout (&self, area: O::Area) -> O::Area { bsp_areas(area, self.0, &self.1, &self.2)[2] } + } -impl Layout for Box> { - fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) } - fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) } - fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) } - fn min_w (&self, a: O::Area) -> O::Unit { (**self).min_w(a) } - fn max_w (&self, a: O::Area) -> O::Unit { (**self).max_w(a) } - fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) } - fn min_h (&self, a: O::Area) -> O::Unit { (**self).min_h(a) } - fn max_h (&self, a: O::Area) -> O::Unit { (**self).max_h(a) } - fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) } -} - -impl> Layout for RwLock { - fn x (&self, a: O::Area) -> O::Unit { self.read().unwrap().x(a) } - fn y (&self, a: O::Area) -> O::Unit { self.read().unwrap().y(a) } - fn w (&self, a: O::Area) -> O::Unit { self.read().unwrap().w(a) } - fn min_w (&self, a: O::Area) -> O::Unit { self.read().unwrap().min_w(a) } - fn max_w (&self, a: O::Area) -> O::Unit { self.read().unwrap().max_w(a) } - fn h (&self, a: O::Area) -> O::Unit { self.read().unwrap().h(a) } - fn min_h (&self, a: O::Area) -> O::Unit { self.read().unwrap().min_h(a) } - fn max_h (&self, a: O::Area) -> O::Unit { self.read().unwrap().max_h(a) } - fn layout (&self, a: O::Area) -> O::Area { self.read().unwrap().layout(a) } -} - -impl> Layout for Option { - fn x (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.x(to)).unwrap_or(to.x()) - } - fn y (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.y(to)).unwrap_or(to.y()) - } - fn min_w (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.min_w(to)).unwrap_or(0.into()) - } - fn max_w (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.max_w(to)).unwrap_or(0.into()) - } - fn w (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.w(to)).unwrap_or(0.into()) - } - fn min_h (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.min_h(to)).unwrap_or(0.into()) - } - fn max_h (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.max_h(to)).unwrap_or(0.into()) - } - fn h (&self, to: O::Area) -> O::Unit { - self.as_ref().map(|c|c.h(to)).unwrap_or(0.into()) - } - fn layout (&self, to: O::Area) -> O::Area { - self.as_ref().map(|c|c.layout([self.x(to), self.y(to), self.w(to), self.h(to)].into())) - .unwrap_or([to.x(), to.y(), 0.into(), 0.into()].into()) - } -} - -pub struct Bounded(pub O::Area, pub D); - -impl> HasContent for Bounded { - fn content (&self) -> impl Content { - &self.1 - } -} - -impl> Draw for Bounded { - fn draw (&self, to: &mut O) { - let area = to.area(); - *to.area_mut() = self.0; - self.1.draw(to); - *to.area_mut() = area; + fn bsp_areas , B: Layout> (area: O::Area, direction: Direction, a: &A, b: &B,) -> [O::Area;3] { + let [x, y, w, h] = area.xywh(); + let [aw, ah] = a.layout(area).wh(); + let [bw, bh] = b.layout(match direction { + Above | Below => area, + South => [x, y + ah, w, h.minus(ah)].into(), + North => [x, y, w, h.minus(ah)].into(), + East => [x + aw, y, w.minus(aw), h].into(), + West => [x, y, w.minus(aw), h].into(), + }).wh(); + match direction { + Above | Below => { + let [x, y, w, h] = area.center_xy([aw.max(bw), ah.max(bh)]); + let a = [(x + w/2.into()).minus(aw/2.into()), (y + h/2.into()).minus(ah/2.into()), aw, ah]; + let b = [(x + w/2.into()).minus(bw/2.into()), (y + h/2.into()).minus(bh/2.into()), bw, bh]; + [a.into(), b.into(), [x, y, w, h].into()] + }, + South => { + let [x, y, w, h] = area.center_xy([aw.max(bw), ah + bh]); + let a = [(x + w/2.into()).minus(aw/2.into()), y, aw, ah]; + let b = [(x + w/2.into()).minus(bw/2.into()), y + ah, bw, bh]; + [a.into(), b.into(), [x, y, w, h].into()] + }, + North => { + let [x, y, w, h] = area.center_xy([aw.max(bw), ah + bh]); + let a = [(x + (w/2.into())).minus(aw/2.into()), y + bh, aw, ah]; + let b = [(x + (w/2.into())).minus(bw/2.into()), y, bw, bh]; + [a.into(), b.into(), [x, y, w, h].into()] + }, + East => { + let [x, y, w, h] = area.center_xy([aw + bw, ah.max(bh)]); + let a = [x, (y + h/2.into()).minus(ah/2.into()), aw, ah]; + let b = [x + aw, (y + h/2.into()).minus(bh/2.into()), bw, bh]; + [a.into(), b.into(), [x, y, w, h].into()] + }, + West => { + let [x, y, w, h] = area.center_xy([aw + bw, ah.max(bh)]); + let a = [x + bw, (y + h/2.into()).minus(ah/2.into()), aw, ah]; + let b = [x, (y + h/2.into()).minus(bh/2.into()), bw, bh]; + [a.into(), b.into(), [x, y, w, h].into()] + }, + } } } diff --git a/output/src/layout/layout_align.rs b/output/src/layout/layout_align.rs deleted file mode 100644 index 0ccdd20..0000000 --- a/output/src/layout/layout_align.rs +++ /dev/null @@ -1,69 +0,0 @@ -//! ``` -//! use ::tengri::{output::*, tui::*}; -//! let area: [u16;4] = [10, 10, 20, 20]; -//! fn test (area: [u16;4], item: &impl Draw, expected: [u16;4]) { -//! assert_eq!(Content::layout(item, area), expected); -//! assert_eq!(Draw::layout(item, area), expected); -//! }; -//! -//! let four = ||Fixed::XY(4, 4, ""); -//! 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::XY(4, 2, ""); -//! 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]); -//! ``` -use crate::*; -use Alignment::*; - -/// 9th of area to place. -#[derive(Debug, Copy, Clone, Default)] -pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W } -pub struct Align(Alignment, T); -impl Align { - #[inline] pub const fn c (a: T) -> Self { Self(Alignment::Center, a) } - #[inline] pub const fn x (a: T) -> Self { Self(Alignment::X, a) } - #[inline] pub const fn y (a: T) -> Self { Self(Alignment::Y, a) } - #[inline] pub const fn n (a: T) -> Self { Self(Alignment::N, a) } - #[inline] pub const fn s (a: T) -> Self { Self(Alignment::S, a) } - #[inline] pub const fn e (a: T) -> Self { Self(Alignment::E, a) } - #[inline] pub const fn w (a: T) -> Self { Self(Alignment::W, a) } - #[inline] pub const fn nw (a: T) -> Self { Self(Alignment::NW, a) } - #[inline] pub const fn sw (a: T) -> Self { Self(Alignment::SW, a) } - #[inline] pub const fn ne (a: T) -> Self { Self(Alignment::NE, a) } - #[inline] pub const fn se (a: T) -> Self { Self(Alignment::SE, a) } -} -impl> Draw for Align { - fn draw (&self, to: &mut O) { Bounded(self.layout(to.area()), &self.1).draw(to) } -} -impl> Layout for Align { - fn x (&self, to: O::Area) -> O::Unit { - match self.0 { - NW | W | SW => to.x(), - N | Center | S => to.x().plus(to.w() / 2.into()).minus(self.1.w(to) / 2.into()), - NE | E | SE => to.x().plus(to.w()).minus(self.1.w(to)), - _ => todo!(), - } - } - fn y (&self, to: O::Area) -> O::Unit { - match self.0 { - NW | N | NE => to.y(), - W | Center | E => to.y().plus(to.h() / 2.into()).minus(self.1.h(to) / 2.into()), - SW | S | SE => to.y().plus(to.h()).minus(self.1.h(to)), - _ => todo!(), - } - } -} diff --git a/output/src/layout/layout_bsp.rs b/output/src/layout/layout_bsp.rs deleted file mode 100644 index bd65e96..0000000 --- a/output/src/layout/layout_bsp.rs +++ /dev/null @@ -1,152 +0,0 @@ -use crate::*; - -/// A binary split or layer. -pub struct Bsp( - pub(crate) Direction, - /// First element. - pub(crate) Head, - /// Second element. - pub(crate) Tail, -); - -impl Bsp { - #[inline] pub const fn n (a: Head, b: Tail) -> Self { Self(North, a, b) } - #[inline] pub const fn s (a: Head, b: Tail) -> Self { Self(South, a, b) } - #[inline] pub const fn e (a: Head, b: Tail) -> Self { Self(East, a, b) } - #[inline] pub const fn w (a: Head, b: Tail) -> Self { Self(West, a, b) } - #[inline] pub const fn a (a: Head, b: Tail) -> Self { Self(Above, a, b) } - #[inline] pub const fn b (a: Head, b: Tail) -> Self { Self(Below, a, b) } -} - -impl, Tail: Content> Draw for Bsp { - fn draw (&self, to: &mut O) { - match self.0 { - South => { - //panic!("{}", self.1.h(to.area())); - let area_1 = self.1.layout(to.area()); - let area_2 = self.2.layout([ - to.area().x(), - to.area().y().plus(area_1.h()), - to.area().w(), - to.area().h().minus(area_1.h()) - ].into()); - //panic!("{area_1:?} {area_2:?}"); - to.place_at(area_1, &self.1); - to.place_at(area_2, &self.2); - }, - _ => todo!("{:?}", self.0) - } - //let [a, b, _] = bsp_areas(to.area(), self.0, &self.1, &self.2); - //panic!("{a:?} {b:?}"); - //if self.0 == Below { - //to.place_at(a, &self.1); - //to.place_at(b, &self.2); - //} else { - //to.place_at(b, &self.2); - //to.place_at(a, &self.1); - //} - } -} -impl, Tail: Layout> Layout for Bsp { - fn w (&self, area: O::Area) -> O::Unit { - match self.0 { - North | South | Above | Below => self.1.w(area).max(self.2.w(area)), - East | West => self.1.min_w(area).plus(self.2.w(area)), - } - } - fn min_w (&self, area: O::Area) -> O::Unit { - match self.0 { - North | South | Above | Below => self.1.min_w(area).max(self.2.min_w(area)), - East | West => self.1.min_w(area).plus(self.2.min_w(area)), - } - } - fn max_w (&self, area: O::Area) -> O::Unit { - match self.0 { - North | South | Above | Below => self.1.max_w(area).max(self.2.max_w(area)), - East | West => self.1.max_w(area).plus(self.2.max_w(area)), - } - } - fn h (&self, area: O::Area) -> O::Unit { - match self.0 { - East | West | Above | Below => self.1.h(area).max(self.2.h(area)), - North | South => self.1.h(area).plus(self.2.h(area)), - } - } - fn min_h (&self, area: O::Area) -> O::Unit { - match self.0 { - East | West | Above | Below => self.1.min_h(area).max(self.2.min_h(area)), - North | South => self.1.min_h(area).plus(self.2.min_h(area)), - } - } - fn max_h (&self, area: O::Area) -> O::Unit { - match self.0 { - North | South | Above | Below => self.1.max_h(area).max(self.2.max_h(area)), - East | West => self.1.max_h(area).plus(self.2.max_h(area)), - } - } - fn layout (&self, area: O::Area) -> O::Area { - bsp_areas(area, self.0, &self.1, &self.2)[2] - } -} - -fn bsp_areas , B: Layout> ( - area: O::Area, direction: Direction, a: &A, b: &B, -) -> [O::Area;3] { - let [x, y, w, h] = area.xywh(); - let [aw, ah] = a.layout(area).wh(); - let [bw, bh] = b.layout(match direction { - Above | Below => area, - South => [x, y + ah, w, h.minus(ah)].into(), - North => [x, y, w, h.minus(ah)].into(), - East => [x + aw, y, w.minus(aw), h].into(), - West => [x, y, w.minus(aw), h].into(), - }).wh(); - match direction { - Above | Below => { - let [x, y, w, h] = area.center_xy([aw.max(bw), ah.max(bh)]); - let a = [(x + w/2.into()).minus(aw/2.into()), (y + h/2.into()).minus(ah/2.into()), aw, ah]; - let b = [(x + w/2.into()).minus(bw/2.into()), (y + h/2.into()).minus(bh/2.into()), bw, bh]; - [a.into(), b.into(), [x, y, w, h].into()] - }, - South => { - let [x, y, w, h] = area.center_xy([aw.max(bw), ah + bh]); - let a = [(x + w/2.into()).minus(aw/2.into()), y, aw, ah]; - let b = [(x + w/2.into()).minus(bw/2.into()), y + ah, bw, bh]; - [a.into(), b.into(), [x, y, w, h].into()] - }, - North => { - let [x, y, w, h] = area.center_xy([aw.max(bw), ah + bh]); - let a = [(x + (w/2.into())).minus(aw/2.into()), y + bh, aw, ah]; - let b = [(x + (w/2.into())).minus(bw/2.into()), y, bw, bh]; - [a.into(), b.into(), [x, y, w, h].into()] - }, - East => { - let [x, y, w, h] = area.center_xy([aw + bw, ah.max(bh)]); - let a = [x, (y + h/2.into()).minus(ah/2.into()), aw, ah]; - let b = [x + aw, (y + h/2.into()).minus(bh/2.into()), bw, bh]; - [a.into(), b.into(), [x, y, w, h].into()] - }, - West => { - let [x, y, w, h] = area.center_xy([aw + bw, ah.max(bh)]); - let a = [x + bw, (y + h/2.into()).minus(ah/2.into()), aw, ah]; - let b = [x, (y + h/2.into()).minus(bh/2.into()), bw, bh]; - [a.into(), b.into(), [x, y, w, h].into()] - }, - } -} - -/// Stack things on top of each other, -#[macro_export] macro_rules! lay (($($expr:expr),* $(,)?) => - {{ let bsp = (); $(let bsp = Bsp::b(bsp, $expr);)*; bsp }}); - -/// Stack southward. -#[macro_export] macro_rules! col (($($expr:expr),* $(,)?) => - {{ let bsp = (); $(let bsp = Bsp::s(bsp, $expr);)*; bsp }}); - -/// Stack northward. -#[macro_export] macro_rules! col_up (($($expr:expr),* $(,)?) => - {{ let bsp = (); $(let bsp = Bsp::n(bsp, $expr);)*; bsp }}); - -/// Stack eastward. -#[macro_export] macro_rules! row (($($expr:expr),* $(,)?) => - {{ let bsp = (); $(let bsp = Bsp::e(bsp, $expr);)*; bsp }}); diff --git a/output/src/lib.rs b/output/src/lib.rs index 17e62f7..7148692 100644 --- a/output/src/lib.rs +++ b/output/src/lib.rs @@ -30,76 +30,6 @@ mod widget; pub use self::widget::*; use crate::{*, Direction::*}; //use proptest_derive::Arbitrary; use proptest::{prelude::*, option::of}; - - proptest! { - #[test] fn proptest_direction ( - d in prop_oneof![ - Just(North), Just(South), - Just(East), Just(West), - Just(Above), Just(Below) - ], - x in u16::MIN..u16::MAX, - y in u16::MIN..u16::MAX, - w in u16::MIN..u16::MAX, - h in u16::MIN..u16::MAX, - a in u16::MIN..u16::MAX, - ) { - let _ = d.split_fixed([x, y, w, h], a); - } - } - - proptest! { - #[test] fn proptest_size ( - x in u16::MIN..u16::MAX, - y in u16::MIN..u16::MAX, - a in u16::MIN..u16::MAX, - b in u16::MIN..u16::MAX, - ) { - let size = [x, y]; - let _ = size.w(); - let _ = size.h(); - let _ = size.wh(); - let _ = size.clip_w(a); - let _ = size.clip_h(b); - let _ = size.expect_min(a, b); - let _ = size.to_area_pos(); - let _ = size.to_area_size(); - } - } - - proptest! { - #[test] fn proptest_area ( - x in u16::MIN..u16::MAX, - y in u16::MIN..u16::MAX, - w in u16::MIN..u16::MAX, - h in u16::MIN..u16::MAX, - a in u16::MIN..u16::MAX, - b in u16::MIN..u16::MAX, - ) { - let _: [u16;4] = <[u16;4] as Area>::zero(); - let _: [u16;4] = <[u16;4] as Area>::from_position([a, b]); - let _: [u16;4] = <[u16;4] as Area>::from_size([a, b]); - let area: [u16;4] = [x, y, w, h]; - let _ = area.expect_min(a, b); - let _ = area.xy(); - let _ = area.wh(); - let _ = area.xywh(); - let _ = area.clip_h(a); - let _ = area.clip_w(b); - let _ = area.clip([a, b]); - let _ = area.set_w(a); - let _ = area.set_h(b); - let _ = area.x2(); - let _ = area.y2(); - let _ = area.lrtb(); - let _ = area.center(); - let _ = area.center_x(a); - let _ = area.center_y(b); - let _ = area.center_xy([a, b]); - let _ = area.centered(); - } - } - macro_rules! test_op_transform { ($fn:ident, $Op:ident) => { proptest! { @@ -115,7 +45,7 @@ mod widget; pub use self::widget::*; if let Some(op) = match (op_x, op_y) { (Some(x), Some(y)) => Some($Op::XY(x, y, content)), (Some(x), None) => Some($Op::X(x, content)), - (None, Some(y)) => Some($Op::y(y, content)), + (None, Some(y)) => Some($Op::Y(y, content)), _ => None } { //assert_eq!(Content::layout(&op, [x, y, w, h]), @@ -125,7 +55,6 @@ mod widget; pub use self::widget::*; } } } - test_op_transform!(proptest_op_fixed, Fixed); test_op_transform!(proptest_op_min, Min); test_op_transform!(proptest_op_max, Max); @@ -157,44 +86,13 @@ mod widget; pub use self::widget::*; } } - #[test] fn test_stub_output () -> Usually<()> { - use crate::*; - struct TestOut([u16;4]); - impl Out for TestOut { - type Unit = u16; - type Size = [u16;2]; - type Area = [u16;4]; - fn area (&self) -> [u16;4] { - self.0 - } - fn area_mut (&mut self) -> &mut [u16;4] { - &mut self.0 - } - fn place_at + ?Sized> (&mut self, area: [u16;4], _: &T) { - println!("place_at: {area:?}"); - () - } - } - impl Draw for String { - fn draw (&self, to: &mut TestOut) { - to.area_mut().set_w(self.len() as u16); - } - } - Ok(()) - } - - #[test] fn test_space () { - use crate::*; - assert_eq!(Area::center(&[10u16, 10, 20, 20]), [20, 20]); - } - - #[test] fn test_iter_map () { - struct Foo; - impl Content for Foo {} - fn _make_map + Send + Sync> (data: &Vec) -> impl Draw { - Map::new(||data.iter(), |_foo, _index|{}) - } - let _data = vec![Foo, Foo, Foo]; - //let map = make_map(&data); - } + //#[test] fn test_iter_map () { + //struct Foo; + //impl Content for Foo {} + //fn _make_map + Send + Sync> (data: &Vec) -> impl Draw { + //Map::new(||data.iter(), |_foo, _index|{}) + //} + //let _data = vec![Foo, Foo, Foo]; + ////let map = make_map(&data); + //} } diff --git a/output/src/output.rs b/output/src/output.rs index 7f0294e..b006d38 100644 --- a/output/src/output.rs +++ b/output/src/output.rs @@ -32,3 +32,29 @@ pub trait Out: Send + Sync + Sized { /// Mutable pointer to area. fn area_mut (&mut self) -> &mut Self::Area; } + +#[cfg(test)] #[test] fn test_stub_output () -> Usually<()> { + use crate::*; + struct TestOut([u16;4]); + impl Out for TestOut { + type Unit = u16; + type Size = [u16;2]; + type Area = [u16;4]; + fn area (&self) -> [u16;4] { + self.0 + } + fn area_mut (&mut self) -> &mut [u16;4] { + &mut self.0 + } + fn place_at + ?Sized> (&mut self, area: [u16;4], _: &T) { + println!("place_at: {area:?}"); + () + } + } + impl Draw for String { + fn draw (&self, to: &mut TestOut) { + to.area_mut().set_w(self.len() as u16); + } + } + Ok(()) +} diff --git a/output/src/space.rs b/output/src/space.rs index 8875438..94f6cf8 100644 --- a/output/src/space.rs +++ b/output/src/space.rs @@ -1,5 +1,441 @@ -mod space_area; pub use self::space_area::*; -mod space_coordinate; pub use self::space_coordinate::*; -mod space_direction; pub use self::space_direction::*; -mod space_measure; pub use self::space_measure::*; -mod space_size; pub use self::space_size::*; +pub(self) use crate::*; +#[cfg(test)] use proptest::{prelude::*, option::of}; + +pub use self::direction::*; mod direction { + use super::*; + /// A cardinal direction. + #[derive(Copy, Clone, PartialEq, Debug)] + #[cfg_attr(test, derive(Arbitrary))] + pub enum Direction { North, South, East, West, Above, Below } + impl Direction { + pub fn split_fixed (self, area: impl Area, a: N) -> ([N;4],[N;4]) { + let [x, y, w, h] = area.xywh(); + match self { + North => ([x, y.plus(h).minus(a), w, a], [x, y, w, h.minus(a)]), + South => ([x, y, w, a], [x, y.plus(a), w, h.minus(a)]), + East => ([x, y, a, h], [x.plus(a), y, w.minus(a), h]), + West => ([x.plus(w).minus(a), y, a, h], [x, y, w.minus(a), h]), + Above | Below => (area.xywh(), area.xywh()) + } + } + } + #[cfg(test)] proptest! { + #[test] fn proptest_direction ( + d in prop_oneof![ + Just(North), Just(South), + Just(East), Just(West), + Just(Above), Just(Below) + ], + x in u16::MIN..u16::MAX, + y in u16::MIN..u16::MAX, + w in u16::MIN..u16::MAX, + h in u16::MIN..u16::MAX, + a in u16::MIN..u16::MAX, + ) { + let _ = d.split_fixed([x, y, w, h], a); + } + } +} + +pub use self::coordinate::*; mod coordinate { + use super::*; + /// A linear coordinate. + pub trait Coordinate: Send + Sync + Copy + + Add + + Sub + + Mul + + Div + + Ord + PartialEq + Eq + + Debug + Display + Default + + From + Into + + Into + + Into + { + fn zero () -> Self { 0.into() } + fn plus (self, other: Self) -> Self; + fn minus (self, other: Self) -> Self { + if self >= other { + self - other + } else { + 0.into() + } + } + } + impl Coordinate for u16 { fn plus (self, other: Self) -> Self { self.saturating_add(other) } } +} + +pub use self::area::*; mod area { + use super::*; + + pub trait Area: From<[N;4]> + Debug + Copy { + fn x (&self) -> N; + fn y (&self) -> N; + fn w (&self) -> N; + fn h (&self) -> N; + fn zero () -> [N;4] { + [N::zero(), N::zero(), N::zero(), N::zero()] + } + fn from_position (pos: impl Size) -> [N;4] { + let [x, y] = pos.wh(); + [x, y, 0.into(), 0.into()] + } + fn from_size (size: impl Size) -> [N;4] { + let [w, h] = size.wh(); + [0.into(), 0.into(), w, h] + } + fn expect_min (&self, w: N, h: N) -> Usually<&Self> { + if self.w() < w || self.h() < h { + Err(format!("min {w}x{h}").into()) + } else { + Ok(self) + } + } + fn xy (&self) -> [N;2] { + [self.x(), self.y()] + } + fn wh (&self) -> [N;2] { + [self.w(), self.h()] + } + fn xywh (&self) -> [N;4] { + [self.x(), self.y(), self.w(), self.h()] + } + fn clip_h (&self, h: N) -> [N;4] { + [self.x(), self.y(), self.w(), self.h().min(h)] + } + fn clip_w (&self, w: N) -> [N;4] { + [self.x(), self.y(), self.w().min(w), self.h()] + } + fn clip (&self, wh: impl Size) -> [N;4] { + [self.x(), self.y(), wh.w(), wh.h()] + } + fn set_w (&self, w: N) -> [N;4] { + [self.x(), self.y(), w, self.h()] + } + fn set_h (&self, h: N) -> [N;4] { + [self.x(), self.y(), self.w(), h] + } + fn x2 (&self) -> N { + self.x().plus(self.w()) + } + fn y2 (&self) -> N { + self.y().plus(self.h()) + } + fn lrtb (&self) -> [N;4] { + [self.x(), self.x2(), self.y(), self.y2()] + } + fn center (&self) -> [N;2] { + [self.x().plus(self.w()/2.into()), self.y().plus(self.h()/2.into())] + } + fn center_x (&self, n: N) -> [N;4] { + let [x, y, w, h] = self.xywh(); + [(x.plus(w / 2.into())).minus(n / 2.into()), y.plus(h / 2.into()), n, 1.into()] + } + fn center_y (&self, n: N) -> [N;4] { + let [x, y, w, h] = self.xywh(); + [x.plus(w / 2.into()), (y.plus(h / 2.into())).minus(n / 2.into()), 1.into(), n] + } + fn center_xy (&self, [n, m]: [N;2]) -> [N;4] { + let [x, y, w, h] = self.xywh(); + [(x.plus(w / 2.into())).minus(n / 2.into()), (y.plus(h / 2.into())).minus(m / 2.into()), n, m] + } + fn centered (&self) -> [N;2] { + [self.x().minus(self.w()/2.into()), self.y().minus(self.h()/2.into())] + } + fn iter_x (&self) -> impl Iterator where N: std::iter::Step { + self.x()..(self.x()+self.w()) + } + fn iter_y (&self) -> impl Iterator where N: std::iter::Step { + self.y()..(self.y()+self.h()) + } + } + + impl Area for (N, N, N, N) { + fn x (&self) -> N { self.0 } + fn y (&self) -> N { self.1 } + fn w (&self) -> N { self.2 } + fn h (&self) -> N { self.3 } + } + + impl Area for [N;4] { + fn x (&self) -> N { self[0] } + fn y (&self) -> N { self[1] } + fn w (&self) -> N { self[2] } + fn h (&self) -> N { self[3] } + } + + #[cfg(test)] #[test] fn test_area () { assert_eq!(Area::center(&[10u16, 10, 20, 20]), [20, 20]); } + #[cfg(test)] proptest! { + #[test] fn proptest_area ( + x in u16::MIN..u16::MAX, + y in u16::MIN..u16::MAX, + w in u16::MIN..u16::MAX, + h in u16::MIN..u16::MAX, + a in u16::MIN..u16::MAX, + b in u16::MIN..u16::MAX, + ) { + let _: [u16;4] = <[u16;4] as Area>::zero(); + let _: [u16;4] = <[u16;4] as Area>::from_position([a, b]); + let _: [u16;4] = <[u16;4] as Area>::from_size([a, b]); + let area: [u16;4] = [x, y, w, h]; + let _ = area.expect_min(a, b); + let _ = area.xy(); + let _ = area.wh(); + let _ = area.xywh(); + let _ = area.clip_h(a); + let _ = area.clip_w(b); + let _ = area.clip([a, b]); + let _ = area.set_w(a); + let _ = area.set_h(b); + let _ = area.x2(); + let _ = area.y2(); + let _ = area.lrtb(); + let _ = area.center(); + let _ = area.center_x(a); + let _ = area.center_y(b); + let _ = area.center_xy([a, b]); + let _ = area.centered(); + } + } +} + +pub use self::size::*; mod size { + use super::*; + /// The size of something. + pub trait Size: From<[N;2]> + Debug/* + Copy*/ { + fn zero () -> [N;2] { [N::zero(), N::zero()] } + fn x (&self) -> N; + fn y (&self) -> N; + fn w (&self) -> N { self.x() } + fn h (&self) -> N { self.y() } + fn wh (&self) -> [N;2] { [self.x(), self.y()] } + fn clip_w (&self, w: N) -> [N;2] { [self.w().min(w), self.h()] } + fn clip_h (&self, h: N) -> [N;2] { [self.w(), self.h().min(h)] } + fn to_area_pos (&self) -> [N;4] { let [x, y] = self.wh(); [x, y, 0.into(), 0.into()] } + fn to_area_size (&self) -> [N;4] { let [w, h] = self.wh(); [0.into(), 0.into(), w, h] } + fn expect_min (&self, w: N, h: N) -> Usually<&Self> { + if self.w() < w || self.h() < h { return Err(format!("min {w}x{h}").into()) } + Ok(self) + } + } + impl Size for (N, N) { fn x (&self) -> N { self.0 } fn y (&self) -> N { self.1 } } + impl Size for [N;2] { fn x (&self) -> N { self[0] } fn y (&self) -> N { self[1] } } + #[cfg(test)] proptest! { + #[test] fn proptest_size ( + x in u16::MIN..u16::MAX, + y in u16::MIN..u16::MAX, + a in u16::MIN..u16::MAX, + b in u16::MIN..u16::MAX, + ) { + let size = [x, y]; + let _ = size.w(); + let _ = size.h(); + let _ = size.wh(); + let _ = size.clip_w(a); + let _ = size.clip_h(b); + let _ = size.expect_min(a, b); + let _ = size.to_area_pos(); + let _ = size.to_area_size(); + } + } + pub trait HasSize { + fn size (&self) -> &Measure; + fn width (&self) -> usize { self.size().w() } + fn height (&self) -> usize { self.size().h() } + } + impl>> HasSize for T { fn size (&self) -> &Measure { self.get() } } + + /// A widget that tracks its rendered width and height + #[derive(Default)] pub struct Measure { __: PhantomData, pub x: Arc, pub y: Arc, } + impl Clone for Measure { fn clone (&self) -> Self { Self { __: Default::default(), x: self.x.clone(), y: self.y.clone(), } } } + impl Layout for Measure {} + impl PartialEq for Measure { + fn eq (&self, other: &Self) -> bool { + self.x.load(Relaxed) == other.x.load(Relaxed) && + self.y.load(Relaxed) == other.y.load(Relaxed) + } + } + // TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small + impl Draw for Measure { + fn draw (&self, to: &mut O) { + self.x.store(to.area().w().into(), Relaxed); + self.y.store(to.area().h().into(), Relaxed); + } + } + impl Debug for Measure { + fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + f.debug_struct("Measure").field("width", &self.x).field("height", &self.y).finish() + } + } + impl Measure { + pub fn set_w (&self, w: impl Into) -> &Self { self.x.store(w.into(), Relaxed); self } + pub fn set_h (&self, h: impl Into) -> &Self { self.y.store(h.into(), Relaxed); self } + pub fn set_wh (&self, w: impl Into, h: impl Into) -> &Self { self.set_w(w); self.set_h(h); self } + pub fn format (&self) -> Arc { format!("{}x{}", self.w(), self.h()).into() } + pub fn of > (&self, item: T) -> Bsp, T> { Bsp::b(Fill::XY(self), item) } + pub fn new (x: O::Unit, y: O::Unit) -> Self { + Self { __: PhantomData::default(), x: Arc::new(x.into()), y: Arc::new(y.into()), } + } + } + impl From<[O::Unit; 2]> for Measure { + fn from ([x, y]: [O::Unit; 2]) -> Self { Self::new(x, y) } + } + //impl Size for Measure { + //fn x (&self) -> O::Unit { self.x.load(Relaxed).into() } + //fn y (&self) -> O::Unit { self.y.load(Relaxed).into() } + //} +} + +pub use self::layout::*; mod layout { + use super::*; + + /// Drawable area of display. + pub trait Layout { + fn x (&self, to: O::Area) -> O::Unit { + to.x() + } + fn y (&self, to: O::Area) -> O::Unit { + to.y() + } + fn min_w (&self, _to: O::Area) -> O::Unit { + 0.into() + } + fn max_w (&self, to: O::Area) -> O::Unit { + to.w() + } + fn w (&self, to: O::Area) -> O::Unit { + to.w().max(self.min_w(to)).min(self.max_w(to)) + } + fn min_h (&self, _to: O::Area) -> O::Unit { + 0.into() + } + fn max_h (&self, to: O::Area) -> O::Unit { + to.h() + } + fn h (&self, to: O::Area) -> O::Unit { + to.h().max(self.min_h(to)).min(self.max_h(to)) + } + fn layout (&self, to: O::Area) -> O::Area { + [self.x(to), self.y(to), self.w(to), self.h(to)].into() + } + } + + impl Layout for () { + fn x (&self, a: O::Area) -> O::Unit { a.x() } + fn y (&self, a: O::Area) -> O::Unit { a.y() } + fn w (&self, _: O::Area) -> O::Unit { 0.into() } + fn min_w (&self, _: O::Area) -> O::Unit { 0.into() } + fn max_w (&self, _: O::Area) -> O::Unit { 0.into() } + fn h (&self, _: O::Area) -> O::Unit { 0.into() } + fn min_h (&self, _: O::Area) -> O::Unit { 0.into() } + fn max_h (&self, _: O::Area) -> O::Unit { 0.into() } + fn layout (&self, a: O::Area) -> O::Area { [a.x(), a.y(), 0.into(), 0.into()].into() } + } + + impl> Layout for &L { + fn x (&self, a: O::Area) -> O::Unit { (*self).x(a) } + fn y (&self, a: O::Area) -> O::Unit { (*self).y(a) } + fn w (&self, a: O::Area) -> O::Unit { (*self).w(a) } + fn min_w (&self, a: O::Area) -> O::Unit { (*self).min_w(a) } + fn max_w (&self, a: O::Area) -> O::Unit { (*self).max_w(a) } + fn h (&self, a: O::Area) -> O::Unit { (*self).h(a) } + fn min_h (&self, a: O::Area) -> O::Unit { (*self).min_h(a) } + fn max_h (&self, a: O::Area) -> O::Unit { (*self).max_h(a) } + fn layout (&self, a: O::Area) -> O::Area { (*self).layout(a) } + } + + impl> Layout for &mut L { + fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) } + fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) } + fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) } + fn min_w (&self, a: O::Area) -> O::Unit { (**self).min_w(a) } + fn max_w (&self, a: O::Area) -> O::Unit { (**self).max_w(a) } + fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) } + fn min_h (&self, a: O::Area) -> O::Unit { (**self).min_h(a) } + fn max_h (&self, a: O::Area) -> O::Unit { (**self).max_h(a) } + fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) } + } + + impl> Layout for Arc { + fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) } + fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) } + fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) } + fn min_w (&self, a: O::Area) -> O::Unit { (**self).min_w(a) } + fn max_w (&self, a: O::Area) -> O::Unit { (**self).max_w(a) } + fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) } + fn min_h (&self, a: O::Area) -> O::Unit { (**self).min_h(a) } + fn max_h (&self, a: O::Area) -> O::Unit { (**self).max_h(a) } + fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) } + } + + impl Layout for Box> { + fn x (&self, a: O::Area) -> O::Unit { (**self).x(a) } + fn y (&self, a: O::Area) -> O::Unit { (**self).y(a) } + fn w (&self, a: O::Area) -> O::Unit { (**self).w(a) } + fn min_w (&self, a: O::Area) -> O::Unit { (**self).min_w(a) } + fn max_w (&self, a: O::Area) -> O::Unit { (**self).max_w(a) } + fn h (&self, a: O::Area) -> O::Unit { (**self).h(a) } + fn min_h (&self, a: O::Area) -> O::Unit { (**self).min_h(a) } + fn max_h (&self, a: O::Area) -> O::Unit { (**self).max_h(a) } + fn layout (&self, a: O::Area) -> O::Area { (**self).layout(a) } + } + + impl> Layout for RwLock { + fn x (&self, a: O::Area) -> O::Unit { self.read().unwrap().x(a) } + fn y (&self, a: O::Area) -> O::Unit { self.read().unwrap().y(a) } + fn w (&self, a: O::Area) -> O::Unit { self.read().unwrap().w(a) } + fn min_w (&self, a: O::Area) -> O::Unit { self.read().unwrap().min_w(a) } + fn max_w (&self, a: O::Area) -> O::Unit { self.read().unwrap().max_w(a) } + fn h (&self, a: O::Area) -> O::Unit { self.read().unwrap().h(a) } + fn min_h (&self, a: O::Area) -> O::Unit { self.read().unwrap().min_h(a) } + fn max_h (&self, a: O::Area) -> O::Unit { self.read().unwrap().max_h(a) } + fn layout (&self, a: O::Area) -> O::Area { self.read().unwrap().layout(a) } + } + + impl> Layout for Option { + fn x (&self, to: O::Area) -> O::Unit { + self.as_ref().map(|c|c.x(to)).unwrap_or(to.x()) + } + fn y (&self, to: O::Area) -> O::Unit { + self.as_ref().map(|c|c.y(to)).unwrap_or(to.y()) + } + fn min_w (&self, to: O::Area) -> O::Unit { + self.as_ref().map(|c|c.min_w(to)).unwrap_or(0.into()) + } + fn max_w (&self, to: O::Area) -> O::Unit { + self.as_ref().map(|c|c.max_w(to)).unwrap_or(0.into()) + } + fn w (&self, to: O::Area) -> O::Unit { + self.as_ref().map(|c|c.w(to)).unwrap_or(0.into()) + } + fn min_h (&self, to: O::Area) -> O::Unit { + self.as_ref().map(|c|c.min_h(to)).unwrap_or(0.into()) + } + fn max_h (&self, to: O::Area) -> O::Unit { + self.as_ref().map(|c|c.max_h(to)).unwrap_or(0.into()) + } + fn h (&self, to: O::Area) -> O::Unit { + self.as_ref().map(|c|c.h(to)).unwrap_or(0.into()) + } + fn layout (&self, to: O::Area) -> O::Area { + self.as_ref().map(|c|c.layout([self.x(to), self.y(to), self.w(to), self.h(to)].into())) + .unwrap_or([to.x(), to.y(), 0.into(), 0.into()].into()) + } + } + + pub struct Bounded(pub O::Area, pub D); + + impl> HasContent for Bounded { + fn content (&self) -> impl Content { + &self.1 + } + } + + impl> Draw for Bounded { + fn draw (&self, to: &mut O) { + let area = to.area(); + *to.area_mut() = self.0; + self.1.draw(to); + *to.area_mut() = area; + } + } +} diff --git a/output/src/space/space_area.rs b/output/src/space/space_area.rs deleted file mode 100644 index e958857..0000000 --- a/output/src/space/space_area.rs +++ /dev/null @@ -1,97 +0,0 @@ -use crate::*; - -pub trait Area: From<[N;4]> + Debug + Copy { - fn x (&self) -> N; - fn y (&self) -> N; - fn w (&self) -> N; - fn h (&self) -> N; - fn zero () -> [N;4] { - [N::zero(), N::zero(), N::zero(), N::zero()] - } - fn from_position (pos: impl Size) -> [N;4] { - let [x, y] = pos.wh(); - [x, y, 0.into(), 0.into()] - } - fn from_size (size: impl Size) -> [N;4] { - let [w, h] = size.wh(); - [0.into(), 0.into(), w, h] - } - fn expect_min (&self, w: N, h: N) -> Usually<&Self> { - if self.w() < w || self.h() < h { - Err(format!("min {w}x{h}").into()) - } else { - Ok(self) - } - } - fn xy (&self) -> [N;2] { - [self.x(), self.y()] - } - fn wh (&self) -> [N;2] { - [self.w(), self.h()] - } - fn xywh (&self) -> [N;4] { - [self.x(), self.y(), self.w(), self.h()] - } - fn clip_h (&self, h: N) -> [N;4] { - [self.x(), self.y(), self.w(), self.h().min(h)] - } - fn clip_w (&self, w: N) -> [N;4] { - [self.x(), self.y(), self.w().min(w), self.h()] - } - fn clip (&self, wh: impl Size) -> [N;4] { - [self.x(), self.y(), wh.w(), wh.h()] - } - fn set_w (&self, w: N) -> [N;4] { - [self.x(), self.y(), w, self.h()] - } - fn set_h (&self, h: N) -> [N;4] { - [self.x(), self.y(), self.w(), h] - } - fn x2 (&self) -> N { - self.x().plus(self.w()) - } - fn y2 (&self) -> N { - self.y().plus(self.h()) - } - fn lrtb (&self) -> [N;4] { - [self.x(), self.x2(), self.y(), self.y2()] - } - fn center (&self) -> [N;2] { - [self.x().plus(self.w()/2.into()), self.y().plus(self.h()/2.into())] - } - fn center_x (&self, n: N) -> [N;4] { - let [x, y, w, h] = self.xywh(); - [(x.plus(w / 2.into())).minus(n / 2.into()), y.plus(h / 2.into()), n, 1.into()] - } - fn center_y (&self, n: N) -> [N;4] { - let [x, y, w, h] = self.xywh(); - [x.plus(w / 2.into()), (y.plus(h / 2.into())).minus(n / 2.into()), 1.into(), n] - } - fn center_xy (&self, [n, m]: [N;2]) -> [N;4] { - let [x, y, w, h] = self.xywh(); - [(x.plus(w / 2.into())).minus(n / 2.into()), (y.plus(h / 2.into())).minus(m / 2.into()), n, m] - } - fn centered (&self) -> [N;2] { - [self.x().minus(self.w()/2.into()), self.y().minus(self.h()/2.into())] - } - fn iter_x (&self) -> impl Iterator where N: std::iter::Step { - self.x()..(self.x()+self.w()) - } - fn iter_y (&self) -> impl Iterator where N: std::iter::Step { - self.y()..(self.y()+self.h()) - } -} - -impl Area for (N, N, N, N) { - fn x (&self) -> N { self.0 } - fn y (&self) -> N { self.1 } - fn w (&self) -> N { self.2 } - fn h (&self) -> N { self.3 } -} - -impl Area for [N;4] { - fn x (&self) -> N { self[0] } - fn y (&self) -> N { self[1] } - fn w (&self) -> N { self[2] } - fn h (&self) -> N { self[3] } -} diff --git a/output/src/space/space_coordinate.rs b/output/src/space/space_coordinate.rs deleted file mode 100644 index d977318..0000000 --- a/output/src/space/space_coordinate.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::*; - -/// A linear coordinate. -pub trait Coordinate: Send + Sync + Copy - + Add - + Sub - + Mul - + Div - + Ord + PartialEq + Eq - + Debug + Display + Default - + From + Into - + Into - + Into -{ - fn zero () -> Self { 0.into() } - fn plus (self, other: Self) -> Self; - fn minus (self, other: Self) -> Self { - if self >= other { - self - other - } else { - 0.into() - } - } -} - -impl Coordinate for u16 { - fn plus (self, other: Self) -> Self { - self.saturating_add(other) - } -} diff --git a/output/src/space/space_direction.rs b/output/src/space/space_direction.rs deleted file mode 100644 index 90dc95e..0000000 --- a/output/src/space/space_direction.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::*; - -/// A cardinal direction. -#[derive(Copy, Clone, PartialEq, Debug)] -#[cfg_attr(test, derive(Arbitrary))] -pub enum Direction { - North, South, East, West, Above, Below -} - -impl Direction { - pub fn split_fixed (self, area: impl Area, a: N) -> ([N;4],[N;4]) { - let [x, y, w, h] = area.xywh(); - match self { - North => ([x, y.plus(h).minus(a), w, a], [x, y, w, h.minus(a)]), - South => ([x, y, w, a], [x, y.plus(a), w, h.minus(a)]), - East => ([x, y, a, h], [x.plus(a), y, w.minus(a), h]), - West => ([x.plus(w).minus(a), y, a, h], [x, y, w.minus(a), h]), - Above | Below => (area.xywh(), area.xywh()) - } - } -} diff --git a/output/src/space/space_measure.rs b/output/src/space/space_measure.rs deleted file mode 100644 index b785bdb..0000000 --- a/output/src/space/space_measure.rs +++ /dev/null @@ -1,83 +0,0 @@ -use crate::*; - -/// A widget that tracks its render width and height -#[derive(Default)] -pub struct Measure { - _engine: PhantomData, - pub x: Arc, - pub y: Arc, -} - -impl PartialEq for Measure { - fn eq (&self, other: &Self) -> bool { - self.x.load(Relaxed) == other.x.load(Relaxed) && - self.y.load(Relaxed) == other.y.load(Relaxed) - } -} - -impl Layout for Measure {} - -// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small -impl Draw for Measure { - fn draw (&self, to: &mut O) { - self.x.store(to.area().w().into(), Relaxed); - self.y.store(to.area().h().into(), Relaxed); - } -} - -impl Clone for Measure { - fn clone (&self) -> Self { - Self { - _engine: Default::default(), - x: self.x.clone(), - y: self.y.clone(), - } - } -} - -impl std::fmt::Debug for Measure { - fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { - f.debug_struct("Measure") - .field("width", &self.x) - .field("height", &self.y) - .finish() - } -} - -impl Measure { - pub fn new () -> Self { - Self { - _engine: PhantomData::default(), - x: Arc::new(0.into()), - y: Arc::new(0.into()), - } - } - pub fn set_w (&self, w: impl Into) -> &Self { - self.x.store(w.into(), Relaxed); - self - } - pub fn set_h (&self, h: impl Into) -> &Self { - self.y.store(h.into(), Relaxed); - self - } - pub fn set_wh (&self, w: impl Into, h: impl Into) -> &Self { - self.set_w(w); - self.set_h(h); - self - } - pub fn w (&self) -> usize { - self.x.load(Relaxed) - } - pub fn h (&self) -> usize { - self.y.load(Relaxed) - } - pub fn wh (&self) -> [usize;2] { - [self.w(), self.h()] - } - pub fn format (&self) -> Arc { - format!("{}x{}", self.w(), self.h()).into() - } - pub fn of > (&self, item: T) -> Bsp, T> { - Bsp::b(Fill::XY(self), item) - } -} diff --git a/output/src/space/space_size.rs b/output/src/space/space_size.rs deleted file mode 100644 index 235f098..0000000 --- a/output/src/space/space_size.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::*; - -pub trait Size: From<[N;2]> + Debug + Copy { - fn x (&self) -> N; - fn y (&self) -> N; - fn w (&self) -> N { self.x() } - fn h (&self) -> N { self.y() } - fn wh (&self) -> [N;2] { [self.x(), self.y()] } - fn clip_w (&self, w: N) -> [N;2] { [self.w().min(w), self.h()] } - fn clip_h (&self, h: N) -> [N;2] { [self.w(), self.h().min(h)] } - fn expect_min (&self, w: N, h: N) -> Usually<&Self> { - if self.w() < w || self.h() < h { - Err(format!("min {w}x{h}").into()) - } else { - Ok(self) - } - } - fn zero () -> [N;2] { - [N::zero(), N::zero()] - } - fn to_area_pos (&self) -> [N;4] { - let [x, y] = self.wh(); - [x, y, 0.into(), 0.into()] - } - fn to_area_size (&self) -> [N;4] { - let [w, h] = self.wh(); - [0.into(), 0.into(), w, h] - } -} - -impl Size for (N, N) { - fn x (&self) -> N { self.0 } - fn y (&self) -> N { self.1 } -} - -impl Size for [N;2] { - fn x (&self) -> N { self[0] } - fn y (&self) -> N { self[1] } -} - -pub trait HasSize { - fn size (&self) -> &Measure; - fn width (&self) -> usize { - self.size().w() - } - fn height (&self) -> usize { - self.size().h() - } -} - -impl>> HasSize for T { - fn size (&self) -> &Measure { - self.get() - } -} diff --git a/output/src/view.rs b/output/src/view.rs index 1857ce2..1630a6f 100644 --- a/output/src/view.rs +++ b/output/src/view.rs @@ -1,6 +1,34 @@ use crate::*; use ::dizzle::{Language, Expression, Symbol, Namespace}; +/// FIXME: This macro should be some variant of `eval`, too. +/// But taking into account the different signatures (resolving them into 1?) +#[macro_export] macro_rules! draw { + ($State:ident: $Output:ident: $layers:expr) => { + impl Draw<$Output> for $State { + fn draw (&self, to: &mut $Output) { + for layer in $layers { layer(self, to) } + } + } + } +} + +/// FIXME: This is generic: should be called `eval` and be part of [dizzle]. +#[macro_export] macro_rules! view { + ($State:ident: $Output:ident: $namespaces:expr) => { + impl View<$Output, ()> for $State { + fn view_expr <'a> (&'a self, to: &mut $Output, expr: &'a impl Expression) -> Usually<()> { + for namespace in $namespaces { if namespace(self, to, expr)? { return Ok(()) } } + Err(format!("{}::<{}, ()>::view_expr: unexpected: {expr:?}", + stringify! { $State }, + stringify! { $Output }).into()) + } + } + } +} + +/// FIXME: This is generic: should be called `Eval` and be part of [dizzle]. +/// Matches [Language] expressions to renderings for a given [Output] target. pub trait View { fn view_expr <'a> (&'a self, _output: &mut O, expr: &'a impl Expression) -> Usually { Err(format!("View::view_expr: no exprs defined: {expr:?}").into()) @@ -34,12 +62,12 @@ pub fn evaluate_output_expression <'a, O: Out + 'a, S> ( let mut frags = head.src()?.unwrap_or_default().split("/"); // The rest of the tokens in the expr are arguments. // Their meanings depend on the dispatched operation - let args = expr.tail(); - let arg0 = args.head(); + let args = expr.tail(); + let arg0 = args.head(); let tail0 = args.tail(); - let arg1 = tail0.head(); + let arg1 = tail0.head(); let tail1 = tail0.tail(); - let arg2 = tail1.head(); + let arg2 = tail1.head(); // And we also have to do the above binding dance // so that the Perhapss remain in scope. match frags.next() { diff --git a/tui/Cargo.toml b/tui/Cargo.toml index 236e778..2c0867a 100644 --- a/tui/Cargo.toml +++ b/tui/Cargo.toml @@ -9,7 +9,7 @@ dsl = [ "tengri_output/dsl" ] bumpalo = [ "dep:bumpalo" ] [dependencies] -dizzle = { workspace = true } +dizzle = { workspace = true } tengri_input = { workspace = true } tengri_output = { workspace = true } diff --git a/tui/examples/tui_00.rs b/tui/examples/tui_00.rs index f13049e..a94b936 100644 --- a/tui/examples/tui_00.rs +++ b/tui/examples/tui_00.rs @@ -1,123 +1,124 @@ -use tengri::{self, Usually, Perhaps, input::*, output::*, tui::*, dsl::*}; -use std::sync::{Arc, RwLock}; -use crate::ratatui::style::Color; -//use crossterm::event::{*, KeyCode::*}; - -fn main () -> Usually<()> { - let state = Example::new(); - Tui::new().unwrap().run(&state)?; - Ok(()) +use ::{std::sync::{Arc, RwLock}, ratatui::style::Color, + tengri::{*, input::*, output::*}, tengri_tui::*}; + tui_main!(State { cursor: 10, ..Default::default() }); +namespace!(State: bool {}); +namespace!(State: u16 {}); +namespace!(State: Color {}); + handle!(TuiIn: |self: State, input|Action::from(input).eval(self).map(|_|None)); + from!(Action: |input: &TuiIn| todo!()); + view!(State: TuiOut: [ evaluate_output_expression, evaluate_output_expression_tui ]); + draw!(State: TuiOut: [ draw_example ]); +#[derive(Debug, Default)] struct State { + /** Rendered window size */ size: Measure, + /** Command history (undo/redo) */ history: Vec, + /** User-controllable value */ cursor: usize, } - -#[derive(Debug)] struct Example(usize, Measure); - -handle!(TuiIn: |self: Example, input|Ok(None)); -enum ExampleCommand { Next, Prev } -impl ExampleCommand { - fn eval (&self, state: &mut Example) -> Perhaps { - match self { - Self::Next => { - state.0 = (state.0 + 1) % Example::VIEWS.len(); - Ok(Some(Self::Prev)) - }, - Self::Prev => { - state.0 = if state.0 > 0 { state.0 - 1 } else { Example::VIEWS.len() - 1 }; - Ok(Some(Self::Next)) - } - } +#[derive(Debug)] enum Action { + /** Increment cursor */ Next, + /** Decrement cursor */ Prev +} +fn draw_example (state: &State, to: &mut TuiOut) { + let index = state.cursor + 1; + let wh = state.size.wh(); + let src = VIEWS.get(state.cursor).unwrap_or(&""); + let heading = format!("State {}/{} in {:?}", index, VIEWS.len(), &wh); + let title = Tui::bg(Color::Rgb(60, 10, 10), Push::Y(1, Align::n(heading))); + let code = Tui::bg(Color::Rgb(10, 60, 10), Push::Y(2, Align::n(format!("{}", src)))); + //let content = ;//();//Tui::bg(Color::Rgb(10, 10, 60), View(state, CstIter::new(src))); + state.size.of(Bsp::s(title, Bsp::n(code, state.view(to, &src).unwrap()))).draw(to) +} +impl Action { + const BINDS: &'static str = stringify! { (@left prev) (@right next) }; + fn eval (&self, state: &mut State) -> Perhaps { + use Action::*; + match self { Next => Self::next(state), Prev => Self::prev(state), } + } + fn next (state: &mut State) -> Perhaps { + state.cursor = (state.cursor + 1) % VIEWS.len(); + Ok(Some(Self::Prev)) + } + fn prev (state: &mut State) -> Perhaps { + state.cursor = if state.cursor > 0 { state.cursor - 1 } else { VIEWS.len() - 1 }; + Ok(Some(Self::Next)) } } +const VIEWS: &'static [&'static str] = &[ -impl Draw for Example { - fn content (&self) -> impl Draw { - let index = self.0 + 1; - let wh = self.1.wh(); - let src = Self::VIEWS.get(self.0).unwrap_or(&""); - let heading = format!("Example {}/{} in {:?}", index, Self::VIEWS.len(), &wh); - let title = Tui::bg(Color::Rgb(60, 10, 10), Push::y(1, Align::n(heading))); - let code = Tui::bg(Color::Rgb(10, 60, 10), Push::y(2, Align::n(format!("{}", src)))); - let content = ();//Tui::bg(Color::Rgb(10, 10, 60), View(self, CstIter::new(src))); - self.1.of(Bsp::s(title, Bsp::n(""/*code*/, content))) - } -} -impl View for Example { - fn view_expr <'a> (&'a self, to: &mut TuiOut, expr: &'a impl DslExpr) -> Usually<()> { - if evaluate_output_expression(self, to, expr)? - || evaluate_output_expression_tui(self, to, expr)? { - Ok(()) - } else { - Err(format!("Example::view_expr: unexpected: {expr:?}").into()) - } - } -} + stringify! { :hello-world }, -impl Example { - fn new () -> Arc> { - Arc::new(RwLock::new(Example(10, Measure::new()))) - } - const BINDS: &'static str = stringify! { - (@left prev) - (@right next) - }; - const VIEWS: &'static [&'static str] = &[ - stringify! { :hello-world }, - stringify! { (fill/xy :hello-world) }, - stringify! { (bsp/s :hello :world) }, - stringify! { (fixed/xy 20 10 :hello-world) }, - stringify! { (bsp/s (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, - stringify! { (bsp/e (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, - stringify! { (bsp/n (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, - stringify! { (bsp/w (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, - stringify! { (bsp/a (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, - stringify! { (bsp/b (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, - stringify! { + stringify! { (fill/xy :hello-world) }, + + stringify! { (bsp/s :hello :world) }, + + stringify! { (fixed/xy 20 10 :hello-world) }, + + stringify! { (bsp/s (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, + + stringify! { (bsp/e (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, + + stringify! { (bsp/n (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, + + stringify! { (bsp/w (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, + + stringify! { (bsp/a (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, + + stringify! { (bsp/b (fixed/xy 5 6 :hello) (fixed/xy 7 8 :world)) }, + + stringify! { + (bsp/s + (bsp/e (align/nw (fixed/xy 5 3 :hello)) + (bsp/e (align/n (fixed/xy 5 3 :hello)) + (align/ne (fixed/xy 5 3 :hello)))) (bsp/s - (bsp/e (align/nw (fixed/xy 5 3 :hello)) - (bsp/e (align/n (fixed/xy 5 3 :hello)) - (align/ne (fixed/xy 5 3 :hello)))) - (bsp/s - (bsp/e (align/w (fixed/xy 5 3 :hello)) - (bsp/e (align/c (fixed/xy 5 3 :hello)) - (align/e (fixed/xy 5 3 :hello)))) - (bsp/e (align/sw (fixed/xy 5 3 :hello)) - (bsp/e (align/s (fixed/xy 5 3 :hello)) - (align/se (fixed/xy 5 3 :hello)))))) - }, - stringify! { - (bsp/s - (bsp/e (fixed/xy 8 5 (align/nw :hello)) - (bsp/e (fixed/xy 8 5 (align/n :hello)) - (fixed/xy 8 5 (align/ne :hello)))) - (bsp/s - (bsp/e (fixed/xy 8 5 (align/w :hello)) - (bsp/e (fixed/xy 8 5 (align/c :hello)) - (fixed/xy 8 5 (align/e :hello)))) - (bsp/e (fixed/xy 8 5 (align/sw :hello)) - (bsp/e (fixed/xy 8 5 (align/s :hello)) - (fixed/xy 8 5 (align/se :hello)))))) - }, - stringify! { - (bsp/s - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/nw :hello))) - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/n :hello))) - (grow/xy 1 1 (fixed/xy 8 5 (align/ne :hello))))) - (bsp/s - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/w :hello))) - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/c :hello))) - (grow/xy 1 1 (fixed/xy 8 5 (align/e :hello))))) - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/sw :hello))) - (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/s :hello))) - (grow/xy 1 1 (fixed/xy 8 5 (align/se :hello))))))) - }, - stringify! { :map-e }, - stringify! { (align/c :map-e) }, - stringify! { :map-s }, - stringify! { (align/c :map-s) }, - stringify! { - (align/c (bg/behind :bg0 (margin/xy 1 1 (col - (bg/behind :bg1 (border/around :border1 (margin/xy 2 1 :label1))) - (bg/behind :bg2 (border/around :border2 (margin/xy 4 2 :label2))) - (bg/behind :bg3 (border/around :border3 (margin/xy 6 3 :label3))))))) - }, - ]; -} + (bsp/e (align/w (fixed/xy 5 3 :hello)) + (bsp/e (align/c (fixed/xy 5 3 :hello)) + (align/e (fixed/xy 5 3 :hello)))) + (bsp/e (align/sw (fixed/xy 5 3 :hello)) + (bsp/e (align/s (fixed/xy 5 3 :hello)) + (align/se (fixed/xy 5 3 :hello)))))) + }, + + stringify! { + (bsp/s + (bsp/e (fixed/xy 8 5 (align/nw :hello)) + (bsp/e (fixed/xy 8 5 (align/n :hello)) + (fixed/xy 8 5 (align/ne :hello)))) + (bsp/s + (bsp/e (fixed/xy 8 5 (align/w :hello)) + (bsp/e (fixed/xy 8 5 (align/c :hello)) + (fixed/xy 8 5 (align/e :hello)))) + (bsp/e (fixed/xy 8 5 (align/sw :hello)) + (bsp/e (fixed/xy 8 5 (align/s :hello)) + (fixed/xy 8 5 (align/se :hello)))))) + }, + + stringify! { + (bsp/s + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/nw :hello))) + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/n :hello))) + (grow/xy 1 1 (fixed/xy 8 5 (align/ne :hello))))) + (bsp/s + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/w :hello))) + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/c :hello))) + (grow/xy 1 1 (fixed/xy 8 5 (align/e :hello))))) + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/sw :hello))) + (bsp/e (grow/xy 1 1 (fixed/xy 8 5 (align/s :hello))) + (grow/xy 1 1 (fixed/xy 8 5 (align/se :hello))))))) + }, + + stringify! { :map-e }, + + stringify! { (align/c :map-e) }, + + stringify! { :map-s }, + + stringify! { (align/c :map-s) }, + + stringify! { + (align/c (bg/behind :bg0 (margin/xy 1 1 (col + (bg/behind :bg1 (border/around :border1 (margin/xy 2 1 :label1))) + (bg/behind :bg2 (border/around :border2 (margin/xy 4 2 :label2))) + (bg/behind :bg3 (border/around :border3 (margin/xy 6 3 :label3))))))) + }, + +]; diff --git a/tui/src/lib.rs b/tui/src/lib.rs index 39d3c59..d9e16c6 100644 --- a/tui/src/lib.rs +++ b/tui/src/lib.rs @@ -1,5 +1,13 @@ #![feature(type_changing_struct_update, trait_alias)] -pub use ::{tengri_input, tengri_output, ratatui, crossterm, palette, better_panic}; +pub use ::{ + dizzle, + tengri_input, + tengri_output, + ratatui, + crossterm, + palette, + better_panic, +}; pub(crate) use ::{ dizzle::*, tengri_input::*, @@ -23,13 +31,23 @@ pub(crate) use ::{ }; mod tui_engine; pub use self::tui_engine::*; mod tui_content; pub use self::tui_content::*; +#[macro_export] macro_rules! tui_main { + ($expr:expr) => { + fn main () -> Usually<()> { + let state = Arc::new(RwLock::new($expr)); + tengri_tui::Tui::new().unwrap().run(&state)?; + Ok(()) + } + }; +} + #[cfg(test)] mod tui_test { use crate::*; #[test] fn test_tui_engine () -> Usually<()> { //use std::sync::{Arc, RwLock}; struct TestComponent(String); - impl Content for TestComponent { - fn content (&self) -> impl Draw { + impl HasContent for TestComponent { + fn content (&self) -> impl Content { Some(self.0.as_str()) } } diff --git a/tui/src/tui_content/tui_color.rs b/tui/src/tui_content/tui_color.rs index 375694b..6fb0c8f 100644 --- a/tui/src/tui_content/tui_color.rs +++ b/tui/src/tui_content/tui_color.rs @@ -40,16 +40,12 @@ pub trait HasColor { fn color (&self) -> ItemColor; } pub okhsl: Okhsl, pub rgb: Color, } - -from!(|okhsl: Okhsl|ItemColor = Self { okhsl, rgb: okhsl_to_rgb(okhsl) }); - +from!(ItemColor: |okhsl: Okhsl|Self { okhsl, rgb: okhsl_to_rgb(okhsl) }); +from!(ItemColor: |rgb: Color|Self { rgb, okhsl: rgb_to_okhsl(rgb) }); pub fn okhsl_to_rgb (color: Okhsl) -> Color { let Srgb { red, green, blue, .. }: Srgb = Srgb::from_color_unclamped(color); Color::Rgb((red * 255.0) as u8, (green * 255.0) as u8, (blue * 255.0) as u8,) } - -from!(|rgb: Color|ItemColor = Self { rgb, okhsl: rgb_to_okhsl(rgb) }); - pub fn rgb_to_okhsl (color: Color) -> Okhsl { if let Color::Rgb(r, g, b) = color { Okhsl::from_color(Srgb::new(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0)) @@ -98,6 +94,8 @@ impl ItemColor { pub darkest: ItemColor, } +from!(ItemTheme: |base: Color| Self::from_tui_color(base)); +from!(ItemTheme: |base: ItemColor|Self::from_item_color(base)); impl ItemTheme { pub const G: [Self;256] = { let mut builder = konst::array::ArrayBuilder::new(); @@ -170,6 +168,3 @@ impl ItemTheme { } } } - -from!(|base: Color| ItemTheme = Self::from_tui_color(base)); -from!(|base: ItemColor|ItemTheme = Self::from_item_color(base)); diff --git a/tui/src/tui_engine/tui_buffer.rs b/tui/src/tui_engine/tui_buffer.rs index 6e3acfd..6f21662 100644 --- a/tui/src/tui_engine/tui_buffer.rs +++ b/tui/src/tui_engine/tui_buffer.rs @@ -41,4 +41,4 @@ impl BigBuffer { } } -from!(|size:(usize, usize)| BigBuffer = Self::new(size.0, size.1)); +from!(BigBuffer: |size:(usize, usize)| Self::new(size.0, size.1));