feat(screens)! switch by using or keys (#127)

* feat(screens) switch by `←` or `→` keys

* test cycling `Content` using `next`/`prev`

* update CL
This commit is contained in:
Jens Krause 2025-10-13 13:28:51 +02:00 committed by GitHub
parent b5f3c709bf
commit 51f83e5b06
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 102 additions and 10 deletions

View File

@ -2,8 +2,14 @@
## [unreleased] ## [unreleased]
### Features
- (event) New `event` screen to count custom date times in the future or past. [#117](https://github.com/sectore/timr-tui/pull/117), [#120](https://github.com/sectore/timr-tui/pull/120), [#122](https://github.com/sectore/timr-tui/pull/122), [#123](https://github.com/sectore/timr-tui/pull/123), [#124](https://github.com/sectore/timr-tui/pull/124), [#125](https://github.com/sectore/timr-tui/pull/125)
- (screens) switch by `←` or `→` keys [#127](https://github.com/sectore/timr-tui/pull/127)
### Breaking change ### Breaking change
- (pomodoro)! new keybindings `ctrl+←` or `ctrl+→` to switch `work`/`pause` [#127](https://github.com/sectore/timr-tui/pull/127)
- (keybindings)! change keys for `screens` [#126](https://github.com/sectore/timr-tui/pull/126) - (keybindings)! change keys for `screens` [#126](https://github.com/sectore/timr-tui/pull/126)
- (cli)! Remove `--countdown-target` argument [#121](https://github.com/sectore/timr-tui/pull/121) - (cli)! Remove `--countdown-target` argument [#121](https://github.com/sectore/timr-tui/pull/121)

View File

@ -126,6 +126,8 @@ Extra option (if `--features sound` is enabled by local build only):
| <kbd>3</kbd> | Timer | | <kbd>3</kbd> | Timer |
| <kbd>4</kbd> | Event | | <kbd>4</kbd> | Event |
| <kbd>0</kbd> | Local Time | | <kbd>0</kbd> | Local Time |
| <kbd></kbd> | next screen |
| <kbd></kbd> | previous screen |
## Controls ## Controls
@ -152,7 +154,7 @@ Extra option (if `--features sound` is enabled by local build only):
| Key | Description | | Key | Description |
| --- | --- | | --- | --- |
| <kbd></kbd> or <kbd></kbd> | switch work/pause | | <kbd>ctrl+</kbd> or <kbd>ctrl+</kbd> | switch work/pause |
| <kbd>ctrl+r</kbd> | reset round | | <kbd>ctrl+r</kbd> | reset round |
| <kbd>ctrl+s</kbd> | save initial value | | <kbd>ctrl+s</kbd> | save initial value |

View File

@ -242,11 +242,19 @@ impl App {
debug!("Received key {:?}", key.code); debug!("Received key {:?}", key.code);
match key.code { match key.code {
KeyCode::Char('q') => app.mode = Mode::Quit, KeyCode::Char('q') => app.mode = Mode::Quit,
KeyCode::Char('1') | KeyCode::Char('c') /* TODO: deprecated, remove it in next verson */ => app.content = Content::Countdown, KeyCode::Char('1') | KeyCode::Char('c') /* TODO: deprecated, remove it in next version */ => app.content = Content::Countdown,
KeyCode::Char('2') | KeyCode::Char('t') /* TODO: deprecated, remove it in next verson */ => app.content = Content::Timer, KeyCode::Char('2') | KeyCode::Char('t') /* TODO: deprecated, remove it in next version */ => app.content = Content::Timer,
KeyCode::Char('3') | KeyCode::Char('p') /* TODO: deprecated, remove it in next verson */ => app.content = Content::Pomodoro, KeyCode::Char('3') | KeyCode::Char('p') /* TODO: deprecated, remove it in next version */ => app.content = Content::Pomodoro,
KeyCode::Char('4') => app.content = Content::Event, KeyCode::Char('4') => app.content = Content::Event,
KeyCode::Char('0') | KeyCode::Char('l') /* TODO: deprecated, remove it in next verson */ => app.content = Content::LocalTime, // toogle app time format
KeyCode::Char('0') | KeyCode::Char('l') /* TODO: deprecated, remove it in next version */ => app.content = Content::LocalTime,
// switch `screens`
KeyCode::Right => {
app.content = app.content.next();
}
KeyCode::Left => {
app.content = app.content.prev();
}
// toogle app time format // toogle app time format
KeyCode::Char(':') => { KeyCode::Char(':') => {
if app.content == Content::LocalTime { if app.content == Content::LocalTime {

View File

@ -21,6 +21,28 @@ pub enum Content {
LocalTime, LocalTime,
} }
impl Content {
pub fn next(&self) -> Self {
match self {
Content::Countdown => Content::Timer,
Content::Timer => Content::Pomodoro,
Content::Pomodoro => Content::Event,
Content::Event => Content::LocalTime,
Content::LocalTime => Content::Countdown,
}
}
pub fn prev(&self) -> Self {
match self {
Content::Countdown => Content::LocalTime,
Content::Timer => Content::Countdown,
Content::Pomodoro => Content::Timer,
Content::Event => Content::Pomodoro,
Content::LocalTime => Content::Event,
}
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum ClockTypeId { pub enum ClockTypeId {
Countdown, Countdown,
@ -254,4 +276,48 @@ mod tests {
"local" "local"
); );
} }
#[test]
fn test_content_next() {
let start = Content::Countdown;
let mut current = start;
// Cycle through: Countdown -> Timer -> Pomodoro -> Event -> LocalTime -> Countdown
current = current.next();
assert_eq!(current, Content::Timer);
current = current.next();
assert_eq!(current, Content::Pomodoro);
current = current.next();
assert_eq!(current, Content::Event);
current = current.next();
assert_eq!(current, Content::LocalTime);
current = current.next();
assert_eq!(current, start, "Should cycle back to start");
}
#[test]
fn test_content_prev() {
let start = Content::Countdown;
let mut current = start;
// Cycle backwards: Countdown -> LocalTime -> Event -> Pomodoro -> Timer -> Countdown
current = current.prev();
assert_eq!(current, Content::LocalTime);
current = current.prev();
assert_eq!(current, Content::Event);
current = current.prev();
assert_eq!(current, Content::Pomodoro);
current = current.prev();
assert_eq!(current, Content::Timer);
current = current.prev();
assert_eq!(current, start, "Should cycle back to start");
}
} }

View File

@ -85,7 +85,7 @@ impl StatefulWidget for Footer {
.render(border_area, buf); .render(border_area, buf);
// show menu // show menu
if state.show_menu { if state.show_menu {
let content_labels: Vec<Span> = content_labels let mut content_labels: Vec<Span> = content_labels
.iter() .iter()
.enumerate() .enumerate()
.map(|(index, (content, label))| { .map(|(index, (content, label))| {
@ -103,6 +103,13 @@ impl StatefulWidget for Footer {
}) })
.collect(); .collect();
content_labels.extend_from_slice(&[
Span::from(SPACE),
Span::from("[→]next"),
Span::from(SPACE),
Span::from("[←]prev."),
]);
const SPACE: &str = " "; // 2 empty spaces const SPACE: &str = " "; // 2 empty spaces
let widths = [Constraint::Length(12), Constraint::Percentage(100)]; let widths = [Constraint::Length(12), Constraint::Percentage(100)];
let mut table_rows = vec![ let mut table_rows = vec![
@ -136,7 +143,10 @@ impl StatefulWidget for Footer {
]), ]),
]; ];
if self.selected_content != Content::LocalTime { // Controls (except for `localtime` and `event`)
if self.selected_content != Content::LocalTime
&& self.selected_content != Content::Event
{
table_rows.extend_from_slice(&[ table_rows.extend_from_slice(&[
// controls - 1. row // controls - 1. row
Row::new(vec![ Row::new(vec![
@ -202,7 +212,7 @@ impl StatefulWidget for Footer {
let mut spans = vec![]; let mut spans = vec![];
if self.selected_content == Content::Pomodoro { if self.selected_content == Content::Pomodoro {
spans.extend_from_slice(&[Span::from( spans.extend_from_slice(&[Span::from(
"[← →]switch work/pause", "[^] or [^→] switch work/pause",
)]); )]);
} }
spans spans

View File

@ -198,12 +198,12 @@ impl TuiEventHandler for PomodoroState {
self.get_clock_mut().toggle_edit(); self.get_clock_mut().toggle_edit();
} }
// toggle WORK/PAUSE // toggle WORK/PAUSE
KeyCode::Left => { KeyCode::Left if key.modifiers.contains(KeyModifiers::CONTROL) => {
// `next` is acting as same as a "prev" function we don't have // `next` is acting as same as a "prev" function we don't have
self.next(); self.next();
} }
// toggle WORK/PAUSE // toggle WORK/PAUSE
KeyCode::Right => { KeyCode::Right if key.modifiers.contains(KeyModifiers::CONTROL) => {
self.next(); self.next();
} }
// reset rounds AND clocks // reset rounds AND clocks