WIP tui code re-organization

This commit is contained in:
Daniel Flanagan 2024-04-19 18:58:44 -05:00
parent 9d61b04731
commit b3b797f33c
4 changed files with 76 additions and 40 deletions

1
Cargo.lock generated
View file

@ -698,6 +698,7 @@ checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
dependencies = [ dependencies = [
"bitflags 2.5.0", "bitflags 2.5.0",
"crossterm_winapi", "crossterm_winapi",
"futures-core",
"libc", "libc",
"mio", "mio",
"parking_lot 0.12.1", "parking_lot 0.12.1",

View file

@ -12,7 +12,7 @@ clap_derive = "4.5.4"
cliclack = "0.2.5" cliclack = "0.2.5"
color-eyre = "0.6.3" color-eyre = "0.6.3"
config = "0.14.0" config = "0.14.0"
crossterm = "0.27.0" crossterm = { version = "0.27.0", features = ["event-stream"] }
futures = "0.3.30" futures = "0.3.30"
keyring = "2.3.2" keyring = "2.3.2"
ratatui = "0.26.2" ratatui = "0.26.2"

View file

@ -113,8 +113,7 @@ mod ui {
impl Args { impl Args {
pub async fn run(&self, tasks: SharedTasks) -> Result<()> { pub async fn run(&self, tasks: SharedTasks) -> Result<()> {
let tui = crate::tui::Tui::new(tasks); crate::tui::Tui::run(tasks).await
tui.run().await
} }
} }
} }

View file

@ -6,6 +6,7 @@ use crossterm::{
ExecutableCommand, ExecutableCommand,
}; };
use futures::stream::StreamExt; use futures::stream::StreamExt;
use futures::FutureExt;
use ratatui::{ use ratatui::{
prelude::{CrosstermBackend, Stylize, Terminal}, prelude::{CrosstermBackend, Stylize, Terminal},
widgets::Paragraph, widgets::Paragraph,
@ -14,48 +15,71 @@ use signal_hook::consts::*;
use signal_hook_tokio::Signals; use signal_hook_tokio::Signals;
use std::io::stdout; use std::io::stdout;
pub struct Tui { pub struct Tui {}
tasks: SharedTasks,
enum Event {
CrosstermEvent(crossterm::event::Event),
// CrosstermError(Arc<dyn std::error::Error>),
Quit,
Tick,
} }
impl Tui { impl Tui {
pub fn new(tasks: SharedTasks) -> Self { pub async fn run(tasks: SharedTasks) -> Result<()> {
Self { tasks }
}
pub async fn run(&self) -> Result<()> {
self.tui().await
}
async fn tui(&self) -> Result<()> {
stdout().execute(EnterAlternateScreen)?; stdout().execute(EnterAlternateScreen)?;
enable_raw_mode()?; enable_raw_mode()?;
let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?; let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
terminal.clear()?; terminal.clear()?;
let mut signals = Signals::new(&[SIGHUP, SIGTERM, SIGINT, SIGQUIT])?;
let signalled: Arc<Mutex<bool>> = Arc::new(Mutex::new(false));
let handle = signals.handle();
let signaler = Arc::clone(&signalled); let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<Event>();
let mut signals = Signals::new(&[SIGHUP, SIGTERM, SIGINT, SIGQUIT])?;
let signal_handle = signals.handle();
let signal_sender = tx.clone();
tokio::spawn(async move { tokio::spawn(async move {
// await a signal - once we receive one mark the boolean as true to // await a signal - once we receive one send the quit event
// quit the app
signals.next().await; signals.next().await;
let mut signalled = signaler.lock().await; signal_sender.send(Event::Quit).unwrap();
*signalled = true;
}); });
// TODO: enable isig to re-allow ctrl-z?
let tick_rate = std::time::Duration::from_millis(1000);
let event_publisher_loop = tokio::spawn(async move {
let mut crossterm_event_stream = crossterm::event::EventStream::new();
let mut interval = tokio::time::interval(tick_rate);
loop { loop {
terminal.draw(|frame| { let delay = interval.tick();
let area = frame.size(); // TODO: read about FusedFutures
frame.render_widget( let crossterm_event = crossterm_event_stream.next().fuse();
Paragraph::new("Hello Ratatui! (press 'q' to quit)").white(),
area, // TODO: fix unwrap?
); tokio::select! {
})?; e = crossterm_event => {
if event::poll(std::time::Duration::from_millis(10))? { match e {
if let event::Event::Key(key) = event::read()? { Some(Ok(e)) => {
tx.send(Event::CrosstermEvent(e)).unwrap();
},
Some(Err(_e)) => {
// tx.send(Event::CrosstermError(Arc::new(e))).unwrap();
},
None => {},
}
}
_ = delay => {
tx.send(Event::Tick).unwrap();
}
}
}
});
loop {
let event = rx.recv().await;
match event {
Some(Event::CrosstermEvent(e)) => match e {
event::Event::Key(key) => {
if key.kind == KeyEventKind::Press if key.kind == KeyEventKind::Press
&& key.code == KeyCode::Char('c') && key.code == KeyCode::Char('c')
&& key.modifiers == KeyModifiers::CONTROL && key.modifiers == KeyModifiers::CONTROL
@ -66,15 +90,27 @@ impl Tui {
break; break;
} }
} }
} _ => {}
if *(*signalled).lock().await { },
break; Some(Event::Tick) => {}
} Some(Event::Quit) => break,
None => {}
} }
handle.close(); terminal.draw(|frame| {
let area = frame.size();
frame.render_widget(
Paragraph::new("Hello Ratatui! (press 'q' to quit)").white(),
area,
);
})?;
}
signal_handle.close();
stdout().execute(LeaveAlternateScreen)?; stdout().execute(LeaveAlternateScreen)?;
disable_raw_mode()?; disable_raw_mode()?;
Ok(()) Ok(())
} }
} }