diff --git a/src/widgets/clock.rs b/src/widgets/clock.rs index 3921a11..620f595 100644 --- a/src/widgets/clock.rs +++ b/src/widgets/clock.rs @@ -67,7 +67,7 @@ impl fmt::Display for Mode { } // Clock format: -// From `1 deciseconds` up to `999y 364d 23:59:59` +// From `1s` up to `999y 364d 23:59:59` #[derive(Debug, Copy, Clone, PartialEq, Eq, Display, PartialOrd, Ord)] pub enum Format { S, @@ -90,6 +90,64 @@ pub enum Format { YyyDddHhMmSs, } +pub fn format_by_duration(d: &DurationEx) -> Format { + if d.years() >= 100 && d.days_mod() >= 100 { + Format::YyyDddHhMmSs + } else if d.years() >= 100 && d.days_mod() >= 10 { + Format::YyyDdHhMmSs + } else if d.years() >= 100 && d.days() >= 1 { + Format::YyyDHhMmSs + } else if d.years() >= 10 && d.days_mod() >= 100 { + Format::YyDddHhMmSs + } else if d.years() >= 10 && d.days_mod() >= 10 { + Format::YyDdHhMmSs + } else if d.years() >= 10 && d.days() >= 1 { + Format::YyDHhMmSs + } else if d.years() >= 1 && d.days_mod() >= 100 { + Format::YDddHhMmSs + } else if d.years() >= 1 && d.days_mod() >= 10 { + Format::YDdHhMmSs + } else if d.years() >= 1 && d.days() >= 1 { + Format::YDHhMmSs + } else if d.days() >= 100 { + Format::DddHhMmSs + } else if d.days() >= 10 { + Format::DdHhMmSs + } else if d.days() >= 1 { + Format::DHhMmSs + } else if d.hours() >= 10 { + Format::HhMmSs + } else if d.hours() >= 1 { + Format::HMmSs + } else if d.minutes() >= 10 { + Format::MmSs + } else if d.minutes() >= 1 { + Format::MSs + } else if d.seconds() >= 10 { + Format::Ss + } else { + Format::S + } +} + +pub fn time_by_format(format: &Format) -> Time { + match format { + Format::YDddHhMmSs + | Format::YDdHhMmSs + | Format::YDHhMmSs + | Format::YyDddHhMmSs + | Format::YyDdHhMmSs + | Format::YyDHhMmSs + | Format::YyyDddHhMmSs + | Format::YyyDdHhMmSs + | Format::YyyDHhMmSs => Time::Years, + Format::DddHhMmSs | Format::DdHhMmSs | Format::DHhMmSs => Time::Days, + Format::HhMmSs | Format::HMmSs => Time::Hours, + Format::MmSs | Format::MSs => Time::Minutes, + Format::Ss | Format::S => Time::Seconds, + } +} + const RANGE_OF_DONE_COUNT: u64 = 4; const MAX_DONE_COUNT: u64 = RANGE_OF_DONE_COUNT * 5; @@ -163,6 +221,10 @@ impl ClockState { } } + pub fn get_format(&self) -> &Format { + &self.format + } + pub fn get_initial_value(&self) -> &DurationEx { &self.initial_value } @@ -298,7 +360,8 @@ impl ClockState { _ => self.current_value, }; self.update_format(); - self.update_mode(); + let updated_format = *self.get_format(); + self.downgrade_mode_by_format(&updated_format); } pub fn is_edit_mode(&self) -> bool { @@ -383,24 +446,23 @@ impl ClockState { self.update_format(); } - fn update_mode(&mut self) { + // Note: Since `Format` does not include `deciseconds` for different reason, + // `Mode::Editable` can be downgraded up to `Time::Seconds` (but not to `Time::Decis`) + fn downgrade_mode_by_format(&mut self, format: &Format) { 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.). + let time = time_by_format(format); self.mode = match mode { - Mode::Editable(Time::Years, prev) if self.format <= Format::DddHhMmSs => { - Mode::Editable(Time::Days, prev) + Mode::Editable(Time::Years, prev) if format <= &Format::DddHhMmSs => { + Mode::Editable(time, prev) } - Mode::Editable(Time::Days, prev) if self.format <= Format::HhMmSs => { - Mode::Editable(Time::Hours, prev) + Mode::Editable(Time::Days, prev) if format <= &Format::HhMmSs => { + Mode::Editable(time, prev) } - Mode::Editable(Time::Hours, prev) if self.format <= Format::MmSs => { - Mode::Editable(Time::Minutes, prev) + Mode::Editable(Time::Hours, prev) if format <= &Format::MmSs => { + Mode::Editable(time, prev) } - Mode::Editable(Time::Minutes, prev) if self.format <= Format::Ss => { - Mode::Editable(Time::Seconds, prev) + Mode::Editable(Time::Minutes, prev) if format <= &Format::Ss => { + Mode::Editable(time, prev) } _ => mode, } @@ -429,48 +491,8 @@ impl ClockState { } fn update_format(&mut self) { - self.format = self.get_format(); - } - - pub fn get_format(&self) -> Format { - let v = self.current_value; - if v.years() >= 100 && v.days_mod() >= 100 { - Format::YyyDddHhMmSs - } 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() >= 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 { - Format::DdHhMmSs - } else if v.days() >= 1 { - Format::DHhMmSs - } else if v.hours() >= 10 { - Format::HhMmSs - } else if v.hours() >= 1 { - Format::HMmSs - } else if v.minutes() >= 10 { - Format::MmSs - } else if v.minutes() >= 1 { - Format::MSs - } else if v.seconds() >= 10 { - Format::Ss - } else { - Format::S - } + let d = self.get_current_value(); + self.format = format_by_duration(d); } /// Updates inner value of `done_count`. diff --git a/src/widgets/clock_test.rs b/src/widgets/clock_test.rs index 6c2e072..6e58588 100644 --- a/src/widgets/clock_test.rs +++ b/src/widgets/clock_test.rs @@ -28,17 +28,17 @@ fn test_type_id() { #[test] fn test_get_format_seconds() { let mut c = ClockState::::new(ClockStateArgs { - initial_value: Duration::from_secs(5), - current_value: Duration::from_secs(5), + initial_value: ONE_SECOND * 5, + current_value: ONE_SECOND * 5, tick_value: ONE_DECI_SECOND, with_decis: false, app_tx: None, }); // S - assert_eq!(c.get_format(), Format::S); + assert_eq!(c.get_format(), &Format::S); // Ss c.set_current_value(Duration::from_secs(15).into()); - assert_eq!(c.get_format(), Format::Ss); + assert_eq!(c.get_format(), &Format::Ss); } #[test] @@ -51,10 +51,10 @@ fn test_get_format_minutes() { app_tx: None, }); // MSs - assert_eq!(c.get_format(), Format::MSs); + assert_eq!(c.get_format(), &Format::MSs); // MmSs - c.set_current_value(Duration::from_secs(610).into()); // 10+ minutes - assert_eq!(c.get_format(), Format::MmSs); + c.set_current_value((ONE_MINUTE * 11).into()); // 10+ minutes + assert_eq!(c.get_format(), &Format::MmSs); } #[test] @@ -67,162 +67,186 @@ fn test_get_format_hours() { app_tx: None, }); // HMmSS - assert_eq!(c.get_format(), Format::HMmSs); + assert_eq!(c.get_format(), &Format::HMmSs); // HhMmSs c.set_current_value((10 * ONE_HOUR).into()); - assert_eq!(c.get_format(), Format::HhMmSs); + assert_eq!(c.get_format(), &Format::HhMmSs); } #[test] -fn test_get_format_boundaries() { - let mut c = ClockState::::new(ClockStateArgs { - initial_value: ONE_SECOND, - current_value: ONE_SECOND, - tick_value: ONE_DECI_SECOND, - with_decis: false, - app_tx: None, - }); - +fn test_format_by_duration_boundaries() { // S - c.set_current_value(Duration::from_secs(9).into()); - assert_eq!(c.get_format(), Format::S); + assert_eq!(format_by_duration(&(ONE_SECOND * 9).into()), Format::S); // Ss - c.set_current_value((10 * ONE_SECOND).into()); - assert_eq!(c.get_format(), Format::Ss); + assert_eq!(format_by_duration(&(10 * ONE_SECOND).into()), Format::Ss); // Ss - c.set_current_value((59 * ONE_SECOND).into()); - assert_eq!(c.get_format(), Format::Ss); + assert_eq!(format_by_duration(&(59 * ONE_SECOND).into()), Format::Ss); // MSs - c.set_current_value(ONE_MINUTE.into()); - assert_eq!(c.get_format(), Format::MSs); + assert_eq!(format_by_duration(&ONE_MINUTE.into()), Format::MSs); // HhMmSs - c.set_current_value((ONE_DAY.saturating_sub(ONE_SECOND)).into()); - assert_eq!(c.get_format(), Format::HhMmSs); + assert_eq!( + format_by_duration(&(ONE_DAY.saturating_sub(ONE_SECOND)).into()), + Format::HhMmSs + ); // DHhMmSs - c.set_current_value(ONE_DAY.into()); - assert_eq!(c.get_format(), Format::DHhMmSs); + assert_eq!(format_by_duration(&ONE_DAY.into()), Format::DHhMmSs); // DHhMmSs - c.set_current_value(((10 * ONE_DAY).saturating_sub(ONE_SECOND)).into()); - assert_eq!(c.get_format(), Format::DHhMmSs); + assert_eq!( + format_by_duration(&((10 * ONE_DAY).saturating_sub(ONE_SECOND)).into()), + Format::DHhMmSs + ); // DdHhMmSs - c.set_current_value((10 * ONE_DAY).into()); - assert_eq!(c.get_format(), Format::DdHhMmSs); + assert_eq!(format_by_duration(&(10 * ONE_DAY).into()), Format::DdHhMmSs); // DdHhMmSs - c.set_current_value(((100 * ONE_DAY).saturating_sub(ONE_SECOND)).into()); - assert_eq!(c.get_format(), Format::DdHhMmSs); + assert_eq!( + format_by_duration(&((100 * ONE_DAY).saturating_sub(ONE_SECOND)).into()), + Format::DdHhMmSs + ); // DddHhMmSs - c.set_current_value((100 * ONE_DAY).into()); - assert_eq!(c.get_format(), Format::DddHhMmSs); + assert_eq!( + format_by_duration(&(100 * ONE_DAY).into()), + Format::DddHhMmSs + ); // DddHhMmSs - c.set_current_value((ONE_YEAR.saturating_sub(ONE_SECOND)).into()); - assert_eq!(c.get_format(), Format::DddHhMmSs); + assert_eq!( + format_by_duration(&(ONE_YEAR.saturating_sub(ONE_SECOND).into())), + Format::DddHhMmSs + ); // YDHhMmSs - c.set_current_value(ONE_YEAR.into()); - assert_eq!(c.get_format(), Format::YDHhMmSs); + assert_eq!(format_by_duration(&ONE_YEAR.into()), Format::YDHhMmSs); // YDdHhMmSs - c.set_current_value((ONE_YEAR + (100 * ONE_DAY).saturating_sub(ONE_SECOND)).into()); - assert_eq!(c.get_format(), Format::YDdHhMmSs); + assert_eq!( + format_by_duration(&(ONE_YEAR + (100 * ONE_DAY).saturating_sub(ONE_SECOND)).into()), + Format::YDdHhMmSs + ); // YDddHhMmSs - c.set_current_value((ONE_YEAR + 100 * ONE_DAY).into()); - assert_eq!(c.get_format(), Format::YDddHhMmSs); + assert_eq!( + format_by_duration(&(ONE_YEAR + 100 * ONE_DAY).into()), + Format::YDddHhMmSs + ); // YDddHhMmSs - c.set_current_value(((10 * ONE_YEAR).saturating_sub(ONE_SECOND)).into()); - assert_eq!(c.get_format(), Format::YDddHhMmSs); + assert_eq!( + format_by_duration(&((10 * ONE_YEAR).saturating_sub(ONE_SECOND)).into()), + Format::YDddHhMmSs + ); // YyDHhMmSs - c.set_current_value((10 * ONE_YEAR).into()); - assert_eq!(c.get_format(), Format::YyDHhMmSs); + assert_eq!( + format_by_duration(&(10 * ONE_YEAR).into()), + Format::YyDHhMmSs + ); // YyDdHhMmSs - c.set_current_value((10 * ONE_YEAR + 10 * ONE_DAY).into()); - assert_eq!(c.get_format(), Format::YyDdHhMmSs); + assert_eq!( + format_by_duration(&(10 * ONE_YEAR + 10 * ONE_DAY).into()), + 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); + assert_eq!( + format_by_duration(&(10 * ONE_YEAR + (100 * ONE_DAY).saturating_sub(ONE_SECOND)).into()), + Format::YyDdHhMmSs + ); // YyDddHhMmSs - c.set_current_value((10 * ONE_YEAR + 100 * ONE_DAY).into()); - assert_eq!(c.get_format(), Format::YyDddHhMmSs); + assert_eq!( + format_by_duration(&(10 * ONE_YEAR + 100 * ONE_DAY).into()), + Format::YyDddHhMmSs + ); // YyDddHhMmSs - c.set_current_value(((100 * ONE_YEAR).saturating_sub(ONE_SECOND)).into()); - assert_eq!(c.get_format(), Format::YyDddHhMmSs); + assert_eq!( + format_by_duration(&((100 * ONE_YEAR).saturating_sub(ONE_SECOND)).into()), + Format::YyDddHhMmSs + ); // YyyDHhMmSs - c.set_current_value((100 * ONE_YEAR).into()); - assert_eq!(c.get_format(), Format::YyyDHhMmSs); + assert_eq!( + format_by_duration(&(100 * ONE_YEAR).into()), + Format::YyyDHhMmSs + ); // YyyDdHhMmSs - c.set_current_value((100 * ONE_YEAR + 10 * ONE_DAY).into()); - assert_eq!(c.get_format(), Format::YyyDdHhMmSs); + assert_eq!( + format_by_duration(&(100 * ONE_YEAR + 10 * ONE_DAY).into()), + 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); + assert_eq!( + format_by_duration(&(100 * ONE_YEAR + (100 * ONE_DAY).saturating_sub(ONE_SECOND)).into()), + Format::YyyDdHhMmSs + ); // YyyDddHhMmSs - c.set_current_value((100 * ONE_YEAR + 100 * ONE_DAY).into()); - assert_eq!(c.get_format(), Format::YyyDddHhMmSs); + assert_eq!( + format_by_duration(&(100 * ONE_YEAR + 100 * ONE_DAY).into()), + Format::YyyDddHhMmSs + ); } #[test] -fn test_get_format_days() { - let mut c = ClockState::::new(ClockStateArgs { - initial_value: ONE_DAY, - current_value: ONE_DAY, - tick_value: ONE_DECI_SECOND, - with_decis: false, - app_tx: None, - }); +fn test_format_by_duration_days() { // DHhMmSs - assert_eq!(c.get_format(), Format::DHhMmSs); + assert_eq!(format_by_duration(&ONE_DAY.into()), Format::DHhMmSs); // DdHhMmSs - c.set_current_value((10 * ONE_DAY).into()); - assert_eq!(c.get_format(), Format::DdHhMmSs); + assert_eq!(format_by_duration(&(10 * ONE_DAY).into()), Format::DdHhMmSs); // DddHhMmSs - c.set_current_value((101 * ONE_DAY).into()); - assert_eq!(c.get_format(), Format::DddHhMmSs); + assert_eq!( + format_by_duration(&(101 * ONE_DAY).into()), + Format::DddHhMmSs + ); } #[test] -fn test_get_format_years() { - let mut c = ClockState::::new(ClockStateArgs { - initial_value: ONE_YEAR, - current_value: ONE_YEAR, - tick_value: ONE_DECI_SECOND, - with_decis: false, - app_tx: None, - }); +fn test_format_by_duration_years() { // YDHhMmSs (1 year, 0 days) - assert_eq!(c.get_format(), Format::YDHhMmSs); + assert_eq!(format_by_duration(&ONE_YEAR.into()), Format::YDHhMmSs); // YDHhMmSs (1 year, 1 day) - c.set_current_value((ONE_YEAR + ONE_DAY).into()); - assert_eq!(c.get_format(), Format::YDHhMmSs); + assert_eq!( + format_by_duration(&(ONE_YEAR + ONE_DAY).into()), + Format::YDHhMmSs + ); // YDdHhMmSs (1 year, 10 days) - c.set_current_value((ONE_YEAR + 10 * ONE_DAY).into()); - assert_eq!(c.get_format(), Format::YDdHhMmSs); + assert_eq!( + format_by_duration(&(ONE_YEAR + 10 * ONE_DAY).into()), + Format::YDdHhMmSs + ); // YDddHhMmSs (1 year, 100 days) - c.set_current_value((ONE_YEAR + 100 * ONE_DAY).into()); - assert_eq!(c.get_format(), Format::YDddHhMmSs); + assert_eq!( + format_by_duration(&(ONE_YEAR + 100 * ONE_DAY).into()), + Format::YDddHhMmSs + ); // YyDHhMmSs (10 years) - c.set_current_value((10 * ONE_YEAR).into()); - assert_eq!(c.get_format(), Format::YyDHhMmSs); + assert_eq!( + format_by_duration(&(10 * ONE_YEAR).into()), + 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); + assert_eq!( + format_by_duration(&(10 * ONE_YEAR + 10 * ONE_DAY).into()), + 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); + assert_eq!( + format_by_duration(&(10 * ONE_YEAR + 100 * ONE_DAY).into()), + Format::YyDddHhMmSs + ); // YyyDHhMmSs (100 years) - c.set_current_value((100 * ONE_YEAR).into()); - assert_eq!(c.get_format(), Format::YyyDHhMmSs); + assert_eq!( + format_by_duration(&(100 * ONE_YEAR).into()), + 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); + assert_eq!( + format_by_duration(&(100 * ONE_YEAR + 10 * ONE_DAY).into()), + 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); + assert_eq!( + format_by_duration(&(100 * ONE_YEAR + 100 * ONE_DAY).into()), + Format::YyyDddHhMmSs + ); } #[test] @@ -371,8 +395,8 @@ fn test_edit_up_overflow_protection() { #[test] fn test_edit_down_years_to_days() { let mut c = ClockState::::new(ClockStateArgs { - initial_value: ONE_YEAR, - current_value: ONE_YEAR, + initial_value: ONE_YEAR + ONE_DAY, + current_value: ONE_YEAR + ONE_DAY, tick_value: ONE_DECI_SECOND, with_decis: false, app_tx: None, @@ -390,8 +414,8 @@ fn test_edit_down_years_to_days() { #[test] fn test_edit_down_days_to_hours() { let mut c = ClockState::::new(ClockStateArgs { - initial_value: ONE_DAY, - current_value: ONE_DAY, + initial_value: ONE_DAY + ONE_HOUR, + current_value: ONE_DAY + ONE_HOUR, tick_value: ONE_DECI_SECOND, with_decis: false, app_tx: None, @@ -408,8 +432,8 @@ fn test_edit_down_days_to_hours() { #[test] fn test_edit_down_hours_to_minutes() { let mut c = ClockState::::new(ClockStateArgs { - initial_value: ONE_HOUR, - current_value: ONE_HOUR, + initial_value: ONE_HOUR + ONE_MINUTE, + current_value: ONE_HOUR + ONE_MINUTE, tick_value: ONE_DECI_SECOND, with_decis: false, app_tx: None, @@ -474,9 +498,6 @@ fn test_edit_hours_in_dhhmmss_format() { app_tx: None, }); - // Should be in D HH:MM:SS format - assert_eq!(c.get_format(), Format::DHhMmSs); - c.toggle_edit(); c.edit_next(); // Move to Hours assert!(matches!(c.get_mode(), Mode::Editable(Time::Hours, _))); diff --git a/src/widgets/countdown.rs b/src/widgets/countdown.rs index 3e31897..e98579b 100644 --- a/src/widgets/countdown.rs +++ b/src/widgets/countdown.rs @@ -372,10 +372,11 @@ impl StatefulWidget for Countdown { .to_uppercase(), ); let widget = ClockWidget::new(self.style, self.blink); + let area = center( area, Constraint::Length(max( - widget.get_width(&state.clock.get_format(), state.clock.with_decis), + widget.get_width(state.clock.get_format(), state.clock.with_decis), label.width() as u16, )), Constraint::Length(widget.get_height() + 1 /* height of label */), diff --git a/src/widgets/pomodoro.rs b/src/widgets/pomodoro.rs index 605474b..b7c1843 100644 --- a/src/widgets/pomodoro.rs +++ b/src/widgets/pomodoro.rs @@ -250,10 +250,8 @@ impl StatefulWidget for PomodoroWidget { let area = center( area, Constraint::Length(max( - clock_widget.get_width( - &state.get_clock().get_format(), - state.get_clock().with_decis, - ), + clock_widget + .get_width(state.get_clock().get_format(), state.get_clock().with_decis), label.width() as u16, )), Constraint::Length( diff --git a/src/widgets/timer.rs b/src/widgets/timer.rs index 3d1ccdf..af2bae2 100644 --- a/src/widgets/timer.rs +++ b/src/widgets/timer.rs @@ -107,7 +107,7 @@ impl StatefulWidget for Timer { let area = center( area, Constraint::Length(max( - clock_widget.get_width(&clock.get_format(), clock.with_decis), + clock_widget.get_width(clock.get_format(), clock.with_decis), label.width() as u16, )), Constraint::Length(clock_widget.get_height() + 1 /* height of label */),