Update keybindings (#76)

* (pomodoro) reset both clocks at once

* quit app by pressing `q` only

* (countdown) enter/esc keybindings

* (timer) enter/esc keybindings

* (pomodoro) enter/esc keybindings

* update footer label

* fix(coundown): don't reset elapsed clock

while skipping editing changes

* fix(clock): order of actions matters for ESC

handling. Set `pause` instead of `initial` mode while toggeling back.

* fix(timer): order of actions matters (ESC key)

* (footer) update order, lowercase standard keys
This commit is contained in:
Jens Krause 2025-04-30 11:11:33 +02:00 committed by GitHub
parent 90d9988e7a
commit e6291a3131
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 117 additions and 60 deletions

View File

@ -202,7 +202,7 @@ impl App {
let handle_key_event = |app: &mut Self, key: KeyEvent| {
debug!("Received key {:?}", key.code);
match key.code {
KeyCode::Char('q') | KeyCode::Esc => app.mode = Mode::Quit,
KeyCode::Char('q') => app.mode = Mode::Quit,
KeyCode::Char('c') => app.content = Content::Countdown,
KeyCode::Char('t') => app.content = Content::Timer,
KeyCode::Char('p') => app.content = Content::Pomodoro,

View File

@ -74,6 +74,7 @@ pub struct ClockState<T> {
name: Option<String>,
initial_value: DurationEx,
current_value: DurationEx,
prev_value: DurationEx,
tick_value: DurationEx,
mode: Mode,
format: Format,
@ -151,14 +152,18 @@ impl<T> ClockState<T> {
self.update_format();
}
pub fn get_prev_value(&self) -> &DurationEx {
&self.prev_value
}
pub fn toggle_edit(&mut self) {
self.mode = match self.mode.clone() {
Mode::Editable(_, prev) => {
let p = *prev;
// special cases: Should `Mode` be updated?
// 1. `Done` -> `Initial` ?
// Update `Mode`
// 1. `Done` -> `Pause`
if p == Mode::Done && self.current_value.gt(&Duration::ZERO.into()) {
Mode::Initial
Mode::Pause
}
// 2. `_` -> `Done` ?
else if p != Mode::Done && self.current_value.eq(&Duration::ZERO.into()) {
@ -170,6 +175,8 @@ impl<T> ClockState<T> {
}
}
mode => {
// store prev. value
self.prev_value = self.current_value;
if self.format <= Format::Ss {
Mode::Editable(Time::Seconds, Box::new(mode))
} else {
@ -402,6 +409,7 @@ impl ClockState<Countdown> {
name: None,
initial_value: initial_value.into(),
current_value: current_value.into(),
prev_value: current_value.into(),
tick_value: tick_value.into(),
mode: if current_value == Duration::ZERO {
Mode::Done
@ -475,6 +483,7 @@ impl ClockState<Timer> {
name: None,
initial_value: initial_value.into(),
current_value: current_value.into(),
prev_value: current_value.into(),
tick_value: tick_value.into(),
mode: if current_value == initial_value {
Mode::Initial

View File

@ -187,33 +187,65 @@ impl TuiEventHandler for CountdownState {
self.edit_time_done(edit_time);
}
}
// STRG + e => toggle edit time
KeyCode::Char('e') if key.modifiers.contains(KeyModifiers::CONTROL) => {
// reset both clocks
self.clock.reset();
self.elapsed_clock.reset();
if let Some(edit_time) = &mut self.edit_time.clone() {
self.edit_time_done(edit_time)
} else {
// update `edit_time`
self.edit_time = Some(EditTimeState::new(EditTimeStateArgs {
time: self.time_to_edit(),
min: self.min_time_to_edit(),
max: self.max_time_to_edit(),
}));
}
}
// e => toggle edit clock
KeyCode::Char('e') => {
// toggle edit mode
// skip editing clock
KeyCode::Esc if self.is_clock_edit_mode() => {
// Important: set current value first
self.clock.set_current_value(*self.clock.get_prev_value());
// before toggling back to non-edit mode
self.clock.toggle_edit();
}
// skip editing by local time
KeyCode::Esc if self.is_time_edit_mode() => {
self.edit_time = None;
}
// stop `elapsed_clock`
// Enter edit by local time mode
KeyCode::Char('e')
if key.modifiers.contains(KeyModifiers::CONTROL)
&& !self.is_time_edit_mode() =>
{
// set `edit_time`
self.edit_time = Some(EditTimeState::new(EditTimeStateArgs {
time: self.time_to_edit(),
min: self.min_time_to_edit(),
max: self.max_time_to_edit(),
}));
// pause `elapsed_clock`
if self.elapsed_clock.is_running() {
self.elapsed_clock.toggle_pause();
}
}
// Enter edit clock
KeyCode::Char('e') if !self.is_clock_edit_mode() => {
// toggle edit mode
self.clock.toggle_edit();
// pause `elapsed_clock`
if self.elapsed_clock.is_running() {
self.elapsed_clock.toggle_pause();
}
}
// Apply changes of editing by local time
KeyCode::Enter if self.is_time_edit_mode() => {
if let Some(edit_time) = &mut self.edit_time.clone() {
self.edit_time_done(edit_time)
}
// always reset `elapsed_clock`
self.elapsed_clock.reset();
}
// Apply changes of editing clock
// Note: Using Ctrl+e is deprecated, use Enter instead
KeyCode::Enter if self.is_clock_edit_mode() => {
// toggle edit mode
self.clock.toggle_edit();
// always reset `elapsed_clock`
self.elapsed_clock.reset();
}
KeyCode::Left if self.is_clock_edit_mode() => {
self.clock.edit_next();
}
@ -230,8 +262,6 @@ impl TuiEventHandler for CountdownState {
}
KeyCode::Up if self.is_clock_edit_mode() => {
self.clock.edit_up();
// whenever `clock`'s value is changed, reset `elapsed_clock`
self.elapsed_clock.reset();
}
KeyCode::Up if self.is_time_edit_mode() => {
// safe unwrap because of previous check in `is_time_edit_mode`
@ -239,8 +269,6 @@ impl TuiEventHandler for CountdownState {
}
KeyCode::Down if self.is_clock_edit_mode() => {
self.clock.edit_down();
// whenever clock value is changed, reset timer
self.elapsed_clock.reset();
}
KeyCode::Down if self.is_time_edit_mode() => {
// safe unwrap because of previous check in `is_time_edit_mode`

View File

@ -104,7 +104,7 @@ impl StatefulWidget for Footer {
let widths = [Constraint::Length(12), Constraint::Percentage(100)];
let table = Table::new(
[
// content
// screens
Row::new(vec![
Cell::from(Span::styled(
"screens",
@ -112,27 +112,7 @@ impl StatefulWidget for Footer {
)),
Cell::from(Line::from(content_labels)),
]),
// format
Row::new(vec![
Cell::from(Span::styled(
"appearance",
Style::default().add_modifier(Modifier::BOLD),
)),
Cell::from(Line::from(vec![
Span::from("[,]change style"),
Span::from(SPACE),
Span::from("[.]toggle deciseconds"),
Span::from(SPACE),
Span::from(format!(
"[:]toggle {} time",
match self.app_time {
AppTime::Local(_) => "local",
AppTime::Utc(_) => "utc",
}
)),
])),
]),
// edit
// controls
Row::new(vec![
Cell::from(Span::styled(
"controls",
@ -170,12 +150,10 @@ impl StatefulWidget for Footer {
}
spans
}
others => vec![
Span::from(match others {
AppEditMode::Clock => "[e]dit done",
AppEditMode::Time => "[^e]dit done",
_ => "",
}),
_ => vec![
Span::from("[enter]apply changes"),
Span::from(SPACE),
Span::from("[esc]skip changes"),
Span::from(SPACE),
Span::from(format!(
"[{} {}]edit selection",
@ -190,6 +168,26 @@ impl StatefulWidget for Footer {
}
})),
]),
// appearance
Row::new(vec![
Cell::from(Span::styled(
"appearance",
Style::default().add_modifier(Modifier::BOLD),
)),
Cell::from(Line::from(vec![
Span::from("[,]change style"),
Span::from(SPACE),
Span::from("[.]toggle deciseconds"),
Span::from(SPACE),
Span::from(format!(
"[:]toggle {} time",
match self.app_time {
AppTime::Local(_) => "local",
AppTime::Utc(_) => "utc",
}
)),
])),
]),
],
widths,
)

View File

@ -145,7 +145,18 @@ impl TuiEventHandler for PomodoroState {
KeyCode::Char('s') => {
self.get_clock_mut().toggle_pause();
}
KeyCode::Char('e') => {
// Skip changes
KeyCode::Esc if edit_mode => {
let clock = self.get_clock_mut();
clock.toggle_edit();
clock.set_current_value(*clock.get_prev_value());
}
// Apply changes
KeyCode::Enter if edit_mode => {
self.get_clock_mut().toggle_edit();
}
// Enter edit mode
KeyCode::Char('e') if !edit_mode => {
self.get_clock_mut().toggle_edit();
}
KeyCode::Left if edit_mode => {
@ -175,7 +186,9 @@ impl TuiEventHandler for PomodoroState {
if self.get_mode() == &Mode::Work && self.get_clock().is_done() {
self.round += 1;
}
self.get_clock_mut().reset();
// reset both clocks
self.clock_map.pause.reset();
self.clock_map.work.reset();
}
_ => return Some(event),
},

View File

@ -46,7 +46,16 @@ impl TuiEventHandler for TimerState {
KeyCode::Char('r') => {
self.clock.reset();
}
KeyCode::Char('e') => {
KeyCode::Esc if edit_mode => {
// Important: set current value first
self.clock.set_current_value(*self.clock.get_prev_value());
// before toggling back to non-edit mode
self.clock.toggle_edit();
}
KeyCode::Enter if edit_mode => {
self.clock.toggle_edit();
}
KeyCode::Char('e') if !edit_mode => {
self.clock.toggle_edit();
}
KeyCode::Left if edit_mode => {