Implement app components

This commit is contained in:
Shav Kinderlehrer 2024-03-06 01:11:00 -05:00
parent 022574877d
commit 557d3f32fd
7 changed files with 90 additions and 11 deletions

View File

@ -1,27 +1,77 @@
use crossterm::event::Event;
use eyre::Result; use eyre::Result;
use ratatui::prelude::*; use ratatui::prelude::*;
use std::io::Stdout;
use std::time::Duration; use std::time::Duration;
use crate::app_event::AppEvent;
use crate::component::Component;
use crate::components;
use crate::tui; use crate::tui;
pub struct App { pub struct App {
pub terminal: Terminal<CrosstermBackend<Stdout>>, pub tui: tui::Tui,
pub tick_rate: Duration, pub tick_rate: Duration,
pub components: Vec<Box<dyn Component>>,
} }
impl App { impl App {
pub fn start(tick_rate: Duration) -> Result<Self> { pub fn new(tick_rate: Duration) -> Result<Self> {
let terminal = tui::init()?; let tui = tui::init()?;
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 {
terminal, tui,
tick_rate, tick_rate,
components: vec![
Box::new(hello_world),
Box::new(hello_world1),
Box::new(hello_world2),
Box::new(hello_world3),
],
}) })
} }
pub fn run(&mut self) -> Result<()> { pub fn run(&mut self) -> Result<()> {
Ok(()) loop {
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 event.is_some() {
for component in self.components.iter_mut() {
let _ = component.handle_event(event.expect(""))?;
}
}
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]);
}
})?;
}
} }
pub fn quit(&mut self) -> Result<()> { pub fn quit(&mut self) -> Result<()> {

View File

@ -1,5 +1,6 @@
use crossterm::event::{KeyEvent, MouseEvent}; use crossterm::event::{KeyEvent, MouseEvent};
#[derive(Clone, Copy)]
pub enum AppEvent { pub enum AppEvent {
Key(KeyEvent), Key(KeyEvent),
Mouse(MouseEvent), Mouse(MouseEvent),

View File

@ -39,5 +39,5 @@ pub trait Component {
Ok(None) Ok(None)
} }
fn render(&mut self, frame: &mut Frame, rect: Rect); fn render(&mut self, frame: &mut Frame, rect: Rect) -> Result<()>;
} }

View File

@ -0,0 +1,17 @@
use ratatui::prelude::*;
use ratatui::widgets::Paragraph;
use crate::component::Component;
#[derive(Default, Clone)]
pub struct HelloWorld {
pub text: String,
}
impl Component for HelloWorld {
fn render(&mut self, frame: &mut Frame, rect: Rect) -> eyre::Result<()> {
frame.render_widget(Paragraph::new(self.text.clone()), rect);
Ok(())
}
}

1
src/components/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod hello_world;

View File

@ -2,12 +2,14 @@ mod action;
mod app; mod app;
mod app_event; mod app_event;
mod component; mod component;
mod components;
mod tui; mod tui;
use eyre::Result; use eyre::Result;
fn main() -> Result<()> { fn main() -> Result<()> {
let mut app = app::App::start(std::time::Duration::from_millis(10))?; let mut app = app::App::new(std::time::Duration::from_millis(10))?;
app.run()?;
app.quit() app.quit()
} }

View File

@ -1,7 +1,7 @@
use std::io::{self, stdout, Stdout}; use crossterm::{event, event::Event, execute, terminal::*};
use crossterm::{execute, terminal::*};
use ratatui::prelude::*; use ratatui::prelude::*;
use std::io;
use std::io::{stdout, Stdout};
pub type Tui = Terminal<CrosstermBackend<Stdout>>; pub type Tui = Terminal<CrosstermBackend<Stdout>>;
@ -18,3 +18,11 @@ pub fn restore() -> io::Result<()> {
Ok(()) Ok(())
} }
pub fn get_event(tick: std::time::Duration) -> io::Result<Option<Event>> {
if event::poll(tick)? {
return Ok(Some(event::read()?));
}
Ok(None)
}