Extend incremental search to albums and tracks #152
@ -6,7 +6,7 @@ use musichoard::collection::{album::Album, artist::Artist, track::Track};
|
||||
use crate::tui::{
|
||||
app::{
|
||||
machine::{App, AppInner, AppMachine},
|
||||
selection::ListSelection,
|
||||
selection::{ListSelection, SelectionState},
|
||||
AppPublic, AppState, Category, IAppInteractSearch,
|
||||
},
|
||||
lib::IMusicHoard,
|
||||
@ -109,9 +109,14 @@ impl<MH: IMusicHoard> IAppInteractSearch for AppMachine<MH, AppSearch> {
|
||||
|
||||
trait IAppInteractSearchPrivate {
|
||||
fn incremental_search(&mut self, next: bool);
|
||||
fn search_category<T, P>(list: &[T], index: usize, next: bool, pred: P) -> Option<usize>
|
||||
fn search_category<T, P>(
|
||||
state: SelectionState<'_, T>,
|
||||
search_name: &str,
|
||||
next: bool,
|
||||
predicate: P,
|
||||
) -> Option<usize>
|
||||
where
|
||||
P: FnMut(&T) -> bool;
|
||||
P: FnMut(bool, bool, &str, &T) -> bool;
|
||||
fn predicate_artists(case_sens: bool, char_sens: bool, search: &str, probe: &Artist) -> bool;
|
||||
fn predicate_albums(case_sens: bool, char_sens: bool, search: &str, probe: &Album) -> bool;
|
||||
fn predicate_tracks(case_sens: bool, char_sens: bool, search: &str, probe: &Track) -> bool;
|
||||
@ -125,73 +130,49 @@ trait IAppInteractSearchPrivate {
|
||||
impl<MH: IMusicHoard> IAppInteractSearchPrivate for AppMachine<MH, AppSearch> {
|
||||
fn incremental_search(&mut self, next: bool) {
|
||||
let collection = self.inner.music_hoard.get_collection();
|
||||
let search_name = &self.state.string;
|
||||
let search = &self.state.string;
|
||||
|
||||
let sel = &mut self.inner.selection;
|
||||
if let Some(index) = sel.selected() {
|
||||
let case_sensitive = Self::is_case_sensitive(search_name);
|
||||
let char_sensitive = Self::is_char_sensitive(search_name);
|
||||
let search = Self::normalize_search(search_name, !case_sensitive, !char_sensitive);
|
||||
|
||||
let result = match sel.active {
|
||||
Category::Artist => {
|
||||
let artists = collection;
|
||||
Self::search_category(artists, index, next, |probe| {
|
||||
Self::predicate_artists(case_sensitive, char_sensitive, &search, probe)
|
||||
})
|
||||
}
|
||||
Category::Album => {
|
||||
let artists = collection;
|
||||
let albums = sel
|
||||
.artist
|
||||
.state
|
||||
.list
|
||||
.selected()
|
||||
.map(|i| &artists[i].albums)
|
||||
.unwrap();
|
||||
Self::search_category(albums, index, next, |probe| {
|
||||
Self::predicate_albums(case_sensitive, char_sensitive, &search, probe)
|
||||
})
|
||||
}
|
||||
Category::Track => {
|
||||
let artists = collection;
|
||||
let albums = sel
|
||||
.artist
|
||||
.state
|
||||
.list
|
||||
.selected()
|
||||
.map(|i| &artists[i].albums)
|
||||
.unwrap();
|
||||
let tracks = sel
|
||||
.artist
|
||||
.album
|
||||
.state
|
||||
.list
|
||||
.selected()
|
||||
.map(|i| &albums[i].tracks)
|
||||
.unwrap();
|
||||
Self::search_category(tracks, index, next, |probe| {
|
||||
Self::predicate_tracks(case_sensitive, char_sensitive, &search, probe)
|
||||
})
|
||||
}
|
||||
Category::Artist => sel.state_artist(collection).and_then(|state| {
|
||||
Self::search_category(state, search, next, Self::predicate_artists)
|
||||
}),
|
||||
Category::Album => sel.state_album(collection).and_then(|state| {
|
||||
Self::search_category(state, search, next, Self::predicate_albums)
|
||||
}),
|
||||
Category::Track => sel.state_track(collection).and_then(|state| {
|
||||
Self::search_category(state, search, next, Self::predicate_tracks)
|
||||
}),
|
||||
};
|
||||
|
||||
if result.is_some() {
|
||||
sel.select(collection, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn search_category<T, P>(list: &[T], mut index: usize, next: bool, pred: P) -> Option<usize>
|
||||
fn search_category<T, P>(
|
||||
state: SelectionState<'_, T>,
|
||||
search_name: &str,
|
||||
next: bool,
|
||||
mut predicate: P,
|
||||
) -> Option<usize>
|
||||
where
|
||||
P: FnMut(&T) -> bool,
|
||||
P: FnMut(bool, bool, &str, &T) -> bool,
|
||||
{
|
||||
if next && ((index + 1) < list.len()) {
|
||||
let case_sens = Self::is_case_sensitive(search_name);
|
||||
let char_sens = Self::is_char_sensitive(search_name);
|
||||
let search = Self::normalize_search(search_name, !case_sens, !char_sens);
|
||||
|
||||
let mut index = state.index;
|
||||
if next && ((index + 1) < state.list.len()) {
|
||||
index += 1;
|
||||
}
|
||||
let slice = &list[index..];
|
||||
let result = slice.iter().position(pred);
|
||||
result.map(|slice_index| index + slice_index)
|
||||
|
||||
let slice = &state.list[index..];
|
||||
slice
|
||||
.iter()
|
||||
.position(|probe| predicate(case_sens, char_sens, &search, probe))
|
||||
.map(|slice_index| index + slice_index)
|
||||
}
|
||||
|
||||
fn predicate_artists(case_sens: bool, char_sens: bool, search: &str, probe: &Artist) -> bool {
|
||||
|
@ -62,6 +62,11 @@ impl Delta {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SelectionState<'a, T> {
|
||||
pub list: &'a [T],
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
impl Selection {
|
||||
pub fn new(artists: &[Artist]) -> Self {
|
||||
Selection {
|
||||
@ -136,6 +141,18 @@ impl Selection {
|
||||
self.artist.selected_track()
|
||||
}
|
||||
|
||||
pub fn state_artist<'a>(&self, coll: &'a Collection) -> Option<SelectionState<'a, Artist>> {
|
||||
self.artist.selection_state(coll)
|
||||
}
|
||||
|
||||
pub fn state_album<'a>(&self, coll: &'a Collection) -> Option<SelectionState<'a, Album>> {
|
||||
self.artist.state_album(coll)
|
||||
}
|
||||
|
||||
pub fn state_track<'a>(&self, coll: &'a Collection) -> Option<SelectionState<'a, Track>> {
|
||||
self.artist.state_track(coll)
|
||||
}
|
||||
|
||||
pub fn reset(&mut self, collection: &Collection) {
|
||||
match self.active {
|
||||
Category::Artist => self.reset_artist(collection),
|
||||
@ -278,6 +295,21 @@ impl ArtistSelection {
|
||||
}
|
||||
}
|
||||
|
||||
fn selection_state<'a>(&self, list: &'a [Artist]) -> Option<SelectionState<'a, Artist>> {
|
||||
let selected = self.state.list.selected();
|
||||
selected.map(|index| SelectionState { list, index })
|
||||
}
|
||||
|
||||
fn state_album<'a>(&self, artists: &'a [Artist]) -> Option<SelectionState<'a, Album>> {
|
||||
let selected = self.state.list.selected();
|
||||
selected.and_then(|index| self.album.selection_state(&artists[index].albums))
|
||||
}
|
||||
|
||||
fn state_track<'a>(&self, artists: &'a [Artist]) -> Option<SelectionState<'a, Track>> {
|
||||
let selected = self.state.list.selected();
|
||||
selected.and_then(|index| self.album.state_tracks(&artists[index].albums))
|
||||
}
|
||||
|
||||
fn reset(&mut self, artists: &[Artist]) {
|
||||
if self.state.list.selected() != Some(0) {
|
||||
self.reinitialise(artists, None);
|
||||
@ -416,6 +448,16 @@ impl AlbumSelection {
|
||||
}
|
||||
}
|
||||
|
||||
fn selection_state<'a>(&self, list: &'a [Album]) -> Option<SelectionState<'a, Album>> {
|
||||
let selected = self.state.list.selected();
|
||||
selected.map(|index| SelectionState { list, index })
|
||||
}
|
||||
|
||||
fn state_tracks<'a>(&self, albums: &'a [Album]) -> Option<SelectionState<'a, Track>> {
|
||||
let selected = self.state.list.selected();
|
||||
selected.and_then(|index| self.track.selection_state(&albums[index].tracks))
|
||||
}
|
||||
|
||||
fn reset(&mut self, albums: &[Album]) {
|
||||
if self.state.list.selected() != Some(0) {
|
||||
self.reinitialise(albums, None);
|
||||
@ -518,6 +560,11 @@ impl TrackSelection {
|
||||
self.state.list.select(Some(to));
|
||||
}
|
||||
|
||||
fn selection_state<'a>(&self, list: &'a [Track]) -> Option<SelectionState<'a, Track>> {
|
||||
let selected = self.state.list.selected();
|
||||
selected.map(|index| SelectionState { list, index })
|
||||
}
|
||||
|
||||
fn reset(&mut self, tracks: &[Track]) {
|
||||
if self.state.list.selected() != Some(0) {
|
||||
self.reinitialise(tracks, None);
|
||||
|
Loading…
Reference in New Issue
Block a user