From 5a352e3c6e40a0bf136dfb3c4461451d4c766f73 Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Sat, 18 Feb 2023 19:46:19 -0800 Subject: [PATCH 1/5] add right-click menu to toggle tracklist columns --- ui/browsing/playlistspage.go | 12 +++++- ui/widgets/artistgenreplaylist.go | 3 +- ui/widgets/listheader.go | 71 ++++++++++++++++++++++++++++++- ui/widgets/tracklist.go | 30 +++++++++++-- 4 files changed, 108 insertions(+), 8 deletions(-) diff --git a/ui/browsing/playlistspage.go b/ui/browsing/playlistspage.go index 95af201..4a2213e 100644 --- a/ui/browsing/playlistspage.go +++ b/ui/browsing/playlistspage.go @@ -99,8 +99,7 @@ func NewPlaylistList() *PlaylistList { a := &PlaylistList{ columnsLayout: layouts.NewColumnsLayout([]float32{-1, -1, 200, 125}), } - a.header = widgets.NewListHeader( - []widgets.ListColumn{{"Name", false}, {"Description", false}, {"Owner", false}, {"Track Count", true}}, a.columnsLayout) + a.buildHeader() a.list = widget.NewList( func() int { return len(a.Playlists) @@ -125,6 +124,15 @@ func NewPlaylistList() *PlaylistList { return a } +func (p *PlaylistList) buildHeader() { + p.header = widgets.NewListHeader([]widgets.ListColumn{ + {"Name", false, false}, + {"Description", false, false}, + {"Owner", false, false}, + {"Track Count", true, false}}, p.columnsLayout) + +} + func (p *PlaylistList) CreateRenderer() fyne.WidgetRenderer { return widget.NewSimpleRenderer(p.container) } diff --git a/ui/widgets/artistgenreplaylist.go b/ui/widgets/artistgenreplaylist.go index c827841..127c8fa 100644 --- a/ui/widgets/artistgenreplaylist.go +++ b/ui/widgets/artistgenreplaylist.go @@ -63,7 +63,8 @@ func NewArtistGenrePlaylist(items []ArtistGenrePlaylistItemModel) *ArtistGenrePl columnsLayout: layouts.NewColumnsLayout([]float32{-1, 125, 125}), } a.ExtendBaseWidget(a) - a.hdr = NewListHeader([]ListColumn{{"Name", false}, {"Album Count", true}, {"Track Count", true}}, a.columnsLayout) + a.hdr = NewListHeader([]ListColumn{ + {"Name", false, false}, {"Album Count", true, false}, {"Track Count", true, false}}, a.columnsLayout) a.list = widget.NewList( func() int { return len(a.Items) }, func() fyne.CanvasObject { diff --git a/ui/widgets/listheader.go b/ui/widgets/listheader.go index 18bcf0a..1223544 100644 --- a/ui/widgets/listheader.go +++ b/ui/widgets/listheader.go @@ -1,6 +1,7 @@ package widgets import ( + "log" "supersonic/ui/layouts" "fyne.io/fyne/v2" @@ -11,18 +12,23 @@ import ( ) type ListColumn struct { - Text string - AlignTrailing bool + Text string + AlignTrailing bool + CanToggleVisible bool } type ListHeader struct { widget.BaseWidget + OnColumnVisibilityChanged func(int, bool) + columns []ListColumn + columnVisible []bool columnsLayout *layouts.ColumnsLayout columnsContainer *fyne.Container container *fyne.Container + popUpMenu *fyne.Container } func NewListHeader(cols []ListColumn, layout *layouts.ColumnsLayout) *ListHeader { @@ -31,6 +37,10 @@ func NewListHeader(cols []ListColumn, layout *layouts.ColumnsLayout) *ListHeader columnsLayout: layout, columnsContainer: container.New(layout), } + l.columnVisible = make([]bool, len(cols)) + for i, _ := range l.columnVisible { + l.columnVisible[i] = true + } l.container = container.NewMax(canvas.NewRectangle(theme.BackgroundColor()), l.columnsContainer) l.ExtendBaseWidget(l) l.buildColumns() @@ -38,11 +48,16 @@ func NewListHeader(cols []ListColumn, layout *layouts.ColumnsLayout) *ListHeader } func (l *ListHeader) SetColumnVisible(colNum int, visible bool) { + if colNum >= len(l.columns) { + log.Println("error: ListHeader.SetColumnVisible: column index out of range") + return + } if visible { l.columnsContainer.Objects[colNum].Show() } else { l.columnsContainer.Objects[colNum].Hide() } + l.columnVisible[colNum] = visible } func (l *ListHeader) buildColumns() { @@ -61,3 +76,55 @@ func (l *ListHeader) buildColumns() { func (l *ListHeader) CreateRenderer() fyne.WidgetRenderer { return widget.NewSimpleRenderer(l.container) } + +func (l *ListHeader) TappedSecondary(e *fyne.PointEvent) { + l.setupPopUpMenu() + if len(l.popUpMenu.Objects) == 0 { + return + } + pop := widget.NewPopUp(l.popUpMenu, fyne.CurrentApp().Driver().CanvasForObject(l)) + pop.ShowAtPosition(e.AbsolutePosition) +} + +func (l *ListHeader) setupPopUpMenu() { + if l.popUpMenu == nil { + l.popUpMenu = container.New(&layouts.VboxCustomPadding{ExtraPad: -10}) + for i, c := range l.columns { + if c.CanToggleVisible { + l.popUpMenu.Add(widget.NewCheck(c.Text, l.createOnChangedCallbk(i))) + } + } + } + objIdx := 0 + for i, col := range l.columns { + if col.CanToggleVisible { + l.popUpMenu.Objects[objIdx].(*widget.Check).Checked = l.columnVisible[i] + objIdx++ + } + } +} + +func (l *ListHeader) createOnChangedCallbk(colNum int) func(bool) { + return func(val bool) { + l.columnVisible[colNum] = val + l.SetColumnVisible(colNum, val) + if l.OnColumnVisibilityChanged != nil { + l.OnColumnVisibilityChanged(colNum, val) + } + } +} + +type colVisibleToggle struct { + widget.Check +} + +func newColVisibleToggle(colNum int, colName string, visibilities []bool, onChanged func(bool)) *colVisibleToggle { + c := &colVisibleToggle{ + Check: widget.Check{ + Text: colName, + OnChanged: onChanged, + }, + } + c.ExtendBaseWidget(c) + return c +} diff --git a/ui/widgets/tracklist.go b/ui/widgets/tracklist.go index d586f48..c5082b1 100644 --- a/ui/widgets/tracklist.go +++ b/ui/widgets/tracklist.go @@ -1,6 +1,7 @@ package widgets import ( + "log" "runtime" "strconv" "supersonic/ui/layouts" @@ -59,9 +60,11 @@ func NewTracklist(tracks []*subsonic.Child) *Tracklist { t.ExtendBaseWidget(t) t.selectionMgr = util.NewListSelectionManager(func() int { return len(t.Tracks) }) t.colLayout = layouts.NewColumnsLayout([]float32{35, -1, -1, -1, 60, 65, 75}) - t.hdr = NewListHeader([]ListColumn{ - {"#", true}, {"Title", false}, {"Artist", false}, {"Album", false}, {"Time", true}, {"Plays", true}, {"Bitrate", true}}, - t.colLayout) + t.buildHeader() + t.hdr.OnColumnVisibilityChanged = func(col int, vis bool) { + // first 2 columns are builtin and always visible + t.SetColumnVisible(col-2, vis) + } playingIcon := container.NewCenter(container.NewHBox(NewHSpace(2), widget.NewIcon(theme.MediaPlayIcon()))) t.list = widget.NewList( func() int { return len(t.Tracks) }, @@ -86,6 +89,18 @@ func NewTracklist(tracks []*subsonic.Child) *Tracklist { return t } +func (t *Tracklist) buildHeader() { + t.hdr = NewListHeader([]ListColumn{ + {"#", true, false}, + {"Title", false, false}, + {"Artist", false, true}, + {"Album", false, true}, + {"Time", true, true}, + {"Plays", true, true}, + {"Bitrate", true, true}}, + t.colLayout) +} + func (t *Tracklist) SetVisibleColumns(cols []TracklistColumn) { for i := range t.visibleColumns { t.visibleColumns[i] = false @@ -95,6 +110,15 @@ func (t *Tracklist) SetVisibleColumns(cols []TracklistColumn) { } } +func (t *Tracklist) SetColumnVisible(colNum int, vis bool) { + if colNum >= len(t.visibleColumns) { + log.Printf("error: Tracklist.SetColumnVisible: column index %d out of range", colNum) + return + } + t.visibleColumns[colNum] = vis + t.Refresh() +} + func (t *Tracklist) SetNowPlaying(trackID string) { t.nowPlayingIdx = -1 for i, tr := range t.Tracks { From afe37609eace18ce29704afd4477e95c4f563b9a Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Sat, 18 Feb 2023 20:30:58 -0800 Subject: [PATCH 2/5] fix toggle column visibility bug with 0 play count tracks --- ui/widgets/listheader.go | 15 ------- ui/widgets/tracklist.go | 89 ++++++++++++++++++---------------------- 2 files changed, 41 insertions(+), 63 deletions(-) diff --git a/ui/widgets/listheader.go b/ui/widgets/listheader.go index 1223544..4fdedc8 100644 --- a/ui/widgets/listheader.go +++ b/ui/widgets/listheader.go @@ -113,18 +113,3 @@ func (l *ListHeader) createOnChangedCallbk(colNum int) func(bool) { } } } - -type colVisibleToggle struct { - widget.Check -} - -func newColVisibleToggle(colNum int, colName string, visibilities []bool, onChanged func(bool)) *colVisibleToggle { - c := &colVisibleToggle{ - Check: widget.Check{ - Text: colName, - OnChanged: onChanged, - }, - } - c.ExtendBaseWidget(c) - return c -} diff --git a/ui/widgets/tracklist.go b/ui/widgets/tracklist.go index c5082b1..7a22888 100644 --- a/ui/widgets/tracklist.go +++ b/ui/widgets/tracklist.go @@ -55,16 +55,13 @@ type Tracklist struct { } func NewTracklist(tracks []*subsonic.Child) *Tracklist { - t := &Tracklist{Tracks: tracks, nowPlayingIdx: -1, visibleColumns: make([]bool, 5)} + t := &Tracklist{Tracks: tracks, nowPlayingIdx: -1, visibleColumns: make([]bool, 7)} t.ExtendBaseWidget(t) t.selectionMgr = util.NewListSelectionManager(func() int { return len(t.Tracks) }) t.colLayout = layouts.NewColumnsLayout([]float32{35, -1, -1, -1, 60, 65, 75}) t.buildHeader() - t.hdr.OnColumnVisibilityChanged = func(col int, vis bool) { - // first 2 columns are builtin and always visible - t.SetColumnVisible(col-2, vis) - } + t.hdr.OnColumnVisibilityChanged = t.setColumnVisible playingIcon := container.NewCenter(container.NewHBox(NewHSpace(2), widget.NewIcon(theme.MediaPlayIcon()))) t.list = widget.NewList( func() int { return len(t.Tracks) }, @@ -102,15 +99,19 @@ func (t *Tracklist) buildHeader() { } func (t *Tracklist) SetVisibleColumns(cols []TracklistColumn) { - for i := range t.visibleColumns { + t.visibleColumns[0] = true + t.visibleColumns[1] = true + for i := 2; i < len(t.visibleColumns); i++ { t.visibleColumns[i] = false + t.hdr.SetColumnVisible(i, false) } for _, col := range cols { t.visibleColumns[col.ColNumber()] = true + t.hdr.SetColumnVisible(col.ColNumber(), true) } } -func (t *Tracklist) SetColumnVisible(colNum int, vis bool) { +func (t *Tracklist) setColumnVisible(colNum int, vis bool) { if colNum >= len(t.visibleColumns) { log.Printf("error: Tracklist.SetColumnVisible: column index %d out of range", colNum) return @@ -153,14 +154,6 @@ func (t *Tracklist) UnselectAll() { t.Refresh() } -func (t *Tracklist) Refresh() { - for i, tf := range t.visibleColumns { - // first 2 columns are built-in and always visible - t.hdr.SetColumnVisible(i+2, tf) - } - t.BaseWidget.Refresh() -} - func (t *Tracklist) CreateRenderer() fyne.WidgetRenderer { return widget.NewSimpleRenderer(t.container) } @@ -258,15 +251,15 @@ func (c TracklistColumn) ColNumber() int { // built-in columns # and Title are always visible switch c { case ColumnArtist: - return 0 - case ColumnAlbum: - return 1 - case ColumnTime: return 2 - case ColumnPlays: + case ColumnAlbum: return 3 - case ColumnBitrate: + case ColumnTime: return 4 + case ColumnPlays: + return 5 + case ColumnBitrate: + return 6 default: return -100 } @@ -327,29 +320,35 @@ func NewTrackRow(tracklist *Tracklist, playingIcon fyne.CanvasObject) *TrackRow } func (t *TrackRow) Update(tr *subsonic.Child, isPlaying bool, rowNum int) { - if tr.ID == t.trackID && isPlaying == t.isPlaying && tr.PlayCount == t.playCount { - return - } - t.isPlaying = isPlaying - t.trackID = tr.ID + if tr.ID != t.trackID || isPlaying != t.isPlaying || tr.PlayCount != t.playCount { + t.isPlaying = isPlaying + t.trackID = tr.ID + t.playCount = tr.PlayCount - if rowNum < 0 { - rowNum = tr.Track - } - t.num.Segments[0].(*widget.TextSegment).Text = strconv.Itoa(rowNum) - t.name.Segments[0].(*widget.TextSegment).Text = tr.Title - t.artist.Segments[0].(*widget.TextSegment).Text = tr.Artist - t.album.Segments[0].(*widget.TextSegment).Text = tr.Album - t.dur.Segments[0].(*widget.TextSegment).Text = util.SecondsToTimeString(float64(tr.Duration)) - t.plays.Segments[0].(*widget.TextSegment).Text = strconv.Itoa(int(tr.PlayCount)) - t.bitrate.Segments[0].(*widget.TextSegment).Text = strconv.Itoa(tr.BitRate) + if rowNum < 0 { + rowNum = tr.Track + } + t.num.Segments[0].(*widget.TextSegment).Text = strconv.Itoa(rowNum) + t.name.Segments[0].(*widget.TextSegment).Text = tr.Title + t.artist.Segments[0].(*widget.TextSegment).Text = tr.Artist + t.album.Segments[0].(*widget.TextSegment).Text = tr.Album + t.dur.Segments[0].(*widget.TextSegment).Text = util.SecondsToTimeString(float64(tr.Duration)) + t.plays.Segments[0].(*widget.TextSegment).Text = strconv.Itoa(int(tr.PlayCount)) + t.bitrate.Segments[0].(*widget.TextSegment).Text = strconv.Itoa(tr.BitRate) - t.name.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = isPlaying - t.artist.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = isPlaying - t.album.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = isPlaying - t.dur.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = isPlaying - t.plays.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = isPlaying - t.bitrate.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = isPlaying + t.name.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = isPlaying + t.artist.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = isPlaying + t.album.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = isPlaying + t.dur.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = isPlaying + t.plays.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = isPlaying + t.bitrate.Segments[0].(*widget.TextSegment).Style.TextStyle.Bold = isPlaying + + if isPlaying { + t.container.Objects[1].(*fyne.Container).Objects[0] = container.NewCenter(t.playingIcon) + } else { + t.container.Objects[1].(*fyne.Container).Objects[0] = t.num + } + } t.artist.Hidden = !t.tracklist.visibleColumns[ColumnArtist.ColNumber()] t.album.Hidden = !t.tracklist.visibleColumns[ColumnAlbum.ColNumber()] @@ -357,12 +356,6 @@ func (t *TrackRow) Update(tr *subsonic.Child, isPlaying bool, rowNum int) { t.plays.Hidden = !t.tracklist.visibleColumns[ColumnPlays.ColNumber()] t.bitrate.Hidden = !t.tracklist.visibleColumns[ColumnBitrate.ColNumber()] - if isPlaying { - t.container.Objects[1].(*fyne.Container).Objects[0] = container.NewCenter(t.playingIcon) - } else { - t.container.Objects[1].(*fyne.Container).Objects[0] = t.num - } - t.Refresh() } From 85e79921da767c785a53825fe79904ba01193f17 Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Sat, 18 Feb 2023 21:04:56 -0800 Subject: [PATCH 3/5] persist album page tracklist column visibility preference --- backend/config.go | 8 +++++ ui/browsing/albumpage.go | 12 +++++-- ui/browsing/nowplayingpage.go | 2 +- ui/browsing/playlistpage.go | 2 +- ui/browsing/router.go | 2 +- ui/widgets/tracklist.go | 61 +++++++++++++++++++++++++---------- 6 files changed, 64 insertions(+), 23 deletions(-) diff --git a/backend/config.go b/backend/config.go index b636019..2696fa1 100644 --- a/backend/config.go +++ b/backend/config.go @@ -20,9 +20,14 @@ type AppConfig struct { WindowHeight int } +type AlbumPageConfig struct { + TracklistColumns []string +} + type Config struct { Application AppConfig Servers []*ServerConfig + AlbumPage AlbumPageConfig } func DefaultConfig() *Config { @@ -31,6 +36,9 @@ func DefaultConfig() *Config { WindowWidth: 1000, WindowHeight: 800, }, + AlbumPage: AlbumPageConfig{ + TracklistColumns: []string{"Artist", "Time", "Plays"}, + }, } } diff --git a/ui/browsing/albumpage.go b/ui/browsing/albumpage.go index e37e979..c5a58f9 100644 --- a/ui/browsing/albumpage.go +++ b/ui/browsing/albumpage.go @@ -31,6 +31,7 @@ type AlbumPage struct { type albumPageState struct { albumID string + cfg *backend.AlbumPageConfig lm *backend.LibraryManager pm *backend.PlaybackManager im *backend.ImageManager @@ -41,6 +42,7 @@ type albumPageState struct { func NewAlbumPage( albumID string, + cfg *backend.AlbumPageConfig, sm *backend.ServerManager, pm *backend.PlaybackManager, lm *backend.LibraryManager, @@ -51,6 +53,7 @@ func NewAlbumPage( a := &AlbumPage{ albumPageState: albumPageState{ albumID: albumID, + cfg: cfg, sm: sm, pm: pm, lm: lm, @@ -62,8 +65,7 @@ func NewAlbumPage( a.ExtendBaseWidget(a) a.header = NewAlbumPageHeader(a) a.tracklist = widgets.NewTracklist(nil) - a.tracklist.SetVisibleColumns([]widgets.TracklistColumn{ - widgets.ColumnArtist, widgets.ColumnTime, widgets.ColumnPlays}) + a.tracklist.SetVisibleColumns(a.cfg.TracklistColumns) // connect tracklist actions a.tracklist.OnPlayTrackAt = a.onPlayTrackAt a.tracklist.OnAddToQueue = func(tracks []*subsonic.Child) { a.pm.LoadTracks(tracks, true, false) } @@ -86,6 +88,10 @@ func (a *AlbumPage) CreateRenderer() fyne.WidgetRenderer { } func (a *AlbumPage) Save() SavedPage { + // TODO: find a better place to update the tracklist columns preference + // If user changes columns but doesn't navigate to another page, + // we won't be persisting the change + a.cfg.TracklistColumns = a.tracklist.VisibleColumns() s := a.albumPageState return &s } @@ -254,5 +260,5 @@ func formatMiscLabelStr(a *subsonic.AlbumID3) string { } func (s *albumPageState) Restore() Page { - return NewAlbumPage(s.albumID, s.sm, s.pm, s.lm, s.im, s.contr, s.nav) + return NewAlbumPage(s.albumID, s.cfg, s.sm, s.pm, s.lm, s.im, s.contr, s.nav) } diff --git a/ui/browsing/nowplayingpage.go b/ui/browsing/nowplayingpage.go index 6fc93e7..68fa924 100644 --- a/ui/browsing/nowplayingpage.go +++ b/ui/browsing/nowplayingpage.go @@ -39,7 +39,7 @@ func NewNowPlayingPage( a := &NowPlayingPage{nowPlayingPageState: nowPlayingPageState{contr: contr, sm: sm, pm: pm, nav: nav}} a.ExtendBaseWidget(a) a.tracklist = widgets.NewTracklist(nil) - a.tracklist.SetVisibleColumns([]widgets.TracklistColumn{ + a.tracklist.SetVisibleColumns([]string{ widgets.ColumnArtist, widgets.ColumnAlbum, widgets.ColumnTime}) a.tracklist.AutoNumber = true a.tracklist.DisablePlaybackMenu = true diff --git a/ui/browsing/playlistpage.go b/ui/browsing/playlistpage.go index 1a70ea6..31a63c2 100644 --- a/ui/browsing/playlistpage.go +++ b/ui/browsing/playlistpage.go @@ -49,7 +49,7 @@ func NewPlaylistPage( a.ExtendBaseWidget(a) a.header = NewPlaylistPageHeader(a) a.tracklist = widgets.NewTracklist(nil) - a.tracklist.SetVisibleColumns([]widgets.TracklistColumn{ + a.tracklist.SetVisibleColumns([]string{ widgets.ColumnArtist, widgets.ColumnAlbum, widgets.ColumnTime, widgets.ColumnPlays}) a.tracklist.AutoNumber = true a.tracklist.AuxiliaryMenuItems = []*fyne.MenuItem{ diff --git a/ui/browsing/router.go b/ui/browsing/router.go index 0146a99..e5138e2 100644 --- a/ui/browsing/router.go +++ b/ui/browsing/router.go @@ -87,7 +87,7 @@ func NewRouter(app *backend.App, controller controller.Controller, nav Navigatio func (r Router) CreatePage(rte Route) Page { switch rte.Page { case Album: - return NewAlbumPage(rte.Arg, r.App.ServerManager, r.App.PlaybackManager, r.App.LibraryManager, r.App.ImageManager, r.Controller, r.OpenRoute) + return NewAlbumPage(rte.Arg, &r.App.Config.AlbumPage, r.App.ServerManager, r.App.PlaybackManager, r.App.LibraryManager, r.App.ImageManager, r.Controller, r.OpenRoute) case Albums: return NewAlbumsPage("Albums", rte.Arg, r.App.PlaybackManager, r.App.LibraryManager, r.App.ImageManager, r.OpenRoute) case Artist: diff --git a/ui/widgets/tracklist.go b/ui/widgets/tracklist.go index 7a22888..7a293ed 100644 --- a/ui/widgets/tracklist.go +++ b/ui/widgets/tracklist.go @@ -18,14 +18,12 @@ import ( "github.com/dweymouth/go-subsonic/subsonic" ) -type TracklistColumn string - const ( - ColumnArtist TracklistColumn = "Artist" - ColumnAlbum TracklistColumn = "Album" - ColumnTime TracklistColumn = "Time" - ColumnPlays TracklistColumn = "Plays" - ColumnBitrate TracklistColumn = "Bitrate" + ColumnArtist = "Artist" + ColumnAlbum = "Album" + ColumnTime = "Time" + ColumnPlays = "Plays" + ColumnBitrate = "Bitrate" ) type Tracklist struct { @@ -98,7 +96,7 @@ func (t *Tracklist) buildHeader() { t.colLayout) } -func (t *Tracklist) SetVisibleColumns(cols []TracklistColumn) { +func (t *Tracklist) SetVisibleColumns(cols []string) { t.visibleColumns[0] = true t.visibleColumns[1] = true for i := 2; i < len(t.visibleColumns); i++ { @@ -106,11 +104,21 @@ func (t *Tracklist) SetVisibleColumns(cols []TracklistColumn) { t.hdr.SetColumnVisible(i, false) } for _, col := range cols { - t.visibleColumns[col.ColNumber()] = true - t.hdr.SetColumnVisible(col.ColNumber(), true) + t.visibleColumns[ColNumber(col)] = true + t.hdr.SetColumnVisible(ColNumber(col), true) } } +func (t *Tracklist) VisibleColumns() []string { + var cols []string + for i := 2; i < len(t.visibleColumns); i++ { + if t.visibleColumns[i] { + cols = append(cols, string(colName(i))) + } + } + return cols +} + func (t *Tracklist) setColumnVisible(colNum int, vis bool) { if colNum >= len(t.visibleColumns) { log.Printf("error: Tracklist.SetColumnVisible: column index %d out of range", colNum) @@ -247,9 +255,9 @@ func (t *Tracklist) SelectedTrackIndexes() []int { return t.selectionMgr.GetSelection() } -func (c TracklistColumn) ColNumber() int { +func ColNumber(colName string) int { // built-in columns # and Title are always visible - switch c { + switch colName { case ColumnArtist: return 2 case ColumnAlbum: @@ -261,10 +269,29 @@ func (c TracklistColumn) ColNumber() int { case ColumnBitrate: return 6 default: + log.Printf("error: Tracklist: invalid column name %s", colName) return -100 } } +func colName(i int) string { + // built-in columns # and Title are always visible + switch i { + case 2: + return ColumnArtist + case 3: + return ColumnAlbum + case 4: + return ColumnTime + case 5: + return ColumnPlays + case 6: + return ColumnBitrate + default: + return "" + } +} + type TrackRow struct { widget.BaseWidget @@ -350,11 +377,11 @@ func (t *TrackRow) Update(tr *subsonic.Child, isPlaying bool, rowNum int) { } } - t.artist.Hidden = !t.tracklist.visibleColumns[ColumnArtist.ColNumber()] - t.album.Hidden = !t.tracklist.visibleColumns[ColumnAlbum.ColNumber()] - t.dur.Hidden = !t.tracklist.visibleColumns[ColumnTime.ColNumber()] - t.plays.Hidden = !t.tracklist.visibleColumns[ColumnPlays.ColNumber()] - t.bitrate.Hidden = !t.tracklist.visibleColumns[ColumnBitrate.ColNumber()] + t.artist.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnArtist)] + t.album.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnAlbum)] + t.dur.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnTime)] + t.plays.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnPlays)] + t.bitrate.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnBitrate)] t.Refresh() } From 64bdca7caf69b06bae3c2dc8fb591526ea95c593 Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Sun, 19 Feb 2023 09:03:13 -0800 Subject: [PATCH 4/5] persist tracklist columns setting in Playlist and Now Playing pages --- backend/config.go | 22 +++++++++++++++++++--- ui/browsing/nowplayingpage.go | 10 ++++++---- ui/browsing/playlistpage.go | 10 ++++++---- ui/browsing/router.go | 4 ++-- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/backend/config.go b/backend/config.go index 2696fa1..ea77b01 100644 --- a/backend/config.go +++ b/backend/config.go @@ -24,10 +24,20 @@ type AlbumPageConfig struct { TracklistColumns []string } +type NowPlayingPageConfig struct { + TracklistColumns []string +} + +type PlaylistPageConfig struct { + TracklistColumns []string +} + type Config struct { - Application AppConfig - Servers []*ServerConfig - AlbumPage AlbumPageConfig + Application AppConfig + Servers []*ServerConfig + AlbumPage AlbumPageConfig + NowPlayingPage NowPlayingPageConfig + PlaylistPage PlaylistPageConfig } func DefaultConfig() *Config { @@ -39,6 +49,12 @@ func DefaultConfig() *Config { AlbumPage: AlbumPageConfig{ TracklistColumns: []string{"Artist", "Time", "Plays"}, }, + NowPlayingPage: NowPlayingPageConfig{ + TracklistColumns: []string{"Artist", "Album", "Time"}, + }, + PlaylistPage: PlaylistPageConfig{ + TracklistColumns: []string{"Artist", "Album", "Time", "Plays"}, + }, } } diff --git a/ui/browsing/nowplayingpage.go b/ui/browsing/nowplayingpage.go index 68fa924..827b334 100644 --- a/ui/browsing/nowplayingpage.go +++ b/ui/browsing/nowplayingpage.go @@ -25,6 +25,7 @@ type NowPlayingPage struct { type nowPlayingPageState struct { contr controller.Controller + conf *backend.NowPlayingPageConfig sm *backend.ServerManager pm *backend.PlaybackManager nav func(Route) @@ -32,15 +33,15 @@ type nowPlayingPageState struct { func NewNowPlayingPage( contr controller.Controller, + conf *backend.NowPlayingPageConfig, sm *backend.ServerManager, pm *backend.PlaybackManager, nav func(Route), ) *NowPlayingPage { - a := &NowPlayingPage{nowPlayingPageState: nowPlayingPageState{contr: contr, sm: sm, pm: pm, nav: nav}} + a := &NowPlayingPage{nowPlayingPageState: nowPlayingPageState{contr: contr, conf: conf, sm: sm, pm: pm, nav: nav}} a.ExtendBaseWidget(a) a.tracklist = widgets.NewTracklist(nil) - a.tracklist.SetVisibleColumns([]string{ - widgets.ColumnArtist, widgets.ColumnAlbum, widgets.ColumnTime}) + a.tracklist.SetVisibleColumns(conf.TracklistColumns) a.tracklist.AutoNumber = true a.tracklist.DisablePlaybackMenu = true a.tracklist.OnPlayTrackAt = a.onPlayTrackAt @@ -61,6 +62,7 @@ func (a *NowPlayingPage) CreateRenderer() fyne.WidgetRenderer { } func (a *NowPlayingPage) Save() SavedPage { + a.conf.TracklistColumns = a.tracklist.VisibleColumns() nps := a.nowPlayingPageState return &nps } @@ -109,5 +111,5 @@ func (a *NowPlayingPage) load() { } func (s *nowPlayingPageState) Restore() Page { - return NewNowPlayingPage(s.contr, s.sm, s.pm, s.nav) + return NewNowPlayingPage(s.contr, s.conf, s.sm, s.pm, s.nav) } diff --git a/ui/browsing/playlistpage.go b/ui/browsing/playlistpage.go index 31a63c2..56ef385 100644 --- a/ui/browsing/playlistpage.go +++ b/ui/browsing/playlistpage.go @@ -30,6 +30,7 @@ type PlaylistPage struct { type playlistPageState struct { playlistID string + conf *backend.PlaylistPageConfig contr controller.Controller sm *backend.ServerManager pm *backend.PlaybackManager @@ -39,18 +40,18 @@ type playlistPageState struct { func NewPlaylistPage( playlistID string, + conf *backend.PlaylistPageConfig, contr controller.Controller, sm *backend.ServerManager, pm *backend.PlaybackManager, im *backend.ImageManager, nav func(Route), ) *PlaylistPage { - a := &PlaylistPage{playlistPageState: playlistPageState{playlistID: playlistID, contr: contr, sm: sm, pm: pm, im: im}} + a := &PlaylistPage{playlistPageState: playlistPageState{playlistID: playlistID, conf: conf, contr: contr, sm: sm, pm: pm, im: im}} a.ExtendBaseWidget(a) a.header = NewPlaylistPageHeader(a) a.tracklist = widgets.NewTracklist(nil) - a.tracklist.SetVisibleColumns([]string{ - widgets.ColumnArtist, widgets.ColumnAlbum, widgets.ColumnTime, widgets.ColumnPlays}) + a.tracklist.SetVisibleColumns(conf.TracklistColumns) a.tracklist.AutoNumber = true a.tracklist.AuxiliaryMenuItems = []*fyne.MenuItem{ fyne.NewMenuItem("Remove from playlist", a.onRemoveSelectedFromPlaylist), @@ -76,6 +77,7 @@ func (a *PlaylistPage) CreateRenderer() fyne.WidgetRenderer { } func (a *PlaylistPage) Save() SavedPage { + a.conf.TracklistColumns = a.tracklist.VisibleColumns() p := a.playlistPageState return &p } @@ -218,5 +220,5 @@ func (a *PlaylistPageHeader) formatPlaylistTrackTimeStr(p *subsonic.Playlist) st } func (s *playlistPageState) Restore() Page { - return NewPlaylistPage(s.playlistID, s.contr, s.sm, s.pm, s.im, s.nav) + return NewPlaylistPage(s.playlistID, s.conf, s.contr, s.sm, s.pm, s.im, s.nav) } diff --git a/ui/browsing/router.go b/ui/browsing/router.go index e5138e2..d9a5c6b 100644 --- a/ui/browsing/router.go +++ b/ui/browsing/router.go @@ -101,9 +101,9 @@ func (r Router) CreatePage(rte Route) Page { case Genres: return NewArtistsGenresPage(true, r.App.ServerManager, r.OpenRoute) case NowPlaying: - return NewNowPlayingPage(r.Controller, r.App.ServerManager, r.App.PlaybackManager, r.OpenRoute) + return NewNowPlayingPage(r.Controller, &r.App.Config.NowPlayingPage, r.App.ServerManager, r.App.PlaybackManager, r.OpenRoute) case Playlist: - return NewPlaylistPage(rte.Arg, r.Controller, r.App.ServerManager, r.App.PlaybackManager, r.App.ImageManager, r.OpenRoute) + return NewPlaylistPage(rte.Arg, &r.App.Config.PlaylistPage, r.Controller, r.App.ServerManager, r.App.PlaybackManager, r.App.ImageManager, r.OpenRoute) case Playlists: return NewPlaylistsPage(r.App.ServerManager, r.OpenRoute) } From 4da2e8e0248fc81d39e9a88a0143e603a736694f Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Sun, 19 Feb 2023 09:07:02 -0800 Subject: [PATCH 5/5] update README that we support tracklist column config --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 79e666d..aa29dbc 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,9 @@ Slightly outdated screenshots of Supersonic running against the Navidrome