diff --git a/output/src/out_structs.rs b/output/src/out_structs.rs index c49375f..a68771a 100644 --- a/output/src/out_structs.rs +++ b/output/src/out_structs.rs @@ -1,43 +1,36 @@ -#[cfg(test)] use proptest_derive::Arbitrary; use crate::*; /// A point (X, Y). /// /// ``` -/// let xy = tengri::output::XY(0u16, 0); +/// let xy: XY = XY(0, 0); /// ``` #[cfg_attr(test, derive(Arbitrary))] -#[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct XY( - pub C, pub C -); +#[derive(Copy, Clone, Debug, Default)] pub struct XY(pub C, pub C); /// A size (Width, Height). /// /// ``` -/// let wh = tengri::output::WH(0u16, 0); +/// let wh: WH = WH(0, 0); /// ``` #[cfg_attr(test, derive(Arbitrary))] -#[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct WH( - pub C, pub C -); +#[derive(Copy, Clone, Debug, Default)] pub struct WH(pub C, pub C); /// Point with size. /// /// ``` -/// let xywh = tengri::output::XYWH(0u16, 0, 0, 0); +/// let xywh: XYWH = XYWH(0, 0, 0, 0); /// ``` /// /// * [ ] 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 -); +#[derive(Copy, Clone, Debug, Default)] pub struct XYWH(pub C, pub C, pub C, pub C); /// A cardinal direction. /// /// ``` -/// let direction = tengri::output::Direction::Above; +/// let direction = Direction::Above; /// ``` #[cfg_attr(test, derive(Arbitrary))] #[derive(Copy, Clone, PartialEq, Debug)] pub enum Direction { @@ -47,7 +40,7 @@ use crate::*; /// 9th of area to place. /// /// ``` -/// let alignment = tengri::output::Alignment::Center; +/// let alignment = Align::Center; /// ``` #[cfg_attr(test, derive(Arbitrary))] #[derive(Debug, Copy, Clone, Default)] pub enum Alignment { @@ -57,7 +50,7 @@ use crate::*; /// A widget that tracks its rendered width and height. /// /// ``` -/// let measure = tengri::output::Measure::::default(); +/// let measure = Measure::default(); /// ``` #[derive(Default)] pub struct Measure { pub __: PhantomData, @@ -68,80 +61,70 @@ use crate::*; /// Show an item only when a condition is true. /// /// ``` -/// fn test () -> impl tengri::output::Draw { -/// tengri::output::when(true, "Yes") -/// } +/// let when = 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::output::Draw { -/// tengri::output::either(true, "Yes", "No") -/// } +/// let either = 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) -} +pub struct Either(pub bool, pub A, pub B, pub PhantomData); /// Increment X and/or Y coordinate. /// /// ``` -/// let pushed = tengri::output::Push::XY(2, 2, "Hello"); +/// let pushed = 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::output::Pull::XY(2, 2, "Hello"); +/// let pulled = 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::output::Fill::XY("Hello"); +/// let filled = Fill::XY("Hello"); /// ``` pub enum Fill { X(A), Y(A), XY(A) } /// Set fixed size for content. /// /// ``` -/// let fixed = tengri::output::Fixed::XY(3, 5, "Hello"); // 3x5 +/// let fixed = 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::output::Min::XY(3, 5, "Hello"); // 3x1 +/// let maximum = 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::output::Min::XY(3, 5, "Hello"); // 5x5 +/// let minimam = 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::output::Shrink::XY(2, 0, "Hello"); // 1x1 +/// let shrunk = 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::output::Expand::XY(5, 3, "HELLO"); // 15x3 +/// let expanded = Expand::XY(5, 3, "HELLO"); // 15x3 /// ``` pub enum Expand { X(U, A), Y(U, A), XY(U, U, A), } @@ -151,9 +134,9 @@ pub enum Expand { X(U, A), Y(U, A), XY(U, U, A), } /// ``` /// use ::tengri::{output::*, tui::*}; /// 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); +/// fn test (area: XYWH<16>, 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, ""); @@ -184,17 +167,14 @@ pub enum Pad { X(U, A), Y(U, A), XY(U, U, A), } /// TODO DOCUMENTME /// /// ``` -/// use tengri::output::{Bounded, XYWH}; -/// let area = XYWH(0, 0, 0, 0); -/// let content = ""; -/// let bounded: Bounded = Bounded(area, content); +/// let bounded = Bounded(XYWH(0, 0, 0, 0), ""); /// ``` pub struct Bounded(pub XYWH, pub D); /// Draws items from an iterator. /// /// ``` -/// // FIXME let map = tengri::output::Map(||[].iter(), |_|{}); +/// let map = Map(||[].iter(), |_|{}); /// ``` pub struct Map where diff --git a/output/src/out_tests.rs b/output/src/out_tests.rs index 72bdabe..784f0a1 100644 --- a/output/src/out_tests.rs +++ b/output/src/out_tests.rs @@ -3,7 +3,7 @@ use proptest_derive::Arbitrary; use crate::*; #[test] fn test_area () { - assert_eq!(XYWH(10u16, 10, 20, 20).center(), XY(20, 20)); + assert_eq!(Area::center(&[10u16, 10, 20, 20]), [20, 20]); } proptest! { @@ -19,7 +19,7 @@ proptest! { h in u16::MIN..u16::MAX, a in u16::MIN..u16::MAX, ) { - let _ = d.split_fixed(XYWH(x, y, w, h), a); + let _ = d.split_fixed([x, y, w, h], a); } } @@ -32,27 +32,27 @@ proptest! { a in u16::MIN..u16::MAX, b in u16::MIN..u16::MAX, ) { - let _: XYWH = XYWH::zero(); - //let _: XYWH = XYWH::from_position([a, b]); - //let _: XYWH = XYWH::from_size([a, b]); - let area: XYWH = XYWH(x, y, w, h); - //let _ = area.expect_min(a, b); + 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.clipped_h(a); - let _ = area.clipped_w(b); - let _ = area.clipped(WH(a, b)); - //let _ = area.set_w(a); - //let _ = area.set_h(b); + 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(); - let _ = area.centered_x(a); - let _ = area.centered_y(b); - let _ = area.centered_xy([a, b]); } } @@ -63,15 +63,15 @@ proptest! { a in u16::MIN..u16::MAX, b in u16::MIN..u16::MAX, ) { - let size = WH(x, y); + 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(); + let _ = size.expect_min(a, b); + let _ = size.to_area_pos(); + let _ = size.to_area_size(); } } diff --git a/output/src/out_traits.rs b/output/src/out_traits.rs index 9c7ec49..2d81030 100644 --- a/output/src/out_traits.rs +++ b/output/src/out_traits.rs @@ -3,12 +3,18 @@ use crate::*; /// Drawing target. /// /// ``` -/// use tengri::output::*; -/// struct TestOut(XYWH); +/// use crate::*; +/// struct TestOut([u16;4]); /// impl Out for TestOut { /// type Unit = u16; -/// fn area (&self) -> XYWH { self.0 } -/// fn area_mut (&mut self) -> &mut XYWH { &mut self.0 } +/// 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: XYWH, _: &T) { /// println!("place_at: {area:?}"); /// () @@ -16,7 +22,7 @@ use crate::*; /// } /// impl Draw for String { /// fn draw (&self, to: &mut TestOut) { -/// //to.area_mut().set_w(self.len() as u16); +/// to.area_mut().set_w(self.len() as u16); /// } /// } /// ``` diff --git a/tui/src/lib.rs b/tui/src/lib.rs index 8e5aa42..6b3a34e 100644 --- a/tui/src/lib.rs +++ b/tui/src/lib.rs @@ -1,16 +1,23 @@ #![feature(type_changing_struct_update, trait_alias)] -pub extern crate dizzle; -pub extern crate tengri_input; -pub extern crate tengri_output; -pub extern crate ratatui; -pub extern crate crossterm; -pub extern crate palette; -pub extern crate better_panic; -pub use ::tengri_output::PerfModel; + use std::{time::Duration, thread::{spawn, JoinHandle}, io::Write}; use unicode_width::*; + +pub use ::{ + dizzle, + tengri_input, + tengri_output::{self, PerfModel}, + ratatui, + crossterm, + palette, + better_panic, +}; + pub(crate) use ::{ - dizzle::*, tengri_input::*, tengri_output::*, + dizzle::*, + tengri_input::*, + tengri_output::*, + atomic_float::AtomicF64, std::{io::{stdout, Stdout}, sync::{Arc, RwLock, atomic::{AtomicBool, Ordering::*}}}, better_panic::{Settings, Verbosity}, palette::{*, convert::*, okhsl::*}, @@ -31,7 +38,8 @@ pub(crate) use ::{ #[macro_export] macro_rules! tui_main { ($expr:expr) => { fn main () -> Usually<()> { - tengri_tui::Tui::run(true, $expr)?; + let state = Arc::new(RwLock::new($expr)); + tengri_tui::Tui::new().unwrap().run(&state)?; Ok(()) } }; @@ -72,14 +80,12 @@ macro_rules! border { )+} } -mod tui_structs; pub use self::tui_structs::*; -mod tui_traits; pub use self::tui_traits::*; -mod tui_impls; -#[cfg(test)] mod tui_test; +mod tui_structs; pub use self::tui_structs::*; +mod tui_traits; pub use self::tui_traits::*; +mod tui_impls; pub use self::tui_impls::*; /// Run an app in the main loop. pub fn tui_run + Handle + 'static> ( - join: bool, state: &Arc> ) -> Usually>> { let backend = CrosstermBackend::new(stdout()); @@ -94,17 +100,15 @@ pub fn tui_run + Handle + 'static> ( let _input_thread = tui_input(tui.clone(), state, Duration::from_millis(100)); tui.write().unwrap().setup()?; let render_thread = tui_output(tui.clone(), state, Duration::from_millis(10))?; - if join { - match render_thread.join() { - Ok(result) => { - tui.write().unwrap().teardown()?; - println!("\n\rRan successfully: {result:?}\n\r"); - }, - Err(error) => { - tui.write().unwrap().teardown()?; - panic!("\n\rDraw thread failed: error={error:?}.\n\r") - }, - } + match render_thread.join() { + Ok(result) => { + tui.write().unwrap().teardown()?; + println!("\n\rRan successfully: {result:?}\n\r"); + }, + Err(error) => { + tui.write().unwrap().teardown()?; + panic!("\n\rDraw thread failed: error={error:?}.\n\r") + }, } Ok(tui) } @@ -488,3 +492,41 @@ pub(crate) fn width_chars_max (max: u16, text: impl AsRef) -> u16 { } return width } + +#[cfg(test)] mod tui_test { + use crate::*; + #[test] fn test_tui_engine () -> Usually<()> { + //use std::sync::{Arc, RwLock}; + struct TestComponent(String); + impl HasContent for TestComponent { + fn content (&self) -> impl Content { + Some(self.0.as_str()) + } + } + impl Handle for TestComponent { + fn handle (&mut self, _from: &TuiIn) -> Perhaps { + Ok(None) + } + } + let engine = Tui::new()?; + engine.read().unwrap().exited.store(true, std::sync::atomic::Ordering::Relaxed); + let state = TestComponent("hello world".into()); + let _state = std::sync::Arc::new(std::sync::RwLock::new(state)); + //engine.run(&state)?; + Ok(()) + } + //#[test] fn test_parse_key () { + ////use KeyModifiers as Mods; + //let _test = |x: &str, y|assert_eq!(KeyMatcher::new(x).build(), Some(Event::Key(y))); + ////test(":x", + ////KeyEvent::new(KeyCode::Char('x'), Mods::NONE)); + ////test(":ctrl-x", + ////KeyEvent::new(KeyCode::Char('x'), Mods::CONTROL)); + ////test(":alt-x", + ////KeyEvent::new(KeyCode::Char('x'), Mods::ALT)); + ////test(":shift-x", + ////KeyEvent::new(KeyCode::Char('x'), Mods::SHIFT)); + ////test(":ctrl-alt-shift-x", + ////KeyEvent::new(KeyCode::Char('x'), Mods::CONTROL | Mods::ALT | Mods::SHIFT )); + //} +} diff --git a/tui/src/tui_impls.rs b/tui/src/tui_impls.rs index 5fc0198..20b3410 100644 --- a/tui/src/tui_impls.rs +++ b/tui/src/tui_impls.rs @@ -4,10 +4,10 @@ use rand::{thread_rng, distributions::uniform::UniformSampler}; impl Tui { /// Create and launch a terminal user interface. - pub fn run (join: bool, state: T) -> Usually>> where - T: Handle + Draw + Send + Sync + 'static - { - tui_run(join, &Arc::new(RwLock::new(state))) + pub fn run + Draw + 'static> ( + state: &Arc> + ) -> Usually>> { + tui_run(state) } /// True if done pub fn exited (&self) -> bool { self.exited.fetch_and(true, Relaxed) } diff --git a/tui/src/tui_test.rs b/tui/src/tui_test.rs index 0d8a1f7..e69de29 100644 --- a/tui/src/tui_test.rs +++ b/tui/src/tui_test.rs @@ -1,34 +0,0 @@ -use crate::*; - -#[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::run(false, TestComponent("hello world".into()))?; - engine.read().unwrap().exited.store(true, std::sync::atomic::Ordering::Relaxed); - //engine.run(&state)?; - Ok(()) -} - -//#[test] fn test_parse_key () { - ////use KeyModifiers as Mods; - //let _test = |x: &str, y|assert_eq!(KeyMatcher::new(x).build(), Some(Event::Key(y))); - ////test(":x", - ////KeyEvent::new(KeyCode::Char('x'), Mods::NONE)); - ////test(":ctrl-x", - ////KeyEvent::new(KeyCode::Char('x'), Mods::CONTROL)); - ////test(":alt-x", - ////KeyEvent::new(KeyCode::Char('x'), Mods::ALT)); - ////test(":shift-x", - ////KeyEvent::new(KeyCode::Char('x'), Mods::SHIFT)); - ////test(":ctrl-alt-shift-x", - ////KeyEvent::new(KeyCode::Char('x'), Mods::CONTROL | Mods::ALT | Mods::SHIFT )); -//}