From c422c14dc1b84b417bf7c2a120e805fa070be952 Mon Sep 17 00:00:00 2001 From: PlexSheep Date: Tue, 9 Jul 2024 23:40:29 +0000 Subject: [PATCH 01/14] automatic cargo CI changes --- src/clock.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/clock.rs b/src/clock.rs index 3bba61a..f8d1118 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -4,7 +4,6 @@ use chrono::{DateTime, Datelike, Local, SubsecRound, Timelike}; use clap::Parser; use libpt::cli::args::HELP_TEMPLATE; -use libpt::cli::clap::builder::styling::Color; use libpt::cli::clap::ArgGroup; use libpt::cli::{args::VerbosityLevel, clap}; use libpt::log::{debug, trace}; From 8b6c7c266a016bd589a6539b0ddd2aa168ce5974 Mon Sep 17 00:00:00 2001 From: PlexSheep Date: Wed, 10 Jul 2024 01:49:50 +0200 Subject: [PATCH 02/14] feat: sunparts fixed length under time --- src/clock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clock.rs b/src/clock.rs index f8d1118..72881c8 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -263,7 +263,7 @@ impl Clock { fn partition(r: Rect) -> Vec { let part = Layout::default() .direction(Direction::Vertical) - .constraints([Constraint::Percentage(43), Constraint::Min(0)]) + .constraints([Constraint::Length(8), Constraint::Min(0)]) .split(r); let subparts = Layout::default() .direction(Direction::Horizontal) From 9f1a17c2eb667729cb4f64cd7c9bef4779a876d2 Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Wed, 10 Jul 2024 11:56:27 +0200 Subject: [PATCH 03/14] refactor: move some things around and make them more clear --- src/clock.rs | 41 +++++++++++++++++++++++------------------ src/main.rs | 2 +- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/clock.rs b/src/clock.rs index 72881c8..580ed6a 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -83,10 +83,9 @@ impl Clock { fn timebar_ratio(&self) -> Option { let len = self.timebar_len()?; - let since = (Local::now() + let since = Local::now() .signed_duration_since(self.last_reset.unwrap()) - .num_seconds() - + 1) as f64; + .num_seconds() as f64; Some((since / len.as_secs() as f64).min(1.0).max(0.0)) } @@ -94,30 +93,29 @@ impl Clock { if let Some(len) = self.timebar_len() { trace!("Local Time: {}", Local::now()); // BUG: these resets trigger multiple times + let since_last_reset = Local::now().signed_duration_since(self.last_reset.unwrap()); match len { TimeBarLength::Custom(_) => { - if Local::now() - .signed_duration_since(self.last_reset.unwrap()) - .num_seconds() - >= len.as_secs() + if since_last_reset.num_seconds() >= 1 + && since_last_reset.num_seconds() >= len.as_secs() { self.last_reset = Some(Local::now()); } } TimeBarLength::Minute => { - if Local::now().second() == 0 { + if since_last_reset.num_seconds() >= 1 && Local::now().second() == 0 { self.last_reset = Some(Local::now()); debug!("reset the time of the time bar (minute)"); } } TimeBarLength::Hour => { - if Local::now().minute() == 0 { + if since_last_reset.num_minutes() >= 1 && Local::now().minute() == 0 { self.last_reset = Some(Local::now()); debug!("reset the time of the time bar (hour)"); } } TimeBarLength::Day => { - if Local::now().hour() == 0 { + if since_last_reset.num_hours() >= 1 && Local::now().hour() == 0 { self.last_reset = Some(Local::now()); debug!("reset the time of the time bar (day)"); } @@ -181,7 +179,8 @@ impl Clock { .collect(); let fdate: String = splits[0].clone(); let ftime: String = splits[1].clone(); - self.ui(terminal, ftime, fdate)?; + let timebar_ratio = self.timebar_ratio(); + self.ui(terminal, ftime, fdate, timebar_ratio)?; let timeout = tick_rate.saturating_sub(last_tick.elapsed()); if poll(timeout)? { if let Event::Key(key) = event::read()? { @@ -208,6 +207,7 @@ impl Clock { terminal: &mut Terminal>, ftime: String, fdate: String, + timebar_ratio: Option, ) -> anyhow::Result<()> { terminal.draw(|frame| { let root = frame.size(); @@ -241,10 +241,8 @@ impl Clock { ))); frame.render_widget(space, root); - frame.render_widget(clockw, parts[0]); - frame.render_widget(datew, parts[1]); - if self.timebar_len().is_some() { - let timebarw = LineGauge::default() + let timebarw: Option = if self.timebar_len().is_some() { + let tmp = LineGauge::default() .filled_style(Style::default().blue()) .unfilled_style(Style::default()) .block(Block::new().padding(Padding::new( @@ -253,10 +251,17 @@ impl Clock { 0, 0, ))) - .ratio(self.timebar_ratio().unwrap()); + .ratio(timebar_ratio.unwrap()); debug!("time bar ration: {}", self.timebar_ratio().unwrap()); - frame.render_widget(timebarw, parts[2]); - } + Some(tmp) + } else { + None + }; + debug!("rendering the configured widgets"); + frame.render_widget(clockw, parts[0]); + frame.render_widget(&timebarw, parts[2]); + frame.render_widget(datew, parts[1]); + debug!("done rendering"); })?; Ok(()) } diff --git a/src/main.rs b/src/main.rs index 20e8a56..80091a1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,7 +22,7 @@ fn main() -> anyhow::Result<()> { .log_to_file(true) .log_dir("/tmp/crock/".into()) .set_level(clock.verbose.level()) - .display_time(false) + .display_time(true) .build()?; } else { // no logger From c1d57e32c171fa40a45a4abd34a1d40c3ffbaecf Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Wed, 10 Jul 2024 11:56:48 +0200 Subject: [PATCH 04/14] fix: sync timebar with clock with a horrible hack --- src/clock.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/clock.rs b/src/clock.rs index 580ed6a..0601de9 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -259,6 +259,9 @@ impl Clock { }; debug!("rendering the configured widgets"); frame.render_widget(clockw, parts[0]); + sleep(500); // HACK: through black magic, this works around the problem that the time bar is + // not rendered at the same time as the clock, and yes, it needs to be + // 500ms for some reason, and yes it makes starting the app slower frame.render_widget(&timebarw, parts[2]); frame.render_widget(datew, parts[1]); debug!("done rendering"); @@ -278,3 +281,7 @@ impl Clock { vec![part[0], subparts[0], subparts[1]] } } + +fn sleep(ms: u64) { + std::thread::sleep(std::time::Duration::from_millis(ms)); +} From edeadb313cfb255d71e580767e0530e7b0dfba19 Mon Sep 17 00:00:00 2001 From: cscherrNT Date: Wed, 10 Jul 2024 10:40:51 +0000 Subject: [PATCH 05/14] automatic cargo CI changes --- src/clock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clock.rs b/src/clock.rs index d634e83..ee51bcb 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -66,7 +66,7 @@ pub struct Clock { } #[derive(Debug, Clone, PartialEq, Default)] -pub(crate) struct UiData { +pub struct UiData { fdate: [String; 2], ftime: [String; 2], timebar_ratio: [Option; 2], From df3850c1ca24d70dec3786f6c84798fabce8eac1 Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Wed, 10 Jul 2024 14:57:59 +0200 Subject: [PATCH 06/14] fix: sync timebar with clock --- src/clock.rs | 73 ++++++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/src/clock.rs b/src/clock.rs index d634e83..786f06d 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -14,6 +14,7 @@ use ratatui::style::{Style, Stylize}; use ratatui::widgets::{Block, LineGauge, Padding, Paragraph}; use ratatui::Terminal; use std::io::Stdout; +use std::thread::{sleep, sleep_ms}; use std::time::{Duration, Instant}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -80,15 +81,24 @@ impl UiData { self.fdate[self.data_idx] = fdate; self.ftime[self.data_idx] = ftime; self.timebar_ratio[self.data_idx] = timebar_ratio; + trace!( + "data after update: {:#?}\nconsidered changed: {}", + self, + self.changed() + ); } /// did the data change with the last update? #[must_use] #[inline] pub fn changed(&self) -> bool { - !(self.fdate[0] == self.fdate[1] - && self.ftime[0] == self.fdate[1] - && self.timebar_ratio[0] == self.timebar_ratio[1]) + let r = + self.fdate[0] != self.fdate[1] || self.ftime[0] != self.ftime[1] + //&& self.timebar_ratio[0] == self.timebar_ratio[1] + // NOTE: the timebar ratio is discarded, so that we only render the ui when the time (second) changes + ; + trace!("changed: {r}"); + r } #[must_use] @@ -255,7 +265,14 @@ impl Clock { terminal: &mut Terminal>, data: &UiData, ) -> anyhow::Result<()> { + let clockw = tui_big_text::BigText::builder() + .style(Style::new().red()) + .lines(vec![data.ftime().into()]) + .alignment(Alignment::Center) + .build() + .expect("could not render time widget"); terminal.draw(|frame| { + debug!("rendering the ui"); let root = frame.size(); let space = Block::bordered() .padding(Padding::new( @@ -269,25 +286,14 @@ impl Clock { .title_alignment(Alignment::Center) .title_style(Style::new().bold()); let a = space.inner(root); - let parts = Self::partition(a); - let clockw = tui_big_text::BigText::builder() - .style(Style::new().red()) - .lines(vec![data.ftime().into()]) - .alignment(Alignment::Center) - .build() - .expect("could not render time widget"); - let datew = Paragraph::new(data.fdate()) - .blue() - .alignment(Alignment::Left) - .block(Block::new().padding(Padding::new( - parts[1].left(), - parts[1].right() / 3, - 0, - 0, - ))); - frame.render_widget(space, root); + let parts = Self::partition(a); + + // 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 tmp = LineGauge::default() .filled_style(Style::default().blue()) .unfilled_style(Style::default()) @@ -298,22 +304,27 @@ impl Clock { 0, ))) .ratio(data.timebar_ratio().unwrap()); - debug!("time bar ration: {}", self.timebar_ratio().unwrap()); Some(tmp) } else { None }; - debug!("rendering the configured widgets"); - #[cfg(debug_assertions)] - let prerender = Instant::now(); - frame.render_widget(clockw, parts[0]); - trace!("{:?} after render clockw", prerender.elapsed()); + + // render the small date + let datew = Paragraph::new(data.fdate()) + .blue() + .alignment(Alignment::Left) + .block(Block::new().padding(Padding::new( + parts[1].left(), + parts[1].right() / 3, + 0, + 0, + ))); frame.render_widget(&timebarw, parts[2]); - trace!("{:?} after render timebarw", prerender.elapsed()); frame.render_widget(datew, parts[1]); - trace!("{:?} to render all widgets", prerender.elapsed()); - debug!("done rendering"); + // render the clock + frame.render_widget(clockw, parts[0]); })?; + debug!("done rendering the ui"); Ok(()) } fn partition(r: Rect) -> Vec { @@ -329,7 +340,3 @@ impl Clock { vec![part[0], subparts[0], subparts[1]] } } - -fn sleep(ms: u64) { - std::thread::sleep(std::time::Duration::from_millis(ms)); -} From 468c48d3277901aff1e83e8de8102ea755fdbe77 Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Wed, 10 Jul 2024 12:39:25 +0200 Subject: [PATCH 07/14] refactor(data): add UiData struct and remove the hacky fix that broke --- src/clock.rs | 74 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 13 deletions(-) diff --git a/src/clock.rs b/src/clock.rs index 0601de9..d634e83 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -65,6 +65,52 @@ pub struct Clock { last_reset: Option>, } +#[derive(Debug, Clone, PartialEq, Default)] +pub(crate) 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; + } + + /// did the data change with the last update? + #[must_use] + #[inline] + pub fn changed(&self) -> bool { + !(self.fdate[0] == self.fdate[1] + && self.ftime[0] == self.fdate[1] + && self.timebar_ratio[0] == self.timebar_ratio[1]) + } + + #[must_use] + #[inline] + pub(crate) fn fdate(&self) -> &str { + &self.fdate[self.data_idx] + } + + #[must_use] + #[inline] + pub(crate) fn ftime(&self) -> &str { + &self.ftime[self.data_idx] + } + + #[must_use] + #[inline] + #[allow(clippy::missing_const_for_fn)] // no it's not const + pub(crate) fn timebar_ratio(&self) -> Option { + self.timebar_ratio[self.data_idx] + } +} + impl Clock { #[must_use] #[allow(clippy::missing_const_for_fn)] @@ -168,6 +214,7 @@ impl Clock { ) -> anyhow::Result<()> { let tick_rate = Duration::from_millis(100); let mut last_tick = Instant::now(); + let mut uidata: UiData = UiData::default(); self.setup()?; loop { let raw_time = chrono::Local::now().round_subsecs(0); @@ -177,10 +224,11 @@ impl Clock { .split_whitespace() .map(str::to_string) .collect(); - let fdate: String = splits[0].clone(); - let ftime: String = splits[1].clone(); - let timebar_ratio = self.timebar_ratio(); - self.ui(terminal, ftime, fdate, timebar_ratio)?; + + uidata.update(splits[0].clone(), splits[1].clone(), self.timebar_ratio()); + if uidata.changed() { + self.ui(terminal, &uidata)?; + } let timeout = tick_rate.saturating_sub(last_tick.elapsed()); if poll(timeout)? { if let Event::Key(key) = event::read()? { @@ -205,9 +253,7 @@ impl Clock { fn ui( &self, terminal: &mut Terminal>, - ftime: String, - fdate: String, - timebar_ratio: Option, + data: &UiData, ) -> anyhow::Result<()> { terminal.draw(|frame| { let root = frame.size(); @@ -226,11 +272,11 @@ impl Clock { let parts = Self::partition(a); let clockw = tui_big_text::BigText::builder() .style(Style::new().red()) - .lines(vec![ftime.into()]) + .lines(vec![data.ftime().into()]) .alignment(Alignment::Center) .build() .expect("could not render time widget"); - let datew = Paragraph::new(fdate) + let datew = Paragraph::new(data.fdate()) .blue() .alignment(Alignment::Left) .block(Block::new().padding(Padding::new( @@ -251,19 +297,21 @@ impl Clock { 0, 0, ))) - .ratio(timebar_ratio.unwrap()); + .ratio(data.timebar_ratio().unwrap()); debug!("time bar ration: {}", self.timebar_ratio().unwrap()); Some(tmp) } else { None }; debug!("rendering the configured widgets"); + #[cfg(debug_assertions)] + let prerender = Instant::now(); frame.render_widget(clockw, parts[0]); - sleep(500); // HACK: through black magic, this works around the problem that the time bar is - // not rendered at the same time as the clock, and yes, it needs to be - // 500ms for some reason, and yes it makes starting the app slower + trace!("{:?} after render clockw", prerender.elapsed()); frame.render_widget(&timebarw, parts[2]); + trace!("{:?} after render timebarw", prerender.elapsed()); frame.render_widget(datew, parts[1]); + trace!("{:?} to render all widgets", prerender.elapsed()); debug!("done rendering"); })?; Ok(()) From e82ae2fa84b003962db3e03b1e1cb1a55c622491 Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Wed, 10 Jul 2024 14:57:59 +0200 Subject: [PATCH 08/14] fix: sync timebar with clock #9 --- src/clock.rs | 79 +++++++++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/src/clock.rs b/src/clock.rs index d634e83..fb0d0ba 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -80,15 +80,24 @@ impl UiData { self.fdate[self.data_idx] = fdate; self.ftime[self.data_idx] = ftime; self.timebar_ratio[self.data_idx] = timebar_ratio; + trace!( + "data after update: {:#?}\nconsidered changed: {}", + self, + self.changed() + ); } /// did the data change with the last update? #[must_use] #[inline] pub fn changed(&self) -> bool { - !(self.fdate[0] == self.fdate[1] - && self.ftime[0] == self.fdate[1] - && self.timebar_ratio[0] == self.timebar_ratio[1]) + let r = + self.fdate[0] != self.fdate[1] || self.ftime[0] != self.ftime[1] + //&& self.timebar_ratio[0] == self.timebar_ratio[1] + // NOTE: the timebar ratio is discarded, so that we only render the ui when the time (second) changes + ; + trace!("changed: {r}"); + r } #[must_use] @@ -127,11 +136,14 @@ impl Clock { } } + // FIXME: This generally works, but we want 0% at the start and 100% at the end, which does not + // fully work. + // We also want 50% at the half etc fn timebar_ratio(&self) -> Option { let len = self.timebar_len()?; - let since = Local::now() + let since = (Local::now() .signed_duration_since(self.last_reset.unwrap()) - .num_seconds() as f64; + .num_seconds()+1) as f64; Some((since / len.as_secs() as f64).min(1.0).max(0.0)) } @@ -255,7 +267,14 @@ impl Clock { terminal: &mut Terminal>, data: &UiData, ) -> anyhow::Result<()> { + let clockw = tui_big_text::BigText::builder() + .style(Style::new().red()) + .lines(vec![data.ftime().into()]) + .alignment(Alignment::Center) + .build() + .expect("could not render time widget"); terminal.draw(|frame| { + debug!("rendering the ui"); let root = frame.size(); let space = Block::bordered() .padding(Padding::new( @@ -269,25 +288,14 @@ impl Clock { .title_alignment(Alignment::Center) .title_style(Style::new().bold()); let a = space.inner(root); - let parts = Self::partition(a); - let clockw = tui_big_text::BigText::builder() - .style(Style::new().red()) - .lines(vec![data.ftime().into()]) - .alignment(Alignment::Center) - .build() - .expect("could not render time widget"); - let datew = Paragraph::new(data.fdate()) - .blue() - .alignment(Alignment::Left) - .block(Block::new().padding(Padding::new( - parts[1].left(), - parts[1].right() / 3, - 0, - 0, - ))); - frame.render_widget(space, root); + let parts = Self::partition(a); + + // 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 tmp = LineGauge::default() .filled_style(Style::default().blue()) .unfilled_style(Style::default()) @@ -298,22 +306,27 @@ impl Clock { 0, ))) .ratio(data.timebar_ratio().unwrap()); - debug!("time bar ration: {}", self.timebar_ratio().unwrap()); Some(tmp) } else { None }; - debug!("rendering the configured widgets"); - #[cfg(debug_assertions)] - let prerender = Instant::now(); - frame.render_widget(clockw, parts[0]); - trace!("{:?} after render clockw", prerender.elapsed()); + + // render the small date + let datew = Paragraph::new(data.fdate()) + .blue() + .alignment(Alignment::Left) + .block(Block::new().padding(Padding::new( + parts[1].left(), + parts[1].right() / 3, + 0, + 0, + ))); frame.render_widget(&timebarw, parts[2]); - trace!("{:?} after render timebarw", prerender.elapsed()); frame.render_widget(datew, parts[1]); - trace!("{:?} to render all widgets", prerender.elapsed()); - debug!("done rendering"); + // render the clock + frame.render_widget(clockw, parts[0]); })?; + debug!("done rendering the ui"); Ok(()) } fn partition(r: Rect) -> Vec { @@ -329,7 +342,3 @@ impl Clock { vec![part[0], subparts[0], subparts[1]] } } - -fn sleep(ms: u64) { - std::thread::sleep(std::time::Duration::from_millis(ms)); -} From 07815f5291b9876476c76cc5e2c069d3a82e1c42 Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Wed, 10 Jul 2024 15:04:07 +0200 Subject: [PATCH 09/14] chore: change a fixme --- src/clock.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/clock.rs b/src/clock.rs index 36174fd..12d36d8 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -138,8 +138,7 @@ impl Clock { } // FIXME: This generally works, but we want 0% at the start and 100% at the end, which does not - // fully work. - // We also want 50% at the half etc + // fully work. We also want 50% at the half etc. #10 fn timebar_ratio(&self) -> Option { let len = self.timebar_len()?; let since = (Local::now() From 25a8ebac80d2a6541e17c8f7198b568373102a5a Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Wed, 10 Jul 2024 15:07:41 +0200 Subject: [PATCH 10/14] chore: change a format because rustfmt errored --- src/clock.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/clock.rs b/src/clock.rs index 12d36d8..f37709c 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -92,11 +92,9 @@ impl UiData { #[must_use] #[inline] pub fn changed(&self) -> bool { - let r = - self.fdate[0] != self.fdate[1] || self.ftime[0] != self.ftime[1] - //&& self.timebar_ratio[0] == self.timebar_ratio[1] - // NOTE: the timebar ratio is discarded, so that we only render the ui when the time (second) changes - ; + // NOTE: the timebar ratio is discarded, so that we only render the ui when the time + // (second) changes + let r = self.fdate[0] != self.fdate[1] || self.ftime[0] != self.ftime[1]; trace!("changed: {r}"); r } @@ -137,13 +135,14 @@ impl Clock { } } - // FIXME: This generally works, but we want 0% at the start and 100% at the end, which does not + // FIXME: This generally works, but we want 0% at the start and 100% at the end, which does not // fully work. We also want 50% at the half etc. #10 fn timebar_ratio(&self) -> Option { let len = self.timebar_len()?; let since = (Local::now() .signed_duration_since(self.last_reset.unwrap()) - .num_seconds()+1) as f64; + .num_seconds() + + 1) as f64; Some((since / len.as_secs() as f64).min(1.0).max(0.0)) } From efd84f8bbb6070d4dc115530bb32876e45e8c4c4 Mon Sep 17 00:00:00 2001 From: cscherrNT Date: Wed, 10 Jul 2024 13:09:04 +0000 Subject: [PATCH 11/14] automatic cargo CI changes --- src/clock.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/clock.rs b/src/clock.rs index f37709c..c82a2fc 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -14,7 +14,6 @@ use ratatui::style::{Style, Stylize}; use ratatui::widgets::{Block, LineGauge, Padding, Paragraph}; use ratatui::Terminal; use std::io::Stdout; -use std::thread::{sleep, sleep_ms}; use std::time::{Duration, Instant}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] From 2149258e8f6620d34f01ef54b878bea43b04fb42 Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Wed, 10 Jul 2024 15:17:25 +0200 Subject: [PATCH 12/14] refactor: address clippy warnings --- src/clock.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/clock.rs b/src/clock.rs index c82a2fc..307909e 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -1,7 +1,7 @@ #![warn(clippy::pedantic, clippy::style, clippy::nursery)] #![allow(clippy::question_mark_used)] -use chrono::{DateTime, Datelike, Local, SubsecRound, Timelike}; +use chrono::{DateTime, Local, SubsecRound, Timelike}; use clap::Parser; use libpt::cli::args::HELP_TEMPLATE; use libpt::cli::clap::ArgGroup; @@ -45,6 +45,8 @@ impl Default for TimeBarLength { #[derive(Parser, Debug, Clone)] #[command(help_template = HELP_TEMPLATE, author, version)] #[clap(group( ArgGroup::new("timebarlen") .args(&["minute","day", "hour", "custom"]),))] +#[allow(clippy::struct_excessive_bools)] // the struct is for cli parsing and we already use an + // ArgGroup pub struct Clock { #[command(flatten)] pub verbose: VerbosityLevel, @@ -136,13 +138,15 @@ impl Clock { // FIXME: This generally works, but we want 0% at the start and 100% at the end, which does not // fully work. We also want 50% at the half etc. #10 + #[allow(clippy::cast_precision_loss)] // okay, good to know, but I accept the loss. It + // shouldn't come to more than 2^52 seconds anyway fn timebar_ratio(&self) -> Option { let len = self.timebar_len()?; let since = (Local::now() .signed_duration_since(self.last_reset.unwrap()) .num_seconds() + 1) as f64; - Some((since / len.as_secs() as f64).min(1.0).max(0.0)) + Some((since / len.as_secs() as f64).clamp(0.0, 1.0)) } fn maybe_reset_since_zero(&mut self) { @@ -213,6 +217,7 @@ impl Clock { } } + #[allow(clippy::unnecessary_wraps)] // we have that to be future proof fn setup(&mut self) -> anyhow::Result<()> { self.setup_last_reset(); Ok(()) From 1db0aae933e1d40feb65de61d5f6b80f0594d3b8 Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Wed, 10 Jul 2024 15:19:31 +0200 Subject: [PATCH 13/14] chore: remove old todo comments --- src/clock.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/clock.rs b/src/clock.rs index 307909e..6452cdf 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -93,7 +93,7 @@ impl UiData { #[must_use] #[inline] pub fn changed(&self) -> bool { - // NOTE: the timebar ratio is discarded, so that we only render the ui when the time + // the timebar ratio is discarded, so that we only render the ui when the time // (second) changes let r = self.fdate[0] != self.fdate[1] || self.ftime[0] != self.ftime[1]; trace!("changed: {r}"); @@ -152,7 +152,6 @@ impl Clock { fn maybe_reset_since_zero(&mut self) { if let Some(len) = self.timebar_len() { trace!("Local Time: {}", Local::now()); - // BUG: these resets trigger multiple times let since_last_reset = Local::now().signed_duration_since(self.last_reset.unwrap()); match len { TimeBarLength::Custom(_) => { From b65f1ba5e94e488123674702853f5e67db59a65d Mon Sep 17 00:00:00 2001 From: PlexSheep Date: Thu, 11 Jul 2024 16:16:25 +0200 Subject: [PATCH 14/14] fix: add one second to the live timebar_ratio now time to align percentages with expectations #10 --- src/clock.rs | 65 +++++++++++++++++++++++++++++++++------------------- src/main.rs | 38 ++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 24 deletions(-) diff --git a/src/clock.rs b/src/clock.rs index 6452cdf..220682e 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -1,12 +1,12 @@ #![warn(clippy::pedantic, clippy::style, clippy::nursery)] #![allow(clippy::question_mark_used)] -use chrono::{DateTime, Local, SubsecRound, Timelike}; +use chrono::{DateTime, Local, SubsecRound, TimeZone, Timelike}; use clap::Parser; use libpt::cli::args::HELP_TEMPLATE; use libpt::cli::clap::ArgGroup; use libpt::cli::{args::VerbosityLevel, clap}; -use libpt::log::{debug, trace}; +use libpt::log::{debug, info, trace}; use ratatui::backend::CrosstermBackend; use ratatui::crossterm::event::{self, poll, Event, KeyCode, KeyModifiers}; use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect}; @@ -64,7 +64,7 @@ pub struct Clock { #[clap(short, long)] pub custom: Option, #[clap(skip)] - last_reset: Option>, + pub(crate) last_reset: Option>, } #[derive(Debug, Clone, PartialEq, Default)] @@ -82,11 +82,10 @@ impl UiData { self.fdate[self.data_idx] = fdate; self.ftime[self.data_idx] = ftime; self.timebar_ratio[self.data_idx] = timebar_ratio; - trace!( - "data after update: {:#?}\nconsidered changed: {}", - self, - self.changed() - ); + #[cfg(debug_assertions)] + if self.changed() { + trace!("update with change: {:#?}", self); + } } /// did the data change with the last update? @@ -95,27 +94,25 @@ impl UiData { pub fn changed(&self) -> bool { // the timebar ratio is discarded, so that we only render the ui when the time // (second) changes - let r = self.fdate[0] != self.fdate[1] || self.ftime[0] != self.ftime[1]; - trace!("changed: {r}"); - r + self.fdate[0] != self.fdate[1] || self.ftime[0] != self.ftime[1] } #[must_use] #[inline] - pub(crate) fn fdate(&self) -> &str { + pub fn fdate(&self) -> &str { &self.fdate[self.data_idx] } #[must_use] #[inline] - pub(crate) fn ftime(&self) -> &str { + 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(crate) fn timebar_ratio(&self) -> Option { + pub fn timebar_ratio(&self) -> Option { self.timebar_ratio[self.data_idx] } } @@ -136,22 +133,22 @@ impl Clock { } } - // FIXME: This generally works, but we want 0% at the start and 100% at the end, which does not - // fully work. We also want 50% at the half etc. #10 #[allow(clippy::cast_precision_loss)] // okay, good to know, but I accept the loss. It // shouldn't come to more than 2^52 seconds anyway - fn timebar_ratio(&self) -> Option { + pub(crate) fn timebar_ratio(&self, current_time: DateTime) -> Option { let len = self.timebar_len()?; - let since = (Local::now() + let since = current_time .signed_duration_since(self.last_reset.unwrap()) - .num_seconds() - + 1) as f64; + .num_seconds() as f64; + #[cfg(debug_assertions)] + if since < 1.0 { + trace!("ratio calculation since is now <1: {:#?}", since); + } Some((since / len.as_secs() as f64).clamp(0.0, 1.0)) } - fn maybe_reset_since_zero(&mut self) { + pub(crate) fn maybe_reset_since_zero(&mut self) { if let Some(len) = self.timebar_len() { - trace!("Local Time: {}", Local::now()); let since_last_reset = Local::now().signed_duration_since(self.last_reset.unwrap()); match len { TimeBarLength::Custom(_) => { @@ -217,7 +214,7 @@ impl Clock { } #[allow(clippy::unnecessary_wraps)] // we have that to be future proof - fn setup(&mut self) -> anyhow::Result<()> { + pub(crate) fn setup(&mut self) -> anyhow::Result<()> { self.setup_last_reset(); Ok(()) } @@ -239,7 +236,27 @@ impl Clock { .map(str::to_string) .collect(); - uidata.update(splits[0].clone(), splits[1].clone(), self.timebar_ratio()); + // We somehow fill timebar_ratio with a bad value here if we don't add 1 second. It's + // always the value that would be right for now-1s. The start of the minute is + // special, with this strategy it is 100%. #10 + // + // If we manually add a second, it works as expected, but it feels weird. We use the + // same time for all of the datapoints here, so it can't be because of time diff in + // calculation. I noticed that we don't start at 0% this way (with len=minute) + // . Normally, chrono does not include 60 seconds, only letting it range between 0 and + // 59. This makes sense but feels weird to the human understanding, of course there are + // seconds in a minute! If we do it this way, we don't quite start at 0%, but 100%, + // which feels correct. + // + // In short: if we add a second here, we get the correct percentages. 01:00 is 100%, + // 01:30 is 50%, 01:59 is 98%, 01:60 does not exist because that's how counting from + // 0 works. + + uidata.update( + splits[0].clone(), + splits[1].clone(), + self.timebar_ratio(raw_time + chrono::Duration::seconds(1)), + ); if uidata.changed() { self.ui(terminal, &uidata)?; } diff --git a/src/main.rs b/src/main.rs index 80091a1..065a46b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,6 +29,9 @@ fn main() -> anyhow::Result<()> { } debug!("set up logger"); + #[cfg(debug_assertions)] + mock_tests(); + debug!("taking over terminal"); // setup terminal enable_raw_mode()?; @@ -53,3 +56,38 @@ fn main() -> anyhow::Result<()> { debug!("done"); result } + +#[cfg(debug_assertions)] +#[allow(clippy::cast_precision_loss)] +fn mock_tests() { + use chrono::{Local, Timelike}; + use libpt::log::info; + + use self::clock::UiData; + info!("doing the mock tests"); + { + let mut c = Clock::parse_from(["some exec", "-mvvv"]); + let now = Local::now(); + c.last_reset = Some(now.with_second(0).unwrap()); + + assert_eq!(c.timebar_ratio(now.with_second(30).unwrap()), Some(0.5)); + info!("30s=0.5"); + assert_eq!( + c.timebar_ratio(now.with_second(59).unwrap()), + Some(0.9833333333333333) + ); + info!("60s=1.0"); + assert_eq!(c.timebar_ratio(now.with_second(0).unwrap()), Some(0.0)); + info!("0s=0.0"); + } + { + let mut data = UiData::default(); + data.update("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)); + assert_eq!(data.timebar_ratio(), Some(0.2)); + data.update("date".to_owned(), "time".to_owned(), Some(0.3)); + assert_eq!(data.timebar_ratio(), Some(0.3)); + } + info!("finished the mock tests"); +}