From 816741f8423e05b3ccc9ccfe9af4fde4ad72e7d5 Mon Sep 17 00:00:00 2001 From: Jens Krause <47693+sectore@users.noreply.github.com> Date: Mon, 29 Sep 2025 16:08:34 +0200 Subject: [PATCH] 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` --- src/constants.rs | 3 - src/widgets/clock.rs | 1613 ++++++++++++++------------------- src/widgets/clock_elements.rs | 4 + src/widgets/clock_test.rs | 100 +- 4 files changed, 777 insertions(+), 943 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index 5feb9f0..da9dd3e 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -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"; diff --git a/src/widgets/clock.rs b/src/widgets/clock.rs index da596f5..3921a11 100644 --- a/src/widgets/clock.rs +++ b/src/widgets/clock.rs @@ -13,7 +13,6 @@ use ratatui::{ use crate::{ common::{ClockTypeId, Style as DigitStyle}, - constants::{LABEL_DAYS, LABEL_YEARS}, duration::{ DurationEx, MAX_DURATION, ONE_DAY, ONE_DECI_SECOND, ONE_HOUR, ONE_MINUTE, ONE_SECOND, ONE_YEAR, @@ -21,7 +20,8 @@ use crate::{ events::{AppEvent, AppEventTx}, utils::center_horizontal, widgets::clock_elements::{ - COLON_WIDTH, Colon, DIGIT_HEIGHT, DIGIT_SPACE_WIDTH, DIGIT_WIDTH, DOT_WIDTH, Digit, Dot, + COLON_WIDTH, Colon, DIGIT_HEIGHT, DIGIT_LABEL_WIDTH, DIGIT_SPACE_WIDTH, DIGIT_WIDTH, + DOT_WIDTH, Digit, Dot, THREE_DIGITS_WIDTH, TWO_DIGITS_WIDTH, }, }; @@ -79,8 +79,14 @@ pub enum Format { DHhMmSs, DdHhMmSs, DddHhMmSs, + YDHhMmSs, + YDdHhMmSs, YDddHhMmSs, + YyDHhMmSs, + YyDdHhMmSs, YyDddHhMmSs, + YyyDHhMmSs, + YyyDdHhMmSs, YyyDddHhMmSs, } @@ -263,7 +269,7 @@ impl ClockState { Mode::Editable(Time::Years, _) => { if self .current_value - .lt(&MAX_DURATION.saturating_sub(ONE_YEAR).into()) + .le(&MAX_DURATION.saturating_sub(ONE_YEAR).into()) { self.current_value.saturating_add(ONE_YEAR.into()) } else { @@ -379,6 +385,10 @@ impl ClockState { fn update_mode(&mut self) { let mode = self.mode.clone(); + + // FIXME: By editing an hour from 01:00:00 down, + // but `Editable` should be `Seconds`, but it's minutes + // Same for others (years, days, etc.). self.mode = match mode { Mode::Editable(Time::Years, prev) if self.format <= Format::DddHhMmSs => { Mode::Editable(Time::Days, prev) @@ -424,12 +434,24 @@ impl ClockState { pub fn get_format(&self) -> Format { let v = self.current_value; - if v.years() >= 100 { + if v.years() >= 100 && v.days_mod() >= 100 { Format::YyyDddHhMmSs - } else if v.years() >= 10 { + } else if v.years() >= 100 && v.days_mod() >= 10 { + Format::YyyDdHhMmSs + } else if v.years() >= 100 && v.days() >= 1 { + Format::YyyDHhMmSs + } else if v.years() >= 10 && v.days_mod() >= 100 { Format::YyDddHhMmSs - } else if v.years() >= 1 { + } else if v.years() >= 10 && v.days_mod() >= 10 { + Format::YyDdHhMmSs + } else if v.years() >= 10 && v.days() >= 1 { + Format::YyDHhMmSs + } else if v.years() >= 1 && v.days_mod() >= 100 { Format::YDddHhMmSs + } else if v.years() >= 1 && v.days_mod() >= 10 { + Format::YDdHhMmSs + } else if v.years() >= 1 && v.days() >= 1 { + Format::YDHhMmSs } else if v.days() >= 100 { Format::DddHhMmSs } else if v.days() >= 10 { @@ -643,223 +665,211 @@ where lengths }; - // TODO: Add x-offset (before+after) - const LABEL_WIDTH: u16 = 1; // `Y` or `D` + const LABEL_WIDTH: u16 = DIGIT_LABEL_WIDTH + DIGIT_SPACE_WIDTH; match format { Format::YyyDddHhMmSs => add_decis( vec![ - DIGIT_WIDTH, // y - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // y - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // y - DIGIT_SPACE_WIDTH, // (space) - LABEL_WIDTH, // label - 2 * DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // d - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // d - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // d - DIGIT_SPACE_WIDTH, // (space) - LABEL_WIDTH, // label - 2 * DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // h - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // h - COLON_WIDTH, // : - DIGIT_WIDTH, // m - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // m - COLON_WIDTH, // : - DIGIT_WIDTH, // s - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // s + THREE_DIGITS_WIDTH, // y_y_y + LABEL_WIDTH, // _l__ + THREE_DIGITS_WIDTH, // d_d_d + LABEL_WIDTH, // _l__ + TWO_DIGITS_WIDTH, // h_h + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // m_m + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // s_s + ], + with_decis, + ), + Format::YyyDdHhMmSs => add_decis( + vec![ + THREE_DIGITS_WIDTH, // y_y_y + LABEL_WIDTH, // _l__ + TWO_DIGITS_WIDTH, // d_d + LABEL_WIDTH, // _l__ + TWO_DIGITS_WIDTH, // h_h + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // m_m + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // s_s + ], + with_decis, + ), + Format::YyyDHhMmSs => add_decis( + vec![ + THREE_DIGITS_WIDTH, // y_y_y + LABEL_WIDTH, // _l__ + DIGIT_WIDTH, // d + LABEL_WIDTH, // _l__ + TWO_DIGITS_WIDTH, // h_h + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // m_m + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // s_s ], with_decis, ), Format::YyDddHhMmSs => add_decis( vec![ - DIGIT_WIDTH, // y - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // y - DIGIT_SPACE_WIDTH, // (space) - LABEL_WIDTH, // label - 2 * DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // d - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // d - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // d - DIGIT_SPACE_WIDTH, // (space) - LABEL_WIDTH, // label - 2 * DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // h - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // h - COLON_WIDTH, // : - DIGIT_WIDTH, // m - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // m - COLON_WIDTH, // : - DIGIT_WIDTH, // s - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // s + TWO_DIGITS_WIDTH, // y_y + LABEL_WIDTH, // _l__ + THREE_DIGITS_WIDTH, // d_d_d + LABEL_WIDTH, // _l__ + TWO_DIGITS_WIDTH, // h_h + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // m_m + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // s_s + ], + with_decis, + ), + Format::YyDdHhMmSs => add_decis( + vec![ + TWO_DIGITS_WIDTH, // y_y + LABEL_WIDTH, // _l__ + TWO_DIGITS_WIDTH, // d_d + LABEL_WIDTH, // _l__ + TWO_DIGITS_WIDTH, // h_h + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // m_m + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // s_s + ], + with_decis, + ), + Format::YyDHhMmSs => add_decis( + vec![ + TWO_DIGITS_WIDTH, // y_y + LABEL_WIDTH, // _l__ + DIGIT_WIDTH, // d + LABEL_WIDTH, // _l__ + TWO_DIGITS_WIDTH, // h_h + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // m_m + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // s_s ], with_decis, ), Format::YDddHhMmSs => add_decis( vec![ - DIGIT_WIDTH, // Y - DIGIT_SPACE_WIDTH, // (space) - LABEL_WIDTH, // label - 2 * DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // d - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // d - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // d - DIGIT_SPACE_WIDTH, // (space) - LABEL_WIDTH, // label - 2 * DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // h - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // h - COLON_WIDTH, // : - DIGIT_WIDTH, // m - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // m - COLON_WIDTH, // : - DIGIT_WIDTH, // s - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // s + DIGIT_WIDTH, // Y + LABEL_WIDTH, // _l__ + THREE_DIGITS_WIDTH, // d_d_d + LABEL_WIDTH, // _l__ + TWO_DIGITS_WIDTH, // h_h + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // m_m + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // s_s ], with_decis, ), + Format::YDdHhMmSs => add_decis( + vec![ + DIGIT_WIDTH, // Y + LABEL_WIDTH, // _l__ + TWO_DIGITS_WIDTH, // d_d + LABEL_WIDTH, // _l__ + TWO_DIGITS_WIDTH, // h_h + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // m_m + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // s_s + ], + with_decis, + ), + Format::YDHhMmSs => add_decis( + vec![ + DIGIT_WIDTH, // Y + LABEL_WIDTH, // _l__ + DIGIT_WIDTH, // d + LABEL_WIDTH, // _l__ + TWO_DIGITS_WIDTH, // h_h + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // m_m + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // s_s + ], + with_decis, + ), + Format::DddHhMmSs => add_decis( vec![ - DIGIT_WIDTH, // d - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // d - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // d - DIGIT_SPACE_WIDTH, // (space) - LABEL_WIDTH, // label - 2 * DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // h - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // h - COLON_WIDTH, // : - DIGIT_WIDTH, // m - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // m - COLON_WIDTH, // : - DIGIT_WIDTH, // s - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // s + THREE_DIGITS_WIDTH, // d_d_d + LABEL_WIDTH, // _l__ + TWO_DIGITS_WIDTH, // h_h + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // m_m + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // s_s ], with_decis, ), Format::DdHhMmSs => add_decis( vec![ - DIGIT_WIDTH, // d - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // d - DIGIT_SPACE_WIDTH, // (space) - LABEL_WIDTH, // label - 2 * DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // h - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // h - COLON_WIDTH, // : - DIGIT_WIDTH, // m - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // m - COLON_WIDTH, // : - DIGIT_WIDTH, // s - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // s + TWO_DIGITS_WIDTH, // d_d + LABEL_WIDTH, // _l__ + TWO_DIGITS_WIDTH, // h_h + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // m_m + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // s_s ], with_decis, ), Format::DHhMmSs => add_decis( vec![ - DIGIT_WIDTH, // D - DIGIT_SPACE_WIDTH, // (space) - LABEL_WIDTH, // label - 2 * DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // h - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // h - COLON_WIDTH, // : - DIGIT_WIDTH, // m - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // m - COLON_WIDTH, // : - DIGIT_WIDTH, // s - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // s + DIGIT_WIDTH, // D + LABEL_WIDTH, // _l__ + TWO_DIGITS_WIDTH, // h_h + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // m_m + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // s_s ], with_decis, ), Format::HhMmSs => add_decis( vec![ - DIGIT_WIDTH, // h - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // h - COLON_WIDTH, // : - DIGIT_WIDTH, // m - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // m - COLON_WIDTH, // : - DIGIT_WIDTH, // s - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // s + TWO_DIGITS_WIDTH, // h_h + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // m_m + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // s_s ], with_decis, ), Format::HMmSs => add_decis( vec![ - DIGIT_WIDTH, // h - COLON_WIDTH, // : - DIGIT_WIDTH, // m - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // m - COLON_WIDTH, // : - DIGIT_WIDTH, // s - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // s + DIGIT_WIDTH, // h + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // m_m + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // s_s ], with_decis, ), Format::MmSs => add_decis( vec![ - DIGIT_WIDTH, // m - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // m - COLON_WIDTH, // : - DIGIT_WIDTH, // s - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // s + TWO_DIGITS_WIDTH, // m_m + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // s_s ], with_decis, ), Format::MSs => add_decis( vec![ - DIGIT_WIDTH, // m - COLON_WIDTH, // : - DIGIT_WIDTH, // s - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // s + DIGIT_WIDTH, // m + COLON_WIDTH, // : + TWO_DIGITS_WIDTH, // s_s ], with_decis, ), Format::Ss => add_decis( vec![ - DIGIT_WIDTH, // s - DIGIT_SPACE_WIDTH, // (space) - DIGIT_WIDTH, // s + TWO_DIGITS_WIDTH, // s_s ], with_decis, ), @@ -920,831 +930,566 @@ where let edit_secs = matches!(state.mode, Mode::Editable(Time::Seconds, _)); let edit_decis = matches!(state.mode, Mode::Editable(Time::Decis, _)); + let render_three_digits = |d1, d2, d3, editable, area, buf: &mut Buffer| { + let [a1, a2, a3] = Layout::horizontal(Constraint::from_lengths([ + DIGIT_WIDTH + DIGIT_SPACE_WIDTH, + DIGIT_WIDTH + DIGIT_SPACE_WIDTH, + DIGIT_WIDTH, + ])) + .areas(area); + Digit::new(d1, editable, symbol).render(a1, buf); + Digit::new(d2, editable, symbol).render(a2, buf); + Digit::new(d3, editable, symbol).render(a3, buf); + }; + + let render_two_digits = |d1, d2, editable, area, buf: &mut Buffer| { + let [a1, a2] = Layout::horizontal(Constraint::from_lengths([ + DIGIT_WIDTH + DIGIT_SPACE_WIDTH, + DIGIT_WIDTH, + ])) + .areas(area); + Digit::new(d1, editable, symbol).render(a1, buf); + Digit::new(d2, editable, symbol).render(a2, buf); + }; + + let render_colon = |area, buf: &mut Buffer| { + Colon::new(symbol).render(area, buf); + }; + + let render_dot = |area, buf: &mut Buffer| { + Dot::new(symbol).render(area, buf); + }; + + let render_yyy = |area, buf| { + render_three_digits( + (state.current_value.years() / 100) % 10, + (state.current_value.years() / 10) % 10, + state.current_value.years() % 10, + edit_years, + area, + buf, + ); + }; + + let render_yy = |area, buf| { + render_two_digits( + (state.current_value.years() / 10) % 10, + state.current_value.years() % 10, + edit_years, + area, + buf, + ); + }; + + let render_y = |area, buf| { + Digit::new(state.current_value.years() % 10, edit_years, symbol).render(area, buf); + }; + + let render_ddd = |area, buf| { + render_three_digits( + (state.current_value.days_mod() / 100) % 10, + (state.current_value.days_mod() / 10) % 10, + state.current_value.days_mod() % 10, + edit_days, + area, + buf, + ); + }; + + let render_dd = |area, buf| { + render_two_digits( + (state.current_value.days_mod() / 10) % 10, + state.current_value.days_mod() % 10, + edit_days, + area, + buf, + ); + }; + + let render_d = |area, buf| { + Digit::new(state.current_value.days_mod() % 10, edit_days, symbol).render(area, buf); + }; + + let render_hh = |area, buf| { + render_two_digits( + state.current_value.hours_mod() / 10, + state.current_value.hours_mod() % 10, + edit_hours, + area, + buf, + ); + }; + + let render_h = |area, buf| { + Digit::new(state.current_value.hours_mod() % 10, edit_hours, symbol).render(area, buf); + }; + + let render_mm = |area, buf| { + render_two_digits( + state.current_value.minutes_mod() / 10, + state.current_value.minutes_mod() % 10, + edit_minutes, + area, + buf, + ); + }; + + let render_m = |area, buf| { + Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) + .render(area, buf); + }; + + let render_ss = |area, buf| { + render_two_digits( + state.current_value.seconds_mod() / 10, + state.current_value.seconds_mod() % 10, + edit_secs, + area, + buf, + ); + }; + + let render_s = |area, buf| { + Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol).render(area, buf); + }; + + let render_ds = |area, buf| { + Digit::new(state.current_value.decis(), edit_decis, symbol).render(area, buf); + }; + + let render_label = |l: &str, area, buf: &mut Buffer| { + Span::styled( + format!(" {l}").to_uppercase(), + Style::default().add_modifier(Modifier::BOLD), + ) + .render(area, buf); + }; + + let render_label_y = |area, buf| { + render_label("Y", area, buf); + }; + + let render_label_d = |area, buf| { + render_label("D", area, buf); + }; + match format { Format::YyyDddHhMmSs if with_decis => { - let [ - yyy, - _, - yy, - _, - y, - _, - ly, - _, - ddd, - _, - dd, - _, - d, - _, - ld, - _, - hh, - _, - h, - c_hm, - mm, - _, - m, - c_ms, - ss, - _, - s, - dot, - ds, - ] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new((state.current_value.years() / 100) % 10, edit_years, symbol) - .render(yyy, buf); - Digit::new((state.current_value.years() / 10) % 10, edit_years, symbol) - .render(yy, buf); - Digit::new(state.current_value.years() % 10, edit_years, symbol).render(y, buf); - Span::styled( - LABEL_YEARS.to_uppercase(), - Style::default().add_modifier(Modifier::BOLD), - ) - .render(ly, buf); - Digit::new( - (state.current_value.days_mod() / 100) % 10, - edit_days, - symbol, - ) - .render(ddd, buf); - Digit::new( - (state.current_value.days_mod() / 10) % 10, - edit_days, - symbol, - ) - .render(dd, buf); - Digit::new(state.current_value.days_mod() % 10, edit_days, symbol).render(d, buf); - Span::styled( - LABEL_DAYS.to_uppercase(), - Style::default().add_modifier(Modifier::BOLD), - ) - .render(ld, buf); - Digit::new(state.current_value.hours_mod() / 10, edit_hours, symbol) - .render(hh, buf); - Digit::new(state.current_value.hours_mod() % 10, edit_hours, symbol).render(h, buf); - Colon::new(symbol).render(c_hm, buf); - Digit::new(state.current_value.minutes_mod() / 10, edit_minutes, symbol) - .render(mm, buf); - Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) - .render(m, buf); - Colon::new(symbol).render(c_ms, buf); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); - Dot::new(symbol).render(dot, buf); - Digit::new(state.current_value.decis(), edit_decis, symbol).render(ds, buf); + let [y_y_y, ly, d_d_d, ld, h_h, c_hm, m_m, c_ms, s_s, dot, ds] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_yyy(y_y_y, buf); + render_label_y(ly, buf); + render_ddd(d_d_d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + render_dot(dot, buf); + render_ds(ds, buf); } Format::YyyDddHhMmSs => { - let [ - yyy, - _, - yy, - _, - y, - _, - ly, - _, - ddd, - _, - dd, - _, - d, - _, - ld, - _, - hh, - _, - h, - c_hm, - mm, - _, - m, - c_ms, - ss, - _, - s, - ] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new((state.current_value.years() / 100) % 10, edit_years, symbol) - .render(yyy, buf); - Digit::new((state.current_value.years() / 10) % 10, edit_years, symbol) - .render(yy, buf); - Digit::new(state.current_value.years() % 10, edit_years, symbol).render(y, buf); - Span::styled( - LABEL_YEARS.to_uppercase(), - Style::default().add_modifier(Modifier::BOLD), - ) - .render(ly, buf); - Digit::new( - (state.current_value.days_mod() / 100) % 10, - edit_days, - symbol, - ) - .render(ddd, buf); - Digit::new( - (state.current_value.days_mod() / 10) % 10, - edit_days, - symbol, - ) - .render(dd, buf); - Digit::new(state.current_value.days_mod() % 10, edit_days, symbol).render(d, buf); - Span::styled( - LABEL_DAYS.to_uppercase(), - Style::default().add_modifier(Modifier::BOLD), - ) - .render(ld, buf); - Digit::new(state.current_value.hours_mod() / 10, edit_hours, symbol) - .render(hh, buf); - Digit::new(state.current_value.hours_mod() % 10, edit_hours, symbol).render(h, buf); - Colon::new(symbol).render(c_hm, buf); - Digit::new(state.current_value.minutes_mod() / 10, edit_minutes, symbol) - .render(mm, buf); - Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) - .render(m, buf); - Colon::new(symbol).render(c_ms, buf); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); + let [y_y_y, ly, d_d_d, ld, h_h, c_hm, m_m, c_ms, s_s] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_yyy(y_y_y, buf); + render_label_y(ly, buf); + render_ddd(d_d_d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + } + Format::YyyDdHhMmSs if with_decis => { + let [y_y_y, ly, d_d, ld, h_h, c_hm, m_m, c_ms, s_s, dot, ds] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_yyy(y_y_y, buf); + render_label_y(ly, buf); + render_dd(d_d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + render_dot(dot, buf); + render_ds(ds, buf); + } + Format::YyyDdHhMmSs => { + let [y_y_y, ly, d_d, ld, h_h, c_hm, m_m, c_ms, s_s] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_yyy(y_y_y, buf); + render_label_y(ly, buf); + render_dd(d_d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + } + Format::YyyDHhMmSs if with_decis => { + let [y_y_y, ly, d, ld, h_h, c_hm, m_m, c_ms, s_s, dot, ds] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_yyy(y_y_y, buf); + render_label_y(ly, buf); + render_d(d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + render_dot(dot, buf); + render_ds(ds, buf); + } + Format::YyyDHhMmSs => { + let [y_y_y, ly, d, ld, h_h, c_hm, m_m, c_ms, s_s] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_yyy(y_y_y, buf); + render_label_y(ly, buf); + render_d(d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); } Format::YyDddHhMmSs if with_decis => { - let [ - yy, - _, - y, - _, - ly, - _, - ddd, - _, - dd, - _, - d, - _, - ld, - _, - hh, - _, - h, - c_hm, - mm, - _, - m, - c_ms, - ss, - _, - s, - dot, - ds, - ] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new((state.current_value.years() / 10) % 10, edit_years, symbol) - .render(yy, buf); - Digit::new(state.current_value.years() % 10, edit_years, symbol).render(y, buf); - Span::styled( - LABEL_YEARS.to_uppercase(), - Style::default().add_modifier(Modifier::BOLD), - ) - .render(ly, buf); - Digit::new( - (state.current_value.days_mod() / 100) % 10, - edit_days, - symbol, - ) - .render(ddd, buf); - Digit::new( - (state.current_value.days_mod() / 10) % 10, - edit_days, - symbol, - ) - .render(dd, buf); - Digit::new(state.current_value.days_mod() % 10, edit_days, symbol).render(d, buf); - Span::styled( - LABEL_DAYS.to_uppercase(), - Style::default().add_modifier(Modifier::BOLD), - ) - .render(ld, buf); - Digit::new(state.current_value.hours_mod() / 10, edit_hours, symbol) - .render(hh, buf); - Digit::new(state.current_value.hours_mod() % 10, edit_hours, symbol).render(h, buf); - Colon::new(symbol).render(c_hm, buf); - Digit::new(state.current_value.minutes_mod() / 10, edit_minutes, symbol) - .render(mm, buf); - Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) - .render(m, buf); - Colon::new(symbol).render(c_ms, buf); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); - Dot::new(symbol).render(dot, buf); - Digit::new(state.current_value.decis(), edit_decis, symbol).render(ds, buf); + let [y_y, ly, d_d_d, ld, h_h, c_hm, m_m, c_ms, s_s, dot, ds] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_yy(y_y, buf); + render_label_y(ly, buf); + render_ddd(d_d_d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + render_dot(dot, buf); + render_ds(ds, buf); } Format::YyDddHhMmSs => { - let [ - yy, - _, - y, - _, - ly, - _, - ddd, - _, - dd, - _, - d, - _, - ld, - _, - hh, - _, - h, - c_hm, - mm, - _, - m, - c_ms, - ss, - _, - s, - ] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new((state.current_value.years() / 10) % 10, edit_years, symbol) - .render(yy, buf); - Digit::new(state.current_value.years() % 10, edit_years, symbol).render(y, buf); - Span::styled( - LABEL_YEARS.to_uppercase(), - Style::default().add_modifier(Modifier::BOLD), - ) - .render(ly, buf); - Digit::new( - (state.current_value.days_mod() / 100) % 10, - edit_days, - symbol, - ) - .render(ddd, buf); - Digit::new( - (state.current_value.days_mod() / 10) % 10, - edit_days, - symbol, - ) - .render(dd, buf); - Digit::new(state.current_value.days_mod() % 10, edit_days, symbol).render(d, buf); - Span::styled( - LABEL_DAYS.to_uppercase(), - Style::default().add_modifier(Modifier::BOLD), - ) - .render(ld, buf); - Digit::new(state.current_value.hours_mod() / 10, edit_hours, symbol) - .render(hh, buf); - Digit::new(state.current_value.hours_mod() % 10, edit_hours, symbol).render(h, buf); - Colon::new(symbol).render(c_hm, buf); - Digit::new(state.current_value.minutes_mod() / 10, edit_minutes, symbol) - .render(mm, buf); - Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) - .render(m, buf); - Colon::new(symbol).render(c_ms, buf); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); + let [y_y, ly, d_d_d, ld, h_h, c_hm, m_m, c_ms, s_s] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_yy(y_y, buf); + render_label_y(ly, buf); + render_ddd(d_d_d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + } + Format::YyDdHhMmSs if with_decis => { + let [y_y, ly, d_d, ld, h_h, c_hm, m_m, c_ms, s_s, dot, ds] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_yy(y_y, buf); + render_label_y(ly, buf); + render_dd(d_d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + render_dot(dot, buf); + render_ds(ds, buf); + } + Format::YyDdHhMmSs => { + let [y_y, ly, d_d, ld, h_h, c_hm, m_m, c_ms, s_s] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_yy(y_y, buf); + render_label_y(ly, buf); + render_dd(d_d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + } + Format::YyDHhMmSs if with_decis => { + let [y_y, ly, d, ld, h_h, c_hm, m_m, c_ms, s_s, dot, ds] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_yy(y_y, buf); + render_label_y(ly, buf); + render_d(d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + render_dot(dot, buf); + render_ds(ds, buf); + } + Format::YyDHhMmSs => { + let [y_y, ly, d, ld, h_h, c_hm, m_m, c_ms, s_s] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_yy(y_y, buf); + render_label_y(ly, buf); + render_d(d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); } Format::YDddHhMmSs if with_decis => { - let [ - y, - _, - ly, - _, - ddd, - _, - dd, - _, - d, - _, - ld, - _, - hh, - _, - h, - c_hm, - mm, - _, - m, - c_ms, - ss, - _, - s, - dot, - ds, - ] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new(state.current_value.years() % 10, edit_years, symbol).render(y, buf); - Span::styled( - LABEL_YEARS.to_uppercase(), - Style::default().add_modifier(Modifier::BOLD), - ) - .render(ly, buf); - Digit::new( - (state.current_value.days_mod() / 100) % 10, - edit_days, - symbol, - ) - .render(ddd, buf); - Digit::new( - (state.current_value.days_mod() / 10) % 10, - edit_days, - symbol, - ) - .render(dd, buf); - Digit::new(state.current_value.days_mod() % 10, edit_days, symbol).render(d, buf); - Span::styled( - LABEL_DAYS.to_uppercase(), - Style::default().add_modifier(Modifier::BOLD), - ) - .render(ld, buf); - Digit::new(state.current_value.hours_mod() / 10, edit_hours, symbol) - .render(hh, buf); - Digit::new(state.current_value.hours_mod() % 10, edit_hours, symbol).render(h, buf); - Colon::new(symbol).render(c_hm, buf); - Digit::new(state.current_value.minutes_mod() / 10, edit_minutes, symbol) - .render(mm, buf); - Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) - .render(m, buf); - Colon::new(symbol).render(c_ms, buf); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); - Dot::new(symbol).render(dot, buf); - Digit::new(state.current_value.decis(), edit_decis, symbol).render(ds, buf); + let [y, ly, d_d_d, ld, h_h, c_hm, m_m, c_ms, s_s, dot, ds] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_y(y, buf); + render_label_y(ly, buf); + render_ddd(d_d_d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + render_dot(dot, buf); + render_ds(ds, buf); } Format::YDddHhMmSs => { - let [ - y, - _, - ly, - _, - ddd, - _, - dd, - _, - d, - _, - ld, - _, - hh, - _, - h, - c_hm, - mm, - _, - m, - c_ms, - ss, - _, - s, - ] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new(state.current_value.years() % 10, edit_years, symbol).render(y, buf); - Span::styled( - LABEL_YEARS.to_uppercase(), - Style::default().add_modifier(Modifier::BOLD), - ) - .render(ly, buf); - Digit::new( - (state.current_value.days_mod() / 100) % 10, - edit_days, - symbol, - ) - .render(ddd, buf); - Digit::new( - (state.current_value.days_mod() / 10) % 10, - edit_days, - symbol, - ) - .render(dd, buf); - Digit::new(state.current_value.days_mod() % 10, edit_days, symbol).render(d, buf); - Span::styled( - LABEL_DAYS.to_uppercase(), - Style::default().add_modifier(Modifier::BOLD), - ) - .render(ld, buf); - Digit::new(state.current_value.hours_mod() / 10, edit_hours, symbol) - .render(hh, buf); - Digit::new(state.current_value.hours_mod() % 10, edit_hours, symbol).render(h, buf); - Colon::new(symbol).render(c_hm, buf); - Digit::new(state.current_value.minutes_mod() / 10, edit_minutes, symbol) - .render(mm, buf); - Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) - .render(m, buf); - Colon::new(symbol).render(c_ms, buf); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); + let [y, ly, d_d_d, ld, h_h, c_hm, m_m, c_ms, s_s] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_y(y, buf); + render_label_y(ly, buf); + render_ddd(d_d_d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + } + Format::YDdHhMmSs if with_decis => { + let [y, ly, d_d, ld, h_h, c_hm, m_m, c_ms, s_s, dot, ds] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_y(y, buf); + render_label_y(ly, buf); + render_dd(d_d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + render_dot(dot, buf); + render_ds(ds, buf); + } + Format::YDdHhMmSs => { + let [y, ly, d_d, ld, h_h, c_hm, m_m, c_ms, s_s] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_y(y, buf); + render_label_y(ly, buf); + render_dd(d_d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + } + Format::YDHhMmSs if with_decis => { + let [y, ly, d, ld, h_h, c_hm, m_m, c_ms, s_s, dot, ds] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_y(y, buf); + render_label_y(ly, buf); + render_d(d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + render_dot(dot, buf); + render_ds(ds, buf); + } + Format::YDHhMmSs => { + let [y, ly, d, ld, h_h, c_hm, m_m, c_ms, s_s] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_y(y, buf); + render_label_y(ly, buf); + render_d(d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); } Format::DddHhMmSs if with_decis => { - let [ - ddd, - _, - dd, - _, - d, - _, - l, - _, - hh, - _, - h, - c_hm, - mm, - _, - m, - c_ms, - ss, - _, - s, - dot, - ds, - ] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new( - (state.current_value.days_mod() / 100) % 10, - edit_days, - symbol, - ) - .render(ddd, buf); - Digit::new( - (state.current_value.days_mod() / 10) % 10, - edit_days, - symbol, - ) - .render(dd, buf); - Digit::new(state.current_value.days_mod() % 10, edit_days, symbol).render(d, buf); - Span::styled( - LABEL_DAYS.to_uppercase(), - Style::default().add_modifier(Modifier::BOLD), - ) - .render(l, buf); - Digit::new(state.current_value.hours_mod() / 10, edit_hours, symbol) - .render(hh, buf); - Digit::new(state.current_value.hours_mod() % 10, edit_hours, symbol).render(h, buf); - Colon::new(symbol).render(c_hm, buf); - Digit::new(state.current_value.minutes_mod() / 10, edit_minutes, symbol) - .render(mm, buf); - Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) - .render(m, buf); - Colon::new(symbol).render(c_ms, buf); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); - Dot::new(symbol).render(dot, buf); - Digit::new(state.current_value.decis(), edit_decis, symbol).render(ds, buf); + let [d_d_d, ld, h_h, c_hm, m_m, c_ms, s_s, dot, ds] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_ddd(d_d_d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + render_dot(dot, buf); + render_ds(ds, buf); } Format::DddHhMmSs => { - let [ - ddd, - _, - dd, - _, - d, - _, - l, - _, - hh, - _, - h, - c_hm, - mm, - _, - m, - c_ms, - ss, - _, - s, - ] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new( - (state.current_value.days_mod() / 100) % 10, - edit_days, - symbol, - ) - .render(ddd, buf); - Digit::new( - (state.current_value.days_mod() / 10) % 10, - edit_days, - symbol, - ) - .render(dd, buf); - Digit::new(state.current_value.days_mod() % 10, edit_days, symbol).render(d, buf); - Span::styled( - LABEL_DAYS.to_uppercase(), - Style::default().add_modifier(Modifier::BOLD), - ) - .render(l, buf); - Digit::new(state.current_value.hours_mod() / 10, edit_hours, symbol) - .render(hh, buf); - Digit::new(state.current_value.hours_mod() % 10, edit_hours, symbol).render(h, buf); - Colon::new(symbol).render(c_hm, buf); - Digit::new(state.current_value.minutes_mod() / 10, edit_minutes, symbol) - .render(mm, buf); - Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) - .render(m, buf); - Colon::new(symbol).render(c_ms, buf); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); + let [d_d_d, ld, h_h, c_hm, m_m, c_ms, s_s] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_ddd(d_d_d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); } Format::DdHhMmSs if with_decis => { - let [ - dd, - _, - d, - _, - l, - _, - hh, - _, - h, - c_hm, - mm, - _, - m, - c_ms, - ss, - _, - s, - dot, - ds, - ] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new( - (state.current_value.days_mod() / 10) % 10, - edit_days, - symbol, - ) - .render(dd, buf); - Digit::new(state.current_value.days_mod() % 10, edit_days, symbol).render(d, buf); - Span::styled( - LABEL_DAYS.to_uppercase(), - Style::default().add_modifier(Modifier::BOLD), - ) - .render(l, buf); - Digit::new(state.current_value.hours_mod() / 10, edit_hours, symbol) - .render(hh, buf); - Digit::new(state.current_value.hours_mod() % 10, edit_hours, symbol).render(h, buf); - Colon::new(symbol).render(c_hm, buf); - Digit::new(state.current_value.minutes_mod() / 10, edit_minutes, symbol) - .render(mm, buf); - Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) - .render(m, buf); - Colon::new(symbol).render(c_ms, buf); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); - Dot::new(symbol).render(dot, buf); - Digit::new(state.current_value.decis(), edit_decis, symbol).render(ds, buf); + let [d_d, ld, h_h, c_hm, m_m, c_ms, s_s, dot, ds] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_dd(d_d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + render_dot(dot, buf); + render_ds(ds, buf); } Format::DdHhMmSs => { - let [dd, _, d, _, l, _, hh, _, h, c_hm, mm, _, m, c_ms, ss, _, s] = + let [d_d, ld, h_h, c_hm, m_m, c_ms, s_s] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new( - (state.current_value.days_mod() / 10) % 10, - edit_days, - symbol, - ) - .render(dd, buf); - Digit::new(state.current_value.days_mod() % 10, edit_days, symbol).render(d, buf); - Span::styled( - LABEL_DAYS.to_uppercase(), - Style::default().add_modifier(Modifier::BOLD), - ) - .render(l, buf); - Digit::new(state.current_value.hours_mod() / 10, edit_hours, symbol) - .render(hh, buf); - Digit::new(state.current_value.hours_mod() % 10, edit_hours, symbol).render(h, buf); - Colon::new(symbol).render(c_hm, buf); - Digit::new(state.current_value.minutes_mod() / 10, edit_minutes, symbol) - .render(mm, buf); - Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) - .render(m, buf); - Colon::new(symbol).render(c_ms, buf); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); + render_dd(d_d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); } Format::DHhMmSs if with_decis => { - let [ - d, - _, - l, - _, - hh, - _, - h, - c_hm, - mm, - _, - m, - c_ms, - ss, - _, - s, - dot, - ds, - ] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new(state.current_value.days_mod() % 10, edit_days, symbol).render(d, buf); - Span::styled( - LABEL_DAYS.to_uppercase(), - Style::default().add_modifier(Modifier::BOLD), - ) - .render(l, buf); - Digit::new(state.current_value.hours_mod() / 10, edit_hours, symbol) - .render(hh, buf); - Digit::new(state.current_value.hours_mod() % 10, edit_hours, symbol).render(h, buf); - Colon::new(symbol).render(c_hm, buf); - Digit::new(state.current_value.minutes_mod() / 10, edit_minutes, symbol) - .render(mm, buf); - Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) - .render(m, buf); - Colon::new(symbol).render(c_ms, buf); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); - Dot::new(symbol).render(dot, buf); - Digit::new(state.current_value.decis(), edit_decis, symbol).render(ds, buf); + let [d, ld, h_h, c_hm, m_m, c_ms, s_s, dot, ds] = + Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_d(d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + render_dot(dot, buf); + render_ds(ds, buf); } Format::DHhMmSs => { - let [d, _, l, _, hh, _, h, c_hm, mm, _, m, c_ms, ss, _, s] = + let [d, ld, h_h, c_hm, m_m, c_ms, s_s] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new(state.current_value.days_mod() % 10, edit_days, symbol).render(d, buf); - Span::styled( - LABEL_DAYS.to_uppercase(), - Style::default().add_modifier(Modifier::BOLD), - ) - .render(l, buf); - Digit::new(state.current_value.hours_mod() / 10, edit_hours, symbol) - .render(hh, buf); - Digit::new(state.current_value.hours_mod() % 10, edit_hours, symbol).render(h, buf); - Colon::new(symbol).render(c_hm, buf); - Digit::new(state.current_value.minutes_mod() / 10, edit_minutes, symbol) - .render(mm, buf); - Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) - .render(m, buf); - Colon::new(symbol).render(c_ms, buf); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); + render_d(d, buf); + render_label_d(ld, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); } Format::HhMmSs if with_decis => { - let [hh, _, h, c_hm, mm, _, m, c_ms, ss, _, s, d, ds] = + let [h_h, c_hm, m_m, c_ms, s_s, dot, ds] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new(state.current_value.hours_mod() / 10, edit_hours, symbol) - .render(hh, buf); - Digit::new(state.current_value.hours_mod() % 10, edit_hours, symbol).render(h, buf); - Colon::new(symbol).render(c_hm, buf); - Digit::new(state.current_value.minutes_mod() / 10, edit_minutes, symbol) - .render(mm, buf); - Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) - .render(m, buf); - Colon::new(symbol).render(c_ms, buf); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); - Dot::new(symbol).render(d, buf); - Digit::new(state.current_value.decis(), edit_decis, symbol).render(ds, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + render_dot(dot, buf); + render_ds(ds, buf); } Format::HhMmSs => { - let [hh, _, h, c_hm, mm, _, m, c_ms, ss, _, s] = + let [h_h, c_hm, m_m, c_ms, s_s] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new(state.current_value.hours_mod() / 10, edit_hours, symbol) - .render(hh, buf); - Digit::new(state.current_value.hours_mod() % 10, edit_hours, symbol).render(h, buf); - Colon::new(symbol).render(c_hm, buf); - Digit::new(state.current_value.minutes_mod() / 10, edit_minutes, symbol) - .render(mm, buf); - Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) - .render(m, buf); - Colon::new(symbol).render(c_ms, buf); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); + render_hh(h_h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); } Format::HMmSs if with_decis => { - let [h, c_hm, mm, _, m, c_ms, ss, _, s, d, ds] = + let [h, c_hm, m_m, c_ms, s_s, dot, ds] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new(state.current_value.hours_mod() % 10, edit_hours, symbol).render(h, buf); - Colon::new(symbol).render(c_hm, buf); - Digit::new(state.current_value.minutes_mod() / 10, edit_minutes, symbol) - .render(mm, buf); - Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) - .render(m, buf); - Colon::new(symbol).render(c_ms, buf); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); - Dot::new(symbol).render(d, buf); - Digit::new(state.current_value.decis(), edit_decis, symbol).render(ds, buf); + render_h(h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + render_dot(dot, buf); + render_ds(ds, buf); } Format::HMmSs => { - let [h, c_hm, mm, _, m, c_ms, ss, _, s] = + let [h, c_hm, m_m, c_ms, s_s] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new(state.current_value.hours_mod() % 10, edit_hours, symbol).render(h, buf); - Colon::new(symbol).render(c_hm, buf); - Digit::new(state.current_value.minutes_mod() / 10, edit_minutes, symbol) - .render(mm, buf); - Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) - .render(m, buf); - Colon::new(symbol).render(c_ms, buf); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); + render_h(h, buf); + render_colon(c_hm, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); } Format::MmSs if with_decis => { - let [mm, _, m, c_ms, ss, _, s, d, ds] = + let [m_m, c_ms, s_s, dot, ds] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new(state.current_value.minutes_mod() / 10, edit_minutes, symbol) - .render(mm, buf); - Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) - .render(m, buf); - Colon::new(symbol).render(c_ms, buf); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); - Dot::new(symbol).render(d, buf); - Digit::new(state.current_value.decis(), edit_decis, symbol).render(ds, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + render_dot(dot, buf); + render_ds(ds, buf); } Format::MmSs => { - let [mm, _, m, c_ms, ss, _, s] = + let [m_m, c_ms, s_s] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new(state.current_value.minutes_mod() / 10, edit_minutes, symbol) - .render(mm, buf); - Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) - .render(m, buf); - Colon::new(symbol).render(c_ms, buf); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); + render_mm(m_m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); } Format::MSs if with_decis => { - let [m, c_ms, ss, _, s, d, ds] = + let [m, c_ms, s_s, dot, ds] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) - .render(m, buf); - Colon::new(symbol).render(c_ms, buf); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); - Dot::new(symbol).render(d, buf); - Digit::new(state.current_value.decis(), edit_decis, symbol).render(ds, buf); + render_m(m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); + render_dot(dot, buf); + render_ds(ds, buf); } Format::MSs => { - let [m, c_ms, ss, _, s] = + let [m, c_ms, s_s] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new(state.current_value.minutes_mod() % 10, edit_minutes, symbol) - .render(m, buf); - Colon::new(symbol).render(c_ms, buf); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); + render_m(m, buf); + render_colon(c_ms, buf); + render_ss(s_s, buf); } Format::Ss if state.with_decis => { - let [ss, _, s, d, ds] = + let [s_s, dot, ds] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); - Dot::new(symbol).render(d, buf); - Digit::new(state.current_value.decis(), edit_decis, symbol).render(ds, buf); + render_ss(s_s, buf); + render_dot(dot, buf); + render_ds(ds, buf); } Format::Ss => { - let [ss, _, s] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new(state.current_value.seconds_mod() / 10, edit_secs, symbol) - .render(ss, buf); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); + let [s_s] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_ss(s_s, buf); } Format::S if with_decis => { - let [s, d, ds] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); - Dot::new(symbol).render(d, buf); - Digit::new(state.current_value.decis(), edit_decis, symbol).render(ds, buf); + let [s, dot, ds] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); + render_s(s, buf); + render_dot(dot, buf); + render_ds(ds, buf); } Format::S => { let [s] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Digit::new(state.current_value.seconds_mod() % 10, edit_secs, symbol) - .render(s, buf); + render_s(s, buf); } } } diff --git a/src/widgets/clock_elements.rs b/src/widgets/clock_elements.rs index c8c9765..95b0490 100644 --- a/src/widgets/clock_elements.rs +++ b/src/widgets/clock_elements.rs @@ -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] = [ diff --git a/src/widgets/clock_test.rs b/src/widgets/clock_test.rs index 0278751..6c2e072 100644 --- a/src/widgets/clock_test.rs +++ b/src/widgets/clock_test.rs @@ -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::::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::::new(ClockStateArgs {