Fix event handling

This commit is contained in:
Shav Kinderlehrer 2024-03-06 11:45:18 -05:00
parent 557d3f32fd
commit a5dbccee4f
8 changed files with 123 additions and 49 deletions

View File

@ -1,2 +0,0 @@
pub enum Action {
}

View File

@ -1,8 +1,8 @@
use crossterm::event::Event; use crossterm::event::Event;
use eyre::Result; use eyre::Result;
use ratatui::prelude::*;
use std::time::Duration; use std::time::Duration;
use crate::app_action::AppAction;
use crate::app_event::AppEvent; use crate::app_event::AppEvent;
use crate::component::Component; use crate::component::Component;
use crate::components; use crate::components;
@ -12,71 +12,92 @@ pub struct App {
pub tui: tui::Tui, pub tui: tui::Tui,
pub tick_rate: Duration, pub tick_rate: Duration,
pub components: Vec<Box<dyn Component>>, pub components: Vec<Box<dyn Component>>,
should_quit: bool,
} }
impl App { impl App {
pub fn new(tick_rate: Duration) -> Result<Self> { pub fn new(tick_rate: Duration) -> Result<Self> {
let tui = tui::init()?; let tui = tui::init()?;
let global_keys = components::global_keys::GlobalKeys::default();
let hello_world = components::hello_world::HelloWorld::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 { Ok(Self {
tui, tui,
tick_rate, tick_rate,
components: vec![ components: vec![Box::new(hello_world), Box::new(global_keys)],
Box::new(hello_world), should_quit: false,
Box::new(hello_world1),
Box::new(hello_world2),
Box::new(hello_world3),
],
}) })
} }
pub fn run(&mut self) -> Result<()> { pub fn run(&mut self) -> Result<()> {
loop { for component in self.components.iter_mut() {
let event: Option<AppEvent> = match tui::get_event(self.tick_rate)? component.init()?;
{ }
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 event.is_some() { loop {
for component in self.components.iter_mut() { if self.should_quit {
let _ = component.handle_event(event.expect(""))?; break Ok(());
}
self.draw()?;
}
}
pub fn draw(&mut self) -> Result<()> {
let event: Option<AppEvent> = 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<AppAction> = vec![];
for component in self.components.iter_mut() {
match component.handle_event(event)? {
Some(action) => actions.push(action),
None => (),
} }
} }
self.tui.draw(|frame| { for action in actions {
let layout = Layout::default() self.handle_action(action)?;
.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]);
}
})?;
} }
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<()> { pub fn quit(&mut self) -> Result<()> {
tui::restore()?; tui::restore()?;
self.should_quit = true;
Ok(()) Ok(())
} }
fn handle_action(&mut self, action: AppAction) -> Result<()> {
match action {
AppAction::Quit => Ok(self.quit()?),
}
}
} }

3
src/app_action.rs Normal file
View File

@ -0,0 +1,3 @@
pub enum AppAction {
Quit
}

View File

@ -2,7 +2,7 @@ use crossterm::event::{KeyEvent, MouseEvent};
use eyre::Result; use eyre::Result;
use ratatui::prelude::{Frame, Rect}; use ratatui::prelude::{Frame, Rect};
use crate::action::Action; use crate::app_action::AppAction;
use crate::app_event::AppEvent; use crate::app_event::AppEvent;
pub trait Component { pub trait Component {
@ -11,7 +11,7 @@ pub trait Component {
} }
#[allow(unused)] #[allow(unused)]
fn handle_event(&mut self, event: AppEvent) -> Result<Option<Action>> { fn handle_event(&mut self, event: AppEvent) -> Result<Option<AppAction>> {
match event { match event {
AppEvent::Key(key_event) => Ok(self.handle_key_event(key_event)?), AppEvent::Key(key_event) => Ok(self.handle_key_event(key_event)?),
AppEvent::Mouse(mouse_event) => { AppEvent::Mouse(mouse_event) => {
@ -22,7 +22,7 @@ pub trait Component {
} }
#[allow(unused)] #[allow(unused)]
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> { fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<AppAction>> {
Ok(None) Ok(None)
} }
@ -30,12 +30,12 @@ pub trait Component {
fn handle_mouse_event( fn handle_mouse_event(
&mut self, &mut self,
mouse: MouseEvent, mouse: MouseEvent,
) -> Result<Option<Action>> { ) -> Result<Option<AppAction>> {
Ok(None) Ok(None)
} }
#[allow(unused)] #[allow(unused)]
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: AppAction) -> Result<Option<AppAction>> {
Ok(None) Ok(None)
} }

View File

@ -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<Option<AppAction>> {
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(())
}
}

View File

@ -9,6 +9,11 @@ pub struct HelloWorld {
} }
impl Component for 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<()> { fn render(&mut self, frame: &mut Frame, rect: Rect) -> eyre::Result<()> {
frame.render_widget(Paragraph::new(self.text.clone()), rect); frame.render_widget(Paragraph::new(self.text.clone()), rect);

View File

@ -1 +1,2 @@
pub mod hello_world; pub mod hello_world;
pub mod global_keys;

View File

@ -1,5 +1,5 @@
mod action;
mod app; mod app;
mod app_action;
mod app_event; mod app_event;
mod component; mod component;
mod components; mod components;