Persist app state (#21)
This commit is contained in:
parent
2cf411e2ae
commit
c9b444e91a
13
Cargo.lock
generated
13
Cargo.lock
generated
@ -893,6 +893,18 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.134"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
@ -1049,6 +1061,7 @@ dependencies = [
|
||||
"futures",
|
||||
"ratatui",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"strum",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
|
||||
@ -9,6 +9,7 @@ crossterm = {version = "0.28.1", features = ["event-stream", "serde"] }
|
||||
color-eyre = "0.6.2"
|
||||
futures = "0.3"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
strum = { version = "0.26.3", features = ["derive"] }
|
||||
tokio = { version = "1.41.1", features = ["full"] }
|
||||
tokio-stream = "0.1.16"
|
||||
|
||||
120
src/app.rs
120
src/app.rs
@ -1,17 +1,19 @@
|
||||
use crate::{
|
||||
args::{Args, ClockStyle, Content},
|
||||
args::Args,
|
||||
constants::TICK_VALUE_MS,
|
||||
events::{Event, EventHandler, Events},
|
||||
storage::AppStorage,
|
||||
terminal::Terminal,
|
||||
widgets::{
|
||||
clock::{self, Clock, ClockArgs},
|
||||
clock::{self, Clock, ClockArgs, Style},
|
||||
countdown::{Countdown, CountdownWidget},
|
||||
footer::Footer,
|
||||
header::Header,
|
||||
pomodoro::{Pomodoro, PomodoroArgs, PomodoroWidget},
|
||||
pomodoro::{Mode as PomodoroMode, Pomodoro, PomodoroArgs, PomodoroWidget},
|
||||
timer::{Timer, TimerWidget},
|
||||
},
|
||||
};
|
||||
use clap::ValueEnum;
|
||||
use color_eyre::Result;
|
||||
use ratatui::{
|
||||
buffer::Buffer,
|
||||
@ -19,9 +21,23 @@ use ratatui::{
|
||||
layout::{Constraint, Layout, Rect},
|
||||
widgets::{StatefulWidget, Widget},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
use tracing::debug;
|
||||
|
||||
#[derive(
|
||||
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Default, Serialize, Deserialize,
|
||||
)]
|
||||
pub enum Content {
|
||||
#[default]
|
||||
#[value(name = "countdown", alias = "c")]
|
||||
Countdown,
|
||||
#[value(name = "timer", alias = "t")]
|
||||
Timer,
|
||||
#[value(name = "pomodoro", alias = "p")]
|
||||
Pomodoro,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Mode {
|
||||
Running,
|
||||
@ -36,49 +52,92 @@ pub struct App {
|
||||
countdown: Countdown,
|
||||
timer: Timer,
|
||||
pomodoro: Pomodoro,
|
||||
clock_style: ClockStyle,
|
||||
style: Style,
|
||||
with_decis: bool,
|
||||
}
|
||||
|
||||
pub struct AppArgs {
|
||||
pub style: Style,
|
||||
pub with_decis: bool,
|
||||
pub content: Content,
|
||||
pub pomodoro_mode: PomodoroMode,
|
||||
pub initial_value_work: Duration,
|
||||
pub current_value_work: Duration,
|
||||
pub initial_value_pause: Duration,
|
||||
pub current_value_pause: Duration,
|
||||
pub initial_value_countdown: Duration,
|
||||
pub current_value_countdown: Duration,
|
||||
pub current_value_timer: Duration,
|
||||
}
|
||||
|
||||
/// Getting `AppArgs` by merging `Args` and `AppStorage`.
|
||||
/// `Args` wins btw.
|
||||
impl From<(Args, AppStorage)> for AppArgs {
|
||||
fn from((args, stg): (Args, AppStorage)) -> Self {
|
||||
AppArgs {
|
||||
with_decis: args.decis || stg.with_decis,
|
||||
content: args.mode.unwrap_or(stg.content),
|
||||
style: args.style.unwrap_or(stg.style),
|
||||
pomodoro_mode: stg.pomodoro_mode,
|
||||
initial_value_work: args.work.unwrap_or(stg.inital_value_work),
|
||||
current_value_work: stg.current_value_work,
|
||||
initial_value_pause: args.pause,
|
||||
current_value_pause: stg.current_value_pause,
|
||||
initial_value_countdown: args.countdown,
|
||||
current_value_countdown: stg.current_value_countdown,
|
||||
current_value_timer: stg.current_value_timer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new(args: Args) -> Self {
|
||||
let Args {
|
||||
pub fn new(args: AppArgs) -> Self {
|
||||
let AppArgs {
|
||||
style,
|
||||
work: work_initial_value,
|
||||
pause: pause_initial_value,
|
||||
mode: content,
|
||||
countdown: countdown_initial_value,
|
||||
decis: with_decis,
|
||||
..
|
||||
initial_value_work,
|
||||
initial_value_pause,
|
||||
initial_value_countdown,
|
||||
current_value_work,
|
||||
current_value_pause,
|
||||
current_value_countdown,
|
||||
current_value_timer,
|
||||
content,
|
||||
with_decis,
|
||||
pomodoro_mode,
|
||||
} = args;
|
||||
Self {
|
||||
mode: Mode::Running,
|
||||
content,
|
||||
show_menu: false,
|
||||
clock_style: style,
|
||||
style,
|
||||
with_decis,
|
||||
countdown: Countdown::new(Clock::<clock::Countdown>::new(ClockArgs {
|
||||
initial_value: countdown_initial_value,
|
||||
initial_value: initial_value_countdown,
|
||||
current_value: current_value_countdown,
|
||||
tick_value: Duration::from_millis(TICK_VALUE_MS),
|
||||
style,
|
||||
with_decis,
|
||||
})),
|
||||
timer: Timer::new(Clock::<clock::Timer>::new(ClockArgs {
|
||||
initial_value: Duration::ZERO,
|
||||
current_value: current_value_timer,
|
||||
tick_value: Duration::from_millis(TICK_VALUE_MS),
|
||||
style,
|
||||
with_decis,
|
||||
})),
|
||||
pomodoro: Pomodoro::new(PomodoroArgs {
|
||||
work: work_initial_value,
|
||||
pause: pause_initial_value,
|
||||
mode: pomodoro_mode,
|
||||
initial_value_work,
|
||||
current_value_work,
|
||||
initial_value_pause,
|
||||
current_value_pause,
|
||||
style,
|
||||
with_decis,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run(&mut self, mut terminal: Terminal, mut events: Events) -> Result<()> {
|
||||
pub async fn run(mut self, mut terminal: Terminal, mut events: Events) -> Result<Self> {
|
||||
while self.is_running() {
|
||||
if let Some(event) = events.next().await {
|
||||
// Pipe events into subviews and handle only 'unhandled' events afterwards
|
||||
@ -97,7 +156,7 @@ impl App {
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn is_running(&self) -> bool {
|
||||
@ -113,11 +172,11 @@ impl App {
|
||||
KeyCode::Char('p') => self.content = Content::Pomodoro,
|
||||
KeyCode::Char('m') => self.show_menu = !self.show_menu,
|
||||
KeyCode::Char(',') => {
|
||||
self.clock_style = self.clock_style.next();
|
||||
self.style = self.style.next();
|
||||
// update clocks
|
||||
self.timer.set_style(self.clock_style);
|
||||
self.countdown.set_style(self.clock_style);
|
||||
self.pomodoro.set_style(self.clock_style);
|
||||
self.timer.set_style(self.style);
|
||||
self.countdown.set_style(self.style);
|
||||
self.pomodoro.set_style(self.style);
|
||||
}
|
||||
KeyCode::Char('.') => {
|
||||
self.with_decis = !self.with_decis;
|
||||
@ -138,6 +197,23 @@ impl App {
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn to_storage(&self) -> AppStorage {
|
||||
AppStorage {
|
||||
content: self.content,
|
||||
show_menu: self.show_menu,
|
||||
style: self.style,
|
||||
with_decis: self.with_decis,
|
||||
pomodoro_mode: self.pomodoro.get_mode().clone(),
|
||||
inital_value_work: self.pomodoro.get_clock_work().initial_value,
|
||||
current_value_work: self.pomodoro.get_clock_work().current_value,
|
||||
inital_value_pause: self.pomodoro.get_clock_pause().initial_value,
|
||||
current_value_pause: self.pomodoro.get_clock_pause().current_value,
|
||||
inital_value_countdown: self.countdown.get_clock().initial_value,
|
||||
current_value_countdown: self.countdown.get_clock().current_value,
|
||||
current_value_timer: self.timer.get_clock().current_value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AppWidget;
|
||||
|
||||
59
src/args.rs
59
src/args.rs
@ -1,42 +1,11 @@
|
||||
use clap::{Parser, ValueEnum};
|
||||
use clap::Parser;
|
||||
use color_eyre::{
|
||||
eyre::{ensure, eyre},
|
||||
Report,
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
|
||||
pub enum Content {
|
||||
#[value(name = "countdown", alias = "c")]
|
||||
Countdown,
|
||||
#[value(name = "timer", alias = "t")]
|
||||
Timer,
|
||||
#[value(name = "pomodoro", alias = "p")]
|
||||
Pomodoro,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, ValueEnum)]
|
||||
pub enum ClockStyle {
|
||||
#[value(name = "bold", alias = "b")]
|
||||
Bold,
|
||||
#[value(name = "empty", alias = "e")]
|
||||
Empty,
|
||||
#[value(name = "thick", alias = "t")]
|
||||
Thick,
|
||||
#[value(name = "cross", alias = "c")]
|
||||
Cross,
|
||||
}
|
||||
|
||||
impl ClockStyle {
|
||||
pub fn next(&self) -> Self {
|
||||
match self {
|
||||
ClockStyle::Bold => ClockStyle::Empty,
|
||||
ClockStyle::Empty => ClockStyle::Thick,
|
||||
ClockStyle::Thick => ClockStyle::Cross,
|
||||
ClockStyle::Cross => ClockStyle::Bold,
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::{app::Content, widgets::clock::Style};
|
||||
|
||||
#[derive(Parser)]
|
||||
pub struct Args {
|
||||
@ -45,41 +14,38 @@ pub struct Args {
|
||||
help = "Countdown time to start from. Format: 'ss', 'mm:ss', or 'hh:mm:ss'"
|
||||
)]
|
||||
pub countdown: Duration,
|
||||
|
||||
#[arg(long, short, value_parser = parse_duration,
|
||||
default_value="25:00" /* 25min */,
|
||||
help = "Work time to count down from. Format: 'ss', 'mm:ss', or 'hh:mm:ss'"
|
||||
)]
|
||||
pub work: Duration,
|
||||
pub work: Option<Duration>,
|
||||
|
||||
#[arg(long, short, value_parser = parse_duration,
|
||||
default_value="5:00" /* 5min */,
|
||||
help = "Pause time to count down from. Format: 'ss', 'mm:ss', or 'hh:mm:ss'"
|
||||
)]
|
||||
pub pause: Duration,
|
||||
|
||||
#[arg(
|
||||
long,
|
||||
short = 'd',
|
||||
default_value = "false",
|
||||
help = "Wether to show deciseconds or not"
|
||||
)]
|
||||
#[arg(long, short = 'd', help = "Whether to show deciseconds or not")]
|
||||
pub decis: bool,
|
||||
|
||||
#[arg(
|
||||
short = 'm',
|
||||
value_enum,
|
||||
default_value = "timer",
|
||||
help = "Mode to start with: [t]imer, [c]ountdown, [p]omodoro"
|
||||
)]
|
||||
pub mode: Content,
|
||||
pub mode: Option<Content>,
|
||||
|
||||
#[arg(
|
||||
long,
|
||||
short = 's',
|
||||
value_enum,
|
||||
default_value = "bold",
|
||||
help = "Style to display time with: [b]old, [t]hick, [c]ross, [e]mpty"
|
||||
)]
|
||||
pub style: ClockStyle,
|
||||
pub style: Option<Style>,
|
||||
|
||||
#[arg(long, short = 'r', help = "Reset stored values to default")]
|
||||
pub reset: bool,
|
||||
}
|
||||
|
||||
fn parse_duration(arg: &str) -> Result<Duration, Report> {
|
||||
@ -130,19 +96,16 @@ mod tests {
|
||||
fn test_parse_duration() {
|
||||
// ss
|
||||
assert_eq!(parse_duration("50").unwrap(), Duration::from_secs(50));
|
||||
|
||||
// mm:ss
|
||||
assert_eq!(
|
||||
parse_duration("01:30").unwrap(),
|
||||
Duration::from_secs(60 + 30)
|
||||
);
|
||||
|
||||
// hh:mm:ss
|
||||
assert_eq!(
|
||||
parse_duration("01:30:00").unwrap(),
|
||||
Duration::from_secs(60 * 60 + 30 * 60)
|
||||
);
|
||||
|
||||
// errors
|
||||
assert!(parse_duration("1:60").is_err()); // invalid seconds
|
||||
assert!(parse_duration("60:00").is_err()); // invalid minutes
|
||||
|
||||
@ -5,18 +5,20 @@ use std::fs;
|
||||
use std::path::PathBuf;
|
||||
pub struct Config {
|
||||
pub log_dir: PathBuf,
|
||||
pub data_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn init() -> Result<Self> {
|
||||
let log_dir = get_default_state_dir()?.join("logs");
|
||||
fs::create_dir_all(&log_dir)?;
|
||||
let data_dir = get_default_state_dir()?.join("data");
|
||||
fs::create_dir_all(&data_dir)?;
|
||||
|
||||
Ok(Self { log_dir })
|
||||
Ok(Self { log_dir, data_dir })
|
||||
}
|
||||
}
|
||||
|
||||
// fn new
|
||||
pub fn get_project_dir() -> Result<ProjectDirs> {
|
||||
let dirs = ProjectDirs::from("", "", APP_NAME)
|
||||
.ok_or_else(|| eyre!("Failed to get project directories"))?;
|
||||
|
||||
29
src/main.rs
29
src/main.rs
@ -6,6 +6,7 @@ mod events;
|
||||
mod logging;
|
||||
|
||||
mod args;
|
||||
mod storage;
|
||||
mod terminal;
|
||||
mod utils;
|
||||
mod widgets;
|
||||
@ -14,20 +15,38 @@ use app::App;
|
||||
use args::Args;
|
||||
use clap::Parser;
|
||||
use color_eyre::Result;
|
||||
use config::Config;
|
||||
use storage::{AppStorage, Storage};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let config = config::Config::init()?;
|
||||
let Config { log_dir, data_dir } = Config::init()?;
|
||||
#[cfg(debug_assertions)]
|
||||
logging::Logger::new(config.log_dir).init()?;
|
||||
logging::Logger::new(log_dir).init()?;
|
||||
|
||||
color_eyre::install()?;
|
||||
|
||||
let args = Args::parse();
|
||||
|
||||
let terminal = terminal::setup()?;
|
||||
let events = events::Events::new();
|
||||
App::new(args).run(terminal, events).await?;
|
||||
|
||||
// get args given by CLI
|
||||
let args = Args::parse();
|
||||
|
||||
// check persistant storage
|
||||
let storage = Storage::new(data_dir);
|
||||
// option to reset previous stored data to `default`
|
||||
let stg = if args.reset {
|
||||
AppStorage::default()
|
||||
} else {
|
||||
storage.load().unwrap_or_default()
|
||||
};
|
||||
|
||||
// merge `Args` and `AppStorage`.
|
||||
let app_args = (args, stg).into();
|
||||
let app_storage = App::new(app_args).run(terminal, events).await?.to_storage();
|
||||
// store app state persistantly
|
||||
storage.save(app_storage)?;
|
||||
|
||||
terminal::teardown()?;
|
||||
|
||||
Ok(())
|
||||
|
||||
82
src/storage.rs
Normal file
82
src/storage.rs
Normal file
@ -0,0 +1,82 @@
|
||||
use crate::{
|
||||
app::Content,
|
||||
constants::APP_NAME,
|
||||
widgets::{clock::Style, pomodoro::Mode as PomodoroMode},
|
||||
};
|
||||
use color_eyre::eyre::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct AppStorage {
|
||||
pub content: Content,
|
||||
pub show_menu: bool,
|
||||
pub style: Style,
|
||||
pub with_decis: bool,
|
||||
pub pomodoro_mode: PomodoroMode,
|
||||
// pomodoro -> work
|
||||
pub inital_value_work: Duration,
|
||||
pub current_value_work: Duration,
|
||||
// pomodoro -> pause
|
||||
pub inital_value_pause: Duration,
|
||||
pub current_value_pause: Duration,
|
||||
// countdown
|
||||
pub inital_value_countdown: Duration,
|
||||
pub current_value_countdown: Duration,
|
||||
// timer
|
||||
pub current_value_timer: Duration,
|
||||
}
|
||||
|
||||
impl Default for AppStorage {
|
||||
fn default() -> Self {
|
||||
const DEFAULT_WORK: Duration = Duration::from_secs(60 * 25); /* 25min */
|
||||
const DEFAULT_PAUSE: Duration = Duration::from_secs(60 * 5); /* 5min */
|
||||
const DEFAULT_COUNTDOWN: Duration = Duration::from_secs(60 * 10); /* 10min */
|
||||
AppStorage {
|
||||
content: Content::default(),
|
||||
show_menu: false,
|
||||
style: Style::default(),
|
||||
with_decis: false,
|
||||
pomodoro_mode: PomodoroMode::Work,
|
||||
// pomodoro -> work
|
||||
inital_value_work: DEFAULT_WORK,
|
||||
current_value_work: DEFAULT_WORK,
|
||||
// pomodoro -> pause
|
||||
inital_value_pause: DEFAULT_PAUSE,
|
||||
current_value_pause: DEFAULT_PAUSE,
|
||||
// countdown
|
||||
inital_value_countdown: DEFAULT_COUNTDOWN,
|
||||
current_value_countdown: DEFAULT_COUNTDOWN,
|
||||
// timer
|
||||
current_value_timer: Duration::ZERO,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Storage {
|
||||
data_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl Storage {
|
||||
pub fn new(data_dir: PathBuf) -> Self {
|
||||
Self { data_dir }
|
||||
}
|
||||
|
||||
fn get_storage_path(&self) -> PathBuf {
|
||||
self.data_dir.join(format!("{}.data", APP_NAME))
|
||||
}
|
||||
|
||||
pub fn save(&self, data: AppStorage) -> Result<()> {
|
||||
let file = fs::File::create(self.get_storage_path())?;
|
||||
serde_json::to_writer(file, &data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load(&self) -> Result<AppStorage> {
|
||||
let file = fs::File::open(self.get_storage_path())?;
|
||||
let data = serde_json::from_reader(file)?;
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,5 @@
|
||||
use clap::ValueEnum;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::time::Duration;
|
||||
@ -9,7 +11,7 @@ use ratatui::{
|
||||
widgets::StatefulWidget,
|
||||
};
|
||||
|
||||
use crate::{args::ClockStyle, utils::center_horizontal};
|
||||
use crate::utils::center_horizontal;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Display, PartialEq, Eq)]
|
||||
pub enum Time {
|
||||
@ -74,22 +76,47 @@ pub enum Format {
|
||||
HhMmSs,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, ValueEnum, Default, Serialize, Deserialize)]
|
||||
pub enum Style {
|
||||
#[default]
|
||||
#[value(name = "bold", alias = "b")]
|
||||
Bold,
|
||||
#[value(name = "empty", alias = "e")]
|
||||
Empty,
|
||||
#[value(name = "thick", alias = "t")]
|
||||
Thick,
|
||||
#[value(name = "cross", alias = "c")]
|
||||
Cross,
|
||||
}
|
||||
|
||||
impl Style {
|
||||
pub fn next(&self) -> Self {
|
||||
match self {
|
||||
Style::Bold => Style::Empty,
|
||||
Style::Empty => Style::Thick,
|
||||
Style::Thick => Style::Cross,
|
||||
Style::Cross => Style::Bold,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Clock<T> {
|
||||
initial_value: Duration,
|
||||
pub initial_value: Duration,
|
||||
pub current_value: Duration,
|
||||
tick_value: Duration,
|
||||
current_value: Duration,
|
||||
mode: Mode,
|
||||
format: Format,
|
||||
pub style: ClockStyle,
|
||||
pub style: Style,
|
||||
pub with_decis: bool,
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
pub struct ClockArgs {
|
||||
pub initial_value: Duration,
|
||||
pub current_value: Duration,
|
||||
pub tick_value: Duration,
|
||||
pub style: ClockStyle,
|
||||
pub style: Style,
|
||||
pub with_decis: bool,
|
||||
}
|
||||
|
||||
@ -315,15 +342,22 @@ impl Clock<Countdown> {
|
||||
pub fn new(args: ClockArgs) -> Self {
|
||||
let ClockArgs {
|
||||
initial_value,
|
||||
current_value,
|
||||
tick_value,
|
||||
style,
|
||||
with_decis,
|
||||
} = args;
|
||||
let mut instance = Self {
|
||||
initial_value,
|
||||
current_value,
|
||||
tick_value,
|
||||
current_value: initial_value,
|
||||
mode: Mode::Initial,
|
||||
mode: if current_value == Duration::ZERO {
|
||||
Mode::Done
|
||||
} else if current_value == initial_value {
|
||||
Mode::Initial
|
||||
} else {
|
||||
Mode::Pause
|
||||
},
|
||||
format: Format::S,
|
||||
style,
|
||||
with_decis,
|
||||
@ -380,15 +414,22 @@ impl Clock<Timer> {
|
||||
pub fn new(args: ClockArgs) -> Self {
|
||||
let ClockArgs {
|
||||
initial_value,
|
||||
current_value,
|
||||
tick_value,
|
||||
style,
|
||||
with_decis,
|
||||
} = args;
|
||||
let mut instance = Self {
|
||||
initial_value,
|
||||
current_value,
|
||||
tick_value,
|
||||
current_value: Duration::ZERO,
|
||||
mode: Mode::Initial,
|
||||
mode: if current_value == initial_value {
|
||||
Mode::Initial
|
||||
} else if current_value >= MAX_DURATION {
|
||||
Mode::Done
|
||||
} else {
|
||||
Mode::Pause
|
||||
},
|
||||
format: Format::S,
|
||||
phantom: PhantomData,
|
||||
style,
|
||||
@ -552,12 +593,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn get_digit_symbol(&self, style: &ClockStyle) -> &str {
|
||||
fn get_digit_symbol(&self, style: &Style) -> &str {
|
||||
match &style {
|
||||
ClockStyle::Bold => "█",
|
||||
ClockStyle::Empty => "░",
|
||||
ClockStyle::Cross => "╬",
|
||||
ClockStyle::Thick => "┃",
|
||||
Style::Bold => "█",
|
||||
Style::Empty => "░",
|
||||
Style::Cross => "╬",
|
||||
Style::Thick => "┃",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -8,10 +8,9 @@ use ratatui::{
|
||||
use std::cmp::max;
|
||||
|
||||
use crate::{
|
||||
args::ClockStyle,
|
||||
events::{Event, EventHandler},
|
||||
utils::center,
|
||||
widgets::clock::{self, Clock, ClockWidget},
|
||||
widgets::clock::{self, Clock, ClockWidget, Style},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -24,13 +23,17 @@ impl Countdown {
|
||||
Self { clock }
|
||||
}
|
||||
|
||||
pub fn set_style(&mut self, style: ClockStyle) {
|
||||
pub fn set_style(&mut self, style: Style) {
|
||||
self.clock.style = style;
|
||||
}
|
||||
|
||||
pub fn set_with_decis(&mut self, with_decis: bool) {
|
||||
self.clock.with_decis = with_decis;
|
||||
}
|
||||
|
||||
pub fn get_clock(&self) -> &Clock<clock::Countdown> {
|
||||
&self.clock
|
||||
}
|
||||
}
|
||||
|
||||
impl EventHandler for Countdown {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::args::Content;
|
||||
use crate::app::Content;
|
||||
use ratatui::{
|
||||
buffer::Buffer,
|
||||
layout::{Constraint, Layout, Rect},
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
use crate::{
|
||||
args::ClockStyle,
|
||||
constants::TICK_VALUE_MS,
|
||||
events::{Event, EventHandler},
|
||||
utils::center,
|
||||
widgets::clock::{Clock, ClockWidget, Countdown},
|
||||
widgets::clock::{Clock, ClockWidget, Countdown, Style},
|
||||
};
|
||||
use ratatui::{
|
||||
buffer::Buffer,
|
||||
@ -16,10 +15,12 @@ use std::{cmp::max, time::Duration};
|
||||
|
||||
use strum::Display;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::clock::ClockArgs;
|
||||
|
||||
#[derive(Debug, Clone, Display, Hash, Eq, PartialEq)]
|
||||
enum Mode {
|
||||
#[derive(Debug, Clone, Display, Hash, Eq, PartialEq, Deserialize, Serialize)]
|
||||
pub enum Mode {
|
||||
Work,
|
||||
Pause,
|
||||
}
|
||||
@ -46,31 +47,39 @@ pub struct Pomodoro {
|
||||
}
|
||||
|
||||
pub struct PomodoroArgs {
|
||||
pub work: Duration,
|
||||
pub pause: Duration,
|
||||
pub style: ClockStyle,
|
||||
pub mode: Mode,
|
||||
pub initial_value_work: Duration,
|
||||
pub current_value_work: Duration,
|
||||
pub initial_value_pause: Duration,
|
||||
pub current_value_pause: Duration,
|
||||
pub style: Style,
|
||||
pub with_decis: bool,
|
||||
}
|
||||
|
||||
impl Pomodoro {
|
||||
pub fn new(args: PomodoroArgs) -> Self {
|
||||
let PomodoroArgs {
|
||||
work,
|
||||
pause,
|
||||
mode,
|
||||
initial_value_work,
|
||||
current_value_work,
|
||||
initial_value_pause,
|
||||
current_value_pause,
|
||||
style,
|
||||
with_decis,
|
||||
} = args;
|
||||
Self {
|
||||
mode: Mode::Work,
|
||||
mode,
|
||||
clock_map: ClockMap {
|
||||
work: Clock::<Countdown>::new(ClockArgs {
|
||||
initial_value: work,
|
||||
initial_value: initial_value_work,
|
||||
current_value: current_value_work,
|
||||
tick_value: Duration::from_millis(TICK_VALUE_MS),
|
||||
style,
|
||||
with_decis,
|
||||
}),
|
||||
pause: Clock::<Countdown>::new(ClockArgs {
|
||||
initial_value: pause,
|
||||
initial_value: initial_value_pause,
|
||||
current_value: current_value_pause,
|
||||
tick_value: Duration::from_millis(TICK_VALUE_MS),
|
||||
style,
|
||||
with_decis,
|
||||
@ -79,11 +88,23 @@ impl Pomodoro {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_clock(&mut self) -> &mut Clock<Countdown> {
|
||||
pub fn get_clock(&mut self) -> &mut Clock<Countdown> {
|
||||
self.clock_map.get(&self.mode)
|
||||
}
|
||||
|
||||
pub fn set_style(&mut self, style: crate::args::ClockStyle) {
|
||||
pub fn get_clock_work(&self) -> &Clock<Countdown> {
|
||||
&self.clock_map.work
|
||||
}
|
||||
|
||||
pub fn get_clock_pause(&self) -> &Clock<Countdown> {
|
||||
&self.clock_map.pause
|
||||
}
|
||||
|
||||
pub fn get_mode(&self) -> &Mode {
|
||||
&self.mode
|
||||
}
|
||||
|
||||
pub fn set_style(&mut self, style: Style) {
|
||||
self.clock_map.work.style = style;
|
||||
self.clock_map.pause.style = style;
|
||||
}
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
use crate::{
|
||||
args::ClockStyle,
|
||||
events::{Event, EventHandler},
|
||||
utils::center,
|
||||
widgets::clock::{self, Clock, ClockWidget},
|
||||
widgets::clock::{self, Clock, ClockWidget, Style},
|
||||
};
|
||||
use ratatui::{
|
||||
buffer::Buffer,
|
||||
@ -23,13 +22,17 @@ impl Timer {
|
||||
Self { clock }
|
||||
}
|
||||
|
||||
pub fn set_style(&mut self, style: ClockStyle) {
|
||||
pub fn set_style(&mut self, style: Style) {
|
||||
self.clock.style = style;
|
||||
}
|
||||
|
||||
pub fn set_with_decis(&mut self, with_decis: bool) {
|
||||
self.clock.with_decis = with_decis;
|
||||
}
|
||||
|
||||
pub fn get_clock(&self) -> &Clock<clock::Timer> {
|
||||
&self.clock
|
||||
}
|
||||
}
|
||||
|
||||
impl EventHandler for Timer {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user