From 857a064a61cbb11268ea83552ccdfd7d1d7fc72b Mon Sep 17 00:00:00 2001 From: Joe Ardent Date: Thu, 13 Oct 2022 15:14:21 -0700 Subject: [PATCH] it counts down --- Cargo.lock | 10 ++++ Cargo.toml | 2 + src/lib.rs | 1 + src/main.rs | 13 +++- src/timer.rs | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 src/lib.rs create mode 100644 src/timer.rs diff --git a/Cargo.lock b/Cargo.lock index 9204118..b22b303 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -588,6 +588,15 @@ dependencies = [ "winit", ] +[[package]] +name = "egui_extras" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f698f685bb0ad39e87109e2f695ded0bccde77d5d40bbf7590cb5561c1e3039d" +dependencies = [ + "egui", +] + [[package]] name = "egui_glow" version = "0.19.0" @@ -1011,6 +1020,7 @@ dependencies = [ "clap", "eframe", "egui", + "egui_extras", "naga 0.10.0", "wgpu 0.14.0", ] diff --git a/Cargo.toml b/Cargo.toml index 618d031..965f61c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ name = "katabastird" version = "0.1.0" edition = "2021" +rust-version = "1.61" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -9,5 +10,6 @@ edition = "2021" clap = { version = "4.0.14", features = ["derive", "env", "unicode", "suggestions", "usage"] } eframe = { version = "0.19.0", features = ["wgpu"] } egui = { version = "0.19.0", features = ["bytemuck"] } +egui_extras = "0.19.0" naga = { version = "0.10.0", features = ["spv-out", "wgsl-out", "wgsl-in"] } wgpu = { version = "0.14.0", features = ["naga", "spirv"] } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..1227065 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1 @@ +pub mod timer; diff --git a/src/main.rs b/src/main.rs index e7a11a9..d5271b9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,14 @@ +use katabastird::timer::Timer; + fn main() { - println!("Hello, world!"); + let options = eframe::NativeOptions { + renderer: eframe::Renderer::Wgpu, + ..Default::default() + }; + + eframe::run_native( + "eframe template", + options, + Box::new(|_cc| Box::new(Timer::default())), + ); } diff --git a/src/timer.rs b/src/timer.rs new file mode 100644 index 0000000..7dc8b09 --- /dev/null +++ b/src/timer.rs @@ -0,0 +1,165 @@ +use egui::{Color32, Direction, FontId, Layout, RichText, Ui}; +use egui_extras::{Size, StripBuilder}; +use std::time::{Duration, Instant}; + +use eframe::App; + +#[derive(Debug, Clone, Copy)] +pub enum CountDirection { + Up, + Down, +} + +#[derive(Debug, Clone, Copy)] +pub struct Timer { + direction: CountDirection, + duration: Duration, + state: State, +} + +#[derive(Debug, Clone, Copy)] +enum State { + Unstarted, + Paused(Duration), // time remaining + Running(RunningState), // time remaining +} + +impl PartialEq for State { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Paused(_), Self::Paused(_)) => true, + (Self::Running(_), Self::Running(_)) => true, + (Self::Unstarted, Self::Unstarted) => true, + _ => false, + } + } +} + +impl Eq for State {} + +#[derive(Debug, Clone, Copy)] +struct RunningState { + started: Instant, + remaining: Duration, +} + +impl Default for Timer { + fn default() -> Self { + let dur = Duration::from_secs(30); + Self { + direction: CountDirection::Down, + duration: dur, + //state: State::Running(dur), + state: State::Unstarted, + } + } +} + +impl App for Timer { + fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { + ctx.request_repaint_after(Duration::from_secs(1)); + egui::CentralPanel::default().show(ctx, |ui| { + let size = ctx.used_size(); + let vsize = if size[1].is_normal() { + size[1].abs() + } else { + 200.0 + }; + + match self.state { + State::Running(_) => self.running(ui, vsize), + State::Paused(_) => {} + State::Unstarted => { + self.unstarted(ui, vsize); + } + } + }); + } +} + +impl Timer { + fn unstarted(&mut self, ui: &mut Ui, size: f32) { + let start = RichText::new("START") + .font(FontId::monospace(size * 0.9)) + .color(Color32::WHITE) + .background_color(Color32::LIGHT_GREEN); + + StripBuilder::new(ui) + .size(Size::remainder()) + .cell_layout(Layout::centered_and_justified(egui::Direction::TopDown)) + .horizontal(|mut strip| { + strip.cell(|ui| { + if ui.button(start).clicked() { + let dur = self.duration; + self.state = State::Running(RunningState { + started: Instant::now(), + remaining: dur, + }); + } + }); + }); + } + + fn paused(&mut self, ui: &mut Ui, size: f32) { + todo!() + } + + fn running(&mut self, ui: &mut Ui, size: f32) { + let tsize = size * 0.2; + let text = RichText::new("PAUSE") + .font(FontId::monospace(tsize)) + .color(Color32::GOLD); + + let elapsed; + let started; + if let State::Running(rs) = self.state { + elapsed = Instant::now() - rs.started; + started = rs.started; + } else { + unreachable!() + } + + let remaining = self.duration.saturating_sub(elapsed); + + // StripBuilder::vertical(); + StripBuilder::new(ui) + .size(Size::remainder()) + .cell_layout(Layout::centered_and_justified(Direction::LeftToRight)) + .horizontal(|mut strip| { + strip.cell(|ui| { + if ui.button(text).clicked() { + self.state = State::Paused(remaining); + } + }); + }); + + if let State::Paused(_) = self.state { + return; + } + + self.state = State::Running(RunningState { started, remaining }); + + let hours = remaining.as_secs() / 3600; + let minutes = (remaining.as_secs() / 60) % 60; + let seconds = remaining.as_secs() % 60; + let tsize = size * 0.7; + let hours = RichText::new(format!("{:02}", hours)).font(FontId::monospace(tsize)); + let minutes = RichText::new(format!("{:02}", minutes)).font(FontId::monospace(tsize)); + let seconds = RichText::new(format!("{:02}", seconds)).font(FontId::monospace(tsize)); + + StripBuilder::new(ui) + .sizes(Size::relative(0.33), 3) + .cell_layout(Layout::centered_and_justified(Direction::TopDown)) + .horizontal(|mut strip| { + strip.cell(|ui| { + ui.label(hours); + }); + strip.cell(|ui| { + ui.label(minutes); + }); + strip.cell(|ui| { + ui.label(seconds); + }); + }); + } +}