Save entire list state when beginning search

This commit is contained in:
Wojciech Kozlowski 2024-02-13 07:11:48 +01:00
parent 5b7263d5d8
commit 9cf29b99b8
2 changed files with 73 additions and 57 deletions

View File

@ -3,7 +3,7 @@
use musichoard::collection::Collection;
use crate::tui::{
app::selection::{ActiveSelection, Delta, Selection},
app::selection::{Delta, IdSelection, Selection, ListSelection},
lib::IMusicHoard,
};
@ -143,7 +143,7 @@ pub struct App<MH: IMusicHoard> {
state: AppState<(), (), (), String, String, String>,
// FIXME: is it possible to use a wrapper struct? - when state() is called return a wrapper
// around App<MH> which will contain App.
orig: Option<usize>,
orig: Option<ListSelection>,
memo: Vec<(Option<usize>, bool)>,
}
@ -250,8 +250,7 @@ impl<MH: IMusicHoard> IAppInteractBrowse for App<MH> {
fn begin_search(&mut self) {
assert!(self.state.is_browse());
self.state = AppState::Search(String::new());
// FIXME: this should be the entire selection - not just the artist.
self.orig = self.selection.selected_artist();
self.orig = Some(ListSelection::get(&self.selection));
self.memo = vec![];
self.selection
.reset_artist(self.music_hoard.get_collection());
@ -267,13 +266,13 @@ impl<MH: IMusicHoard> IAppInteractInfo for App<MH> {
impl<MH: IMusicHoard> IAppInteractReload for App<MH> {
fn reload_library(&mut self) {
let previous = ActiveSelection::get(self.music_hoard.get_collection(), &self.selection);
let previous = IdSelection::get(self.music_hoard.get_collection(), &self.selection);
let result = self.music_hoard.rescan_library();
self.refresh(previous, result);
}
fn reload_database(&mut self) {
let previous = ActiveSelection::get(self.music_hoard.get_collection(), &self.selection);
let previous = IdSelection::get(self.music_hoard.get_collection(), &self.selection);
let result = self.music_hoard.load_from_database();
self.refresh(previous, result);
}
@ -285,11 +284,11 @@ impl<MH: IMusicHoard> IAppInteractReload for App<MH> {
}
trait IAppInteractReloadPrivate {
fn refresh(&mut self, previous: ActiveSelection, result: Result<(), musichoard::Error>);
fn refresh(&mut self, previous: IdSelection, result: Result<(), musichoard::Error>);
}
impl<MH: IMusicHoard> IAppInteractReloadPrivate for App<MH> {
fn refresh(&mut self, previous: ActiveSelection, result: Result<(), musichoard::Error>) {
fn refresh(&mut self, previous: IdSelection, result: Result<(), musichoard::Error>) {
assert!(self.state.is_reload());
match result {
Ok(()) => {
@ -342,8 +341,7 @@ impl<MH: IMusicHoard> IAppInteractSearch for App<MH> {
fn cancel_search(&mut self) {
assert!(self.state.is_search());
let collection = self.music_hoard.get_collection();
self.selection.select_artist(collection, self.orig);
self.selection.select_by_list(self.orig.take().unwrap());
self.state = AppState::Browse(());
}
}

View File

@ -70,12 +70,14 @@ impl Selection {
}
}
pub fn select_by_id(&mut self, artists: &[Artist], selected: ActiveSelection) {
self.artist.reinitialise(artists, selected.artist);
pub fn select_by_list(&mut self, selected: ListSelection) {
self.artist.state.list = selected.artist;
self.artist.album.state.list = selected.album;
self.artist.album.track.state.list = selected.track;
}
pub fn selected_artist(&mut self) -> Option<usize> {
self.artist.state.list.selected()
pub fn select_by_id(&mut self, artists: &[Artist], selected: IdSelection) {
self.artist.reinitialise(artists, selected.artist);
}
pub fn select_artist(&mut self, artists: &[Artist], index: Option<usize>) {
@ -84,7 +86,7 @@ impl Selection {
pub fn reset_artist(&mut self, artists: &[Artist]) {
if self.artist.state.list.selected() != Some(0) {
self.select_by_id(artists, ActiveSelection { artist: None });
self.select_by_id(artists, IdSelection { artist: None });
}
}
@ -165,7 +167,7 @@ impl ArtistSelection {
selection
}
fn reinitialise(&mut self, artists: &[Artist], active: Option<ActiveArtist>) {
fn reinitialise(&mut self, artists: &[Artist], active: Option<IdSelectArtist>) {
if let Some(active) = active {
let result = artists.binary_search_by(|a| a.get_sort_key().cmp(&active.artist_id));
match result {
@ -181,7 +183,7 @@ impl ArtistSelection {
&mut self,
artists: &[Artist],
index: usize,
active_album: Option<ActiveAlbum>,
active_album: Option<IdSelectAlbum>,
) {
if artists.is_empty() {
self.state.list.select(None);
@ -359,7 +361,7 @@ impl AlbumSelection {
selection
}
fn reinitialise(&mut self, albums: &[Album], album: Option<ActiveAlbum>) {
fn reinitialise(&mut self, albums: &[Album], album: Option<IdSelectAlbum>) {
if let Some(album) = album {
let result = albums.binary_search_by(|a| a.get_sort_key().cmp(&album.album_id));
match result {
@ -375,7 +377,7 @@ impl AlbumSelection {
&mut self,
albums: &[Album],
index: usize,
active_track: Option<ActiveTrack>,
active_track: Option<IdSelectTrack>,
) {
if albums.is_empty() {
self.state.list.select(None);
@ -443,7 +445,7 @@ impl TrackSelection {
selection
}
fn reinitialise(&mut self, tracks: &[Track], track: Option<ActiveTrack>) {
fn reinitialise(&mut self, tracks: &[Track], track: Option<IdSelectTrack>) {
if let Some(track) = track {
let result = tracks.binary_search_by(|t| t.get_sort_key().cmp(&track.track_id));
match result {
@ -494,61 +496,77 @@ impl TrackSelection {
}
}
pub struct ActiveSelection {
artist: Option<ActiveArtist>,
pub struct ListSelection {
artist: ListState,
album: ListState,
track: ListState,
}
struct ActiveArtist {
artist_id: ArtistId,
album: Option<ActiveAlbum>,
}
struct ActiveAlbum {
album_id: AlbumId,
track: Option<ActiveTrack>,
}
struct ActiveTrack {
track_id: TrackId,
}
impl ActiveSelection {
pub fn get(collection: &Collection, selection: &Selection) -> Self {
ActiveSelection {
artist: ActiveArtist::get(collection, &selection.artist),
impl ListSelection {
pub fn get(selection: &Selection) -> Self {
ListSelection {
artist: selection.artist.state.list.clone(),
album: selection.artist.album.state.list.clone(),
track: selection.artist.album.track.state.list.clone(),
}
}
}
impl ActiveArtist {
pub struct IdSelection {
artist: Option<IdSelectArtist>,
}
struct IdSelectArtist {
artist_id: ArtistId,
album: Option<IdSelectAlbum>,
}
struct IdSelectAlbum {
album_id: AlbumId,
track: Option<IdSelectTrack>,
}
struct IdSelectTrack {
track_id: TrackId,
}
impl IdSelection {
pub fn get(collection: &Collection, selection: &Selection) -> Self {
IdSelection {
artist: IdSelectArtist::get(collection, &selection.artist),
}
}
}
impl IdSelectArtist {
fn get(artists: &[Artist], selection: &ArtistSelection) -> Option<Self> {
selection.state.list.selected().map(|index| {
let artist = &artists[index];
ActiveArtist {
IdSelectArtist {
artist_id: artist.get_sort_key().clone(),
album: ActiveAlbum::get(&artist.albums, &selection.album),
album: IdSelectAlbum::get(&artist.albums, &selection.album),
}
})
}
}
impl ActiveAlbum {
impl IdSelectAlbum {
fn get(albums: &[Album], selection: &AlbumSelection) -> Option<Self> {
selection.state.list.selected().map(|index| {
let album = &albums[index];
ActiveAlbum {
IdSelectAlbum {
album_id: album.get_sort_key().clone(),
track: ActiveTrack::get(&album.tracks, &selection.track),
track: IdSelectTrack::get(&album.tracks, &selection.track),
}
})
}
}
impl ActiveTrack {
impl IdSelectTrack {
fn get(tracks: &[Track], selection: &TrackSelection) -> Option<Self> {
selection.state.list.selected().map(|index| {
let track = &tracks[index];
ActiveTrack {
IdSelectTrack {
track_id: track.get_sort_key().clone(),
}
})
@ -626,20 +644,20 @@ mod tests {
// Re-initialise.
let expected = sel.clone();
let active_track = ActiveTrack::get(tracks, &sel);
let active_track = IdSelectTrack::get(tracks, &sel);
sel.reinitialise(tracks, active_track);
assert_eq!(sel, expected);
// Re-initialise out-of-bounds.
let mut expected = sel.clone();
expected.decrement(tracks, Delta::Line);
let active_track = ActiveTrack::get(tracks, &sel);
let active_track = IdSelectTrack::get(tracks, &sel);
sel.reinitialise(&tracks[..(tracks.len() - 1)], active_track);
assert_eq!(sel, expected);
// Re-initialise empty.
let expected = TrackSelection::initialise(&[]);
let active_track = ActiveTrack::get(tracks, &sel);
let active_track = IdSelectTrack::get(tracks, &sel);
sel.reinitialise(&[], active_track);
assert_eq!(sel, expected);
}
@ -748,20 +766,20 @@ mod tests {
// Re-initialise.
let expected = sel.clone();
let active_album = ActiveAlbum::get(albums, &sel);
let active_album = IdSelectAlbum::get(albums, &sel);
sel.reinitialise(albums, active_album);
assert_eq!(sel, expected);
// Re-initialise out-of-bounds.
let mut expected = sel.clone();
expected.decrement(albums, Delta::Line);
let active_album = ActiveAlbum::get(albums, &sel);
let active_album = IdSelectAlbum::get(albums, &sel);
sel.reinitialise(&albums[..(albums.len() - 1)], active_album);
assert_eq!(sel, expected);
// Re-initialise empty.
let expected = AlbumSelection::initialise(&[]);
let active_album = ActiveAlbum::get(albums, &sel);
let active_album = IdSelectAlbum::get(albums, &sel);
sel.reinitialise(&[], active_album);
assert_eq!(sel, expected);
}
@ -870,20 +888,20 @@ mod tests {
// Re-initialise.
let expected = sel.clone();
let active_artist = ActiveArtist::get(artists, &sel);
let active_artist = IdSelectArtist::get(artists, &sel);
sel.reinitialise(artists, active_artist);
assert_eq!(sel, expected);
// Re-initialise out-of-bounds.
let mut expected = sel.clone();
expected.decrement(artists, Delta::Line);
let active_artist = ActiveArtist::get(artists, &sel);
let active_artist = IdSelectArtist::get(artists, &sel);
sel.reinitialise(&artists[..(artists.len() - 1)], active_artist);
assert_eq!(sel, expected);
// Re-initialise empty.
let expected = ArtistSelection::initialise(&[]);
let active_artist = ActiveArtist::get(artists, &sel);
let active_artist = IdSelectArtist::get(artists, &sel);
sel.reinitialise(&[], active_artist);
assert_eq!(sel, expected);
}