Stateful closing and io input
This commit is contained in:
+39
-16
@@ -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
@@ -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
@@ -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![
|
||||||
|
|||||||
Reference in New Issue
Block a user