diff --git a/src/app.rs b/src/app.rs index 951ca7b..4602c9a 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,8 +1,37 @@ +/// Rendering thread Event thread Main thread +/// +/// ┌─────────────────┐ +/// │ get key event │ +/// └─────────────────┘ +/// │ +/// ▼ +/// ┌─────────────────┐ +/// │ map event to │ +/// │ action │ +/// └─────────────────┘ +/// │ +/// ▼ +/// ┌──────────────────────────┐ ┌─────────────────┐ +/// │ send action on action_tx │ ─────▶ │ Recv action │ +/// └──────────────────────────┘ └─────────────────┘ +/// │ +/// ▼ +/// ┌───────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +/// │ Recv on render_rx │ ◀────────────────────────────────────── │ Dispatch action │ ───────── │ dispatch action │ +/// └───────────────────┘ └─────────────────┘ └─────────────────┘ +/// │ │ +/// ▼ ▼ +/// ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ +/// │ Render component │ │ Update component │ │ update component │ +/// └──────────────────┘ └──────────────────┘ └──────────────────┘ +/// +use std::sync::Arc; + use color_eyre::Result; use crossterm::event::KeyEvent; use ratatui::prelude::Rect; use serde::{Deserialize, Serialize}; -use tokio::sync::mpsc; +use tokio::sync::{mpsc, Mutex}; use tracing::{debug, info}; use crate::{ @@ -16,7 +45,7 @@ pub struct App { config: Config, tick_rate: f64, frame_rate: f64, - components: Vec>, + components: Arc>>>, should_quit: bool, should_suspend: bool, mode: Mode, @@ -37,7 +66,10 @@ impl App { Ok(Self { tick_rate, frame_rate, - components: vec![Box::new(Home::new()), Box::new(FpsCounter::default())], + components: Arc::new(Mutex::new(vec![ + Box::new(Home::new()), + Box::new(FpsCounter::default()), + ])), should_quit: false, should_suspend: false, config: Config::new()?, @@ -49,22 +81,48 @@ impl App { } pub async fn run(&mut self) -> Result<()> { - let mut tui = Tui::new()? - // .mouse(true) // uncomment this line to enable mouse support - .tick_rate(self.tick_rate) - .frame_rate(self.frame_rate); - tui.enter()?; + // Rendering loop + let (render_tx, mut render_rx) = mpsc::unbounded_channel(); - for component in self.components.iter_mut() { - component.register_action_handler(self.action_tx.clone())?; - } - for component in self.components.iter_mut() { - component.register_config_handler(self.config.clone())?; - } - for component in self.components.iter_mut() { - component.init(tui.size()?)?; - } + tokio::spawn(async move { + let tui = Tui::new()? + .tick_rate(self.tick_rate) + .frame_rate(self.frame_rate); + tui.enter(); + + let components = self.components.clone(); + for component in self.components.lock().await.iter_mut() { + component.register_action_handler(self.action_tx.clone())?; + } + for component in self.components.lock().await.iter_mut() { + component.register_config_handler(self.config.clone())?; + } + for component in self.components.lock().await.iter_mut() { + component.init(tui.size()?)?; + } + + loop { + // add + if let Some(_) = render_rx.recv().await { + let c = components.lock().await; + tui.terminal.draw(|frame| { + for component in c.iter() { + if let Err(err) = component.draw(frame, frame.area()) { + let _ = self + .action_tx + .send(Action::Error(format!("Failed to draw: {:?}", err))); + } + } + })?; + } + } + + tui.exit() + }); + + // Event handling loop + // Action handling loop let action_tx = self.action_tx.clone(); loop { self.handle_events(&mut tui).await?; @@ -80,7 +138,6 @@ impl App { break; } } - tui.exit()?; Ok(()) }