From d6102dffa2cdcabd74d03c25332a54dc1a02c958 Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Wed, 17 Jul 2024 16:30:39 +0200 Subject: [PATCH 01/10] refactor: move clock parts to submodules --- src/clock.rs | 81 +++----------------------------------------- src/clock/timebar.rs | 27 +++++++++++++++ src/clock/uidata.rs | 51 ++++++++++++++++++++++++++++ src/main.rs | 2 +- 4 files changed, 83 insertions(+), 78 deletions(-) create mode 100644 src/clock/timebar.rs create mode 100644 src/clock/uidata.rs diff --git a/src/clock.rs b/src/clock.rs index 97b948c..5d90bd0 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -16,33 +16,10 @@ use ratatui::Terminal; use std::io::{Cursor, Stdout, Write}; use std::time::{Duration, Instant}; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TimeBarLength { - Minute, - Hour, - Custom(i128), - /// implementing a bar that would grow smaller would be weird, so it's a count up instead of - /// a countdown - Countup(i128), - Day, -} - -impl TimeBarLength { - pub(crate) const fn as_secs(self) -> i128 { - match self { - Self::Minute => 60, - Self::Day => 24 * 60 * 60, - Self::Hour => 60 * 60, - Self::Custom(secs) | Self::Countup(secs) => secs, - } - } -} - -impl Default for TimeBarLength { - fn default() -> Self { - Self::Minute - } -} +pub mod timebar; +pub mod uidata; +use timebar::TimeBarLength; +use uidata::UiData; /// Make your terminal into a big clock #[derive(Parser, Debug, Clone)] @@ -89,56 +66,6 @@ pub struct Clock { pub(crate) did_notify: bool, } -#[derive(Debug, Clone, PartialEq, Default)] -pub struct UiData { - fdate: [String; 2], - ftime: [String; 2], - timebar_ratio: [Option; 2], - - data_idx: usize, -} - -impl UiData { - pub fn update(&mut self, fdate: String, ftime: String, timebar_ratio: Option) { - self.data_idx ^= 1; - self.fdate[self.data_idx] = fdate; - self.ftime[self.data_idx] = ftime; - self.timebar_ratio[self.data_idx] = timebar_ratio; - #[cfg(debug_assertions)] - if self.changed() { - trace!("update with change: {:#?}", self); - } - } - - /// did the data change with the last update? - #[must_use] - #[inline] - pub fn changed(&self) -> bool { - // the timebar ratio is discarded, so that we only render the ui when the time - // (second) changes - self.fdate[0] != self.fdate[1] || self.ftime[0] != self.ftime[1] - } - - #[must_use] - #[inline] - pub fn fdate(&self) -> &str { - &self.fdate[self.data_idx] - } - - #[must_use] - #[inline] - pub fn ftime(&self) -> &str { - &self.ftime[self.data_idx] - } - - #[must_use] - #[inline] - #[allow(clippy::missing_const_for_fn)] // no it's not const - pub fn timebar_ratio(&self) -> Option { - self.timebar_ratio[self.data_idx] - } -} - impl Clock { #[must_use] #[allow(clippy::missing_const_for_fn)] diff --git a/src/clock/timebar.rs b/src/clock/timebar.rs new file mode 100644 index 0000000..5886308 --- /dev/null +++ b/src/clock/timebar.rs @@ -0,0 +1,27 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TimeBarLength { + Minute, + Hour, + Custom(i128), + /// implementing a bar that would grow smaller would be weird, so it's a count up instead of + /// a countdown + Countup(i128), + Day, +} + +impl TimeBarLength { + pub(crate) const fn as_secs(self) -> i128 { + match self { + Self::Minute => 60, + Self::Day => 24 * 60 * 60, + Self::Hour => 60 * 60, + Self::Custom(secs) | Self::Countup(secs) => secs, + } + } +} + +impl Default for TimeBarLength { + fn default() -> Self { + Self::Minute + } +} diff --git a/src/clock/uidata.rs b/src/clock/uidata.rs new file mode 100644 index 0000000..c801c6e --- /dev/null +++ b/src/clock/uidata.rs @@ -0,0 +1,51 @@ +use libpt::log::trace; + +#[derive(Debug, Clone, PartialEq, Default)] +pub struct UiData { + fdate: [String; 2], + ftime: [String; 2], + timebar_ratio: [Option; 2], + + data_idx: usize, +} + +impl UiData { + pub fn update(&mut self, fdate: String, ftime: String, timebar_ratio: Option) { + self.data_idx ^= 1; + self.fdate[self.data_idx] = fdate; + self.ftime[self.data_idx] = ftime; + self.timebar_ratio[self.data_idx] = timebar_ratio; + #[cfg(debug_assertions)] + if self.changed() { + trace!("update with change: {:#?}", self); + } + } + + /// did the data change with the last update? + #[must_use] + #[inline] + pub fn changed(&self) -> bool { + // the timebar ratio is discarded, so that we only render the ui when the time + // (second) changes + self.fdate[0] != self.fdate[1] || self.ftime[0] != self.ftime[1] + } + + #[must_use] + #[inline] + pub fn fdate(&self) -> &str { + &self.fdate[self.data_idx] + } + + #[must_use] + #[inline] + pub fn ftime(&self) -> &str { + &self.ftime[self.data_idx] + } + + #[must_use] + #[inline] + #[allow(clippy::missing_const_for_fn)] // no it's not const + pub fn timebar_ratio(&self) -> Option { + self.timebar_ratio[self.data_idx] + } +} diff --git a/src/main.rs b/src/main.rs index 065a46b..c933f04 100644 --- a/src/main.rs +++ b/src/main.rs @@ -63,7 +63,7 @@ fn mock_tests() { use chrono::{Local, Timelike}; use libpt::log::info; - use self::clock::UiData; + use crate::clock::uidata::UiData; info!("doing the mock tests"); { let mut c = Clock::parse_from(["some exec", "-mvvv"]); From 700f236649c7dc0a1ee5f31078afc8daa4b6677b Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Wed, 17 Jul 2024 16:50:32 +0200 Subject: [PATCH 02/10] refactor: partition to a HashMap instead of a Vec --- src/clock.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/clock.rs b/src/clock.rs index 5d90bd0..7d9f961 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -13,6 +13,7 @@ use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect}; use ratatui::style::{Style, Stylize}; use ratatui::widgets::{Block, LineGauge, Padding, Paragraph}; use ratatui::Terminal; +use std::collections::HashMap; use std::io::{Cursor, Stdout, Write}; use std::time::{Duration, Instant}; @@ -304,8 +305,8 @@ impl Clock { #[allow(clippy::cast_sign_loss)] #[allow(clippy::cast_possible_truncation)] let padding = [ - (f32::from(parts[2].width) * 0.43) as u16, - (f32::from(parts[2].width) * 0.25) as u16, + (f32::from(parts["timebarw"].width) * 0.43) as u16, + (f32::from(parts["timebarw"].width) * 0.25) as u16, ]; let timebarw = LineGauge::default() .filled_style(if self.did_notify { @@ -335,10 +336,10 @@ impl Clock { .blue() .block(Block::default().padding(Padding::right(2))) .alignment(Alignment::Right); - frame.render_widget(&timebarw, parts[2]); - frame.render_widget(datew, parts[1]); + frame.render_widget(&timebarw, parts["timebarw"]); + frame.render_widget(datew, parts["datew"]); // render the clock - frame.render_widget(clockw, parts[0]); + frame.render_widget(clockw, parts["clockw"]); })?; debug!("done rendering the ui"); Ok(()) @@ -412,7 +413,7 @@ impl Clock { std::io::stdout().flush()?; Ok(()) } - fn partition(r: Rect) -> Vec { + fn partition(r: Rect) -> HashMap<&'static str, Rect> { let part = Layout::default() .direction(Direction::Vertical) .constraints([ @@ -431,6 +432,10 @@ impl Clock { ]) .split(part[0]); - vec![part[1], subparts[0], subparts[1]] + HashMap::from([ + ("clockw", part[1]), + ("timebarw", subparts[1]), + ("datew", subparts[0]), + ]) } } From 74e39a08dc223fd80f1c6ba1e3674ea219520dfc Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Wed, 17 Jul 2024 17:46:22 +0200 Subject: [PATCH 03/10] feat(timebar): show mode and counter for timebar --- src/clock.rs | 88 ++++++++--------------- src/clock/timebar.rs | 38 +++++++++- src/clock/ui.rs | 163 +++++++++++++++++++++++++++++++++++++++++++ src/clock/uidata.rs | 51 -------------- src/main.rs | 9 +-- 5 files changed, 233 insertions(+), 116 deletions(-) create mode 100644 src/clock/ui.rs delete mode 100644 src/clock/uidata.rs diff --git a/src/clock.rs b/src/clock.rs index 7d9f961..a0cde82 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -15,12 +15,12 @@ use ratatui::widgets::{Block, LineGauge, Padding, Paragraph}; use ratatui::Terminal; use std::collections::HashMap; use std::io::{Cursor, Stdout, Write}; -use std::time::{Duration, Instant}; +use std::time::Instant; pub mod timebar; -pub mod uidata; +pub mod ui; use timebar::TimeBarLength; -use uidata::UiData; +use ui::UiData; /// Make your terminal into a big clock #[derive(Parser, Debug, Clone)] @@ -78,13 +78,11 @@ impl Clock { } else if self.hour { Some(TimeBarLength::Hour) } else if self.countdown.is_some() { - Some(TimeBarLength::Countup(i128::from( - self.countdown.unwrap().as_secs(), - ))) + Some(TimeBarLength::Countup( + self.countdown.unwrap().as_secs() as i64 + )) } else if self.custom.is_some() { - Some(TimeBarLength::Custom(i128::from( - self.custom.unwrap().as_secs(), - ))) + Some(TimeBarLength::Custom(self.custom.unwrap().as_secs() as i64)) } else { None } @@ -113,7 +111,7 @@ impl Clock { } TimeBarLength::Custom(_) => { if since_last_reset.num_seconds() >= 1 - && i128::from(since_last_reset.num_seconds()) >= len.as_secs() + && i64::from(since_last_reset.num_seconds()) >= len.as_secs() { self.last_reset = Some(Local::now()); } @@ -190,7 +188,7 @@ impl Clock { mut self, terminal: &mut Terminal>, ) -> anyhow::Result<()> { - let tick_rate = Duration::from_millis(100); + let tick_rate = std::time::Duration::from_millis(100); let mut last_tick = Instant::now(); let mut uidata: UiData = UiData::default(); self.setup()?; @@ -219,10 +217,12 @@ impl Clock { // 01:30 is 50%, 01:59 is 98%, 01:60 does not exist because that's how counting from // 0 works. + let now = raw_time + chrono::Duration::seconds(1); uidata.update( + now, splits[0].clone(), splits[1].clone(), - self.timebar_ratio(raw_time + chrono::Duration::seconds(1)), + self.timebar_ratio(now), ); if uidata.changed() { self.ui(terminal, &uidata)?; @@ -267,12 +267,12 @@ impl Clock { .title_bottom(env!("CARGO_PKG_VERSION")) .title_alignment(Alignment::Center) .title_style(Style::new().bold()); - let a = space.inner(root); + let inner_rect = space.inner(root); frame.render_widget(space, root); - let parts = Self::partition(a); + let parts = Self::partition(inner_rect); let mut clockw = tui_big_text::BigText::builder(); - if a.width > 80 { + if inner_rect.width > 80 { clockw.pixel_size(tui_big_text::PixelSize::Full); } else { clockw.pixel_size(tui_big_text::PixelSize::Quadrant); @@ -288,48 +288,13 @@ impl Clock { // render the timebar which counts up to the full minute and so on // // Will not be rendered if it is None - let timebarw: Option = if self.timebar_len().is_some() { - debug!("time bar ration: {:?}", data.timebar_ratio()); - let ratio = data.timebar_ratio().unwrap(); - - if !self.did_notify && (ratio - 1.0).abs() < 0.000_001 { - if let Some(TimeBarLength::Countup(_)) = self.timebar_len() { - let _ = self.notify().inspect_err(|e| { - error!("could not notify: {e}"); - debug!("complete error: {e:#?}"); - }); - self.did_notify = true; - } - } - - #[allow(clippy::cast_sign_loss)] - #[allow(clippy::cast_possible_truncation)] - let padding = [ - (f32::from(parts["timebarw"].width) * 0.43) as u16, - (f32::from(parts["timebarw"].width) * 0.25) as u16, - ]; - let timebarw = LineGauge::default() - .filled_style(if self.did_notify { - Style::default() - .slow_blink() - .bold() - .underlined() - .yellow() - .crossed_out() - } else { - Style::default().blue() - }) - .unfilled_style(Style::default()) - .block(Block::default().padding(Padding::right(if a.width > 80 { - padding[0] - } else { - padding[1] - }))) - .ratio(ratio); - Some(timebarw) - } else { - None - }; + let timebarw_padding = [ + (f32::from(parts["timebarw"].width) * 0.43) as u16, + (f32::from(parts["timebarw"].width) * 0.25) as u16, + ]; + let timebarw = ui::timebarw(self, data, &timebarw_padding, inner_rect); + let timebarw_label: Option = + ui::timebarw_label(self, data, &timebarw_padding, inner_rect); // render the small date let datew = Paragraph::new(data.fdate()) @@ -337,6 +302,7 @@ impl Clock { .block(Block::default().padding(Padding::right(2))) .alignment(Alignment::Right); frame.render_widget(&timebarw, parts["timebarw"]); + frame.render_widget(&timebarw_label, parts["timebarw_label"]); frame.render_widget(datew, parts["datew"]); // render the clock frame.render_widget(clockw, parts["clockw"]); @@ -432,9 +398,15 @@ impl Clock { ]) .split(part[0]); + let timebarw_spaces = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Length(1), Constraint::Length(1)]) + .split(subparts[1]); + HashMap::from([ ("clockw", part[1]), - ("timebarw", subparts[1]), + ("timebarw", timebarw_spaces[0]), + ("timebarw_label", timebarw_spaces[1]), ("datew", subparts[0]), ]) } diff --git a/src/clock/timebar.rs b/src/clock/timebar.rs index 5886308..410f1d6 100644 --- a/src/clock/timebar.rs +++ b/src/clock/timebar.rs @@ -1,16 +1,20 @@ +use std::fmt::Display; + +use chrono::{Duration, TimeDelta}; + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TimeBarLength { Minute, Hour, - Custom(i128), + Custom(i64), /// implementing a bar that would grow smaller would be weird, so it's a count up instead of /// a countdown - Countup(i128), + Countup(i64), Day, } impl TimeBarLength { - pub(crate) const fn as_secs(self) -> i128 { + pub(crate) const fn as_secs(self) -> i64 { match self { Self::Minute => 60, Self::Day => 24 * 60 * 60, @@ -25,3 +29,31 @@ impl Default for TimeBarLength { Self::Minute } } + +impl Display for TimeBarLength { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let buf = match self { + Self::Minute => humantime::Duration::from( + Duration::minutes(1) + .to_std() + .expect("could not convert chrono time to std time"), + ), + Self::Hour => humantime::Duration::from( + Duration::hours(1) + .to_std() + .expect("could not convert chrono time to std time"), + ), + Self::Day => humantime::Duration::from( + Duration::days(1) + .to_std() + .expect("could not convert chrono time to std time"), + ), + Self::Custom(secs) | Self::Countup(secs) => humantime::Duration::from( + Duration::seconds(*secs) + .to_std() + .expect("could not convert chrono time to std time"), + ), + }; + write!(f, "{buf}") + } +} diff --git a/src/clock/ui.rs b/src/clock/ui.rs new file mode 100644 index 0000000..2f5f72e --- /dev/null +++ b/src/clock/ui.rs @@ -0,0 +1,163 @@ +use chrono::{DateTime, Local, SubsecRound}; +use libpt::log::{debug, error, trace}; +use ratatui::layout::{Alignment, Rect}; +use ratatui::style::{Style, Stylize}; +use ratatui::widgets::{Block, LineGauge, Padding, Paragraph}; + +use crate::clock::timebar::TimeBarLength; + +use super::Clock; + +#[derive(Debug, Clone, PartialEq, Default)] +pub struct UiData { + now: [DateTime; 2], + fdate: [String; 2], + ftime: [String; 2], + timebar_ratio: [Option; 2], + + data_idx: usize, +} + +impl UiData { + pub fn update( + &mut self, + now: DateTime, + fdate: String, + ftime: String, + timebar_ratio: Option, + ) { + self.data_idx ^= 1; + self.now[self.data_idx] = now; + self.fdate[self.data_idx] = fdate; + self.ftime[self.data_idx] = ftime; + self.timebar_ratio[self.data_idx] = timebar_ratio; + #[cfg(debug_assertions)] + if self.changed() { + trace!("update with change: {:#?}", self); + } + } + + /// did the data change with the last update? + #[must_use] + #[inline] + pub fn changed(&self) -> bool { + // the timebar ratio is discarded, so that we only render the ui when the time + // (second) changes + self.fdate[0] != self.fdate[1] || self.ftime[0] != self.ftime[1] + } + + #[must_use] + #[inline] + pub fn fdate(&self) -> &str { + &self.fdate[self.data_idx] + } + + #[must_use] + #[inline] + pub fn ftime(&self) -> &str { + &self.ftime[self.data_idx] + } + + #[must_use] + #[inline] + pub fn now(&self) -> &DateTime { + &self.now[self.data_idx] + } + + #[must_use] + #[inline] + #[allow(clippy::missing_const_for_fn)] // no it's not const + pub fn timebar_ratio(&self) -> Option { + self.timebar_ratio[self.data_idx] + } +} + +pub fn timebarw<'a>( + clock: &mut Clock, + data: &UiData, + timebarw_padding: &[u16], + inner_rect: Rect, +) -> Option> { + if clock.timebar_len().is_some() { + debug!("time bar ration: {:?}", data.timebar_ratio()); + let ratio = data.timebar_ratio().unwrap(); + + if !clock.did_notify && (ratio - 1.0).abs() < 0.000_001 { + if let Some(TimeBarLength::Countup(_)) = clock.timebar_len() { + let _ = clock.notify().inspect_err(|e| { + error!("could not notify: {e}"); + debug!("complete error: {e:#?}"); + }); + clock.did_notify = true; + } + } + + #[allow(clippy::cast_sign_loss)] + #[allow(clippy::cast_possible_truncation)] + let timebarw = LineGauge::default() + .filled_style(if clock.did_notify { + Style::default() + .slow_blink() + .bold() + .underlined() + .yellow() + .crossed_out() + } else { + Style::default().blue() + }) + .unfilled_style(Style::default()) + .block( + Block::default().padding(Padding::right(if inner_rect.width > 80 { + timebarw_padding[0] + } else { + timebarw_padding[1] + })), + ) + .ratio(ratio); + Some(timebarw) + } else { + None + } +} + +pub fn timebarw_label<'a>( + clock: &Clock, + data: &UiData, + timebarw_padding: &[u16], + inner_rect: Rect, +) -> Option> { + clock.timebar_len().map(|len| { + let last_reset = clock.last_reset.unwrap().round_subsecs(0); + let time_now = match clock.timebar_len().unwrap() { + TimeBarLength::Countup(secs) => { + if clock.did_notify { + humantime::Duration::from(chrono::Duration::seconds(secs).to_std().unwrap()) + } else { + humantime::Duration::from( + data.now() + .round_subsecs(0) + .signed_duration_since(last_reset) + .to_std() + .unwrap(), + ) + } + } + _ => humantime::Duration::from( + data.now() + .round_subsecs(0) + .signed_duration_since(last_reset) + .to_std() + .unwrap(), + ), + }; + Paragraph::new(format!("{} / {}", time_now, len)) + .alignment(Alignment::Center) + .block( + Block::default().padding(Padding::right(if inner_rect.width > 80 { + timebarw_padding[0] + } else { + timebarw_padding[1] + })), + ) + }) +} diff --git a/src/clock/uidata.rs b/src/clock/uidata.rs deleted file mode 100644 index c801c6e..0000000 --- a/src/clock/uidata.rs +++ /dev/null @@ -1,51 +0,0 @@ -use libpt::log::trace; - -#[derive(Debug, Clone, PartialEq, Default)] -pub struct UiData { - fdate: [String; 2], - ftime: [String; 2], - timebar_ratio: [Option; 2], - - data_idx: usize, -} - -impl UiData { - pub fn update(&mut self, fdate: String, ftime: String, timebar_ratio: Option) { - self.data_idx ^= 1; - self.fdate[self.data_idx] = fdate; - self.ftime[self.data_idx] = ftime; - self.timebar_ratio[self.data_idx] = timebar_ratio; - #[cfg(debug_assertions)] - if self.changed() { - trace!("update with change: {:#?}", self); - } - } - - /// did the data change with the last update? - #[must_use] - #[inline] - pub fn changed(&self) -> bool { - // the timebar ratio is discarded, so that we only render the ui when the time - // (second) changes - self.fdate[0] != self.fdate[1] || self.ftime[0] != self.ftime[1] - } - - #[must_use] - #[inline] - pub fn fdate(&self) -> &str { - &self.fdate[self.data_idx] - } - - #[must_use] - #[inline] - pub fn ftime(&self) -> &str { - &self.ftime[self.data_idx] - } - - #[must_use] - #[inline] - #[allow(clippy::missing_const_for_fn)] // no it's not const - pub fn timebar_ratio(&self) -> Option { - self.timebar_ratio[self.data_idx] - } -} diff --git a/src/main.rs b/src/main.rs index c933f04..6f44694 100644 --- a/src/main.rs +++ b/src/main.rs @@ -63,7 +63,7 @@ fn mock_tests() { use chrono::{Local, Timelike}; use libpt::log::info; - use crate::clock::uidata::UiData; + use crate::clock::ui::UiData; info!("doing the mock tests"); { let mut c = Clock::parse_from(["some exec", "-mvvv"]); @@ -82,11 +82,12 @@ fn mock_tests() { } { let mut data = UiData::default(); - data.update("date".to_owned(), "time".to_owned(), Some(0.1)); + let now = Local::now(); + data.update(now, "date".to_owned(), "time".to_owned(), Some(0.1)); assert_eq!(data.timebar_ratio(), Some(0.1)); - data.update("date".to_owned(), "time".to_owned(), Some(0.2)); + data.update(now, "date".to_owned(), "time".to_owned(), Some(0.2)); assert_eq!(data.timebar_ratio(), Some(0.2)); - data.update("date".to_owned(), "time".to_owned(), Some(0.3)); + data.update(now, "date".to_owned(), "time".to_owned(), Some(0.3)); assert_eq!(data.timebar_ratio(), Some(0.3)); } info!("finished the mock tests"); From 290704de46cb7be95bf924f2ebfcf037c3669040 Mon Sep 17 00:00:00 2001 From: cscherrNT Date: Wed, 17 Jul 2024 15:48:15 +0000 Subject: [PATCH 04/10] automatic cargo CI changes --- src/clock.rs | 4 ++-- src/clock/timebar.rs | 2 +- src/clock/ui.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/clock.rs b/src/clock.rs index a0cde82..9f45cc9 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -11,7 +11,7 @@ use ratatui::backend::CrosstermBackend; use ratatui::crossterm::event::{self, poll, Event, KeyCode, KeyModifiers}; use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect}; use ratatui::style::{Style, Stylize}; -use ratatui::widgets::{Block, LineGauge, Padding, Paragraph}; +use ratatui::widgets::{Block, Padding, Paragraph}; use ratatui::Terminal; use std::collections::HashMap; use std::io::{Cursor, Stdout, Write}; @@ -111,7 +111,7 @@ impl Clock { } TimeBarLength::Custom(_) => { if since_last_reset.num_seconds() >= 1 - && i64::from(since_last_reset.num_seconds()) >= len.as_secs() + && since_last_reset.num_seconds() >= len.as_secs() { self.last_reset = Some(Local::now()); } diff --git a/src/clock/timebar.rs b/src/clock/timebar.rs index 410f1d6..3d761d2 100644 --- a/src/clock/timebar.rs +++ b/src/clock/timebar.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use chrono::{Duration, TimeDelta}; +use chrono::Duration; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TimeBarLength { diff --git a/src/clock/ui.rs b/src/clock/ui.rs index 2f5f72e..fe6dc7b 100644 --- a/src/clock/ui.rs +++ b/src/clock/ui.rs @@ -150,7 +150,7 @@ pub fn timebarw_label<'a>( .unwrap(), ), }; - Paragraph::new(format!("{} / {}", time_now, len)) + Paragraph::new(format!("{time_now} / {len}")) .alignment(Alignment::Center) .block( Block::default().padding(Padding::right(if inner_rect.width > 80 { From 19cc0d5d1adab41151a996480139817b623115c2 Mon Sep 17 00:00:00 2001 From: PlexSheep Date: Wed, 17 Jul 2024 17:52:35 +0200 Subject: [PATCH 05/10] chore: bump version to alpha --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index bbd6632..c0b0bc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "crock" -version = "0.2.0" +version = "0.2.1-alpha.0" edition = "2021" publish = true authors = ["Christoph J. Scherr "] From 3a4c3d7c00dd77b4a7488b2a22254c174d3e581f Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Fri, 19 Jul 2024 09:42:33 +0200 Subject: [PATCH 06/10] feat(modeshow): add a timestamp that shows when the timer ends #14 --- src/clock/timebar.rs | 6 ++++++ src/clock/ui.rs | 9 +++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/clock/timebar.rs b/src/clock/timebar.rs index 3d761d2..4be656e 100644 --- a/src/clock/timebar.rs +++ b/src/clock/timebar.rs @@ -24,6 +24,12 @@ impl TimeBarLength { } } +impl From for chrono::Duration { + fn from(value: TimeBarLength) -> Self { + Self::new(value.as_secs(), 0).expect("seconds out of bounds, cannot create duration") + } +} + impl Default for TimeBarLength { fn default() -> Self { Self::Minute diff --git a/src/clock/ui.rs b/src/clock/ui.rs index fe6dc7b..0027ab0 100644 --- a/src/clock/ui.rs +++ b/src/clock/ui.rs @@ -1,4 +1,4 @@ -use chrono::{DateTime, Local, SubsecRound}; +use chrono::{DateTime, Local, SubsecRound, Timelike}; use libpt::log::{debug, error, trace}; use ratatui::layout::{Alignment, Rect}; use ratatui::style::{Style, Stylize}; @@ -150,7 +150,12 @@ pub fn timebarw_label<'a>( .unwrap(), ), }; - Paragraph::new(format!("{time_now} / {len}")) + let until = last_reset + .checked_add_signed(len.into()) + .expect("could not calculate when the countdown finishes"); + let timestamp_until: String = + format!("{}:{}:{}", until.hour(), until.minute(), until.second()); + Paragraph::new(format!("{time_now} / {len} ({timestamp_until})")) .alignment(Alignment::Center) .block( Block::default().padding(Padding::right(if inner_rect.width > 80 { From 4ec8be1e1be8124c54d82e598c6728c91680ab8d Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Fri, 19 Jul 2024 09:58:54 +0200 Subject: [PATCH 07/10] chore: add bug comment for #17 --- src/clock/ui.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/clock/ui.rs b/src/clock/ui.rs index 0027ab0..91453b0 100644 --- a/src/clock/ui.rs +++ b/src/clock/ui.rs @@ -151,6 +151,8 @@ pub fn timebarw_label<'a>( ), }; let until = last_reset + // BUG: seconds are sometimes a little too much, for + // example with `-o` #17 .checked_add_signed(len.into()) .expect("could not calculate when the countdown finishes"); let timestamp_until: String = From 252677102660fbf8bc77b14f2ce674673515f837 Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Fri, 19 Jul 2024 09:59:18 +0200 Subject: [PATCH 08/10] refactor(uidata): change name of the struct and the idx --- src/clock.rs | 6 +++--- src/clock/ui.rs | 28 ++++++++++++++-------------- src/main.rs | 4 ++-- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/clock.rs b/src/clock.rs index 9f45cc9..99ad61b 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -20,7 +20,7 @@ use std::time::Instant; pub mod timebar; pub mod ui; use timebar::TimeBarLength; -use ui::UiData; +use ui::Data; /// Make your terminal into a big clock #[derive(Parser, Debug, Clone)] @@ -190,7 +190,7 @@ impl Clock { ) -> anyhow::Result<()> { let tick_rate = std::time::Duration::from_millis(100); let mut last_tick = Instant::now(); - let mut uidata: UiData = UiData::default(); + let mut uidata: Data = Data::default(); self.setup()?; loop { let raw_time = chrono::Local::now().round_subsecs(0); @@ -251,7 +251,7 @@ impl Clock { fn ui( &mut self, terminal: &mut Terminal>, - data: &UiData, + data: &Data, ) -> anyhow::Result<()> { terminal.draw(|frame| { debug!("rendering the ui"); diff --git a/src/clock/ui.rs b/src/clock/ui.rs index 91453b0..0aa3ebd 100644 --- a/src/clock/ui.rs +++ b/src/clock/ui.rs @@ -9,16 +9,16 @@ use crate::clock::timebar::TimeBarLength; use super::Clock; #[derive(Debug, Clone, PartialEq, Default)] -pub struct UiData { +pub struct Data { now: [DateTime; 2], fdate: [String; 2], ftime: [String; 2], timebar_ratio: [Option; 2], - data_idx: usize, + idx: usize, } -impl UiData { +impl Data { pub fn update( &mut self, now: DateTime, @@ -26,11 +26,11 @@ impl UiData { ftime: String, timebar_ratio: Option, ) { - self.data_idx ^= 1; - self.now[self.data_idx] = now; - self.fdate[self.data_idx] = fdate; - self.ftime[self.data_idx] = ftime; - self.timebar_ratio[self.data_idx] = timebar_ratio; + self.idx ^= 1; + self.now[self.idx] = now; + self.fdate[self.idx] = fdate; + self.ftime[self.idx] = ftime; + self.timebar_ratio[self.idx] = timebar_ratio; #[cfg(debug_assertions)] if self.changed() { trace!("update with change: {:#?}", self); @@ -49,32 +49,32 @@ impl UiData { #[must_use] #[inline] pub fn fdate(&self) -> &str { - &self.fdate[self.data_idx] + &self.fdate[self.idx] } #[must_use] #[inline] pub fn ftime(&self) -> &str { - &self.ftime[self.data_idx] + &self.ftime[self.idx] } #[must_use] #[inline] pub fn now(&self) -> &DateTime { - &self.now[self.data_idx] + &self.now[self.idx] } #[must_use] #[inline] #[allow(clippy::missing_const_for_fn)] // no it's not const pub fn timebar_ratio(&self) -> Option { - self.timebar_ratio[self.data_idx] + self.timebar_ratio[self.idx] } } pub fn timebarw<'a>( clock: &mut Clock, - data: &UiData, + data: &Data, timebarw_padding: &[u16], inner_rect: Rect, ) -> Option> { @@ -122,7 +122,7 @@ pub fn timebarw<'a>( pub fn timebarw_label<'a>( clock: &Clock, - data: &UiData, + data: &Data, timebarw_padding: &[u16], inner_rect: Rect, ) -> Option> { diff --git a/src/main.rs b/src/main.rs index 6f44694..2294a51 100644 --- a/src/main.rs +++ b/src/main.rs @@ -63,7 +63,7 @@ fn mock_tests() { use chrono::{Local, Timelike}; use libpt::log::info; - use crate::clock::ui::UiData; + use crate::clock::ui::Data; info!("doing the mock tests"); { let mut c = Clock::parse_from(["some exec", "-mvvv"]); @@ -81,7 +81,7 @@ fn mock_tests() { info!("0s=0.0"); } { - let mut data = UiData::default(); + let mut data = Data::default(); let now = Local::now(); data.update(now, "date".to_owned(), "time".to_owned(), Some(0.1)); assert_eq!(data.timebar_ratio(), Some(0.1)); From 01c3f44026c2b52c7485a7efb490e5eb1e257c01 Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Fri, 19 Jul 2024 10:00:48 +0200 Subject: [PATCH 09/10] fix(modeshow): force all segments of the time to have 2 digits --- src/clock/ui.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/clock/ui.rs b/src/clock/ui.rs index 0aa3ebd..3fd8480 100644 --- a/src/clock/ui.rs +++ b/src/clock/ui.rs @@ -155,8 +155,12 @@ pub fn timebarw_label<'a>( // example with `-o` #17 .checked_add_signed(len.into()) .expect("could not calculate when the countdown finishes"); - let timestamp_until: String = - format!("{}:{}:{}", until.hour(), until.minute(), until.second()); + let timestamp_until: String = format!( + "{:02}:{:02}:{:02}", + until.hour(), + until.minute(), + until.second() + ); Paragraph::new(format!("{time_now} / {len} ({timestamp_until})")) .alignment(Alignment::Center) .block( From b0d0651cc79bf9f0370dd050146a01498ac385a1 Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Fri, 19 Jul 2024 10:03:33 +0200 Subject: [PATCH 10/10] chore: bump alpha version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c0b0bc6..ef338fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "crock" -version = "0.2.1-alpha.0" +version = "0.2.1-alpha.1" edition = "2021" publish = true authors = ["Christoph J. Scherr "]