From d51e4d216a7579f85febbff927b2dc2520cfb296 Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Thu, 30 May 2024 18:29:25 -0700 Subject: [PATCH] much cleaner approach to handling different columns between compact/expanded tracklists --- backend/config.go | 6 +- ui/widgets/tracklist.go | 120 ++++++++------------------------- ui/widgets/tracklistrow.go | 132 +++++++++++++++++++++++++------------ 3 files changed, 120 insertions(+), 138 deletions(-) diff --git a/backend/config.go b/backend/config.go index 0b1fe9a..b232ef9 100644 --- a/backend/config.go +++ b/backend/config.go @@ -176,11 +176,11 @@ func DefaultConfig(appVersionTag string) *Config { SortOrder: string("Name (A-Z)"), }, FavoritesPage: FavoritesPageConfig{ - TracklistColumns: []string{"Artist", "Album", "Time", "Plays"}, + TracklistColumns: []string{"Album", "Time", "Plays"}, InitialView: "Albums", }, PlaylistPage: PlaylistPageConfig{ - TracklistColumns: []string{"Artist", "Album", "Time", "Plays"}, + TracklistColumns: []string{"Album", "Time", "Plays"}, }, PlaylistsPage: PlaylistsPageConfig{ InitialView: "List", @@ -189,7 +189,7 @@ func DefaultConfig(appVersionTag string) *Config { InitialView: "Play Queue", }, TracksPage: TracksPageConfig{ - TracklistColumns: []string{"Artist", "Album", "Time", "Plays"}, + TracklistColumns: []string{"Album", "Time", "Plays"}, }, LocalPlayback: LocalPlaybackConfig{ // "auto" is the name to pass to MPV for autoselecting the output device diff --git a/ui/widgets/tracklist.go b/ui/widgets/tracklist.go index aa6497c..98c0ec3 100644 --- a/ui/widgets/tracklist.go +++ b/ui/widgets/tracklist.go @@ -22,29 +22,6 @@ import ( "fyne.io/fyne/v2/widget" ) -const ( - ColumnNum = "Num" - ColumnTitle = "Title" - ColumnArtist = "Artist" - ColumnAlbum = "Album" - ColumnTime = "Time" - ColumnYear = "Year" - ColumnFavorite = "Favorite" - ColumnRating = "Rating" - ColumnPlays = "Plays" - ColumnComment = "Comment" - ColumnBitrate = "Bitrate" - ColumnSize = "Size" - ColumnPath = "Path" - - numColumns = 13 -) - -var columns = []string{ - ColumnNum, ColumnTitle, ColumnArtist, ColumnAlbum, ColumnTime, ColumnYear, ColumnFavorite, - ColumnRating, ColumnPlays, ColumnComment, ColumnBitrate, ColumnSize, ColumnPath, -} - type TracklistSort struct { SortOrder SortType ColumnName string @@ -81,6 +58,7 @@ type Tracklist struct { widget.BaseWidget compactRows bool + columns []TracklistColumn Options TracklistOptions @@ -122,22 +100,23 @@ type Tracklist struct { } func NewTracklist(tracks []*mediaprovider.Track, im *backend.ImageManager, useCompactRows bool) *Tracklist { - t := &Tracklist{visibleColumns: make([]bool, numColumns), compactRows: useCompactRows} + t := &Tracklist{compactRows: useCompactRows} + t.columns = ExpandedTracklistRowColumns + colWidths := ExpandedTracklistRowColumnWidths + if useCompactRows { + t.columns = CompactTracklistRowColumns + colWidths = CompactTracklistRowColumnWidths + } + t.visibleColumns = make([]bool, len(t.columns)) t.ExtendBaseWidget(t) if len(tracks) > 0 { t._setTracks(tracks) } - // #, Title/Artist, Album, Time, Year, Favorite, Rating, Plays, Comment, Bitrate, Size, Path - colWidths := []float32{40, -1, -1, 60, 60, 55, 100, 65, -1, 75, 75, -1} - if useCompactRows { - // #, Title, Artist, Album, Time, Year, Favorite, Rating, Plays, Comment, Bitrate, Size, Path - colWidths = []float32{40, -1, -1, -1, 60, 60, 55, 100, 65, -1, 75, 75, -1} - } - t.colLayout = layouts.NewColumnsLayout(colWidths) - t.buildHeader() + t.hdr = NewListHeader(sharedutil.MapSlice(t.columns, + func(t TracklistColumn) ListColumn { return t.Col }), t.colLayout) t.hdr.OnColumnSortChanged = t.onSorted t.hdr.OnColumnVisibilityChanged = t.setColumnVisible t.hdr.OnColumnVisibilityMenuShown = func(pop *widget.PopUp) { @@ -212,34 +191,6 @@ func (t *Tracklist) Scroll(amount float32) { t.list.ScrollToOffset(t.list.GetScrollOffset() + amount) } -func (t *Tracklist) buildHeader() { - cols := []ListColumn{{Text: "#", Alignment: fyne.TextAlignTrailing, CanToggleVisible: false}} - if t.compactRows { - cols = append(cols, - ListColumn{Text: "Title", Alignment: fyne.TextAlignLeading, CanToggleVisible: false}, - ListColumn{Text: "Artist", Alignment: fyne.TextAlignLeading, CanToggleVisible: true}, - ) - } else { - cols = append(cols, - ListColumn{Text: " Title / Aritst", Alignment: fyne.TextAlignLeading, CanToggleVisible: false}, - ) - } - cols = append(cols, - ListColumn{Text: "Album", Alignment: fyne.TextAlignLeading, CanToggleVisible: true}, - ListColumn{Text: "Time", Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}, - ListColumn{Text: "Year", Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}, - ListColumn{Text: " Fav.", Alignment: fyne.TextAlignCenter, CanToggleVisible: true}, - ListColumn{Text: "Rating", Alignment: fyne.TextAlignLeading, CanToggleVisible: true}, - ListColumn{Text: "Plays", Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}, - ListColumn{Text: "Comment", Alignment: fyne.TextAlignLeading, CanToggleVisible: true}, - ListColumn{Text: "Bitrate", Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}, - ListColumn{Text: "Size", Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}, - ListColumn{Text: "File Path", Alignment: fyne.TextAlignLeading, CanToggleVisible: true}, - ) - - t.hdr = NewListHeader(cols, t.colLayout) -} - // Gets the track at the given index. Thread-safe. func (t *Tracklist) TrackAt(idx int) *mediaprovider.Track { t.tracksMutex.RLock() @@ -254,30 +205,15 @@ func (t *Tracklist) TrackAt(idx int) *mediaprovider.Track { func (t *Tracklist) SetVisibleColumns(cols []string) { t.visibleColumns[0] = true t.visibleColumns[1] = true - if !t.compactRows { - // hard-code artist column to visible since it's part of title col - t.visibleColumns[2] = true - } - l := len(t.visibleColumns) - if !t.compactRows { - l-- // expanded rows have one fewer column due to merged title/artist - } - for i := 2; i < l; i++ { + for i := 2; i < len(t.columns); i++ { t.hdr.SetColumnVisible(i, false) } for _, col := range cols { - if num := ColNumber(col); num < 0 { + if num := t.ColNumber(col); num < 0 { log.Printf("Unknown tracklist column %q", col) } else { - if num == 2 && !t.compactRows { - // Artist column is hard-coded visible (part of title), skip - continue - } t.visibleColumns[num] = true - if !t.compactRows && num >= 3 { - num -= 1 // shift cols by 1 to account for Title/Artist combined col - } t.hdr.SetColumnVisible(num, true) } } @@ -287,7 +223,7 @@ func (t *Tracklist) VisibleColumns() []string { var cols []string for i := 2; i < len(t.visibleColumns); i++ { if t.visibleColumns[i] { - cols = append(cols, string(t.colName(i, false))) + cols = append(cols, string(t.colName(i))) } } return cols @@ -298,9 +234,6 @@ func (t *Tracklist) setColumnVisible(colNum int, vis bool) { log.Printf("error: Tracklist.SetColumnVisible: column index %d out of range", colNum) return } - if !t.compactRows && colNum >= 2 { - colNum++ // account for off-by-one from merged title/artist column - } t.visibleColumns[colNum] = vis t.list.Refresh() if t.OnVisibleColumnsChanged != nil { @@ -315,13 +248,15 @@ func (t *Tracklist) Sorting() TracklistSort { func (t *Tracklist) SetSorting(sorting TracklistSort) { if sorting.ColumnName == "" { // nil case - reset current sort - if slices.Contains(columns, t.sorting.ColumnName) { - t.hdr.SetSorting(ListHeaderSort{ColNumber: ColNumber(t.sorting.ColumnName), Type: SortNone}) + if slices.ContainsFunc(t.columns, func(c TracklistColumn) bool { + return c.Name == t.sorting.ColumnName + }) { + t.hdr.SetSorting(ListHeaderSort{ColNumber: t.ColNumber(t.sorting.ColumnName), Type: SortNone}) } return } // actual sorting will be handled in callback from header - t.hdr.SetSorting(ListHeaderSort{ColNumber: ColNumber(sorting.ColumnName), Type: sorting.SortOrder}) + t.hdr.SetSorting(ListHeaderSort{ColNumber: t.ColNumber(sorting.ColumnName), Type: sorting.SortOrder}) } // Sets the currently playing track ID and updates the list rendering @@ -511,7 +446,7 @@ func (t *Tracklist) doSortTracks() { } func (t *Tracklist) onSorted(sort ListHeaderSort) { - t.sorting = TracklistSort{ColumnName: t.colName(sort.ColNumber, true), SortOrder: sort.Type} + t.sorting = TracklistSort{ColumnName: t.colName(sort.ColNumber), SortOrder: sort.Type} t.tracksMutex.Lock() t.doSortTracks() t.tracksMutex.Unlock() @@ -729,20 +664,19 @@ func (t *Tracklist) lenTracks() int { return len(t.tracks) } -func ColNumber(colName string) int { - i := slices.Index(columns, colName) +func (t *Tracklist) ColNumber(colName string) int { + i := slices.IndexFunc(t.columns, func(c TracklistColumn) bool { + return c.Name == colName + }) if i < 0 { log.Printf("error: Tracklist: invalid column name %s", colName) } return i } -func (t *Tracklist) colName(i int, adjustForExpandedRows bool) string { - if adjustForExpandedRows && !t.compactRows && i >= 2 { - i += 1 // adjust for off-by-one from combined title/artist column - } - if i < len(columns) { - return columns[i] +func (t *Tracklist) colName(i int) string { + if i < len(t.columns) { + return t.columns[i].Name } log.Println("notReached: Tracklist.colName") return "" diff --git a/ui/widgets/tracklistrow.go b/ui/widgets/tracklistrow.go index fecda8d..06c1e6d 100644 --- a/ui/widgets/tracklistrow.go +++ b/ui/widgets/tracklistrow.go @@ -17,6 +17,67 @@ import ( const tracklistThumbnailSize = 48 +type TracklistColumn struct { + Name string + Col ListColumn +} + +const ( + ColumnNum = "Num" + ColumnTitle = "Title" + ColumnArtist = "Artist" + ColumnTitleArtist = "Title/Artist" + ColumnAlbum = "Album" + ColumnTime = "Time" + ColumnYear = "Year" + ColumnFavorite = "Favorite" + ColumnRating = "Rating" + ColumnPlays = "Plays" + ColumnComment = "Comment" + ColumnBitrate = "Bitrate" + ColumnSize = "Size" + ColumnPath = "Path" +) + +var ( + ExpandedTracklistRowColumns = []TracklistColumn{ + {Name: ColumnNum, Col: ListColumn{Text: "#", Alignment: fyne.TextAlignTrailing, CanToggleVisible: false}}, + {Name: ColumnTitleArtist, Col: ListColumn{Text: "Title / Artist", Alignment: fyne.TextAlignLeading, CanToggleVisible: false}}, + {Name: ColumnAlbum, Col: ListColumn{Text: "Album", Alignment: fyne.TextAlignLeading, CanToggleVisible: true}}, + {Name: ColumnTime, Col: ListColumn{Text: "Time", Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}}, + {Name: ColumnYear, Col: ListColumn{Text: "Year", Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}}, + {Name: ColumnFavorite, Col: ListColumn{Text: " Fav.", Alignment: fyne.TextAlignCenter, CanToggleVisible: true}}, + {Name: ColumnRating, Col: ListColumn{Text: "Rating", Alignment: fyne.TextAlignLeading, CanToggleVisible: true}}, + {Name: ColumnPlays, Col: ListColumn{Text: "Plays", Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}}, + {Name: ColumnComment, Col: ListColumn{Text: "Comment", Alignment: fyne.TextAlignLeading, CanToggleVisible: true}}, + {Name: ColumnBitrate, Col: ListColumn{Text: "Bitrate", Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}}, + {Name: ColumnSize, Col: ListColumn{Text: "Size", Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}}, + {Name: ColumnPath, Col: ListColumn{Text: "File Path", Alignment: fyne.TextAlignLeading, CanToggleVisible: true}}, + } + + // #, Title/Artist, Album, Time, Year, Favorite, Rating, Plays, Comment, Bitrate, Size, Path + ExpandedTracklistRowColumnWidths = []float32{40, -1, -1, 60, 60, 55, 100, 65, -1, 75, 75, -1} + + CompactTracklistRowColumns = []TracklistColumn{ + {Name: ColumnNum, Col: ListColumn{Text: "#", Alignment: fyne.TextAlignTrailing, CanToggleVisible: false}}, + {Name: ColumnTitle, Col: ListColumn{Text: "Title", Alignment: fyne.TextAlignLeading, CanToggleVisible: false}}, + {Name: ColumnArtist, Col: ListColumn{Text: "Artist", Alignment: fyne.TextAlignLeading, CanToggleVisible: true}}, + {Name: ColumnAlbum, Col: ListColumn{Text: "Album", Alignment: fyne.TextAlignLeading, CanToggleVisible: true}}, + {Name: ColumnTime, Col: ListColumn{Text: "Time", Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}}, + {Name: ColumnYear, Col: ListColumn{Text: "Year", Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}}, + {Name: ColumnFavorite, Col: ListColumn{Text: " Fav.", Alignment: fyne.TextAlignCenter, CanToggleVisible: true}}, + {Name: ColumnRating, Col: ListColumn{Text: "Rating", Alignment: fyne.TextAlignLeading, CanToggleVisible: true}}, + {Name: ColumnPlays, Col: ListColumn{Text: "Plays", Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}}, + {Name: ColumnComment, Col: ListColumn{Text: "Comment", Alignment: fyne.TextAlignLeading, CanToggleVisible: true}}, + {Name: ColumnBitrate, Col: ListColumn{Text: "Bitrate", Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}}, + {Name: ColumnSize, Col: ListColumn{Text: "Size", Alignment: fyne.TextAlignTrailing, CanToggleVisible: true}}, + {Name: ColumnPath, Col: ListColumn{Text: "File Path", Alignment: fyne.TextAlignLeading, CanToggleVisible: true}}, + } + + // #, Title, Artist, Album, Time, Year, Favorite, Rating, Plays, Comment, Bitrate, Size, Path + CompactTracklistRowColumnWidths = []float32{40, -1, -1, -1, 60, 60, 55, 100, 65, -1, 75, 75, -1} +) + type tracklistRowBase struct { FocusListRowBase @@ -47,7 +108,7 @@ type tracklistRowBase struct { size *widget.Label path *widget.Label - colHiddenPtrMap map[string]*bool + setColVisibility func(int, bool) bool } type TracklistRow interface { @@ -99,19 +160,11 @@ func NewExpandedTracklistRow(tracklist *Tracklist, im *backend.ImageManager, pla container := container.New(tracklist.colLayout, v(t.num), titleArtistImg, v(t.album), v(t.dur), v(t.year), v(t.favorite), v(t.rating), v(t.plays), v(t.comment), v(t.bitrate), v(t.size), v(t.path)) t.Content = container - var dummy bool - t.colHiddenPtrMap = map[string]*bool{ - ColumnArtist: &dummy, // artist column is never hidden - ColumnAlbum: &container.Objects[2].(*fyne.Container).Hidden, - ColumnTime: &container.Objects[3].(*fyne.Container).Hidden, - ColumnYear: &container.Objects[4].(*fyne.Container).Hidden, - ColumnFavorite: &container.Objects[5].(*fyne.Container).Hidden, - ColumnRating: &container.Objects[6].(*fyne.Container).Hidden, - ColumnPlays: &container.Objects[7].(*fyne.Container).Hidden, - ColumnComment: &container.Objects[8].(*fyne.Container).Hidden, - ColumnBitrate: &container.Objects[9].(*fyne.Container).Hidden, - ColumnSize: &container.Objects[10].(*fyne.Container).Hidden, - ColumnPath: &container.Objects[11].(*fyne.Container).Hidden, + t.setColVisibility = func(colNum int, vis bool) bool { + c := container.Objects[colNum].(*fyne.Container) + wasHidden := c.Hidden + c.Hidden = vis + return c.Hidden != wasHidden } return t } @@ -133,18 +186,27 @@ func NewCompactTracklistRow(tracklist *Tracklist, playingIcon fyne.CanvasObject) t.Content = container.New(tracklist.colLayout, t.num, t.name, t.artist, t.album, t.dur, t.year, t.favorite, t.rating, t.plays, t.comment, t.bitrate, t.size, t.path) - t.colHiddenPtrMap = map[string]*bool{ - ColumnArtist: &t.artist.Hidden, - ColumnAlbum: &t.album.Hidden, - ColumnTime: &t.dur.Hidden, - ColumnYear: &t.year.Hidden, - ColumnFavorite: &t.favorite.Hidden, - ColumnRating: &t.rating.Hidden, - ColumnPlays: &t.plays.Hidden, - ColumnComment: &t.comment.Hidden, - ColumnBitrate: &t.bitrate.Hidden, - ColumnSize: &t.size.Hidden, - ColumnPath: &t.path.Hidden, + colHiddenPtrMap := map[int]*bool{ + 2: &t.artist.Hidden, + 3: &t.album.Hidden, + 4: &t.dur.Hidden, + 5: &t.year.Hidden, + 6: &t.favorite.Hidden, + 7: &t.rating.Hidden, + 8: &t.plays.Hidden, + 9: &t.comment.Hidden, + 10: &t.bitrate.Hidden, + 11: &t.size.Hidden, + 12: &t.path.Hidden, + } + t.setColVisibility = func(colNum int, vis bool) bool { + ptr, ok := colHiddenPtrMap[colNum] + if !ok { + return false // column is always visible + } + wasHidden := *ptr + *ptr = vis + return vis != wasHidden } return t } @@ -266,25 +328,11 @@ func (t *tracklistRowBase) Update(tm *util.TrackListModel, rowNum int) { } // Show only columns configured to be visible - updateHidden := func(colName string) { - colHidden := !t.tracklist.visibleColumns[ColNumber(colName)] - hiddenPtr := t.colHiddenPtrMap[colName] - if colHidden != *hiddenPtr { - *hiddenPtr = colHidden + for i := 2; i < len(t.tracklist.columns); i++ { + if ch := t.setColVisibility(i, !t.tracklist.visibleColumns[i]); ch { changed = true } } - updateHidden(ColumnArtist) - updateHidden(ColumnAlbum) - updateHidden(ColumnTime) - updateHidden(ColumnYear) - updateHidden(ColumnFavorite) - updateHidden(ColumnRating) - updateHidden(ColumnPlays) - updateHidden(ColumnComment) - updateHidden(ColumnBitrate) - updateHidden(ColumnSize) - updateHidden(ColumnPath) if changed { t.Refresh()