WIP tui code re-organization
This commit is contained in:
parent
9d61b04731
commit
b3b797f33c
4 changed files with 76 additions and 40 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
110
src/tui.rs
110
src/tui.rs
|
@ -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,39 +15,88 @@ 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;
|
});
|
||||||
|
|
||||||
|
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 {
|
||||||
|
let delay = interval.tick();
|
||||||
|
// TODO: read about FusedFutures
|
||||||
|
let crossterm_event = crossterm_event_stream.next().fuse();
|
||||||
|
|
||||||
|
// TODO: fix unwrap?
|
||||||
|
tokio::select! {
|
||||||
|
e = crossterm_event => {
|
||||||
|
match e {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// TODO: enable isig to re-allow ctrl-z?
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
let event = rx.recv().await;
|
||||||
|
|
||||||
|
match event {
|
||||||
|
Some(Event::CrosstermEvent(e)) => match e {
|
||||||
|
event::Event::Key(key) => {
|
||||||
|
if key.kind == KeyEventKind::Press
|
||||||
|
&& key.code == KeyCode::Char('c')
|
||||||
|
&& key.modifiers == KeyModifiers::CONTROL
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
Some(Event::Tick) => {}
|
||||||
|
Some(Event::Quit) => break,
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
|
||||||
terminal.draw(|frame| {
|
terminal.draw(|frame| {
|
||||||
let area = frame.size();
|
let area = frame.size();
|
||||||
frame.render_widget(
|
frame.render_widget(
|
||||||
|
@ -54,27 +104,13 @@ impl Tui {
|
||||||
area,
|
area,
|
||||||
);
|
);
|
||||||
})?;
|
})?;
|
||||||
if event::poll(std::time::Duration::from_millis(10))? {
|
|
||||||
if let event::Event::Key(key) = event::read()? {
|
|
||||||
if key.kind == KeyEventKind::Press
|
|
||||||
&& key.code == KeyCode::Char('c')
|
|
||||||
&& key.modifiers == KeyModifiers::CONTROL
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if *(*signalled).lock().await {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handle.close();
|
signal_handle.close();
|
||||||
|
|
||||||
stdout().execute(LeaveAlternateScreen)?;
|
stdout().execute(LeaveAlternateScreen)?;
|
||||||
disable_raw_mode()?;
|
disable_raw_mode()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue