Extend incremental search to albums and tracks #152
306
src/tui/app/selection/album.rs
Normal file
306
src/tui/app/selection/album.rs
Normal file
@ -0,0 +1,306 @@
|
||||
use std::cmp;
|
||||
|
||||
use musichoard::collection::{album::{Album, AlbumId}, track::Track};
|
||||
|
||||
use crate::tui::app::selection::{
|
||||
track::{IdSelectTrack, TrackSelection},
|
||||
Delta, SelectionState, WidgetState,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct AlbumSelection {
|
||||
pub state: WidgetState,
|
||||
pub track: TrackSelection,
|
||||
}
|
||||
|
||||
impl AlbumSelection {
|
||||
pub fn initialise(albums: &[Album]) -> Self {
|
||||
let mut selection = AlbumSelection {
|
||||
state: WidgetState::default(),
|
||||
track: TrackSelection::initialise(&[]),
|
||||
};
|
||||
selection.reinitialise(albums, None);
|
||||
selection
|
||||
}
|
||||
|
||||
pub 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 {
|
||||
Ok(index) => self.reinitialise_with_index(albums, index, album.track),
|
||||
Err(index) => self.reinitialise_with_index(albums, index, None),
|
||||
}
|
||||
} else {
|
||||
self.reinitialise_with_index(albums, 0, None)
|
||||
}
|
||||
}
|
||||
|
||||
fn reinitialise_with_index(
|
||||
&mut self,
|
||||
albums: &[Album],
|
||||
index: usize,
|
||||
active_track: Option<IdSelectTrack>,
|
||||
) {
|
||||
if albums.is_empty() {
|
||||
self.state.list.select(None);
|
||||
self.track = TrackSelection::initialise(&[]);
|
||||
} else if index >= albums.len() {
|
||||
let end = albums.len() - 1;
|
||||
self.state.list.select(Some(end));
|
||||
self.track = TrackSelection::initialise(&albums[end].tracks);
|
||||
} else {
|
||||
self.state.list.select(Some(index));
|
||||
self.track.reinitialise(&albums[index].tracks, active_track);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn selected(&self) -> Option<usize> {
|
||||
self.state.list.selected()
|
||||
}
|
||||
|
||||
pub fn selected_track(&self) -> Option<usize> {
|
||||
self.track.selected()
|
||||
}
|
||||
|
||||
pub fn select(&mut self, albums: &[Album], to: Option<usize>) {
|
||||
match to {
|
||||
Some(to) => self.select_to(albums, to),
|
||||
None => self.state.list.select(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub 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);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn selection_state<'a>(&self, list: &'a [Album]) -> Option<SelectionState<'a, Album>> {
|
||||
let selected = self.state.list.selected();
|
||||
selected.map(|index| SelectionState { list, index })
|
||||
}
|
||||
|
||||
pub 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))
|
||||
}
|
||||
|
||||
pub fn reset(&mut self, albums: &[Album]) {
|
||||
if self.state.list.selected() != Some(0) {
|
||||
self.reinitialise(albums, None);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_track(&mut self, albums: &[Album]) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
self.track.reset(&albums[index].tracks);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn increment(&mut self, albums: &[Album], delta: Delta) {
|
||||
self.increment_by(albums, delta.as_usize(&self.state));
|
||||
}
|
||||
|
||||
pub 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 increment_by(&mut self, albums: &[Album], by: usize) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
let mut result = index.saturating_add(by);
|
||||
if result >= albums.len() {
|
||||
result = albums.len() - 1;
|
||||
}
|
||||
if self.state.list.selected() != Some(result) {
|
||||
self.state.list.select(Some(result));
|
||||
self.track = TrackSelection::initialise(&albums[result].tracks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrement(&mut self, albums: &[Album], delta: Delta) {
|
||||
self.decrement_by(albums, delta.as_usize(&self.state));
|
||||
}
|
||||
|
||||
pub fn decrement_track(&mut self, albums: &[Album], delta: Delta) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
self.track.decrement(&albums[index].tracks, delta);
|
||||
}
|
||||
}
|
||||
|
||||
fn decrement_by(&mut self, albums: &[Album], by: usize) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
let result = index.saturating_sub(by);
|
||||
if self.state.list.selected() != Some(result) {
|
||||
self.state.list.select(Some(result));
|
||||
self.track = TrackSelection::initialise(&albums[result].tracks);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IdSelectAlbum {
|
||||
album_id: AlbumId,
|
||||
track: Option<IdSelectTrack>,
|
||||
}
|
||||
|
||||
impl IdSelectAlbum {
|
||||
pub fn get(albums: &[Album], selection: &AlbumSelection) -> Option<Self> {
|
||||
selection.state.list.selected().map(|index| {
|
||||
let album = &albums[index];
|
||||
IdSelectAlbum {
|
||||
album_id: album.get_sort_key().clone(),
|
||||
track: IdSelectTrack::get(&album.tracks, &selection.track),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tui::testmod::COLLECTION;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
||||
#[test]
|
||||
fn album_selection() {
|
||||
let albums = &COLLECTION[0].albums;
|
||||
assert!(albums.len() > 1);
|
||||
|
||||
let mut empty = AlbumSelection::initialise(&[]);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
assert_eq!(empty.track.state.list.selected(), None);
|
||||
|
||||
empty.increment(albums, Delta::Line);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
assert_eq!(empty.track.state.list.selected(), None);
|
||||
|
||||
empty.decrement(albums, Delta::Line);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
assert_eq!(empty.track.state.list.selected(), None);
|
||||
|
||||
let mut sel = AlbumSelection::initialise(albums);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(0));
|
||||
|
||||
sel.increment_track(albums, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(1));
|
||||
|
||||
// Verify that decrement that doesn't change index does not reset track.
|
||||
sel.decrement(albums, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(1));
|
||||
|
||||
sel.increment(albums, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(1));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(0));
|
||||
|
||||
sel.decrement(albums, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(0));
|
||||
|
||||
for _ in 0..(albums.len() + 5) {
|
||||
sel.increment(albums, Delta::Line);
|
||||
}
|
||||
assert_eq!(sel.state.list.selected(), Some(albums.len() - 1));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(0));
|
||||
|
||||
sel.increment_track(albums, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(albums.len() - 1));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(1));
|
||||
|
||||
// Verify that increment that doesn't change index does not reset track.
|
||||
sel.increment(albums, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(albums.len() - 1));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn album_delta_page() {
|
||||
let albums = &COLLECTION[1].albums;
|
||||
assert!(albums.len() > 1);
|
||||
|
||||
let empty = AlbumSelection::initialise(&[]);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
|
||||
let mut sel = AlbumSelection::initialise(albums);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(0));
|
||||
|
||||
assert!(albums.len() >= 4);
|
||||
sel.state.height = 3;
|
||||
|
||||
sel.increment_track(albums, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(1));
|
||||
|
||||
// Verify that decrement that doesn't change index does not reset track.
|
||||
sel.decrement(albums, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(1));
|
||||
|
||||
sel.increment(albums, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(2));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(0));
|
||||
|
||||
sel.decrement(albums, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(0));
|
||||
|
||||
for _ in 0..(albums.len() + 5) {
|
||||
sel.increment(albums, Delta::Page);
|
||||
}
|
||||
assert_eq!(sel.state.list.selected(), Some(albums.len() - 1));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(0));
|
||||
|
||||
sel.increment_track(albums, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(albums.len() - 1));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(1));
|
||||
|
||||
// Verify that increment that doesn't change index does not reset track.
|
||||
sel.increment(albums, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(albums.len() - 1));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn album_reinitialise() {
|
||||
let albums = &COLLECTION[0].albums;
|
||||
assert!(albums.len() > 1);
|
||||
|
||||
let mut sel = AlbumSelection::initialise(albums);
|
||||
sel.state.list.select(Some(albums.len() - 1));
|
||||
sel.track.state.list.select(Some(1));
|
||||
|
||||
// Re-initialise.
|
||||
let expected = sel.clone();
|
||||
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 = 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 = IdSelectAlbum::get(albums, &sel);
|
||||
sel.reinitialise(&[], active_album);
|
||||
assert_eq!(sel, expected);
|
||||
}
|
||||
}
|
338
src/tui/app/selection/artist.rs
Normal file
338
src/tui/app/selection/artist.rs
Normal file
@ -0,0 +1,338 @@
|
||||
use std::cmp;
|
||||
|
||||
use musichoard::collection::{
|
||||
album::Album,
|
||||
artist::{Artist, ArtistId},
|
||||
track::Track,
|
||||
};
|
||||
|
||||
use crate::tui::app::selection::{
|
||||
album::{AlbumSelection, IdSelectAlbum},
|
||||
Delta, SelectionState, WidgetState,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ArtistSelection {
|
||||
pub state: WidgetState,
|
||||
pub album: AlbumSelection,
|
||||
}
|
||||
|
||||
impl ArtistSelection {
|
||||
pub fn initialise(artists: &[Artist]) -> Self {
|
||||
let mut selection = ArtistSelection {
|
||||
state: WidgetState::default(),
|
||||
album: AlbumSelection::initialise(&[]),
|
||||
};
|
||||
selection.reinitialise(artists, None);
|
||||
selection
|
||||
}
|
||||
|
||||
pub 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 {
|
||||
Ok(index) => self.reinitialise_with_index(artists, index, active.album),
|
||||
Err(index) => self.reinitialise_with_index(artists, index, None),
|
||||
}
|
||||
} else {
|
||||
self.reinitialise_with_index(artists, 0, None)
|
||||
}
|
||||
}
|
||||
|
||||
fn reinitialise_with_index(
|
||||
&mut self,
|
||||
artists: &[Artist],
|
||||
index: usize,
|
||||
active_album: Option<IdSelectAlbum>,
|
||||
) {
|
||||
if artists.is_empty() {
|
||||
self.state.list.select(None);
|
||||
self.album = AlbumSelection::initialise(&[]);
|
||||
} else if index >= artists.len() {
|
||||
let end = artists.len() - 1;
|
||||
self.state.list.select(Some(end));
|
||||
self.album = AlbumSelection::initialise(&artists[end].albums);
|
||||
} else {
|
||||
self.state.list.select(Some(index));
|
||||
self.album
|
||||
.reinitialise(&artists[index].albums, active_album);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn selected(&self) -> Option<usize> {
|
||||
self.state.list.selected()
|
||||
}
|
||||
|
||||
pub fn selected_album(&self) -> Option<usize> {
|
||||
self.album.selected()
|
||||
}
|
||||
|
||||
pub fn selected_track(&self) -> Option<usize> {
|
||||
self.album.selected_track()
|
||||
}
|
||||
|
||||
pub fn select(&mut self, artists: &[Artist], to: Option<usize>) {
|
||||
match to {
|
||||
Some(to) => self.select_to(artists, to),
|
||||
None => self.state.list.select(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub 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);
|
||||
}
|
||||
}
|
||||
|
||||
pub 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) {
|
||||
self.state.list.select(Some(to));
|
||||
self.album = AlbumSelection::initialise(&artists[to].albums);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn selection_state<'a>(&self, list: &'a [Artist]) -> Option<SelectionState<'a, Artist>> {
|
||||
let selected = self.state.list.selected();
|
||||
selected.map(|index| SelectionState { list, index })
|
||||
}
|
||||
|
||||
pub 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))
|
||||
}
|
||||
|
||||
pub 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))
|
||||
}
|
||||
|
||||
pub fn reset(&mut self, artists: &[Artist]) {
|
||||
if self.state.list.selected() != Some(0) {
|
||||
self.reinitialise(artists, None);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_album(&mut self, artists: &[Artist]) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
self.album.reset(&artists[index].albums);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_track(&mut self, artists: &[Artist]) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
self.album.reset_track(&artists[index].albums);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn increment(&mut self, artists: &[Artist], delta: Delta) {
|
||||
self.increment_by(artists, delta.as_usize(&self.state));
|
||||
}
|
||||
|
||||
pub fn increment_album(&mut self, artists: &[Artist], delta: Delta) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
self.album.increment(&artists[index].albums, delta);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn increment_track(&mut self, artists: &[Artist], delta: Delta) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
self.album.increment_track(&artists[index].albums, delta);
|
||||
}
|
||||
}
|
||||
|
||||
fn increment_by(&mut self, artists: &[Artist], by: usize) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
let result = index.saturating_add(by);
|
||||
self.select_to(artists, result);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrement(&mut self, artists: &[Artist], delta: Delta) {
|
||||
self.decrement_by(artists, delta.as_usize(&self.state));
|
||||
}
|
||||
|
||||
pub fn decrement_album(&mut self, artists: &[Artist], delta: Delta) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
self.album.decrement(&artists[index].albums, delta);
|
||||
}
|
||||
}
|
||||
|
||||
pub 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);
|
||||
}
|
||||
}
|
||||
|
||||
fn decrement_by(&mut self, artists: &[Artist], by: usize) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
let result = index.saturating_sub(by);
|
||||
if self.state.list.selected() != Some(result) {
|
||||
self.state.list.select(Some(result));
|
||||
self.album = AlbumSelection::initialise(&artists[result].albums);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IdSelectArtist {
|
||||
artist_id: ArtistId,
|
||||
album: Option<IdSelectAlbum>,
|
||||
}
|
||||
|
||||
impl IdSelectArtist {
|
||||
pub fn get(artists: &[Artist], selection: &ArtistSelection) -> Option<Self> {
|
||||
selection.state.list.selected().map(|index| {
|
||||
let artist = &artists[index];
|
||||
IdSelectArtist {
|
||||
artist_id: artist.get_sort_key().clone(),
|
||||
album: IdSelectAlbum::get(&artist.albums, &selection.album),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tui::testmod::COLLECTION;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
||||
#[test]
|
||||
fn artist_selection() {
|
||||
let artists = &COLLECTION;
|
||||
assert!(artists.len() > 1);
|
||||
|
||||
let mut empty = ArtistSelection::initialise(&[]);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
assert_eq!(empty.album.state.list.selected(), None);
|
||||
|
||||
empty.increment(artists, Delta::Line);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
assert_eq!(empty.album.state.list.selected(), None);
|
||||
|
||||
empty.decrement(artists, Delta::Line);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
assert_eq!(empty.album.state.list.selected(), None);
|
||||
|
||||
let mut sel = ArtistSelection::initialise(artists);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(0));
|
||||
|
||||
sel.increment_album(artists, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(1));
|
||||
|
||||
// Verify that decrement that doesn't change index does not reset album.
|
||||
sel.decrement(artists, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(1));
|
||||
|
||||
sel.increment(artists, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(1));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(0));
|
||||
|
||||
sel.decrement(artists, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(0));
|
||||
|
||||
for _ in 0..(artists.len() + 5) {
|
||||
sel.increment(artists, Delta::Line);
|
||||
}
|
||||
assert_eq!(sel.state.list.selected(), Some(artists.len() - 1));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(0));
|
||||
|
||||
sel.increment_album(artists, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(artists.len() - 1));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(1));
|
||||
|
||||
// Verify that increment that doesn't change index does not reset album.
|
||||
sel.increment(artists, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(artists.len() - 1));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn artist_delta_page() {
|
||||
let artists = &COLLECTION;
|
||||
assert!(artists.len() > 1);
|
||||
|
||||
let empty = ArtistSelection::initialise(&[]);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
|
||||
let mut sel = ArtistSelection::initialise(artists);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(0));
|
||||
|
||||
assert!(artists.len() >= 4);
|
||||
sel.state.height = 3;
|
||||
|
||||
sel.increment_album(artists, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(1));
|
||||
|
||||
// Verify that decrement that doesn't change index does not reset album.
|
||||
sel.decrement(artists, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(1));
|
||||
|
||||
sel.increment(artists, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(2));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(0));
|
||||
|
||||
sel.decrement(artists, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(0));
|
||||
|
||||
for _ in 0..(artists.len() + 5) {
|
||||
sel.increment(artists, Delta::Page);
|
||||
}
|
||||
assert_eq!(sel.state.list.selected(), Some(artists.len() - 1));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(0));
|
||||
|
||||
sel.increment_album(artists, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(artists.len() - 1));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(1));
|
||||
|
||||
// Verify that increment that doesn't change index does not reset album.
|
||||
sel.increment(artists, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(artists.len() - 1));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn artist_reinitialise() {
|
||||
let artists = &COLLECTION;
|
||||
assert!(artists.len() > 1);
|
||||
|
||||
let mut sel = ArtistSelection::initialise(artists);
|
||||
sel.state.list.select(Some(artists.len() - 1));
|
||||
sel.album.state.list.select(Some(1));
|
||||
|
||||
// Re-initialise.
|
||||
let expected = sel.clone();
|
||||
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 = 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 = IdSelectArtist::get(artists, &sel);
|
||||
sel.reinitialise(&[], active_artist);
|
||||
assert_eq!(sel, expected);
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
use musichoard::collection::{
|
||||
album::{Album, AlbumId},
|
||||
artist::{Artist, ArtistId},
|
||||
track::{Track, TrackId},
|
||||
Collection,
|
||||
};
|
||||
mod album;
|
||||
mod artist;
|
||||
mod track;
|
||||
|
||||
use musichoard::collection::{album::Album, artist::Artist, track::Track, Collection};
|
||||
use ratatui::widgets::ListState;
|
||||
use std::cmp;
|
||||
|
||||
use artist::{ArtistSelection, IdSelectArtist};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Category {
|
||||
@ -31,21 +31,9 @@ pub struct Selection {
|
||||
pub artist: ArtistSelection,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ArtistSelection {
|
||||
pub state: WidgetState,
|
||||
pub album: AlbumSelection,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct AlbumSelection {
|
||||
pub state: WidgetState,
|
||||
pub track: TrackSelection,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TrackSelection {
|
||||
pub state: WidgetState,
|
||||
pub struct SelectionState<'a, T> {
|
||||
pub list: &'a [T],
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
pub enum Delta {
|
||||
@ -62,11 +50,6 @@ impl Delta {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SelectionState<'a, T> {
|
||||
pub list: &'a [T],
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
impl Selection {
|
||||
pub fn new(artists: &[Artist]) -> Self {
|
||||
Selection {
|
||||
@ -214,393 +197,6 @@ impl Selection {
|
||||
}
|
||||
}
|
||||
|
||||
impl ArtistSelection {
|
||||
fn initialise(artists: &[Artist]) -> Self {
|
||||
let mut selection = ArtistSelection {
|
||||
state: WidgetState::default(),
|
||||
album: AlbumSelection::initialise(&[]),
|
||||
};
|
||||
selection.reinitialise(artists, None);
|
||||
selection
|
||||
}
|
||||
|
||||
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 {
|
||||
Ok(index) => self.reinitialise_with_index(artists, index, active.album),
|
||||
Err(index) => self.reinitialise_with_index(artists, index, None),
|
||||
}
|
||||
} else {
|
||||
self.reinitialise_with_index(artists, 0, None)
|
||||
}
|
||||
}
|
||||
|
||||
fn reinitialise_with_index(
|
||||
&mut self,
|
||||
artists: &[Artist],
|
||||
index: usize,
|
||||
active_album: Option<IdSelectAlbum>,
|
||||
) {
|
||||
if artists.is_empty() {
|
||||
self.state.list.select(None);
|
||||
self.album = AlbumSelection::initialise(&[]);
|
||||
} else if index >= artists.len() {
|
||||
let end = artists.len() - 1;
|
||||
self.state.list.select(Some(end));
|
||||
self.album = AlbumSelection::initialise(&artists[end].albums);
|
||||
} else {
|
||||
self.state.list.select(Some(index));
|
||||
self.album
|
||||
.reinitialise(&artists[index].albums, active_album);
|
||||
}
|
||||
}
|
||||
|
||||
fn selected(&self) -> Option<usize> {
|
||||
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),
|
||||
None => self.state.list.select(None),
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
self.state.list.select(Some(to));
|
||||
self.album = AlbumSelection::initialise(&artists[to].albums);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
self.select_to(artists, result);
|
||||
}
|
||||
}
|
||||
|
||||
fn increment(&mut self, artists: &[Artist], delta: Delta) {
|
||||
self.increment_by(artists, delta.as_usize(&self.state));
|
||||
}
|
||||
|
||||
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 increment_track(&mut self, artists: &[Artist], delta: Delta) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
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.saturating_sub(by);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AlbumSelection {
|
||||
fn initialise(albums: &[Album]) -> Self {
|
||||
let mut selection = AlbumSelection {
|
||||
state: WidgetState::default(),
|
||||
track: TrackSelection::initialise(&[]),
|
||||
};
|
||||
selection.reinitialise(albums, None);
|
||||
selection
|
||||
}
|
||||
|
||||
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 {
|
||||
Ok(index) => self.reinitialise_with_index(albums, index, album.track),
|
||||
Err(index) => self.reinitialise_with_index(albums, index, None),
|
||||
}
|
||||
} else {
|
||||
self.reinitialise_with_index(albums, 0, None)
|
||||
}
|
||||
}
|
||||
|
||||
fn reinitialise_with_index(
|
||||
&mut self,
|
||||
albums: &[Album],
|
||||
index: usize,
|
||||
active_track: Option<IdSelectTrack>,
|
||||
) {
|
||||
if albums.is_empty() {
|
||||
self.state.list.select(None);
|
||||
self.track = TrackSelection::initialise(&[]);
|
||||
} else if index >= albums.len() {
|
||||
let end = albums.len() - 1;
|
||||
self.state.list.select(Some(end));
|
||||
self.track = TrackSelection::initialise(&albums[end].tracks);
|
||||
} else {
|
||||
self.state.list.select(Some(index));
|
||||
self.track.reinitialise(&albums[index].tracks, active_track);
|
||||
}
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
if result >= albums.len() {
|
||||
result = albums.len() - 1;
|
||||
}
|
||||
if self.state.list.selected() != Some(result) {
|
||||
self.state.list.select(Some(result));
|
||||
self.track = TrackSelection::initialise(&albums[result].tracks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn increment(&mut self, albums: &[Album], delta: Delta) {
|
||||
self.increment_by(albums, delta.as_usize(&self.state));
|
||||
}
|
||||
|
||||
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.saturating_sub(by);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TrackSelection {
|
||||
fn initialise(tracks: &[Track]) -> Self {
|
||||
let mut selection = TrackSelection {
|
||||
state: WidgetState::default(),
|
||||
};
|
||||
selection.reinitialise(tracks, None);
|
||||
selection
|
||||
}
|
||||
|
||||
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 {
|
||||
Ok(index) | Err(index) => self.reinitialise_with_index(tracks, index),
|
||||
}
|
||||
} else {
|
||||
self.reinitialise_with_index(tracks, 0)
|
||||
}
|
||||
}
|
||||
|
||||
fn reinitialise_with_index(&mut self, tracks: &[Track], index: usize) {
|
||||
if tracks.is_empty() {
|
||||
self.state.list.select(None);
|
||||
} else if index >= tracks.len() {
|
||||
self.state.list.select(Some(tracks.len() - 1));
|
||||
} else {
|
||||
self.state.list.select(Some(index));
|
||||
}
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
||||
fn increment_by(&mut self, tracks: &[Track], by: usize) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
let mut result = index.saturating_add(by);
|
||||
if result >= tracks.len() {
|
||||
result = tracks.len() - 1;
|
||||
}
|
||||
if self.state.list.selected() != Some(result) {
|
||||
self.state.list.select(Some(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn increment(&mut self, tracks: &[Track], delta: Delta) {
|
||||
self.increment_by(tracks, delta.as_usize(&self.state));
|
||||
}
|
||||
|
||||
fn decrement_by(&mut self, _tracks: &[Track], by: usize) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
let result = index.saturating_sub(by);
|
||||
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 ListSelection {
|
||||
pub artist: ListState,
|
||||
pub album: ListState,
|
||||
@ -621,20 +217,6 @@ 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 {
|
||||
@ -643,398 +225,12 @@ impl IdSelection {
|
||||
}
|
||||
}
|
||||
|
||||
impl IdSelectArtist {
|
||||
fn get(artists: &[Artist], selection: &ArtistSelection) -> Option<Self> {
|
||||
selection.state.list.selected().map(|index| {
|
||||
let artist = &artists[index];
|
||||
IdSelectArtist {
|
||||
artist_id: artist.get_sort_key().clone(),
|
||||
album: IdSelectAlbum::get(&artist.albums, &selection.album),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl IdSelectAlbum {
|
||||
fn get(albums: &[Album], selection: &AlbumSelection) -> Option<Self> {
|
||||
selection.state.list.selected().map(|index| {
|
||||
let album = &albums[index];
|
||||
IdSelectAlbum {
|
||||
album_id: album.get_sort_key().clone(),
|
||||
track: IdSelectTrack::get(&album.tracks, &selection.track),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl IdSelectTrack {
|
||||
fn get(tracks: &[Track], selection: &TrackSelection) -> Option<Self> {
|
||||
selection.state.list.selected().map(|index| {
|
||||
let track = &tracks[index];
|
||||
IdSelectTrack {
|
||||
track_id: track.get_sort_key().clone(),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tui::testmod::COLLECTION;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn track_selection() {
|
||||
let tracks = &COLLECTION[0].albums[0].tracks;
|
||||
assert!(tracks.len() > 1);
|
||||
|
||||
let mut empty = TrackSelection::initialise(&[]);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
|
||||
empty.increment(tracks, Delta::Line);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
|
||||
empty.decrement(tracks, Delta::Line);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
|
||||
let mut sel = TrackSelection::initialise(tracks);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
|
||||
sel.decrement(tracks, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
|
||||
sel.increment(tracks, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(1));
|
||||
|
||||
sel.decrement(tracks, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
|
||||
for _ in 0..(tracks.len() + 5) {
|
||||
sel.increment(tracks, Delta::Line);
|
||||
}
|
||||
assert_eq!(sel.state.list.selected(), Some(tracks.len() - 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn track_delta_page() {
|
||||
let tracks = &COLLECTION[0].albums[0].tracks;
|
||||
assert!(tracks.len() > 1);
|
||||
|
||||
let empty = TrackSelection::initialise(&[]);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
|
||||
let mut sel = TrackSelection::initialise(tracks);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
|
||||
assert!(tracks.len() >= 4);
|
||||
sel.state.height = 3;
|
||||
|
||||
sel.decrement(tracks, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
|
||||
sel.increment(tracks, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(2));
|
||||
|
||||
sel.decrement(tracks, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
|
||||
for _ in 0..(tracks.len() + 5) {
|
||||
sel.increment(tracks, Delta::Page);
|
||||
}
|
||||
assert_eq!(sel.state.list.selected(), Some(tracks.len() - 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn track_reinitialise() {
|
||||
let tracks = &COLLECTION[0].albums[0].tracks;
|
||||
assert!(tracks.len() > 1);
|
||||
|
||||
let mut sel = TrackSelection::initialise(tracks);
|
||||
sel.state.list.select(Some(tracks.len() - 1));
|
||||
|
||||
// Re-initialise.
|
||||
let expected = sel.clone();
|
||||
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 = 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 = IdSelectTrack::get(tracks, &sel);
|
||||
sel.reinitialise(&[], active_track);
|
||||
assert_eq!(sel, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn album_selection() {
|
||||
let albums = &COLLECTION[0].albums;
|
||||
assert!(albums.len() > 1);
|
||||
|
||||
let mut empty = AlbumSelection::initialise(&[]);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
assert_eq!(empty.track.state.list.selected(), None);
|
||||
|
||||
empty.increment(albums, Delta::Line);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
assert_eq!(empty.track.state.list.selected(), None);
|
||||
|
||||
empty.decrement(albums, Delta::Line);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
assert_eq!(empty.track.state.list.selected(), None);
|
||||
|
||||
let mut sel = AlbumSelection::initialise(albums);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(0));
|
||||
|
||||
sel.increment_track(albums, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(1));
|
||||
|
||||
// Verify that decrement that doesn't change index does not reset track.
|
||||
sel.decrement(albums, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(1));
|
||||
|
||||
sel.increment(albums, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(1));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(0));
|
||||
|
||||
sel.decrement(albums, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(0));
|
||||
|
||||
for _ in 0..(albums.len() + 5) {
|
||||
sel.increment(albums, Delta::Line);
|
||||
}
|
||||
assert_eq!(sel.state.list.selected(), Some(albums.len() - 1));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(0));
|
||||
|
||||
sel.increment_track(albums, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(albums.len() - 1));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(1));
|
||||
|
||||
// Verify that increment that doesn't change index does not reset track.
|
||||
sel.increment(albums, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(albums.len() - 1));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn album_delta_page() {
|
||||
let albums = &COLLECTION[1].albums;
|
||||
assert!(albums.len() > 1);
|
||||
|
||||
let empty = AlbumSelection::initialise(&[]);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
|
||||
let mut sel = AlbumSelection::initialise(albums);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(0));
|
||||
|
||||
assert!(albums.len() >= 4);
|
||||
sel.state.height = 3;
|
||||
|
||||
sel.increment_track(albums, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(1));
|
||||
|
||||
// Verify that decrement that doesn't change index does not reset track.
|
||||
sel.decrement(albums, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(1));
|
||||
|
||||
sel.increment(albums, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(2));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(0));
|
||||
|
||||
sel.decrement(albums, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(0));
|
||||
|
||||
for _ in 0..(albums.len() + 5) {
|
||||
sel.increment(albums, Delta::Page);
|
||||
}
|
||||
assert_eq!(sel.state.list.selected(), Some(albums.len() - 1));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(0));
|
||||
|
||||
sel.increment_track(albums, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(albums.len() - 1));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(1));
|
||||
|
||||
// Verify that increment that doesn't change index does not reset track.
|
||||
sel.increment(albums, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(albums.len() - 1));
|
||||
assert_eq!(sel.track.state.list.selected(), Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn album_reinitialise() {
|
||||
let albums = &COLLECTION[0].albums;
|
||||
assert!(albums.len() > 1);
|
||||
|
||||
let mut sel = AlbumSelection::initialise(albums);
|
||||
sel.state.list.select(Some(albums.len() - 1));
|
||||
sel.track.state.list.select(Some(1));
|
||||
|
||||
// Re-initialise.
|
||||
let expected = sel.clone();
|
||||
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 = 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 = IdSelectAlbum::get(albums, &sel);
|
||||
sel.reinitialise(&[], active_album);
|
||||
assert_eq!(sel, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn artist_selection() {
|
||||
let artists = &COLLECTION;
|
||||
assert!(artists.len() > 1);
|
||||
|
||||
let mut empty = ArtistSelection::initialise(&[]);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
assert_eq!(empty.album.state.list.selected(), None);
|
||||
|
||||
empty.increment(artists, Delta::Line);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
assert_eq!(empty.album.state.list.selected(), None);
|
||||
|
||||
empty.decrement(artists, Delta::Line);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
assert_eq!(empty.album.state.list.selected(), None);
|
||||
|
||||
let mut sel = ArtistSelection::initialise(artists);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(0));
|
||||
|
||||
sel.increment_album(artists, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(1));
|
||||
|
||||
// Verify that decrement that doesn't change index does not reset album.
|
||||
sel.decrement(artists, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(1));
|
||||
|
||||
sel.increment(artists, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(1));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(0));
|
||||
|
||||
sel.decrement(artists, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(0));
|
||||
|
||||
for _ in 0..(artists.len() + 5) {
|
||||
sel.increment(artists, Delta::Line);
|
||||
}
|
||||
assert_eq!(sel.state.list.selected(), Some(artists.len() - 1));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(0));
|
||||
|
||||
sel.increment_album(artists, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(artists.len() - 1));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(1));
|
||||
|
||||
// Verify that increment that doesn't change index does not reset album.
|
||||
sel.increment(artists, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(artists.len() - 1));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn artist_delta_page() {
|
||||
let artists = &COLLECTION;
|
||||
assert!(artists.len() > 1);
|
||||
|
||||
let empty = ArtistSelection::initialise(&[]);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
|
||||
let mut sel = ArtistSelection::initialise(artists);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(0));
|
||||
|
||||
assert!(artists.len() >= 4);
|
||||
sel.state.height = 3;
|
||||
|
||||
sel.increment_album(artists, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(1));
|
||||
|
||||
// Verify that decrement that doesn't change index does not reset album.
|
||||
sel.decrement(artists, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(1));
|
||||
|
||||
sel.increment(artists, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(2));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(0));
|
||||
|
||||
sel.decrement(artists, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(0));
|
||||
|
||||
for _ in 0..(artists.len() + 5) {
|
||||
sel.increment(artists, Delta::Page);
|
||||
}
|
||||
assert_eq!(sel.state.list.selected(), Some(artists.len() - 1));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(0));
|
||||
|
||||
sel.increment_album(artists, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(artists.len() - 1));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(1));
|
||||
|
||||
// Verify that increment that doesn't change index does not reset album.
|
||||
sel.increment(artists, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(artists.len() - 1));
|
||||
assert_eq!(sel.album.state.list.selected(), Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn artist_reinitialise() {
|
||||
let artists = &COLLECTION;
|
||||
assert!(artists.len() > 1);
|
||||
|
||||
let mut sel = ArtistSelection::initialise(artists);
|
||||
sel.state.list.select(Some(artists.len() - 1));
|
||||
sel.album.state.list.select(Some(1));
|
||||
|
||||
// Re-initialise.
|
||||
let expected = sel.clone();
|
||||
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 = 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 = IdSelectArtist::get(artists, &sel);
|
||||
sel.reinitialise(&[], active_artist);
|
||||
assert_eq!(sel, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn selection() {
|
||||
let mut selection = Selection::new(&COLLECTION);
|
||||
|
208
src/tui/app/selection/track.rs
Normal file
208
src/tui/app/selection/track.rs
Normal file
@ -0,0 +1,208 @@
|
||||
use std::cmp;
|
||||
|
||||
use musichoard::collection::track::{Track, TrackId};
|
||||
|
||||
use crate::tui::app::selection::{Delta, SelectionState, WidgetState};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TrackSelection {
|
||||
pub state: WidgetState,
|
||||
}
|
||||
|
||||
impl TrackSelection {
|
||||
pub fn initialise(tracks: &[Track]) -> Self {
|
||||
let mut selection = TrackSelection {
|
||||
state: WidgetState::default(),
|
||||
};
|
||||
selection.reinitialise(tracks, None);
|
||||
selection
|
||||
}
|
||||
|
||||
pub 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 {
|
||||
Ok(index) | Err(index) => self.reinitialise_with_index(tracks, index),
|
||||
}
|
||||
} else {
|
||||
self.reinitialise_with_index(tracks, 0)
|
||||
}
|
||||
}
|
||||
|
||||
fn reinitialise_with_index(&mut self, tracks: &[Track], index: usize) {
|
||||
if tracks.is_empty() {
|
||||
self.state.list.select(None);
|
||||
} else if index >= tracks.len() {
|
||||
self.state.list.select(Some(tracks.len() - 1));
|
||||
} else {
|
||||
self.state.list.select(Some(index));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn selected(&self) -> Option<usize> {
|
||||
self.state.list.selected()
|
||||
}
|
||||
|
||||
pub 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));
|
||||
}
|
||||
|
||||
pub fn selection_state<'a>(&self, list: &'a [Track]) -> Option<SelectionState<'a, Track>> {
|
||||
let selected = self.state.list.selected();
|
||||
selected.map(|index| SelectionState { list, index })
|
||||
}
|
||||
|
||||
pub fn reset(&mut self, tracks: &[Track]) {
|
||||
if self.state.list.selected() != Some(0) {
|
||||
self.reinitialise(tracks, None);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn increment(&mut self, tracks: &[Track], delta: Delta) {
|
||||
self.increment_by(tracks, delta.as_usize(&self.state));
|
||||
}
|
||||
|
||||
fn increment_by(&mut self, tracks: &[Track], by: usize) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
let mut result = index.saturating_add(by);
|
||||
if result >= tracks.len() {
|
||||
result = tracks.len() - 1;
|
||||
}
|
||||
if self.state.list.selected() != Some(result) {
|
||||
self.state.list.select(Some(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrement(&mut self, tracks: &[Track], delta: Delta) {
|
||||
self.decrement_by(tracks, delta.as_usize(&self.state));
|
||||
}
|
||||
|
||||
fn decrement_by(&mut self, _tracks: &[Track], by: usize) {
|
||||
if let Some(index) = self.state.list.selected() {
|
||||
let result = index.saturating_sub(by);
|
||||
if self.state.list.selected() != Some(result) {
|
||||
self.state.list.select(Some(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IdSelectTrack {
|
||||
track_id: TrackId,
|
||||
}
|
||||
|
||||
impl IdSelectTrack {
|
||||
pub fn get(tracks: &[Track], selection: &TrackSelection) -> Option<Self> {
|
||||
selection.state.list.selected().map(|index| {
|
||||
let track = &tracks[index];
|
||||
IdSelectTrack {
|
||||
track_id: track.get_sort_key().clone(),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tui::testmod::COLLECTION;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn track_selection() {
|
||||
let tracks = &COLLECTION[0].albums[0].tracks;
|
||||
assert!(tracks.len() > 1);
|
||||
|
||||
let mut empty = TrackSelection::initialise(&[]);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
|
||||
empty.increment(tracks, Delta::Line);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
|
||||
empty.decrement(tracks, Delta::Line);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
|
||||
let mut sel = TrackSelection::initialise(tracks);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
|
||||
sel.decrement(tracks, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
|
||||
sel.increment(tracks, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(1));
|
||||
|
||||
sel.decrement(tracks, Delta::Line);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
|
||||
for _ in 0..(tracks.len() + 5) {
|
||||
sel.increment(tracks, Delta::Line);
|
||||
}
|
||||
assert_eq!(sel.state.list.selected(), Some(tracks.len() - 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn track_delta_page() {
|
||||
let tracks = &COLLECTION[0].albums[0].tracks;
|
||||
assert!(tracks.len() > 1);
|
||||
|
||||
let empty = TrackSelection::initialise(&[]);
|
||||
assert_eq!(empty.state.list.selected(), None);
|
||||
|
||||
let mut sel = TrackSelection::initialise(tracks);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
|
||||
assert!(tracks.len() >= 4);
|
||||
sel.state.height = 3;
|
||||
|
||||
sel.decrement(tracks, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
|
||||
sel.increment(tracks, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(2));
|
||||
|
||||
sel.decrement(tracks, Delta::Page);
|
||||
assert_eq!(sel.state.list.selected(), Some(0));
|
||||
|
||||
for _ in 0..(tracks.len() + 5) {
|
||||
sel.increment(tracks, Delta::Page);
|
||||
}
|
||||
assert_eq!(sel.state.list.selected(), Some(tracks.len() - 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn track_reinitialise() {
|
||||
let tracks = &COLLECTION[0].albums[0].tracks;
|
||||
assert!(tracks.len() > 1);
|
||||
|
||||
let mut sel = TrackSelection::initialise(tracks);
|
||||
sel.state.list.select(Some(tracks.len() - 1));
|
||||
|
||||
// Re-initialise.
|
||||
let expected = sel.clone();
|
||||
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 = 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 = IdSelectTrack::get(tracks, &sel);
|
||||
sel.reinitialise(&[], active_track);
|
||||
assert_eq!(sel, expected);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user