Make selection fields private
All checks were successful
Cargo CI / Build and Test (pull_request) Successful in 1m42s
Cargo CI / Lint (pull_request) Successful in 1m14s

This commit is contained in:
Wojciech Kozlowski 2024-03-01 22:27:26 +01:00
parent 42d1edb69c
commit a656f3897c
7 changed files with 140 additions and 110 deletions

View File

@ -113,43 +113,38 @@ mod tests {
fn increment_decrement() {
let mut browse = AppMachine::browse(inner(music_hoard(COLLECTION.to_owned())));
let sel = &browse.inner.selection;
assert_eq!(sel.active, Category::Artist);
assert_eq!(sel.artist.state.list.selected(), Some(0));
assert_eq!(sel.category(), Category::Artist);
assert_eq!(sel.selected(), Some(0));
browse = browse.increment_selection(Delta::Line).unwrap_browse();
let sel = &browse.inner.selection;
assert_eq!(sel.active, Category::Artist);
assert_eq!(sel.artist.state.list.selected(), Some(1));
assert_eq!(sel.category(), Category::Artist);
assert_eq!(sel.selected(), Some(1));
browse = browse.increment_category().unwrap_browse();
let sel = &browse.inner.selection;
assert_eq!(sel.active, Category::Album);
assert_eq!(sel.artist.state.list.selected(), Some(1));
assert_eq!(sel.artist.album.state.list.selected(), Some(0));
assert_eq!(sel.category(), Category::Album);
assert_eq!(sel.selected(), Some(0));
browse = browse.increment_selection(Delta::Line).unwrap_browse();
let sel = &browse.inner.selection;
assert_eq!(sel.active, Category::Album);
assert_eq!(sel.artist.state.list.selected(), Some(1));
assert_eq!(sel.artist.album.state.list.selected(), Some(1));
assert_eq!(sel.category(), Category::Album);
assert_eq!(sel.selected(), Some(1));
browse = browse.decrement_selection(Delta::Line).unwrap_browse();
let sel = &browse.inner.selection;
assert_eq!(sel.active, Category::Album);
assert_eq!(sel.artist.state.list.selected(), Some(1));
assert_eq!(sel.artist.album.state.list.selected(), Some(0));
assert_eq!(sel.category(), Category::Album);
assert_eq!(sel.selected(), Some(0));
browse = browse.decrement_category().unwrap_browse();
let sel = &browse.inner.selection;
assert_eq!(sel.active, Category::Artist);
assert_eq!(sel.artist.state.list.selected(), Some(1));
assert_eq!(sel.artist.album.state.list.selected(), Some(0));
assert_eq!(sel.category(), Category::Artist);
assert_eq!(sel.selected(), Some(1));
browse = browse.decrement_selection(Delta::Line).unwrap_browse();
let sel = &browse.inner.selection;
assert_eq!(sel.active, Category::Artist);
assert_eq!(sel.artist.state.list.selected(), Some(0));
assert_eq!(sel.artist.album.state.list.selected(), Some(0));
assert_eq!(sel.category(), Category::Artist);
assert_eq!(sel.selected(), Some(0));
}
#[test]

View File

@ -133,7 +133,7 @@ impl<MH: IMusicHoard> IAppInteractSearchPrivate for AppMachine<MH, AppSearch> {
let search = &self.state.string;
let sel = &self.inner.selection;
let result = match sel.active {
let result = match sel.category() {
Category::Artist => sel
.state_artist(collection)
.and_then(|state| Self::search_artists(search, next, state)),
@ -262,153 +262,156 @@ mod tests {
fn artist_incremental_search() {
// Empty collection.
let mut search = AppMachine::search(inner(music_hoard(vec![])), orig(None));
assert_eq!(search.inner.selection.artist.state.list.selected(), None);
assert_eq!(search.inner.selection.selected(), None);
search.state.string = String::from("album_artist 'a'");
search.incremental_search(false);
assert_eq!(search.inner.selection.artist.state.list.selected(), None);
assert_eq!(search.inner.selection.selected(), None);
// Basic test, first element.
let mut search =
AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
search.state.string = String::from("");
search.incremental_search(false);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
search.state.string = String::from("album_artist ");
search.incremental_search(false);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
search.state.string = String::from("album_artist 'a'");
search.incremental_search(false);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
// Basic test, non-first element.
let mut search =
AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
search.state.string = String::from("album_artist ");
search.incremental_search(false);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
search.state.string = String::from("album_artist 'c'");
search.incremental_search(false);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(2));
assert_eq!(search.inner.selection.selected(), Some(2));
// Non-lowercase.
let mut search =
AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
search.state.string = String::from("Album_Artist ");
search.incremental_search(false);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
search.state.string = String::from("Album_Artist 'C'");
search.incremental_search(false);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(2));
assert_eq!(search.inner.selection.selected(), Some(2));
// Non-ascii.
let mut search =
AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
search.state.string = String::from("album_artist ");
search.incremental_search(false);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
search.state.string = String::from("album_artist c");
search.incremental_search(false);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(2));
assert_eq!(search.inner.selection.selected(), Some(2));
// Non-lowercase, non-ascii.
let mut search =
AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
search.state.string = String::from("Album_Artist ");
search.incremental_search(false);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
search.state.string = String::from("Album_Artist C");
search.incremental_search(false);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(2));
assert_eq!(search.inner.selection.selected(), Some(2));
// Stop at name, not sort name.
let mut search =
AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
search.state.string = String::from("the ");
search.incremental_search(false);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(2));
assert_eq!(search.inner.selection.selected(), Some(2));
search.state.string = String::from("the album_artist 'c'");
search.incremental_search(false);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(2));
assert_eq!(search.inner.selection.selected(), Some(2));
// Search next with common prefix.
let mut search =
AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
search.state.string = String::from("album_artist");
search.incremental_search(false);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
search.incremental_search(true);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(1));
assert_eq!(search.inner.selection.selected(), Some(1));
search.incremental_search(true);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(2));
assert_eq!(search.inner.selection.selected(), Some(2));
search.incremental_search(true);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(3));
assert_eq!(search.inner.selection.selected(), Some(3));
search.incremental_search(true);
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(3));
assert_eq!(search.inner.selection.selected(), Some(3));
}
#[test]
fn album_incremental_search() {
let mut search =
AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
search.inner.selection.active = Category::Album;
search.inner.selection.increment_category();
assert_eq!(search.inner.selection.category(), Category::Album);
let sel = &search.inner.selection;
assert_eq!(sel.artist.album.state.list.selected(), Some(0));
assert_eq!(sel.selected(), Some(0));
search.state.string = String::from("album_title ");
search.incremental_search(false);
let sel = &search.inner.selection;
assert_eq!(sel.artist.album.state.list.selected(), Some(0));
assert_eq!(sel.selected(), Some(0));
let search = search.append_character('a').unwrap_search();
let search = search.append_character('.').unwrap_search();
let search = search.append_character('b').unwrap_search();
let sel = &search.inner.selection;
assert_eq!(sel.artist.album.state.list.selected(), Some(1));
assert_eq!(sel.selected(), Some(1));
}
#[test]
fn track_incremental_search() {
let mut search =
AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(1)));
search.inner.selection.active = Category::Track;
search.inner.selection.increment_category();
search.inner.selection.increment_category();
assert_eq!(search.inner.selection.category(), Category::Track);
let sel = &search.inner.selection;
assert_eq!(sel.artist.album.track.state.list.selected(), Some(0));
assert_eq!(sel.selected(), Some(0));
search.state.string = String::from("track ");
search.incremental_search(false);
let sel = &search.inner.selection;
assert_eq!(sel.artist.album.track.state.list.selected(), Some(0));
assert_eq!(sel.selected(), Some(0));
let search = search.append_character('a').unwrap_search();
let search = search.append_character('.').unwrap_search();
@ -417,13 +420,13 @@ mod tests {
let search = search.append_character('2').unwrap_search();
let sel = &search.inner.selection;
assert_eq!(sel.artist.album.track.state.list.selected(), Some(1));
assert_eq!(sel.selected(), Some(1));
}
#[test]
fn search() {
let search = AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(2)));
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
let search = search.append_character('a').unwrap_search();
let search = search.append_character('l').unwrap_search();
@ -438,69 +441,69 @@ mod tests {
let search = search.append_character('s').unwrap_search();
let search = search.append_character('t').unwrap_search();
let search = search.append_character(' ').unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
let search = search.append_character('\'').unwrap_search();
let search = search.append_character('c').unwrap_search();
let search = search.append_character('\'').unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(2));
assert_eq!(search.inner.selection.selected(), Some(2));
let search = search.step_back().unwrap_search();
let search = search.step_back().unwrap_search();
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
let search = search.append_character('\'').unwrap_search();
let search = search.append_character('b').unwrap_search();
let search = search.append_character('\'').unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(1));
assert_eq!(search.inner.selection.selected(), Some(1));
let app = search.finish_search();
let browse = app.unwrap_browse();
assert_eq!(browse.inner.selection.artist.state.list.selected(), Some(1));
assert_eq!(browse.inner.selection.selected(), Some(1));
}
#[test]
fn search_next_step_back() {
let search = AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(2)));
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
let search = search.append_character('a').unwrap_search();
let search = search.search_next().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(1));
assert_eq!(search.inner.selection.selected(), Some(1));
let search = search.search_next().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(2));
assert_eq!(search.inner.selection.selected(), Some(2));
let search = search.search_next().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(3));
assert_eq!(search.inner.selection.selected(), Some(3));
let search = search.search_next().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(3));
assert_eq!(search.inner.selection.selected(), Some(3));
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(3));
assert_eq!(search.inner.selection.selected(), Some(3));
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(2));
assert_eq!(search.inner.selection.selected(), Some(2));
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(1));
assert_eq!(search.inner.selection.selected(), Some(1));
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
}
#[test]
fn cancel_search() {
let search = AppMachine::search(inner(music_hoard(COLLECTION.to_owned())), orig(Some(2)));
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(0));
assert_eq!(search.inner.selection.selected(), Some(0));
let search = search.append_character('a').unwrap_search();
let search = search.append_character('l').unwrap_search();
@ -518,31 +521,31 @@ mod tests {
let search = search.append_character('\'').unwrap_search();
let search = search.append_character('b').unwrap_search();
let search = search.append_character('\'').unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), Some(1));
assert_eq!(search.inner.selection.selected(), Some(1));
let browse = search.cancel_search().unwrap_browse();
assert_eq!(browse.inner.selection.artist.state.list.selected(), Some(2));
assert_eq!(browse.inner.selection.selected(), Some(2));
}
#[test]
fn empty_search() {
let search = AppMachine::search(inner(music_hoard(vec![])), orig(None));
assert_eq!(search.inner.selection.artist.state.list.selected(), None);
assert_eq!(search.inner.selection.selected(), None);
let search = search.append_character('a').unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), None);
assert_eq!(search.inner.selection.selected(), None);
let search = search.search_next().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), None);
assert_eq!(search.inner.selection.selected(), None);
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), None);
assert_eq!(search.inner.selection.selected(), None);
let search = search.step_back().unwrap_search();
assert_eq!(search.inner.selection.artist.state.list.selected(), None);
assert_eq!(search.inner.selection.selected(), None);
let browse = search.cancel_search().unwrap_browse();
assert_eq!(browse.inner.selection.artist.state.list.selected(), None);
assert_eq!(browse.inner.selection.selected(), None);
}
#[test]

