use channels to send signals
This commit is contained in:
parent
4ccc2e4738
commit
58319dffff
3 changed files with 187 additions and 113 deletions
71
src/timer/gui.rs
Normal file
71
src/timer/gui.rs
Normal 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);
|
||||||
|
});
|
||||||
|
}
|
218
src/timer/mod.rs
218
src/timer/mod.rs
|
@ -1,14 +1,17 @@
|
||||||
|
use std::sync::mpsc::channel;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use egui::{Color32, Direction, FontId, Layout, RichText, Ui};
|
use egui::{Color32, FontId, Layout, RichText, Ui};
|
||||||
use egui_extras::{Size, StripBuilder};
|
use egui_extras::{Size, StripBuilder};
|
||||||
|
|
||||||
use crate::{cli::Cli, util::*, AIRHORN, DIGIT_FACTOR, PREDATOR_FONT, TEXT_FACTOR};
|
use crate::{cli::Cli, util::*, AIRHORN, DIGIT_FACTOR, PREDATOR_FONT, TEXT_FACTOR};
|
||||||
|
|
||||||
mod state;
|
mod state;
|
||||||
use state::{ChronoState, TimerState};
|
use state::{ChronoState, NextTimerState, TimerState};
|
||||||
mod eframe_app;
|
mod eframe_app;
|
||||||
|
mod gui;
|
||||||
|
use gui::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum CountDirection {
|
pub enum CountDirection {
|
||||||
|
@ -111,34 +114,8 @@ impl Timer {
|
||||||
.font(FontId::monospace(tsize))
|
.font(FontId::monospace(tsize))
|
||||||
.color(Color32::GOLD);
|
.color(Color32::GOLD);
|
||||||
|
|
||||||
let mut is_paused = false;
|
|
||||||
let elapsed = Instant::now() - cs.updated;
|
let elapsed = Instant::now() - cs.updated;
|
||||||
let remaining = cs.remaining.saturating_sub(elapsed);
|
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 remaining.is_zero() {
|
||||||
if let Some(alarm_file) = &self.alarm {
|
if let Some(alarm_file) = &self.alarm {
|
||||||
let alarm_file = alarm_file.to_owned();
|
let alarm_file = alarm_file.to_owned();
|
||||||
|
@ -148,15 +125,33 @@ impl Timer {
|
||||||
return;
|
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 {
|
let cs = ChronoState {
|
||||||
remaining,
|
remaining,
|
||||||
updated: Instant::now(),
|
updated: Instant::now(),
|
||||||
};
|
};
|
||||||
if is_paused {
|
|
||||||
// did we click the reset button?
|
if rx.recv().is_ok() {
|
||||||
if self.state == TimerState::Unstarted {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.state = TimerState::Paused(cs);
|
self.state = TimerState::Paused(cs);
|
||||||
} else {
|
} else {
|
||||||
self.state = TimerState::Running(cs);
|
self.state = TimerState::Running(cs);
|
||||||
|
@ -164,60 +159,61 @@ impl Timer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paused(&mut self, ui: &mut Ui, vsize: f32, cs: ChronoState) {
|
fn paused(&mut self, ui: &mut Ui, vsize: f32, cs: ChronoState) {
|
||||||
let mut is_running = false;
|
|
||||||
let tsize = vsize * TEXT_FACTOR;
|
let tsize = vsize * TEXT_FACTOR;
|
||||||
|
|
||||||
let remaining = cs.remaining;
|
let remaining = cs.remaining;
|
||||||
StripBuilder::new(ui)
|
let resume = RichText::new("RESUME")
|
||||||
.size(Size::relative(0.33333))
|
.color(Color32::GREEN)
|
||||||
.size(Size::remainder())
|
.font(FontId::monospace(tsize));
|
||||||
.cell_layout(Layout::centered_and_justified(Direction::LeftToRight))
|
let reset = RichText::new("RESET")
|
||||||
.vertical(|mut strip| {
|
.color(Color32::RED)
|
||||||
strip.strip(|pstrip| {
|
.font(FontId::monospace(tsize));
|
||||||
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);
|
|
||||||
});
|
|
||||||
|
|
||||||
if is_running {
|
let (sender, rx) = channel();
|
||||||
let cs = ChronoState {
|
{
|
||||||
remaining,
|
let color = {
|
||||||
updated: Instant::now(),
|
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::Up => self.duration,
|
||||||
CountDirection::Down => Duration::from_nanos(0),
|
CountDirection::Down => Duration::from_nanos(0),
|
||||||
};
|
};
|
||||||
StripBuilder::new(ui)
|
|
||||||
.size(Size::relative(0.33333))
|
let tsize = vsize * 0.3;
|
||||||
.size(Size::remainder())
|
let reset = RichText::new("RESTART")
|
||||||
.cell_layout(Layout::centered_and_justified(Direction::LeftToRight))
|
.color(Color32::DARK_GREEN)
|
||||||
.vertical(|mut strip| {
|
.font(FontId::monospace(tsize));
|
||||||
strip.strip(|pstrip| {
|
let color = {
|
||||||
pstrip
|
let blink = (Instant::now() - self.tstart).as_secs() % 2 == 0;
|
||||||
.size(Size::remainder())
|
if blink {
|
||||||
.cell_layout(Layout::centered_and_justified(Direction::TopDown))
|
Color32::BLACK
|
||||||
.horizontal(|mut pstrip| {
|
} else {
|
||||||
pstrip.cell(|ui| {
|
Color32::DARK_GRAY
|
||||||
let tsize = vsize * 0.3;
|
}
|
||||||
let reset = RichText::new("RESTART")
|
};
|
||||||
.color(Color32::DARK_GREEN)
|
let (sender, rx) = channel();
|
||||||
.font(FontId::monospace(tsize));
|
|
||||||
if ui.button(reset).clicked() {
|
one_button(
|
||||||
self.state = TimerState::Unstarted;
|
ui,
|
||||||
}
|
reset,
|
||||||
});
|
NextTimerState::Unstarted,
|
||||||
});
|
sender,
|
||||||
});
|
remaining,
|
||||||
let color = {
|
color,
|
||||||
let blink = (Instant::now() - self.tstart).as_secs() % 2 == 0;
|
tsize,
|
||||||
if blink {
|
);
|
||||||
Color32::BLACK
|
if rx.recv().is_ok() {
|
||||||
} else {
|
self.state = TimerState::Unstarted;
|
||||||
Color32::DARK_GRAY
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
display_digits(&mut strip, remaining, color, vsize * DIGIT_FACTOR);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,16 @@ pub(crate) enum TimerState {
|
||||||
Finished,
|
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 {
|
impl PartialEq for TimerState {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
|
@ -21,6 +31,7 @@ impl PartialEq for TimerState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for TimerState {}
|
impl Eq for TimerState {}
|
||||||
|
impl Eq for NextTimerState {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub(crate) struct ChronoState {
|
pub(crate) struct ChronoState {
|
||||||
|
|
Loading…
Reference in a new issue