From 905486edbd4702c6337d3f2460de13e8b2ae3888 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sun, 5 Jan 2025 08:16:15 +0100 Subject: [PATCH] break down engine modules --- edn/src/edn_item.rs | 46 +---------- edn/src/edn_iter.rs | 45 ++++++++++ edn/src/edn_main.rs | 2 - edn/src/lib.rs | 7 +- engine/src/engine.rs | 142 +------------------------------- engine/src/input.rs | 58 +------------ engine/src/input/handle.rs | 59 +++++++++++++ engine/src/output.rs | 39 ++------- engine/src/output/area.rs | 84 +++++++++++++++++++ engine/src/output/content.rs | 17 ++++ engine/src/output/coordinate.rs | 26 ++++++ engine/src/output/render.rs | 12 +++ engine/src/output/size.rs | 32 +++++++ engine/src/output/thunk.rs | 10 +++ engine/src/tui.rs | 74 +---------------- engine/src/tui/tui_run.rs | 75 +++++++++++++++++ 16 files changed, 376 insertions(+), 352 deletions(-) create mode 100644 edn/src/edn_iter.rs create mode 100644 engine/src/input/handle.rs create mode 100644 engine/src/output/area.rs create mode 100644 engine/src/output/content.rs create mode 100644 engine/src/output/coordinate.rs create mode 100644 engine/src/output/render.rs create mode 100644 engine/src/output/size.rs create mode 100644 engine/src/output/thunk.rs create mode 100644 engine/src/tui/tui_run.rs diff --git a/edn/src/edn_item.rs b/edn/src/edn_item.rs index d4af7b6b..d766f08f 100644 --- a/edn/src/edn_item.rs +++ b/edn/src/edn_item.rs @@ -55,52 +55,8 @@ impl + PartialEq + Default + Clone + std::fmt::Debug> EdnItem { EdnIterator::new(&self) } } -enum EdnIterator<'a, T>{ - Nil, - Sym(&'a T), - Exp(Vec>) -} -impl<'a, T: AsRef> EdnIterator<'a, T> { - fn new (item: &'a EdnItem) -> Self { - use EdnItem::*; - match item { - Sym(t) => Self::Sym(t), - Exp(i) => Self::Exp(i.iter().map(EdnIterator::new).collect()), - _ => Self::Nil, - } - } -} -impl<'a, T: AsRef> Iterator for EdnIterator<'a, T> { - type Item = &'a T; - fn next (&mut self) -> Option { - use EdnIterator::*; - match self { - Sym(t) => { - let t = *t; - *self = Nil; - Some(t) - }, - Exp(v) => match v.as_mut_slice() { - [a] => if let Some(next) = a.next() { - Some(next) - } else { - *self = Exp(v.split_off(1)); - self.next() - }, - _ => { - *self = Nil; - None - } - }, - _ => { - *self = Nil; - None - } - } - } -} impl EdnItem { - fn from (other: EdnItem>) -> EdnItem { + pub fn from (other: EdnItem>) -> EdnItem { use EdnItem::*; match other { Nil => Nil, diff --git a/edn/src/edn_iter.rs b/edn/src/edn_iter.rs new file mode 100644 index 00000000..55409cb0 --- /dev/null +++ b/edn/src/edn_iter.rs @@ -0,0 +1,45 @@ +use crate::*; +pub enum EdnIterator<'a, T>{ + Nil, + Sym(&'a T), + Exp(Vec>) +} +impl<'a, T: AsRef> EdnIterator<'a, T> { + pub fn new (item: &'a EdnItem) -> Self { + use EdnItem::*; + match item { + Sym(t) => Self::Sym(t), + Exp(i) => Self::Exp(i.iter().map(EdnIterator::new).collect()), + _ => Self::Nil, + } + } +} +impl<'a, T: AsRef> Iterator for EdnIterator<'a, T> { + type Item = &'a T; + fn next (&mut self) -> Option { + use EdnIterator::*; + match self { + Sym(t) => { + let t = *t; + *self = Nil; + Some(t) + }, + Exp(v) => match v.as_mut_slice() { + [a] => if let Some(next) = a.next() { + Some(next) + } else { + *self = Exp(v.split_off(1)); + self.next() + }, + _ => { + *self = Nil; + None + } + }, + _ => { + *self = Nil; + None + } + } + } +} diff --git a/edn/src/edn_main.rs b/edn/src/edn_main.rs index f565d808..1bd543b9 100644 --- a/edn/src/edn_main.rs +++ b/edn/src/edn_main.rs @@ -1,5 +1,3 @@ -use crate::*; - #[macro_export] macro_rules! run_tek_edn { ($source:expr) => { struct EdnRunner; diff --git a/edn/src/lib.rs b/edn/src/lib.rs index 857ab98e..397cd068 100644 --- a/edn/src/lib.rs +++ b/edn/src/lib.rs @@ -5,16 +5,13 @@ pub(crate) use std::{ fmt::{Debug, Formatter, Error as FormatError} }; pub use ::tek_layout; -pub(crate) use ::tek_layout::{ - *, - tek_engine::{Usually, Content, Render, Engine, Thunk} -}; mod edn_error; pub use self::edn_error::*; mod edn_item; pub use self::edn_item::*; +mod edn_iter; pub use self::edn_iter::*; +mod edn_main; pub use self::edn_main::*; mod edn_token; pub use self::edn_token::*; mod edn_view; pub use self::edn_view::*; -mod edn_main; pub use self::edn_main::*; #[cfg(test)] #[test] fn test_edn () -> Result<(), ParseError> { use EdnItem::*; diff --git a/engine/src/engine.rs b/engine/src/engine.rs index 886f9670..b3a862cd 100644 --- a/engine/src/engine.rs +++ b/engine/src/engine.rs @@ -1,6 +1,4 @@ use crate::*; -use std::fmt::{Debug, Display}; -use std::ops::{Add, Sub, Mul, Div}; /// Platform backend. pub trait Engine: Send + Sync + Sized { @@ -13,9 +11,9 @@ pub trait Engine: Send + Sync + Sized { /// Unit of length type Unit: Coordinate; /// Rectangle without offset - type Size: Size + From<[Self::Unit;2]> + Debug + Copy; + type Size: Size; /// Rectangle with offset - type Area: Area + From<[Self::Unit;4]> + Debug + Copy; + type Area: Area; /// Prepare before run fn setup (&mut self) -> Usually<()> { Ok(()) } /// True if done @@ -23,139 +21,3 @@ pub trait Engine: Send + Sync + Sized { /// Clean up after run fn teardown (&mut self) -> Usually<()> { Ok(()) } } - -/// A linear coordinate. -pub trait Coordinate: Send + Sync + Copy - + Add - + Sub - + Mul - + Div - + Ord + PartialEq + Eq - + Debug + Display + Default - + From + Into - + Into - + Into -{ - #[inline] fn minus (self, other: Self) -> Self { - if self >= other { - self - other - } else { - 0.into() - } - } - #[inline] fn zero () -> Self { - 0.into() - } -} - -pub trait Size { - fn x (&self) -> N; - fn y (&self) -> N; - #[inline] fn w (&self) -> N { self.x() } - #[inline] fn h (&self) -> N { self.y() } - #[inline] fn wh (&self) -> [N;2] { [self.x(), self.y()] } - #[inline] fn clip_w (&self, w: N) -> [N;2] { [self.w().min(w), self.h()] } - #[inline] fn clip_h (&self, h: N) -> [N;2] { [self.w(), self.h().min(h)] } - #[inline] 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) - } - } - #[inline] fn zero () -> [N;2] { - [N::zero(), N::zero()] - } -} - -impl Size for (N, N) { - #[inline] fn x (&self) -> N { self.0 } - #[inline] fn y (&self) -> N { self.1 } -} - -impl Size for [N;2] { - #[inline] fn x (&self) -> N { self[0] } - #[inline] fn y (&self) -> N { self[1] } -} - -pub trait Area { - fn x (&self) -> N; - fn y (&self) -> N; - fn w (&self) -> N; - fn h (&self) -> N; - #[inline] 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) - } - } - #[inline] fn xy (&self) -> [N;2] { - [self.x(), self.y()] - } - #[inline] fn wh (&self) -> [N;2] { - [self.w(), self.h()] - } - #[inline] fn xywh (&self) -> [N;4] { - [self.x(), self.y(), self.w(), self.h()] - } - #[inline] fn clip_h (&self, h: N) -> [N;4] { - [self.x(), self.y(), self.w(), self.h().min(h)] - } - #[inline] fn clip_w (&self, w: N) -> [N;4] { - [self.x(), self.y(), self.w().min(w), self.h()] - } - #[inline] fn clip (&self, wh: impl Size) -> [N;4] { - [self.x(), self.y(), wh.w(), wh.h()] - } - #[inline] fn set_w (&self, w: N) -> [N;4] { - [self.x(), self.y(), w, self.h()] - } - #[inline] fn set_h (&self, h: N) -> [N;4] { - [self.x(), self.y(), self.w(), h] - } - #[inline] fn x2 (&self) -> N { - self.x() + self.w() - } - #[inline] fn y2 (&self) -> N { - self.y() + self.h() - } - #[inline] fn lrtb (&self) -> [N;4] { - [self.x(), self.x2(), self.y(), self.y2()] - } - #[inline] fn center (&self) -> [N;2] { - [self.x() + self.w()/2.into(), self.y() + self.h()/2.into()] - } - #[inline] fn center_x (&self, n: N) -> [N;4] { - let [x, y, w, h] = self.xywh(); - [(x + w / 2.into()).minus(n / 2.into()), y + h / 2.into(), n, 1.into()] - } - #[inline] fn center_y (&self, m: N) -> [N;4] { - let [x, y, w, h] = self.xywh(); - [x + w / 2.into(), (y + h / 2.into()).minus(m / 2.into()), 1.into(), m] - } - #[inline] fn center_xy (&self, [n, m]: [N;2]) -> [N;4] { - let [x, y, w, h] = self.xywh(); - [(x + w / 2.into()).minus(n / 2.into()), (y + h / 2.into()).minus(m / 2.into()), n, m] - } - #[inline] fn centered (&self) -> [N;2] { - [self.x().minus(self.w()/2.into()), self.y().minus(self.h()/2.into())] - } - fn zero () -> [N;4] { - [N::zero(), N::zero(), N::zero(), N::zero()] - } -} - -impl Area for (N, N, N, N) { - #[inline] fn x (&self) -> N { self.0 } - #[inline] fn y (&self) -> N { self.1 } - #[inline] fn w (&self) -> N { self.2 } - #[inline] fn h (&self) -> N { self.3 } -} - -impl Area for [N;4] { - #[inline] fn x (&self) -> N { self[0] } - #[inline] fn y (&self) -> N { self[1] } - #[inline] fn w (&self) -> N { self[2] } - #[inline] fn h (&self) -> N { self[3] } -} diff --git a/engine/src/input.rs b/engine/src/input.rs index 0ec9dbf3..0cc8718a 100644 --- a/engine/src/input.rs +++ b/engine/src/input.rs @@ -1,12 +1,6 @@ use crate::*; -use std::sync::{Arc, Mutex, RwLock}; -/// Handle input -pub trait Handle: Send + Sync { - fn handle (&mut self, _input: &E::Input) -> Perhaps { - Ok(None) - } -} +mod handle; pub use self::handle::*; /// Current input state pub trait Input { @@ -19,53 +13,3 @@ pub trait Input { /// Mark component as done fn done (&self); } - -#[macro_export] macro_rules! handle { - (<$E:ty>|$self:ident:$Struct:ty,$input:ident|$handler:expr) => { - impl Handle<$E> for $Struct { - fn handle (&mut $self, $input: &<$E as Engine>::Input) -> Perhaps<<$E as Engine>::Handled> { - $handler - } - } - } -} - -impl> Handle for &mut H { - fn handle (&mut self, context: &E::Input) -> Perhaps { - (*self).handle(context) - } -} - -impl> Handle for Option { - fn handle (&mut self, context: &E::Input) -> Perhaps { - if let Some(ref mut handle) = self { - handle.handle(context) - } else { - Ok(None) - } - } -} - -impl Handle for Mutex where H: Handle { - fn handle (&mut self, context: &E::Input) -> Perhaps { - self.get_mut().unwrap().handle(context) - } -} - -impl Handle for Arc> where H: Handle { - fn handle (&mut self, context: &E::Input) -> Perhaps { - self.lock().unwrap().handle(context) - } -} - -impl Handle for RwLock where H: Handle { - fn handle (&mut self, context: &E::Input) -> Perhaps { - self.write().unwrap().handle(context) - } -} - -impl Handle for Arc> where H: Handle { - fn handle (&mut self, context: &E::Input) -> Perhaps { - self.write().unwrap().handle(context) - } -} diff --git a/engine/src/input/handle.rs b/engine/src/input/handle.rs new file mode 100644 index 00000000..15a9b5e1 --- /dev/null +++ b/engine/src/input/handle.rs @@ -0,0 +1,59 @@ +use crate::*; +use std::sync::{Mutex, Arc, RwLock}; + +/// Handle input +pub trait Handle: Send + Sync { + fn handle (&mut self, _input: &E::Input) -> Perhaps { + Ok(None) + } +} + +#[macro_export] macro_rules! handle { + (<$E:ty>|$self:ident:$Struct:ty,$input:ident|$handler:expr) => { + impl Handle<$E> for $Struct { + fn handle (&mut $self, $input: &<$E as Engine>::Input) -> Perhaps<<$E as Engine>::Handled> { + $handler + } + } + } +} + +impl> Handle for &mut H { + fn handle (&mut self, context: &E::Input) -> Perhaps { + (*self).handle(context) + } +} + +impl> Handle for Option { + fn handle (&mut self, context: &E::Input) -> Perhaps { + if let Some(ref mut handle) = self { + handle.handle(context) + } else { + Ok(None) + } + } +} + +impl Handle for Mutex where H: Handle { + fn handle (&mut self, context: &E::Input) -> Perhaps { + self.get_mut().unwrap().handle(context) + } +} + +impl Handle for Arc> where H: Handle { + fn handle (&mut self, context: &E::Input) -> Perhaps { + self.lock().unwrap().handle(context) + } +} + +impl Handle for RwLock where H: Handle { + fn handle (&mut self, context: &E::Input) -> Perhaps { + self.write().unwrap().handle(context) + } +} + +impl Handle for Arc> where H: Handle { + fn handle (&mut self, context: &E::Input) -> Perhaps { + self.write().unwrap().handle(context) + } +} diff --git a/engine/src/output.rs b/engine/src/output.rs index ed3b9b8c..2e891766 100644 --- a/engine/src/output.rs +++ b/engine/src/output.rs @@ -1,5 +1,13 @@ use crate::*; -use std::marker::PhantomData; + +mod coordinate; pub use self::coordinate::*; +mod size; pub use self::size::*; +mod area; pub use self::area::*; + +mod render; pub use self::render::*; +mod content; pub use self::content::*; +mod thunk; pub use self::thunk::*; + /// Rendering target pub trait Output { /// Current output area @@ -14,35 +22,6 @@ pub trait Output { #[inline] fn h (&self) -> E::Unit { self.area().h() } #[inline] fn wh (&self) -> E::Size { self.area().wh().into() } } -pub trait Render: Send + Sync { - fn layout (&self, area: E::Area) -> E::Area; - fn render (&self, output: &mut E::Output); -} -pub trait Content: Send + Sync + Sized { - fn content (&self) -> impl Render { () } - fn layout (&self, area: E::Area) -> E::Area { self.content().layout(area) } - fn render (&self, output: &mut E::Output) { self.content().render(output) } -} -impl> Render for C { - fn layout (&self, area: E::Area) -> E::Area { Content::layout(self, area) } - fn render (&self, output: &mut E::Output) { Content::render(self, output) } -} -impl<'a, E: Engine> Content for Box + 'a> { - fn content (&self) -> impl Render { self } -} -impl<'a, E: Engine> Content for Box + Send + Sync + 'a> { - fn content (&self) -> impl Render { self } -} -impl Content for &(dyn Render + '_) { - fn content (&self) -> impl Render { self } -} -pub struct Thunk, F: Fn()->T + Send + Sync>(F, PhantomData); -impl, F: Fn()->T + Send + Sync> Thunk { - pub fn new (thunk: F) -> Self { Self(thunk, Default::default()) } -} -impl, F: Fn()->T + Send + Sync> Content for Thunk { - fn content (&self) -> impl Render { (self.0)() } -} impl> Content for &T { fn content (&self) -> impl Render { (*self).content() diff --git a/engine/src/output/area.rs b/engine/src/output/area.rs new file mode 100644 index 00000000..f17bad73 --- /dev/null +++ b/engine/src/output/area.rs @@ -0,0 +1,84 @@ +use crate::*; +use std::fmt::Debug; + +pub trait Area: From<[N;4]> + Debug + Copy { + fn x (&self) -> N; + fn y (&self) -> N; + fn w (&self) -> N; + fn h (&self) -> N; + #[inline] 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) + } + } + #[inline] fn xy (&self) -> [N;2] { + [self.x(), self.y()] + } + #[inline] fn wh (&self) -> [N;2] { + [self.w(), self.h()] + } + #[inline] fn xywh (&self) -> [N;4] { + [self.x(), self.y(), self.w(), self.h()] + } + #[inline] fn clip_h (&self, h: N) -> [N;4] { + [self.x(), self.y(), self.w(), self.h().min(h)] + } + #[inline] fn clip_w (&self, w: N) -> [N;4] { + [self.x(), self.y(), self.w().min(w), self.h()] + } + #[inline] fn clip (&self, wh: impl Size) -> [N;4] { + [self.x(), self.y(), wh.w(), wh.h()] + } + #[inline] fn set_w (&self, w: N) -> [N;4] { + [self.x(), self.y(), w, self.h()] + } + #[inline] fn set_h (&self, h: N) -> [N;4] { + [self.x(), self.y(), self.w(), h] + } + #[inline] fn x2 (&self) -> N { + self.x() + self.w() + } + #[inline] fn y2 (&self) -> N { + self.y() + self.h() + } + #[inline] fn lrtb (&self) -> [N;4] { + [self.x(), self.x2(), self.y(), self.y2()] + } + #[inline] fn center (&self) -> [N;2] { + [self.x() + self.w()/2.into(), self.y() + self.h()/2.into()] + } + #[inline] fn center_x (&self, n: N) -> [N;4] { + let [x, y, w, h] = self.xywh(); + [(x + w / 2.into()).minus(n / 2.into()), y + h / 2.into(), n, 1.into()] + } + #[inline] fn center_y (&self, m: N) -> [N;4] { + let [x, y, w, h] = self.xywh(); + [x + w / 2.into(), (y + h / 2.into()).minus(m / 2.into()), 1.into(), m] + } + #[inline] fn center_xy (&self, [n, m]: [N;2]) -> [N;4] { + let [x, y, w, h] = self.xywh(); + [(x + w / 2.into()).minus(n / 2.into()), (y + h / 2.into()).minus(m / 2.into()), n, m] + } + #[inline] fn centered (&self) -> [N;2] { + [self.x().minus(self.w()/2.into()), self.y().minus(self.h()/2.into())] + } + fn zero () -> [N;4] { + [N::zero(), N::zero(), N::zero(), N::zero()] + } +} + +impl Area for (N, N, N, N) { + #[inline] fn x (&self) -> N { self.0 } + #[inline] fn y (&self) -> N { self.1 } + #[inline] fn w (&self) -> N { self.2 } + #[inline] fn h (&self) -> N { self.3 } +} + +impl Area for [N;4] { + #[inline] fn x (&self) -> N { self[0] } + #[inline] fn y (&self) -> N { self[1] } + #[inline] fn w (&self) -> N { self[2] } + #[inline] fn h (&self) -> N { self[3] } +} diff --git a/engine/src/output/content.rs b/engine/src/output/content.rs new file mode 100644 index 00000000..746dcd28 --- /dev/null +++ b/engine/src/output/content.rs @@ -0,0 +1,17 @@ +use crate::*; +/// Build a [Render]able out of other [Render]ables, +/// then apply optional custom render/layout on top. +pub trait Content: Send + Sync + Sized { + fn content (&self) -> impl Render { () } + fn layout (&self, area: E::Area) -> E::Area { self.content().layout(area) } + fn render (&self, output: &mut E::Output) { self.content().render(output) } +} +impl<'a, E: Engine> Content for Box + 'a> { + fn content (&self) -> impl Render { self } +} +impl<'a, E: Engine> Content for Box + Send + Sync + 'a> { + fn content (&self) -> impl Render { self } +} +impl Content for &(dyn Render + '_) { + fn content (&self) -> impl Render { self } +} diff --git a/engine/src/output/coordinate.rs b/engine/src/output/coordinate.rs new file mode 100644 index 00000000..5adf9385 --- /dev/null +++ b/engine/src/output/coordinate.rs @@ -0,0 +1,26 @@ +use std::fmt::{Debug, Display}; +use std::ops::{Add, Sub, Mul, Div}; + +/// A linear coordinate. +pub trait Coordinate: Send + Sync + Copy + + Add + + Sub + + Mul + + Div + + Ord + PartialEq + Eq + + Debug + Display + Default + + From + Into + + Into + + Into +{ + #[inline] fn minus (self, other: Self) -> Self { + if self >= other { + self - other + } else { + 0.into() + } + } + #[inline] fn zero () -> Self { + 0.into() + } +} diff --git a/engine/src/output/render.rs b/engine/src/output/render.rs new file mode 100644 index 00000000..5ba5e1dc --- /dev/null +++ b/engine/src/output/render.rs @@ -0,0 +1,12 @@ +use crate::*; + +/// Custom layout and rendering. +pub trait Render: Send + Sync { + fn layout (&self, area: E::Area) -> E::Area; + fn render (&self, output: &mut E::Output); +} + +impl> Render for C { + fn layout (&self, area: E::Area) -> E::Area { Content::layout(self, area) } + fn render (&self, output: &mut E::Output) { Content::render(self, output) } +} diff --git a/engine/src/output/size.rs b/engine/src/output/size.rs new file mode 100644 index 00000000..cc5fa329 --- /dev/null +++ b/engine/src/output/size.rs @@ -0,0 +1,32 @@ +use crate::*; +use std::fmt::Debug; + +pub trait Size: From<[N;2]> + Debug + Copy { + fn x (&self) -> N; + fn y (&self) -> N; + #[inline] fn w (&self) -> N { self.x() } + #[inline] fn h (&self) -> N { self.y() } + #[inline] fn wh (&self) -> [N;2] { [self.x(), self.y()] } + #[inline] fn clip_w (&self, w: N) -> [N;2] { [self.w().min(w), self.h()] } + #[inline] fn clip_h (&self, h: N) -> [N;2] { [self.w(), self.h().min(h)] } + #[inline] 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) + } + } + #[inline] fn zero () -> [N;2] { + [N::zero(), N::zero()] + } +} + +impl Size for (N, N) { + #[inline] fn x (&self) -> N { self.0 } + #[inline] fn y (&self) -> N { self.1 } +} + +impl Size for [N;2] { + #[inline] fn x (&self) -> N { self[0] } + #[inline] fn y (&self) -> N { self[1] } +} diff --git a/engine/src/output/thunk.rs b/engine/src/output/thunk.rs new file mode 100644 index 00000000..a4bbd303 --- /dev/null +++ b/engine/src/output/thunk.rs @@ -0,0 +1,10 @@ +use crate::*; +use std::marker::PhantomData; +/// Lazily-evaluated [Render]able. +pub struct Thunk, F: Fn()->T + Send + Sync>(F, PhantomData); +impl, F: Fn()->T + Send + Sync> Thunk { + pub fn new (thunk: F) -> Self { Self(thunk, Default::default()) } +} +impl, F: Fn()->T + Send + Sync> Content for Thunk { + fn content (&self) -> impl Render { (self.0)() } +} diff --git a/engine/src/tui.rs b/engine/src/tui.rs index 68116829..c7579dd0 100644 --- a/engine/src/tui.rs +++ b/engine/src/tui.rs @@ -1,11 +1,10 @@ mod tui_output; pub use self::tui_output::*; mod tui_input; pub use self::tui_input::*; +mod tui_run; pub use self::tui_run::*; use crate::*; use std::sync::{Arc, RwLock, atomic::{AtomicBool, Ordering::*}}; use std::io::{stdout, Stdout}; -use std::time::Duration; -use std::thread::{spawn, JoinHandle}; pub use ::better_panic; pub(crate) use better_panic::{Settings, Verbosity}; @@ -91,74 +90,3 @@ impl Tui { buffer } } - -pub trait TuiRun + Handle + Sized + 'static> { - /// Run an app in the main loop. - fn run (&self, state: &Arc>) -> Usually<()>; - /// Spawn the input thread. - fn run_input (&self, state: &Arc>, poll: Duration) -> JoinHandle<()>; - /// Spawn the output thread. - fn run_output (&self, state: &Arc>, sleep: Duration) -> JoinHandle<()>; -} - -impl + Handle + Sized + 'static> TuiRun for Arc> { - fn run (&self, state: &Arc>) -> Usually<()> { - let _input_thread = self.run_input(state, Duration::from_millis(100)); - self.write().unwrap().setup()?; - let render_thread = self.run_output(state, Duration::from_millis(10)); - render_thread.join().expect("main thread failed"); - self.write().unwrap().teardown()?; - Ok(()) - } - fn run_input (&self, state: &Arc>, poll: Duration) -> JoinHandle<()> { - let exited = self.read().unwrap().exited.clone(); - let state = state.clone(); - spawn(move || loop { - if exited.fetch_and(true, Relaxed) { - break - } - if ::crossterm::event::poll(poll).is_ok() { - let event = ::crossterm::event::read().unwrap(); - match event { - kpat!(Ctrl-KeyCode::Char('c')) => { - exited.store(true, Relaxed); - }, - _ => { - let exited = exited.clone(); - if let Err(e) = state.write().unwrap().handle(&TuiIn(exited, event)) { - panic!("{e}") - } - } - } - } - }) - } - fn run_output (&self, state: &Arc>, sleep: Duration) -> JoinHandle<()> { - let exited = self.read().unwrap().exited.clone(); - let engine = self.clone(); - let state = state.clone(); - let Size { width, height } = engine.read().unwrap().backend.size().expect("get size failed"); - let mut buffer = Buffer::empty(Rect { x: 0, y: 0, width, height }); - spawn(move || loop { - if exited.fetch_and(true, Relaxed) { - break - } - let Size { width, height } = engine.read().unwrap().backend.size() - .expect("get size failed"); - if let Ok(state) = state.try_read() { - let size = Rect { x: 0, y: 0, width, height }; - if buffer.area != size { - engine.write().unwrap().backend.clear_region(ClearType::All) - .expect("clear failed"); - buffer.resize(size); - buffer.reset(); - } - let mut output = TuiOut { buffer, area: [0, 0, width, height] }; - state.render(&mut output); - buffer = engine.write().unwrap().flip(output.buffer, size); - } - std::thread::sleep(sleep); - }) - } -} - diff --git a/engine/src/tui/tui_run.rs b/engine/src/tui/tui_run.rs new file mode 100644 index 00000000..86c895bb --- /dev/null +++ b/engine/src/tui/tui_run.rs @@ -0,0 +1,75 @@ +use crate::{*, tui::*}; +use ratatui::prelude::Size; +use std::time::Duration; +use std::thread::{spawn, JoinHandle}; + +pub trait TuiRun + Handle + Sized + 'static> { + /// Run an app in the main loop. + fn run (&self, state: &Arc>) -> Usually<()>; + /// Spawn the input thread. + fn run_input (&self, state: &Arc>, poll: Duration) -> JoinHandle<()>; + /// Spawn the output thread. + fn run_output (&self, state: &Arc>, sleep: Duration) -> JoinHandle<()>; +} + +impl + Handle + Sized + 'static> TuiRun for Arc> { + fn run (&self, state: &Arc>) -> Usually<()> { + let _input_thread = self.run_input(state, Duration::from_millis(100)); + self.write().unwrap().setup()?; + let render_thread = self.run_output(state, Duration::from_millis(10)); + render_thread.join().expect("main thread failed"); + self.write().unwrap().teardown()?; + Ok(()) + } + fn run_input (&self, state: &Arc>, poll: Duration) -> JoinHandle<()> { + let exited = self.read().unwrap().exited.clone(); + let state = state.clone(); + spawn(move || loop { + if exited.fetch_and(true, Relaxed) { + break + } + if ::crossterm::event::poll(poll).is_ok() { + let event = ::crossterm::event::read().unwrap(); + match event { + kpat!(Ctrl-KeyCode::Char('c')) => { + exited.store(true, Relaxed); + }, + _ => { + let exited = exited.clone(); + if let Err(e) = state.write().unwrap().handle(&TuiIn(exited, event)) { + panic!("{e}") + } + } + } + } + }) + } + fn run_output (&self, state: &Arc>, sleep: Duration) -> JoinHandle<()> { + let exited = self.read().unwrap().exited.clone(); + let engine = self.clone(); + let state = state.clone(); + let Size { width, height } = engine.read().unwrap().backend.size().expect("get size failed"); + let mut buffer = Buffer::empty(Rect { x: 0, y: 0, width, height }); + spawn(move || loop { + if exited.fetch_and(true, Relaxed) { + break + } + let Size { width, height } = engine.read().unwrap().backend.size() + .expect("get size failed"); + if let Ok(state) = state.try_read() { + let size = Rect { x: 0, y: 0, width, height }; + if buffer.area != size { + engine.write().unwrap().backend.clear_region(ClearType::All) + .expect("clear failed"); + buffer.resize(size); + buffer.reset(); + } + let mut output = TuiOut { buffer, area: [0, 0, width, height] }; + state.render(&mut output); + buffer = engine.write().unwrap().flip(output.buffer, size); + } + std::thread::sleep(sleep); + }) + } +} +