diff --git a/src/timer/gui.rs b/src/timer/gui.rs new file mode 100644 index 0000000..5ce2802 --- /dev/null +++ b/src/timer/gui.rs @@ -0,0 +1,71 @@ +use crate::util::display_digits; + +use super::state::NextTimerState; + +use std::{sync::mpsc::Sender, time::Duration}; + +use egui::{Color32, Direction, Layout, RichText, Ui}; +use egui_extras::{Size, StripBuilder}; + +pub(crate) fn two_button( + ui: &mut Ui, + button1: RichText, + button2: RichText, + signal1: NextTimerState, + signal2: NextTimerState, + sender: Sender, + remaining: Duration, + color: Color32, + size: f32, +) { + let sender2 = sender.clone(); + StripBuilder::new(ui) + .size(Size::relative(0.33333)) + .size(Size::remainder()) + .cell_layout(Layout::centered_and_justified(Direction::LeftToRight)) + .vertical(|mut strip| { + strip.strip(|pstrip| { + pstrip + .sizes(Size::remainder(), 2) + .cell_layout(Layout::centered_and_justified(Direction::TopDown)) + .horizontal(|mut pstrip| { + pstrip.cell(|ui| { + if ui.button(button1).clicked() { + sender.send(signal1).unwrap(); + } + }); + pstrip.cell(|ui| { + if ui.button(button2).clicked() { + sender2.send(signal2).unwrap(); + } + }); + }); + }); + + display_digits(&mut strip, remaining, color, size); + }); +} + +pub(crate) fn one_button( + ui: &mut Ui, + button1: RichText, + signal1: NextTimerState, + sender: Sender, + remaining: Duration, + color: Color32, + size: f32, +) { + StripBuilder::new(ui) + .size(Size::relative(0.33333)) + .size(Size::remainder()) + .cell_layout(Layout::centered_and_justified(Direction::LeftToRight)) + .vertical(|mut strip| { + strip.cell(|ui| { + if ui.button(button1).clicked() { + sender.send(signal1).unwrap(); + } + }); + + display_digits(&mut strip, remaining, color, size); + }); +} diff --git a/src/timer/mod.rs b/src/timer/mod.rs index edab29c..17bf7c7 100644 --- a/src/timer/mod.rs +++ b/src/timer/mod.rs @@ -1,14 +1,17 @@ +use std::sync::mpsc::channel; use std::time::{Duration, Instant}; use clap::Parser; -use egui::{Color32, Direction, FontId, Layout, RichText, Ui}; +use egui::{Color32, FontId, Layout, RichText, Ui}; use egui_extras::{Size, StripBuilder}; use crate::{cli::Cli, util::*, AIRHORN, DIGIT_FACTOR, PREDATOR_FONT, TEXT_FACTOR}; mod state; -use state::{ChronoState, TimerState}; +use state::{ChronoState, NextTimerState, TimerState}; mod eframe_app; +mod gui; +use gui::*; #[derive(Debug, Clone, Copy)] pub enum CountDirection { @@ -111,34 +114,8 @@ impl Timer { .font(FontId::monospace(tsize)) .color(Color32::GOLD); - let mut is_paused = false; let elapsed = Instant::now() - cs.updated; let remaining = cs.remaining.saturating_sub(elapsed); - - StripBuilder::new(ui) - .size(Size::relative(0.3333)) - .size(Size::remainder()) - .cell_layout(Layout::centered_and_justified(Direction::LeftToRight)) - .vertical(|mut strip| { - // first the pause - strip.cell(|ui| { - if ui.button(text).clicked() { - is_paused = true; - } - }); - - // if we're counting up, do the right thing - let remaining = match self.direction { - CountDirection::Down => remaining, - CountDirection::Up => self.duration - remaining, - }; - // now the numbers - let color = Color32::DARK_GRAY; - let tsize = size * DIGIT_FACTOR; - - display_digits(&mut strip, remaining, color, tsize); - }); - if remaining.is_zero() { if let Some(alarm_file) = &self.alarm { let alarm_file = alarm_file.to_owned(); @@ -148,15 +125,33 @@ impl Timer { return; } + let (sender, rx) = channel(); + { + // if we're counting up, do the right thing + let remaining = match self.direction { + CountDirection::Down => remaining, + CountDirection::Up => self.duration - remaining, + }; + // now the numbers + let color = Color32::DARK_GRAY; + let tsize = size * DIGIT_FACTOR; + one_button( + ui, + text, + state::NextTimerState::Paused, + sender, + remaining, + color, + tsize, + ) + } + let cs = ChronoState { remaining, updated: Instant::now(), }; - if is_paused { - // did we click the reset button? - if self.state == TimerState::Unstarted { - return; - } + + if rx.recv().is_ok() { self.state = TimerState::Paused(cs); } else { self.state = TimerState::Running(cs); @@ -164,60 +159,61 @@ impl Timer { } fn paused(&mut self, ui: &mut Ui, vsize: f32, cs: ChronoState) { - let mut is_running = false; let tsize = vsize * TEXT_FACTOR; let remaining = cs.remaining; - StripBuilder::new(ui) - .size(Size::relative(0.33333)) - .size(Size::remainder()) - .cell_layout(Layout::centered_and_justified(Direction::LeftToRight)) - .vertical(|mut strip| { - strip.strip(|pstrip| { - pstrip - .sizes(Size::remainder(), 2) - .cell_layout(Layout::centered_and_justified(Direction::TopDown)) - .horizontal(|mut pstrip| { - pstrip.cell(|ui| { - let resume = RichText::new("RESUME") - .color(Color32::GREEN) - .font(FontId::monospace(tsize)); - if ui.button(resume).clicked() { - is_running = true; - } - }); - pstrip.cell(|ui| { - let reset = RichText::new("RESET") - .color(Color32::RED) - .font(FontId::monospace(tsize)); - if ui.button(reset).clicked() { - self.state = TimerState::Unstarted; - } - }); - }); - }); - let color = { - let blink = (Instant::now() - self.tstart).as_secs() % 2 == 0; - if blink { - Color32::BLACK - } else { - Color32::DARK_GRAY - } - }; - // if we're counting up, do the right thing - let remaining = match self.direction { - CountDirection::Down => remaining, - CountDirection::Up => self.duration - remaining, - }; - display_digits(&mut strip, remaining, color, vsize * DIGIT_FACTOR); - }); + let resume = RichText::new("RESUME") + .color(Color32::GREEN) + .font(FontId::monospace(tsize)); + let reset = RichText::new("RESET") + .color(Color32::RED) + .font(FontId::monospace(tsize)); - if is_running { - let cs = ChronoState { - remaining, - updated: Instant::now(), + let (sender, rx) = channel(); + { + let color = { + let blink = (Instant::now() - self.tstart).as_secs() % 2 == 0; + if blink { + Color32::BLACK + } else { + Color32::DARK_GRAY + } }; - self.state = TimerState::Running(cs); + // if we're counting up, do the right thing + let remaining = match self.direction { + CountDirection::Down => remaining, + CountDirection::Up => self.duration - remaining, + }; + + two_button( + ui, + resume, + reset, + NextTimerState::Running, + NextTimerState::Unstarted, + sender, + remaining, + color, + vsize * DIGIT_FACTOR, + ); + } + + let cs = ChronoState { + remaining, + updated: Instant::now(), + }; + if let Ok(s) = rx.recv() { + match s { + NextTimerState::Running => { + self.state = TimerState::Running(cs); + } + NextTimerState::Unstarted => { + self.state = TimerState::Unstarted; + } + _ => unreachable!(), + } + } else { + self.state = TimerState::Paused(cs); } } @@ -226,36 +222,32 @@ impl Timer { CountDirection::Up => self.duration, CountDirection::Down => Duration::from_nanos(0), }; - StripBuilder::new(ui) - .size(Size::relative(0.33333)) - .size(Size::remainder()) - .cell_layout(Layout::centered_and_justified(Direction::LeftToRight)) - .vertical(|mut strip| { - strip.strip(|pstrip| { - pstrip - .size(Size::remainder()) - .cell_layout(Layout::centered_and_justified(Direction::TopDown)) - .horizontal(|mut pstrip| { - pstrip.cell(|ui| { - let tsize = vsize * 0.3; - let reset = RichText::new("RESTART") - .color(Color32::DARK_GREEN) - .font(FontId::monospace(tsize)); - if ui.button(reset).clicked() { - self.state = TimerState::Unstarted; - } - }); - }); - }); - let color = { - let blink = (Instant::now() - self.tstart).as_secs() % 2 == 0; - if blink { - Color32::BLACK - } else { - Color32::DARK_GRAY - } - }; - display_digits(&mut strip, remaining, color, vsize * DIGIT_FACTOR); - }); + + let tsize = vsize * 0.3; + let reset = RichText::new("RESTART") + .color(Color32::DARK_GREEN) + .font(FontId::monospace(tsize)); + let color = { + let blink = (Instant::now() - self.tstart).as_secs() % 2 == 0; + if blink { + Color32::BLACK + } else { + Color32::DARK_GRAY + } + }; + let (sender, rx) = channel(); + + one_button( + ui, + reset, + NextTimerState::Unstarted, + sender, + remaining, + color, + tsize, + ); + if rx.recv().is_ok() { + self.state = TimerState::Unstarted; + } } } diff --git a/src/timer/state.rs b/src/timer/state.rs index e08bf5b..f83057a 100644 --- a/src/timer/state.rs +++ b/src/timer/state.rs @@ -8,6 +8,16 @@ pub(crate) enum TimerState { Finished, } +// this is a bit of a hack to deat with not being able to have const instances of the regular +// timerstate. +#[derive(Clone, Copy, Debug, PartialEq)] +pub(crate) enum NextTimerState { + Unstarted, + Paused, + Running, + // no need for finished, there will never be a button to click on that says "finished" +} + impl PartialEq for TimerState { fn eq(&self, other: &Self) -> bool { matches!( @@ -21,6 +31,7 @@ impl PartialEq for TimerState { } impl Eq for TimerState {} +impl Eq for NextTimerState {} #[derive(Debug, Clone, Copy)] pub(crate) struct ChronoState {