Add incremental search

This commit is contained in:
Wojciech Kozlowski 2024-02-11 10:03:49 +01:00
parent 8fd11c4f57
commit cea3516c60
3 changed files with 89 additions and 36 deletions

View File

@ -224,6 +224,8 @@ impl<MH: IMusicHoard> IAppInteractBrowse for App<MH> {
fn begin_search(&mut self) {
assert!(self.state.is_browse());
self.state = AppState::Search(String::new());
self.selection
.reset_artist(self.music_hoard.get_collection());
}
}
@ -274,14 +276,24 @@ impl<MH: IMusicHoard> IAppInteractReloadPrivate for App<MH> {
impl<MH: IMusicHoard> IAppInteractSearch for App<MH> {
fn append_character(&mut self, ch: char) {
match self.state {
AppState::Search(ref mut s) => s.push(ch),
AppState::Search(ref mut s) => {
s.push(ch);
self.selection
.incremental_artist_search(self.music_hoard.get_collection(), s);
}
_ => unreachable!(),
}
}
fn remove_character(&mut self) {
match self.state {
AppState::Search(ref mut s) => s.pop(),
AppState::Search(ref mut s) => {
s.pop();
self.selection
.reset_artist(self.music_hoard.get_collection());
self.selection
.incremental_artist_search(self.music_hoard.get_collection(), s);
}
_ => unreachable!(),
};
}

View File

@ -73,6 +73,12 @@ impl Selection {
self.artist.reinitialise(artists, selected.artist);
}
pub fn reset_artist(&mut self, artists: &[Artist]) {
if self.artist.state.list.selected() != Some(0) {
self.select(artists, ActiveSelection { artist: None });
}
}
pub fn increment_category(&mut self) {
self.active = match self.active {
Category::Artist => Category::Album,
@ -89,6 +95,10 @@ impl Selection {
};
}
pub fn incremental_artist_search(&mut self, collection: &Collection, artist_name: &str) {
self.artist.incremental_search(collection, artist_name);
}
pub fn increment_selection(&mut self, collection: &Collection, delta: Delta) {
match self.active {
Category::Artist => self.increment_artist(collection, delta),
@ -172,16 +182,47 @@ impl ArtistSelection {
}
}
fn select_to(&mut self, artists: &[Artist], mut to: usize) {
if to >= artists.len() {
to = artists.len() - 1;
}
if self.state.list.selected() != Some(to) {
self.state.list.select(Some(to));
self.album = AlbumSelection::initialise(&artists[to].albums);
}
}
fn incremental_search(&mut self, artists: &[Artist], artist_name: &str) {
if let Some(index) = self.state.list.selected() {
let case_sensitive = artist_name
.chars()
.any(|ch| !(ch.is_lowercase() || ch.is_whitespace()));
let slice = &artists[index..];
let result = if case_sensitive {
slice.binary_search_by(|probe| probe.get_sort_key().name.as_str().cmp(artist_name))
} else {
slice.binary_search_by(|probe| {
probe
.get_sort_key()
.name
.to_lowercase()
.as_str()
.cmp(artist_name)
})
};
let new_index = match result {
Ok(slice_index) | Err(slice_index) => index + slice_index,
};
self.select_to(artists, new_index);
}
}
fn increment_by(&mut self, artists: &[Artist], by: usize) {
if let Some(index) = self.state.list.selected() {
let mut result = index.saturating_add(by);
if result >= artists.len() {
result = artists.len() - 1;
}
if self.state.list.selected() != Some(result) {
self.state.list.select(Some(result));
self.album = AlbumSelection::initialise(&artists[result].albums);
}
let result = index.saturating_add(by);
self.select_to(artists, result);
}
}

View File

@ -53,35 +53,35 @@ impl<APP: IAppInteract> IEventHandler<APP> for EventHandler {
impl<APP: IAppInteract> IEventHandlerPrivate<APP> for EventHandler {
fn handle_key_event(app: &mut APP, key_event: KeyEvent) {
match key_event.code {
// Exit application on `Ctrl-C`.
KeyCode::Char('c') | KeyCode::Char('C') => {
if key_event.modifiers == KeyModifiers::CONTROL {
if key_event.modifiers == KeyModifiers::CONTROL {
match key_event.code {
// Exit application on `Ctrl-C`.
KeyCode::Char('c') | KeyCode::Char('C') => {
app.force_quit();
}
_ => {}
}
}
match app.state() {
AppState::Browse(browse) => {
<Self as IEventHandlerPrivate<APP>>::handle_browse_key_event(browse, key_event);
}
AppState::Info(info) => {
<Self as IEventHandlerPrivate<APP>>::handle_info_key_event(info, key_event);
}
AppState::Reload(reload) => {
<Self as IEventHandlerPrivate<APP>>::handle_reload_key_event(reload, key_event);
}
AppState::Search(search) => {
<Self as IEventHandlerPrivate<APP>>::handle_search_key_event(search, key_event);
}
AppState::Error(error) => {
<Self as IEventHandlerPrivate<APP>>::handle_error_key_event(error, key_event);
}
AppState::Critical(critical) => {
<Self as IEventHandlerPrivate<APP>>::handle_critical_key_event(critical, key_event);
}
_ => match app.state() {
AppState::Browse(browse) => {
<Self as IEventHandlerPrivate<APP>>::handle_browse_key_event(browse, key_event);
}
AppState::Info(info) => {
<Self as IEventHandlerPrivate<APP>>::handle_info_key_event(info, key_event);
}
AppState::Reload(reload) => {
<Self as IEventHandlerPrivate<APP>>::handle_reload_key_event(reload, key_event);
}
AppState::Search(search) => {
<Self as IEventHandlerPrivate<APP>>::handle_search_key_event(search, key_event);
}
AppState::Error(error) => {
<Self as IEventHandlerPrivate<APP>>::handle_error_key_event(error, key_event);
}
AppState::Critical(critical) => {
<Self as IEventHandlerPrivate<APP>>::handle_critical_key_event(
critical, key_event,
);
}
},
}
}