diff --git a/src/tui/app/machine/browse_state.rs b/src/tui/app/machine/browse_state.rs index 2e71f73..f1bd64d 100644 --- a/src/tui/app/machine/browse_state.rs +++ b/src/tui/app/machine/browse_state.rs @@ -11,6 +11,7 @@ impl AppMachine { AppMachine { inner, state: BrowseState, + input: None, } } } @@ -26,6 +27,7 @@ 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 9242cbc..80ef163 100644 --- a/src/tui/app/machine/critical_state.rs +++ b/src/tui/app/machine/critical_state.rs @@ -14,6 +14,7 @@ impl AppMachine { state: CriticalState { string: string.into(), }, + input: None } } } @@ -29,6 +30,7 @@ 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 2150b2d..42a7a46 100644 --- a/src/tui/app/machine/error_state.rs +++ b/src/tui/app/machine/error_state.rs @@ -14,6 +14,7 @@ impl AppMachine { state: ErrorState { string: string.into(), }, + input: None, } } } @@ -29,6 +30,7 @@ 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 78d09eb..a16327c 100644 --- a/src/tui/app/machine/fetch_state.rs +++ b/src/tui/app/machine/fetch_state.rs @@ -40,7 +40,11 @@ pub type FetchReceiver = mpsc::Receiver; impl AppMachine { fn fetch_state(inner: AppInner, state: FetchState) -> Self { - AppMachine { inner, state } + AppMachine { + inner, + state, + input: None, + } } pub fn app_fetch_new(inner: AppInner) -> App { @@ -173,6 +177,7 @@ 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 a4e5ef6..c066bcf 100644 --- a/src/tui/app/machine/info_state.rs +++ b/src/tui/app/machine/info_state.rs @@ -10,6 +10,7 @@ impl AppMachine { AppMachine { inner, state: InfoState, + input: None, } } } @@ -25,6 +26,7 @@ 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 new file mode 100644 index 0000000..ee5ea9d --- /dev/null +++ b/src/tui/app/machine/input.rs @@ -0,0 +1,44 @@ +use tui_input::backend::crossterm::EventHandler; + +use crate::tui::app::{ + machine::{App, AppMachine}, + IAppInput, InputEvent, InputPublic, +}; + +#[derive(Default)] +pub struct Input(tui_input::Input); + +impl<'app> From<&'app Input> for InputPublic<'app> { + fn from(value: &'app Input) -> Self { + &value.0 + } +} + +impl IAppInput for AppMachine +where + AppMachine: Into, +{ + 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() + } + + fn confirm(self) -> Self::APP { + self.cancel() + } + + fn cancel(mut self) -> Self::APP { + self.input = None; + self.into() + } +} diff --git a/src/tui/app/machine/input_state.rs b/src/tui/app/machine/input_state.rs deleted file mode 100644 index 9595759..0000000 --- a/src/tui/app/machine/input_state.rs +++ /dev/null @@ -1,78 +0,0 @@ -use tui_input::{backend::crossterm::EventHandler, Input}; - -use crate::tui::app::{ - machine::{App, AppInner, AppMachine}, - AppPublic, AppState, IAppInteractInput, InputClientPublic, InputEvent, InputStatePublic, -}; - -use super::match_state::MatchState; - -pub struct InputState { - input: Input, - client: InputClient, -} - -pub enum InputClient { - Match(MatchState), -} - -impl AppMachine { - pub fn input_state(inner: AppInner, client: InputClient) -> Self { - AppMachine { - inner, - state: InputState { - input: Input::default(), - client, - }, - } - } -} - -impl From> for App { - fn from(machine: AppMachine) -> Self { - AppState::Input(machine) - } -} - -impl<'a> From<&'a mut InputClient> for InputClientPublic<'a> { - fn from(client: &'a mut InputClient) -> Self { - match client { - InputClient::Match(state) => InputClientPublic::Match(state.into()), - } - } -} - -impl<'a> From<&'a mut AppMachine> for AppPublic<'a> { - fn from(machine: &'a mut AppMachine) -> Self { - AppPublic { - inner: (&mut machine.inner).into(), - state: AppState::Input(InputStatePublic { - input: &machine.state.input, - client: (&mut machine.state.client).into(), - }), - } - } -} - -impl IAppInteractInput for AppMachine { - type APP = App; - - fn input(mut self, input: InputEvent) -> Self::APP { - self.state - .input - .handle_event(&crossterm::event::Event::Key(input)); - self.into() - } - - fn confirm(self) -> Self::APP { - match self.state.client { - InputClient::Match(state) => AppMachine::match_state(self.inner, state).into(), - } - } - - fn cancel(self) -> Self::APP { - match self.state.client { - InputClient::Match(state) => AppMachine::match_state(self.inner, state).into(), - } - } -} diff --git a/src/tui/app/machine/match_state.rs b/src/tui/app/machine/match_state.rs index 4729ede..2ab81d2 100644 --- a/src/tui/app/machine/match_state.rs +++ b/src/tui/app/machine/match_state.rs @@ -6,7 +6,7 @@ use crate::tui::app::{ MatchStateInfo, MatchStatePublic, WidgetState, }; -use super::{fetch_state::FetchState, input_state::InputClient}; +use super::{fetch_state::FetchState, input::Input}; impl ArtistMatches { fn len(&self) -> usize { @@ -98,7 +98,11 @@ impl MatchState { impl AppMachine { pub fn match_state(inner: AppInner, state: MatchState) -> Self { - AppMachine { inner, state } + AppMachine { + inner, + state, + input: None, + } } } @@ -122,6 +126,7 @@ 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), } } } @@ -152,7 +157,7 @@ impl IAppInteractMatch for AppMachine { self.into() } - fn select(self) -> Self::APP { + fn select(mut self) -> Self::APP { if let Some(index) = self.state.state.list.selected() { // selected() implies current exists if self @@ -162,7 +167,8 @@ impl IAppInteractMatch for AppMachine { .unwrap() .is_manual_input_mbid(index) { - return AppMachine::input_state(self.inner, InputClient::Match(self.state)).into(); + self.input = Some(Input::default()); + return self.into(); } } AppMachine::app_fetch_next(self.inner, self.state.fetch) diff --git a/src/tui/app/machine/mod.rs b/src/tui/app/machine/mod.rs index d6b91dd..5598c76 100644 --- a/src/tui/app/machine/mod.rs +++ b/src/tui/app/machine/mod.rs @@ -3,7 +3,7 @@ mod critical_state; mod error_state; mod fetch_state; mod info_state; -mod input_state; +mod input; mod match_state; mod reload_state; mod search_state; @@ -21,11 +21,12 @@ use critical_state::CriticalState; use error_state::ErrorState; use fetch_state::FetchState; use info_state::InfoState; -use input_state::InputState; use match_state::MatchState; use reload_state::ReloadState; use search_state::SearchState; +use input::Input; + use super::IAppBase; pub type App = AppState< @@ -35,7 +36,6 @@ pub type App = AppState< AppMachine, AppMachine, AppMachine, - AppMachine, AppMachine, AppMachine, >; @@ -43,6 +43,7 @@ pub type App = AppState< pub struct AppMachine { inner: AppInner, state: STATE, + input: Option, } pub struct AppInner { @@ -80,7 +81,6 @@ impl App { AppState::Search(search_state) => &search_state.inner, AppState::Fetch(fetch_state) => &fetch_state.inner, AppState::Match(match_state) => &match_state.inner, - AppState::Input(input_state) => &input_state.inner, AppState::Error(error_state) => &error_state.inner, AppState::Critical(critical_state) => &critical_state.inner, } @@ -94,7 +94,6 @@ impl App { AppState::Search(search_state) => &mut search_state.inner, AppState::Fetch(fetch_state) => &mut fetch_state.inner, AppState::Match(match_state) => &mut match_state.inner, - AppState::Input(input_state) => &mut input_state.inner, AppState::Error(error_state) => &mut error_state.inner, AppState::Critical(critical_state) => &mut critical_state.inner, } @@ -108,7 +107,6 @@ impl IApp for App { type SearchState = AppMachine; type FetchState = AppMachine; type MatchState = AppMachine; - type InputState = AppMachine; type ErrorState = AppMachine; type CriticalState = AppMachine; @@ -130,7 +128,6 @@ impl IApp for App { Self::SearchState, Self::FetchState, Self::MatchState, - Self::InputState, Self::ErrorState, Self::CriticalState, > { @@ -155,7 +152,6 @@ impl IAppAccess for App { AppState::Search(state) => state.into(), AppState::Fetch(state) => state.into(), AppState::Match(state) => state.into(), - AppState::Input(state) => state.into(), AppState::Error(state) => state.into(), AppState::Critical(state) => state.into(), } @@ -192,7 +188,6 @@ impl<'a> From<&'a mut AppInner> for AppPublicInner<'a> { mod tests { use std::sync::mpsc; - use input_state::InputClient; use musichoard::collection::Collection; use crate::tui::{ @@ -210,7 +205,6 @@ mod tests { SearchState, FetchState, MatchState, - InputState, ErrorState, CriticalState, > @@ -221,7 +215,6 @@ mod tests { SearchState, FetchState, MatchState, - InputState, ErrorState, CriticalState, > @@ -415,7 +408,12 @@ mod tests { let (_, rx) = mpsc::channel(); let inner = app.unwrap_browse().inner; let state = FetchState::new(rx); - app = AppMachine { inner, state }.into(); + app = AppMachine { + inner, + state, + input: None, + } + .into(); let state = app.state(); assert!(matches!(state, AppState::Fetch(_))); @@ -459,33 +457,6 @@ mod tests { assert!(!app.is_running()); } - #[test] - fn state_input() { - let mut app = App::new(music_hoard_init(vec![]), mb_api(), events()); - assert!(app.is_running()); - - let (_, rx) = mpsc::channel(); - let fetch = FetchState::new(rx); - let match_state = MatchState::new(None, fetch); - let input_client = InputClient::Match(match_state); - app = AppMachine::input_state(app.unwrap_browse().inner, input_client).into(); - - let state = app.state(); - assert!(matches!(state, AppState::Input(_))); - app = state; - - app = app.no_op(); - let state = app.state(); - assert!(matches!(state, AppState::Input(_))); - app = state; - - let public = app.get(); - assert!(matches!(public.state, AppState::Input(_))); - - app = app.force_quit(); - assert!(!app.is_running()); - } - #[test] fn state_error() { let mut app = App::new(music_hoard_init(vec![]), mb_api(), events()); diff --git a/src/tui/app/machine/reload_state.rs b/src/tui/app/machine/reload_state.rs index 4c9323f..ad45f40 100644 --- a/src/tui/app/machine/reload_state.rs +++ b/src/tui/app/machine/reload_state.rs @@ -11,6 +11,7 @@ impl AppMachine { AppMachine { inner, state: ReloadState, + input: None, } } } @@ -25,6 +26,7 @@ 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 a658dd1..9185e48 100644 --- a/src/tui/app/machine/search_state.rs +++ b/src/tui/app/machine/search_state.rs @@ -40,6 +40,7 @@ impl AppMachine { orig, memo: vec![], }, + input: None, } } } @@ -55,6 +56,7 @@ 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 85e79bc..754c089 100644 --- a/src/tui/app/mod.rs +++ b/src/tui/app/mod.rs @@ -15,7 +15,6 @@ pub enum AppState< SearchState, FetchState, MatchState, - InputState, ErrorState, CriticalState, > { @@ -25,7 +24,6 @@ pub enum AppState< Search(SearchState), Fetch(FetchState), Match(MatchState), - Input(InputState), Error(ErrorState), Critical(CriticalState), } @@ -38,8 +36,7 @@ pub trait IApp { type FetchState: IAppBase + IAppInteractFetch + IAppEventFetch; - type MatchState: IAppBase + IAppInteractMatch; - type InputState: IAppBase + IAppInteractInput; + type MatchState: IAppBase + IAppInteractMatch + IAppInput; type ErrorState: IAppBase + IAppInteractError; type CriticalState: IAppBase; @@ -56,7 +53,6 @@ pub trait IApp { Self::SearchState, Self::FetchState, Self::MatchState, - Self::InputState, Self::ErrorState, Self::CriticalState, >; @@ -134,9 +130,11 @@ pub trait IAppInteractMatch { } type InputEvent = crossterm::event::KeyEvent; -pub trait IAppInteractInput { +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; @@ -159,6 +157,7 @@ pub trait IAppAccess { pub struct AppPublic<'app> { pub inner: AppPublicInner<'app>, pub state: AppPublicState<'app>, + pub input: AppPublicInput<'app>, } pub struct AppPublicInner<'app> { @@ -214,27 +213,8 @@ pub struct MatchStatePublic<'app> { pub state: &'app mut WidgetState, } -pub enum InputClientPublic<'app> { - Match(MatchStatePublic<'app>), -} - -pub type Input = tui_input::Input; -pub struct InputStatePublic<'app> { - pub input: &'app Input, - pub client: InputClientPublic<'app> -} - -pub type AppPublicState<'app> = AppState< - (), - (), - (), - &'app str, - (), - MatchStatePublic<'app>, - InputStatePublic<'app>, - &'app str, - &'app str, ->; +pub type AppPublicState<'app> = + AppState<(), (), (), &'app str, (), MatchStatePublic<'app>, &'app str, &'app str>; impl< BrowseState, @@ -243,7 +223,6 @@ impl< SearchState, FetchState, MatchState, - InputState, ErrorState, CriticalState, > @@ -254,7 +233,6 @@ impl< SearchState, FetchState, MatchState, - InputState, ErrorState, CriticalState, > @@ -264,6 +242,9 @@ 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 e06f964..a112b7d 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, IAppInteractInput}; +use super::app::{IAppBase, IAppEventFetch, IAppInput}; #[cfg_attr(test, automock)] pub trait IEventHandler { @@ -26,11 +26,12 @@ trait IEventHandlerPrivate { fn handle_search_key_event(app: ::SearchState, key_event: KeyEvent) -> APP; fn handle_fetch_key_event(app: ::FetchState, key_event: KeyEvent) -> APP; fn handle_match_key_event(app: ::MatchState, key_event: KeyEvent) -> APP; - fn handle_input_key_event(app: ::InputState, key_event: KeyEvent) -> APP; fn handle_error_key_event(app: ::ErrorState, key_event: KeyEvent) -> APP; fn handle_critical_key_event(app: ::CriticalState, key_event: KeyEvent) -> APP; fn handle_fetch_result_ready_event(app: APP) -> APP; + + fn handle_input_key_event>(app: Input, key_event: KeyEvent) -> APP; } pub struct EventHandler { @@ -75,8 +76,13 @@ impl IEventHandlerPrivate for EventHandler { 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::Input(input_state) => Self::handle_input_key_event(input_state, key_event), + AppState::Match(match_state) => { + if match_state.taking_input() { + Self::handle_input_key_event(match_state, key_event) + } else { + 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) @@ -92,7 +98,6 @@ impl IEventHandlerPrivate for EventHandler { AppState::Search(state) => state.no_op(), AppState::Fetch(fetch_state) => fetch_state.fetch_result_ready(), AppState::Match(state) => state.no_op(), - AppState::Input(state) => state.no_op(), AppState::Error(state) => state.no_op(), AppState::Critical(state) => state.no_op(), } @@ -178,6 +183,13 @@ impl IEventHandlerPrivate for EventHandler { } fn handle_fetch_key_event(app: ::FetchState, key_event: KeyEvent) -> APP { + if key_event.modifiers == KeyModifiers::CONTROL { + return match key_event.code { + KeyCode::Char('g') | KeyCode::Char('G') => app.abort(), + _ => app.no_op(), + } + } + match key_event.code { // Abort. KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('Q') => app.abort(), @@ -187,6 +199,13 @@ impl IEventHandlerPrivate for EventHandler { } fn handle_match_key_event(app: ::MatchState, key_event: KeyEvent) -> APP { + if key_event.modifiers == KeyModifiers::CONTROL { + return match key_event.code { + KeyCode::Char('g') | KeyCode::Char('G') => app.abort(), + _ => app.no_op(), + } + } + match key_event.code { // Abort. KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('Q') => app.abort(), @@ -199,22 +218,6 @@ impl IEventHandlerPrivate for EventHandler { } } - fn handle_input_key_event(app: ::InputState, key_event: KeyEvent) -> APP { - if key_event.modifiers == KeyModifiers::CONTROL { - match key_event.code { - KeyCode::Char('g') | KeyCode::Char('G') => return app.cancel(), - _ => {}, - }; - } - - match key_event.code { - // Return. - KeyCode::Esc | KeyCode::Enter => app.confirm(), - // Othey keys. - _ => app.input(key_event), - } - } - fn handle_error_key_event(app: ::ErrorState, _key_event: KeyEvent) -> APP { // Any key dismisses the error. app.dismiss_error() @@ -224,5 +227,22 @@ impl IEventHandlerPrivate for EventHandler { // No action is allowed. app.no_op() } + + fn handle_input_key_event>(app: Input, key_event: KeyEvent) -> APP { + if key_event.modifiers == KeyModifiers::CONTROL { + match key_event.code { + KeyCode::Char('g') | KeyCode::Char('G') => return app.cancel(), + _ => {} + }; + } + + match key_event.code { + // Return. + KeyCode::Esc => app.cancel(), + KeyCode::Enter => app.confirm(), + // Othey keys. + _ => app.input(key_event), + } + } } // GRCOV_EXCL_STOP diff --git a/src/tui/ui/browse_state.rs b/src/tui/ui/browse_state.rs index 22df107..e01845c 100644 --- a/src/tui/ui/browse_state.rs +++ b/src/tui/ui/browse_state.rs @@ -28,10 +28,14 @@ pub struct TrackArea { pub info: Rect, } -pub struct FrameArea { +pub struct BrowseArea { pub artist: ArtistArea, pub album: AlbumArea, pub track: TrackArea, +} + +pub struct FrameArea { + pub browse: BrowseArea, pub minibuffer: Rect, } @@ -91,14 +95,16 @@ impl FrameArea { }; FrameArea { - artist: ArtistArea { list: artist_list }, - album: AlbumArea { - list: album_list, - info: album_info, - }, - track: TrackArea { - list: track_list, - info: track_info, + browse: BrowseArea { + artist: ArtistArea { list: artist_list }, + album: AlbumArea { + list: album_list, + info: album_info, + }, + track: TrackArea { + list: track_list, + info: track_info, + }, }, minibuffer, } diff --git a/src/tui/ui/minibuffer.rs b/src/tui/ui/minibuffer.rs index 7247b60..7db8361 100644 --- a/src/tui/ui/minibuffer.rs +++ b/src/tui/ui/minibuffer.rs @@ -57,20 +57,16 @@ impl Minibuffer<'_> { columns, }, AppState::Fetch(()) => Minibuffer { - paragraphs: vec![Paragraph::new("fetching..."), Paragraph::new("q: abort")], + paragraphs: vec![ + Paragraph::new("fetching..."), + Paragraph::new("ctrl+g: abort"), + ], columns: 2, }, AppState::Match(public) => Minibuffer { paragraphs: vec![ Paragraph::new(UiDisplay::display_matching_info(public.info)), - Paragraph::new("q: abort"), - ], - columns: 2, - }, - AppState::Input(_) => Minibuffer { - paragraphs: vec![ - Paragraph::new("enter: confirm"), - Paragraph::new("ctrl+g: cancel"), + Paragraph::new("ctrl+g: abort"), ], columns: 2, }, diff --git a/src/tui/ui/mod.rs b/src/tui/ui/mod.rs index 5bacefe..0d14dfb 100644 --- a/src/tui/ui/mod.rs +++ b/src/tui/ui/mod.rs @@ -10,6 +10,7 @@ mod reload_state; mod style; mod widgets; +use browse_state::BrowseArea; use ratatui::{layout::Rect, widgets::Paragraph, Frame}; use musichoard::collection::{album::Album, Collection}; @@ -32,7 +33,7 @@ use crate::tui::{ }, }; -use super::app::{Input, InputClientPublic}; +use super::app::InputPublic; pub trait IUi { fn render(app: &mut APP, frame: &mut Frame); @@ -66,11 +67,10 @@ impl Ui { fn render_browse_frame( artists: &Collection, selection: &mut Selection, - state: &AppPublicState, + areas: BrowseArea, frame: &mut Frame, ) { let active = selection.category(); - let areas = FrameArea::new(frame.area()); let artist_state = ArtistState::new( active == Category::Artist, @@ -103,8 +103,6 @@ impl Ui { ); Self::render_track_column(track_state, areas.track, frame); - - Self::render_minibuffer(state, areas.minibuffer, frame); } fn render_info_overlay(artists: &Collection, selection: &mut Selection, frame: &mut Frame) { @@ -149,7 +147,7 @@ impl Ui { UiWidget::render_overlay_list_widget(&st.matching, st.list, st.state, true, area, frame) } - fn render_input_overlay(input: &Input, frame: &mut Frame) { + fn render_input_overlay(input: InputPublic, frame: &mut Frame) { let area = OverlayBuilder::default() .with_width(OverlaySize::MarginFactor(4)) .with_height(OverlaySize::Value(3)) @@ -182,24 +180,24 @@ impl IUi for Ui { let selection = app.inner.selection; let state = app.state; - Self::render_browse_frame(collection, selection, &state, frame); + let areas = FrameArea::new(frame.area()); + + Self::render_browse_frame(collection, selection, areas.browse, frame); + Self::render_minibuffer(&state, areas.minibuffer, frame); + match state { AppState::Info(()) => Self::render_info_overlay(collection, selection, frame), AppState::Reload(()) => Self::render_reload_overlay(frame), AppState::Fetch(()) => Self::render_fetch_overlay(frame), AppState::Match(public) => Self::render_match_overlay(public.info, public.state, frame), - AppState::Input(input) => { - match input.client { - InputClientPublic::Match(public) => { - Self::render_match_overlay(public.info, public.state, frame) - } - } - Self::render_input_overlay(input.input, frame); - } AppState::Error(msg) => Self::render_error_overlay("Error", msg, frame), AppState::Critical(msg) => Self::render_error_overlay("Critical Error", msg, frame), _ => {} } + + if let Some(input) = app.input { + Self::render_input_overlay(input, frame); + } } } @@ -211,10 +209,7 @@ mod tests { }; use crate::tui::{ - app::{ - AppPublic, AppPublicInner, Delta, InputClientPublic, InputStatePublic, MatchOption, - MatchStatePublic, - }, + app::{AppPublic, AppPublicInner, Delta, MatchOption, MatchStatePublic}, lib::interface::musicbrainz::Match, testmod::COLLECTION, tests::terminal, @@ -240,20 +235,10 @@ mod tests { info: m.info, state: m.state, }), - AppState::Input(ref mut i) => AppState::Input(InputStatePublic { - input: i.input, - client: match i.client { - InputClientPublic::Match(ref mut m) => { - InputClientPublic::Match(MatchStatePublic { - info: m.info, - state: m.state, - }) - } - }, - }), AppState::Error(s) => AppState::Error(s), AppState::Critical(s) => AppState::Critical(s), }, + input: self.input, } } } @@ -286,6 +271,7 @@ 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(); @@ -364,6 +350,7 @@ mod tests { info: None, state: &mut widget_state, }), + input: None, }; terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap(); } @@ -393,6 +380,7 @@ mod tests { info: Some(&artist_matches), state: &mut widget_state, }), + input: None, }; terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap(); } @@ -427,6 +415,7 @@ mod tests { info: Some(&album_matches), state: &mut widget_state, }), + input: None, }; terminal.draw(|frame| Ui::render(&mut app, frame)).unwrap(); }