timr-tui/src/widgets/edit_time.rs
Jens Krause 3d9b235f12
Rust 1.85.0 + Rust 2024 Edition (#68)
- Add `rust-toolchain.toml`
- Refactor `flake` to consider `rust-toolchain.toml`, especially Rust version
- `just`: Add run-sound command
- Ignore sound files
- Format
2025-02-25 20:30:20 +01:00

232 lines
6.4 KiB
Rust

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<u16> {
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);
}
}