parent
a062817ae7
commit
d9d5945422
195
src/tui/ui.rs
195
src/tui/ui.rs
@ -1,7 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use musichoard::collection::{
|
use musichoard::collection::{
|
||||||
album::{Album, AlbumDate, AlbumSeq, AlbumStatus},
|
album::{Album, AlbumDate, AlbumPrimaryType, AlbumSecondaryType, AlbumSeq, AlbumStatus},
|
||||||
artist::Artist,
|
artist::Artist,
|
||||||
musicbrainz::IMusicBrainzRef,
|
musicbrainz::IMusicBrainzRef,
|
||||||
track::{Track, TrackFormat, TrackQuality},
|
track::{Track, TrackFormat, TrackQuality},
|
||||||
@ -198,15 +198,18 @@ impl<'a, 'b> ArtistState<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct InfoOverlay;
|
||||||
|
|
||||||
|
impl InfoOverlay {
|
||||||
|
const ITEM_INDENT: &'static str = " ";
|
||||||
|
const LIST_INDENT: &'static str = " - ";
|
||||||
|
}
|
||||||
|
|
||||||
struct ArtistOverlay<'a> {
|
struct ArtistOverlay<'a> {
|
||||||
properties: Paragraph<'a>,
|
properties: Paragraph<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ArtistOverlay<'a> {
|
impl<'a> ArtistOverlay<'a> {
|
||||||
fn opt_opt_to_str<S: AsRef<str> + ?Sized>(opt: Option<Option<&S>>) -> &str {
|
|
||||||
opt.flatten().map(|item| item.as_ref()).unwrap_or("")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn opt_hashmap_to_string<K: Ord + AsRef<str>, T: AsRef<str>>(
|
fn opt_hashmap_to_string<K: Ord + AsRef<str>, T: AsRef<str>>(
|
||||||
opt_map: Option<&HashMap<K, Vec<T>>>,
|
opt_map: Option<&HashMap<K, Vec<T>>>,
|
||||||
item_indent: &str,
|
item_indent: &str,
|
||||||
@ -254,8 +257,8 @@ impl<'a> ArtistOverlay<'a> {
|
|||||||
fn new(artists: &'a [Artist], state: &ListState) -> ArtistOverlay<'a> {
|
fn new(artists: &'a [Artist], state: &ListState) -> ArtistOverlay<'a> {
|
||||||
let artist = state.selected().map(|i| &artists[i]);
|
let artist = state.selected().map(|i| &artists[i]);
|
||||||
|
|
||||||
let item_indent = " ";
|
let item_indent = InfoOverlay::ITEM_INDENT;
|
||||||
let list_indent = " - ";
|
let list_indent = InfoOverlay::LIST_INDENT;
|
||||||
|
|
||||||
let double_item_indent = format!("{item_indent}{item_indent}");
|
let double_item_indent = format!("{item_indent}{item_indent}");
|
||||||
let double_list_indent = format!("{item_indent}{list_indent}");
|
let double_list_indent = format!("{item_indent}{list_indent}");
|
||||||
@ -265,7 +268,9 @@ impl<'a> ArtistOverlay<'a> {
|
|||||||
MusicBrainz: {}\n{item_indent}\
|
MusicBrainz: {}\n{item_indent}\
|
||||||
Properties: {}",
|
Properties: {}",
|
||||||
artist.map(|a| a.id.name.as_str()).unwrap_or(""),
|
artist.map(|a| a.id.name.as_str()).unwrap_or(""),
|
||||||
Self::opt_opt_to_str(artist.map(|a| a.musicbrainz.as_ref().map(|mb| mb.url()))),
|
artist
|
||||||
|
.and_then(|a| a.musicbrainz.as_ref().map(|mb| mb.url().as_str()))
|
||||||
|
.unwrap_or(""),
|
||||||
Self::opt_hashmap_to_string(
|
Self::opt_hashmap_to_string(
|
||||||
artist.map(|a| &a.properties),
|
artist.map(|a| &a.properties),
|
||||||
&double_item_indent,
|
&double_item_indent,
|
||||||
@ -296,14 +301,15 @@ impl<'a, 'b> AlbumState<'a, 'b> {
|
|||||||
let album = state.list.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\
|
||||||
Date: {}{}\n\
|
Date: {}\n\
|
||||||
|
Type: {}\n\
|
||||||
Status: {}",
|
Status: {}",
|
||||||
album.map(|a| a.id.title.as_str()).unwrap_or(""),
|
album.map(|a| a.id.title.as_str()).unwrap_or(""),
|
||||||
album
|
album
|
||||||
.map(|a| Self::display_album_date(&a.date))
|
.map(|a| Self::display_date(&a.date, &a.seq))
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
album
|
album
|
||||||
.map(|a| Self::display_album_seq(&a.seq))
|
.map(|a| Self::display_type(&a.primary_type, &a.secondary_types))
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
album
|
album
|
||||||
.map(|a| Self::display_album_status(&a.get_status()))
|
.map(|a| Self::display_album_status(&a.get_status()))
|
||||||
@ -329,6 +335,14 @@ impl<'a, 'b> AlbumState<'a, 'b> {
|
|||||||
ListItem::new(line)
|
ListItem::new(line)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn display_date(date: &AlbumDate, seq: &AlbumSeq) -> String {
|
||||||
|
if seq.0 > 0 {
|
||||||
|
format!("{} ({})", Self::display_album_date(date), seq.0)
|
||||||
|
} else {
|
||||||
|
Self::display_album_date(date)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn display_album_date(date: &AlbumDate) -> String {
|
fn display_album_date(date: &AlbumDate) -> String {
|
||||||
match date.year {
|
match date.year {
|
||||||
Some(year) => match date.month {
|
Some(year) => match date.month {
|
||||||
@ -342,13 +356,56 @@ impl<'a, 'b> AlbumState<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_album_seq(seq: &AlbumSeq) -> String {
|
fn display_type(
|
||||||
if seq.0 > 0 {
|
primary: &Option<AlbumPrimaryType>,
|
||||||
format!(" ({})", seq.0)
|
secondary: &Vec<AlbumSecondaryType>,
|
||||||
|
) -> String {
|
||||||
|
match primary {
|
||||||
|
Some(ref primary) => {
|
||||||
|
if secondary.is_empty() {
|
||||||
|
Self::display_primary_type(primary).to_string()
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
format!(
|
||||||
|
"{} ({})",
|
||||||
|
Self::display_primary_type(primary),
|
||||||
|
Self::display_secondary_types(secondary)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
None => String::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_primary_type(value: &AlbumPrimaryType) -> &'static str {
|
||||||
|
match value {
|
||||||
|
AlbumPrimaryType::Album => "Album",
|
||||||
|
AlbumPrimaryType::Single => "Single",
|
||||||
|
AlbumPrimaryType::Ep => "EP",
|
||||||
|
AlbumPrimaryType::Broadcast => "Broadcast",
|
||||||
|
AlbumPrimaryType::Other => "Other",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_secondary_types(values: &Vec<AlbumSecondaryType>) -> String {
|
||||||
|
let mut types: Vec<&'static str> = vec![];
|
||||||
|
for value in values {
|
||||||
|
match value {
|
||||||
|
AlbumSecondaryType::Compilation => types.push("Compilation"),
|
||||||
|
AlbumSecondaryType::Soundtrack => types.push("Soundtrack"),
|
||||||
|
AlbumSecondaryType::Spokenword => types.push("Spokenword"),
|
||||||
|
AlbumSecondaryType::Interview => types.push("Interview"),
|
||||||
|
AlbumSecondaryType::Audiobook => types.push("Audiobook"),
|
||||||
|
AlbumSecondaryType::AudioDrama => types.push("Audio drama"),
|
||||||
|
AlbumSecondaryType::Live => types.push("Live"),
|
||||||
|
AlbumSecondaryType::Remix => types.push("Remix"),
|
||||||
|
AlbumSecondaryType::DjMix => types.push("DJ-mix"),
|
||||||
|
AlbumSecondaryType::MixtapeStreet => types.push("Mixtape/Street"),
|
||||||
|
AlbumSecondaryType::Demo => types.push("Demo"),
|
||||||
|
AlbumSecondaryType::FieldRecording => types.push("Field recording"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
types.join(", ")
|
||||||
|
}
|
||||||
|
|
||||||
fn display_album_status(status: &AlbumStatus) -> &'static str {
|
fn display_album_status(status: &AlbumStatus) -> &'static str {
|
||||||
match status {
|
match status {
|
||||||
@ -361,6 +418,29 @@ impl<'a, 'b> AlbumState<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct AlbumOverlay<'a> {
|
||||||
|
properties: Paragraph<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> AlbumOverlay<'a> {
|
||||||
|
fn new(albums: &'a [Album], state: &ListState) -> AlbumOverlay<'a> {
|
||||||
|
let album = state.selected().map(|i| &albums[i]);
|
||||||
|
|
||||||
|
let item_indent = InfoOverlay::ITEM_INDENT;
|
||||||
|
|
||||||
|
let properties = Paragraph::new(format!(
|
||||||
|
"Album: {}\n\n{item_indent}\
|
||||||
|
MusicBrainz: {}",
|
||||||
|
album.map(|a| a.id.title.as_str()).unwrap_or(""),
|
||||||
|
album
|
||||||
|
.and_then(|a| a.musicbrainz.as_ref().map(|mb| mb.url().as_str()))
|
||||||
|
.unwrap_or(""),
|
||||||
|
));
|
||||||
|
|
||||||
|
AlbumOverlay { properties }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct TrackState<'a, 'b> {
|
struct TrackState<'a, 'b> {
|
||||||
active: bool,
|
active: bool,
|
||||||
list: List<'a>,
|
list: List<'a>,
|
||||||
@ -698,9 +778,18 @@ impl Ui {
|
|||||||
fn render_info_overlay(artists: &Collection, selection: &mut Selection, frame: &mut Frame) {
|
fn render_info_overlay(artists: &Collection, selection: &mut Selection, frame: &mut Frame) {
|
||||||
let area = OverlayBuilder::default().build(frame.size());
|
let area = OverlayBuilder::default().build(frame.size());
|
||||||
|
|
||||||
|
if selection.category() == Category::Artist {
|
||||||
let artist_overlay = ArtistOverlay::new(artists, &selection.widget_state_artist().list);
|
let artist_overlay = ArtistOverlay::new(artists, &selection.widget_state_artist().list);
|
||||||
|
|
||||||
Self::render_overlay_widget("Artist", artist_overlay.properties, area, false, frame);
|
Self::render_overlay_widget("Artist", artist_overlay.properties, area, false, frame);
|
||||||
|
} else {
|
||||||
|
let no_albums: Vec<Album> = vec![];
|
||||||
|
let albums = selection
|
||||||
|
.state_album(artists)
|
||||||
|
.map(|st| st.list)
|
||||||
|
.unwrap_or_else(|| &no_albums);
|
||||||
|
let album_overlay = AlbumOverlay::new(albums, &selection.widget_state_album().list);
|
||||||
|
Self::render_overlay_widget("Album", album_overlay.properties, area, false, frame);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_reload_overlay(frame: &mut Frame) {
|
fn render_reload_overlay(frame: &mut Frame) {
|
||||||
@ -818,10 +907,76 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn display_album_seq() {
|
fn display_date() {
|
||||||
assert_eq!(AlbumState::display_album_seq(&AlbumSeq::default()), "");
|
let date: AlbumDate = 1990.into();
|
||||||
assert_eq!(AlbumState::display_album_seq(&AlbumSeq(0)), "");
|
assert_eq!(
|
||||||
assert_eq!(AlbumState::display_album_seq(&AlbumSeq(5)), " (5)");
|
AlbumState::display_date(&date, &AlbumSeq::default()),
|
||||||
|
"1990"
|
||||||
|
);
|
||||||
|
assert_eq!(AlbumState::display_date(&date, &AlbumSeq(0)), "1990");
|
||||||
|
assert_eq!(AlbumState::display_date(&date, &AlbumSeq(5)), "1990 (5)");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display_primary_type() {
|
||||||
|
assert_eq!(
|
||||||
|
AlbumState::display_primary_type(&AlbumPrimaryType::Album),
|
||||||
|
"Album"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
AlbumState::display_primary_type(&AlbumPrimaryType::Single),
|
||||||
|
"Single"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
AlbumState::display_primary_type(&AlbumPrimaryType::Ep),
|
||||||
|
"EP"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
AlbumState::display_primary_type(&AlbumPrimaryType::Broadcast),
|
||||||
|
"Broadcast"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
AlbumState::display_primary_type(&AlbumPrimaryType::Other),
|
||||||
|
"Other"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display_secondary_types() {
|
||||||
|
assert_eq!(
|
||||||
|
AlbumState::display_secondary_types(&vec![
|
||||||
|
AlbumSecondaryType::Compilation,
|
||||||
|
AlbumSecondaryType::Soundtrack,
|
||||||
|
AlbumSecondaryType::Spokenword,
|
||||||
|
AlbumSecondaryType::Interview,
|
||||||
|
AlbumSecondaryType::Audiobook,
|
||||||
|
AlbumSecondaryType::AudioDrama,
|
||||||
|
AlbumSecondaryType::Live,
|
||||||
|
AlbumSecondaryType::Remix,
|
||||||
|
AlbumSecondaryType::DjMix,
|
||||||
|
AlbumSecondaryType::MixtapeStreet,
|
||||||
|
AlbumSecondaryType::Demo,
|
||||||
|
AlbumSecondaryType::FieldRecording,
|
||||||
|
]),
|
||||||
|
"Compilation, Soundtrack, Spokenword, Interview, Audiobook, Audio drama, Live, Remix, \
|
||||||
|
DJ-mix, Mixtape/Street, Demo, Field recording"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display_type() {
|
||||||
|
assert_eq!(AlbumState::display_type(&None, &vec![]), "");
|
||||||
|
assert_eq!(
|
||||||
|
AlbumState::display_type(&Some(AlbumPrimaryType::Album), &vec![]),
|
||||||
|
"Album"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
AlbumState::display_type(
|
||||||
|
&Some(AlbumPrimaryType::Album),
|
||||||
|
&vec![AlbumSecondaryType::Live, AlbumSecondaryType::Compilation]
|
||||||
|
),
|
||||||
|
"Album (Live, Compilation)"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Loading…
Reference in New Issue
Block a user