improve format handling + fix days rendering (#107)

* render_(format) functions, compact `YyyDddHhMmSs`

* compact rendering of other formats

* add `YDHhMmSs`+`YDdHhMmSs` formats

* tests for `YDHhMmSs`+`YDdHhMmSs`

* `YyDHhMmSs` + `YyDdHhMmSs`

* `YyyDHhMmSs` + `YyyDdHhMmSs`

* fix `edit_up` to compare `years` properly

and add `test_edit_up_overflow_protection`

* fix rendering `Format::YyyDdHhMmSs`
This commit is contained in:
Jens Krause 2025-09-29 16:08:34 +02:00 committed by GitHub
parent 40eb602953
commit 816741f842
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 777 additions and 943 deletions

View File

@ -2,6 +2,3 @@ pub static APP_NAME: &str = env!("CARGO_PKG_NAME");
pub static TICK_VALUE_MS: u64 = 1000 / 10; // 0.1 sec in milliseconds
pub static FPS_VALUE_MS: u64 = 1000 / 60; // 60 FPS in milliseconds
pub static LABEL_DAYS: &str = "d";
pub static LABEL_YEARS: &str = "y";

File diff suppressed because it is too large Load Diff

View File

@ -7,9 +7,13 @@ use ratatui::{
pub const DIGIT_SIZE: usize = 5;
pub const DIGIT_WIDTH: u16 = DIGIT_SIZE as u16;
pub const DIGIT_HEIGHT: u16 = DIGIT_SIZE as u16 + 1 /* border height */;
pub const TWO_DIGITS_WIDTH: u16 = DIGIT_WIDTH + DIGIT_SPACE_WIDTH + DIGIT_WIDTH; // digit-space-digit
pub const THREE_DIGITS_WIDTH: u16 =
DIGIT_WIDTH + DIGIT_SPACE_WIDTH + DIGIT_WIDTH + DIGIT_SPACE_WIDTH + DIGIT_WIDTH; // digit-space-digit-space-digit
pub const COLON_WIDTH: u16 = 4; // incl. padding left + padding right
pub const DOT_WIDTH: u16 = 4; // incl. padding left + padding right
pub const DIGIT_SPACE_WIDTH: u16 = 1; // space between digits
pub const DIGIT_LABEL_WIDTH: u16 = 3; // label (single char) incl. padding left + padding right
#[rustfmt::skip]
const DIGIT_0: [u8; DIGIT_SIZE * DIGIT_SIZE] = [

View File

@ -1,6 +1,8 @@
use crate::{
common::ClockTypeId,
duration::{ONE_DAY, ONE_DECI_SECOND, ONE_HOUR, ONE_MINUTE, ONE_SECOND, ONE_YEAR},
duration::{
MAX_DURATION, ONE_DAY, ONE_DECI_SECOND, ONE_HOUR, ONE_MINUTE, ONE_SECOND, ONE_YEAR,
},
widgets::clock::*,
};
use std::time::Duration;
@ -114,20 +116,44 @@ fn test_get_format_boundaries() {
// DddHhMmSs
c.set_current_value((ONE_YEAR.saturating_sub(ONE_SECOND)).into());
assert_eq!(c.get_format(), Format::DddHhMmSs);
// YDddHhMmSs
// YDHhMmSs
c.set_current_value(ONE_YEAR.into());
assert_eq!(c.get_format(), Format::YDHhMmSs);
// YDdHhMmSs
c.set_current_value((ONE_YEAR + (100 * ONE_DAY).saturating_sub(ONE_SECOND)).into());
assert_eq!(c.get_format(), Format::YDdHhMmSs);
// YDddHhMmSs
c.set_current_value((ONE_YEAR + 100 * ONE_DAY).into());
assert_eq!(c.get_format(), Format::YDddHhMmSs);
// YDddHhMmSs
c.set_current_value(((10 * ONE_YEAR).saturating_sub(ONE_SECOND)).into());
assert_eq!(c.get_format(), Format::YDddHhMmSs);
// YyDddHhMmSs
// YyDHhMmSs
c.set_current_value((10 * ONE_YEAR).into());
assert_eq!(c.get_format(), Format::YyDHhMmSs);
// YyDdHhMmSs
c.set_current_value((10 * ONE_YEAR + 10 * ONE_DAY).into());
assert_eq!(c.get_format(), Format::YyDdHhMmSs);
// YyDdHhMmSs
c.set_current_value((10 * ONE_YEAR + (100 * ONE_DAY).saturating_sub(ONE_SECOND)).into());
assert_eq!(c.get_format(), Format::YyDdHhMmSs);
// YyDddHhMmSs
c.set_current_value((10 * ONE_YEAR + 100 * ONE_DAY).into());
assert_eq!(c.get_format(), Format::YyDddHhMmSs);
// YyDddHhMmSs
c.set_current_value(((100 * ONE_YEAR).saturating_sub(ONE_SECOND)).into());
assert_eq!(c.get_format(), Format::YyDddHhMmSs);
// YyyDddHhMmSs
// YyyDHhMmSs
c.set_current_value((100 * ONE_YEAR).into());
assert_eq!(c.get_format(), Format::YyyDHhMmSs);
// YyyDdHhMmSs
c.set_current_value((100 * ONE_YEAR + 10 * ONE_DAY).into());
assert_eq!(c.get_format(), Format::YyyDdHhMmSs);
// YyyDdHhMmSs
c.set_current_value((100 * ONE_YEAR + (100 * ONE_DAY).saturating_sub(ONE_SECOND)).into());
assert_eq!(c.get_format(), Format::YyyDdHhMmSs);
// YyyDddHhMmSs
c.set_current_value((100 * ONE_YEAR + 100 * ONE_DAY).into());
assert_eq!(c.get_format(), Format::YyyDddHhMmSs);
}
@ -159,13 +185,43 @@ fn test_get_format_years() {
with_decis: false,
app_tx: None,
});
// YDddHhMmSs
// YDHhMmSs (1 year, 0 days)
assert_eq!(c.get_format(), Format::YDHhMmSs);
// YDHhMmSs (1 year, 1 day)
c.set_current_value((ONE_YEAR + ONE_DAY).into());
assert_eq!(c.get_format(), Format::YDHhMmSs);
// YDdHhMmSs (1 year, 10 days)
c.set_current_value((ONE_YEAR + 10 * ONE_DAY).into());
assert_eq!(c.get_format(), Format::YDdHhMmSs);
// YDddHhMmSs (1 year, 100 days)
c.set_current_value((ONE_YEAR + 100 * ONE_DAY).into());
assert_eq!(c.get_format(), Format::YDddHhMmSs);
// YyDddHhMmSs
// YyDHhMmSs (10 years)
c.set_current_value((10 * ONE_YEAR).into());
assert_eq!(c.get_format(), Format::YyDHhMmSs);
// YyDdHhMmSs (10 years, 10 days)
c.set_current_value((10 * ONE_YEAR + 10 * ONE_DAY).into());
assert_eq!(c.get_format(), Format::YyDdHhMmSs);
// YyDddHhMmSs (10 years, 100 days)
c.set_current_value((10 * ONE_YEAR + 100 * ONE_DAY).into());
assert_eq!(c.get_format(), Format::YyDddHhMmSs);
// YyyDHhMmSs (100 years)
c.set_current_value((100 * ONE_YEAR).into());
assert_eq!(c.get_format(), Format::YyyDHhMmSs);
// YyyDdHhMmSs (100 years, 10 days)
c.set_current_value((100 * ONE_YEAR + 10 * ONE_DAY).into());
assert_eq!(c.get_format(), Format::YyyDdHhMmSs);
// YyyDddHhMmSs (100 years, 100 days)
c.set_current_value((100 * ONE_YEAR + 100 * ONE_DAY).into());
assert_eq!(c.get_format(), Format::YyyDddHhMmSs);
}
@ -280,6 +336,38 @@ fn test_edit_up_stays_in_days() {
assert!(matches!(c.get_mode(), Mode::Editable(Time::Days, _)));
}
#[test]
fn test_edit_up_overflow_protection() {
let mut c = ClockState::<Timer>::new(ClockStateArgs {
initial_value: MAX_DURATION.saturating_sub(ONE_SECOND),
current_value: MAX_DURATION.saturating_sub(ONE_SECOND),
tick_value: ONE_DECI_SECOND,
with_decis: false,
app_tx: None,
});
c.toggle_edit();
c.edit_next(); // Hours
c.edit_next(); // Days
c.edit_next(); // Years
c.edit_up(); // +1y
assert!(Duration::from(*c.get_current_value()) <= MAX_DURATION);
c.edit_prev(); // Days
c.edit_up(); // +1d
assert!(Duration::from(*c.get_current_value()) <= MAX_DURATION);
c.edit_prev(); // Hours
c.edit_up(); // +1h
assert!(Duration::from(*c.get_current_value()) <= MAX_DURATION);
c.edit_prev(); // Minutes
c.edit_up(); // +1m
assert!(Duration::from(*c.get_current_value()) <= MAX_DURATION);
c.edit_prev(); // Sec.
c.edit_up(); // +1s
c.edit_up(); // +1s
c.edit_up(); // +1s
assert!(Duration::from(*c.get_current_value()) <= MAX_DURATION);
}
#[test]
fn test_edit_down_years_to_days() {
let mut c = ClockState::<Timer>::new(ClockStateArgs {