Event handling (#5)
- Refactor `event` handling (heavily inspired by [crates-tui](https://github.com/ratatui/crates-tui/) via [Tui with Terminal and EventHandler](https://ratatui.rs/recipes/apps/terminal-and-event-handler/)) - Refactor widget structure - Disable `nixos-unstable` temporarily - Add `.rustfmt.toml`
This commit is contained in:
121
src/app.rs
121
src/app.rs
@@ -1,47 +1,70 @@
|
||||
use color_eyre::{eyre::Context, Result};
|
||||
use crossterm::event;
|
||||
use crate::{
|
||||
events::{Event, Events},
|
||||
terminal::Terminal,
|
||||
utils::center,
|
||||
widgets::{
|
||||
countdown::Countdown, footer::Footer, header::Header, pomodoro::Pomodoro, timer::Timer,
|
||||
},
|
||||
};
|
||||
use color_eyre::Result;
|
||||
use ratatui::{
|
||||
buffer::Buffer,
|
||||
crossterm::event::{Event, KeyCode, KeyEventKind},
|
||||
crossterm::event::{KeyCode, KeyEvent},
|
||||
layout::{Constraint, Layout, Rect},
|
||||
widgets::{Block, Paragraph, Widget},
|
||||
DefaultTerminal, Frame,
|
||||
widgets::{Block, Widget},
|
||||
};
|
||||
|
||||
use crate::footer::Footer;
|
||||
use crate::pomodoro::Pomodoro;
|
||||
use crate::timer::Timer;
|
||||
use crate::{countdown::Countdown, utils::center};
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Mode {
|
||||
#[default]
|
||||
Running,
|
||||
Quit,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Content {
|
||||
#[default]
|
||||
Countdown,
|
||||
Timer,
|
||||
Pomodoro,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug)]
|
||||
pub struct App {
|
||||
content: Content,
|
||||
mode: Mode,
|
||||
show_menu: bool,
|
||||
tick: u128,
|
||||
}
|
||||
|
||||
impl Default for App {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
mode: Mode::Running,
|
||||
content: Content::Countdown,
|
||||
show_menu: false,
|
||||
tick: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub async fn run(&mut self, mut terminal: Terminal, mut events: Events) -> Result<()> {
|
||||
while self.is_running() {
|
||||
terminal
|
||||
.draw(|frame| self.draw(frame))
|
||||
.wrap_err("terminal.draw")?;
|
||||
self.handle_events()?;
|
||||
if let Some(event) = events.next().await {
|
||||
match event {
|
||||
Event::Render | Event::Resize(_, _) => {
|
||||
self.draw(&mut terminal)?;
|
||||
}
|
||||
Event::Tick => {
|
||||
self.tick = self.tick.saturating_add(1);
|
||||
}
|
||||
Event::Key(key) => self.handle_key_event(key),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -50,27 +73,33 @@ impl App {
|
||||
self.mode != Mode::Quit
|
||||
}
|
||||
|
||||
/// Draw a single frame of the app.
|
||||
fn draw(&self, frame: &mut Frame) {
|
||||
frame.render_widget(self, frame.area());
|
||||
fn handle_key_event(&mut self, key: KeyEvent) {
|
||||
match key.code {
|
||||
KeyCode::Char('q') | KeyCode::Esc => self.mode = Mode::Quit,
|
||||
KeyCode::Char('c') => self.content = Content::Countdown,
|
||||
KeyCode::Char('t') => self.content = Content::Timer,
|
||||
KeyCode::Char('p') => self.content = Content::Pomodoro,
|
||||
KeyCode::Char('m') => self.show_menu = !self.show_menu,
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
fn handle_events(&mut self) -> Result<()> {
|
||||
if let Event::Key(key) = event::read()? {
|
||||
if key.kind != KeyEventKind::Press {
|
||||
return Ok(());
|
||||
}
|
||||
match key.code {
|
||||
KeyCode::Char('q') | KeyCode::Esc => self.mode = Mode::Quit,
|
||||
KeyCode::Char('c') => self.content = Content::Countdown,
|
||||
KeyCode::Char('t') => self.content = Content::Timer,
|
||||
KeyCode::Char('p') => self.content = Content::Pomodoro,
|
||||
KeyCode::Char('m') => self.show_menu = !self.show_menu,
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
fn draw(&self, terminal: &mut Terminal) -> Result<()> {
|
||||
terminal.draw(|frame| {
|
||||
frame.render_widget(self, frame.area());
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render_content(&self, area: Rect, buf: &mut Buffer) {
|
||||
// center content
|
||||
let area = center(area, Constraint::Length(50), Constraint::Length(1));
|
||||
match self.content {
|
||||
Content::Timer => Timer::new(200, "Timer".into()).render(area, buf),
|
||||
Content::Countdown => Countdown::new("Countdown".into()).render(area, buf),
|
||||
Content::Pomodoro => Pomodoro::new("Pomodoro".into()).render(area, buf),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for &App {
|
||||
@@ -83,24 +112,8 @@ impl Widget for &App {
|
||||
let [header_area, content_area, footer_area] = vertical.areas(area);
|
||||
|
||||
Block::new().render(area, buf);
|
||||
self.render_header(header_area, buf);
|
||||
Header::new(self.tick).render(header_area, buf);
|
||||
self.render_content(content_area, buf);
|
||||
Footer::new(self.show_menu, self.content).render(footer_area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn render_header(&self, area: Rect, buf: &mut Buffer) {
|
||||
Paragraph::new("tim:r").render(area, buf);
|
||||
}
|
||||
|
||||
fn render_content(&self, area: Rect, buf: &mut Buffer) {
|
||||
// center content
|
||||
let area = center(area, Constraint::Length(50), Constraint::Length(1));
|
||||
match self.content {
|
||||
Content::Timer => Timer::new(200, "Timer".into()).render(area, buf),
|
||||
Content::Countdown => Countdown::new("Countdown".into()).render(area, buf),
|
||||
Content::Pomodoro => Pomodoro::new("Pomodoro".into()).render(area, buf),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user