diff --git a/pulse-ui/src/lib.rs b/pulse-ui/src/lib.rs index fa13f10..f60bd7b 100644 --- a/pulse-ui/src/lib.rs +++ b/pulse-ui/src/lib.rs @@ -11,25 +11,15 @@ pub mod widget; #[allow(async_fn_in_trait)] pub trait App { // State - async fn init(&mut self, ctx: state::Context); - async fn update(&mut self, event: Box); + async fn init(&mut self, ctx: &state::Context); + async fn update(&mut self, ctx: &state::Context, event: Box); // Rendering async fn layout(&self) -> layout::LayoutItem; async fn render(&mut self, allocation: layout::Allocation); async fn refresh(&mut self) { - crossterm::execute!( - std::io::stdout(), - crossterm::terminal::Clear(crossterm::terminal::ClearType::All) - ) - .unwrap(); - - crossterm::execute!( - std::io::stdout(), - crossterm::terminal::Clear(crossterm::terminal::ClearType::Purge) - ) - .unwrap(); + clear(); crossterm::execute!(std::io::stdout(), crossterm::cursor::Hide).unwrap(); @@ -41,18 +31,35 @@ pub trait App { pub async fn run(create_app: impl FnOnce(&state::Context) -> A) { let (tx, mut rx) = mpsc::channel(100); - let ctx = state::Context { tx }; + let ctx = state::Context { tx: tx.clone() }; + + crossterm::terminal::enable_raw_mode().unwrap(); + + tokio::spawn(async move { + tx.send(Box::new(crossterm::event::read().unwrap())) + .await + .unwrap(); + }); let mut app = create_app(&ctx); - app.init(ctx).await; + app.init(&ctx).await; app.refresh().await; while let Some(event) = rx.recv().await { - app.update(event).await; + if let Some(state::Close) = event.downcast_ref() { + break; + } + + app.update(&ctx, event).await; app.refresh().await; } + + crossterm::terminal::disable_raw_mode().unwrap(); + crossterm::execute!(std::io::stdout(), crossterm::cursor::Show).unwrap(); + + clear(); } pub fn get_screen() -> unit::Rect { @@ -65,3 +72,19 @@ pub fn get_screen() -> unit::Rect { height, } } + +pub fn clear() { + crossterm::execute!( + std::io::stdout(), + crossterm::terminal::Clear(crossterm::terminal::ClearType::All) + ) + .unwrap(); + + crossterm::execute!( + std::io::stdout(), + crossterm::terminal::Clear(crossterm::terminal::ClearType::Purge) + ) + .unwrap(); + + crossterm::execute!(std::io::stdout(), crossterm::cursor::MoveTo(0, 0)).unwrap(); +} diff --git a/pulse-ui/src/state.rs b/pulse-ui/src/state.rs index 279386c..28b7141 100644 --- a/pulse-ui/src/state.rs +++ b/pulse-ui/src/state.rs @@ -3,25 +3,26 @@ use std::{any::Any, sync::Arc}; use tokio::sync::{Mutex, MutexGuard, mpsc::Sender}; pub struct Refresh; +pub struct Close; #[derive(Debug, Clone)] pub struct Context { - pub(crate) tx: Sender>, + pub(crate) tx: Sender>, } pub struct State { pub value: Arc>, - tx: Sender>, + tx: Sender>, } pub struct StateGuard<'a, T> { is_mutated: bool, value: MutexGuard<'a, T>, - tx: &'a Sender>, + tx: &'a Sender>, } impl Context { - pub fn use_state(&self, v: T) -> State { + pub fn use_state(&self, v: T) -> State { State { value: Arc::new(Mutex::new(v)), tx: self.tx.clone(), @@ -62,3 +63,9 @@ impl<'a, T> Drop for StateGuard<'a, T> { } } } + +impl Context { + pub async fn close(&self) { + self.tx.send(Box::new(Close)).await.unwrap() + } +} diff --git a/src/main.rs b/src/main.rs index 74f9a7f..5187e18 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +use std::any::Any; + use pulse_ui::{ App, layout::{LayoutItem, layout}, @@ -10,9 +12,11 @@ pub struct PulseTradeApp { } impl App for PulseTradeApp { - async fn init(&mut self, ctx: pulse_ui::state::Context) {} + async fn init(&mut self, ctx: &pulse_ui::state::Context) {} - async fn update(&mut self, event: Box) {} + async fn update(&mut self, ctx: &pulse_ui::state::Context, event: Box) { + ctx.close().await + } async fn layout(&self) -> pulse_ui::layout::LayoutItem { layout(vec![