Add support for PgUp and PgDn scrolling #121

Merged
wojtek merged 4 commits from 119---add-support-for-pgup-and-pgdn-scrolling into main 2024-02-05 23:44:30 +01:00
4 changed files with 373 additions and 284 deletions
Showing only changes of commit 9d9e9d211e - Show all commits

View File

@ -3,7 +3,7 @@
use musichoard::collection::Collection; use musichoard::collection::Collection;
use crate::tui::{ use crate::tui::{
app::selection::{ActiveSelection, Selection}, app::selection::{ActiveSelection, Delta, Selection},
lib::IMusicHoard, lib::IMusicHoard,
}; };
@ -50,8 +50,8 @@ pub trait IAppInteract {
pub trait IAppInteractBrowse { pub trait IAppInteractBrowse {
fn increment_category(&mut self); fn increment_category(&mut self);
fn decrement_category(&mut self); fn decrement_category(&mut self);
fn increment_selection(&mut self); fn increment_selection(&mut self, delta: Delta);
fn decrement_selection(&mut self); fn decrement_selection(&mut self, delta: Delta);
fn show_info_overlay(&mut self); fn show_info_overlay(&mut self);
@ -160,14 +160,14 @@ impl<MH: IMusicHoard> IAppInteractBrowse for App<MH> {
self.selection.decrement_category(); self.selection.decrement_category();
} }
fn increment_selection(&mut self) { fn increment_selection(&mut self, delta: Delta) {
self.selection self.selection
.increment_selection(self.music_hoard.get_collection()); .increment_selection(self.music_hoard.get_collection(), delta);
} }
fn decrement_selection(&mut self) { fn decrement_selection(&mut self, delta: Delta) {
self.selection self.selection
.decrement_selection(self.music_hoard.get_collection()); .decrement_selection(self.music_hoard.get_collection(), delta);
} }
fn show_info_overlay(&mut self) { fn show_info_overlay(&mut self) {
@ -362,77 +362,113 @@ mod tests {
assert!(app.is_running()); assert!(app.is_running());
assert_eq!(app.selection.active, Category::Artist); assert_eq!(app.selection.active, Category::Artist);
assert_eq!(app.selection.artist.state.selected(), Some(0)); assert_eq!(app.selection.artist.state.list.selected(), Some(0));
assert_eq!(app.selection.artist.album.state.selected(), Some(0)); assert_eq!(app.selection.artist.album.state.list.selected(), Some(0));
assert_eq!(app.selection.artist.album.track.state.selected(), Some(0)); assert_eq!(
app.selection.artist.album.track.state.list.selected(),
Some(0)
);
app.increment_selection(); app.increment_selection();
assert_eq!(app.selection.active, Category::Artist); assert_eq!(app.selection.active, Category::Artist);
assert_eq!(app.selection.artist.state.selected(), Some(1)); assert_eq!(app.selection.artist.state.list.selected(), Some(1));
assert_eq!(app.selection.artist.album.state.selected(), Some(0)); assert_eq!(app.selection.artist.album.state.list.selected(), Some(0));
assert_eq!(app.selection.artist.album.track.state.selected(), Some(0)); assert_eq!(
app.selection.artist.album.track.state.list.selected(),
Some(0)
);
app.increment_category(); app.increment_category();
assert_eq!(app.selection.active, Category::Album); assert_eq!(app.selection.active, Category::Album);
assert_eq!(app.selection.artist.state.selected(), Some(1)); assert_eq!(app.selection.artist.state.list.selected(), Some(1));
assert_eq!(app.selection.artist.album.state.selected(), Some(0)); assert_eq!(app.selection.artist.album.state.list.selected(), Some(0));
assert_eq!(app.selection.artist.album.track.state.selected(), Some(0)); assert_eq!(
app.selection.artist.album.track.state.list.selected(),
Some(0)
);
app.increment_selection(); app.increment_selection();
assert_eq!(app.selection.active, Category::Album); assert_eq!(app.selection.active, Category::Album);
assert_eq!(app.selection.artist.state.selected(), Some(1)); assert_eq!(app.selection.artist.state.list.selected(), Some(1));
assert_eq!(app.selection.artist.album.state.selected(), Some(1)); assert_eq!(app.selection.artist.album.state.list.selected(), Some(1));
assert_eq!(app.selection.artist.album.track.state.selected(), Some(0)); assert_eq!(
app.selection.artist.album.track.state.list.selected(),
Some(0)
);
app.increment_category(); app.increment_category();
assert_eq!(app.selection.active, Category::Track); assert_eq!(app.selection.active, Category::Track);
assert_eq!(app.selection.artist.state.selected(), Some(1)); assert_eq!(app.selection.artist.state.list.selected(), Some(1));
assert_eq!(app.selection.artist.album.state.selected(), Some(1)); assert_eq!(app.selection.artist.album.state.list.selected(), Some(1));
assert_eq!(app.selection.artist.album.track.state.selected(), Some(0)); assert_eq!(
app.selection.artist.album.track.state.list.selected(),
Some(0)
);
app.increment_selection(); app.increment_selection();
assert_eq!(app.selection.active, Category::Track); assert_eq!(app.selection.active, Category::Track);
assert_eq!(app.selection.artist.state.selected(), Some(1)); assert_eq!(app.selection.artist.state.list.selected(), Some(1));
assert_eq!(app.selection.artist.album.state.selected(), Some(1)); assert_eq!(app.selection.artist.album.state.list.selected(), Some(1));
assert_eq!(app.selection.artist.album.track.state.selected(), Some(1)); assert_eq!(
app.selection.artist.album.track.state.list.selected(),
Some(1)
);
app.increment_category(); app.increment_category();
assert_eq!(app.selection.active, Category::Track); assert_eq!(app.selection.active, Category::Track);
assert_eq!(app.selection.artist.state.selected(), Some(1)); assert_eq!(app.selection.artist.state.list.selected(), Some(1));
assert_eq!(app.selection.artist.album.state.selected(), Some(1)); assert_eq!(app.selection.artist.album.state.list.selected(), Some(1));
assert_eq!(app.selection.artist.album.track.state.selected(), Some(1)); assert_eq!(
app.selection.artist.album.track.state.list.selected(),
Some(1)
);
app.decrement_selection(); app.decrement_selection();
assert_eq!(app.selection.active, Category::Track); assert_eq!(app.selection.active, Category::Track);
assert_eq!(app.selection.artist.state.selected(), Some(1)); assert_eq!(app.selection.artist.state.list.selected(), Some(1));
assert_eq!(app.selection.artist.album.state.selected(), Some(1)); assert_eq!(app.selection.artist.album.state.list.selected(), Some(1));
assert_eq!(app.selection.artist.album.track.state.selected(), Some(0)); assert_eq!(
app.selection.artist.album.track.state.list.selected(),
Some(0)
);
app.increment_selection(); app.increment_selection();
app.decrement_category(); app.decrement_category();
assert_eq!(app.selection.active, Category::Album); assert_eq!(app.selection.active, Category::Album);
assert_eq!(app.selection.artist.state.selected(), Some(1)); assert_eq!(app.selection.artist.state.list.selected(), Some(1));
assert_eq!(app.selection.artist.album.state.selected(), Some(1)); assert_eq!(app.selection.artist.album.state.list.selected(), Some(1));
assert_eq!(app.selection.artist.album.track.state.selected(), Some(1)); assert_eq!(
app.selection.artist.album.track.state.list.selected(),
Some(1)
);
app.decrement_selection(); app.decrement_selection();
assert_eq!(app.selection.active, Category::Album); assert_eq!(app.selection.active, Category::Album);
assert_eq!(app.selection.artist.state.selected(), Some(1)); assert_eq!(app.selection.artist.state.list.selected(), Some(1));
assert_eq!(app.selection.artist.album.state.selected(), Some(0)); assert_eq!(app.selection.artist.album.state.list.selected(), Some(0));
assert_eq!(app.selection.artist.album.track.state.selected(), Some(0)); assert_eq!(
app.selection.artist.album.track.state.list.selected(),
Some(0)
);
app.increment_selection(); app.increment_selection();
app.decrement_category(); app.decrement_category();
assert_eq!(app.selection.active, Category::Artist); assert_eq!(app.selection.active, Category::Artist);
assert_eq!(app.selection.artist.state.selected(), Some(1)); assert_eq!(app.selection.artist.state.list.selected(), Some(1));
assert_eq!(app.selection.artist.album.state.selected(), Some(1)); assert_eq!(app.selection.artist.album.state.list.selected(), Some(1));
assert_eq!(app.selection.artist.album.track.state.selected(), Some(0)); assert_eq!(
app.selection.artist.album.track.state.list.selected(),
Some(0)
);
app.decrement_selection(); app.decrement_selection();
assert_eq!(app.selection.active, Category::Artist); assert_eq!(app.selection.active, Category::Artist);
assert_eq!(app.selection.artist.state.selected(), Some(0)); assert_eq!(app.selection.artist.state.list.selected(), Some(0));
assert_eq!(app.selection.artist.album.state.selected(), Some(0)); assert_eq!(app.selection.artist.album.state.list.selected(), Some(0));
assert_eq!(app.selection.artist.album.track.state.selected(), Some(0)); assert_eq!(
app.selection.artist.album.track.state.list.selected(),
Some(0)
);
app.increment_category(); app.increment_category();
app.increment_selection(); app.increment_selection();
@ -440,9 +476,12 @@ mod tests {
app.decrement_selection(); app.decrement_selection();
app.decrement_category(); app.decrement_category();
assert_eq!(app.selection.active, Category::Artist); assert_eq!(app.selection.active, Category::Artist);
assert_eq!(app.selection.artist.state.selected(), Some(0)); assert_eq!(app.selection.artist.state.list.selected(), Some(0));
assert_eq!(app.selection.artist.album.state.selected(), Some(1)); assert_eq!(app.selection.artist.album.state.list.selected(), Some(1));
assert_eq!(app.selection.artist.album.track.state.selected(), Some(0)); assert_eq!(
app.selection.artist.album.track.state.list.selected(),
Some(0)
);
} }
#[test] #[test]
@ -454,24 +493,24 @@ mod tests {
assert!(app.is_running()); assert!(app.is_running());
assert_eq!(app.selection.active, Category::Artist); assert_eq!(app.selection.active, Category::Artist);
assert_eq!(app.selection.artist.state.selected(), Some(0)); assert_eq!(app.selection.artist.state.list.selected(), Some(0));
assert_eq!(app.selection.artist.album.state.selected(), Some(0)); assert_eq!(app.selection.artist.album.state.list.selected(), Some(0));
assert_eq!(app.selection.artist.album.track.state.selected(), None); assert_eq!(app.selection.artist.album.track.state.list.selected(), None);
app.increment_category(); app.increment_category();
app.increment_category(); app.increment_category();
app.increment_selection(); app.increment_selection();
assert_eq!(app.selection.active, Category::Track); assert_eq!(app.selection.active, Category::Track);
assert_eq!(app.selection.artist.state.selected(), Some(0)); assert_eq!(app.selection.artist.state.list.selected(), Some(0));
assert_eq!(app.selection.artist.album.state.selected(), Some(0)); assert_eq!(app.selection.artist.album.state.list.selected(), Some(0));
assert_eq!(app.selection.artist.album.track.state.selected(), None); assert_eq!(app.selection.artist.album.track.state.list.selected(), None);
app.decrement_selection(); app.decrement_selection();
assert_eq!(app.selection.active, Category::Track); assert_eq!(app.selection.active, Category::Track);
assert_eq!(app.selection.artist.state.selected(), Some(0)); assert_eq!(app.selection.artist.state.list.selected(), Some(0));
assert_eq!(app.selection.artist.album.state.selected(), Some(0)); assert_eq!(app.selection.artist.album.state.list.selected(), Some(0));
assert_eq!(app.selection.artist.album.track.state.selected(), None); assert_eq!(app.selection.artist.album.track.state.list.selected(), None);
} }
#[test] #[test]
@ -483,37 +522,37 @@ mod tests {
assert!(app.is_running()); assert!(app.is_running());
assert_eq!(app.selection.active, Category::Artist); assert_eq!(app.selection.active, Category::Artist);
assert_eq!(app.selection.artist.state.selected(), Some(0)); assert_eq!(app.selection.artist.state.list.selected(), Some(0));
assert_eq!(app.selection.artist.album.state.selected(), None); assert_eq!(app.selection.artist.album.state.list.selected(), None);
assert_eq!(app.selection.artist.album.track.state.selected(), None); assert_eq!(app.selection.artist.album.track.state.list.selected(), None);
app.increment_category(); app.increment_category();
app.increment_selection(); app.increment_selection();
assert_eq!(app.selection.active, Category::Album); assert_eq!(app.selection.active, Category::Album);
assert_eq!(app.selection.artist.state.selected(), Some(0)); assert_eq!(app.selection.artist.state.list.selected(), Some(0));
assert_eq!(app.selection.artist.album.state.selected(), None); assert_eq!(app.selection.artist.album.state.list.selected(), None);
assert_eq!(app.selection.artist.album.track.state.selected(), None); assert_eq!(app.selection.artist.album.track.state.list.selected(), None);
app.decrement_selection(); app.decrement_selection();
assert_eq!(app.selection.active, Category::Album); assert_eq!(app.selection.active, Category::Album);
assert_eq!(app.selection.artist.state.selected(), Some(0)); assert_eq!(app.selection.artist.state.list.selected(), Some(0));
assert_eq!(app.selection.artist.album.state.selected(), None); assert_eq!(app.selection.artist.album.state.list.selected(), None);
assert_eq!(app.selection.artist.album.track.state.selected(), None); assert_eq!(app.selection.artist.album.track.state.list.selected(), None);
app.increment_category(); app.increment_category();
app.increment_selection(); app.increment_selection();
assert_eq!(app.selection.active, Category::Track); assert_eq!(app.selection.active, Category::Track);
assert_eq!(app.selection.artist.state.selected(), Some(0)); assert_eq!(app.selection.artist.state.list.selected(), Some(0));
assert_eq!(app.selection.artist.album.state.selected(), None); assert_eq!(app.selection.artist.album.state.list.selected(), None);
assert_eq!(app.selection.artist.album.track.state.selected(), None); assert_eq!(app.selection.artist.album.track.state.list.selected(), None);
app.decrement_selection(); app.decrement_selection();
assert_eq!(app.selection.active, Category::Track); assert_eq!(app.selection.active, Category::Track);
assert_eq!(app.selection.artist.state.selected(), Some(0)); assert_eq!(app.selection.artist.state.list.selected(), Some(0));
assert_eq!(app.selection.artist.album.state.selected(), None); assert_eq!(app.selection.artist.album.state.list.selected(), None);
assert_eq!(app.selection.artist.album.track.state.selected(), None); assert_eq!(app.selection.artist.album.track.state.list.selected(), None);
} }
#[test] #[test]
@ -522,49 +561,49 @@ mod tests {
assert!(app.is_running()); assert!(app.is_running());
assert_eq!(app.selection.active, Category::Artist); assert_eq!(app.selection.active, Category::Artist);
assert_eq!(app.selection.artist.state.selected(), None); assert_eq!(app.selection.artist.state.list.selected(), None);
assert_eq!(app.selection.artist.album.state.selected(), None); assert_eq!(app.selection.artist.album.state.list.selected(), None);
assert_eq!(app.selection.artist.album.track.state.selected(), None); assert_eq!(app.selection.artist.album.track.state.list.selected(), None);
app.increment_selection(); app.increment_selection();
assert_eq!(app.selection.active, Category::Artist); assert_eq!(app.selection.active, Category::Artist);
assert_eq!(app.selection.artist.state.selected(), None); assert_eq!(app.selection.artist.state.list.selected(), None);
assert_eq!(app.selection.artist.album.state.selected(), None); assert_eq!(app.selection.artist.album.state.list.selected(), None);
assert_eq!(app.selection.artist.album.track.state.selected(), None); assert_eq!(app.selection.artist.album.track.state.list.selected(), None);
app.decrement_selection(); app.decrement_selection();
assert_eq!(app.selection.active, Category::Artist); assert_eq!(app.selection.active, Category::Artist);
assert_eq!(app.selection.artist.state.selected(), None); assert_eq!(app.selection.artist.state.list.selected(), None);
assert_eq!(app.selection.artist.album.state.selected(), None); assert_eq!(app.selection.artist.album.state.list.selected(), None);
assert_eq!(app.selection.artist.album.track.state.selected(), None); assert_eq!(app.selection.artist.album.track.state.list.selected(), None);
app.increment_category(); app.increment_category();
app.increment_selection(); app.increment_selection();
assert_eq!(app.selection.active, Category::Album); assert_eq!(app.selection.active, Category::Album);
assert_eq!(app.selection.artist.state.selected(), None); assert_eq!(app.selection.artist.state.list.selected(), None);
assert_eq!(app.selection.artist.album.state.selected(), None); assert_eq!(app.selection.artist.album.state.list.selected(), None);
assert_eq!(app.selection.artist.album.track.state.selected(), None); assert_eq!(app.selection.artist.album.track.state.list.selected(), None);
app.decrement_selection(); app.decrement_selection();
assert_eq!(app.selection.active, Category::Album); assert_eq!(app.selection.active, Category::Album);
assert_eq!(app.selection.artist.state.selected(), None); assert_eq!(app.selection.artist.state.list.selected(), None);
assert_eq!(app.selection.artist.album.state.selected(), None); assert_eq!(app.selection.artist.album.state.list.selected(), None);
assert_eq!(app.selection.artist.album.track.state.selected(), None); assert_eq!(app.selection.artist.album.track.state.list.selected(), None);
app.increment_category(); app.increment_category();
app.increment_selection(); app.increment_selection();
assert_eq!(app.selection.active, Category::Track); assert_eq!(app.selection.active, Category::Track);
assert_eq!(app.selection.artist.state.selected(), None); assert_eq!(app.selection.artist.state.list.selected(), None);
assert_eq!(app.selection.artist.album.state.selected(), None); assert_eq!(app.selection.artist.album.state.list.selected(), None);
assert_eq!(app.selection.artist.album.track.state.selected(), None); assert_eq!(app.selection.artist.album.track.state.list.selected(), None);
app.decrement_selection(); app.decrement_selection();
assert_eq!(app.selection.active, Category::Track); assert_eq!(app.selection.active, Category::Track);
assert_eq!(app.selection.artist.state.selected(), None); assert_eq!(app.selection.artist.state.list.selected(), None);
assert_eq!(app.selection.artist.album.state.selected(), None); assert_eq!(app.selection.artist.album.state.list.selected(), None);
assert_eq!(app.selection.artist.album.track.state.selected(), None); assert_eq!(app.selection.artist.album.track.state.list.selected(), None);
} }
#[test] #[test]

View File

@ -13,43 +13,52 @@ pub enum Category {
Track, Track,
} }
#[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)
}
}
pub struct Selection { pub struct Selection {
pub active: Category, pub active: Category,
pub artist: ArtistSelection, pub artist: ArtistSelection,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
pub struct ArtistSelection { pub struct ArtistSelection {
pub state: ListState, pub state: WidgetState,
pub album: AlbumSelection, pub album: AlbumSelection,
} }
impl PartialEq for ArtistSelection { #[derive(Clone, Debug, PartialEq)]
fn eq(&self, other: &Self) -> bool {
self.state.selected().eq(&other.state.selected()) && self.album.eq(&other.album)
}
}
#[derive(Clone, Debug)]
pub struct AlbumSelection { pub struct AlbumSelection {
pub state: ListState, pub state: WidgetState,
pub track: TrackSelection, pub track: TrackSelection,
} }
impl PartialEq for AlbumSelection { #[derive(Clone, Debug, PartialEq)]
fn eq(&self, other: &Self) -> bool { pub struct TrackSelection {
self.state.selected().eq(&other.state.selected()) && self.track.eq(&other.track) pub state: WidgetState,
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct TrackSelection { pub enum Delta {
pub state: ListState, Line,
Page,
} }
impl PartialEq for TrackSelection { impl Delta {
fn eq(&self, other: &Self) -> bool { fn as_usize(&self, state: &WidgetState) -> usize {
self.state.selected().eq(&other.state.selected()) match self {
Delta::Line => 1,
Delta::Page => state.height.checked_sub(1).unwrap_or(0),
}
} }
} }
@ -81,51 +90,51 @@ impl Selection {
}; };
} }
pub fn increment_selection(&mut self, collection: &Collection) { pub fn increment_selection(&mut self, collection: &Collection, delta: Delta) {
match self.active { match self.active {
Category::Artist => self.increment_artist(collection), Category::Artist => self.increment_artist(collection, delta),
Category::Album => self.increment_album(collection), Category::Album => self.increment_album(collection, delta),
Category::Track => self.increment_track(collection), Category::Track => self.increment_track(collection, delta),
} }
} }
pub fn decrement_selection(&mut self, collection: &Collection) { pub fn decrement_selection(&mut self, collection: &Collection, delta: Delta) {
match self.active { match self.active {
Category::Artist => self.decrement_artist(collection), Category::Artist => self.decrement_artist(collection, delta),
Category::Album => self.decrement_album(collection), Category::Album => self.decrement_album(collection, delta),
Category::Track => self.decrement_track(collection), Category::Track => self.decrement_track(collection, delta),
} }
} }
fn increment_artist(&mut self, artists: &[Artist]) { fn increment_artist(&mut self, artists: &[Artist], delta: Delta) {
self.artist.increment(artists); self.artist.increment(artists, delta);
} }
fn decrement_artist(&mut self, artists: &[Artist]) { fn decrement_artist(&mut self, artists: &[Artist], delta: Delta) {
self.artist.decrement(artists); self.artist.decrement(artists, delta);
} }
fn increment_album(&mut self, artists: &[Artist]) { fn increment_album(&mut self, artists: &[Artist], delta: Delta) {
self.artist.increment_album(artists); self.artist.increment_album(artists, delta);
} }
fn decrement_album(&mut self, artists: &[Artist]) { fn decrement_album(&mut self, artists: &[Artist], delta: Delta) {
self.artist.decrement_album(artists); self.artist.decrement_album(artists, delta);
} }
fn increment_track(&mut self, artists: &[Artist]) { fn increment_track(&mut self, artists: &[Artist], delta: Delta) {
self.artist.increment_track(artists); self.artist.increment_track(artists, delta);
} }
fn decrement_track(&mut self, artists: &[Artist]) { fn decrement_track(&mut self, artists: &[Artist], delta: Delta) {
self.artist.decrement_track(artists); self.artist.decrement_track(artists, delta);
} }
} }
impl ArtistSelection { impl ArtistSelection {
fn initialise(artists: &[Artist]) -> Self { fn initialise(artists: &[Artist]) -> Self {
let mut selection = ArtistSelection { let mut selection = ArtistSelection {
state: ListState::default(), state: WidgetState::default(),
album: AlbumSelection::initialise(&[]), album: AlbumSelection::initialise(&[]),
}; };
selection.reinitialise(artists, None); selection.reinitialise(artists, None);
@ -151,60 +160,71 @@ impl ArtistSelection {
active_album: Option<ActiveAlbum>, active_album: Option<ActiveAlbum>,
) { ) {
if artists.is_empty() { if artists.is_empty() {
self.state.select(None); self.state.list.select(None);
self.album = AlbumSelection::initialise(&[]); self.album = AlbumSelection::initialise(&[]);
} else if index >= artists.len() { } else if index >= artists.len() {
let end = artists.len() - 1; let end = artists.len() - 1;
self.state.select(Some(end)); self.state.list.select(Some(end));
self.album = AlbumSelection::initialise(&artists[end].albums); self.album = AlbumSelection::initialise(&artists[end].albums);
} else { } else {
self.state.select(Some(index)); self.state.list.select(Some(index));
self.album self.album
.reinitialise(&artists[index].albums, active_album); .reinitialise(&artists[index].albums, active_album);
} }
} }
fn increment(&mut self, artists: &[Artist]) { fn increment_by(&mut self, artists: &[Artist], by: usize) {
if let Some(index) = self.state.selected() { if let Some(index) = self.state.list.selected() {
if let Some(result) = index.checked_add(1) { let result = index
if result < artists.len() { .checked_add(by)
self.state.select(Some(result)); .filter(|i| i < &artists.len())
self.album = AlbumSelection::initialise(&artists[result].albums); .unwrap_or_else(|| artists.len() - 1);
} if self.state.list.selected() != Some(result) {
} self.state.list.select(Some(result));
}
}
fn increment_album(&mut self, artists: &[Artist]) {
if let Some(index) = self.state.selected() {
self.album.increment(&artists[index].albums);
}
}
fn increment_track(&mut self, artists: &[Artist]) {
if let Some(index) = self.state.selected() {
self.album.increment_track(&artists[index].albums);
}
}
fn decrement(&mut self, artists: &[Artist]) {
if let Some(index) = self.state.selected() {
if let Some(result) = index.checked_sub(1) {
self.state.select(Some(result));
self.album = AlbumSelection::initialise(&artists[result].albums); self.album = AlbumSelection::initialise(&artists[result].albums);
} }
} }
} }
fn decrement_album(&mut self, artists: &[Artist]) { fn increment(&mut self, artists: &[Artist], delta: Delta) {
if let Some(index) = self.state.selected() { self.increment_by(artists, delta.as_usize(&self.state));
self.album.decrement(&artists[index].albums); }
fn increment_album(&mut self, artists: &[Artist], delta: Delta) {
if let Some(index) = self.state.list.selected() {
self.album.increment(&artists[index].albums, delta);
} }
} }
fn decrement_track(&mut self, artists: &[Artist]) { fn increment_track(&mut self, artists: &[Artist], delta: Delta) {
if let Some(index) = self.state.selected() { if let Some(index) = self.state.list.selected() {
self.album.decrement_track(&artists[index].albums); self.album.increment_track(&artists[index].albums, delta);
}
}
fn decrement_by(&mut self, artists: &[Artist], by: usize) {
if let Some(index) = self.state.list.selected() {
let result = index.checked_sub(by).unwrap_or(0);
if self.state.list.selected() != Some(result) {
self.state.list.select(Some(result));
self.album = AlbumSelection::initialise(&artists[result].albums);
}
}
}
fn decrement(&mut self, artists: &[Artist], delta: Delta) {
self.decrement_by(artists, delta.as_usize(&self.state));
}
fn decrement_album(&mut self, artists: &[Artist], delta: Delta) {
if let Some(index) = self.state.list.selected() {
self.album.decrement(&artists[index].albums, delta);
}
}
fn decrement_track(&mut self, artists: &[Artist], delta: Delta) {
if let Some(index) = self.state.list.selected() {
self.album.decrement_track(&artists[index].albums, delta);
} }
} }
} }
@ -212,7 +232,7 @@ impl ArtistSelection {
impl AlbumSelection { impl AlbumSelection {
fn initialise(albums: &[Album]) -> Self { fn initialise(albums: &[Album]) -> Self {
let mut selection = AlbumSelection { let mut selection = AlbumSelection {
state: ListState::default(), state: WidgetState::default(),
track: TrackSelection::initialise(&[]), track: TrackSelection::initialise(&[]),
}; };
selection.reinitialise(albums, None); selection.reinitialise(albums, None);
@ -238,47 +258,58 @@ impl AlbumSelection {
active_track: Option<ActiveTrack>, active_track: Option<ActiveTrack>,
) { ) {
if albums.is_empty() { if albums.is_empty() {
self.state.select(None); self.state.list.select(None);
self.track = TrackSelection::initialise(&[]); self.track = TrackSelection::initialise(&[]);
} else if index >= albums.len() { } else if index >= albums.len() {
let end = albums.len() - 1; let end = albums.len() - 1;
self.state.select(Some(end)); self.state.list.select(Some(end));
self.track = TrackSelection::initialise(&albums[end].tracks); self.track = TrackSelection::initialise(&albums[end].tracks);
} else { } else {
self.state.select(Some(index)); self.state.list.select(Some(index));
self.track.reinitialise(&albums[index].tracks, active_track); self.track.reinitialise(&albums[index].tracks, active_track);
} }
} }
fn increment(&mut self, albums: &[Album]) { fn increment_by(&mut self, albums: &[Album], by: usize) {
if let Some(index) = self.state.selected() { if let Some(index) = self.state.list.selected() {
if let Some(result) = index.checked_add(1) { let result = index
if result < albums.len() { .checked_add(by)
self.state.select(Some(result)); .filter(|i| i < &albums.len())
self.track = TrackSelection::initialise(&albums[result].tracks); .unwrap_or_else(|| albums.len() - 1);
} if self.state.list.selected() != Some(result) {
} self.state.list.select(Some(result));
}
}
fn increment_track(&mut self, albums: &[Album]) {
if let Some(index) = self.state.selected() {
self.track.increment(&albums[index].tracks);
}
}
fn decrement(&mut self, albums: &[Album]) {
if let Some(index) = self.state.selected() {
if let Some(result) = index.checked_sub(1) {
self.state.select(Some(result));
self.track = TrackSelection::initialise(&albums[result].tracks); self.track = TrackSelection::initialise(&albums[result].tracks);
} }
} }
} }
fn decrement_track(&mut self, albums: &[Album]) { fn increment(&mut self, albums: &[Album], delta: Delta) {
if let Some(index) = self.state.selected() { self.increment_by(albums, delta.as_usize(&self.state));
self.track.decrement(&albums[index].tracks); }
fn increment_track(&mut self, albums: &[Album], delta: Delta) {
if let Some(index) = self.state.list.selected() {
self.track.increment(&albums[index].tracks, delta);
}
}
fn decrement_by(&mut self, albums: &[Album], by: usize) {
if let Some(index) = self.state.list.selected() {
let result = index.checked_sub(by).unwrap_or(0);
if self.state.list.selected() != Some(result) {
self.state.list.select(Some(result));
self.track = TrackSelection::initialise(&albums[result].tracks);
}
}
}
fn decrement(&mut self, albums: &[Album], delta: Delta) {
self.decrement_by(albums, delta.as_usize(&self.state));
}
fn decrement_track(&mut self, albums: &[Album], delta: Delta) {
if let Some(index) = self.state.list.selected() {
self.track.decrement(&albums[index].tracks, delta);
} }
} }
} }
@ -286,7 +317,7 @@ impl AlbumSelection {
impl TrackSelection { impl TrackSelection {
fn initialise(tracks: &[Track]) -> Self { fn initialise(tracks: &[Track]) -> Self {
let mut selection = TrackSelection { let mut selection = TrackSelection {
state: ListState::default(), state: WidgetState::default(),
}; };
selection.reinitialise(tracks, None); selection.reinitialise(tracks, None);
selection selection
@ -305,31 +336,42 @@ impl TrackSelection {
fn reinitialise_with_index(&mut self, tracks: &[Track], index: usize) { fn reinitialise_with_index(&mut self, tracks: &[Track], index: usize) {
if tracks.is_empty() { if tracks.is_empty() {
self.state.select(None); self.state.list.select(None);
} else if index >= tracks.len() { } else if index >= tracks.len() {
self.state.select(Some(tracks.len() - 1)); self.state.list.select(Some(tracks.len() - 1));
} else { } else {
self.state.select(Some(index)); self.state.list.select(Some(index));
} }
} }
fn increment(&mut self, tracks: &[Track]) { fn increment_by(&mut self, tracks: &[Track], by: usize) {
if let Some(index) = self.state.selected() { if let Some(index) = self.state.list.selected() {
if let Some(result) = index.checked_add(1) { let result = index
if result < tracks.len() { .checked_add(by)
self.state.select(Some(result)); .filter(|i| i < &tracks.len())
} .unwrap_or_else(|| tracks.len() - 1);
if self.state.list.selected() != Some(result) {
self.state.list.select(Some(result));
} }
} }
} }
fn decrement(&mut self, _tracks: &[Track]) { fn increment(&mut self, tracks: &[Track], delta: Delta) {
if let Some(index) = self.state.selected() { self.increment_by(tracks, delta.as_usize(&self.state));
if let Some(result) = index.checked_sub(1) { }
self.state.select(Some(result));
fn decrement_by(&mut self, _tracks: &[Track], by: usize) {
if let Some(index) = self.state.list.selected() {
let result = index.checked_sub(by).unwrap_or(0);
if self.state.list.selected() != Some(result) {
self.state.list.select(Some(result));
} }
} }
} }
fn decrement(&mut self, tracks: &[Track], delta: Delta) {
self.decrement_by(tracks, delta.as_usize(&self.state));
}
} }
pub struct ActiveSelection { pub struct ActiveSelection {
@ -360,7 +402,7 @@ impl ActiveSelection {
impl ActiveArtist { impl ActiveArtist {
fn get(artists: &[Artist], selection: &ArtistSelection) -> Option<Self> { fn get(artists: &[Artist], selection: &ArtistSelection) -> Option<Self> {
selection.state.selected().map(|index| { selection.state.list.selected().map(|index| {
let artist = &artists[index]; let artist = &artists[index];
ActiveArtist { ActiveArtist {
artist_id: artist.get_sort_key().clone(), artist_id: artist.get_sort_key().clone(),
@ -372,7 +414,7 @@ impl ActiveArtist {
impl ActiveAlbum { impl ActiveAlbum {
fn get(albums: &[Album], selection: &AlbumSelection) -> Option<Self> { fn get(albums: &[Album], selection: &AlbumSelection) -> Option<Self> {
selection.state.selected().map(|index| { selection.state.list.selected().map(|index| {
let album = &albums[index]; let album = &albums[index];
ActiveAlbum { ActiveAlbum {
album_id: album.get_sort_key().clone(), album_id: album.get_sort_key().clone(),
@ -384,7 +426,7 @@ impl ActiveAlbum {
impl ActiveTrack { impl ActiveTrack {
fn get(tracks: &[Track], selection: &TrackSelection) -> Option<Self> { fn get(tracks: &[Track], selection: &TrackSelection) -> Option<Self> {
selection.state.selected().map(|index| { selection.state.list.selected().map(|index| {
let track = &tracks[index]; let track = &tracks[index];
ActiveTrack { ActiveTrack {
track_id: track.get_sort_key().clone(), track_id: track.get_sort_key().clone(),
@ -405,24 +447,24 @@ mod tests {
assert!(tracks.len() > 1); assert!(tracks.len() > 1);
let empty = TrackSelection::initialise(&[]); let empty = TrackSelection::initialise(&[]);
assert_eq!(empty.state.selected(), None); assert_eq!(empty.state.list.selected(), None);
let mut sel = TrackSelection::initialise(tracks); let mut sel = TrackSelection::initialise(tracks);
assert_eq!(sel.state.selected(), Some(0)); assert_eq!(sel.state.list.selected(), Some(0));
sel.decrement(tracks); sel.decrement(tracks);
assert_eq!(sel.state.selected(), Some(0)); assert_eq!(sel.state.list.selected(), Some(0));
sel.increment(tracks); sel.increment(tracks);
assert_eq!(sel.state.selected(), Some(1)); assert_eq!(sel.state.list.selected(), Some(1));
sel.decrement(tracks); sel.decrement(tracks);
assert_eq!(sel.state.selected(), Some(0)); assert_eq!(sel.state.list.selected(), Some(0));
for _ in 0..(tracks.len() + 5) { for _ in 0..(tracks.len() + 5) {
sel.increment(tracks); sel.increment(tracks);
} }
assert_eq!(sel.state.selected(), Some(tracks.len() - 1)); assert_eq!(sel.state.list.selected(), Some(tracks.len() - 1));
// Re-initialise. // Re-initialise.
let expected = sel.clone(); let expected = sel.clone();
@ -444,11 +486,11 @@ mod tests {
assert_eq!(sel, expected); assert_eq!(sel, expected);
// Artifical test case to verify upper limit. // Artifical test case to verify upper limit.
sel.state.select(Some(std::usize::MAX)); sel.state.list.select(Some(std::usize::MAX));
assert_eq!(sel.state.selected(), Some(std::usize::MAX)); assert_eq!(sel.state.list.selected(), Some(std::usize::MAX));
sel.increment(&[]); sel.increment(&[]);
assert_eq!(sel.state.selected(), Some(std::usize::MAX)); assert_eq!(sel.state.list.selected(), Some(std::usize::MAX));
} }
#[test] #[test]
@ -457,43 +499,43 @@ mod tests {
assert!(albums.len() > 1); assert!(albums.len() > 1);
let empty = AlbumSelection::initialise(&[]); let empty = AlbumSelection::initialise(&[]);
assert_eq!(empty.state.selected(), None); assert_eq!(empty.state.list.selected(), None);
let mut sel = AlbumSelection::initialise(albums); let mut sel = AlbumSelection::initialise(albums);
assert_eq!(sel.state.selected(), Some(0)); assert_eq!(sel.state.list.selected(), Some(0));
assert_eq!(sel.track.state.selected(), Some(0)); assert_eq!(sel.track.state.list.selected(), Some(0));
sel.increment_track(albums); sel.increment_track(albums);
assert_eq!(sel.state.selected(), Some(0)); assert_eq!(sel.state.list.selected(), Some(0));
assert_eq!(sel.track.state.selected(), Some(1)); assert_eq!(sel.track.state.list.selected(), Some(1));
// Verify that decrement that doesn't change index does not reset track. // Verify that decrement that doesn't change index does not reset track.
sel.decrement(albums); sel.decrement(albums);
assert_eq!(sel.state.selected(), Some(0)); assert_eq!(sel.state.list.selected(), Some(0));
assert_eq!(sel.track.state.selected(), Some(1)); assert_eq!(sel.track.state.list.selected(), Some(1));
sel.increment(albums); sel.increment(albums);
assert_eq!(sel.state.selected(), Some(1)); assert_eq!(sel.state.list.selected(), Some(1));
assert_eq!(sel.track.state.selected(), Some(0)); assert_eq!(sel.track.state.list.selected(), Some(0));
sel.decrement(albums); sel.decrement(albums);
assert_eq!(sel.state.selected(), Some(0)); assert_eq!(sel.state.list.selected(), Some(0));
assert_eq!(sel.track.state.selected(), Some(0)); assert_eq!(sel.track.state.list.selected(), Some(0));
for _ in 0..(albums.len() + 5) { for _ in 0..(albums.len() + 5) {
sel.increment(albums); sel.increment(albums);
} }
assert_eq!(sel.state.selected(), Some(albums.len() - 1)); assert_eq!(sel.state.list.selected(), Some(albums.len() - 1));
assert_eq!(sel.track.state.selected(), Some(0)); assert_eq!(sel.track.state.list.selected(), Some(0));
sel.increment_track(albums); sel.increment_track(albums);
assert_eq!(sel.state.selected(), Some(albums.len() - 1)); assert_eq!(sel.state.list.selected(), Some(albums.len() - 1));
assert_eq!(sel.track.state.selected(), Some(1)); assert_eq!(sel.track.state.list.selected(), Some(1));
// Verify that increment that doesn't change index does not reset track. // Verify that increment that doesn't change index does not reset track.
sel.increment(albums); sel.increment(albums);
assert_eq!(sel.state.selected(), Some(albums.len() - 1)); assert_eq!(sel.state.list.selected(), Some(albums.len() - 1));
assert_eq!(sel.track.state.selected(), Some(1)); assert_eq!(sel.track.state.list.selected(), Some(1));
// Re-initialise. // Re-initialise.
let expected = sel.clone(); let expected = sel.clone();
@ -515,14 +557,14 @@ mod tests {
assert_eq!(sel, expected); assert_eq!(sel, expected);
// Artifical test case to verify upper limit. // Artifical test case to verify upper limit.
sel.state.select(Some(std::usize::MAX)); sel.state.list.select(Some(std::usize::MAX));
sel.track.state.select(Some(1)); sel.track.state.list.select(Some(1));
assert_eq!(sel.state.selected(), Some(std::usize::MAX)); assert_eq!(sel.state.list.selected(), Some(std::usize::MAX));
assert_eq!(sel.track.state.selected(), Some(1)); assert_eq!(sel.track.state.list.selected(), Some(1));
sel.increment(&[]); sel.increment(&[]);
assert_eq!(sel.state.selected(), Some(std::usize::MAX)); assert_eq!(sel.state.list.selected(), Some(std::usize::MAX));
assert_eq!(sel.track.state.selected(), Some(1)); assert_eq!(sel.track.state.list.selected(), Some(1));
} }
#[test] #[test]
@ -531,43 +573,43 @@ mod tests {
assert!(artists.len() > 1); assert!(artists.len() > 1);
let empty = ArtistSelection::initialise(&[]); let empty = ArtistSelection::initialise(&[]);
assert_eq!(empty.state.selected(), None); assert_eq!(empty.state.list.selected(), None);
let mut sel = ArtistSelection::initialise(artists); let mut sel = ArtistSelection::initialise(artists);
assert_eq!(sel.state.selected(), Some(0)); assert_eq!(sel.state.list.selected(), Some(0));
assert_eq!(sel.album.state.selected(), Some(0)); assert_eq!(sel.album.state.list.selected(), Some(0));
sel.increment_album(artists); sel.increment_album(artists);
assert_eq!(sel.state.selected(), Some(0)); assert_eq!(sel.state.list.selected(), Some(0));
assert_eq!(sel.album.state.selected(), Some(1)); assert_eq!(sel.album.state.list.selected(), Some(1));
// Verify that decrement that doesn't change index does not reset album. // Verify that decrement that doesn't change index does not reset album.
sel.decrement(artists); sel.decrement(artists);
assert_eq!(sel.state.selected(), Some(0)); assert_eq!(sel.state.list.selected(), Some(0));
assert_eq!(sel.album.state.selected(), Some(1)); assert_eq!(sel.album.state.list.selected(), Some(1));
sel.increment(artists); sel.increment(artists);
assert_eq!(sel.state.selected(), Some(1)); assert_eq!(sel.state.list.selected(), Some(1));
assert_eq!(sel.album.state.selected(), Some(0)); assert_eq!(sel.album.state.list.selected(), Some(0));
sel.decrement(artists); sel.decrement(artists);
assert_eq!(sel.state.selected(), Some(0)); assert_eq!(sel.state.list.selected(), Some(0));
assert_eq!(sel.album.state.selected(), Some(0)); assert_eq!(sel.album.state.list.selected(), Some(0));
for _ in 0..(artists.len() + 5) { for _ in 0..(artists.len() + 5) {
sel.increment(artists); sel.increment(artists);
} }
assert_eq!(sel.state.selected(), Some(artists.len() - 1)); assert_eq!(sel.state.list.selected(), Some(artists.len() - 1));
assert_eq!(sel.album.state.selected(), Some(0)); assert_eq!(sel.album.state.list.selected(), Some(0));
sel.increment_album(artists); sel.increment_album(artists);
assert_eq!(sel.state.selected(), Some(artists.len() - 1)); assert_eq!(sel.state.list.selected(), Some(artists.len() - 1));
assert_eq!(sel.album.state.selected(), Some(1)); assert_eq!(sel.album.state.list.selected(), Some(1));
// Verify that increment that doesn't change index does not reset album. // Verify that increment that doesn't change index does not reset album.
sel.increment(artists); sel.increment(artists);
assert_eq!(sel.state.selected(), Some(artists.len() - 1)); assert_eq!(sel.state.list.selected(), Some(artists.len() - 1));
assert_eq!(sel.album.state.selected(), Some(1)); assert_eq!(sel.album.state.list.selected(), Some(1));
// Re-initialise. // Re-initialise.
let expected = sel.clone(); let expected = sel.clone();
@ -589,13 +631,13 @@ mod tests {
assert_eq!(sel, expected); assert_eq!(sel, expected);
// Artifical test case to verify upper limit. // Artifical test case to verify upper limit.
sel.state.select(Some(std::usize::MAX)); sel.state.list.select(Some(std::usize::MAX));
sel.album.state.select(Some(1)); sel.album.state.list.select(Some(1));
assert_eq!(sel.state.selected(), Some(std::usize::MAX)); assert_eq!(sel.state.list.selected(), Some(std::usize::MAX));
assert_eq!(sel.album.state.selected(), Some(1)); assert_eq!(sel.album.state.list.selected(), Some(1));
sel.increment(&[]); sel.increment(&[]);
assert_eq!(sel.state.selected(), Some(std::usize::MAX)); assert_eq!(sel.state.list.selected(), Some(std::usize::MAX));
assert_eq!(sel.album.state.selected(), Some(1)); assert_eq!(sel.album.state.list.selected(), Some(1));
} }
} }

View File

@ -4,9 +4,12 @@ use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use mockall::automock; use mockall::automock;
use crate::tui::{ use crate::tui::{
app::app::{ app::{
AppState, IAppInteract, IAppInteractBrowse, IAppInteractError, IAppInteractInfo, app::{
IAppInteractReload, AppState, IAppInteract, IAppInteractBrowse, IAppInteractError, IAppInteractInfo,
IAppInteractReload,
},
selection::Delta,
}, },
event::{Event, EventError, EventReceiver}, event::{Event, EventError, EventReceiver},
}; };
@ -83,8 +86,10 @@ impl<APP: IAppInteract> IEventHandlerPrivate<APP> for EventHandler {
KeyCode::Left => app.decrement_category(), KeyCode::Left => app.decrement_category(),
KeyCode::Right => app.increment_category(), KeyCode::Right => app.increment_category(),
// Selection change. // Selection change.
KeyCode::Up => app.decrement_selection(), KeyCode::Up => app.decrement_selection(Delta::Line),
KeyCode::Down => app.increment_selection(), KeyCode::Down => app.increment_selection(Delta::Line),
KeyCode::PageUp => app.decrement_selection(Delta::Page),
KeyCode::PageDown => app.increment_selection(Delta::Page),
// Toggle overlay. // Toggle overlay.
KeyCode::Char('m') | KeyCode::Char('M') => app.show_info_overlay(), KeyCode::Char('m') | KeyCode::Char('M') => app.show_info_overlay(),
// Toggle Reload // Toggle Reload

View File

@ -14,7 +14,7 @@ use ratatui::{
use crate::tui::app::{ use crate::tui::app::{
app::{AppState, IAppAccess}, app::{AppState, IAppAccess},
selection::{Category, Selection}, selection::{Category, Selection, WidgetState},
}; };
pub trait IUi { pub trait IUi {
@ -159,11 +159,11 @@ impl OverlayBuilder {
struct ArtistState<'a, 'b> { struct ArtistState<'a, 'b> {
active: bool, active: bool,
list: List<'a>, list: List<'a>,
state: &'b mut ListState, state: &'b mut WidgetState,
} }
impl<'a, 'b> ArtistState<'a, 'b> { impl<'a, 'b> ArtistState<'a, 'b> {
fn new(active: bool, artists: &'a [Artist], state: &'b mut ListState) -> ArtistState<'a, 'b> { fn new(active: bool, artists: &'a [Artist], state: &'b mut WidgetState) -> ArtistState<'a, 'b> {
let list = List::new( let list = List::new(
artists artists
.iter() .iter()
@ -234,12 +234,12 @@ impl<'a> ArtistOverlay<'a> {
struct AlbumState<'a, 'b> { struct AlbumState<'a, 'b> {
active: bool, active: bool,
list: List<'a>, list: List<'a>,
state: &'b mut ListState, state: &'b mut WidgetState,
info: Paragraph<'a>, info: Paragraph<'a>,
} }
impl<'a, 'b> AlbumState<'a, 'b> { impl<'a, 'b> AlbumState<'a, 'b> {
fn new(active: bool, albums: &'a [Album], state: &'b mut ListState) -> AlbumState<'a, 'b> { fn new(active: bool, albums: &'a [Album], state: &'b mut WidgetState) -> AlbumState<'a, 'b> {
let list = List::new( let list = List::new(
albums albums
.iter() .iter()
@ -247,7 +247,7 @@ impl<'a, 'b> AlbumState<'a, 'b> {
.collect::<Vec<ListItem>>(), .collect::<Vec<ListItem>>(),
); );
let album = state.selected().map(|i| &albums[i]); let album = state.list.selected().map(|i| &albums[i]);
let info = Paragraph::new(format!( let info = Paragraph::new(format!(
"Title: {}\n\ "Title: {}\n\
Year: {}", Year: {}",
@ -267,12 +267,12 @@ impl<'a, 'b> AlbumState<'a, 'b> {
struct TrackState<'a, 'b> { struct TrackState<'a, 'b> {
active: bool, active: bool,
list: List<'a>, list: List<'a>,
state: &'b mut ListState, state: &'b mut WidgetState,
info: Paragraph<'a>, info: Paragraph<'a>,
} }
impl<'a, 'b> TrackState<'a, 'b> { impl<'a, 'b> TrackState<'a, 'b> {
fn new(active: bool, tracks: &'a [Track], state: &'b mut ListState) -> TrackState<'a, 'b> { fn new(active: bool, tracks: &'a [Track], state: &'b mut WidgetState) -> TrackState<'a, 'b> {
let list = List::new( let list = List::new(
tracks tracks
.iter() .iter()
@ -280,7 +280,7 @@ impl<'a, 'b> TrackState<'a, 'b> {
.collect::<Vec<ListItem>>(), .collect::<Vec<ListItem>>(),
); );
let track = state.selected().map(|i| &tracks[i]); let track = state.list.selected().map(|i| &tracks[i]);
let info = Paragraph::new(format!( let info = Paragraph::new(format!(
"Track: {}\n\ "Track: {}\n\
Title: {}\n\ Title: {}\n\
@ -342,7 +342,7 @@ impl Ui {
fn render_list_widget<B: Backend>( fn render_list_widget<B: Backend>(
title: &str, title: &str,
list: List, list: List,
list_state: &mut ListState, state: &mut WidgetState,
active: bool, active: bool,
area: Rect, area: Rect,
frame: &mut Frame<'_, B>, frame: &mut Frame<'_, B>,
@ -353,8 +353,9 @@ impl Ui {
.style(Self::style(active, false)) .style(Self::style(active, false))
.block(Self::block(title, active, false)), .block(Self::block(title, active, false)),
area, area,
list_state, &mut state.list,
); );
state.height = area.height.checked_sub(2).unwrap_or(0) as usize;
} }
fn render_info_widget<B: Backend>( fn render_info_widget<B: Backend>(
@ -422,6 +423,7 @@ impl Ui {
let no_albums: Vec<Album> = vec![]; let no_albums: Vec<Album> = vec![];
let albums = artist_selection let albums = artist_selection
.state .state
.list
.selected() .selected()
.map(|i| &artists[i].albums) .map(|i| &artists[i].albums)
.unwrap_or_else(|| &no_albums); .unwrap_or_else(|| &no_albums);
@ -437,6 +439,7 @@ impl Ui {
let no_tracks: Vec<Track> = vec![]; let no_tracks: Vec<Track> = vec![];
let tracks = album_selection let tracks = album_selection
.state .state
.list
.selected() .selected()
.map(|i| &albums[i].tracks) .map(|i| &albums[i].tracks)
.unwrap_or_else(|| &no_tracks); .unwrap_or_else(|| &no_tracks);
@ -458,7 +461,7 @@ impl Ui {
let area = OverlayBuilder::default().build(frame.size()); let area = OverlayBuilder::default().build(frame.size());
let artist_selection = &mut selection.artist; let artist_selection = &mut selection.artist;
let artist_overlay = ArtistOverlay::new(artists, &artist_selection.state); let artist_overlay = ArtistOverlay::new(artists, &artist_selection.state.list);
Self::render_overlay_widget("Artist", artist_overlay.properties, area, false, frame); Self::render_overlay_widget("Artist", artist_overlay.properties, area, false, frame);
} }