use channels to send signals

This commit is contained in:
Joe Ardent 2022-10-22 17:25:52 -07:00
parent 4ccc2e4738
commit 58319dffff
3 changed files with 187 additions and 113 deletions

71
src/timer/gui.rs Normal file
View file

@ -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<NextTimerState>,
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<NextTimerState>,
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);
});
}

View file

@ -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,38 +159,18 @@ 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 (sender, rx) = channel();
{
let color = {
let blink = (Instant::now() - self.tstart).as_secs() % 2 == 0;
if blink {
@ -209,16 +184,37 @@ impl Timer {
CountDirection::Down => remaining,
CountDirection::Up => self.duration - remaining,
};
display_digits(&mut strip, remaining, color, vsize * DIGIT_FACTOR);
});
if is_running {
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);
}
}
fn finished(&mut self, ui: &mut Ui, vsize: f32) {
@ -226,27 +222,11 @@ 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 {
@ -255,7 +235,19 @@ impl Timer {
Color32::DARK_GRAY
}
};
display_digits(&mut strip, remaining, color, vsize * DIGIT_FACTOR);
});
let (sender, rx) = channel();
one_button(
ui,
reset,
NextTimerState::Unstarted,
sender,
remaining,
color,
tsize,
);
if rx.recv().is_ok() {
self.state = TimerState::Unstarted;
}
}
}

View file

@ -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 {