chore: add pause functionality

This commit is contained in:
Rekai Nyangadzayi Musuka 2020-12-13 21:44:56 -06:00
parent efe1a1ad37
commit ce566c20ce
2 changed files with 61 additions and 28 deletions

View File

@ -20,8 +20,8 @@ fn main() {
let mut domasi: Pomodoro = Default::default(); let mut domasi: Pomodoro = Default::default();
match get_alert() { match get_alert() {
Some(alert) => domasi.start_with_alert(alert), Some(alert) => domasi.start(Some(alert)).unwrap(),
None => domasi.start(), None => domasi.start(None).unwrap(),
} }
} }

View File

@ -1,8 +1,9 @@
use super::Alert; use super::Alert;
use crossterm::event::{self, Event, KeyCode, KeyModifiers};
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use std::error::Error; use std::error::Error;
use std::fmt::{Display, Formatter, Result as FmtResult}; use std::fmt::{Display, Formatter, Result as FmtResult};
use std::io::{self, Write}; use std::io::{self, Write};
use std::thread;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
#[derive(Debug, Copy, Clone, Default)] #[derive(Debug, Copy, Clone, Default)]
@ -18,34 +19,49 @@ static WORK_TIME: u64 = 1500; // Default: 1500 (25min)
static SHORT_BREAK_TIME: u64 = 300; // Default: 300 (5min) static SHORT_BREAK_TIME: u64 = 300; // Default: 300 (5min)
static LONG_BREAK_TIME: u64 = 600; // Default: 600 (10min) static LONG_BREAK_TIME: u64 = 600; // Default: 600 (10min)
static POLLING_RATE: Duration = Duration::from_millis(300); static POLLING_RATE: Duration = Duration::from_millis(100);
impl Pomodoro { impl Pomodoro {
pub fn new() -> Self { pub fn new() -> Self {
Self::default() Self::default()
} }
pub fn start_with_alert(&mut self, alert: Alert) { pub fn start(&mut self, maybe_alert: Option<Alert>) -> crossterm::Result<()> {
enable_raw_mode()?;
loop { loop {
if let Status::Complete = self.poll(Some(&alert)) { if let Status::Complete = self.poll(maybe_alert.as_ref()) {
break; break;
} }
Self::sleep(); if event::poll(POLLING_RATE)? {
if let Event::Key(key_event) = event::read()? {
match key_event.code {
KeyCode::Char('p') | KeyCode::Char('P') => {
self.paused.toggle();
if !self.paused.value {
// Apply the elapsed time
let duration = self.paused.duration;
match self.wait_start {
Some(instant) => self.wait_start = Some(instant + duration),
None => {
unreachable!("Unable to resume from a non-existent timer.")
} }
} }
pub fn start(&mut self) { self.paused = Default::default();
loop { }
if let Status::Complete = self.poll(None) { }
break; KeyCode::Char('q') | KeyCode::Char('Q') => break,
KeyCode::Char('c') if key_event.modifiers == KeyModifiers::CONTROL => break,
_ => {}
}
} }
Self::sleep()
} }
} }
fn sleep() { disable_raw_mode()?;
thread::sleep(POLLING_RATE); Ok(())
} }
fn notify_complete(&self) -> Result<(), Box<dyn Error>> { fn notify_complete(&self) -> Result<(), Box<dyn Error>> {
@ -116,6 +132,7 @@ impl Pomodoro {
}; };
self.paused.previous = Some(now); self.paused.previous = Some(now);
self.display_paused();
} }
Status::Active => self.display_time(), Status::Active => self.display_time(),
Status::NextState => { Status::NextState => {
@ -140,7 +157,6 @@ impl Pomodoro {
} }
Status::Complete => { Status::Complete => {
println!("\rPomodoro cycle complete!"); println!("\rPomodoro cycle complete!");
let _ = self.notify_complete(); let _ = self.notify_complete();
} }
Status::Inactive => {} Status::Inactive => {}
@ -150,10 +166,18 @@ impl Pomodoro {
} }
fn display_time(&self) { fn display_time(&self) {
if let Some(earlier) = self.wait_start { if let Some(start) = self.wait_start {
let wait = Instant::now() - earlier; let remainder: Clock = (Self::wait_times(self.state) - (Instant::now() - start)).into();
let left: Clock = (Self::wait_times(self.state) - wait).into(); let _ = Self::print(&format!("\r{} {}", self.state, remainder));
let _ = Self::print(&format!("\r{} {}", self.state, left)); }
}
fn display_paused(&self) {
if let Some(start) = self.wait_start {
let stop_time: Clock = (Self::wait_times(self.state)
- ((Instant::now() - self.paused.duration) - start))
.into();
let _ = Self::print(&format!("\r[PAUSED] {} {}", self.state, stop_time));
} }
} }
@ -163,6 +187,9 @@ impl Pomodoro {
// Empty String so that we can clear line before writing // Empty String so that we can clear line before writing
// from the most-left side of the terminal again. // from the most-left side of the terminal again.
//
// We write 24 spaces so that the entire line is always
// written over
handle.write_all(b"\r ")?; handle.write_all(b"\r ")?;
handle.write_all(text.as_bytes())?; handle.write_all(text.as_bytes())?;
handle.flush()?; handle.flush()?;
@ -274,6 +301,12 @@ struct PausedState {
previous: Option<Instant>, previous: Option<Instant>,
} }
impl PausedState {
pub fn toggle(&mut self) {
self.value = !self.value;
}
}
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
enum Count { enum Count {
Increase, Increase,
@ -306,10 +339,10 @@ impl Default for State {
impl Display for State { impl Display for State {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match *self { match *self {
State::Work => f.write_str("Working: "), State::Work => f.write_str("Working:"),
State::ShortBreak => f.write_str("Resting: "), State::ShortBreak => f.write_str("Resting:"),
State::LongBreak => f.write_str("REALLY Resting: "), State::LongBreak => f.write_str("REALLY Resting:"),
State::Inactive => f.write_str("Inactive: "), State::Inactive => f.write_str("Inactive:"),
} }
} }
} }