generated from PlexSheep/rs-base
support mupltiple durations for timer (#24)
This commit is contained in:
parent
aacb453fdb
commit
0e12a7f532
2 changed files with 63 additions and 28 deletions
|
@ -42,8 +42,17 @@ pub enum Mode {
|
|||
/// The timer mode displays the remaining time until the timer is finished.
|
||||
Timer {
|
||||
/// Initial duration for timer, value can be 10s for 10 seconds, 1m for 1 minute, etc.
|
||||
#[clap(short, long, value_parser = parse_duration, default_value = "5m")]
|
||||
duration: Duration,
|
||||
/// Also accept mulitple duration value and run the timers sequentially, eg. 25m 5m
|
||||
#[clap(short, long="duration", value_parser = parse_duration, min_values=1, default_value = "5m")]
|
||||
durations: Vec<Duration>,
|
||||
|
||||
/// Set the title for the timer, also accept mulitple titles for each durations correspondingly
|
||||
#[clap(short, long = "title", min_values = 0)]
|
||||
titles: Vec<String>,
|
||||
|
||||
/// Restart the timer when timer is over
|
||||
#[clap(long, short, takes_value = false)]
|
||||
repeat: bool,
|
||||
|
||||
/// Hide milliseconds
|
||||
#[clap(long = "no-millis", short = 'M', takes_value = false)]
|
||||
|
@ -150,7 +159,9 @@ impl App {
|
|||
});
|
||||
}
|
||||
Mode::Timer {
|
||||
duration,
|
||||
durations,
|
||||
titles,
|
||||
repeat,
|
||||
no_millis,
|
||||
paused,
|
||||
execute,
|
||||
|
@ -161,9 +172,11 @@ impl App {
|
|||
DurationFormat::HourMinSecDeci
|
||||
};
|
||||
self.timer = Some(Timer::new(
|
||||
*duration,
|
||||
self.size,
|
||||
style,
|
||||
durations.to_owned(),
|
||||
titles.to_owned(),
|
||||
*repeat,
|
||||
format,
|
||||
*paused,
|
||||
execute.to_owned(),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{cell::RefCell, process::Command};
|
||||
use std::{cell::RefCell, cmp::min, process::Command};
|
||||
|
||||
use crate::clock_text::BricksText;
|
||||
use chrono::{DateTime, Duration, Local};
|
||||
|
@ -11,40 +11,60 @@ use super::{format_duration, render_centered, DurationFormat};
|
|||
pub struct Timer {
|
||||
pub size: u16,
|
||||
pub style: Style,
|
||||
pub repeat: bool,
|
||||
pub durations: Vec<Duration>,
|
||||
pub titles: Vec<String>,
|
||||
pub execute: Vec<String>,
|
||||
format: DurationFormat,
|
||||
duration: Duration,
|
||||
ended_at: Option<DateTime<Local>>,
|
||||
passed: Duration,
|
||||
started_at: Option<DateTime<Local>>,
|
||||
execute_result: RefCell<Option<String>>,
|
||||
}
|
||||
|
||||
impl Timer {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new(
|
||||
duration: Duration,
|
||||
size: u16,
|
||||
style: Style,
|
||||
durations: Vec<Duration>,
|
||||
titles: Vec<String>,
|
||||
repeat: bool,
|
||||
format: DurationFormat,
|
||||
paused: bool,
|
||||
execute: Vec<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
duration,
|
||||
size,
|
||||
execute,
|
||||
style,
|
||||
durations,
|
||||
titles,
|
||||
repeat,
|
||||
execute,
|
||||
format,
|
||||
ended_at: (!paused).then(|| Local::now() + duration),
|
||||
passed: Duration::zero(),
|
||||
started_at: (!paused).then(Local::now),
|
||||
execute_result: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn remaining_time(&self) -> Duration {
|
||||
if let Some(end_at) = self.ended_at {
|
||||
let now = Local::now();
|
||||
end_at.signed_duration_since(now)
|
||||
pub(crate) fn remaining_time(&self) -> (Duration, usize) {
|
||||
let total_passed = if let Some(started_at) = self.started_at {
|
||||
self.passed + (Local::now() - started_at)
|
||||
} else {
|
||||
self.duration
|
||||
self.passed
|
||||
};
|
||||
|
||||
let mut idx = 0;
|
||||
let mut next_checkpoint = self.durations[idx];
|
||||
while next_checkpoint < total_passed {
|
||||
if idx >= self.durations.len() - 1 && !self.repeat {
|
||||
break;
|
||||
}
|
||||
idx = (idx + 1) % self.durations.len();
|
||||
next_checkpoint = next_checkpoint + self.durations[idx];
|
||||
}
|
||||
|
||||
(next_checkpoint - total_passed, idx)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,7 +90,7 @@ fn execute(execute: &[String]) -> String {
|
|||
|
||||
impl Widget for &Timer {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let remaining_time = self.remaining_time();
|
||||
let (remaining_time, idx) = self.remaining_time();
|
||||
let time_str = if remaining_time < Duration::zero() {
|
||||
if !self.execute.is_empty() && self.execute_result.borrow().is_none() {
|
||||
let result = execute(&self.execute);
|
||||
|
@ -85,35 +105,37 @@ impl Widget for &Timer {
|
|||
format_duration(remaining_time, self.format)
|
||||
};
|
||||
|
||||
let header = if self.titles.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self.titles[min(idx, self.titles.len() - 1)].clone())
|
||||
};
|
||||
|
||||
let text = BricksText::new(time_str.as_str(), self.size, self.size, self.style);
|
||||
let footer = if self.is_paused() {
|
||||
Some("PAUSED (press <SPACE> to resume)".to_string())
|
||||
} else {
|
||||
self.execute_result.borrow().clone()
|
||||
};
|
||||
render_centered(area, buf, &text, None, footer);
|
||||
render_centered(area, buf, &text, header, footer);
|
||||
}
|
||||
}
|
||||
|
||||
impl Pause for Timer {
|
||||
fn is_paused(&self) -> bool {
|
||||
self.ended_at.is_none()
|
||||
self.started_at.is_none()
|
||||
}
|
||||
|
||||
fn pause(&mut self) {
|
||||
if let Some(end_at) = self.ended_at {
|
||||
if end_at <= Local::now() {
|
||||
self.duration = Duration::zero();
|
||||
} else {
|
||||
self.duration = end_at - Local::now();
|
||||
self.ended_at = None;
|
||||
}
|
||||
if let Some(started_at) = self.started_at {
|
||||
self.passed = self.passed + (Local::now() - started_at);
|
||||
self.started_at = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn resume(&mut self) {
|
||||
if self.ended_at.is_none() {
|
||||
self.ended_at = Some(Local::now() + self.duration);
|
||||
if self.started_at.is_none() {
|
||||
self.started_at = Some(Local::now());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue