mod app;
pub mod input;
pub mod state;
pub mod widgets;
use anyhow::Result;
use app::App;
use ralph_proto::{Event, HatId};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use tokio::sync::watch;
pub use app::dispatch_action;
pub use state::TuiState;
pub use widgets::{footer, header};
pub struct Tui {
state: Arc<Mutex<TuiState>>,
terminated_rx: Option<watch::Receiver<bool>>,
interrupt_tx: Option<watch::Sender<bool>>,
}
impl Tui {
pub fn new() -> Self {
Self {
state: Arc::new(Mutex::new(TuiState::new())),
terminated_rx: None,
interrupt_tx: None,
}
}
#[must_use]
pub fn with_hat_map(self, hat_map: HashMap<String, (HatId, String)>) -> Self {
if let Ok(mut state) = self.state.lock() {
*state = TuiState::with_hat_map(hat_map);
}
self
}
#[must_use]
pub fn with_termination_signal(mut self, terminated_rx: watch::Receiver<bool>) -> Self {
self.terminated_rx = Some(terminated_rx);
self
}
#[must_use]
pub fn with_interrupt_tx(mut self, interrupt_tx: watch::Sender<bool>) -> Self {
self.interrupt_tx = Some(interrupt_tx);
self
}
pub fn state(&self) -> Arc<Mutex<TuiState>> {
Arc::clone(&self.state)
}
pub fn observer(&self) -> impl Fn(&Event) + Send + 'static {
let state = Arc::clone(&self.state);
move |event: &Event| {
if let Ok(mut s) = state.lock() {
s.update(event);
}
}
}
pub async fn run(self) -> Result<()> {
let terminated_rx = self
.terminated_rx
.expect("Termination signal not set - call with_termination_signal() first");
let app = App::new(Arc::clone(&self.state), terminated_rx, self.interrupt_tx);
app.run().await
}
}
impl Default for Tui {
fn default() -> Self {
Self::new()
}
}