My personal-knowledge-system, with deeply integrated task tracking and long term goal planning capabilities.
at main 203 lines 5.9 kB view raw
1use color_eyre::eyre::Result; 2use crossterm::event::KeyEvent; 3use ratatui::layout::Rect; 4use serde::{Deserialize, Serialize}; 5use strum::{Display, EnumIter}; 6use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; 7use tracing::debug; 8 9use crate::{ 10 components::Component, 11 config::Config, 12 signal::Signal, 13 tui::{Event, Tui}, 14}; 15 16pub struct App { 17 config: Config, 18 tick_rate: f64, 19 frame_rate: f64, 20 components: Vec<Box<dyn Component>>, 21 should_quit: bool, 22 should_suspend: bool, 23 #[allow(dead_code)] 24 region: Region, 25 last_tick_key_events: Vec<KeyEvent>, 26 signal_tx: UnboundedSender<Signal>, 27 signal_rx: UnboundedReceiver<Signal>, 28} 29 30/// The different regions of the application that the user can 31/// be interacting with. Think of these kind of like the highest class of 32/// components. 33#[derive( 34 Default, Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumIter, Display, 35)] 36pub enum Region { 37 #[default] 38 Home, 39} 40 41impl App { 42 /// Construct a new `App` instance. 43 pub fn new(tick_rate: f64, frame_rate: f64) -> Self { 44 let (signal_tx, signal_rx) = mpsc::unbounded_channel(); 45 46 Self { 47 tick_rate, 48 frame_rate, 49 components: vec![], 50 should_quit: false, 51 should_suspend: false, 52 config: Config::new(), 53 region: Region::default(), 54 last_tick_key_events: Vec::new(), 55 signal_tx, 56 signal_rx, 57 } 58 } 59 60 pub async fn run(&mut self) -> Result<()> { 61 let mut tui = Tui::new()? 62 .with_tick_rate(self.tick_rate) 63 .with_frame_rate(self.frame_rate); 64 tui.enter()?; 65 66 for component in &mut self.components { 67 component.register_signal_handler(self.signal_tx.clone())?; 68 } 69 for component in &mut self.components { 70 component.register_config_handler(self.config.clone())?; 71 } 72 73 for component in &mut self.components { 74 component.init(tui.size()?)?; 75 } 76 77 let signal_tx = self.signal_tx.clone(); 78 79 loop { 80 self.handle_events(&mut tui).await?; 81 self.handle_signals(&mut tui)?; 82 if self.should_suspend { 83 tui.suspend()?; 84 85 // We are sending resume here because once its done suspending, 86 // it will continue execution here. 87 signal_tx.send(Signal::Resume)?; 88 signal_tx.send(Signal::ClearScreen)?; 89 tui.enter()?; 90 } else if self.should_quit { 91 tui.stop(); 92 break; 93 } 94 } 95 96 tui.exit()?; 97 98 Ok(()) 99 } 100 101 async fn handle_events(&mut self, tui: &mut Tui) -> Result<()> { 102 let Some(event) = tui.next_event().await else { 103 return Ok(()); 104 }; 105 106 debug!("received event: {event:?}"); 107 108 let signal_tx = self.signal_tx.clone(); 109 110 match event { 111 Event::Quit => signal_tx.send(Signal::Quit)?, 112 Event::Tick => signal_tx.send(Signal::Tick)?, 113 Event::Render => signal_tx.send(Signal::Render)?, 114 Event::Resize(x, y) => signal_tx.send(Signal::Resize(x, y))?, 115 Event::Key(key) => self.handle_key_event(key)?, 116 117 _ => {} 118 } 119 120 for component in &mut self.components { 121 if let Some(signal) = component.handle_events(Some(event.clone()))? { 122 signal_tx.send(signal)?; 123 } 124 } 125 126 Ok(()) 127 } 128 129 fn handle_key_event(&mut self, key: KeyEvent) -> Result<()> { 130 debug!("key received: {key:#?}"); 131 132 let signal_tx = self.signal_tx.clone(); 133 134 let Some(region_keymap) = self.config.keymap.get(&self.region) else { 135 return Ok(()); 136 }; 137 138 if let Some(signal) = region_keymap.get(&vec![key]) { 139 signal_tx.send(signal.clone())?; 140 } else { 141 self.last_tick_key_events.push(key); 142 if let Some(signal) = region_keymap.get(&self.last_tick_key_events) { 143 debug!("Got signal: {signal:?}"); 144 signal_tx.send(signal.clone())?; 145 } 146 } 147 148 Ok(()) 149 } 150 151 fn handle_signals(&mut self, tui: &mut Tui) -> Result<()> { 152 while let Ok(signal) = self.signal_rx.try_recv() { 153 if signal != Signal::Tick && signal != Signal::Render { 154 debug!("handling signal: {signal:?}"); 155 } 156 157 match signal { 158 Signal::Tick => { 159 self.last_tick_key_events.drain(..); 160 } 161 162 Signal::Quit => self.should_quit = true, 163 164 Signal::Suspend => self.should_suspend = true, 165 166 Signal::Resume => self.should_suspend = false, 167 168 Signal::ClearScreen => tui.terminal.clear()?, 169 Signal::Resize(x, y) => self.handle_resize(tui, x, y)?, 170 Signal::Render => self.render(tui)?, 171 _ => {} 172 } 173 174 for component in &mut self.components { 175 if let Some(signal) = component.update(signal.clone())? { 176 self.signal_tx.send(signal)?; 177 } 178 } 179 } 180 Ok(()) 181 } 182 183 fn handle_resize(&mut self, tui: &mut Tui, w: u16, h: u16) -> Result<()> { 184 tui.resize(Rect::new(0, 0, w, h))?; 185 186 self.render(tui)?; 187 Ok(()) 188 } 189 190 fn render(&mut self, tui: &mut Tui) -> Result<()> { 191 tui.draw(|frame| { 192 for component in &mut self.components { 193 if let Err(err) = component.draw(frame, frame.area()) { 194 let _ = self 195 .signal_tx 196 .send(Signal::Error(format!("Failed to draw: {err:?}"))); 197 } 198 } 199 })?; 200 201 Ok(()) 202 } 203}