From cb6c2d514212d1229f92455c13b29bfff41dee39 Mon Sep 17 00:00:00 2001
From: Jens Krause <47693+sectore@users.noreply.github.com>
Date: Tue, 30 Sep 2025 12:38:25 +0200
Subject: [PATCH] feat(edit): 10x up/down (#110)
* feat(edit): 10x up/down
* fix `MAX_DURATION` (decisecond)
* footer: add `edit 10x` keybindings
* README: update keybindings
---
README.md | 10 +--
src/duration.rs | 6 +-
src/widgets/clock.rs | 134 ++++++++++++++-------------------------
src/widgets/countdown.rs | 6 ++
src/widgets/footer.rs | 12 ++++
src/widgets/timer.rs | 7 ++
6 files changed, 83 insertions(+), 92 deletions(-)
diff --git a/README.md b/README.md
index 3827ccb..1ed93c1 100644
--- a/README.md
+++ b/README.md
@@ -142,22 +142,24 @@ Extra option (if `--features sound` is enabled by local build only):
| Esc | skip changes |
| ← or → | change selection |
| ↑ | edit to go up |
+| ctrl+↑ | edit to go up 10x |
| ↓ | edit to go down |
+| ctrl+↓ | edit to go down 10x |
**In `Pomodoro` screen only:**
| Key | Description |
| --- | --- |
| ← or → | switch work/pause |
-| ^r | reset round |
-| ^s | save initial value |
+| ctrl+r | reset round |
+| ctrl+s | save initial value |
**In `Countdown` screen only:**
| Key | Description |
| --- | --- |
-| ^e | edit by local time |
-| ^s | save initial value |
+| ctrl+e | edit by local time |
+| ctrl+s | save initial value |
## Appearance
diff --git a/src/duration.rs b/src/duration.rs
index 4e8126d..bf3f3c5 100644
--- a/src/duration.rs
+++ b/src/duration.rs
@@ -29,8 +29,10 @@ const HOURS_PER_DAY: u64 = 24;
// ^ https://www.math.net/days-in-a-year
const DAYS_PER_YEAR: u64 = 365; // ignore leap year of 366 days
-// max. 999y 364d 23:59:59 (1000 years - 1 second)
-pub const MAX_DURATION: Duration = ONE_YEAR.saturating_mul(1000).saturating_sub(ONE_SECOND);
+// max. 999y 364d 23:59:59.9 (1000 years - 1 decisecond)
+pub const MAX_DURATION: Duration = ONE_YEAR
+ .saturating_mul(1000)
+ .saturating_sub(ONE_DECI_SECOND);
#[derive(Debug, Clone, Copy, PartialOrd)]
pub struct DurationEx {
diff --git a/src/widgets/clock.rs b/src/widgets/clock.rs
index 620f595..0205a30 100644
--- a/src/widgets/clock.rs
+++ b/src/widgets/clock.rs
@@ -1,6 +1,6 @@
-use std::fmt;
use std::marker::PhantomData;
use std::time::Duration;
+use std::{fmt, ops::Mul};
use strum::Display;
use ratatui::{
@@ -148,6 +148,18 @@ pub fn time_by_format(format: &Format) -> Time {
}
}
+pub fn count_by_mode(times: u32, mode: &Mode) -> Duration {
+ match mode {
+ Mode::Editable(Time::Decis, _) => ONE_DECI_SECOND.mul(times),
+ Mode::Editable(Time::Seconds, _) => ONE_SECOND.mul(times),
+ Mode::Editable(Time::Minutes, _) => ONE_MINUTE.mul(times),
+ Mode::Editable(Time::Hours, _) => ONE_HOUR.mul(times),
+ Mode::Editable(Time::Days, _) => ONE_DAY.mul(times),
+ Mode::Editable(Time::Years, _) => ONE_YEAR.mul(times),
+ _ => Duration::ZERO,
+ }
+}
+
const RANGE_OF_DONE_COUNT: u64 = 4;
const MAX_DONE_COUNT: u64 = RANGE_OF_DONE_COUNT * 5;
@@ -276,89 +288,23 @@ impl ClockState {
};
}
- pub fn edit_current_up(&mut self) {
- self.current_value = match self.mode {
- Mode::Editable(Time::Decis, _) => {
- if self
- .current_value
- .le(&MAX_DURATION.saturating_sub(ONE_DECI_SECOND).into())
- {
- self.current_value.saturating_add(ONE_DECI_SECOND.into())
- } else {
- self.current_value
- }
- }
- Mode::Editable(Time::Seconds, _) => {
- if self
- .current_value
- .le(&MAX_DURATION.saturating_sub(ONE_SECOND).into())
- {
- self.current_value.saturating_add(ONE_SECOND.into())
- } else {
- self.current_value
- }
- }
- Mode::Editable(Time::Minutes, _) => {
- if self
- .current_value
- .le(&MAX_DURATION.saturating_sub(ONE_MINUTE).into())
- {
- self.current_value.saturating_add(ONE_MINUTE.into())
- } else {
- self.current_value
- }
- }
- Mode::Editable(Time::Hours, _) => {
- if self
- .current_value
- .le(&MAX_DURATION.saturating_sub(ONE_HOUR).into())
- {
- self.current_value.saturating_add(ONE_HOUR.into())
- } else {
- self.current_value
- }
- }
- Mode::Editable(Time::Days, _) => {
- if self
- .current_value
- .le(&MAX_DURATION.saturating_sub(ONE_DAY).into())
- {
- self.current_value.saturating_add(ONE_DAY.into())
- } else {
- self.current_value
- }
- }
- Mode::Editable(Time::Years, _) => {
- if self
- .current_value
- .le(&MAX_DURATION.saturating_sub(ONE_YEAR).into())
- {
- self.current_value.saturating_add(ONE_YEAR.into())
- } else {
- self.current_value
- }
- }
- _ => self.current_value,
- };
- self.update_format();
+ fn edit_current_up(&mut self, times: u32) {
+ let count_value = count_by_mode(times, self.get_mode());
+
+ if self
+ .get_current_value()
+ .le(&MAX_DURATION.saturating_sub(count_value).into())
+ {
+ self.current_value = self.get_current_value().saturating_add(count_value.into());
+ self.update_format();
+ }
}
- pub fn edit_current_down(&mut self) {
- self.current_value = match self.mode {
- Mode::Editable(Time::Decis, _) => {
- self.current_value.saturating_sub(ONE_DECI_SECOND.into())
- }
- Mode::Editable(Time::Seconds, _) => {
- self.current_value.saturating_sub(ONE_SECOND.into())
- }
- Mode::Editable(Time::Minutes, _) => {
- self.current_value.saturating_sub(ONE_MINUTE.into())
- }
- Mode::Editable(Time::Hours, _) => self.current_value.saturating_sub(ONE_HOUR.into()),
- Mode::Editable(Time::Days, _) => self.current_value.saturating_sub(ONE_DAY.into()),
- Mode::Editable(Time::Years, _) => self.current_value.saturating_sub(ONE_YEAR.into()),
- _ => self.current_value,
- };
+ fn edit_current_down(&mut self, times: u32) {
+ let count_value = count_by_mode(times, self.get_mode()).into();
+
+ self.current_value = self.get_current_value().saturating_sub(count_value);
+
self.update_format();
let updated_format = *self.get_format();
self.downgrade_mode_by_format(&updated_format);
@@ -579,11 +525,19 @@ impl ClockState {
}
pub fn edit_up(&mut self) {
- self.edit_current_up();
+ self.edit_current_up(1);
}
pub fn edit_down(&mut self) {
- self.edit_current_down();
+ self.edit_current_down(1);
+ }
+
+ pub fn edit_jump_up(&mut self) {
+ self.edit_current_up(10);
+ }
+
+ pub fn edit_jump_down(&mut self) {
+ self.edit_current_down(10);
}
}
@@ -647,11 +601,19 @@ impl ClockState {
}
pub fn edit_up(&mut self) {
- self.edit_current_up();
+ self.edit_current_up(1);
}
pub fn edit_down(&mut self) {
- self.edit_current_down();
+ self.edit_current_down(1);
+ }
+
+ pub fn edit_jump_up(&mut self) {
+ self.edit_current_up(10);
+ }
+
+ pub fn edit_jump_down(&mut self) {
+ self.edit_current_down(10);
}
}
diff --git a/src/widgets/countdown.rs b/src/widgets/countdown.rs
index e98579b..4d93de6 100644
--- a/src/widgets/countdown.rs
+++ b/src/widgets/countdown.rs
@@ -194,9 +194,15 @@ impl TuiEventHandler for CountdownState {
KeyCode::Left => {
self.clock.edit_next();
}
+ KeyCode::Up if key.modifiers.contains(KeyModifiers::CONTROL) => {
+ self.clock.edit_jump_up();
+ }
KeyCode::Up => {
self.clock.edit_up();
}
+ KeyCode::Down if key.modifiers.contains(KeyModifiers::CONTROL) => {
+ self.clock.edit_jump_down();
+ }
KeyCode::Down => {
self.clock.edit_down();
}
diff --git a/src/widgets/footer.rs b/src/widgets/footer.rs
index b825803..c7c9be8 100644
--- a/src/widgets/footer.rs
+++ b/src/widgets/footer.rs
@@ -220,11 +220,23 @@ impl StatefulWidget for Footer {
scrollbar::VERTICAL.begin
)),
Span::from(SPACE),
+ Span::from(format!(
+ // ctrl + ↑
+ "[^{}]edit up 10x",
+ scrollbar::VERTICAL.begin
+ )),
+ Span::from(SPACE),
Span::from(format!(
// ↓
"[{}]edit up",
scrollbar::VERTICAL.end
)),
+ Span::from(SPACE),
+ Span::from(format!(
+ // ctrl + ↓
+ "[^{}]edit up 10x",
+ scrollbar::VERTICAL.end
+ )),
],
}
})),
diff --git a/src/widgets/timer.rs b/src/widgets/timer.rs
index af2bae2..abb48d4 100644
--- a/src/widgets/timer.rs
+++ b/src/widgets/timer.rs
@@ -4,6 +4,7 @@ use crate::{
utils::center,
widgets::clock::{self, ClockState, ClockWidget},
};
+use crossterm::event::KeyModifiers;
use ratatui::{
buffer::Buffer,
crossterm::event::KeyCode,
@@ -60,11 +61,17 @@ impl TuiEventHandler for TimerState {
KeyCode::Right => {
self.clock.edit_prev();
}
+ KeyCode::Up if key.modifiers.contains(KeyModifiers::CONTROL) => {
+ self.clock.edit_jump_up();
+ }
// change value up
KeyCode::Up => {
self.clock.edit_up();
}
// change value down
+ KeyCode::Down if key.modifiers.contains(KeyModifiers::CONTROL) => {
+ self.clock.edit_jump_down();
+ }
KeyCode::Down => {
self.clock.edit_down();
}