diff --git a/src/tui/ui.rs b/src/tui/ui.rs index 24a1037..1b0ecce 100644 --- a/src/tui/ui.rs +++ b/src/tui/ui.rs @@ -13,44 +13,44 @@ use ratatui::{ use super::Error; struct TrackSelection { - selection: ListState, + state: ListState, } struct AlbumSelection { - selection: ListState, + state: ListState, track: TrackSelection, } struct ArtistSelection { - selection: ListState, + state: ListState, album: AlbumSelection, } impl TrackSelection { fn initialise(tracks: Option<&[Track]>) -> Self { - let mut selection = ListState::default(); + let mut state = ListState::default(); if let Some(tracks) = tracks { - selection.select(if !tracks.is_empty() { Some(0) } else { None }); + state.select(if !tracks.is_empty() { Some(0) } else { None }); } else { - selection.select(None); + state.select(None); }; - TrackSelection { selection } + TrackSelection { state } } fn increment(&mut self, tracks: &[Track]) { - if let Some(index) = self.selection.selected() { + if let Some(index) = self.state.selected() { if let Some(result) = index.checked_add(1) { if result < tracks.len() { - self.selection.select(Some(result)); + self.state.select(Some(result)); } } } } fn decrement(&mut self, _tracks: &[Track]) { - if let Some(index) = self.selection.selected() { + if let Some(index) = self.state.selected() { if let Some(result) = index.checked_sub(1) { - self.selection.select(Some(result)); + self.state.select(Some(result)); } } } @@ -58,23 +58,23 @@ impl TrackSelection { impl AlbumSelection { fn initialise(albums: Option<&[Album]>) -> Self { - let mut selection = ListState::default(); + let mut state = ListState::default(); let track: TrackSelection; if let Some(albums) = albums { - selection.select(if !albums.is_empty() { Some(0) } else { None }); + state.select(if !albums.is_empty() { Some(0) } else { None }); track = TrackSelection::initialise(albums.get(0).map(|a| a.tracks.as_slice())); } else { - selection.select(None); + state.select(None); track = TrackSelection::initialise(None); } - AlbumSelection { selection, track } + AlbumSelection { state, track } } fn increment(&mut self, albums: &[Album]) { - if let Some(index) = self.selection.selected() { + if let Some(index) = self.state.selected() { if let Some(result) = index.checked_add(1) { if result < albums.len() { - self.selection.select(Some(result)); + self.state.select(Some(result)); self.track = TrackSelection::initialise(Some(&albums[result].tracks)); } } @@ -82,22 +82,22 @@ impl AlbumSelection { } fn increment_track(&mut self, albums: &[Album]) { - if let Some(index) = self.selection.selected() { + if let Some(index) = self.state.selected() { self.track.increment(&albums[index].tracks); } } fn decrement(&mut self, albums: &[Album]) { - if let Some(index) = self.selection.selected() { + if let Some(index) = self.state.selected() { if let Some(result) = index.checked_sub(1) { - self.selection.select(Some(result)); + self.state.select(Some(result)); self.track = TrackSelection::initialise(Some(&albums[result].tracks)); } } } fn decrement_track(&mut self, albums: &[Album]) { - if let Some(index) = self.selection.selected() { + if let Some(index) = self.state.selected() { self.track.decrement(&albums[index].tracks); } } @@ -105,23 +105,23 @@ impl AlbumSelection { impl ArtistSelection { fn initialise(artists: Option<&[Artist]>) -> Self { - let mut selection = ListState::default(); + let mut state = ListState::default(); let album: AlbumSelection; if let Some(artists) = artists { - selection.select(if !artists.is_empty() { Some(0) } else { None }); + state.select(if !artists.is_empty() { Some(0) } else { None }); album = AlbumSelection::initialise(artists.get(0).map(|a| a.albums.as_slice())); } else { - selection.select(None); + state.select(None); album = AlbumSelection::initialise(None); } - ArtistSelection { selection, album } + ArtistSelection { state, album } } fn increment(&mut self, artists: &[Artist]) { - if let Some(index) = self.selection.selected() { + if let Some(index) = self.state.selected() { if let Some(result) = index.checked_add(1) { if result < artists.len() { - self.selection.select(Some(result)); + self.state.select(Some(result)); self.album = AlbumSelection::initialise(Some(&artists[result].albums)); } } @@ -129,34 +129,34 @@ impl ArtistSelection { } fn increment_album(&mut self, artists: &[Artist]) { - if let Some(index) = self.selection.selected() { + 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.selection.selected() { + 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.selection.selected() { + if let Some(index) = self.state.selected() { if let Some(result) = index.checked_sub(1) { - self.selection.select(Some(result)); + self.state.select(Some(result)); self.album = AlbumSelection::initialise(Some(&artists[result].albums)); } } } fn decrement_album(&mut self, artists: &[Artist]) { - if let Some(index) = self.selection.selected() { + if let Some(index) = self.state.selected() { self.album.decrement(&artists[index].albums); } } fn decrement_track(&mut self, artists: &[Artist]) { - if let Some(index) = self.selection.selected() { + if let Some(index) = self.state.selected() { self.album.decrement_track(&artists[index].albums); } } @@ -247,13 +247,11 @@ pub struct MhUi { struct ArtistArea { list: Rect, - album: AlbumArea, } struct AlbumArea { list: Rect, info: Rect, - track: TrackArea, } struct TrackArea { @@ -261,32 +259,14 @@ struct TrackArea { info: Rect, } -struct ArtistState<'a> { - list: List<'a>, +struct FrameArea { + artist: ArtistArea, + album: AlbumArea, + track: TrackArea, } -struct AlbumState<'a> { - list: List<'a>, - info: Paragraph<'a>, -} - -struct TrackState<'a> { - list: List<'a>, - info: Paragraph<'a>, -} - -impl MhUi { - pub fn new(mut collection_manager: CM) -> Result { - collection_manager.rescan_library()?; - let selection = Selection::new(Some(collection_manager.get_collection())); - Ok(MhUi { - collection_manager, - selection, - running: true, - }) - } - - fn construct_areas(frame: Rect) -> ArtistArea { +impl FrameArea { + fn new(frame: Rect) -> FrameArea { let width_one_third = frame.width / 3; let height_one_third = frame.height / 3; @@ -330,20 +310,28 @@ impl MhUi { height: panel_height_bottom, }; - ArtistArea { - list: artist_list, + FrameArea { + artist: ArtistArea { list: artist_list }, album: AlbumArea { list: album_list, info: album_info, - track: TrackArea { - list: track_list, - info: track_info, - }, + }, + track: TrackArea { + list: track_list, + info: track_info, }, } } +} - fn construct_artist_state(artists: &[Artist]) -> ArtistState { +struct ArtistState<'a, 'b> { + active: bool, + list: List<'a>, + state: &'b mut ListState, +} + +impl<'a, 'b> ArtistState<'a, 'b> { + fn new(active: bool, artists: &'a [Artist], state: &'b mut ListState) -> ArtistState<'a, 'b> { let list = List::new( artists .iter() @@ -351,10 +339,23 @@ impl MhUi { .collect::>(), ); - ArtistState { list } + ArtistState { + active, + list, + state, + } } +} - fn construct_album_state(albums: &[Album], selected: Option) -> AlbumState { +struct AlbumState<'a, 'b> { + active: bool, + list: List<'a>, + state: &'b mut ListState, + info: Paragraph<'a>, +} + +impl<'a, 'b> AlbumState<'a, 'b> { + fn new(active: bool, albums: &'a [Album], state: &'b mut ListState) -> AlbumState<'a, 'b> { let list = List::new( albums .iter() @@ -362,7 +363,7 @@ impl MhUi { .collect::>(), ); - let album = selected.map(|i| &albums[i]); + let album = state.selected().map(|i| &albums[i]); let info = Paragraph::new(format!( "Title: {}\n\ Year: {}", @@ -372,10 +373,24 @@ impl MhUi { .unwrap_or_else(|| "".to_string()), )); - AlbumState { list, info } + AlbumState { + active, + list, + state, + info, + } } +} - fn construct_track_state(tracks: &[Track], selected: Option) -> TrackState { +struct TrackState<'a, 'b> { + active: bool, + list: List<'a>, + state: &'b mut ListState, + info: Paragraph<'a>, +} + +impl<'a, 'b> TrackState<'a, 'b> { + fn new(active: bool, tracks: &'a [Track], state: &'b mut ListState) -> TrackState<'a, 'b> { let list = List::new( tracks .iter() @@ -383,12 +398,12 @@ impl MhUi { .collect::>(), ); - let track = selected.map(|i| &tracks[i]); + let track = state.selected().map(|i| &tracks[i]); let info = Paragraph::new(format!( "Track: {}\n\ - Title: {}\n\ - Artist: {}\n\ - Format: {}", + Title: {}\n\ + Artist: {}\n\ + Format: {}", track .map(|t| t.number.to_string()) .unwrap_or_else(|| "".to_string()), @@ -404,7 +419,24 @@ impl MhUi { .unwrap_or(""), )); - TrackState { list, info } + TrackState { + active, + list, + state, + info, + } + } +} + +impl MhUi { + pub fn new(mut collection_manager: CM) -> Result { + collection_manager.rescan_library()?; + let selection = Selection::new(Some(collection_manager.get_collection())); + Ok(MhUi { + collection_manager, + selection, + running: true, + }) } fn style(_active: bool) -> Style { @@ -465,98 +497,18 @@ impl MhUi { ); } - fn render_artist_column( - artists: &[Artist], - category: Category, - selection: &mut ArtistSelection, - area: ArtistArea, - frame: &mut Frame<'_, B>, - ) { - let state = Self::construct_artist_state(artists); - Self::render_list_widget( - "Artists", - state.list, - &mut selection.selection, - category == Category::Artist, - area.list, - frame, - ); - - let empty_vec: Vec = vec![]; - Self::render_album_column( - if let Some(artist_index) = selection.selection.selected() { - &artists[artist_index].albums - } else { - &empty_vec - }, - category, - &mut selection.album, - area.album, - frame, - ); + fn render_artist_column(st: ArtistState, ar: ArtistArea, fr: &mut Frame<'_, B>) { + Self::render_list_widget("Artists", st.list, st.state, st.active, ar.list, fr); } - fn render_album_column( - albums: &[Album], - category: Category, - selection: &mut AlbumSelection, - area: AlbumArea, - frame: &mut Frame<'_, B>, - ) { - let state = Self::construct_album_state(albums, selection.selection.selected()); - Self::render_list_widget( - "Albums", - state.list, - &mut selection.selection, - category == Category::Album, - area.list, - frame, - ); - Self::render_info_widget( - "Album info", - state.info, - category == Category::Album, - area.info, - frame, - ); - - let empty_vec: Vec = vec![]; - Self::render_track_column( - if let Some(album_index) = selection.selection.selected() { - &albums[album_index].tracks - } else { - &empty_vec - }, - category, - &mut selection.track, - area.track, - frame, - ); + fn render_album_column(st: AlbumState, ar: AlbumArea, fr: &mut Frame<'_, B>) { + Self::render_list_widget("Albums", st.list, st.state, st.active, ar.list, fr); + Self::render_info_widget("Album info", st.info, st.active, ar.info, fr); } - fn render_track_column( - tracks: &[Track], - category: Category, - selection: &mut TrackSelection, - area: TrackArea, - frame: &mut Frame<'_, B>, - ) { - let state = Self::construct_track_state(tracks, selection.selection.selected()); - Self::render_list_widget( - "Tracks", - state.list, - &mut selection.selection, - category == Category::Track, - area.list, - frame, - ); - Self::render_info_widget( - "Track info", - state.info, - category == Category::Track, - area.info, - frame, - ); + fn render_track_column(st: TrackState, ar: TrackArea, fr: &mut Frame<'_, B>) { + Self::render_list_widget("Tracks", st.list, st.state, st.active, ar.list, fr); + Self::render_info_widget("Track info", st.info, st.active, ar.info, fr); } } @@ -601,15 +553,48 @@ impl Ui for MhUi { } fn render(&mut self, frame: &mut Frame<'_, B>) { - let areas = Self::construct_areas(frame.size()); + let active = self.selection.active; + let areas = FrameArea::new(frame.size()); - Self::render_artist_column( - self.collection_manager.get_collection(), - self.selection.active, - &mut self.selection.artist, - areas, - frame, + let artists = self.collection_manager.get_collection(); + let artist_selection = &mut self.selection.artist; + let artist_state = ArtistState::new( + active == Category::Artist, + artists, + &mut artist_selection.state, ); + + Self::render_artist_column(artist_state, areas.artist, frame); + + let no_albums: Vec = vec![]; + let albums = artist_selection + .state + .selected() + .map(|i| &artists[i].albums) + .unwrap_or_else(|| &no_albums); + let album_selection = &mut artist_selection.album; + let album_state = AlbumState::new( + active == Category::Album, + albums, + &mut album_selection.state, + ); + + Self::render_album_column(album_state, areas.album, frame); + + let no_tracks: Vec = vec![]; + let tracks = album_selection + .state + .selected() + .map(|i| &albums[i].tracks) + .unwrap_or_else(|| &no_tracks); + let track_selection = &mut album_selection.track; + let track_state = TrackState::new( + active == Category::Track, + tracks, + &mut track_selection.state, + ); + + Self::render_track_column(track_state, areas.track, frame); } }