View File

@ -96,6 +96,14 @@ impl AlbumSelection {
selected.and_then(|index| self.track.selection_state(&albums[index].tracks))
}
pub fn widget_state(&mut self) -> &mut WidgetState {
&mut self.state
}
pub fn widget_state_track(&mut self) -> &mut WidgetState {
self.track.widget_state()
}
pub fn reset(&mut self, albums: &[Album]) {
if self.state.list.selected() != Some(0) {
self.reinitialise(albums, None);

View File

@ -113,6 +113,18 @@ impl ArtistSelection {
selected.and_then(|index| self.album.state_tracks(&artists[index].albums))
}
pub fn widget_state(&mut self) -> &mut WidgetState {
&mut self.state
}
pub fn widget_state_album(&mut self) -> &mut WidgetState {
self.album.widget_state()
}
pub fn widget_state_track(&mut self) -> &mut WidgetState {
self.album.widget_state_track()
}
pub fn reset(&mut self, artists: &[Artist]) {
if self.state.list.selected() != Some(0) {
self.reinitialise(artists, None);

View File

@ -14,6 +14,16 @@ pub enum Category {
Track,
}
pub struct Selection {
active: Category,
artist: ArtistSelection,
}
pub struct SelectionState<'a, T> {
pub list: &'a [T],
pub index: usize,
}
#[derive(Clone, Debug, Default)]
pub struct WidgetState {
pub list: ListState,
@ -26,16 +36,6 @@ impl PartialEq for WidgetState {
}
}
pub struct Selection {
pub active: Category,
pub artist: ArtistSelection,
}
pub struct SelectionState<'a, T> {
pub list: &'a [T],
pub index: usize,
}
pub enum Delta {
Line,
Page,
@ -104,6 +104,10 @@ impl Selection {
self.artist.select_track(artists, index);
}
pub fn category(&self) -> Category {
self.active
}
pub fn selected(&self) -> Option<usize> {
match self.active {
Category::Artist => self.selected_artist(),
@ -136,6 +140,18 @@ impl Selection {
self.artist.state_track(coll)
}
pub fn widget_state_artist(&mut self) -> &mut WidgetState {
self.artist.widget_state()
}
pub fn widget_state_album(&mut self) -> &mut WidgetState {
self.artist.widget_state_album()
}
pub fn widget_state_track(&mut self) -> &mut WidgetState {
self.artist.widget_state_track()
}
pub fn reset(&mut self, collection: &Collection) {
match self.active {
Category::Artist => self.reset_artist(collection),

View File

@ -60,6 +60,10 @@ impl TrackSelection {
selected.map(|index| SelectionState { list, index })
}
pub fn widget_state(&mut self) -> &mut WidgetState {
&mut self.state
}
pub fn reset(&mut self, tracks: &[Track]) {
if self.state.list.selected() != Some(0) {
self.reinitialise(tracks, None);

View File

@ -590,46 +590,39 @@ impl Ui {
state: &AppPublicState,
frame: &mut Frame,
) {
let active = selection.active;
let active = selection.category();
let areas = FrameArea::new(frame.size());
let artist_selection = &mut selection.artist;
let artist_state = ArtistState::new(
active == Category::Artist,
artists,
&mut artist_selection.state,
selection.widget_state_artist(),
);
Self::render_artist_column(artist_state, areas.artist, frame);
let no_albums: Vec<Album> = vec![];
let albums = artist_selection
.state
.list
.selected()
.map(|i| &artists[i].albums)
let albums = selection
.state_album(artists)
.map(|st| st.list)
.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,
selection.widget_state_album(),
);
Self::render_album_column(album_state, areas.album, frame);
let no_tracks: Vec<Track> = vec![];
let tracks = album_selection
.state
.list
.selected()
.map(|i| &albums[i].tracks)
let tracks = selection
.state_track(artists)
.map(|st| st.list)
.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,
selection.widget_state_track(),
);
Self::render_track_column(track_state, areas.track, frame);
@ -640,8 +633,7 @@ impl Ui {
fn render_info_overlay(artists: &Collection, selection: &mut Selection, frame: &mut Frame) {
let area = OverlayBuilder::default().build(frame.size());
let artist_selection = &mut selection.artist;
let artist_overlay = ArtistOverlay::new(artists, &artist_selection.state.list);
let artist_overlay = ArtistOverlay::new(artists, &selection.widget_state_artist().list);
Self::render_overlay_widget("Artist", artist_overlay.properties, area, false, frame);
}