diff --git a/output/src/out_structs.rs b/output/src/out_structs.rs index a68771a..34dc857 100644 --- a/output/src/out_structs.rs +++ b/output/src/out_structs.rs @@ -1,3 +1,4 @@ +#[cfg(test)] use proptest_derive::Arbitrary; use crate::*; /// A point (X, Y). @@ -6,7 +7,9 @@ use crate::*; /// let xy: XY = XY(0, 0); /// ``` #[cfg_attr(test, derive(Arbitrary))] -#[derive(Copy, Clone, Debug, Default)] pub struct XY(pub C, pub C); +#[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct XY( + pub C, pub C +); /// A size (Width, Height). /// @@ -14,7 +17,9 @@ use crate::*; /// let wh: WH = WH(0, 0); /// ``` #[cfg_attr(test, derive(Arbitrary))] -#[derive(Copy, Clone, Debug, Default)] pub struct WH(pub C, pub C); +#[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct WH( + pub C, pub C +); /// Point with size. /// @@ -25,7 +30,9 @@ use crate::*; /// * [ ] TODO: anchor field (determines at which corner/side is X0 Y0) /// #[cfg_attr(test, derive(Arbitrary))] -#[derive(Copy, Clone, Debug, Default)] pub struct XYWH(pub C, pub C, pub C, pub C); +#[derive(Copy, Clone, Debug, Default, PartialEq)] pub struct XYWH( + pub C, pub C, pub C, pub C +); /// A cardinal direction. /// diff --git a/output/src/out_tests.rs b/output/src/out_tests.rs index 784f0a1..72bdabe 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!(Area::center(&[10u16, 10, 20, 20]), [20, 20]); + assert_eq!(XYWH(10u16, 10, 20, 20).center(), XY(20, 20)); } proptest! { @@ -19,7 +19,7 @@ proptest! { h in u16::MIN..u16::MAX, a in u16::MIN..u16::MAX, ) { - let _ = d.split_fixed([x, y, w, h], a); + let _ = d.split_fixed(XYWH(x, y, w, h), a); } } @@ -32,27 +32,27 @@ proptest! { 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 _: 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 _ = 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.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.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 = [x, y]; + let size = WH(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/tui/src/lib.rs b/tui/src/lib.rs index 6b3a34e..8e5aa42 100644 --- a/tui/src/lib.rs +++ b/tui/src/lib.rs @@ -1,23 +1,16 @@ #![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::*, - atomic_float::AtomicF64, + dizzle::*, tengri_input::*, tengri_output::*, std::{io::{stdout, Stdout}, sync::{Arc, RwLock, atomic::{AtomicBool, Ordering::*}}}, better_panic::{Settings, Verbosity}, palette::{*, convert::*, okhsl::*}, @@ -38,8 +31,7 @@ pub(crate) use ::{ #[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)?; + tengri_tui::Tui::run(true, $expr)?; Ok(()) } }; @@ -80,12 +72,14 @@ macro_rules! border { )+} } -mod tui_structs; pub use self::tui_structs::*; -mod tui_traits; pub use self::tui_traits::*; -mod tui_impls; pub use self::tui_impls::*; +mod tui_structs; pub use self::tui_structs::*; +mod tui_traits; pub use self::tui_traits::*; +mod tui_impls; +#[cfg(test)] mod tui_test; /// Run an app in the main loop. pub fn tui_run + Handle + 'static> ( + join: bool, state: &Arc> ) -> Usually>> { let backend = CrosstermBackend::new(stdout()); @@ -100,15 +94,17 @@ 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))?; - 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") - }, + 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") + }, + } } Ok(tui) } @@ -492,41 +488,3 @@ 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 20b3410..5fc0198 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 + Draw + 'static> ( - state: &Arc> - ) -> Usually>> { - tui_run(state) + pub fn run (join: bool, state: T) -> Usually>> where + T: Handle + Draw + Send + Sync + 'static + { + tui_run(join, &Arc::new(RwLock::new(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 e69de29..1e3f9b3 100644 --- a/tui/src/tui_test.rs +++ b/tui/src/tui_test.rs @@ -0,0 +1,33 @@ +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 )); +//}