diff --git a/Cargo.lock b/Cargo.lock index 177e248..be9dae7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1164,8 +1164,9 @@ dependencies = [ [[package]] name = "tengri" -version = "0.14.0" +version = "0.15.0" dependencies = [ + "anyhow", "atomic_float", "better-panic", "bumpalo", @@ -1184,7 +1185,7 @@ dependencies = [ [[package]] name = "tengri_proc" -version = "0.14.0" +version = "0.15.0" dependencies = [ "dizzle", "heck", diff --git a/Cargo.toml b/Cargo.toml index e9e0667..13ad326 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,14 +18,10 @@ atomic_float = { version = "1" } better-panic = { version = "0.3.0" } bumpalo = { version = "3.19.0", optional = true } crossterm = { version = "0.29.0" } -heck = { version = "0.5" } palette = { version = "0.7.6", features = [ "random" ] } -proc-macro2 = { version = "1", features = ["span-locations"] } quanta = { version = "0.12.3" } -quote = { version = "1" } rand = { version = "0.8.5" } ratatui = { version = "0.29.0", features = [ "unstable-widget-ref", "underline-color" ] } -syn = { version = "2", features = ["full", "extra-traits"] } unicode-width = { version = "0.2" } dizzle = { path = "../dizzle" } diff --git a/examples/tui_00.rs b/examples/tui_00.rs index a94b936..94bf069 100644 --- a/examples/tui_00.rs +++ b/examples/tui_00.rs @@ -1,5 +1,4 @@ -use ::{std::sync::{Arc, RwLock}, ratatui::style::Color, - tengri::{*, input::*, output::*}, tengri_tui::*}; +use ::{std::{io::stdout, sync::{Arc, RwLock}}, ratatui::style::Color, tengri::*}; tui_main!(State { cursor: 10, ..Default::default() }); namespace!(State: bool {}); namespace!(State: u16 {}); @@ -25,7 +24,7 @@ fn draw_example (state: &State, to: &mut TuiOut) { 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) + state.size.of(Bsp::s(title, Bsp::n(code, state.understand(to, &src).unwrap()))).draw(to) } impl Action { const BINDS: &'static str = stringify! { (@left prev) (@right next) }; diff --git a/examples/tui_01.rs b/examples/tui_01.rs index 9d62bdf..9fa7a93 100644 --- a/examples/tui_01.rs +++ b/examples/tui_01.rs @@ -1,3 +1,4 @@ +use ::{std::sync::{Arc, RwLock}, ratatui::style::Color, tengri::*}; fn main () {} //#[tengri_proc::expose] diff --git a/proc/Cargo.toml b/proc/Cargo.toml index 839b1c6..c509384 100644 --- a/proc/Cargo.toml +++ b/proc/Cargo.toml @@ -1,18 +1,18 @@ [package] name = "tengri_proc" description = "UI metaframework, procedural macros." -version = { workspace = true } -edition = { workspace = true } +version = "0.15.0" +edition = "2024" [lib] proc-macro = true [dependencies] dizzle = { path = "../../dizzle" } -syn = { workspace = true } -quote = { workspace = true } -proc-macro2 = { workspace = true } -heck = { workspace = true } +quote = { version = "1" } +syn = { version = "2", features = ["full", "extra-traits"] } +heck = { version = "0.5" } +proc-macro2 = { version = "1", features = ["span-locations"] } [target.'cfg(target_os = "linux")'] rustflags = ["-C", "link-arg=-fuse-ld=mold"] diff --git a/proc/README.md b/proc/README.md new file mode 100644 index 0000000..48f19c8 --- /dev/null +++ b/proc/README.md @@ -0,0 +1 @@ +currently unused. diff --git a/src/tengri.rs b/src/tengri.rs index 7f88c03..e542ea2 100644 --- a/src/tengri.rs +++ b/src/tengri.rs @@ -4,18 +4,20 @@ #![feature(const_option_ops)] #![feature(const_precise_live_drops)] #![feature(const_trait_impl)] -#![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(step_trait)] #![feature(trait_alias)] #![feature(type_alias_impl_trait)] #![feature(type_changing_struct_update)] - -//pub(crate) use quanta::Clock; - -pub extern crate atomic_float; -pub(crate) use atomic_float::AtomicF64; - +mod tengri_impl; +mod tengri_trait; pub use self::tengri_trait::*; +mod tengri_struct; pub use self::tengri_struct::*; +#[cfg(test)] pub(crate) use proptest_derive::Arbitrary; +pub extern crate dizzle; pub use dizzle::*; +pub extern crate atomic_float; pub(crate) use atomic_float::AtomicF64; +pub extern crate palette; pub(crate) use ::palette::{*, convert::*, okhsl::*}; +pub extern crate better_panic; pub(crate) use better_panic::{Settings, Verbosity}; +pub extern crate unicode_width; pub(crate) use unicode_width::*; pub extern crate ratatui; pub(crate) use ::ratatui::{ prelude::{Color, Style, Buffer, Position}, style::{Stylize, Modifier, Color::*}, @@ -23,39 +25,19 @@ pub extern crate ratatui; pub(crate) use ::ratatui::{ layout::{Size, Rect}, buffer::Cell }; - -pub extern crate crossterm; -pub(crate) use ::crossterm::{ +pub extern crate crossterm; pub(crate) use ::crossterm::{ ExecutableCommand, terminal::{EnterAlternateScreen, LeaveAlternateScreen, enable_raw_mode, disable_raw_mode}, event::{poll, read, Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState}, }; - -pub extern crate palette; -pub(crate) use ::palette::{*, convert::*, okhsl::*}; - -pub extern crate better_panic; -pub(crate) use better_panic::{Settings, Verbosity}; - -pub extern crate unicode_width; -pub(crate) use unicode_width::*; - -//#[cfg(test)] extern crate tengri_proc; -mod tengri_impl; -mod tengri_type; pub use self::tengri_type::*; -mod tengri_trait; pub use self::tengri_trait::*; -mod tengri_struct; pub use self::tengri_struct::*; - -#[macro_export] pub extern crate dizzle; -pub use dizzle::*; - -use std::{time::Duration, thread::{spawn, JoinHandle}, io::Write}; pub(crate) use ::std::{ - io::{stdout, Stdout}, + io::{stdout, Stdout, Write}, sync::{Arc, RwLock, atomic::{AtomicBool, AtomicUsize, Ordering::*}}, fmt::{Debug, Display}, ops::{Add, Sub, Mul, Div}, marker::PhantomData, + time::Duration, + thread::{spawn, JoinHandle} }; // Define macros first, so that private macros are available in private modules: @@ -92,16 +74,24 @@ pub(crate) use ::std::{ } /// Stack things on top of each other, -#[macro_export] macro_rules! lay (($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::b(bsp, $expr);)*; bsp }}); +#[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 }}); +#[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 }}); +#[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 }}); +#[macro_export] macro_rules! row (($($expr:expr),* $(,)?) => {{ + let bsp = (); $(let bsp = Bsp::e(bsp, $expr);)*; bsp +}}); /// Implement [Command] for given `State` and `handler` #[macro_export] macro_rules! command { @@ -154,7 +144,9 @@ pub(crate) use ::std::{ #[macro_export] macro_rules! tui_main { ($expr:expr) => { fn main () -> Usually<()> { - tengri::Tui::new(stdout()).run(true, $expr)?; + let engine = ::tengri::Tui::new(Box::new(stdout()))?; + let state = ::std::sync::Arc::new(std::sync::RwLock::new($expr)); + engine.run(true, &state)?; Ok(()) } }; @@ -328,20 +320,28 @@ pub fn tui_input + Send + Sync + 'static> ( /// Define layout operation. /// /// ``` -/// struct Target; -/// impl tengri::Output for Target { type Unit = u16; } +/// # use tengri::*; +/// struct Target { xywh: XYWH /*platform-specific*/} +/// impl tengri::Out for Target { +/// type Unit = u16; +/// fn area (&self) -> XYWH { self.xywh } +/// fn area_mut (&mut self) -> &mut XYWH { &mut self.xywh } +/// fn place_at <'t, T: Draw + ?Sized> (&mut self, area: XYWH, content: &'t T) {} +/// } /// -/// struct State; +/// struct State {/*app-specific*/} /// impl<'b> Namespace<'b, bool> for State {} /// impl<'b> Namespace<'b, u16> for State {} /// impl Understand for State {} /// -/// let state = State; -/// let target = Target::default(); -/// tengri::evaluate_output_expression(&state, &mut out, "")?; -/// tengri::evaluate_output_expression(&state, &mut out, "(when true (text hello))")?; -/// tengri::evaluate_output_expression(&state, &mut out, "(either true (text hello) (text world)"))?; +/// # fn main () -> tengri::Usually<()> { +/// let state = State {}; +/// let mut target = Target { xywh: Default::default() }; +/// evaluate_output_expression(&state, &mut target, &"")?; +/// evaluate_output_expression(&state, &mut target, &"(when true (text hello))")?; +/// evaluate_output_expression(&state, &mut target, &"(either true (text hello) (text world))")?; /// // TODO test all +/// # Ok(()) } /// ``` #[cfg(feature = "dsl")] pub fn evaluate_output_expression <'a, O: Out + 'a, S> ( state: &S, output: &mut O, expr: &'a impl Expression @@ -473,18 +473,22 @@ pub fn tui_input + Send + Sync + 'static> ( /// Should be impl something or other... /// /// ``` +/// use tengri::{Namespace, Understand, TuiOut, ratatui::prelude::Color}; +/// /// struct State; /// impl<'b> Namespace<'b, bool> for State {} /// impl<'b> Namespace<'b, u16> for State {} /// impl<'b> Namespace<'b, Color> for State {} /// impl Understand for State {} +/// # fn main () -> tengri::Usually<()> { /// let state = State; -/// let out = TuiOut::default(); +/// let mut out = TuiOut::default(); /// tengri::evaluate_output_expression_tui(&state, &mut out, "")?; /// tengri::evaluate_output_expression_tui(&state, &mut out, "text Hello world!")?; /// tengri::evaluate_output_expression_tui(&state, &mut out, "fg (g 0) (text Hello world!)")?; /// tengri::evaluate_output_expression_tui(&state, &mut out, "bg (g 2) (text Hello world!)")?; /// tengri::evaluate_output_expression_tui(&state, &mut out, "(bg (g 3) (fg (g 4) (text Hello world!)))")?; +/// # Ok(()) } /// ``` #[cfg(feature = "dsl")] pub fn evaluate_output_expression_tui <'a, S> ( state: &S, output: &mut TuiOut, expr: impl Expression + 'a @@ -577,8 +581,8 @@ pub fn named_key (token: &str) -> Option { } /// ``` -/// let _ = button_2("", "", true); -/// let _ = button_2("", "", false); +/// let _ = tengri::button_2("", "", true); +/// let _ = tengri::button_2("", "", false); /// ``` pub fn button_2 <'a> (key: impl Content, label: impl Content, editing: bool) -> impl Content { Tui::bold(true, Bsp::e( @@ -587,8 +591,8 @@ pub fn button_2 <'a> (key: impl Content, label: impl Content, ed } /// ``` -/// let _ = button_3("", "", "", true); -/// let _ = button_3("", "", "", false); +/// let _ = tengri::button_3("", "", "", true); +/// let _ = tengri::button_3("", "", "", false); /// ``` pub fn button_3 <'a> ( key: impl Content, label: impl Content, value: impl Content, editing: bool, @@ -776,6 +780,7 @@ pub(crate) fn width_chars_max (max: u16, text: impl AsRef) -> u16 { use proptest::{prelude::*, option::of}; use proptest_derive::Arbitrary; use crate::*; + use Direction::*; proptest! { #[test] fn proptest_direction ( @@ -902,22 +907,18 @@ pub(crate) fn width_chars_max (max: u16, text: impl AsRef) -> u16 { } } - #[test] fn test_tui_engine () -> Usually<()> { - //use std::sync::{Arc, RwLock}; - struct TestComponent(String); - impl Draw for TestComponent { - fn draw (&self, _to: &mut TuiOut) { - } - } - impl Handle for TestComponent { - fn handle (&mut self, _from: &TuiIn) -> Perhaps { - Ok(None) - } - } - let mut output = String::new(); - let engine = Tui::new(&mut output).run(false, TestComponent("hello world".into()))?; - engine.read().unwrap().exited.store(true, std::sync::atomic::Ordering::Relaxed); - //engine.run(&state)?; - Ok(()) - } + //#[test] fn test_tui_engine () -> Usually<()> { + ////use std::sync::{Arc, RwLock}; + //struct TestComponent(String); + //impl Draw for TestComponent { + //fn draw (&self, _to: &mut TuiOut) {} + //} + //impl Handle for TestComponent { + //fn handle (&mut self, _from: &TuiIn) -> Perhaps { Ok(None) } + //} + //let engine = Tui::new(Box::<&mut Vec>::new(vec![0u8;0].as_mut()))?; + //let state = engine.run(false, &Arc::new(RwLock::new(TestComponent("hello world".into()))))?; + //state.read().unwrap().exited.store(true, std::sync::atomic::Ordering::Relaxed); + //Ok(()) + //} } diff --git a/src/tengri_impl.rs b/src/tengri_impl.rs index b67740b..40d444f 100644 --- a/src/tengri_impl.rs +++ b/src/tengri_impl.rs @@ -270,15 +270,11 @@ impl> Draw for Option { //} impl, F: Fn()->T> Lazy { - pub const fn new (thunk: F) -> Self { - Self(thunk, PhantomData) - } + pub const fn new (thunk: F) -> Self { Self(thunk, PhantomData) } } impl Thunk { - pub const fn new (draw: F) -> Self { - Self(PhantomData, draw) - } + pub const fn new (draw: F) -> Self { Self(PhantomData, draw) } } impl Layout for Thunk {} @@ -672,57 +668,15 @@ impl Bsp { impl, Tail: Content> Draw for Bsp { fn draw (&self, to: &mut O) { - match self.0 { - North => { // FIXME - let area_1 = self.1.layout(to.area()); - let area_2 = self.2.layout(XYWH( - to.area().x(), to.area().y().plus(area_1.h()), - to.area().w(), to.area().h().minus(area_1.h()) - )); - to.place_at(area_1, &self.1); - to.place_at(area_2, &self.2); - }, - South => { - let area_1 = self.1.layout(to.area()); - let area_2 = self.2.layout(XYWH( - to.area().x(), to.area().y().plus(area_1.h()), - to.area().w(), to.area().h().minus(area_1.h()) - )); - to.place_at(area_1, &self.1); - to.place_at(area_2, &self.2); - }, - East => { - let area_1 = self.1.layout(to.area()); - let area_2 = self.2.layout(XYWH( - to.area().x().plus(area_1.w()), to.area().y(), - to.area().w().minus(area_1.w()), to.area().h() - )); - to.place_at(area_1, &self.1); - to.place_at(area_2, &self.2); - }, - Above => { - let area_1 = self.1.layout(to.area()); - let area_2 = self.2.layout(to.area()); - to.place_at(area_2, &self.2); - to.place_at(area_1, &self.1); - }, - Below => { - let area_1 = self.1.layout(to.area()); - let area_2 = self.2.layout(to.area()); - 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); + 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); - //} + 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); + } } } @@ -1011,7 +965,7 @@ impl PerfModel { } impl Tui { - pub fn new (output: Stdout) -> Usually { + pub fn new (output: Box) -> Usually { let backend = CrosstermBackend::new(output); let Size { width, height } = backend.size()?; Ok(Self { @@ -1061,6 +1015,8 @@ impl Input for TuiIn { type Event = TuiEvent; type Handled = bool; fn event (&self) -> &TuiEvent { &self.event } +} +impl Done for TuiIn { fn done (&self) { self.exited.store(true, Relaxed); } fn is_done (&self) -> bool { self.exited.fetch_and(true, Relaxed) } } @@ -1076,6 +1032,11 @@ impl TuiEvent { Ok(TuiKey::from_dsl(dsl)?.to_crossterm().map(Self)) } } +impl From for TuiEvent { + fn from (e: Event) -> Self { + Self(e) + } +} impl From for TuiEvent { fn from (c: char) -> Self { Self(Event::Key(KeyEvent::new(KeyCode::Char(c), KeyModifiers::NONE))) diff --git a/src/tengri_struct.rs b/src/tengri_struct.rs index 59ae480..234cded 100644 --- a/src/tengri_struct.rs +++ b/src/tengri_struct.rs @@ -1,394 +1,418 @@ -#[cfg(test)] use proptest_derive::Arbitrary; -use crate::*; +pub use self::logical::*; mod logical { + use crate::*; -/// The `Tui` struct (the *engine*) implements the -/// `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: -/// `Arc>` and `Arc`. Thus, at launch the engine and application instances are expected to be wrapped in `Arc`. -pub struct Tui { - pub exited: Arc, - pub backend: CrosstermBackend, - pub buffer: Buffer, - pub area: [u16;4], - pub perf: PerfModel, + /// Thunks can be natural error boundaries! + pub struct ErrorBoundary>( + pub std::marker::PhantomData, + pub Perhaps + ); + + // TODO DOCUMENTME + pub struct Lazy( + pub F, + pub PhantomData<(O, T)> + ); + + // TODO DOCUMENTME + pub struct Thunk( + pub PhantomData, + pub F + ); + + // TODO DOCUMENTME + #[derive(Debug, Default)] pub struct Memo { + pub value: T, + pub view: Arc> + } } +pub use self::temporal::*; mod temporal { + use crate::*; -#[derive(Debug, Clone)] pub struct TuiIn { - /// Input event - pub event: TuiEvent, - /// Exit flag - pub exited: Arc, + /// Performance counter + #[derive(Debug)] + pub struct PerfModel { + pub enabled: bool, + + pub clock: quanta::Clock, + // In nanoseconds. Time used by last iteration. + pub used: AtomicF64, + // In microseconds. Max prescribed time for iteration (frame, chunk...). + pub window: AtomicF64, + } } -#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)] pub struct TuiEvent( - pub Event -); +pub use self::spatial::*; mod spatial { + use crate::*; -#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)] pub struct TuiKey( - pub Option, - pub KeyModifiers -); + // TODO DOCUMENTME + pub struct Bordered(pub bool, pub S, pub W); -#[derive(Default)] pub struct TuiOut { - pub buffer: Buffer, - pub area: XYWH, + // TODO DOCUMENTME + pub struct Border(pub bool, pub S); + + /// A binary split or layer. + pub struct Bsp( + /// Direction of split + pub(crate) Direction, + /// First element. + pub(crate) Head, + /// Second element. + pub(crate) Tail, + ); + + /// A point (X, Y). + /// + /// ``` + /// let xy = tengri::XY(0u16, 0); + /// ``` + #[cfg_attr(test, derive(Arbitrary))] + #[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct XY( + pub C, pub C + ); + + /// A size (Width, Height). + /// + /// ``` + /// let wh = tengri::WH(0u16, 0); + /// ``` + #[cfg_attr(test, derive(Arbitrary))] + #[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct WH( + pub C, pub C + ); + + /// Point with size. + /// + /// ``` + /// let xywh = tengri::XYWH(0u16, 0, 0, 0); + /// assert_eq!(tengri::XYWH(10u16, 10, 20, 20).center(), tengri::XY(20, 20)); + /// ``` + /// + /// * [ ] TODO: anchor field (determines at which corner/side is X0 Y0) + /// + #[cfg_attr(test, derive(Arbitrary))] + #[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct XYWH( + pub C, pub C, pub C, pub C + ); + + /// A cardinal direction. + /// + /// ``` + /// let direction = tengri::Direction::Above; + /// ``` + #[cfg_attr(test, derive(Arbitrary))] + #[derive(Copy, Clone, PartialEq, Debug)] pub enum Direction { + North, South, East, West, Above, Below + } + + /// 9th of area to place. + /// + /// ``` + /// let alignment = tengri::Alignment::Center; + /// ``` + #[cfg_attr(test, derive(Arbitrary))] + #[derive(Debug, Copy, Clone, Default)] pub enum Alignment { + #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W + } + + /// A widget that tracks its rendered width and height. + /// + /// ``` + /// let measure = tengri::Measure::::default(); + /// ``` + #[derive(Default)] pub struct Measure { + pub __: PhantomData, + pub x: Arc, + pub y: Arc, + } + + /// Show an item only when a condition is true. + /// + /// ``` + /// fn test () -> impl tengri::Draw { + /// tengri::when(true, "Yes") + /// } + /// ``` + pub struct When(pub bool, pub T, pub PhantomData); + pub const fn when(condition: bool, content: T) -> When { + When(condition, content, PhantomData) + } + + /// Show one item if a condition is true and another if the condition is false. + /// + /// ``` + /// fn test () -> impl tengri::Draw { + /// tengri::either(true, "Yes", "No") + /// } + /// ``` + pub struct Either(pub bool, pub A, pub B, pub PhantomData); + pub const fn either(condition: bool, content_a: A, content_b: B) -> Either { + Either(condition, content_a, content_b, PhantomData) + } + + /// Increment X and/or Y coordinate. + /// + /// ``` + /// let pushed = tengri::Push::XY(2, 2, "Hello"); + /// ``` + pub enum Push { X(U, A), Y(U, A), XY(U, U, A), } + + /// Decrement X and/or Y coordinate. + /// + /// ``` + /// let pulled = tengri::Pull::XY(2, 2, "Hello"); + /// ``` + pub enum Pull { X(U, A), Y(U, A), XY(U, U, A), } + + /// Set the content to fill the container. + /// + /// ``` + /// let filled = tengri::Fill::XY("Hello"); + /// ``` + pub enum Fill { X(A), Y(A), XY(A) } + + /// Set fixed size for content. + /// + /// ``` + /// let fixed = tengri::Fixed::XY(3, 5, "Hello"); // 3x5 + /// ``` + pub enum Fixed { X(U, A), Y(U, A), XY(U, U, A), } + + /// Set the maximum width and/or height of the content. + /// + /// ``` + /// let maximum = tengri::Min::XY(3, 5, "Hello"); // 3x1 + /// ``` + pub enum Max { X(U, A), Y(U, A), XY(U, U, A), } + + /// Set the minimum width and/or height of the content. + /// + /// ``` + /// let minimam = tengri::Min::XY(3, 5, "Hello"); // 5x5 + /// ``` + pub enum Min { X(U, A), Y(U, A), XY(U, U, A), } + + /// Decrease the width and/or height of the content. + /// + /// ``` + /// let shrunk = tengri::Shrink::XY(2, 0, "Hello"); // 1x1 + /// ``` + pub enum Shrink { X(U, A), Y(U, A), XY(U, U, A), } + + /// Increaase the width and/or height of the content. + /// + /// ``` + /// let expanded = tengri::Expand::XY(5, 3, "HELLO"); // 15x3 + /// ``` + pub enum Expand { X(U, A), Y(U, A), XY(U, U, A), } + + /// Align position of inner area to middle, side, or corner of outer area. + /// + /// + /// ``` + /// use ::tengri::*; + /// let area = XYWH(10u16, 10, 20, 20); + /// fn test (area: XYWH, item: &impl Draw, expected: [u16;4]) { + /// //assert_eq!(Lay::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(pub Alignment, pub T); + + // TODO DOCUMENTME + pub enum Pad { X(U, A), Y(U, A), XY(U, U, A), } + + /// TODO DOCUMENTME + /// + /// ``` + /// use tengri::{Bounded, XYWH}; + /// let area = XYWH(0, 0, 0, 0); + /// let content = ""; + /// let bounded: Bounded = Bounded(area, content); + /// ``` + pub struct Bounded(pub XYWH, pub D); + + /// Draws items from an iterator. + /// + /// ``` + /// // FIXME let map = tengri::Map(||[].iter(), |_|{}); + /// ``` + pub struct Map + where + I: Iterator + Send + Sync, + F: Fn() -> I + Send + Sync, + { + /// Function that returns iterator over stacked components + pub get_iter: F, + /// Function that returns each stacked component + pub get_item: G, + + pub __: PhantomData<(O, B)>, + } + + /// A color in OKHSL and RGB representations. + #[derive(Debug, Default, Copy, Clone, PartialEq)] pub struct ItemColor { + pub okhsl: Okhsl, + pub rgb: Color, + } + /// A color in OKHSL and RGB with lighter and darker variants. + #[derive(Debug, Default, Copy, Clone, PartialEq)] pub struct ItemTheme { + pub base: ItemColor, + pub light: ItemColor, + pub lighter: ItemColor, + pub lightest: ItemColor, + pub dark: ItemColor, + pub darker: ItemColor, + pub darkest: ItemColor, + } } -/// TUI buffer sized by `usize` instead of `u16`. -#[derive(Default)] pub struct BigBuffer { - pub width: usize, - pub height: usize, - pub content: Vec -} - -/// A color in OKHSL and RGB representations. -#[derive(Debug, Default, Copy, Clone, PartialEq)] pub struct ItemColor { - pub okhsl: Okhsl, - pub rgb: Color, -} -/// A color in OKHSL and RGB with lighter and darker variants. -#[derive(Debug, Default, Copy, Clone, PartialEq)] pub struct ItemTheme { - pub base: ItemColor, - pub light: ItemColor, - pub lighter: ItemColor, - pub lightest: ItemColor, - pub dark: ItemColor, - pub darker: ItemColor, - pub darkest: ItemColor, -} - -pub struct Modify(pub bool, pub Modifier, pub T); - -pub struct Styled(pub Option