footer (#3)
This commit is contained in:
parent
b1e9b027a2
commit
a9e573122d
47
src/app.rs
47
src/app.rs
@ -2,40 +2,47 @@ use color_eyre::{eyre::Context, Result};
|
|||||||
use crossterm::event;
|
use crossterm::event;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
buffer::Buffer,
|
buffer::Buffer,
|
||||||
crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind},
|
crossterm::event::{Event, KeyCode, KeyEventKind},
|
||||||
layout::{Constraint, Layout, Rect},
|
layout::{Constraint, Layout, Rect},
|
||||||
widgets::{Block, Paragraph, Widget},
|
widgets::{Block, Paragraph, Widget},
|
||||||
DefaultTerminal, Frame,
|
DefaultTerminal, Frame,
|
||||||
};
|
};
|
||||||
use std::time::Duration;
|
|
||||||
use strum::{Display, EnumIter, FromRepr};
|
use strum::{Display, EnumIter, FromRepr};
|
||||||
|
|
||||||
|
use crate::footer::Footer;
|
||||||
use crate::pomodoro::Pomodoro;
|
use crate::pomodoro::Pomodoro;
|
||||||
use crate::timer::Timer;
|
use crate::timer::Timer;
|
||||||
use crate::{countdown::Countdown, utils::center};
|
use crate::{countdown::Countdown, utils::center};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
enum Mode {
|
enum Mode {
|
||||||
#[default]
|
|
||||||
Running,
|
Running,
|
||||||
Quit,
|
Quit,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default, Display, EnumIter, FromRepr, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, Display, EnumIter, FromRepr, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
enum Content {
|
pub enum Content {
|
||||||
#[default]
|
|
||||||
Countdown,
|
Countdown,
|
||||||
Timer,
|
Timer,
|
||||||
Pomodoro,
|
Pomodoro,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct App {
|
pub struct App {
|
||||||
content: Content,
|
content: Content,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
|
show_menu: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
content: Content::Countdown,
|
||||||
|
mode: Mode::Running,
|
||||||
|
show_menu: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> {
|
pub fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> {
|
||||||
while self.is_running() {
|
while self.is_running() {
|
||||||
terminal
|
terminal
|
||||||
@ -56,41 +63,36 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_events(&mut self) -> Result<()> {
|
fn handle_events(&mut self) -> Result<()> {
|
||||||
let timeout = Duration::from_secs_f64(1.0 / 50.0);
|
if let Event::Key(key) = event::read()? {
|
||||||
if !event::poll(timeout)? {
|
if key.kind != KeyEventKind::Press {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
match event::read()? {
|
|
||||||
Event::Key(key) if key.kind == KeyEventKind::Press => self.handle_key_press(key),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_key_press(&mut self, key: KeyEvent) {
|
|
||||||
match key.code {
|
match key.code {
|
||||||
KeyCode::Char('q') | KeyCode::Esc => self.mode = Mode::Quit,
|
KeyCode::Char('q') | KeyCode::Esc => self.mode = Mode::Quit,
|
||||||
KeyCode::Char('c') => self.content = Content::Countdown,
|
KeyCode::Char('c') => self.content = Content::Countdown,
|
||||||
KeyCode::Char('t') => self.content = Content::Timer,
|
KeyCode::Char('t') => self.content = Content::Timer,
|
||||||
KeyCode::Char('p') => self.content = Content::Pomodoro,
|
KeyCode::Char('p') => self.content = Content::Pomodoro,
|
||||||
|
KeyCode::Char('m') => self.show_menu = !self.show_menu,
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for &App {
|
impl Widget for &App {
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
let vertical = Layout::vertical([
|
let vertical = Layout::vertical([
|
||||||
Constraint::Length(1),
|
Constraint::Length(1),
|
||||||
Constraint::Min(0),
|
Constraint::Fill(0),
|
||||||
Constraint::Length(1),
|
Constraint::Length(if self.show_menu { 2 } else { 1 }),
|
||||||
]);
|
]);
|
||||||
let [header_area, content_area, footer_area] = vertical.areas(area);
|
let [header_area, content_area, footer_area] = vertical.areas(area);
|
||||||
|
|
||||||
Block::new().render(area, buf);
|
Block::new().render(area, buf);
|
||||||
self.render_header(header_area, buf);
|
self.render_header(header_area, buf);
|
||||||
self.render_content(content_area, buf);
|
self.render_content(content_area, buf);
|
||||||
self.render_footer(footer_area, buf);
|
Footer::new(self.show_menu, self.content).render(footer_area, buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,9 +100,6 @@ impl App {
|
|||||||
fn render_header(&self, area: Rect, buf: &mut Buffer) {
|
fn render_header(&self, area: Rect, buf: &mut Buffer) {
|
||||||
Paragraph::new("tim:r").render(area, buf);
|
Paragraph::new("tim:r").render(area, buf);
|
||||||
}
|
}
|
||||||
fn render_footer(&self, area: Rect, buf: &mut Buffer) {
|
|
||||||
Paragraph::new("footer").render(area, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_content(&self, area: Rect, buf: &mut Buffer) {
|
fn render_content(&self, area: Rect, buf: &mut Buffer) {
|
||||||
// center content
|
// center content
|
||||||
|
|||||||
71
src/footer.rs
Normal file
71
src/footer.rs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use crate::app::Content;
|
||||||
|
use ratatui::{
|
||||||
|
buffer::Buffer,
|
||||||
|
layout::{Constraint, Layout, Rect},
|
||||||
|
style::{Modifier, Style},
|
||||||
|
symbols,
|
||||||
|
text::{Line, Span},
|
||||||
|
widgets::{Block, Borders, Widget},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Footer {
|
||||||
|
show_menu: bool,
|
||||||
|
selected_content: Content,
|
||||||
|
content_labels: BTreeMap<Content, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Footer {
|
||||||
|
pub fn new(show_menu: bool, selected_content: Content) -> Self {
|
||||||
|
Self {
|
||||||
|
show_menu,
|
||||||
|
selected_content,
|
||||||
|
content_labels: BTreeMap::from([
|
||||||
|
(Content::Countdown, "[c]ountdown".into()),
|
||||||
|
(Content::Timer, "[t]imer".into()),
|
||||||
|
(Content::Pomodoro, "[p]omodoro".into()),
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for Footer {
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
let [border_area, menu_area] =
|
||||||
|
Layout::vertical([Constraint::Length(1), Constraint::Fill(0)]).areas(area);
|
||||||
|
Block::new()
|
||||||
|
.borders(Borders::TOP)
|
||||||
|
.title(format! {"[m]enu {:} ", if self.show_menu {"↓"} else {"↑"}})
|
||||||
|
.border_set(symbols::border::DOUBLE)
|
||||||
|
.render(border_area, buf);
|
||||||
|
// show menu
|
||||||
|
if self.show_menu {
|
||||||
|
let [title_area, labels_area] =
|
||||||
|
Layout::horizontal([Constraint::Length(12), Constraint::Fill(0)]).areas(menu_area);
|
||||||
|
|
||||||
|
Span::styled("screens", Style::default().add_modifier(Modifier::BOLD))
|
||||||
|
.render(title_area, buf);
|
||||||
|
let spans: Vec<Span> = self
|
||||||
|
.content_labels
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(index, (content, label))| {
|
||||||
|
let mut style = Style::default();
|
||||||
|
// Add space for all except last
|
||||||
|
let label = if index < self.content_labels.len() - 1 {
|
||||||
|
format!("{} ", label)
|
||||||
|
} else {
|
||||||
|
label.into()
|
||||||
|
};
|
||||||
|
if *content == self.selected_content {
|
||||||
|
style = style.add_modifier(Modifier::BOLD);
|
||||||
|
}
|
||||||
|
Span::styled(label, style)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Line::from(spans).render(labels_area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
mod app;
|
mod app;
|
||||||
mod countdown;
|
mod countdown;
|
||||||
|
mod footer;
|
||||||
mod pomodoro;
|
mod pomodoro;
|
||||||
mod timer;
|
mod timer;
|
||||||
mod utils;
|
mod utils;
|
||||||
@ -10,7 +11,7 @@ use color_eyre::{eyre::Context, Result};
|
|||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
let terminal = ratatui::init();
|
let terminal = ratatui::init();
|
||||||
let app_result = App::default().run(terminal).context("app loop failed");
|
let app_result = App::new().run(terminal).context("app loop failed");
|
||||||
ratatui::restore();
|
ratatui::restore();
|
||||||
app_result
|
app_result
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user