From ab10a38901476da559d8ff2abd11d6feca2399aa Mon Sep 17 00:00:00 2001 From: "Jens K." <47693+sectore@users.noreply.github.com> Date: Wed, 18 Dec 2024 13:51:22 +0100 Subject: [PATCH] clock style (#18) --- src/app.rs | 17 +++++-- src/widgets/clock.rs | 102 +++++++++++++++++++++++++-------------- src/widgets/countdown.rs | 8 ++- src/widgets/pomodoro.rs | 16 +++--- src/widgets/timer.rs | 18 ++++--- 5 files changed, 105 insertions(+), 56 deletions(-) diff --git a/src/app.rs b/src/app.rs index 6a1f55e..48aed1e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -4,7 +4,7 @@ use crate::{ events::{Event, EventHandler, Events}, terminal::Terminal, widgets::{ - clock::{self, Clock}, + clock::{self, Clock, Style as ClockStyle}, countdown::{Countdown, CountdownWidget}, footer::Footer, header::Header, @@ -43,6 +43,7 @@ pub struct App { countdown: Countdown, timer: Timer, pomodoro: Pomodoro, + clock_style: ClockStyle, } impl App { @@ -51,6 +52,7 @@ impl App { mode: Mode::Running, content: Content::Countdown, show_menu: false, + clock_style: ClockStyle::Default, countdown: Countdown::new(Clock::::new( args.countdown, Duration::from_millis(TICK_VALUE_MS), @@ -100,6 +102,7 @@ impl App { KeyCode::Char('t') => self.content = Content::Timer, KeyCode::Char('p') => self.content = Content::Pomodoro, KeyCode::Char('m') => self.show_menu = !self.show_menu, + KeyCode::Char('b') => self.clock_style = self.clock_style.next(), KeyCode::Up => self.show_menu = true, KeyCode::Down => self.show_menu = false, _ => {} @@ -119,9 +122,15 @@ struct AppWidget; impl AppWidget { fn render_content(&self, area: Rect, buf: &mut Buffer, state: &mut App) { match state.content { - Content::Timer => TimerWidget.render(area, buf, &mut state.timer), - Content::Countdown => CountdownWidget.render(area, buf, &mut state.countdown), - Content::Pomodoro => PomodoroWidget.render(area, buf, &mut state.pomodoro), + Content::Timer => { + TimerWidget.render(area, buf, &mut (state.clock_style, state.timer.clone())) + } + Content::Countdown => { + CountdownWidget.render(area, buf, &mut (state.clock_style, state.countdown.clone())) + } + Content::Pomodoro => { + PomodoroWidget.render(area, buf, &mut (state.clock_style, state.pomodoro.clone())) + } }; } } diff --git a/src/widgets/clock.rs b/src/widgets/clock.rs index 300ea2f..3fc14bb 100644 --- a/src/widgets/clock.rs +++ b/src/widgets/clock.rs @@ -11,6 +11,25 @@ use ratatui::{ use crate::utils::center_horizontal; +#[derive(Debug, Copy, Clone)] +pub enum Style { + Default, + Empty, + Thick, + Cross, +} + +impl Style { + pub fn next(&self) -> Self { + match self { + Style::Default => Style::Empty, + Style::Empty => Style::Thick, + Style::Thick => Style::Cross, + Style::Cross => Style::Default, + } + } +} + #[derive(Debug, Copy, Clone, Display, PartialEq, Eq)] pub enum Time { Seconds, @@ -404,8 +423,6 @@ impl Clock { } } -const DIGIT_SYMBOL: &str = "█"; - const DIGIT_SIZE: usize = 5; const DIGIT_WIDTH: u16 = DIGIT_SIZE as u16; const DIGIT_HEIGHT: u16 = DIGIT_SIZE as u16 + 1 /* border height */; @@ -515,6 +532,7 @@ pub struct ClockWidget where T: std::fmt::Debug, { + style: Style, phantom: PhantomData, } @@ -522,12 +540,22 @@ impl ClockWidget where T: std::fmt::Debug, { - pub fn new() -> Self { + pub fn new(style: Style) -> Self { Self { + style, phantom: PhantomData, } } + fn get_digit_symbol(&self) -> &str { + match &self.style { + Style::Default => "█", + Style::Empty => "░", + Style::Cross => "╬", + Style::Thick => "┃", + } + } + fn get_horizontal_lengths(&self, format: &Format) -> Vec { match format { Format::HhMmSs => vec![ @@ -589,7 +617,7 @@ where DIGIT_HEIGHT } - fn render_digit(number: u64, with_border: bool, area: Rect, buf: &mut Buffer) { + fn render_digit(&self, number: u64, with_border: bool, area: Rect, buf: &mut Buffer) { let left = area.left(); let top = area.top(); @@ -616,7 +644,7 @@ where y: top + y as u16, }; if let Some(cell) = buf.cell_mut(p) { - cell.set_symbol(DIGIT_SYMBOL); + cell.set_symbol(self.get_digit_symbol()); } } }); @@ -635,7 +663,7 @@ where } } - fn render_colon(area: Rect, buf: &mut Buffer) { + fn render_colon(&self, area: Rect, buf: &mut Buffer) { let left = area.left(); let top = area.top(); @@ -660,7 +688,7 @@ where for pos in positions { if let Some(cell) = buf.cell_mut(pos) { - cell.set_symbol(DIGIT_SYMBOL); + cell.set_symbol(self.get_digit_symbol()); } } } @@ -673,9 +701,9 @@ where type State = Clock; fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { - let format = &state.format; - let widths = self.get_horizontal_lengths(format); - let area = center_horizontal(area, Constraint::Length(self.get_width(format))); + let format = state.format; + let widths = self.get_horizontal_lengths(&format); + let area = center_horizontal(area, Constraint::Length(self.get_width(&format))); let edit_hours = matches!(state.mode, Mode::Editable(Time::Hours, _)); let edit_minutes = matches!(state.mode, Mode::Editable(Time::Minutes, _)); let edit_secs = matches!(state.mode, Mode::Editable(Time::Seconds, _)); @@ -683,51 +711,51 @@ where Format::HhMmSs => { let [hh, _, h, c_hm, mm, _, m, c_ms, ss, _, s] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Self::render_digit(state.current_hours() / 10, edit_hours, hh, buf); - Self::render_digit(state.current_hours() % 10, edit_hours, h, buf); - Self::render_colon(c_hm, buf); - Self::render_digit(state.current_minutes_mod() / 10, edit_minutes, mm, buf); - Self::render_digit(state.current_minutes_mod() % 10, edit_minutes, m, buf); - Self::render_colon(c_ms, buf); - Self::render_digit(state.current_seconds_mod() / 10, edit_secs, ss, buf); - Self::render_digit(state.current_seconds_mod() % 10, edit_secs, s, buf); + self.render_digit(state.current_hours() / 10, edit_hours, hh, buf); + self.render_digit(state.current_hours() % 10, edit_hours, h, buf); + self.render_colon(c_hm, buf); + self.render_digit(state.current_minutes_mod() / 10, edit_minutes, mm, buf); + self.render_digit(state.current_minutes_mod() % 10, edit_minutes, m, buf); + self.render_colon(c_ms, buf); + self.render_digit(state.current_seconds_mod() / 10, edit_secs, ss, buf); + self.render_digit(state.current_seconds_mod() % 10, edit_secs, s, buf); } Format::HMmSs => { let [h, c_hm, mm, _, m, c_ms, ss, _, s] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Self::render_digit(state.current_hours() % 10, edit_hours, h, buf); - Self::render_colon(c_hm, buf); - Self::render_digit(state.current_minutes_mod() / 10, edit_minutes, mm, buf); - Self::render_digit(state.current_minutes_mod() % 10, edit_minutes, m, buf); - Self::render_colon(c_ms, buf); - Self::render_digit(state.current_seconds_mod() / 10, edit_secs, ss, buf); - Self::render_digit(state.current_seconds_mod() % 10, edit_secs, s, buf); + self.render_digit(state.current_hours() % 10, edit_hours, h, buf); + self.render_colon(c_hm, buf); + self.render_digit(state.current_minutes_mod() / 10, edit_minutes, mm, buf); + self.render_digit(state.current_minutes_mod() % 10, edit_minutes, m, buf); + self.render_colon(c_ms, buf); + self.render_digit(state.current_seconds_mod() / 10, edit_secs, ss, buf); + self.render_digit(state.current_seconds_mod() % 10, edit_secs, s, buf); } Format::MmSs => { let [mm, _, m, c_ms, ss, _, s] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Self::render_digit(state.current_minutes_mod() / 10, edit_minutes, mm, buf); - Self::render_digit(state.current_minutes_mod() % 10, edit_minutes, m, buf); - Self::render_colon(c_ms, buf); - Self::render_digit(state.current_seconds_mod() / 10, edit_secs, ss, buf); - Self::render_digit(state.current_seconds_mod() % 10, edit_secs, s, buf); + self.render_digit(state.current_minutes_mod() / 10, edit_minutes, mm, buf); + self.render_digit(state.current_minutes_mod() % 10, edit_minutes, m, buf); + self.render_colon(c_ms, buf); + self.render_digit(state.current_seconds_mod() / 10, edit_secs, ss, buf); + self.render_digit(state.current_seconds_mod() % 10, edit_secs, s, buf); } Format::MSs => { let [m, c_ms, ss, _, s] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Self::render_digit(state.current_minutes_mod() % 10, edit_minutes, m, buf); - Self::render_colon(c_ms, buf); - Self::render_digit(state.current_seconds_mod() / 10, edit_secs, ss, buf); - Self::render_digit(state.current_seconds_mod() % 10, edit_secs, s, buf); + self.render_digit(state.current_minutes_mod() % 10, edit_minutes, m, buf); + self.render_colon(c_ms, buf); + self.render_digit(state.current_seconds_mod() / 10, edit_secs, ss, buf); + self.render_digit(state.current_seconds_mod() % 10, edit_secs, s, buf); } Format::Ss => { let [ss, _, s] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Self::render_digit(state.current_seconds_mod() / 10, edit_secs, ss, buf); - Self::render_digit(state.current_seconds_mod() % 10, edit_secs, s, buf); + self.render_digit(state.current_seconds_mod() / 10, edit_secs, ss, buf); + self.render_digit(state.current_seconds_mod() % 10, edit_secs, s, buf); } Format::S => { let [s] = Layout::horizontal(Constraint::from_lengths(widths)).areas(area); - Self::render_digit(state.current_seconds_mod() % 10, edit_secs, s, buf); + self.render_digit(state.current_seconds_mod() % 10, edit_secs, s, buf); } } } diff --git a/src/widgets/countdown.rs b/src/widgets/countdown.rs index 9e61138..975c81f 100644 --- a/src/widgets/countdown.rs +++ b/src/widgets/countdown.rs @@ -13,6 +13,8 @@ use crate::{ widgets::clock::{self, Clock, ClockWidget}, }; +use super::clock::Style; + #[derive(Debug, Clone)] pub struct Countdown { clock: Clock, @@ -67,9 +69,11 @@ impl EventHandler for Countdown { pub struct CountdownWidget; impl StatefulWidget for CountdownWidget { - type State = Countdown; + type State = (Style, Countdown); fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { - let clock = ClockWidget::new(); + let style = state.0; + let state = &mut state.1; + let clock = ClockWidget::new(style); let label = Line::raw((format!("Countdown {}", state.clock.get_mode())).to_uppercase()); let area = center( diff --git a/src/widgets/pomodoro.rs b/src/widgets/pomodoro.rs index c3d4ff4..3b9caf4 100644 --- a/src/widgets/pomodoro.rs +++ b/src/widgets/pomodoro.rs @@ -15,6 +15,8 @@ use std::{cmp::max, time::Duration}; use strum::Display; +use super::clock::Style; + #[derive(Debug, Clone, Display, Hash, Eq, PartialEq)] enum Mode { Work, @@ -117,9 +119,11 @@ impl EventHandler for Pomodoro { pub struct PomodoroWidget; impl StatefulWidget for PomodoroWidget { - type State = Pomodoro; + type State = (Style, Pomodoro); fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { - let clock = ClockWidget::new(); + let style = state.0; + let state = &mut state.1; + let clock_widget = ClockWidget::new(style); let label = Line::raw( (format!( "Pomodoro {} {}", @@ -132,16 +136,16 @@ impl StatefulWidget for PomodoroWidget { let area = center( area, Constraint::Length(max( - clock.get_width(&state.get_clock().get_format()), + clock_widget.get_width(&state.get_clock().get_format()), label.width() as u16, )), - Constraint::Length(clock.get_height() + 1 /* height of mode_str */), + Constraint::Length(clock_widget.get_height() + 1 /* height of mode_str */), ); let [v1, v2] = - Layout::vertical(Constraint::from_lengths([clock.get_height(), 1])).areas(area); + Layout::vertical(Constraint::from_lengths([clock_widget.get_height(), 1])).areas(area); - clock.render(v1, buf, state.get_clock()); + clock_widget.render(v1, buf, state.get_clock()); label.centered().render(v2, buf); } } diff --git a/src/widgets/timer.rs b/src/widgets/timer.rs index a2aa60c..d2eb8bc 100644 --- a/src/widgets/timer.rs +++ b/src/widgets/timer.rs @@ -12,6 +12,8 @@ use ratatui::{ }; use std::cmp::max; +use super::clock::Style; + #[derive(Debug, Clone)] pub struct Timer { clock: Clock, @@ -63,23 +65,25 @@ impl EventHandler for Timer { pub struct TimerWidget; impl StatefulWidget for &TimerWidget { - type State = Timer; + type State = (Style, Timer); fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { - let clock = ClockWidget::new(); - let label = Line::raw((format!("Timer {}", state.clock.get_mode())).to_uppercase()); + let style = state.0; + let clock = &mut state.1.clock; + let clock_widget = ClockWidget::new(style); + let label = Line::raw((format!("Timer {}", clock.get_mode())).to_uppercase()); let area = center( area, Constraint::Length(max( - clock.get_width(&state.clock.get_format()), + clock_widget.get_width(&clock.get_format()), label.width() as u16, )), - Constraint::Length(clock.get_height() + 1 /* height of label */), + Constraint::Length(clock_widget.get_height() + 1 /* height of label */), ); let [v1, v2] = - Layout::vertical(Constraint::from_lengths([clock.get_height(), 1])).areas(area); + Layout::vertical(Constraint::from_lengths([clock_widget.get_height(), 1])).areas(area); - clock.render(v1, buf, &mut state.clock); + clock_widget.render(v1, buf, clock); label.centered().render(v2, buf); } }