pub mod error;
use crate::error::RextTuiError;
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
use ratatui::{
DefaultTerminal, Frame,
style::Stylize,
symbols::border,
text::{Line, Text},
widgets::{Block, Paragraph},
};
#[derive(Debug, Default)]
pub struct App {
pub running: bool,
pub counter: u8,
}
impl App {
pub fn new() -> Self {
Self::default()
}
fn increment_counter(&mut self) {
self.counter += 1;
}
fn decrement_counter(&mut self) {
self.counter -= 1;
}
pub fn run(mut self, mut terminal: DefaultTerminal) -> Result<(), RextTuiError> {
self.running = true;
while self.running {
terminal.draw(|frame| self.render(frame))?;
self.handle_crossterm_events()?;
}
Ok(())
}
fn render(&mut self, frame: &mut Frame) {
let title = Line::from("Rext").bold().yellow().centered();
let instructions = Line::from(vec![
" Decrement ".into(),
"<Left>".bold().blue(),
" Increment ".into(),
"<Right>".bold().blue(),
" Quit ".into(),
"<Esc>".bold().blue(),
]);
let block = Block::bordered()
.title(title.centered())
.title_bottom(instructions.centered())
.border_set(border::THICK);
let text = Text::from(vec![Line::from(vec![
"Value: ".into(),
self.counter.to_string().yellow(),
])]);
frame.render_widget(Paragraph::new(text).block(block).centered(), frame.area());
}
fn handle_crossterm_events(&mut self) -> Result<(), RextTuiError> {
match event::read()? {
Event::Key(key) if key.kind == KeyEventKind::Press => self.on_key_event(key),
Event::Mouse(_) => {}
Event::Resize(_, _) => {}
_ => {}
}
Ok(())
}
pub fn on_key_event(&mut self, key: KeyEvent) {
match (key.modifiers, key.code) {
(_, KeyCode::Esc | KeyCode::Char('q'))
| (KeyModifiers::CONTROL, KeyCode::Char('c') | KeyCode::Char('C')) => self.quit(),
(_, KeyCode::Left) => self.decrement_counter(),
(_, KeyCode::Right) => self.increment_counter(),
_ => {}
}
}
fn quit(&mut self) {
self.running = false;
}
}