Fix empty list issue

This commit is contained in:
Wojciech Kozlowski 2023-04-12 17:36:20 +02:00
parent 9fd09e9db2
commit f8b5680dc0
2 changed files with 272 additions and 114 deletions

View File

@ -9,11 +9,105 @@ pub enum Category {
Track,
}
pub struct Selection {
struct TrackSelection {
index: u16,
}
impl TrackSelection {
fn initialise(tracks: &Vec<Track>) -> Option<TrackSelection> {
if !tracks.is_empty() {
Some(TrackSelection { index: 0 })
} else {
None
}
}
fn increment(&mut self, tracks: &Vec<Track>) {
if let Some(result) = self.index.checked_add(1) {
if (result as usize) < tracks.len() {
self.index = result;
}
}
}
fn decrement(&mut self, _tracks: &Vec<Track>) {
if let Some(result) = self.index.checked_sub(1) {
self.index = result;
}
}
}
struct AlbumSelection {
index: u16,
track: Option<TrackSelection>,
}
impl AlbumSelection {
fn initialise(albums: &Vec<Album>) -> Option<AlbumSelection> {
if !albums.is_empty() {
Some(AlbumSelection {
index: 0,
track: TrackSelection::initialise(&albums[0].tracks),
})
} else {
None
}
}
fn increment(&mut self, albums: &Vec<Album>) {
if let Some(result) = self.index.checked_add(1) {
if (result as usize) < albums.len() {
self.index = result;
self.track = TrackSelection::initialise(&albums[self.index as usize].tracks);
}
}
}
fn decrement(&mut self, albums: &Vec<Album>) {
if let Some(result) = self.index.checked_sub(1) {
self.index = result;
self.track = TrackSelection::initialise(&albums[self.index as usize].tracks);
}
}
}
struct ArtistSelection {
index: u16,
album: Option<AlbumSelection>,
}
impl ArtistSelection {
fn initialise(artists: &Vec<Artist>) -> Option<ArtistSelection> {
if !artists.is_empty() {
Some(ArtistSelection {
index: 0,
album: AlbumSelection::initialise(&artists[0].albums),
})
} else {
None
}
}
fn increment(&mut self, artists: &Vec<Artist>) {
if let Some(result) = self.index.checked_add(1) {
if (result as usize) < artists.len() {
self.index = result;
self.album = AlbumSelection::initialise(&artists[self.index as usize].albums);
}
}
}
fn decrement(&mut self, artists: &Vec<Artist>) {
if let Some(result) = self.index.checked_sub(1) {
self.index = result;
self.album = AlbumSelection::initialise(&artists[self.index as usize].albums);
}
}
}
struct Selection {
active: Category,
artist: u16,
album: u16,
track: u16,
artist: Option<ArtistSelection>,
}
pub struct App {
@ -25,14 +119,13 @@ pub struct App {
impl App {
pub fn new(mut collection_manager: CollectionManager) -> Result<Self, Error> {
collection_manager.rescan_library()?;
let selection = Selection {
active: Category::Artist,
artist: ArtistSelection::initialise(collection_manager.get_collection()),
};
Ok(App {
collection_manager,
selection: Selection {
active: Category::Artist,
artist: 0,
album: 0,
track: 0,
},
selection,
running: true,
})
}
@ -74,56 +167,62 @@ impl App {
}
fn increment_artist_selection(&mut self) {
if let Some(result) = self.selection.artist.checked_add(1) {
let artists: &Vec<Artist> = self.collection_manager.get_collection();
if (result as usize) < artists.len() {
self.selection.artist = result;
self.selection.album = 0;
self.selection.track = 0;
}
if let Some(ref mut artist_selection) = self.selection.artist {
let artists = &self.collection_manager.get_collection();
artist_selection.increment(artists);
}
}
fn decrement_artist_selection(&mut self) {
if let Some(result) = self.selection.artist.checked_sub(1) {
self.selection.artist = result;
self.selection.album = 0;
self.selection.track = 0;
if let Some(ref mut artist_selection) = self.selection.artist {
let artists = &self.collection_manager.get_collection();
artist_selection.decrement(artists);
}
}
fn increment_album_selection(&mut self) {
if let Some(result) = self.selection.album.checked_add(1) {
let artists: &Vec<Artist> = self.collection_manager.get_collection();
let albums: &Vec<Album> = &artists[self.selection.artist as usize].albums;
if (result as usize) < albums.len() {
self.selection.album = result;
self.selection.track = 0;
if let Some(ref mut artist_selection) = self.selection.artist {
if let Some(ref mut album_selection) = artist_selection.album {
let artists = &self.collection_manager.get_collection();
let albums = &artists[artist_selection.index as usize].albums;
album_selection.increment(albums);
}
}
}
fn decrement_album_selection(&mut self) {
if let Some(result) = self.selection.album.checked_sub(1) {
self.selection.album = result;
self.selection.track = 0;
if let Some(ref mut artist_selection) = self.selection.artist {
if let Some(ref mut album_selection) = artist_selection.album {
let artists = &self.collection_manager.get_collection();
let albums = &artists[artist_selection.index as usize].albums;
album_selection.decrement(albums);
}
}
}
fn increment_track_selection(&mut self) {
if let Some(result) = self.selection.track.checked_add(1) {
let artists: &Vec<Artist> = self.collection_manager.get_collection();
let albums: &Vec<Album> = &artists[self.selection.artist as usize].albums;
let tracks: &Vec<Track> = &albums[self.selection.album as usize].tracks;
if (result as usize) < tracks.len() {
self.selection.track = result;
if let Some(ref mut artist_selection) = self.selection.artist {
if let Some(ref mut album_selection) = artist_selection.album {
if let Some(ref mut track_selection) = album_selection.track {
let artists = &self.collection_manager.get_collection();
let albums = &artists[artist_selection.index as usize].albums;
let tracks = &albums[album_selection.index as usize].tracks;
track_selection.increment(tracks);
}
}
}
}
fn decrement_track_selection(&mut self) {
if let Some(result) = self.selection.track.checked_sub(1) {
self.selection.track = result;
if let Some(ref mut artist_selection) = self.selection.artist {
if let Some(ref mut album_selection) = artist_selection.album {
if let Some(ref mut track_selection) = album_selection.track {
let artists = &self.collection_manager.get_collection();
let albums = &artists[artist_selection.index as usize].albums;
let tracks = &albums[album_selection.index as usize].tracks;
track_selection.decrement(tracks);
}
}
}
}
@ -131,40 +230,92 @@ impl App {
self.selection.active
}
pub fn get_artists(&self) -> Vec<&ArtistId> {
self.collection_manager
.get_collection()
.iter()
.map(|a| &a.id)
.collect()
fn get_artists(&self) -> Option<&Vec<Artist>> {
Some(&self.collection_manager.get_collection())
}
pub fn selected_artist(&self) -> usize {
self.selection.artist as usize
fn get_albums(&self) -> Option<&Vec<Album>> {
if let Some(artists) = self.get_artists() {
if let Some(artist_index) = self.selected_artist() {
Some(&artists[artist_index].albums)
} else {
None
}
} else {
None
}
}
pub fn get_albums(&self) -> Vec<&AlbumId> {
self.collection_manager.get_collection()[self.selection.artist as usize]
.albums
.iter()
.map(|a| &a.id)
.collect()
fn get_tracks(&self) -> Option<&Vec<Track>> {
if let Some(albums) = self.get_albums() {
if let Some(album_index) = self.selected_album() {
Some(&albums[album_index].tracks)
} else {
None
}
} else {
None
}
}
pub fn selected_album(&self) -> usize {
self.selection.album as usize
pub fn get_artist_ids(&self) -> Vec<&ArtistId> {
if let Some(artists) = self.get_artists() {
artists.iter().map(|a| &a.id).collect()
} else {
vec![]
}
}
pub fn get_tracks(&self) -> Vec<&Track> {
self.collection_manager.get_collection()[self.selection.artist as usize].albums
[self.selection.album as usize]
.tracks
.iter()
.collect()
pub fn get_album_ids(&self) -> Vec<&AlbumId> {
if let Some(albums) = self.get_albums() {
albums.iter().map(|a| &a.id).collect()
} else {
vec![]
}
}
pub fn selected_track(&self) -> usize {
self.selection.track as usize
pub fn get_track_ids(&self) -> Vec<&Track> {
if let Some(tracks) = self.get_tracks() {
tracks.iter().collect()
} else {
vec![]
}
}
pub fn selected_artist(&self) -> Option<usize> {
if let Some(ref artist_selection) = self.selection.artist {
Some(artist_selection.index as usize)
} else {
None
}
}
pub fn selected_album(&self) -> Option<usize> {
if let Some(ref artist_selection) = self.selection.artist {
if let Some(ref album_selection) = artist_selection.album {
Some(album_selection.index as usize)
} else {
None
}
} else {
None
}
}
pub fn selected_track(&self) -> Option<usize> {
if let Some(ref artist_selection) = self.selection.artist {
if let Some(ref album_selection) = artist_selection.album {
if let Some(ref track_selection) = album_selection.track {
Some(track_selection.index as usize)
} else {
None
}
} else {
None
}
} else {
None
}
}
pub fn quit(&mut self) {

View File

@ -32,21 +32,23 @@ struct FrameAreas {
struct SelectionList<'a> {
list: List<'a>,
state: ListState,
active: bool,
}
struct ArtistState<'a> {
list: SelectionList<'a>,
active: bool,
}
struct AlbumState<'a> {
list: SelectionList<'a>,
info: Paragraph<'a>,
active: bool,
}
struct TrackState<'a> {
list: SelectionList<'a>,
info: Paragraph<'a>,
active: bool,
}
struct AppState<'a> {
@ -58,10 +60,6 @@ struct AppState<'a> {
pub struct Ui {}
impl Ui {
const COLOR_FG: Color = Color::White;
const COLOR_BG: Color = Color::Black;
const COLOR_HL: Color = Color::DarkGray;
pub fn new() -> Self {
Ui {}
}
@ -124,7 +122,7 @@ impl Ui {
}
fn construct_artist_list(app: &App) -> ArtistState {
let artists = app.get_artists();
let artists = app.get_artist_ids();
let list = List::new(
artists
.iter()
@ -135,21 +133,18 @@ impl Ui {
let selected_artist = app.selected_artist();
let mut state = ListState::default();
state.select(Some(selected_artist));
state.select(selected_artist);
let active = app.get_active_category() == Category::Artist;
ArtistState {
list: SelectionList {
list,
state,
active,
},
list: SelectionList { list, state },
active,
}
}
fn construct_album_list(app: &App) -> AlbumState {
let albums = app.get_albums();
let albums = app.get_album_ids();
let list = List::new(
albums
.iter()
@ -160,29 +155,29 @@ impl Ui {
let selected_album = app.selected_album();
let mut state = ListState::default();
state.select(Some(selected_album));
state.select(selected_album);
let active = app.get_active_category() == Category::Album;
let album = albums[selected_album];
let album = selected_album.map(|i| albums[i]);
let info = Paragraph::new(format!(
"Title: {}\n\
Year: {}",
album.title, album.year,
album.map(|a| a.title.as_str()).unwrap_or(""),
album
.map(|a| a.year.to_string())
.unwrap_or_else(|| "".to_string()),
));
AlbumState {
list: SelectionList {
list,
state,
active,
},
list: SelectionList { list, state },
info,
active,
}
}
fn construct_track_list(app: &App) -> TrackState {
let tracks = app.get_tracks();
let tracks = app.get_track_ids();
let list = List::new(
tracks
.iter()
@ -193,32 +188,35 @@ impl Ui {
let selected_track = app.selected_track();
let mut state = ListState::default();
state.select(Some(selected_track));
state.select(selected_track);
let active = app.get_active_category() == Category::Track;
let track = tracks[selected_track];
let track = selected_track.map(|i| tracks[i]);
let info = Paragraph::new(format!(
"Track: {}\n\
Title: {}\n\
Artist: {}\n\
Format: {}",
track.number,
track.title,
track.artist.join("; "),
match track.format {
TrackFormat::Flac => "FLAC",
TrackFormat::Mp3 => "MP3",
},
track
.map(|t| t.number.to_string())
.unwrap_or_else(|| "".to_string()),
track.map(|t| t.title.as_str()).unwrap_or(""),
track
.map(|t| t.artist.join("; "))
.unwrap_or_else(|| "".to_string()),
track
.map(|t| match t.format {
TrackFormat::Flac => "FLAC",
TrackFormat::Mp3 => "MP3",
})
.unwrap_or(""),
));
TrackState {
list: SelectionList {
list,
state,
active,
},
list: SelectionList { list, state },
info,
active,
}
}
@ -230,38 +228,44 @@ impl Ui {
}
}
fn style() -> Style {
Style::default().fg(Self::COLOR_FG).bg(Self::COLOR_BG)
fn style(_active: bool) -> Style {
Style::default().fg(Color::White).bg(Color::Black)
}
fn block_style(active: bool) -> Style {
Self::style(active)
}
fn highlight_style(active: bool) -> Style {
Style::default().bg(if active {
Self::COLOR_HL
if active {
Style::default().fg(Color::White).bg(Color::DarkGray)
} else {
Self::COLOR_BG
})
Self::style(false)
}
}
fn block<'a>(title: &str) -> Block<'a> {
fn block<'a>(title: &str, active: bool) -> Block<'a> {
Block::default()
.title_alignment(Alignment::Center)
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.style(Self::block_style(active))
.title(format!(" {title} "))
}
fn render_list_widget<B: Backend>(
title: &str,
mut list: SelectionList,
active: bool,
area: Rect,
frame: &mut Frame<'_, B>,
) {
frame.render_stateful_widget(
list.list
.highlight_style(Self::highlight_style(list.active))
.highlight_style(Self::highlight_style(active))
.highlight_symbol(">> ")
.style(Self::style())
.block(Self::block(title)),
.style(Self::style(active))
.block(Self::block(title, active)),
area,
&mut list.state,
);
@ -270,11 +274,14 @@ impl Ui {
fn render_info_widget<B: Backend>(
title: &str,
paragraph: Paragraph,
active: bool,
area: Rect,
frame: &mut Frame<'_, B>,
) {
frame.render_widget(
paragraph.style(Self::style()).block(Self::block(title)),
paragraph
.style(Self::style(active))
.block(Self::block(title, active)),
area,
);
}
@ -284,7 +291,7 @@ impl Ui {
area: ArtistArea,
frame: &mut Frame<'_, B>,
) {
Self::render_list_widget("Artists", state.list, area.list, frame);
Self::render_list_widget("Artists", state.list, state.active, area.list, frame);
}
fn render_album_column<B: Backend>(
@ -292,8 +299,8 @@ impl Ui {
area: AlbumArea,
frame: &mut Frame<'_, B>,
) {
Self::render_list_widget("Albums", state.list, area.list, frame);
Self::render_info_widget("Album info", state.info, area.info, frame);
Self::render_list_widget("Albums", state.list, state.active, area.list, frame);
Self::render_info_widget("Album info", state.info, state.active, area.info, frame);
}
fn render_track_column<B: Backend>(
@ -301,8 +308,8 @@ impl Ui {
area: TrackArea,
frame: &mut Frame<'_, B>,
) {
Self::render_list_widget("Tracks", state.list, area.list, frame);
Self::render_info_widget("Track info", state.info, area.info, frame);
Self::render_list_widget("Tracks", state.list, state.active, area.list, frame);
Self::render_info_widget("Track info", state.info, state.active, area.info, frame);
}
pub fn render<B: Backend>(&mut self, app: &App, frame: &mut Frame<'_, B>) {