Stateful closing and io input

This commit is contained in:
2026-07-14 21:42:29 +02:00
parent 641805e1b9
commit 809e5d1529
3 changed files with 56 additions and 22 deletions
+39 -16
View File
@@ -11,25 +11,15 @@ pub mod widget;
#[allow(async_fn_in_trait)] #[allow(async_fn_in_trait)]
pub trait App { pub trait App {
// State // State
async fn init(&mut self, ctx: state::Context); async fn init(&mut self, ctx: &state::Context);
async fn update(&mut self, event: Box<dyn Any>); async fn update(&mut self, ctx: &state::Context, event: Box<dyn Any + Send + Sync>);
// Rendering // Rendering
async fn layout(&self) -> layout::LayoutItem; async fn layout(&self) -> layout::LayoutItem;
async fn render(&mut self, allocation: layout::Allocation); async fn render(&mut self, allocation: layout::Allocation);
async fn refresh(&mut self) { async fn refresh(&mut self) {
crossterm::execute!( clear();
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::Hide).unwrap(); crossterm::execute!(std::io::stdout(), crossterm::cursor::Hide).unwrap();
@@ -41,18 +31,35 @@ pub trait App {
pub async fn run<A: App>(create_app: impl FnOnce(&state::Context) -> A) { pub async fn run<A: App>(create_app: impl FnOnce(&state::Context) -> A) {
let (tx, mut rx) = mpsc::channel(100); 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); let mut app = create_app(&ctx);
app.init(ctx).await; app.init(&ctx).await;
app.refresh().await; app.refresh().await;
while let Some(event) = rx.recv().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; 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 { pub fn get_screen() -> unit::Rect {
@@ -65,3 +72,19 @@ pub fn get_screen() -> unit::Rect {
height, 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();
}
+11 -4
View File
@@ -3,25 +3,26 @@ use std::{any::Any, sync::Arc};
use tokio::sync::{Mutex, MutexGuard, mpsc::Sender}; use tokio::sync::{Mutex, MutexGuard, mpsc::Sender};
pub struct Refresh; pub struct Refresh;
pub struct Close;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Context { pub struct Context {
pub(crate) tx: Sender<Box<dyn Any>>, pub(crate) tx: Sender<Box<dyn Any + Send + Sync>>,
} }
pub struct State<T> { pub struct State<T> {
pub value: Arc<Mutex<T>>, pub value: Arc<Mutex<T>>,
tx: Sender<Box<dyn Any>>, tx: Sender<Box<dyn Any + Send + Sync>>,
} }
pub struct StateGuard<'a, T> { pub struct StateGuard<'a, T> {
is_mutated: bool, is_mutated: bool,
value: MutexGuard<'a, T>, value: MutexGuard<'a, T>,
tx: &'a Sender<Box<dyn Any>>, tx: &'a Sender<Box<dyn Any + Send + Sync>>,
} }
impl Context { impl Context {
pub fn use_state<T>(&self, v: T) -> State<T> { pub fn use_state<T: Send + Sync>(&self, v: T) -> State<T> {
State { State {
value: Arc::new(Mutex::new(v)), value: Arc::new(Mutex::new(v)),
tx: self.tx.clone(), 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()
}
}
+6 -2
View File
@@ -1,3 +1,5 @@
use std::any::Any;
use pulse_ui::{ use pulse_ui::{
App, App,
layout::{LayoutItem, layout}, layout::{LayoutItem, layout},
@@ -10,9 +12,11 @@ pub struct PulseTradeApp {
} }
impl App for 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<dyn std::any::Any>) {} async fn update(&mut self, ctx: &pulse_ui::state::Context, event: Box<dyn Any + Send + Sync>) {
ctx.close().await
}
async fn layout(&self) -> pulse_ui::layout::LayoutItem { async fn layout(&self) -> pulse_ui::layout::LayoutItem {
layout(vec![ layout(vec![