From 66b273bef4303f64f0647d15b96e30e05dd19ada Mon Sep 17 00:00:00 2001 From: Wojciech Kozlowski Date: Sun, 29 Sep 2024 21:17:17 +0200 Subject: [PATCH] Paging of match page --- src/tui/app/machine/browse_state.rs | 4 +-- src/tui/app/machine/match_state.rs | 40 ++++++++++++++-------------- src/tui/app/mod.rs | 41 ++++++++++++++++++++++++++--- src/tui/app/selection/album.rs | 8 +++--- src/tui/app/selection/artist.rs | 8 +++--- src/tui/app/selection/mod.rs | 39 +++------------------------ src/tui/app/selection/track.rs | 2 +- src/tui/handler.rs | 6 +++-- 8 files changed, 79 insertions(+), 69 deletions(-) diff --git a/src/tui/app/machine/browse_state.rs b/src/tui/app/machine/browse_state.rs index a215740..95c15e0 100644 --- a/src/tui/app/machine/browse_state.rs +++ b/src/tui/app/machine/browse_state.rs @@ -1,7 +1,7 @@ use crate::tui::app::{ machine::{App, AppInner, AppMachine}, - selection::{Delta, ListSelection}, - AppPublicState, AppState, IAppInteractBrowse, + selection::ListSelection, + AppPublicState, AppState, IAppInteractBrowse, Delta }; pub struct BrowseState; diff --git a/src/tui/app/machine/match_state.rs b/src/tui/app/machine/match_state.rs index de68e30..19e0b3c 100644 --- a/src/tui/app/machine/match_state.rs +++ b/src/tui/app/machine/match_state.rs @@ -9,8 +9,8 @@ use musichoard::collection::{ use crate::tui::{ app::{ machine::{fetch_state::FetchState, input::Input, App, AppInner, AppMachine}, - AlbumMatches, AppPublicState, AppState, ArtistMatches, IAppInteractMatch, ListOption, - MatchOption, MatchStateInfo, MatchStatePublic, WidgetState, + AlbumMatches, AppPublicState, AppState, ArtistMatches, Delta, IAppInteractMatch, + ListOption, MatchOption, MatchStateInfo, MatchStatePublic, WidgetState, }, lib::interface::musicbrainz::api::{Lookup, Match}, }; @@ -243,19 +243,19 @@ impl<'a> From<&'a mut MatchState> for AppPublicState<'a> { impl IAppInteractMatch for AppMachine { type APP = App; - fn prev_match(mut self) -> Self::APP { + fn decrement_match(mut self, delta: Delta) -> Self::APP { if let Some(index) = self.state.state.list.selected() { - let result = index.saturating_sub(1); + let result = index.saturating_sub(delta.as_usize(&self.state.state)); self.state.state.list.select(Some(result)); } self.into() } - fn next_match(mut self) -> Self::APP { + fn increment_match(mut self, delta: Delta) -> Self::APP { let index = self.state.state.list.selected().unwrap(); let to = cmp::min( - index.saturating_add(1), + index.saturating_add(delta.as_usize(&self.state.state)), self.state.current.len().saturating_sub(1), ); self.state.state.list.select(Some(to)); @@ -473,38 +473,38 @@ mod tests { assert_eq!(matches.state.current, matches_info); assert_eq!(matches.state.state, widget_state); - let matches = matches.prev_match().unwrap_match(); + let matches = matches.decrement_match(Delta::Line).unwrap_match(); assert_eq!(matches.state.current, matches_info); assert_eq!(matches.state.state.list.selected(), Some(0)); let mut matches = matches; for ii in 1..len { - matches = matches.next_match().unwrap_match(); + matches = matches.increment_match(Delta::Line).unwrap_match(); assert_eq!(matches.state.current, matches_info); assert_eq!(matches.state.state.list.selected(), Some(ii)); } // Next is CannotHaveMBID - let matches = matches.next_match().unwrap_match(); + let matches = matches.increment_match(Delta::Line).unwrap_match(); assert_eq!(matches.state.current, matches_info); assert_eq!(matches.state.state.list.selected(), Some(len)); // Next is ManualInputMbid - let matches = matches.next_match().unwrap_match(); + let matches = matches.increment_match(Delta::Line).unwrap_match(); assert_eq!(matches.state.current, matches_info); assert_eq!(matches.state.state.list.selected(), Some(len + 1)); - let matches = matches.next_match().unwrap_match(); + let matches = matches.increment_match(Delta::Line).unwrap_match(); assert_eq!(matches.state.current, matches_info); assert_eq!(matches.state.state.list.selected(), Some(len + 1)); // Go prev_match first as selecting on manual input does not go back to fetch. - let matches = matches.prev_match().unwrap_match(); + let matches = matches.decrement_match(Delta::Line).unwrap_match(); matches.select().unwrap_fetch(); } @@ -619,10 +619,10 @@ mod tests { AppMachine::match_state(inner(music_hoard(vec![])), match_state(album_match())); // album_match has two matches which means that the fourth option should be manual input. - let matches = matches.next_match().unwrap_match(); - let matches = matches.next_match().unwrap_match(); - let matches = matches.next_match().unwrap_match(); - let matches = matches.next_match().unwrap_match(); + let matches = matches.increment_match(Delta::Line).unwrap_match(); + let matches = matches.increment_match(Delta::Line).unwrap_match(); + let matches = matches.increment_match(Delta::Line).unwrap_match(); + let matches = matches.increment_match(Delta::Line).unwrap_match(); let app = matches.select(); @@ -657,8 +657,8 @@ mod tests { ); // There are no matches which means that the second option should be manual input. - let matches = matches.next_match().unwrap_match(); - let matches = matches.next_match().unwrap_match(); + let matches = matches.increment_match(Delta::Line).unwrap_match(); + let matches = matches.increment_match(Delta::Line).unwrap_match(); let mut app = matches.select(); app = input_mbid(app); @@ -691,8 +691,8 @@ mod tests { ); // There are no matches which means that the second option should be manual input. - let matches = matches.next_match().unwrap_match(); - let matches = matches.next_match().unwrap_match(); + let matches = matches.increment_match(Delta::Line).unwrap_match(); + let matches = matches.increment_match(Delta::Line).unwrap_match(); let mut app = matches.select(); app = input_mbid(app); diff --git a/src/tui/app/mod.rs b/src/tui/app/mod.rs index a44cc28..e7bd921 100644 --- a/src/tui/app/mod.rs +++ b/src/tui/app/mod.rs @@ -2,7 +2,8 @@ mod machine; mod selection; pub use machine::App; -pub use selection::{Category, Delta, Selection, WidgetState}; +use ratatui::widgets::ListState; +pub use selection::{Category, Selection}; use musichoard::collection::{ album::AlbumMeta, @@ -124,8 +125,8 @@ pub trait IAppEventFetch { pub trait IAppInteractMatch { type APP: IApp; - fn prev_match(self) -> Self::APP; - fn next_match(self) -> Self::APP; + fn decrement_match(self, delta: Delta) -> Self::APP; + fn increment_match(self, delta: Delta) -> Self::APP; fn select(self) -> Self::APP; fn abort(self) -> Self::APP; @@ -159,6 +160,40 @@ pub trait IAppInteractError { fn dismiss_error(self) -> Self::APP; } +#[derive(Clone, Debug, Default)] +pub struct WidgetState { + pub list: ListState, + pub height: usize, +} + +impl PartialEq for WidgetState { + fn eq(&self, other: &Self) -> bool { + self.list.selected().eq(&other.list.selected()) && self.height.eq(&other.height) + } +} + +impl WidgetState { + #[must_use] + pub const fn with_selected(mut self, selected: Option) -> Self { + self.list = self.list.with_selected(selected); + self + } +} + +pub enum Delta { + Line, + Page, +} + +impl Delta { + fn as_usize(&self, state: &WidgetState) -> usize { + match self { + Delta::Line => 1, + Delta::Page => state.height.saturating_sub(1), + } + } +} + // It would be preferable to have a getter for each field separately. However, the selection field // needs to be mutably accessible requiring a mutable borrow of the entire struct if behind a trait. // This in turn complicates simultaneous field access since only a single mutable borrow is allowed. diff --git a/src/tui/app/selection/album.rs b/src/tui/app/selection/album.rs index 2f89967..15cd1da 100644 --- a/src/tui/app/selection/album.rs +++ b/src/tui/app/selection/album.rs @@ -5,9 +5,11 @@ use musichoard::collection::{ track::Track, }; -use crate::tui::app::selection::{ - track::{KeySelectTrack, TrackSelection}, - Delta, SelectionState, WidgetState, +use crate::tui::app::{ + selection::{ + track::{KeySelectTrack, TrackSelection}, + SelectionState, + }, Delta, WidgetState }; #[derive(Clone, Debug, PartialEq)] diff --git a/src/tui/app/selection/artist.rs b/src/tui/app/selection/artist.rs index 045455a..110bd66 100644 --- a/src/tui/app/selection/artist.rs +++ b/src/tui/app/selection/artist.rs @@ -6,9 +6,11 @@ use musichoard::collection::{ track::Track, }; -use crate::tui::app::selection::{ - album::{AlbumSelection, KeySelectAlbum}, - Delta, SelectionState, WidgetState, +use crate::tui::app::{ + selection::{ + album::{AlbumSelection, KeySelectAlbum}, + SelectionState, + }, Delta, WidgetState }; #[derive(Clone, Debug, PartialEq)] diff --git a/src/tui/app/selection/mod.rs b/src/tui/app/selection/mod.rs index da31713..947db24 100644 --- a/src/tui/app/selection/mod.rs +++ b/src/tui/app/selection/mod.rs @@ -5,7 +5,10 @@ mod track; use musichoard::collection::{album::Album, artist::Artist, track::Track, Collection}; use ratatui::widgets::ListState; -use artist::{ArtistSelection, KeySelectArtist}; +use crate::tui::app::{ + selection::artist::{ArtistSelection, KeySelectArtist}, + Delta, WidgetState, +}; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Category { @@ -24,40 +27,6 @@ pub struct SelectionState<'a, T> { pub index: usize, } -#[derive(Clone, Debug, Default)] -pub struct WidgetState { - pub list: ListState, - pub height: usize, -} - -impl PartialEq for WidgetState { - fn eq(&self, other: &Self) -> bool { - self.list.selected().eq(&other.list.selected()) && self.height.eq(&other.height) - } -} - -impl WidgetState { - #[must_use] - pub const fn with_selected(mut self, selected: Option) -> Self { - self.list = self.list.with_selected(selected); - self - } -} - -pub enum Delta { - Line, - Page, -} - -impl Delta { - fn as_usize(&self, state: &WidgetState) -> usize { - match self { - Delta::Line => 1, - Delta::Page => state.height.saturating_sub(1), - } - } -} - impl Selection { pub fn new(artists: &[Artist]) -> Self { Selection { diff --git a/src/tui/app/selection/track.rs b/src/tui/app/selection/track.rs index adec55b..1fa18d5 100644 --- a/src/tui/app/selection/track.rs +++ b/src/tui/app/selection/track.rs @@ -2,7 +2,7 @@ use std::cmp; use musichoard::collection::track::{Track, TrackId, TrackNum}; -use crate::tui::app::selection::{Delta, SelectionState, WidgetState}; +use crate::tui::app::{selection::SelectionState, Delta, WidgetState}; #[derive(Clone, Debug, PartialEq)] pub struct TrackSelection { diff --git a/src/tui/handler.rs b/src/tui/handler.rs index cf04371..f046da3 100644 --- a/src/tui/handler.rs +++ b/src/tui/handler.rs @@ -212,8 +212,10 @@ impl IEventHandlerPrivate for EventHandler { // Abort. KeyCode::Esc | KeyCode::Char('q') | KeyCode::Char('Q') => app.abort(), // Select. - KeyCode::Up => app.prev_match(), - KeyCode::Down => app.next_match(), + KeyCode::Up => app.decrement_match(Delta::Line), + KeyCode::Down => app.increment_match(Delta::Line), + KeyCode::PageUp => app.decrement_match(Delta::Page), + KeyCode::PageDown => app.increment_match(Delta::Page), KeyCode::Enter => app.select(), // Othey keys. _ => app.no_op(),