Skip to content

Commit 2c01669

Browse files
committed
refactor: separate UI keys (custom type around crossterm keys) from controller keys (raw crossterm keys)
for allowing more UI choices while keeping the abstraction at the controller side
1 parent 66a94e3 commit 2c01669

3 files changed

Lines changed: 62 additions & 66 deletions

File tree

src/configure/app/ir_enabler.rs

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::helper::*;
22
use crate::configure::ui::ir_enabler::{IrEnablerCtx, View, ui};
3-
use crate::configure::ui::keys::*;
3+
use crate::configure::ui::{DeviceSettingsCtx, SearchSettingsCtx};
44
use crate::video::ir::analyzer::{
55
self, IsIrWorking as AnalyzerResponse, Message as AnalyzerRequest, StreamAnalyzer,
66
};
@@ -24,6 +24,13 @@ use tokio::{
2424
task,
2525
};
2626

27+
const KEY_YES: KeyCode = KeyCode::Char('y');
28+
const KEY_NO: KeyCode = KeyCode::Char('n');
29+
const KEY_EXIT: KeyCode = KeyCode::Esc;
30+
const KEY_NAVIGATE: KeyCode = KeyCode::Tab;
31+
const KEY_CONTINUE: KeyCode = KeyCode::Enter;
32+
const KEY_DELETE: KeyCode = KeyCode::Backspace;
33+
2734
#[derive(Debug)]
2835
pub struct Config {
2936
/// Path to the video device.
@@ -422,13 +429,13 @@ impl App {
422429
}
423430

424431
/// Handles a key event based on the current application state.
425-
async fn handle_key_press(&mut self, key: Key) -> Result<()> {
432+
async fn handle_key_press(&mut self, key: KeyCode) -> Result<()> {
426433
match self.state() {
427434
State::Menu => match key {
428435
KEY_EXIT => self.set_state(State::Failure),
429436
KEY_NAVIGATE => self.next_setting(),
430437
KEY_DELETE => self.edit_setting(None),
431-
Key(KeyCode::Char(c)) => self.edit_setting(Some(c)),
438+
KeyCode::Char(c) => self.edit_setting(Some(c)),
432439
KEY_CONTINUE => self.set_state(State::ConfirmStart),
433440
_ => {}
434441
},
@@ -458,7 +465,7 @@ impl App {
458465
/// In both of the two case, also changes the state to [`State::Running`].
459466
///
460467
/// Otherwise, does nothing.
461-
async fn confirm_working(&mut self, k: Key) -> Result<()> {
468+
async fn confirm_working(&mut self, k: KeyCode) -> Result<()> {
462469
let mut response = IREnablerResponse::No;
463470
if k == KEY_YES {
464471
response = IREnablerResponse::Yes;
@@ -477,7 +484,7 @@ impl App {
477484
/// and sends [`IREnablerResponse::Abort`] to the configurator task.
478485
///
479486
/// If the key is [`KEY_NO`], change the state back to [`State::Running`].
480-
async fn abort_or_continue(&mut self, k: Key) -> Result<()> {
487+
async fn abort_or_continue(&mut self, k: KeyCode) -> Result<()> {
481488
match k {
482489
KEY_NO | KEY_EXIT => self.set_state(self.prev_state()),
483490
KEY_YES => {
@@ -498,7 +505,7 @@ impl App {
498505
/// If the key is [`KEY_NO`], change the state back to the previous state.
499506
///
500507
/// Returns directly an error if the video stream is already started.
501-
fn start_or_back(&mut self, k: Key) -> Result<()> {
508+
fn start_or_back(&mut self, k: KeyCode) -> Result<()> {
502509
// check that the path exists
503510
if !self.is_device_valid() {
504511
self.set_state(State::Menu);
@@ -594,6 +601,18 @@ impl IrEnablerCtx for App {
594601
fn show_menu_start_prompt(&self) -> bool {
595602
self.state() == State::ConfirmStart
596603
}
604+
fn controls_list_state(&mut self) -> &mut ListState {
605+
&mut self.controls_list_state
606+
}
607+
fn controls(&self) -> &[XuControl] {
608+
&self.controls
609+
}
610+
fn image(&self) -> Option<&Image> {
611+
self.image.as_ref()
612+
}
613+
}
614+
615+
impl DeviceSettingsCtx for App {
597616
fn device_settings_list_state(&mut self) -> &mut ListState {
598617
&mut self.device_settings_list_state
599618
}
@@ -615,6 +634,9 @@ impl IrEnablerCtx for App {
615634
fn fps(&self) -> Option<u32> {
616635
self.config.fps
617636
}
637+
}
638+
639+
impl SearchSettingsCtx for App {
618640
fn search_settings_list_state(&mut self) -> &mut ListState {
619641
&mut self.search_settings_list_state
620642
}
@@ -633,15 +655,6 @@ impl IrEnablerCtx for App {
633655
fn inc_step(&self) -> u8 {
634656
self.config.inc_step
635657
}
636-
fn controls_list_state(&mut self) -> &mut ListState {
637-
&mut self.controls_list_state
638-
}
639-
fn controls(&self) -> &[XuControl] {
640-
&self.controls
641-
}
642-
fn image(&self) -> Option<&Image> {
643-
self.image.as_ref()
644-
}
645658
}
646659

647660
#[cfg(test)]
@@ -655,16 +668,16 @@ mod tests {
655668
App::new()
656669
}
657670

658-
fn make_key_event(keycode: Key) -> KeyEvent {
671+
fn make_key_event(keycode: KeyCode) -> KeyEvent {
659672
KeyEvent {
660-
code: keycode.into(),
673+
code: keycode,
661674
modifiers: KeyModifiers::NONE,
662675
kind: KeyEventKind::Press,
663676
state: crossterm::event::KeyEventState::NONE,
664677
}
665678
}
666679

667-
fn make_term_key_event(keycode: Key) -> Event {
680+
fn make_term_key_event(keycode: KeyCode) -> Event {
668681
Event::Key(make_key_event(keycode))
669682
}
670683

src/configure/app/tool_menu.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
use crate::configure::app::ir_enabler::App as IREnablerApp;
2-
use crate::configure::ui::keys::{KEY_CONTINUE, KEY_EXIT, KEY_NAVIGATE};
32
use crate::configure::ui::tool_menu::ui;
43

54
use anyhow::Result;
6-
use crossterm::event;
5+
use crossterm::event::{self, KeyCode};
76
use crossterm::event::{Event, KeyEventKind};
87

98
/// Application state for the tool menu.
@@ -31,10 +30,10 @@ impl App {
3130
terminal.draw(|f| ui(f, self))?;
3231
match event::read()? {
3332
Event::Key(key_event) if key_event.kind == KeyEventKind::Press => {
34-
match key_event.code.into() {
35-
KEY_NAVIGATE => self.next_tool(),
36-
KEY_CONTINUE => return self.start_tool(terminal).await,
37-
KEY_EXIT => return Ok(""),
33+
match key_event.code {
34+
KeyCode::Tab => self.next_tool(),
35+
KeyCode::Enter => return self.start_tool(terminal).await,
36+
KeyCode::Esc => return Ok(""),
3837
_ => {}
3938
}
4039
}

src/configure/ui.rs

Lines changed: 26 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,55 +3,40 @@ use helper::*;
33
pub mod ir_enabler;
44
mod shared;
55
use shared::*;
6+
pub use shared::{DeviceSettingsCtx, SearchSettingsCtx};
67
pub mod tool_menu;
8+
pub mod tweaker;
79

8-
pub mod keys {
10+
mod keys {
911
use crossterm::event::KeyCode;
10-
use derive_more::{From, Into};
1112
use ratatui::{
1213
style::Stylize,
1314
style::{Color, Style},
1415
text::{Line, Span},
1516
};
1617

17-
#[derive(Debug, Clone, Copy, PartialEq, Eq, From, Into)]
18-
pub(crate) struct Key(pub KeyCode);
18+
#[derive(Debug, Clone, Copy)]
19+
pub struct Key {
20+
code: KeyCode,
21+
name: &'static str,
22+
color: Color,
23+
}
24+
25+
impl Key {
26+
const fn new(code: KeyCode, name: &'static str, color: Color) -> Self {
27+
Self { code, name, color }
28+
}
29+
}
1930

2031
pub fn keys_to_line(keys: &[Key]) -> Line<'static> {
2132
let mut spans = Vec::with_capacity(keys.len() * 3);
2233
for (i, key) in keys.iter().enumerate() {
23-
match key.0 {
24-
KeyCode::Esc => {
25-
spans.push("Quit <".bold());
26-
spans.push(Span::styled("Esc", Style::default().fg(Color::Red)));
27-
spans.push(">".bold());
28-
}
29-
KeyCode::Tab => {
30-
spans.push("Navigate <".bold());
31-
spans.push(Span::styled("Tab", Style::default().fg(Color::Yellow)));
32-
spans.push(">".bold());
33-
}
34-
KeyCode::Enter => {
35-
spans.push("Continue <".bold());
36-
spans.push(Span::styled("Enter", Style::default().fg(Color::Green)));
37-
spans.push(">".bold());
38-
}
39-
KeyCode::Char('y') => {
40-
spans.push("Yes <".bold());
41-
spans.push(Span::styled("y", Style::default().fg(Color::Green)));
42-
spans.push(">".bold());
43-
}
44-
KeyCode::Char('n') => {
45-
spans.push("No <".bold());
46-
spans.push(Span::styled("n", Style::default().fg(Color::Red)));
47-
spans.push(">".bold());
48-
}
49-
_ => {
50-
spans.push("? <".bold());
51-
spans.push(Span::raw(format!("{:?}", key.0)));
52-
spans.push(">".bold());
53-
}
54-
}
34+
spans.push(format!("{} <", key.name).bold());
35+
spans.push(Span::styled(
36+
key.code.to_string(),
37+
Style::default().fg(key.color),
38+
));
39+
spans.push(">".bold());
5540

5641
if i != keys.len() - 1 {
5742
spans.push(Span::raw(" "));
@@ -60,12 +45,11 @@ pub mod keys {
6045
Line::from(spans)
6146
}
6247

63-
pub const KEY_EXIT: Key = Key(KeyCode::Esc);
64-
pub const KEY_NAVIGATE: Key = Key(KeyCode::Tab);
65-
pub const KEY_CONTINUE: Key = Key(KeyCode::Enter);
66-
pub const KEY_YES: Key = Key(KeyCode::Char('y'));
67-
pub const KEY_NO: Key = Key(KeyCode::Char('n'));
68-
pub const KEY_DELETE: Key = Key(KeyCode::Backspace);
48+
pub const KEY_EXIT: Key = Key::new(KeyCode::Esc, "Quit", Color::Red);
49+
pub const KEY_NAVIGATE: Key = Key::new(KeyCode::Tab, "Navigate", Color::Yellow);
50+
pub const KEY_CONTINUE: Key = Key::new(KeyCode::Enter, "Continue", Color::Green);
51+
pub const KEY_YES: Key = Key::new(KeyCode::Char('y'), "Yes", Color::Green);
52+
pub const KEY_NO: Key = Key::new(KeyCode::Char('n'), "No", Color::Red);
6953
}
7054

7155
#[cfg(test)]

0 commit comments

Comments
 (0)