feat(edit): 10x up/down (#110)

* feat(edit): 10x up/down

* fix `MAX_DURATION` (decisecond)

* footer: add `edit 10x` keybindings

* README: update keybindings
This commit is contained in:
Jens Krause 2025-09-30 12:38:25 +02:00 committed by GitHub
parent 6dc7eb81c2
commit cb6c2d5142
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 83 additions and 92 deletions

View File

@ -142,22 +142,24 @@ Extra option (if `--features sound` is enabled by local build only):
| <kbd>Esc</kbd> | skip changes |
| <kbd></kbd> or <kbd></kbd> | change selection |
| <kbd></kbd> | edit to go up |
| <kbd>ctrl+↑</kbd> | edit to go up 10x |
| <kbd></kbd> | edit to go down |
| <kbd>ctrl+↓</kbd> | edit to go down 10x |
**In `Pomodoro` screen only:**
| Key | Description |
| --- | --- |
| <kbd></kbd> or <kbd></kbd> | switch work/pause |
| <kbd>^r</kbd> | reset round |
| <kbd>^s</kbd> | save initial value |
| <kbd>ctrl+r</kbd> | reset round |
| <kbd>ctrl+s</kbd> | save initial value |
**In `Countdown` screen only:**
| Key | Description |
| --- | --- |
| <kbd>^e</kbd> | edit by local time |
| <kbd>^s</kbd> | save initial value |
| <kbd>ctrl+e</kbd> | edit by local time |
| <kbd>ctrl+s</kbd> | save initial value |
## Appearance

View File

@ -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 {

View File

@ -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<T> ClockState<T> {
};
}
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<Countdown> {
}
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<Timer> {
}
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);
}
}

View File

@ -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();
}

View File

@ -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
)),
],
}
})),

View File

@ -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();
}