feat(countdown): rocket countdown (#45)

This commit is contained in:
Jens Krause
2025-01-08 18:52:18 +01:00
committed by GitHub
parent 468b4a5abf
commit c8af76c9e5
7 changed files with 111 additions and 11 deletions

View File

@@ -179,7 +179,7 @@ impl App {
fn clock_is_running(&self) -> bool {
match self.content {
Content::Countdown => self.countdown.get_clock().is_running(),
Content::Countdown => self.countdown.is_running(),
Content::Timer => self.timer.get_clock().is_running(),
Content::Pomodoro => self.pomodoro.get_clock().is_running(),
}

View File

@@ -86,6 +86,10 @@ impl DurationEx {
let inner = self.inner.saturating_sub(ex.inner);
Self { inner }
}
pub fn to_string_with_decis(self) -> String {
format!("{}.{}", self, self.decis())
}
}
impl fmt::Display for DurationEx {

View File

@@ -207,10 +207,18 @@ impl<T> ClockState<T> {
&self.mode
}
pub fn run(&mut self) {
self.mode = Mode::Tick
}
pub fn is_running(&self) -> bool {
self.mode == Mode::Tick
}
pub fn is_initial(&self) -> bool {
self.mode == Mode::Initial
}
pub fn is_edit_mode(&self) -> bool {
matches!(self.mode, Mode::Editable(_, _))
}
@@ -294,6 +302,10 @@ impl<T> ClockState<T> {
self.update_format();
}
pub fn is_done(&self) -> bool {
self.mode == Mode::Done
}
fn update_format(&mut self) {
self.format = self.get_format();
}

View File

@@ -5,32 +5,50 @@ use ratatui::{
text::Line,
widgets::{StatefulWidget, Widget},
};
use std::cmp::max;
use std::{cmp::max, time::Duration};
use crate::{
common::Style,
constants::TICK_VALUE_MS,
events::{Event, EventHandler},
utils::center,
widgets::clock::{self, ClockState, ClockWidget},
widgets::clock::{self, ClockState, ClockStateArgs, ClockWidget},
};
/// State for Countdown Widget
#[derive(Debug, Clone)]
pub struct CountdownState {
/// clock to count down
clock: ClockState<clock::Countdown>,
/// clock to count up afterwards
timer: ClockState<clock::Timer>,
}
impl CountdownState {
pub const fn new(clock: ClockState<clock::Countdown>) -> Self {
Self { clock }
pub fn new(clock: ClockState<clock::Countdown>) -> Self {
Self {
clock,
timer: ClockState::<clock::Timer>::new(ClockStateArgs {
initial_value: Duration::ZERO,
current_value: Duration::ZERO,
tick_value: Duration::from_millis(TICK_VALUE_MS),
with_decis: false,
}),
}
}
pub fn set_with_decis(&mut self, with_decis: bool) {
self.clock.with_decis = with_decis;
self.timer.with_decis = with_decis;
}
pub fn get_clock(&self) -> &ClockState<clock::Countdown> {
&self.clock
}
pub fn is_running(&self) -> bool {
self.clock.is_running() || self.timer.is_running()
}
}
impl EventHandler for CountdownState {
@@ -38,20 +56,35 @@ impl EventHandler for CountdownState {
let edit_mode = self.clock.is_edit_mode();
match event {
Event::Tick => {
self.clock.tick();
}
Event::Key(key) if key.code == KeyCode::Char('r') => {
self.clock.reset();
if !self.clock.is_done() {
self.clock.tick();
} else {
self.timer.tick();
if self.timer.is_initial() {
self.timer.run();
}
}
}
Event::Key(key) => match key.code {
KeyCode::Char('r') => {
// reset both clocks
self.clock.reset();
self.timer.reset();
}
KeyCode::Char('s') => {
self.clock.toggle_pause();
// toggle pause status depending on who is running
if !self.clock.is_done() {
self.clock.toggle_pause();
} else {
self.timer.toggle_pause();
}
}
KeyCode::Char('e') => {
self.clock.toggle_edit();
// stop + reset timer entering `edit` mode
if self.timer.is_running() {
self.timer.toggle_pause();
}
}
KeyCode::Left if edit_mode => {
self.clock.edit_next();
@@ -61,9 +94,13 @@ impl EventHandler for CountdownState {
}
KeyCode::Up if edit_mode => {
self.clock.edit_up();
// whenever clock value is changed, reset timer
self.timer.reset();
}
KeyCode::Down if edit_mode => {
self.clock.edit_down();
// whenever clock value is changed, reset timer
self.timer.reset();
}
_ => return Some(event),
},
@@ -81,7 +118,27 @@ impl StatefulWidget for Countdown {
type State = CountdownState;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
let clock = ClockWidget::new(self.style);
let label = Line::raw((format!("Countdown {}", state.clock.get_mode())).to_uppercase());
let label = Line::raw(
if state.clock.is_done() {
if state.clock.with_decis {
format!(
"Countdown {} +{}",
state.clock.get_mode(),
state.timer.get_current_value().to_string_with_decis()
)
} else {
format!(
"Countdown {} +{}",
state.clock.get_mode(),
state.timer.get_current_value()
)
}
} else {
format!("Countdown {}", state.clock.get_mode())
}
.to_uppercase(),
);
let area = center(
area,