Cleanup (#23)
* simplify footer * improve: &T vs. &mut T * remove header * adjust table space * update README
This commit is contained in:
parent
98ee2bc16b
commit
d86f8905f2
24
README.md
24
README.md
@ -2,9 +2,6 @@
|
||||
|
||||
**Pronounced `/ˈtʌɪmə/` or `/ˈtaɪmər/`.**
|
||||
|
||||
> [!WARNING]
|
||||
> _Still WIP_
|
||||
|
||||
# About
|
||||
|
||||
`tim:r` is a TUI app to help you to organize one of the most important thing you have in live: `time`!
|
||||
@ -14,7 +11,7 @@
|
||||
- `[p]omodoro` Organize your working time to be focused all the time by following the [Pomodoro Technique](https://en.wikipedia.org/wiki/Pomodoro_Technique).
|
||||
|
||||
|
||||
It's built with [`ratatui`](https://ratatui.rs/) ([Rust](https://www.rust-lang.org/))
|
||||
It's built with [`ratatui`](https://ratatui.rs/) ([Rust](https://www.rust-lang.org/)).
|
||||
|
||||
|
||||
# Screens
|
||||
@ -33,6 +30,7 @@ Options:
|
||||
-d, --decis Wether to show deciseconds or not
|
||||
-m, --mode <CONTENT> Mode to start with: [t]imer, [c]ountdown, [p]omodoro [default: timer] [possible values: countdown, timer, pomodoro]
|
||||
-s, --style <STYLE> Style to display time with: [b]old, [t]hick, [c]ross, [e]mpty [default: bold] [possible values: bold, empty, thick, cross]
|
||||
-r, --reset Reset stored values to default
|
||||
-h, --help Print help
|
||||
```
|
||||
|
||||
@ -40,21 +38,21 @@ Options:
|
||||
|
||||
## Requirements
|
||||
|
||||
### Nix (recommend)
|
||||
### Nix users (recommend)
|
||||
|
||||
`cd` into root directory.
|
||||
|
||||
[`direnv`](https://direnv.net) users run `direnv allow` once to install dependencies. Others run `nix develop`.
|
||||
If you have [`direnv`](https://direnv.net) installed, run `direnv allow` once to install dependencies. In other case run `nix develop`.
|
||||
|
||||
|
||||
### Non Nix user
|
||||
### Non Nix users
|
||||
|
||||
- [`Rust`](https://www.rust-lang.org/learn/get-started)
|
||||
- [`Clippy`](https://github.com/rust-lang/rust-clippy)
|
||||
- [`rustfmt`](https://github.com/rust-lang/rustfmt)
|
||||
- [`just`](https://just.systems)
|
||||
|
||||
### Commands to `run`, `lint`, `format` etc.
|
||||
### Commands
|
||||
|
||||
```sh
|
||||
just --list
|
||||
@ -87,10 +85,20 @@ nix build .#windows
|
||||
|
||||
# Misc.
|
||||
|
||||
## Persistant app state
|
||||
|
||||
Stored on file system.
|
||||
|
||||
- `Linux`
|
||||
```sh
|
||||
cat ~/.local/state/timr/data/timr.data
|
||||
```
|
||||
|
||||
## Logs
|
||||
|
||||
In `debug` mode only.
|
||||
|
||||
- `Linux`
|
||||
```sh
|
||||
tail -f ~/.local/state/timr/logs/timr.log
|
||||
```
|
||||
|
||||
28
src/app.rs
28
src/app.rs
@ -7,8 +7,7 @@ use crate::{
|
||||
widgets::{
|
||||
clock::{self, Clock, ClockArgs, Style},
|
||||
countdown::{Countdown, CountdownWidget},
|
||||
footer::{Footer, FooterArgs},
|
||||
header::Header,
|
||||
footer::Footer,
|
||||
pomodoro::{Mode as PomodoroMode, Pomodoro, PomodoroArgs, PomodoroWidget},
|
||||
timer::{Timer, TimerWidget},
|
||||
},
|
||||
@ -166,10 +165,10 @@ impl App {
|
||||
self.mode != Mode::Quit
|
||||
}
|
||||
|
||||
fn is_edit_mode(&mut self) -> bool {
|
||||
fn is_edit_mode(&self) -> bool {
|
||||
match self.content {
|
||||
Content::Countdown => self.countdown.get_clock().clone().is_edit_mode(),
|
||||
Content::Timer => self.timer.get_clock().clone().is_edit_mode(),
|
||||
Content::Countdown => self.countdown.get_clock().is_edit_mode(),
|
||||
Content::Timer => self.timer.get_clock().is_edit_mode(),
|
||||
Content::Pomodoro => self.pomodoro.get_clock().is_edit_mode(),
|
||||
}
|
||||
}
|
||||
@ -250,24 +249,21 @@ impl AppWidget {
|
||||
impl StatefulWidget for AppWidget {
|
||||
type State = App;
|
||||
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||
let vertical = Layout::vertical([
|
||||
Constraint::Length(1),
|
||||
let [v0, v1] = Layout::vertical([
|
||||
Constraint::Percentage(100),
|
||||
Constraint::Length(if state.show_menu { 5 } else { 1 }),
|
||||
]);
|
||||
let [v0, v1, v2] = vertical.areas(area);
|
||||
Constraint::Length(if state.show_menu { 4 } else { 1 }),
|
||||
])
|
||||
.areas(area);
|
||||
|
||||
// header
|
||||
Header::new(true).render(v0, buf);
|
||||
// content
|
||||
self.render_content(v1, buf, state);
|
||||
self.render_content(v0, buf, state);
|
||||
// footer
|
||||
Footer::new(FooterArgs {
|
||||
Footer {
|
||||
show_menu: state.show_menu,
|
||||
running_clock: state.clock_is_running(),
|
||||
selected_content: state.content,
|
||||
edit_mode: state.is_edit_mode(),
|
||||
})
|
||||
.render(v2, buf);
|
||||
}
|
||||
.render(v1, buf);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
pub mod clock;
|
||||
pub mod countdown;
|
||||
pub mod footer;
|
||||
pub mod header;
|
||||
pub mod pomodoro;
|
||||
pub mod timer;
|
||||
|
||||
@ -207,7 +207,7 @@ impl<T> Clock<T> {
|
||||
self.update_mode();
|
||||
}
|
||||
|
||||
pub fn get_mode(&mut self) -> &Mode {
|
||||
pub fn get_mode(&self) -> &Mode {
|
||||
&self.mode
|
||||
}
|
||||
|
||||
@ -215,7 +215,7 @@ impl<T> Clock<T> {
|
||||
self.mode == Mode::Tick
|
||||
}
|
||||
|
||||
pub fn is_edit_mode(&mut self) -> bool {
|
||||
pub fn is_edit_mode(&self) -> bool {
|
||||
matches!(self.mode, Mode::Editable(_, _))
|
||||
}
|
||||
|
||||
@ -301,7 +301,7 @@ impl<T> Clock<T> {
|
||||
(self.current_value.subsec_millis() / 100) as u64
|
||||
}
|
||||
|
||||
pub fn is_done(&mut self) -> bool {
|
||||
pub fn is_done(&self) -> bool {
|
||||
self.mode == Mode::Done
|
||||
}
|
||||
|
||||
@ -375,12 +375,12 @@ impl Clock<Countdown> {
|
||||
pub fn tick(&mut self) {
|
||||
if self.mode == Mode::Tick {
|
||||
self.current_value = self.current_value.saturating_sub(self.tick_value);
|
||||
self.check_done();
|
||||
self.set_done();
|
||||
self.update_format();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_done(&mut self) {
|
||||
pub fn set_done(&mut self) {
|
||||
if self.current_value.is_zero() {
|
||||
self.mode = Mode::Done;
|
||||
}
|
||||
@ -447,12 +447,12 @@ impl Clock<Timer> {
|
||||
pub fn tick(&mut self) {
|
||||
if self.mode == Mode::Tick {
|
||||
self.current_value = self.current_value.saturating_add(self.tick_value);
|
||||
self.check_done();
|
||||
self.set_done();
|
||||
self.update_format();
|
||||
}
|
||||
}
|
||||
|
||||
fn check_done(&mut self) {
|
||||
fn set_done(&mut self) {
|
||||
if self.current_value >= MAX_DURATION {
|
||||
self.mode = Mode::Done;
|
||||
}
|
||||
|
||||
@ -12,46 +12,25 @@ use ratatui::{
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Footer {
|
||||
show_menu: bool,
|
||||
running_clock: bool,
|
||||
selected_content: Content,
|
||||
content_labels: BTreeMap<Content, String>,
|
||||
edit_mode: bool,
|
||||
}
|
||||
|
||||
pub struct FooterArgs {
|
||||
pub show_menu: bool,
|
||||
pub running_clock: bool,
|
||||
pub selected_content: Content,
|
||||
pub edit_mode: bool,
|
||||
}
|
||||
|
||||
impl Footer {
|
||||
pub fn new(args: FooterArgs) -> Self {
|
||||
let FooterArgs {
|
||||
show_menu,
|
||||
running_clock,
|
||||
selected_content,
|
||||
edit_mode,
|
||||
} = args;
|
||||
Self {
|
||||
show_menu,
|
||||
running_clock,
|
||||
selected_content,
|
||||
edit_mode,
|
||||
content_labels: BTreeMap::from([
|
||||
(Content::Countdown, "[c]ountdown".into()),
|
||||
(Content::Timer, "[t]imer".into()),
|
||||
(Content::Pomodoro, "[p]omodoro".into()),
|
||||
]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Footer {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let content_labels: BTreeMap<Content, &str> = BTreeMap::from([
|
||||
(Content::Countdown, "[c]ountdown"),
|
||||
(Content::Timer, "[t]imer"),
|
||||
(Content::Pomodoro, "[p]omodoro"),
|
||||
]);
|
||||
|
||||
let [_, area] =
|
||||
Layout::horizontal([Constraint::Length(1), Constraint::Percentage(100)]).areas(area);
|
||||
|
||||
let [border_area, menu_area] =
|
||||
Layout::vertical([Constraint::Length(2), Constraint::Percentage(100)]).areas(area);
|
||||
Layout::vertical([Constraint::Length(1), Constraint::Percentage(100)]).areas(area);
|
||||
Block::new()
|
||||
.borders(Borders::TOP)
|
||||
.title(format! {"[m]enu {:} ", if self.show_menu {"↓"} else {"↑"}})
|
||||
@ -59,17 +38,16 @@ impl Widget for Footer {
|
||||
.render(border_area, buf);
|
||||
// show menu
|
||||
if self.show_menu {
|
||||
let content_labels: Vec<Span> = self
|
||||
.content_labels
|
||||
let content_labels: Vec<Span> = content_labels
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, (content, label))| {
|
||||
let mut style = Style::default();
|
||||
// Add space for all except last
|
||||
let label = if index < self.content_labels.len() - 1 {
|
||||
let label = if index < content_labels.len() - 1 {
|
||||
format!("{} ", label)
|
||||
} else {
|
||||
label.into()
|
||||
label.to_string()
|
||||
};
|
||||
if *content == self.selected_content {
|
||||
style = style.add_modifier(Modifier::BOLD);
|
||||
@ -144,6 +122,7 @@ impl Widget for Footer {
|
||||
],
|
||||
widths,
|
||||
)
|
||||
.column_spacing(1)
|
||||
.render(menu_area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
use ratatui::{
|
||||
buffer::Buffer,
|
||||
layout::{Constraint, Layout, Rect},
|
||||
text::Span,
|
||||
widgets::Widget,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Header {
|
||||
show_fps: bool,
|
||||
}
|
||||
|
||||
impl Header {
|
||||
pub fn new(show_fps: bool) -> Self {
|
||||
Self { show_fps }
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Header {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let fps_txt = if self.show_fps { "FPS (soon)" } else { "" };
|
||||
let fps_span = Span::raw(fps_txt);
|
||||
let fps_width = fps_span.width().try_into().unwrap_or(0);
|
||||
let [h1, h2] =
|
||||
Layout::horizontal([Constraint::Fill(1), Constraint::Length(fps_width)]).areas(area);
|
||||
|
||||
Span::raw("tim:r").render(h1, buf);
|
||||
fps_span.render(h2, buf);
|
||||
}
|
||||
}
|
||||
@ -32,12 +32,18 @@ pub struct ClockMap {
|
||||
}
|
||||
|
||||
impl ClockMap {
|
||||
fn get(&mut self, mode: &Mode) -> &mut Clock<Countdown> {
|
||||
fn get_mut(&mut self, mode: &Mode) -> &mut Clock<Countdown> {
|
||||
match mode {
|
||||
Mode::Work => &mut self.work,
|
||||
Mode::Pause => &mut self.pause,
|
||||
}
|
||||
}
|
||||
fn get(&self, mode: &Mode) -> &Clock<Countdown> {
|
||||
match mode {
|
||||
Mode::Work => &self.work,
|
||||
Mode::Pause => &self.pause,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -88,7 +94,11 @@ impl Pomodoro {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_clock(&mut self) -> &mut Clock<Countdown> {
|
||||
fn get_clock_mut(&mut self) -> &mut Clock<Countdown> {
|
||||
self.clock_map.get_mut(&self.mode)
|
||||
}
|
||||
|
||||
pub fn get_clock(&self) -> &Clock<Countdown> {
|
||||
self.clock_map.get(&self.mode)
|
||||
}
|
||||
|
||||
@ -127,36 +137,36 @@ impl EventHandler for Pomodoro {
|
||||
let edit_mode = self.get_clock().is_edit_mode();
|
||||
match event {
|
||||
Event::Tick => {
|
||||
self.get_clock().tick();
|
||||
self.get_clock_mut().tick();
|
||||
}
|
||||
Event::Key(key) => match key.code {
|
||||
KeyCode::Char('s') => {
|
||||
self.get_clock().toggle_pause();
|
||||
self.get_clock_mut().toggle_pause();
|
||||
}
|
||||
KeyCode::Char('e') => {
|
||||
self.get_clock().toggle_edit();
|
||||
self.get_clock_mut().toggle_edit();
|
||||
}
|
||||
KeyCode::Left if edit_mode => {
|
||||
self.get_clock().edit_next();
|
||||
self.get_clock_mut().edit_next();
|
||||
}
|
||||
KeyCode::Left => {
|
||||
// `next` is acting as same as a `prev` function, we don't have
|
||||
self.next();
|
||||
}
|
||||
KeyCode::Right if edit_mode => {
|
||||
self.get_clock().edit_prev();
|
||||
self.get_clock_mut().edit_prev();
|
||||
}
|
||||
KeyCode::Right => {
|
||||
self.next();
|
||||
}
|
||||
KeyCode::Up if edit_mode => {
|
||||
self.get_clock().edit_up();
|
||||
self.get_clock_mut().edit_up();
|
||||
}
|
||||
KeyCode::Down if edit_mode => {
|
||||
self.get_clock().edit_down();
|
||||
self.get_clock_mut().edit_down();
|
||||
}
|
||||
KeyCode::Char('r') => {
|
||||
self.get_clock().reset();
|
||||
self.get_clock_mut().reset();
|
||||
}
|
||||
_ => return Some(event),
|
||||
},
|
||||
@ -176,7 +186,7 @@ impl StatefulWidget for PomodoroWidget {
|
||||
(format!(
|
||||
"Pomodoro {} {}",
|
||||
state.mode.clone(),
|
||||
state.get_clock().get_mode()
|
||||
state.get_clock_mut().get_mode()
|
||||
))
|
||||
.to_uppercase(),
|
||||
);
|
||||
@ -196,7 +206,7 @@ impl StatefulWidget for PomodoroWidget {
|
||||
let [v1, v2] =
|
||||
Layout::vertical(Constraint::from_lengths([clock_widget.get_height(), 1])).areas(area);
|
||||
|
||||
clock_widget.render(v1, buf, state.get_clock());
|
||||
clock_widget.render(v1, buf, state.get_clock_mut());
|
||||
label.centered().render(v2, buf);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user