Working search
This commit is contained in:
parent
fd19ea3eb3
commit
a381705a1c
@ -77,7 +77,7 @@ impl<MH: IMusicHoard> IAppInteractBrowse for AppMachine<MH, AppBrowse> {
|
||||
let orig = ListSelection::get(&self.inner.selection);
|
||||
self.inner
|
||||
.selection
|
||||
.reset_artist(self.inner.music_hoard.get_collection());
|
||||
.reset(self.inner.music_hoard.get_collection());
|
||||
AppMachine::search(self.inner, orig).into()
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
use aho_corasick::AhoCorasick;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use musichoard::collection::artist::Artist;
|
||||
use musichoard::collection::{album::Album, artist::Artist, track::Track};
|
||||
|
||||
use crate::tui::{
|
||||
app::{
|
||||
machine::{App, AppInner, AppMachine},
|
||||
selection::ListSelection,
|
||||
AppPublic, AppState, IAppInteractSearch,
|
||||
AppPublic, AppState, Category, IAppInteractSearch,
|
||||
},
|
||||
lib::IMusicHoard,
|
||||
};
|
||||
@ -67,7 +67,7 @@ impl<MH: IMusicHoard> IAppInteractSearch for AppMachine<MH, AppSearch> {
|
||||
|
||||
fn append_character(mut self, ch: char) -> Self::APP {
|
||||
self.state.string.push(ch);
|
||||
let index = self.inner.selection.artist.state.list.selected();
|
||||
let index = self.inner.selection.selected();
|
||||
self.state.memo.push(AppSearchMemo { index, char: true });
|
||||
self.incremental_search(false);
|
||||
self.into()
|
||||
@ -75,7 +75,7 @@ impl<MH: IMusicHoard> IAppInteractSearch for AppMachine<MH, AppSearch> {
|
||||
|
||||
fn search_next(mut self) -> Self::APP {
|
||||
if !self.state.string.is_empty() {
|
||||
let index = self.inner.selection.artist.state.list.selected();
|
||||
let index = self.inner.selection.selected();
|
||||
self.state.memo.push(AppSearchMemo { index, char: false });
|
||||
self.incremental_search(true);
|
||||
}
|
||||
@ -88,7 +88,7 @@ impl<MH: IMusicHoard> IAppInteractSearch for AppMachine<MH, AppSearch> {
|
||||
if memo.char {
|
||||
self.state.string.pop();
|
||||
}
|
||||
self.inner.selection.select_artist(collection, memo.index);
|
||||
self.inner.selection.select(collection, memo.index);
|
||||
}
|
||||
self.into()
|
||||
}
|
||||
@ -109,12 +109,13 @@ impl<MH: IMusicHoard> IAppInteractSearch for AppMachine<MH, AppSearch> {
|
||||
|
||||
trait IAppInteractSearchPrivate {
|
||||
fn incremental_search(&mut self, next: bool);
|
||||
fn incremental_search_predicate(
|
||||
case_sensitive: bool,
|
||||
char_sensitive: bool,
|
||||
search_name: &str,
|
||||
probe: &Artist,
|
||||
) -> bool;
|
||||
fn search_category<T, P>(list: &[T], index: usize, next: bool, pred: P) -> Option<usize>
|
||||
where
|
||||
P: FnMut(&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;
|
||||
fn predicate_title(case_sens: bool, char_sens: bool, search: &str, title: &str) -> bool;
|
||||
|
||||
fn is_case_sensitive(artist_name: &str) -> bool;
|
||||
fn is_char_sensitive(artist_name: &str) -> bool;
|
||||
@ -123,50 +124,102 @@ trait IAppInteractSearchPrivate {
|
||||
|
||||
impl<MH: IMusicHoard> IAppInteractSearchPrivate for AppMachine<MH, AppSearch> {
|
||||
fn incremental_search(&mut self, next: bool) {
|
||||
let artists = self.inner.music_hoard.get_collection();
|
||||
let artist_name = &self.state.string;
|
||||
let collection = self.inner.music_hoard.get_collection();
|
||||
let search_name = &self.state.string;
|
||||
|
||||
let sel = &mut self.inner.selection;
|
||||
if let Some(mut index) = sel.selected_artist() {
|
||||
let case_sensitive = Self::is_case_sensitive(artist_name);
|
||||
let char_sensitive = Self::is_char_sensitive(artist_name);
|
||||
let search = Self::normalize_search(artist_name, !case_sensitive, !char_sensitive);
|
||||
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);
|
||||
|
||||
if next && ((index + 1) < artists.len()) {
|
||||
index += 1;
|
||||
}
|
||||
let slice = &artists[index..];
|
||||
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)
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let result = slice.iter().position(|probe| {
|
||||
Self::incremental_search_predicate(case_sensitive, char_sensitive, &search, probe)
|
||||
});
|
||||
|
||||
if let Some(slice_index) = result {
|
||||
sel.select_artist(artists, Some(index + slice_index));
|
||||
if result.is_some() {
|
||||
sel.select(collection, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn incremental_search_predicate(
|
||||
case_sensitive: bool,
|
||||
char_sensitive: bool,
|
||||
search_name: &str,
|
||||
probe: &Artist,
|
||||
) -> bool {
|
||||
let name = Self::normalize_search(&probe.id.name, !case_sensitive, !char_sensitive);
|
||||
let mut result = name.starts_with(search_name);
|
||||
fn search_category<T, P>(list: &[T], mut index: usize, next: bool, pred: P) -> Option<usize>
|
||||
where
|
||||
P: FnMut(&T) -> bool,
|
||||
{
|
||||
if next && ((index + 1) < list.len()) {
|
||||
index += 1;
|
||||
}
|
||||
let slice = &list[index..];
|
||||
let result = slice.iter().position(pred);
|
||||
result.map(|slice_index| index + slice_index)
|
||||
}
|
||||
|
||||
fn predicate_artists(case_sens: bool, char_sens: bool, search: &str, probe: &Artist) -> bool {
|
||||
let name = Self::normalize_search(&probe.id.name, !case_sens, !char_sens);
|
||||
let mut result = name.starts_with(search);
|
||||
|
||||
if let Some(ref probe_sort) = probe.sort {
|
||||
if !result {
|
||||
let name =
|
||||
Self::normalize_search(&probe_sort.name, !case_sensitive, !char_sensitive);
|
||||
result = name.starts_with(search_name);
|
||||
let name = Self::normalize_search(&probe_sort.name, !case_sens, !char_sens);
|
||||
result = name.starts_with(search);
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn predicate_albums(case_sens: bool, char_sens: bool, search: &str, probe: &Album) -> bool {
|
||||
Self::predicate_title(case_sens, char_sens, search, &probe.id.title)
|
||||
}
|
||||
|
||||
fn predicate_tracks(case_sens: bool, char_sens: bool, search: &str, probe: &Track) -> bool {
|
||||
Self::predicate_title(case_sens, char_sens, search, &probe.id.title)
|
||||
}
|
||||
|
||||
fn predicate_title(case_sens: bool, char_sens: bool, search: &str, title: &str) -> bool {
|
||||
Self::normalize_search(title, !case_sens, !char_sens).starts_with(search)
|
||||
}
|
||||
|
||||
fn is_case_sensitive(artist_name: &str) -> bool {
|
||||
artist_name
|
||||
.chars()
|
||||
|
@ -80,20 +80,6 @@ impl Selection {
|
||||
self.artist.reinitialise(artists, selected.artist);
|
||||
}
|
||||
|
||||
pub fn select_artist(&mut self, artists: &[Artist], index: Option<usize>) {
|
||||
self.artist.select(artists, index);
|
||||
}
|
||||
|
||||
pub fn selected_artist(&self) -> Option<usize> {
|
||||
self.artist.selected()
|
||||
}
|
||||
|
||||
pub fn reset_artist(&mut self, artists: &[Artist]) {
|
||||
if self.artist.state.list.selected() != Some(0) {
|
||||
self.select_by_id(artists, IdSelection { artist: None });
|
||||
}
|
||||
}
|
||||
|
||||
pub fn increment_category(&mut self) {
|
||||
self.active = match self.active {
|
||||
Category::Artist => Category::Album,
|
||||
@ -110,6 +96,66 @@ impl Selection {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn select(&mut self, collection: &Collection, index: Option<usize>) {
|
||||
match self.active {
|
||||
Category::Artist => self.select_artist(collection, index),
|
||||
Category::Album => self.select_album(collection, index),
|
||||
Category::Track => self.select_track(collection, index),
|
||||
}
|
||||
}
|
||||
|
||||
fn select_artist(&mut self, artists: &[Artist], index: Option<usize>) {
|
||||
self.artist.select(artists, index);
|
||||
}
|
||||
|
||||
fn select_album(&mut self, artists: &[Artist], index: Option<usize>) {
|
||||
self.artist.select_album(artists, index);
|
||||
}
|
||||
|
||||
fn select_track(&mut self, artists: &[Artist], index: Option<usize>) {
|
||||
self.artist.select_track(artists, index);
|
||||
}
|
||||
|
||||
pub fn selected(&self) -> Option<usize> {
|
||||
match self.active {
|
||||
Category::Artist => self.selected_artist(),
|
||||
Category::Album => self.selected_album(),
|
||||
Category::Track => self.selected_track(),
|
||||
}
|
||||
}
|
||||
|
||||
fn selected_artist(&self) -> Option<usize> {
|
||||
self.artist.selected()
|
||||
}
|
||||
|
||||
fn selected_album(&self) -> Option<usize> {
|
||||
self.artist.selected_album()
|
||||
}
|
||||
|
||||
fn selected_track(&self) -> Option<usize> {
|
||||
self.artist.selected_track()
|
||||
}
|
||||
|
||||
pub fn reset(&mut self, collection: &Collection) {
|
||||
match self.active {
|
||||
Category::Artist => self.reset_artist(collection),
|
||||
Category::Album => self.reset_album(collection),
|
||||
Category::Track => self.reset_track(collection),
|
||||
}
|
||||
}
|
||||
|
||||
fn reset_artist(&mut self, artists: &[Artist]) {
|
||||
self.artist.reset(artists);
|
||||
}
|
||||
|
||||
fn reset_album(&mut self, artists: &[Artist]) {
|
||||
self.artist.reset_album(artists);
|
||||
}
|
||||
|
||||
fn reset_track(&mut self, artists: &[Artist]) {
|
||||
self.artist.reset_track(artists);
|
||||
}
|
||||
|
||||
pub fn increment_selection(&mut self, collection: &Collection, delta: Delta) {
|
||||
match self.active {
|
||||
Category::Artist => self.increment_artist(collection, delta),
|
||||
@ -118,6 +164,18 @@ impl Selection {
|
||||
}
|
||||
}
|
||||
|
||||
fn increment_artist(&mut self, artists: &[Artist], delta: Delta) {
|
||||
self.artist.increment(artists, delta);
|
||||
}
|
||||
|
||||
fn increment_album(&mut self, artists: &[Artist], delta: Delta) {
|
||||
self.artist.increment_album(artists, delta);
|
||||
}
|
||||
|
||||
fn increment_track(&mut self, artists: &[Artist], delta: Delta) {
|
||||
self.artist.increment_track(artists, delta);
|
||||
}
|
||||
|
||||
pub fn decrement_selection(&mut self, collection: &Collection, delta: Delta) {
|
||||
match self.active {
|
||||
Category::Artist => self.decrement_artist(collection, delta),
|
||||
@ -126,26 +184,14 @@ impl Selection {
|
||||
}
|
||||
}
|
||||
|
||||
fn increment_artist(&mut self, artists: &[Artist], delta: Delta) {
|
||||
self.artist.increment(artists, delta);
|
||||
}
|
||||
|
||||
fn decrement_artist(&mut self, artists: &[Artist], delta: Delta) {
|
||||
self.artist.decrement(artists, delta);
|
||||
}
|
||||
|
||||
fn increment_album(&mut self, artists: &[Artist], delta: Delta) {
|
||||
self.artist.increment_album(artists, delta);
|
||||
}
|
||||
|
||||
fn decrement_album(&mut self, artists: &[Artist], delta: Delta) {
|
||||
self.artist.decrement_album(artists, delta);
|
||||
}
|
||||
|
||||
fn increment_track(&mut self, artists: &[Artist], delta: Delta) {
|
||||
self.artist.increment_track(artists, delta);
|
||||
}
|
||||
|
||||
fn decrement_track(&mut self, artists: &[Artist], delta: Delta) {
|
||||
self.artist.decrement_track(artists, delta);
|
||||
}
|
||||
@ -197,6 +243,14 @@ impl ArtistSelection {
|
||||
self.state.list.selected()
|
||||
}
|
||||
|
||||
fn selected_album(&self) -> Option<usize> {
|
||||
self.album.selected()
|
||||
}
|
||||
|
||||
fn selected_track(&self) -> Option<usize> {
|
||||
self.album.selected_track()
|
||||
}
|
||||
|
||||
fn select(&mut self, artists: &[Artist], to: Option<usize>) {
|
||||
match to {
|
||||
Some(to) => self.select_to(artists, to),
|
||||
@ -204,6 +258,18 @@ impl ArtistSelection {
|
||||
}
|
||||
}
|
||||
|
||||
fn select_album(&mut self, artists: &[Artist], to: Option<usize>) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
self.album.select(&artists[index].albums, to);
|
||||
}
|
||||
}
|
||||
|
||||
fn select_track(&mut self, artists: &[Artist], to: Option<usize>) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
self.album.select_track(&artists[index].albums, to);
|
||||
}
|
||||
}
|
||||
|
||||
fn select_to(&mut self, artists: &[Artist], mut to: usize) {
|
||||
to = cmp::min(to, artists.len() - 1);
|
||||
if self.state.list.selected() != Some(to) {
|
||||
@ -212,6 +278,24 @@ impl ArtistSelection {
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self, artists: &[Artist]) {
|
||||
if self.state.list.selected() != Some(0) {
|
||||
self.reinitialise(artists, None);
|
||||
}
|
||||
}
|
||||
|
||||
fn reset_album(&mut self, artists: &[Artist]) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
self.album.reset(&artists[index].albums);
|
||||
}
|
||||
}
|
||||
|
||||
fn reset_track(&mut self, artists: &[Artist]) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
self.album.reset_track(&artists[index].albums);
|
||||
}
|
||||
}
|
||||
|
||||
fn increment_by(&mut self, artists: &[Artist], by: usize) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
let result = index.saturating_add(by);
|
||||
@ -303,6 +387,47 @@ impl AlbumSelection {
|
||||
}
|
||||
}
|
||||
|
||||
fn selected(&self) -> Option<usize> {
|
||||
self.state.list.selected()
|
||||
}
|
||||
|
||||
fn selected_track(&self) -> Option<usize> {
|
||||
self.track.selected()
|
||||
}
|
||||
|
||||
fn select(&mut self, albums: &[Album], to: Option<usize>) {
|
||||
match to {
|
||||
Some(to) => self.select_to(albums, to),
|
||||
None => self.state.list.select(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn select_track(&mut self, albums: &[Album], to: Option<usize>) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
self.track.select(&albums[index].tracks, to);
|
||||
}
|
||||
}
|
||||
|
||||
fn select_to(&mut self, albums: &[Album], mut to: usize) {
|
||||
to = cmp::min(to, albums.len() - 1);
|
||||
if self.state.list.selected() != Some(to) {
|
||||
self.state.list.select(Some(to));
|
||||
self.track = TrackSelection::initialise(&albums[to].tracks);
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self, albums: &[Album]) {
|
||||
if self.state.list.selected() != Some(0) {
|
||||
self.reinitialise(albums, None);
|
||||
}
|
||||
}
|
||||
|
||||
fn reset_track(&mut self, albums: &[Album]) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
self.track.reset(&albums[index].tracks);
|
||||
}
|
||||
}
|
||||
|
||||
fn increment_by(&mut self, albums: &[Album], by: usize) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
let mut result = index.saturating_add(by);
|
||||
@ -377,6 +502,28 @@ impl TrackSelection {
|
||||
}
|
||||
}
|
||||
|
||||
fn selected(&self) -> Option<usize> {
|
||||
self.state.list.selected()
|
||||
}
|
||||
|
||||
fn select(&mut self, tracks: &[Track], to: Option<usize>) {
|
||||
match to {
|
||||
Some(to) => self.select_to(tracks, to),
|
||||
None => self.state.list.select(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn select_to(&mut self, tracks: &[Track], mut to: usize) {
|
||||
to = cmp::min(to, tracks.len() - 1);
|
||||
self.state.list.select(Some(to));
|
||||
}
|
||||
|
||||
fn reset(&mut self, tracks: &[Track]) {
|
||||
if self.state.list.selected() != Some(0) {
|
||||
self.reinitialise(tracks, None);
|
||||
}
|
||||
}
|
||||
|
||||
fn increment_by(&mut self, tracks: &[Track], by: usize) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
let mut result = index.saturating_add(by);
|
||||
|
Loading…
Reference in New Issue
Block a user