diff --git a/src/tui/app/machine/browse_state.rs b/src/tui/app/machine/browse_state.rs index f1bd64d..2e71f73 100644 --- a/src/tui/app/machine/browse_state.rs +++ b/src/tui/app/machine/browse_state.rs @@ -11,7 +11,6 @@ impl AppMachine { AppMachine { inner, state: BrowseState, - input: None, } } } @@ -27,7 +26,6 @@ impl<'a> From<&'a mut AppMachine> for AppPublic<'a> { AppPublic { inner: (&mut machine.inner).into(), state: AppState::Browse(()), - input: (&machine.input.as_ref()).map(Into::into), } } } diff --git a/src/tui/app/machine/critical_state.rs b/src/tui/app/machine/critical_state.rs index 80ef163..9242cbc 100644 --- a/src/tui/app/machine/critical_state.rs +++ b/src/tui/app/machine/critical_state.rs @@ -14,7 +14,6 @@ impl AppMachine { state: CriticalState { string: string.into(), }, - input: None } } } @@ -30,7 +29,6 @@ impl<'a> From<&'a mut AppMachine> for AppPublic<'a> { AppPublic { inner: (&mut machine.inner).into(), state: AppState::Critical(&machine.state.string), - input: (&machine.input.as_ref()).map(Into::into), } } } diff --git a/src/tui/app/machine/error_state.rs b/src/tui/app/machine/error_state.rs index 42a7a46..2150b2d 100644 --- a/src/tui/app/machine/error_state.rs +++ b/src/tui/app/machine/error_state.rs @@ -14,7 +14,6 @@ impl AppMachine { state: ErrorState { string: string.into(), }, - input: None, } } } @@ -30,7 +29,6 @@ impl<'a> From<&'a mut AppMachine> for AppPublic<'a> { AppPublic { inner: (&mut machine.inner).into(), state: AppState::Error(&machine.state.string), - input: (&machine.input.as_ref()).map(Into::into), } } } diff --git a/src/tui/app/machine/fetch_state.rs b/src/tui/app/machine/fetch_state.rs index a16327c..78d09eb 100644 --- a/src/tui/app/machine/fetch_state.rs +++ b/src/tui/app/machine/fetch_state.rs @@ -40,11 +40,7 @@ pub type FetchReceiver = mpsc::Receiver; impl AppMachine { fn fetch_state(inner: AppInner, state: FetchState) -> Self { - AppMachine { - inner, - state, - input: None, - } + AppMachine { inner, state } } pub fn app_fetch_new(inner: AppInner) -> App { @@ -177,7 +173,6 @@ impl<'a> From<&'a mut AppMachine> for AppPublic<'a> { AppPublic { inner: (&mut machine.inner).into(), state: AppState::Fetch(()), - input: (&machine.input.as_ref()).map(Into::into), } } } diff --git a/src/tui/app/machine/info_state.rs b/src/tui/app/machine/info_state.rs index c066bcf..a4e5ef6 100644 --- a/src/tui/app/machine/info_state.rs +++ b/src/tui/app/machine/info_state.rs @@ -10,7 +10,6 @@ impl AppMachine { AppMachine { inner, state: InfoState, - input: None, } } } @@ -26,7 +25,6 @@ impl<'a> From<&'a mut AppMachine> for AppPublic<'a> { AppPublic { inner: (&mut machine.inner).into(), state: AppState::Info(()), - input: (&machine.input.as_ref()).map(Into::into), } } } diff --git a/src/tui/app/machine/input.rs b/src/tui/app/machine/input.rs index ee5ea9d..5cd5a86 100644 --- a/src/tui/app/machine/input.rs +++ b/src/tui/app/machine/input.rs @@ -1,9 +1,8 @@ use tui_input::backend::crossterm::EventHandler; -use crate::tui::app::{ - machine::{App, AppMachine}, - IAppInput, InputEvent, InputPublic, -}; +use crate::tui::app::{machine::App, AppState, IAppInput, InputEvent, InputPublic}; + +use super::AppInputMode; #[derive(Default)] pub struct Input(tui_input::Input); @@ -14,23 +13,15 @@ impl<'app> From<&'app Input> for InputPublic<'app> { } } -impl IAppInput for AppMachine -where - AppMachine: Into, -{ +impl IAppInput for AppInputMode { type APP = App; - fn taking_input(&self) -> bool { - self.input.is_some() - } - fn input(mut self, input: InputEvent) -> Self::APP { self.input - .as_mut() - .unwrap() // FIXME .0 .handle_event(&crossterm::event::Event::Key(input)); - self.into() + self.app.inner_mut().input.replace(self.input); + self.app } fn confirm(self) -> Self::APP { @@ -38,7 +29,10 @@ where } fn cancel(mut self) -> Self::APP { - self.input = None; - self.into() + match &mut self.app { + AppState::Match(state) => state.submit_input(self.input), + _ => {} + } + self.app } } diff --git a/src/tui/app/machine/match_state.rs b/src/tui/app/machine/match_state.rs index 2ab81d2..ff67f06 100644 --- a/src/tui/app/machine/match_state.rs +++ b/src/tui/app/machine/match_state.rs @@ -98,12 +98,10 @@ impl MatchState { impl AppMachine { pub fn match_state(inner: AppInner, state: MatchState) -> Self { - AppMachine { - inner, - state, - input: None, - } + AppMachine { inner, state } } + + pub fn submit_input(&mut self, _input: Input) {} } impl From> for App { @@ -126,7 +124,6 @@ impl<'a> From<&'a mut AppMachine> for AppPublic<'a> { AppPublic { inner: (&mut machine.inner).into(), state: AppState::Match((&mut machine.state).into()), - input: (&machine.input.as_ref()).map(Into::into), } } } @@ -167,7 +164,7 @@ impl IAppInteractMatch for AppMachine { .unwrap() .is_manual_input_mbid(index) { - self.input = Some(Input::default()); + self.inner.input = Some(Input::default()); return self.into(); } } diff --git a/src/tui/app/machine/mod.rs b/src/tui/app/machine/mod.rs index 5598c76..8e89a8e 100644 --- a/src/tui/app/machine/mod.rs +++ b/src/tui/app/machine/mod.rs @@ -21,13 +21,12 @@ use critical_state::CriticalState; use error_state::ErrorState; use fetch_state::FetchState; use info_state::InfoState; +use input::Input; use match_state::MatchState; use reload_state::ReloadState; use search_state::SearchState; -use input::Input; - -use super::IAppBase; +use super::{AppMode, IAppBase}; pub type App = AppState< AppMachine, @@ -43,7 +42,6 @@ pub type App = AppState< pub struct AppMachine { inner: AppInner, state: STATE, - input: Option, } pub struct AppInner { @@ -52,6 +50,18 @@ pub struct AppInner { musicbrainz: Arc>, selection: Selection, events: EventSender, + input: Option, +} + +pub struct AppInputMode { + input: Input, + app: App, +} + +impl AppInputMode { + fn new(input: Input, app: App) -> Self { + AppInputMode { input, app } + } } impl App { @@ -109,6 +119,7 @@ impl IApp for App { type MatchState = AppMachine; type ErrorState = AppMachine; type CriticalState = AppMachine; + type InputMode = AppInputMode; fn is_running(&self) -> bool { self.inner_ref().running @@ -133,6 +144,28 @@ impl IApp for App { > { self } + + fn mode( + mut self, + ) -> super::AppMode< + AppState< + Self::BrowseState, + Self::InfoState, + Self::ReloadState, + Self::SearchState, + Self::FetchState, + Self::MatchState, + Self::ErrorState, + Self::CriticalState, + >, + Self::InputMode, + > { + if let Some(input) = self.inner_mut().input.take() { + AppMode::Input(AppInputMode::new(input, self.state())) + } else { + AppMode::Browse(self.state()) + } + } } impl> IAppBase for T { @@ -171,6 +204,7 @@ impl AppInner { musicbrainz: Arc::new(Mutex::new(musicbrainz)), selection, events, + input: None, } } } @@ -180,6 +214,7 @@ impl<'a> From<&'a mut AppInner> for AppPublicInner<'a> { AppPublicInner { collection: inner.music_hoard.get_collection(), selection: &mut inner.selection, + input: inner.input.as_ref().map(Into::into), } } } @@ -408,12 +443,7 @@ mod tests { let (_, rx) = mpsc::channel(); let inner = app.unwrap_browse().inner; let state = FetchState::new(rx); - app = AppMachine { - inner, - state, - input: None, - } - .into(); + app = AppMachine { inner, state }.into(); let state = app.state(); assert!(matches!(state, AppState::Fetch(_))); diff --git a/src/tui/app/machine/reload_state.rs b/src/tui/app/machine/reload_state.rs index ad45f40..4c9323f 100644 --- a/src/tui/app/machine/reload_state.rs +++ b/src/tui/app/machine/reload_state.rs @@ -11,7 +11,6 @@ impl AppMachine { AppMachine { inner, state: ReloadState, - input: None, } } } @@ -26,7 +25,6 @@ impl<'a> From<&'a mut AppMachine> for AppPublic<'a> { AppPublic { inner: (&mut machine.inner).into(), state: AppState::Reload(()), - input: (&machine.input.as_ref()).map(Into::into), } } } diff --git a/src/tui/app/machine/search_state.rs b/src/tui/app/machine/search_state.rs index 9185e48..a658dd1 100644 --- a/src/tui/app/machine/search_state.rs +++ b/src/tui/app/machine/search_state.rs @@ -40,7 +40,6 @@ impl AppMachine { orig, memo: vec![], }, - input: None, } } } @@ -56,7 +55,6 @@ impl<'a> From<&'a mut AppMachine> for AppPublic<'a> { AppPublic { inner: (&mut machine.inner).into(), state: AppState::Search(&machine.state.string), - input: (&machine.input.as_ref()).map(Into::into), } } } diff --git a/src/tui/app/mod.rs b/src/tui/app/mod.rs index 754c089..2cefc08 100644 --- a/src/tui/app/mod.rs +++ b/src/tui/app/mod.rs @@ -28,6 +28,11 @@ pub enum AppState< Critical(CriticalState), } +pub enum AppMode { + Browse(BrowseMode), + Input(InputMode), +} + pub trait IApp { type BrowseState: IAppBase + IAppInteractBrowse; type InfoState: IAppBase + IAppInteractInfo; @@ -36,9 +41,10 @@ pub trait IApp { type FetchState: IAppBase + IAppInteractFetch + IAppEventFetch; - type MatchState: IAppBase + IAppInteractMatch + IAppInput; + type MatchState: IAppBase + IAppInteractMatch; type ErrorState: IAppBase + IAppInteractError; type CriticalState: IAppBase; + type InputMode: IAppInput; fn is_running(&self) -> bool; fn force_quit(self) -> Self; @@ -56,6 +62,23 @@ pub trait IApp { Self::ErrorState, Self::CriticalState, >; + + #[allow(clippy::type_complexity)] + fn mode( + self, + ) -> AppMode< + AppState< + Self::BrowseState, + Self::InfoState, + Self::ReloadState, + Self::SearchState, + Self::FetchState, + Self::MatchState, + Self::ErrorState, + Self::CriticalState, + >, + Self::InputMode, + >; } pub trait IAppBase { @@ -133,8 +156,6 @@ type InputEvent = crossterm::event::KeyEvent; pub trait IAppInput { type APP: IApp; - fn taking_input(&self) -> bool; - fn input(self, input: InputEvent) -> Self::APP; fn confirm(self) -> Self::APP; fn cancel(self) -> Self::APP; @@ -157,14 +178,16 @@ pub trait IAppAccess { pub struct AppPublic<'app> { pub inner: AppPublicInner<'app>, pub state: AppPublicState<'app>, - pub input: AppPublicInput<'app>, } pub struct AppPublicInner<'app> { pub collection: &'app Collection, pub selection: &'app mut Selection, + pub input: Option>, } +pub type InputPublic<'app> = &'app tui_input::Input; + #[derive(Clone, Debug, PartialEq, Eq)] pub enum MatchOption { Match(Match), @@ -242,9 +265,6 @@ impl< } } -pub type InputPublic<'app> = &'app tui_input::Input; -pub type AppPublicInput<'app> = Option>; - #[cfg(test)] mod tests { use super::*; diff --git a/src/tui/handler.rs b/src/tui/handler.rs index a112b7d..ef0b5fc 100644 --- a/src/tui/handler.rs +++ b/src/tui/handler.rs @@ -11,7 +11,7 @@ use crate::tui::{ event::{Event, EventError, EventReceiver}, }; -use super::app::{IAppBase, IAppEventFetch, IAppInput}; +use super::app::{AppMode, IAppBase, IAppEventFetch, IAppInput}; #[cfg_attr(test, automock)] pub trait IEventHandler { @@ -64,29 +64,32 @@ impl IEventHandlerPrivate for EventHandler { }; } - match app.state() { - AppState::Browse(browse_state) => { - Self::handle_browse_key_event(browse_state, key_event) - } - AppState::Info(info_state) => Self::handle_info_key_event(info_state, key_event), - AppState::Reload(reload_state) => { - Self::handle_reload_key_event(reload_state, key_event) - } - AppState::Search(search_state) => { - Self::handle_search_key_event(search_state, key_event) - } - AppState::Fetch(fetch_state) => Self::handle_fetch_key_event(fetch_state, key_event), - AppState::Match(match_state) => { - if match_state.taking_input() { - Self::handle_input_key_event(match_state, key_event) - } else { + match app.mode() { + AppMode::Input(input_mode) => Self::handle_input_key_event(input_mode, key_event), + AppMode::Browse(browse_mode) => match browse_mode { + AppState::Browse(browse_state) => { + Self::handle_browse_key_event(browse_state, key_event) + } + AppState::Info(info_state) => Self::handle_info_key_event(info_state, key_event), + AppState::Reload(reload_state) => { + Self::handle_reload_key_event(reload_state, key_event) + } + AppState::Search(search_state) => { + Self::handle_search_key_event(search_state, key_event) + } + AppState::Fetch(fetch_state) => { + Self::handle_fetch_key_event(fetch_state, key_event) + } + AppState::Match(match_state) => { Self::handle_match_key_event(match_state, key_event) } - } - AppState::Error(error_state) => Self::handle_error_key_event(error_state, key_event), - AppState::Critical(critical_state) => { - Self::handle_critical_key_event(critical_state, key_event) - } + AppState::Error(error_state) => { + Self::handle_error_key_event(error_state, key_event) + } + AppState::Critical(critical_state) => { + Self::handle_critical_key_event(critical_state, key_event) + } + }, } } @@ -187,7 +190,7 @@ impl IEventHandlerPrivate for EventHandler { return match key_event.code { KeyCode::Char('g') | KeyCode::Char('G') => app.abort(), _ => app.no_op(), - } + }; } match key_event.code { @@ -203,7 +206,7 @@ impl IEventHandlerPrivate for EventHandler { return match key_event.code { KeyCode::Char('g') | KeyCode::Char('G') => app.abort(), _ => app.no_op(), - } + }; } match key_event.code { diff --git a/src/tui/ui/mod.rs b/src/tui/ui/mod.rs index 0d14dfb..76f1b65 100644 --- a/src/tui/ui/mod.rs +++ b/src/tui/ui/mod.rs @@ -195,7 +195,7 @@ impl IUi for Ui { _ => {} } - if let Some(input) = app.input { + if let Some(input) = app.inner.input { Self::render_input_overlay(input, frame); } } @@ -224,6 +224,7 @@ mod tests { inner: AppPublicInner { collection: self.inner.collection, selection: self.inner.selection, + input: self.inner.input, }, state: match self.state { AppState::Browse(()) => AppState::Browse(()), @@ -238,7 +239,6 @@ mod tests { AppState::Error(s) => AppState::Error(s), AppState::Critical(s) => AppState::Critical(s), }, - input: self.input, } } } @@ -250,6 +250,7 @@ mod tests { AppPublicInner { collection, selection, + input: None, } } @@ -271,7 +272,6 @@ mod tests { let mut app = AppPublic { inner: public_inner(collection, selection), state: AppState::Browse(()), - input: None, }; terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap(); @@ -350,7 +350,6 @@ mod tests { info: None, state: &mut widget_state, }), - input: None, }; terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap(); } @@ -380,7 +379,6 @@ mod tests { info: Some(&artist_matches), state: &mut widget_state, }), - input: None, }; terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap(); } @@ -415,7 +413,6 @@ mod tests { info: Some(&album_matches), state: &mut widget_state, }), - input: None, }; terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap(); }