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]
### 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
- (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)
- (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>4</kbd> | Event |
| <kbd>0</kbd> | Local Time |
| <kbd></kbd> | next screen |
| <kbd></kbd> | previous screen |
## Controls
@ -152,7 +154,7 @@ Extra option (if `--features sound` is enabled by local build only):
| 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+s</kbd> | save initial value |

View File

@ -242,11 +242,19 @@ impl App {
debug!("Received key {:?}", key.code);
match key.code {
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('2') | KeyCode::Char('t') /* TODO: deprecated, remove it in next verson */ => app.content = Content::Timer,
KeyCode::Char('3') | KeyCode::Char('p') /* TODO: deprecated, remove it in next verson */ => app.content = Content::Pomodoro,
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 version */ => app.content = Content::Timer,
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('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
KeyCode::Char(':') => {
if app.content == Content::LocalTime {

View File

@ -21,6 +21,28 @@ pub enum Content {
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)]
pub enum ClockTypeId {
Countdown,
@ -254,4 +276,48 @@ mod tests {
"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);
// show menu
if state.show_menu {
let content_labels: Vec<Span> = content_labels
let mut content_labels: Vec<Span> = content_labels
.iter()
.enumerate()
.map(|(index, (content, label))| {
@ -103,6 +103,13 @@ impl StatefulWidget for Footer {
})
.collect();
content_labels.extend_from_slice(&[
Span::from(SPACE),
Span::from("[→]next"),
Span::from(SPACE),
Span::from("[←]prev."),
]);
const SPACE: &str = " "; // 2 empty spaces
let widths = [Constraint::Length(12), Constraint::Percentage(100)];
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(&[
// controls - 1. row
Row::new(vec![
@ -202,7 +212,7 @@ impl StatefulWidget for Footer {
let mut spans = vec![];
if self.selected_content == Content::Pomodoro {
spans.extend_from_slice(&[Span::from(
"[← →]switch work/pause",
"[^] or [^→] switch work/pause",
)]);
}
spans

View File

@ -198,12 +198,12 @@ impl TuiEventHandler for PomodoroState {
self.get_clock_mut().toggle_edit();
}
// 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
self.next();
}
// toggle WORK/PAUSE
KeyCode::Right => {
KeyCode::Right if key.modifiers.contains(KeyModifiers::CONTROL) => {
self.next();
}
// reset rounds AND clocks