State machine solution works
This commit is contained in:
parent
5979e75dcd
commit
b74eafc9de
1352
src/tui/app/app.rs
1352
src/tui/app/app.rs
File diff suppressed because it is too large
Load Diff
@ -14,19 +14,21 @@ use crate::tui::{
|
|||||||
event::{Event, EventError, EventReceiver},
|
event::{Event, EventError, EventReceiver},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::app::app::IAppInteractCritical;
|
||||||
|
|
||||||
#[cfg_attr(test, automock)]
|
#[cfg_attr(test, automock)]
|
||||||
pub trait IEventHandler<APP: IAppInteract> {
|
pub trait IEventHandler<APP: IAppInteract> {
|
||||||
fn handle_next_event(&self, app: &mut APP) -> Result<(), EventError>;
|
fn handle_next_event(&self, app: APP) -> Result<APP, EventError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
trait IEventHandlerPrivate<APP: IAppInteract> {
|
trait IEventHandlerPrivate<APP: IAppInteract> {
|
||||||
fn handle_key_event(app: &mut APP, key_event: KeyEvent);
|
fn handle_key_event(app: APP, key_event: KeyEvent) -> APP;
|
||||||
fn handle_browse_key_event(app: &mut <APP as IAppInteract>::BS, key_event: KeyEvent);
|
fn handle_browse_key_event(app: <APP as IAppInteract>::BS, key_event: KeyEvent) -> APP;
|
||||||
fn handle_info_key_event(app: &mut <APP as IAppInteract>::IS, key_event: KeyEvent);
|
fn handle_info_key_event(app: <APP as IAppInteract>::IS, key_event: KeyEvent) -> APP;
|
||||||
fn handle_reload_key_event(app: &mut <APP as IAppInteract>::RS, key_event: KeyEvent);
|
fn handle_reload_key_event(app: <APP as IAppInteract>::RS, key_event: KeyEvent) -> APP;
|
||||||
fn handle_search_key_event(app: &mut <APP as IAppInteract>::SS, key_event: KeyEvent);
|
fn handle_search_key_event(app: <APP as IAppInteract>::SS, key_event: KeyEvent) -> APP;
|
||||||
fn handle_error_key_event(app: &mut <APP as IAppInteract>::ES, key_event: KeyEvent);
|
fn handle_error_key_event(app: <APP as IAppInteract>::ES, key_event: KeyEvent) -> APP;
|
||||||
fn handle_critical_key_event(app: &mut <APP as IAppInteract>::CS, key_event: KeyEvent);
|
fn handle_critical_key_event(app: <APP as IAppInteract>::CS, key_event: KeyEvent) -> APP;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EventHandler {
|
pub struct EventHandler {
|
||||||
@ -41,58 +43,52 @@ impl EventHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<APP: IAppInteract> IEventHandler<APP> for EventHandler {
|
impl<APP: IAppInteract> IEventHandler<APP> for EventHandler {
|
||||||
fn handle_next_event(&self, app: &mut APP) -> Result<(), EventError> {
|
fn handle_next_event(&self, mut app: APP) -> Result<APP, EventError> {
|
||||||
match self.events.recv()? {
|
match self.events.recv()? {
|
||||||
Event::Key(key_event) => Self::handle_key_event(app, key_event),
|
Event::Key(key_event) => app = Self::handle_key_event(app, key_event),
|
||||||
Event::Mouse(_) => {}
|
Event::Mouse(_) => {}
|
||||||
Event::Resize(_, _) => {}
|
Event::Resize(_, _) => {}
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(app)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<APP: IAppInteract> IEventHandlerPrivate<APP> for EventHandler {
|
impl<APP: IAppInteract> IEventHandlerPrivate<APP> for EventHandler {
|
||||||
fn handle_key_event(app: &mut APP, key_event: KeyEvent) {
|
fn handle_key_event(app: APP, key_event: KeyEvent) -> APP {
|
||||||
if key_event.modifiers == KeyModifiers::CONTROL {
|
if key_event.modifiers == KeyModifiers::CONTROL {
|
||||||
match key_event.code {
|
match key_event.code {
|
||||||
// Exit application on `Ctrl-C`.
|
// Exit application on `Ctrl-C`.
|
||||||
KeyCode::Char('c') | KeyCode::Char('C') => {
|
KeyCode::Char('c') | KeyCode::Char('C') => return app.force_quit(),
|
||||||
app.force_quit();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
match app.state() {
|
match app.state() {
|
||||||
AppState::Browse(browse) => {
|
AppState::Browse(browse) => {
|
||||||
<Self as IEventHandlerPrivate<APP>>::handle_browse_key_event(browse, key_event);
|
<Self as IEventHandlerPrivate<APP>>::handle_browse_key_event(browse, key_event)
|
||||||
}
|
}
|
||||||
AppState::Info(info) => {
|
AppState::Info(info) => {
|
||||||
<Self as IEventHandlerPrivate<APP>>::handle_info_key_event(info, key_event);
|
<Self as IEventHandlerPrivate<APP>>::handle_info_key_event(info, key_event)
|
||||||
}
|
}
|
||||||
AppState::Reload(reload) => {
|
AppState::Reload(reload) => {
|
||||||
<Self as IEventHandlerPrivate<APP>>::handle_reload_key_event(reload, key_event);
|
<Self as IEventHandlerPrivate<APP>>::handle_reload_key_event(reload, key_event)
|
||||||
}
|
}
|
||||||
AppState::Search(search) => {
|
AppState::Search(search) => {
|
||||||
<Self as IEventHandlerPrivate<APP>>::handle_search_key_event(search, key_event);
|
<Self as IEventHandlerPrivate<APP>>::handle_search_key_event(search, key_event)
|
||||||
}
|
}
|
||||||
AppState::Error(error) => {
|
AppState::Error(error) => {
|
||||||
<Self as IEventHandlerPrivate<APP>>::handle_error_key_event(error, key_event);
|
<Self as IEventHandlerPrivate<APP>>::handle_error_key_event(error, key_event)
|
||||||
}
|
}
|
||||||
AppState::Critical(critical) => {
|
AppState::Critical(critical) => {
|
||||||
<Self as IEventHandlerPrivate<APP>>::handle_critical_key_event(critical, key_event);
|
<Self as IEventHandlerPrivate<APP>>::handle_critical_key_event(critical, key_event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_browse_key_event(app: &mut <APP as IAppInteract>::BS, key_event: KeyEvent) {
|
fn handle_browse_key_event(app: <APP as IAppInteract>::BS, key_event: KeyEvent) -> APP {
|
||||||
match key_event.code {
|
match key_event.code {
|
||||||
// Exit application on `ESC` or `q`.
|
// Exit application on `ESC` or `q`.
|
||||||
KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('Q') => {
|
KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('Q') => app.save_and_quit(),
|
||||||
app.save();
|
|
||||||
app.quit();
|
|
||||||
}
|
|
||||||
// Category change.
|
// Category change.
|
||||||
KeyCode::Left => app.decrement_category(),
|
KeyCode::Left => app.decrement_category(),
|
||||||
KeyCode::Right => app.increment_category(),
|
KeyCode::Right => app.increment_category(),
|
||||||
@ -108,15 +104,17 @@ impl<APP: IAppInteract> IEventHandlerPrivate<APP> for EventHandler {
|
|||||||
// Toggle search.
|
// Toggle search.
|
||||||
KeyCode::Char('s') | KeyCode::Char('S') => {
|
KeyCode::Char('s') | KeyCode::Char('S') => {
|
||||||
if key_event.modifiers == KeyModifiers::CONTROL {
|
if key_event.modifiers == KeyModifiers::CONTROL {
|
||||||
app.begin_search();
|
app.begin_search()
|
||||||
|
} else {
|
||||||
|
app.no_op()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Othey keys.
|
// Othey keys.
|
||||||
_ => {}
|
_ => app.no_op(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_info_key_event(app: &mut <APP as IAppInteract>::IS, key_event: KeyEvent) {
|
fn handle_info_key_event(app: <APP as IAppInteract>::IS, key_event: KeyEvent) -> APP {
|
||||||
match key_event.code {
|
match key_event.code {
|
||||||
// Toggle overlay.
|
// Toggle overlay.
|
||||||
KeyCode::Esc
|
KeyCode::Esc
|
||||||
@ -125,11 +123,11 @@ impl<APP: IAppInteract> IEventHandlerPrivate<APP> for EventHandler {
|
|||||||
| KeyCode::Char('m')
|
| KeyCode::Char('m')
|
||||||
| KeyCode::Char('M') => app.hide_info_overlay(),
|
| KeyCode::Char('M') => app.hide_info_overlay(),
|
||||||
// Othey keys.
|
// Othey keys.
|
||||||
_ => {}
|
_ => app.no_op(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_reload_key_event(app: &mut <APP as IAppInteract>::RS, key_event: KeyEvent) {
|
fn handle_reload_key_event(app: <APP as IAppInteract>::RS, key_event: KeyEvent) -> APP {
|
||||||
match key_event.code {
|
match key_event.code {
|
||||||
// Reload keys.
|
// Reload keys.
|
||||||
KeyCode::Char('l') | KeyCode::Char('L') => app.reload_library(),
|
KeyCode::Char('l') | KeyCode::Char('L') => app.reload_library(),
|
||||||
@ -141,22 +139,17 @@ impl<APP: IAppInteract> IEventHandlerPrivate<APP> for EventHandler {
|
|||||||
| KeyCode::Char('g')
|
| KeyCode::Char('g')
|
||||||
| KeyCode::Char('G') => app.hide_reload_menu(),
|
| KeyCode::Char('G') => app.hide_reload_menu(),
|
||||||
// Othey keys.
|
// Othey keys.
|
||||||
_ => {}
|
_ => app.no_op(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_search_key_event(app: &mut <APP as IAppInteract>::SS, key_event: KeyEvent) {
|
fn handle_search_key_event(app: <APP as IAppInteract>::SS, key_event: KeyEvent) -> APP {
|
||||||
if key_event.modifiers == KeyModifiers::CONTROL {
|
if key_event.modifiers == KeyModifiers::CONTROL {
|
||||||
match key_event.code {
|
return match key_event.code {
|
||||||
KeyCode::Char('s') | KeyCode::Char('S') => {
|
KeyCode::Char('s') | KeyCode::Char('S') => app.search_next(),
|
||||||
app.search_next();
|
KeyCode::Char('g') | KeyCode::Char('G') => app.cancel_search(),
|
||||||
}
|
_ => app.no_op(),
|
||||||
KeyCode::Char('g') | KeyCode::Char('G') => {
|
};
|
||||||
app.cancel_search();
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match key_event.code {
|
match key_event.code {
|
||||||
@ -166,17 +159,18 @@ impl<APP: IAppInteract> IEventHandlerPrivate<APP> for EventHandler {
|
|||||||
// Return.
|
// Return.
|
||||||
KeyCode::Esc | KeyCode::Enter => app.finish_search(),
|
KeyCode::Esc | KeyCode::Enter => app.finish_search(),
|
||||||
// Othey keys.
|
// Othey keys.
|
||||||
_ => {}
|
_ => app.no_op(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_error_key_event(app: &mut <APP as IAppInteract>::ES, _key_event: KeyEvent) {
|
fn handle_error_key_event(app: <APP as IAppInteract>::ES, _key_event: KeyEvent) -> APP {
|
||||||
// Any key dismisses the error.
|
// Any key dismisses the error.
|
||||||
app.dismiss_error();
|
app.dismiss_error()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_critical_key_event(_app: &mut <APP as IAppInteract>::CS, _key_event: KeyEvent) {
|
fn handle_critical_key_event(app: <APP as IAppInteract>::CS, _key_event: KeyEvent) -> APP {
|
||||||
// No action is allowed.
|
// No action is allowed.
|
||||||
|
app.no_op()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// GRCOV_EXCL_STOP
|
// GRCOV_EXCL_STOP
|
||||||
|
@ -75,7 +75,7 @@ impl<B: Backend, UI: IUi, APP: IAppInteract + IAppAccess> Tui<B, UI, APP> {
|
|||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
while app.is_running() {
|
while app.is_running() {
|
||||||
self.terminal.draw(|frame| UI::render(&mut app, frame))?;
|
self.terminal.draw(|frame| UI::render(&mut app, frame))?;
|
||||||
handler.handle_next_event(&mut app)?;
|
app = handler.handle_next_event(app)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -219,10 +219,7 @@ mod tests {
|
|||||||
let mut handler = MockIEventHandler::new();
|
let mut handler = MockIEventHandler::new();
|
||||||
handler
|
handler
|
||||||
.expect_handle_next_event()
|
.expect_handle_next_event()
|
||||||
.return_once(|app: &mut App<MockIMusicHoard>| {
|
.return_once(|app: App<MockIMusicHoard>| Ok(app.force_quit()));
|
||||||
app.force_quit();
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
handler
|
handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -681,12 +681,12 @@ impl IUi for Ui {
|
|||||||
let selection = app.selection;
|
let selection = app.selection;
|
||||||
let state = app.state;
|
let state = app.state;
|
||||||
|
|
||||||
Self::render_main_frame(collection, selection, state, frame);
|
Self::render_main_frame(collection, selection, &state, frame);
|
||||||
match state {
|
match state {
|
||||||
AppState::Info(_) => Self::render_info_overlay(collection, selection, frame),
|
AppState::Info(_) => Self::render_info_overlay(collection, selection, frame),
|
||||||
AppState::Reload(_) => Self::render_reload_overlay(frame),
|
AppState::Reload(_) => Self::render_reload_overlay(frame),
|
||||||
AppState::Error(ref msg) => Self::render_error_overlay("Error", msg, frame),
|
AppState::Error(msg) => Self::render_error_overlay("Error", msg, frame),
|
||||||
AppState::Critical(ref msg) => Self::render_error_overlay("Critical Error", msg, frame),
|
AppState::Critical(msg) => Self::render_error_overlay("Critical Error", msg, frame),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -719,26 +719,23 @@ mod tests {
|
|||||||
let mut app = AppPublic {
|
let mut app = AppPublic {
|
||||||
collection,
|
collection,
|
||||||
selection,
|
selection,
|
||||||
state: &AppState::Browse(()),
|
state: AppState::Browse(()),
|
||||||
};
|
};
|
||||||
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
|
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
|
||||||
|
|
||||||
app.state = &AppState::Info(());
|
app.state = AppState::Info(());
|
||||||
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
|
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
|
||||||
|
|
||||||
app.state = &AppState::Reload(());
|
app.state = AppState::Reload(());
|
||||||
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
|
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
|
||||||
|
|
||||||
let binding = AppState::Search(String::new());
|
app.state = AppState::Search("");
|
||||||
app.state = &binding;
|
|
||||||
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
|
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
|
||||||
|
|
||||||
let binding = AppState::Error(String::from("get rekt scrub"));
|
app.state = AppState::Error("get rekt scrub");
|
||||||
app.state = &binding;
|
|
||||||
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
|
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
|
||||||
|
|
||||||
let binding = AppState::Critical(String::from("get critically rekt scrub"));
|
app.state = AppState::Critical("get critically rekt scrub");
|
||||||
app.state = &binding;
|
|
||||||
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
|
terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user