Skip to content

Commit bdfef0b

Browse files
committed
refactor: use key up and down to navigate in menus
1 parent c09975b commit bdfef0b

15 files changed

+85
-29
lines changed

src/configure/app/ir_enabler.rs

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ use tokio::{
2727
const KEY_YES: KeyCode = KeyCode::Char('y');
2828
const KEY_NO: KeyCode = KeyCode::Char('n');
2929
const KEY_EXIT: KeyCode = KeyCode::Esc;
30-
const KEY_NAVIGATE: KeyCode = KeyCode::Tab;
30+
const KEY_NAVIGATE_UP: KeyCode = KeyCode::Up;
31+
const KEY_NAVIGATE_DOWN: KeyCode = KeyCode::Down;
3132
const KEY_CONTINUE: KeyCode = KeyCode::Enter;
3233
const KEY_DELETE: KeyCode = KeyCode::Backspace;
3334

@@ -433,7 +434,8 @@ impl App {
433434
match self.state() {
434435
State::Menu => match key {
435436
KEY_EXIT => self.set_state(State::Failure),
436-
KEY_NAVIGATE => self.next_setting(),
437+
KEY_NAVIGATE_UP => self.prev_setting(),
438+
KEY_NAVIGATE_DOWN => self.next_setting(),
437439
KEY_DELETE => self.edit_setting(None),
438440
KeyCode::Char(c) => self.edit_setting(Some(c)),
439441
KEY_CONTINUE => self.set_state(State::ConfirmStart),
@@ -541,15 +543,22 @@ impl App {
541543
self.device_settings_list_state.select(None);
542544
self.search_settings_list_state.select_first();
543545
}
544-
} else if let Some(i) = self.search_settings_list_state.selected() {
545-
if i < 3 {
546-
self.search_settings_list_state.select_next();
546+
} else {
547+
self.search_settings_list_state.select_next();
548+
}
549+
}
550+
551+
/// Moves the selection to the previous setting in the settings lists.
552+
fn prev_setting(&mut self) {
553+
if let Some(i) = self.search_settings_list_state.selected() {
554+
if i > 0 {
555+
self.search_settings_list_state.select_previous();
547556
} else {
548557
self.search_settings_list_state.select(None);
549-
self.device_settings_list_state.select_first();
558+
self.device_settings_list_state.select_last();
550559
}
551560
} else {
552-
self.device_settings_list_state.select_first();
561+
self.device_settings_list_state.select_previous();
553562
}
554563
}
555564

@@ -726,7 +735,7 @@ mod tests {
726735
}
727736

728737
#[test]
729-
fn test_next_setting_device_to_search_and_back() {
738+
fn test_next_setting_device_to_search() {
730739
let mut app = make_app();
731740
// Move through device settings (0..4)
732741
for i in 0..4 {
@@ -737,14 +746,47 @@ mod tests {
737746
app.next_setting();
738747
assert!(app.device_settings_list_state.selected().is_none());
739748
assert_eq!(app.search_settings_list_state.selected(), Some(0));
740-
// Move through search settings (0..2)
749+
// Move through search settings (0..3)
741750
for i in 0..3 {
742751
app.next_setting();
743752
assert_eq!(app.search_settings_list_state.selected(), Some(i + 1));
744753
}
745-
// After 2, should wrap to device settings first
754+
// After 3 we should stay at the last search setting
746755
app.next_setting();
756+
assert_eq!(app.search_settings_list_state.selected(), Some(4));
757+
}
758+
759+
#[test]
760+
fn test_prev_setting_device_to_search() {
761+
let mut app = make_app();
762+
// Start at the end of search settings
763+
app.device_settings_list_state.select(None);
764+
app.search_settings_list_state.select(Some(4));
765+
766+
// Move through search settings (4..0)
767+
for i in (1..=4).rev() {
768+
assert_eq!(app.search_settings_list_state.selected(), Some(i));
769+
app.prev_setting();
770+
}
771+
772+
// At index 0 of search settings, prev_setting should move to device settings last
773+
assert_eq!(app.search_settings_list_state.selected(), Some(0));
774+
app.prev_setting();
747775
assert!(app.search_settings_list_state.selected().is_none());
776+
777+
// NOTE: select_last() sets to usize::MAX until screen is rendered to know that it is actually 4
778+
assert_eq!(app.device_settings_list_state.selected(), Some(usize::MAX));
779+
// so let's cheat
780+
app.device_settings_list_state.select(Some(4));
781+
782+
// Move through device settings (4..0)
783+
for i in (1..=4).rev() {
784+
app.prev_setting();
785+
assert_eq!(app.device_settings_list_state.selected(), Some(i - 1));
786+
}
787+
788+
// At the first device setting, we should stay there
789+
app.prev_setting();
748790
assert_eq!(app.device_settings_list_state.selected(), Some(0));
749791
}
750792

@@ -893,7 +935,7 @@ mod tests {
893935
let mut app = make_app();
894936
app.set_state(State::Menu);
895937
app.device_settings_list_state.select(Some(0));
896-
let key_event = make_term_key_event(KEY_NAVIGATE);
938+
let key_event = make_term_key_event(KEY_NAVIGATE_DOWN);
897939
let res = app.handle_term_event(key_event).await;
898940
assert!(res.is_ok(), "{:?}", res.err());
899941
assert_eq!(app.device_settings_list_state.selected(), Some(1));

src/configure/snapshots/linux_enable_ir_emitter__configure__ui__tests__render_keys.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ source: src/configure/ui.rs
33
expression: terminal.backend()
44
---
55
"Quit <Esc> "
6-
"Navigate <Tab> "
6+
"Navigate <↑↓> "
77
"Continue <Enter> "
88
"Yes <y> "
99
"No <n> "

src/configure/ui.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,26 @@ mod keys {
1515
text::{Line, Span},
1616
};
1717

18+
#[derive(Debug, Clone, Copy)]
19+
enum KeyRepr {
20+
Code(KeyCode),
21+
Str(&'static str),
22+
}
23+
1824
#[derive(Debug, Clone, Copy)]
1925
pub struct Key {
20-
code: KeyCode,
26+
repr: KeyRepr,
2127
name: &'static str,
2228
color: Color,
2329
}
2430

2531
impl Key {
2632
const fn new(code: KeyCode, name: &'static str, color: Color) -> Self {
27-
Self { code, name, color }
33+
Self { repr: KeyRepr::Code(code), name, color }
34+
}
35+
36+
const fn custom(repr: &'static str, name: &'static str, color: Color) -> Self {
37+
Self { repr: KeyRepr::Str(repr), name, color }
2838
}
2939
}
3040

@@ -33,7 +43,10 @@ mod keys {
3343
for (i, key) in keys.iter().enumerate() {
3444
spans.push(format!("{} <", key.name).bold());
3545
spans.push(Span::styled(
36-
key.code.to_string(),
46+
match key.repr {
47+
KeyRepr::Code(code) => code.to_string(),
48+
KeyRepr::Str(s) => s.to_string(),
49+
},
3750
Style::default().fg(key.color),
3851
));
3952
spans.push(">".bold());
@@ -46,6 +59,7 @@ mod keys {
4659
}
4760

4861
pub const KEY_EXIT: Key = Key::new(KeyCode::Esc, "Quit", Color::Red);
62+
pub const KEYS_NAVIGATE: Key = Key::custom("↑↓", "Navigate", Color::Yellow);
4963
pub const KEY_NAVIGATE: Key = Key::new(KeyCode::Tab, "Navigate", Color::Yellow);
5064
pub const KEY_CONTINUE: Key = Key::new(KeyCode::Enter, "Continue", Color::Green);
5165
pub const KEY_YES: Key = Key::new(KeyCode::Char('y'), "Yes", Color::Green);
@@ -62,7 +76,7 @@ mod tests {
6276
#[test]
6377
fn test_render_keys() {
6478
assert_ui_snapshot!(|frame| {
65-
let keys = [KEY_EXIT, KEY_NAVIGATE, KEY_CONTINUE, KEY_YES, KEY_NO];
79+
let keys = [KEY_EXIT, KEYS_NAVIGATE, KEY_CONTINUE, KEY_YES, KEY_NO];
6680

6781
let chunks =
6882
Layout::vertical(vec![Constraint::Length(1); keys.len()]).split(frame.area());

src/configure/ui/ir_enabler.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::{
22
DeviceSettingsCtx, SearchSettingsCtx,
3-
keys::{KEY_CONTINUE, KEY_EXIT, KEY_NAVIGATE, KEY_NO, KEY_YES, keys_to_line},
3+
keys::{KEY_CONTINUE, KEY_EXIT, KEYS_NAVIGATE, KEY_NO, KEY_YES, keys_to_line},
44
popup_area, render_full_menu, render_main_window, render_video_preview,
55
};
66
use crate::video::stream::Image;
@@ -141,7 +141,7 @@ where
141141
{
142142
match app.view() {
143143
View::Menu => {
144-
let main_area = render_main_window(frame, &[KEY_NAVIGATE, KEY_CONTINUE, KEY_EXIT]);
144+
let main_area = render_main_window(frame, &[KEYS_NAVIGATE, KEY_CONTINUE, KEY_EXIT]);
145145
render_full_menu(
146146
frame,
147147
main_area,

src/configure/ui/shared.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ mod tests {
205205
assert_ui_snapshot!(|frame| {
206206
let _ = render_main_window(
207207
frame,
208-
&[keys::KEY_NAVIGATE, keys::KEY_CONTINUE, keys::KEY_EXIT],
208+
&[keys::KEYS_NAVIGATE, keys::KEY_CONTINUE, keys::KEY_EXIT],
209209
);
210210
});
211211
}

src/configure/ui/snapshots/linux_enable_ir_emitter__configure__ui__ir_enabler__tests__menu_empty.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,5 @@ expression: terminal.backend()
3030
"┃ ┃"
3131
"┃ ┃"
3232
"┃──────────────────────────────────────────────────────────────────────────────────────────────────┃"
33-
"┃ Navigate <Tab> Continue <Enter> Quit <Esc> ┃"
33+
" Navigate <↑↓> Continue <Enter> Quit <Esc> ┃"
3434
"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"

src/configure/ui/snapshots/linux_enable_ir_emitter__configure__ui__ir_enabler__tests__menu_strart.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,5 @@ expression: terminal.backend()
3030
"┃ ┃"
3131
"┃ ┃"
3232
"┃──────────────────────────────────────────────────────────────────────────────────────────────────┃"
33-
"┃ Navigate <Tab> Continue <Enter> Quit <Esc> ┃"
33+
" Navigate <↑↓> Continue <Enter> Quit <Esc> ┃"
3434
"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"

src/configure/ui/snapshots/linux_enable_ir_emitter__configure__ui__ir_enabler__tests__menu_valid_values.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,5 @@ expression: terminal.backend()
3030
"┃ ┃"
3131
"┃ ┃"
3232
"┃──────────────────────────────────────────────────────────────────────────────────────────────────┃"
33-
"┃ Navigate <Tab> Continue <Enter> Quit <Esc> ┃"
33+
" Navigate <↑↓> Continue <Enter> Quit <Esc> ┃"
3434
"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"

src/configure/ui/snapshots/linux_enable_ir_emitter__configure__ui__shared__tests__render_main_window.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,5 @@ expression: terminal.backend()
3030
"┃ ┃"
3131
"┃ ┃"
3232
"┃──────────────────────────────────────────────────────────────────────────────────────────────────┃"
33-
"┃ Navigate <Tab> Continue <Enter> Quit <Esc> ┃"
33+
" Navigate <↑↓> Continue <Enter> Quit <Esc> ┃"
3434
"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"

src/configure/ui/snapshots/linux_enable_ir_emitter__configure__ui__tweaker__tests__main_empty.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,5 @@ expression: terminal.backend()
3030
"┃│ ││ │┃"
3131
"┃└───────────────────────────────────────────────┘└───────────────────────────────────────────────┘┃"
3232
"┃──────────────────────────────────────────────────────────────────────────────────────────────────┃"
33-
"┃ Navigate <Tab> Edit <Enter> Quit <Esc> ┃"
33+
" Navigate <↑↓> Edit <Enter> Quit <Esc> ┃"
3434
"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"

0 commit comments

Comments
 (0)