use std::fmt; use time::OffsetDateTime; use ratatui::{ buffer::Buffer, layout::{Constraint, Layout, Rect}, widgets::{StatefulWidget, Widget}, }; use crate::{ common::Style, widgets::clock_elements::{COLON_WIDTH, Colon, DIGIT_SPACE_WIDTH, DIGIT_WIDTH, Digit}, }; use super::clock_elements::DIGIT_HEIGHT; #[derive(Debug, Clone)] pub enum Selected { Seconds, Minutes, Hours, } impl Selected { pub fn next(&self) -> Self { match self { Selected::Seconds => Selected::Minutes, Selected::Minutes => Selected::Hours, Selected::Hours => Selected::Seconds, } } pub fn prev(&self) -> Self { match self { Selected::Seconds => Selected::Hours, Selected::Minutes => Selected::Seconds, Selected::Hours => Selected::Minutes, } } } impl fmt::Display for Selected { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Selected::Seconds => write!(f, "[edit seconds]"), Selected::Minutes => write!(f, "[edit minutes]"), Selected::Hours => write!(f, "[edit hours]"), } } } #[derive(Debug, Clone)] pub struct EditTimeState { selected: Selected, time: OffsetDateTime, min: OffsetDateTime, max: OffsetDateTime, } #[derive(Debug, Clone)] pub struct EditTimeStateArgs { pub time: OffsetDateTime, pub min: OffsetDateTime, pub max: OffsetDateTime, } impl EditTimeState { pub fn new(args: EditTimeStateArgs) -> Self { EditTimeState { time: args.time, min: args.min, max: args.max, selected: Selected::Minutes, } } pub fn set_time(&mut self, time: OffsetDateTime) { self.time = time; } pub fn set_min_time(&mut self, min: OffsetDateTime) { self.min = min; } pub fn set_max_time(&mut self, min: OffsetDateTime) { self.max = min; } pub fn get_time(&mut self) -> &OffsetDateTime { &self.time } pub fn get_selected(&mut self) -> &Selected { &self.selected } pub fn next(&mut self) { self.selected = self.selected.next(); } pub fn prev(&mut self) { self.selected = self.selected.prev(); } pub fn up(&mut self) { self.time = match self.selected { Selected::Seconds => { if self .time .lt(&self.max.saturating_sub(time::Duration::new(1, 0))) { self.time.saturating_add(time::Duration::new(1, 0)) } else { self.time } } Selected::Minutes => { if self .time .lt(&self.max.saturating_sub(time::Duration::new(60, 0))) { self.time.saturating_add(time::Duration::new(60, 0)) } else { self.time } } Selected::Hours => { if self .time .lt(&self.max.saturating_sub(time::Duration::new(60 * 60, 0))) { self.time.saturating_add(time::Duration::new(60 * 60, 0)) } else { self.time } } } } pub fn down(&mut self) { self.time = match self.selected { Selected::Seconds => { if self .time .ge(&self.min.saturating_add(time::Duration::new(1, 0))) { self.time.saturating_sub(time::Duration::new(1, 0)) } else { self.time } } Selected::Minutes => { if self .time .ge(&self.min.saturating_add(time::Duration::new(60, 0))) { self.time.saturating_sub(time::Duration::new(60, 0)) } else { self.time } } Selected::Hours => { if self .time .ge(&self.min.saturating_add(time::Duration::new(60 * 60, 0))) { self.time.saturating_sub(time::Duration::new(60 * 60, 0)) } else { self.time } } } } } #[derive(Debug, Clone)] pub struct EditTimeWidget { style: Style, } impl EditTimeWidget { pub fn new(style: Style) -> Self { Self { style } } fn get_horizontal_lengths(&self) -> Vec { vec![ DIGIT_WIDTH, // h DIGIT_SPACE_WIDTH, // (space) DIGIT_WIDTH, // h COLON_WIDTH, // : DIGIT_WIDTH, // m DIGIT_SPACE_WIDTH, // (space) DIGIT_WIDTH, // m COLON_WIDTH, // : DIGIT_WIDTH, // s DIGIT_SPACE_WIDTH, // (space) DIGIT_WIDTH, // s ] } pub fn get_width(&self) -> u16 { self.get_horizontal_lengths().iter().sum() } pub fn get_height(&self) -> u16 { DIGIT_HEIGHT } } impl StatefulWidget for EditTimeWidget { type State = EditTimeState; fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { let symbol = self.style.get_digit_symbol(); let edit_hours = matches!(state.selected, Selected::Hours); let edit_minutes = matches!(state.selected, Selected::Minutes); let edit_secs = matches!(state.selected, Selected::Seconds); let [hh, _, h, c_hm, mm, _, m, c_ms, ss, _, s] = Layout::horizontal(Constraint::from_lengths(self.get_horizontal_lengths())).areas(area); Digit::new((state.time.hour() as u64) / 10, edit_hours, symbol).render(hh, buf); Digit::new((state.time.hour() as u64) % 10, edit_hours, symbol).render(h, buf); Colon::new(symbol).render(c_hm, buf); Digit::new((state.time.minute() as u64) / 10, edit_minutes, symbol).render(mm, buf); Digit::new((state.time.minute() as u64) % 10, edit_minutes, symbol).render(m, buf); Colon::new(symbol).render(c_ms, buf); Digit::new((state.time.second() as u64) / 10, edit_secs, symbol).render(ss, buf); Digit::new((state.time.second() as u64) % 10, edit_secs, symbol).render(s, buf); } }