diff --git a/src/action.rs b/src/action.rs deleted file mode 100644 index 4272dea..0000000 --- a/src/action.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub enum Action { -} diff --git a/src/app.rs b/src/app.rs index b894f1c..22a8c40 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,8 +1,8 @@ use crossterm::event::Event; use eyre::Result; -use ratatui::prelude::*; use std::time::Duration; +use crate::app_action::AppAction; use crate::app_event::AppEvent; use crate::component::Component; use crate::components; @@ -12,71 +12,92 @@ pub struct App { pub tui: tui::Tui, pub tick_rate: Duration, pub components: Vec>, + + should_quit: bool, } impl App { pub fn new(tick_rate: Duration) -> Result { let tui = tui::init()?; + let global_keys = components::global_keys::GlobalKeys::default(); let hello_world = components::hello_world::HelloWorld::default(); - let hello_world1 = components::hello_world::HelloWorld::default(); - let hello_world2 = components::hello_world::HelloWorld::default(); - let hello_world3 = components::hello_world::HelloWorld::default(); Ok(Self { tui, tick_rate, - components: vec![ - Box::new(hello_world), - Box::new(hello_world1), - Box::new(hello_world2), - Box::new(hello_world3), - ], + components: vec![Box::new(hello_world), Box::new(global_keys)], + should_quit: false, }) } pub fn run(&mut self) -> Result<()> { - loop { - let event: Option = match tui::get_event(self.tick_rate)? - { - Some(event) => match event { - Event::Key(key) => Some(AppEvent::Key(key)), - Event::Mouse(mouse) => Some(AppEvent::Mouse(mouse)), - Event::FocusGained => todo!(), - Event::FocusLost => todo!(), - Event::Paste(_) => todo!(), - Event::Resize(_, _) => todo!(), - }, - None => None, - }; + for component in self.components.iter_mut() { + component.init()?; + } - if event.is_some() { - for component in self.components.iter_mut() { - let _ = component.handle_event(event.expect(""))?; + loop { + if self.should_quit { + break Ok(()); + } + + self.draw()?; + } + } + + pub fn draw(&mut self) -> Result<()> { + let event: Option = match tui::get_event(self.tick_rate)? { + Some(event) => match event { + Event::Key(key) => Some(AppEvent::Key(key)), + Event::Mouse(mouse) => Some(AppEvent::Mouse(mouse)), + Event::FocusGained => todo!(), + Event::FocusLost => todo!(), + Event::Paste(_) => todo!(), + Event::Resize(_, _) => todo!(), + }, + None => None, + }; + + if let Some(event) = event { + let mut actions: Vec = vec![]; + for component in self.components.iter_mut() { + match component.handle_event(event)? { + Some(action) => actions.push(action), + None => (), } } - self.tui.draw(|frame| { - let layout = Layout::default() - .direction(Direction::Vertical) - .constraints([ - Constraint::Percentage(25), - Constraint::Percentage(25), - Constraint::Percentage(25), - Constraint::Percentage(25), - ]) - .split(frame.size()); - - for (i, component) in self.components.iter_mut().enumerate() { - let _ = component.render(frame, layout[i]); - } - })?; + for action in actions { + self.handle_action(action)?; + } } + + if self.should_quit { + return Ok(()); + } + + self.tui.draw(|frame| { + for (_i, component) in self.components.iter_mut().enumerate() { + match component.render(frame, frame.size()) { + Ok(_) => (), + Err(_) => (), + } + } + })?; + + Ok(()) } pub fn quit(&mut self) -> Result<()> { tui::restore()?; + self.should_quit = true; Ok(()) } + + fn handle_action(&mut self, action: AppAction) -> Result<()> { + match action { + AppAction::Quit => Ok(self.quit()?), + } + } } diff --git a/src/app_action.rs b/src/app_action.rs new file mode 100644 index 0000000..104e4f9 --- /dev/null +++ b/src/app_action.rs @@ -0,0 +1,3 @@ +pub enum AppAction { + Quit +} diff --git a/src/component.rs b/src/component.rs index d8ca48f..8ec7d26 100644 --- a/src/component.rs +++ b/src/component.rs @@ -2,7 +2,7 @@ use crossterm::event::{KeyEvent, MouseEvent}; use eyre::Result; use ratatui::prelude::{Frame, Rect}; -use crate::action::Action; +use crate::app_action::AppAction; use crate::app_event::AppEvent; pub trait Component { @@ -11,7 +11,7 @@ pub trait Component { } #[allow(unused)] - fn handle_event(&mut self, event: AppEvent) -> Result> { + fn handle_event(&mut self, event: AppEvent) -> Result> { match event { AppEvent::Key(key_event) => Ok(self.handle_key_event(key_event)?), AppEvent::Mouse(mouse_event) => { @@ -22,7 +22,7 @@ pub trait Component { } #[allow(unused)] - fn handle_key_event(&mut self, key: KeyEvent) -> Result> { + fn handle_key_event(&mut self, key: KeyEvent) -> Result> { Ok(None) } @@ -30,12 +30,12 @@ pub trait Component { fn handle_mouse_event( &mut self, mouse: MouseEvent, - ) -> Result> { + ) -> Result> { Ok(None) } #[allow(unused)] - fn update(&mut self, action: Action) -> Result> { + fn update(&mut self, action: AppAction) -> Result> { Ok(None) } diff --git a/src/components/global_keys.rs b/src/components/global_keys.rs new file mode 100644 index 0000000..e99e2e0 --- /dev/null +++ b/src/components/global_keys.rs @@ -0,0 +1,46 @@ +use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; +use ratatui::prelude::*; +use ratatui::widgets::*; + +use crate::app_action::AppAction; +use crate::component::Component; + +#[derive(Default, Clone, Copy)] +pub struct GlobalKeys { + should_show: bool, +} + +impl Component for GlobalKeys { + fn handle_key_event( + &mut self, + key: KeyEvent, + ) -> eyre::Result> { + if key.kind == KeyEventKind::Press { + return match key.code { + KeyCode::Char('q') => Ok(Some(AppAction::Quit)), + KeyCode::Char('?') => { + self.should_show = !self.should_show; + Ok(None) + } + _ => Ok(None), + }; + } + + Ok(None) + } + + fn render(&mut self, frame: &mut Frame, rect: Rect) -> eyre::Result<()> { + let horizontal_center = Layout::default() + .direction(Direction::Horizontal); + + let block = Block::default() + .title("Keyboard shortcuts") + .borders(Borders::ALL); + + if self.should_show { + frame.render_widget(block, rect); + } + + Ok(()) + } +} diff --git a/src/components/hello_world.rs b/src/components/hello_world.rs index 22c966c..afb9d47 100644 --- a/src/components/hello_world.rs +++ b/src/components/hello_world.rs @@ -9,6 +9,11 @@ pub struct HelloWorld { } impl Component for HelloWorld { + fn init(&mut self) -> eyre::Result<()> { + self.text = "Hello, world!".to_string(); + Ok(()) + } + fn render(&mut self, frame: &mut Frame, rect: Rect) -> eyre::Result<()> { frame.render_widget(Paragraph::new(self.text.clone()), rect); diff --git a/src/components/mod.rs b/src/components/mod.rs index c6dbc18..78abbca 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1 +1,2 @@ pub mod hello_world; +pub mod global_keys; diff --git a/src/main.rs b/src/main.rs index 29c6439..16bee88 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ -mod action; mod app; +mod app_action; mod app_event; mod component; mod components;