more pooling of widgets, rename and rework a few things
This commit is contained in:
+38
-23
@@ -25,6 +25,7 @@ type AlbumPage struct {
|
||||
|
||||
albumPageState
|
||||
|
||||
disposed bool
|
||||
header *AlbumPageHeader
|
||||
tracks []*mediaprovider.Track
|
||||
tracklist *widgets.Tracklist
|
||||
@@ -33,32 +34,32 @@ type AlbumPage struct {
|
||||
}
|
||||
|
||||
type albumPageState struct {
|
||||
albumID string
|
||||
sort widgets.TracklistSort
|
||||
cfg *backend.AlbumPageConfig
|
||||
widgetCache *util.WidgetCache
|
||||
mp mediaprovider.MediaProvider
|
||||
pm *backend.PlaybackManager
|
||||
im *backend.ImageManager
|
||||
contr *controller.Controller
|
||||
albumID string
|
||||
sort widgets.TracklistSort
|
||||
cfg *backend.AlbumPageConfig
|
||||
pool *util.WidgetPool
|
||||
mp mediaprovider.MediaProvider
|
||||
pm *backend.PlaybackManager
|
||||
im *backend.ImageManager
|
||||
contr *controller.Controller
|
||||
}
|
||||
|
||||
func NewAlbumPage(
|
||||
albumID string,
|
||||
cfg *backend.AlbumPageConfig,
|
||||
cache *util.WidgetCache,
|
||||
pool *util.WidgetPool,
|
||||
pm *backend.PlaybackManager,
|
||||
mp mediaprovider.MediaProvider,
|
||||
im *backend.ImageManager,
|
||||
contr *controller.Controller,
|
||||
) *AlbumPage {
|
||||
return newAlbumPage(albumID, cfg, cache, pm, mp, im, contr, widgets.TracklistSort{})
|
||||
return newAlbumPage(albumID, cfg, pool, pm, mp, im, contr, widgets.TracklistSort{})
|
||||
}
|
||||
|
||||
func newAlbumPage(
|
||||
albumID string,
|
||||
cfg *backend.AlbumPageConfig,
|
||||
cache *util.WidgetCache,
|
||||
pool *util.WidgetPool,
|
||||
pm *backend.PlaybackManager,
|
||||
mp mediaprovider.MediaProvider,
|
||||
im *backend.ImageManager,
|
||||
@@ -67,24 +68,29 @@ func newAlbumPage(
|
||||
) *AlbumPage {
|
||||
a := &AlbumPage{
|
||||
albumPageState: albumPageState{
|
||||
albumID: albumID,
|
||||
cfg: cfg,
|
||||
widgetCache: cache,
|
||||
pm: pm,
|
||||
mp: mp,
|
||||
im: im,
|
||||
contr: contr,
|
||||
albumID: albumID,
|
||||
cfg: cfg,
|
||||
pool: pool,
|
||||
pm: pm,
|
||||
mp: mp,
|
||||
im: im,
|
||||
contr: contr,
|
||||
},
|
||||
}
|
||||
a.ExtendBaseWidget(a)
|
||||
if h := a.widgetCache.Obtain(util.WidgetTypeAlbumPageHeader); h != nil {
|
||||
if h := a.pool.Obtain(util.WidgetTypeAlbumPageHeader); h != nil {
|
||||
a.header = h.(*AlbumPageHeader)
|
||||
a.header.Clear()
|
||||
} else {
|
||||
a.header = NewAlbumPageHeader(a)
|
||||
}
|
||||
a.header.page = a
|
||||
a.tracklist = widgets.NewTracklist(nil)
|
||||
if t := a.pool.Obtain(util.WidgetTypeTracklist); t != nil {
|
||||
a.tracklist = t.(*widgets.Tracklist)
|
||||
a.tracklist.Reset()
|
||||
} else {
|
||||
a.tracklist = widgets.NewTracklist(nil)
|
||||
}
|
||||
a.tracklist.SetVisibleColumns(a.cfg.TracklistColumns)
|
||||
a.tracklist.SetSorting(sort)
|
||||
a.tracklist.OnVisibleColumnsChanged = func(cols []string) {
|
||||
@@ -105,9 +111,12 @@ func (a *AlbumPage) CreateRenderer() fyne.WidgetRenderer {
|
||||
}
|
||||
|
||||
func (a *AlbumPage) Save() SavedPage {
|
||||
a.disposed = true
|
||||
s := a.albumPageState
|
||||
s.sort = a.tracklist.Sorting()
|
||||
a.widgetCache.Release(util.WidgetTypeAlbumPageHeader, a.header)
|
||||
a.header.page = nil
|
||||
a.pool.Release(util.WidgetTypeAlbumPageHeader, a.header)
|
||||
a.pool.Release(util.WidgetTypeTracklist, a.tracklist)
|
||||
return &s
|
||||
}
|
||||
|
||||
@@ -144,6 +153,9 @@ func (a *AlbumPage) load() {
|
||||
log.Printf("Failed to get album: %s", err.Error())
|
||||
return
|
||||
}
|
||||
if a.disposed {
|
||||
return
|
||||
}
|
||||
a.header.Update(album, a.im)
|
||||
a.tracklist.Options = widgets.TracklistOptions{
|
||||
ShowDiscNumber: album.Tracks[0].DiscNumber != album.Tracks[len(album.Tracks)-1].DiscNumber,
|
||||
@@ -279,6 +291,7 @@ func (a *AlbumPageHeader) Clear() {
|
||||
a.miscLabel.SetText("")
|
||||
a.toggleFavButton.IsFavorited = false
|
||||
a.cover.Image.Image = nil
|
||||
a.cover.Refresh()
|
||||
}
|
||||
|
||||
func (a *AlbumPageHeader) toggleFavorited() {
|
||||
@@ -292,7 +305,9 @@ func (a *AlbumPageHeader) showPopUpCover() {
|
||||
log.Printf("error getting full size album cover: %s", err.Error())
|
||||
return
|
||||
}
|
||||
a.page.contr.ShowPopUpImage(cover)
|
||||
if a.page != nil {
|
||||
a.page.contr.ShowPopUpImage(cover)
|
||||
}
|
||||
}
|
||||
|
||||
func formatMiscLabelStr(a *mediaprovider.AlbumWithTracks) string {
|
||||
@@ -308,5 +323,5 @@ func formatMiscLabelStr(a *mediaprovider.AlbumWithTracks) string {
|
||||
}
|
||||
|
||||
func (s *albumPageState) Restore() Page {
|
||||
return newAlbumPage(s.albumID, s.cfg, s.widgetCache, s.pm, s.mp, s.im, s.contr, s.sort)
|
||||
return newAlbumPage(s.albumID, s.cfg, s.pool, s.pm, s.mp, s.im, s.contr, s.sort)
|
||||
}
|
||||
|
||||
+41
-24
@@ -28,18 +28,19 @@ type artistPageState struct {
|
||||
activeView int
|
||||
trackSort widgets.TracklistSort
|
||||
|
||||
widgetCache *util.WidgetCache
|
||||
cfg *backend.ArtistPageConfig
|
||||
pm *backend.PlaybackManager
|
||||
mp mediaprovider.MediaProvider
|
||||
im *backend.ImageManager
|
||||
contr *controller.Controller
|
||||
pool *util.WidgetPool
|
||||
cfg *backend.ArtistPageConfig
|
||||
pm *backend.PlaybackManager
|
||||
mp mediaprovider.MediaProvider
|
||||
im *backend.ImageManager
|
||||
contr *controller.Controller
|
||||
}
|
||||
|
||||
type ArtistPage struct {
|
||||
widget.BaseWidget
|
||||
|
||||
artistPageState
|
||||
disposed bool
|
||||
|
||||
artistInfo *mediaprovider.ArtistWithAlbums
|
||||
|
||||
@@ -50,34 +51,34 @@ type ArtistPage struct {
|
||||
container *fyne.Container
|
||||
}
|
||||
|
||||
func NewArtistPage(artistID string, cfg *backend.ArtistPageConfig, cache *util.WidgetCache, pm *backend.PlaybackManager, mp mediaprovider.MediaProvider, im *backend.ImageManager, contr *controller.Controller) *ArtistPage {
|
||||
func NewArtistPage(artistID string, cfg *backend.ArtistPageConfig, pool *util.WidgetPool, pm *backend.PlaybackManager, mp mediaprovider.MediaProvider, im *backend.ImageManager, contr *controller.Controller) *ArtistPage {
|
||||
activeView := 0
|
||||
if cfg.InitialView == "Top Tracks" {
|
||||
activeView = 1
|
||||
}
|
||||
return newArtistPage(artistID, cfg, cache, pm, mp, im, contr, activeView, widgets.TracklistSort{})
|
||||
return newArtistPage(artistID, cfg, pool, pm, mp, im, contr, activeView, widgets.TracklistSort{})
|
||||
}
|
||||
|
||||
func newArtistPage(artistID string, cfg *backend.ArtistPageConfig, cache *util.WidgetCache, pm *backend.PlaybackManager, mp mediaprovider.MediaProvider, im *backend.ImageManager, contr *controller.Controller, activeView int, sort widgets.TracklistSort) *ArtistPage {
|
||||
func newArtistPage(artistID string, cfg *backend.ArtistPageConfig, pool *util.WidgetPool, pm *backend.PlaybackManager, mp mediaprovider.MediaProvider, im *backend.ImageManager, contr *controller.Controller, activeView int, sort widgets.TracklistSort) *ArtistPage {
|
||||
a := &ArtistPage{artistPageState: artistPageState{
|
||||
artistID: artistID,
|
||||
cfg: cfg,
|
||||
widgetCache: cache,
|
||||
pm: pm,
|
||||
mp: mp,
|
||||
im: im,
|
||||
contr: contr,
|
||||
activeView: activeView,
|
||||
trackSort: sort,
|
||||
artistID: artistID,
|
||||
cfg: cfg,
|
||||
pool: pool,
|
||||
pm: pm,
|
||||
mp: mp,
|
||||
im: im,
|
||||
contr: contr,
|
||||
activeView: activeView,
|
||||
trackSort: sort,
|
||||
}}
|
||||
a.ExtendBaseWidget(a)
|
||||
if h := a.widgetCache.Obtain(util.WidgetTypeArtistPageHeader); h != nil {
|
||||
if h := a.pool.Obtain(util.WidgetTypeArtistPageHeader); h != nil {
|
||||
a.header = h.(*ArtistPageHeader)
|
||||
a.header.Clear()
|
||||
} else {
|
||||
a.header = NewArtistPageHeader(a)
|
||||
}
|
||||
a.header.artistPage = a
|
||||
a.header.Clear()
|
||||
if img, ok := im.GetCachedArtistImage(artistID); ok {
|
||||
a.header.artistImage.SetImage(img, true /*tappable*/)
|
||||
}
|
||||
@@ -120,11 +121,14 @@ func (a *ArtistPage) Reload() {
|
||||
}
|
||||
|
||||
func (a *ArtistPage) Save() SavedPage {
|
||||
a.disposed = true
|
||||
s := a.artistPageState
|
||||
if a.tracklistCtr != nil {
|
||||
s.trackSort = a.tracklistCtr.Objects[0].(*widgets.Tracklist).Sorting()
|
||||
tl := a.tracklistCtr.Objects[0].(*widgets.Tracklist)
|
||||
s.trackSort = tl.Sorting()
|
||||
a.pool.Release(util.WidgetTypeTracklist, tl)
|
||||
}
|
||||
a.widgetCache.Release(util.WidgetTypeArtistPageHeader, a.header)
|
||||
a.pool.Release(util.WidgetTypeArtistPageHeader, a.header)
|
||||
return &s
|
||||
}
|
||||
|
||||
@@ -150,6 +154,9 @@ func (a *ArtistPage) load() {
|
||||
log.Printf("Failed to get artist: %s", err.Error())
|
||||
return
|
||||
}
|
||||
if a.disposed {
|
||||
return
|
||||
}
|
||||
a.artistInfo = artist
|
||||
a.header.Update(artist)
|
||||
if a.activeView == 0 {
|
||||
@@ -198,7 +205,17 @@ func (a *ArtistPage) showTopTracks() {
|
||||
log.Printf("error getting top songs: %s", err.Error())
|
||||
return
|
||||
}
|
||||
tl := widgets.NewTracklist(ts)
|
||||
if a.disposed {
|
||||
return
|
||||
}
|
||||
var tl *widgets.Tracklist
|
||||
if t := a.pool.Obtain(util.WidgetTypeTracklist); t != nil {
|
||||
tl = t.(*widgets.Tracklist)
|
||||
tl.Reset()
|
||||
tl.SetTracks(ts)
|
||||
} else {
|
||||
tl = widgets.NewTracklist(ts)
|
||||
}
|
||||
tl.Options = widgets.TracklistOptions{AutoNumber: true}
|
||||
tl.SetVisibleColumns(a.cfg.TracklistColumns)
|
||||
tl.SetSorting(a.trackSort)
|
||||
@@ -237,7 +254,7 @@ func (a *ArtistPage) CreateRenderer() fyne.WidgetRenderer {
|
||||
}
|
||||
|
||||
func (s *artistPageState) Restore() Page {
|
||||
return newArtistPage(s.artistID, s.cfg, s.widgetCache, s.pm, s.mp, s.im, s.contr, s.activeView, s.trackSort)
|
||||
return newArtistPage(s.artistID, s.cfg, s.pool, s.pm, s.mp, s.im, s.contr, s.activeView, s.trackSort)
|
||||
}
|
||||
|
||||
const artistBioNotAvailableStr = "Artist biography not available."
|
||||
|
||||
@@ -23,11 +23,13 @@ type FavoritesPage struct {
|
||||
widget.BaseWidget
|
||||
|
||||
cfg *backend.FavoritesPageConfig
|
||||
pool *util.WidgetPool
|
||||
contr *controller.Controller
|
||||
pm *backend.PlaybackManager
|
||||
im *backend.ImageManager
|
||||
mp mediaprovider.MediaProvider
|
||||
|
||||
disposed bool
|
||||
trackSort widgets.TracklistSort
|
||||
filter mediaprovider.AlbumFilter
|
||||
searchText string
|
||||
@@ -45,10 +47,11 @@ type FavoritesPage struct {
|
||||
container *fyne.Container
|
||||
}
|
||||
|
||||
func NewFavoritesPage(cfg *backend.FavoritesPageConfig, contr *controller.Controller, mp mediaprovider.MediaProvider, pm *backend.PlaybackManager, im *backend.ImageManager) *FavoritesPage {
|
||||
func NewFavoritesPage(cfg *backend.FavoritesPageConfig, pool *util.WidgetPool, contr *controller.Controller, mp mediaprovider.MediaProvider, pm *backend.PlaybackManager, im *backend.ImageManager) *FavoritesPage {
|
||||
a := &FavoritesPage{
|
||||
filter: mediaprovider.AlbumFilter{ExcludeUnfavorited: true},
|
||||
cfg: cfg,
|
||||
pool: pool,
|
||||
contr: contr,
|
||||
pm: pm,
|
||||
mp: mp,
|
||||
@@ -99,6 +102,7 @@ func restoreFavoritesPage(saved *savedFavoritesPage) *FavoritesPage {
|
||||
a := &FavoritesPage{
|
||||
cfg: saved.cfg,
|
||||
contr: saved.contr,
|
||||
pool: saved.pool,
|
||||
pm: saved.pm,
|
||||
mp: saved.mp,
|
||||
im: saved.im,
|
||||
@@ -154,6 +158,9 @@ func (a *FavoritesPage) Reload() {
|
||||
log.Printf("error getting starred items: %s", err.Error())
|
||||
return
|
||||
}
|
||||
if a.disposed {
|
||||
return
|
||||
}
|
||||
if a.tracklistCtr != nil {
|
||||
// refresh favorite songs view
|
||||
tr := a.tracklistCtr.Objects[0].(*widgets.Tracklist)
|
||||
@@ -176,9 +183,11 @@ func (a *FavoritesPage) Reload() {
|
||||
}
|
||||
|
||||
func (a *FavoritesPage) Save() SavedPage {
|
||||
a.disposed = true
|
||||
sf := &savedFavoritesPage{
|
||||
cfg: a.cfg,
|
||||
contr: a.contr,
|
||||
pool: a.pool,
|
||||
pm: a.pm,
|
||||
mp: a.mp,
|
||||
im: a.im,
|
||||
@@ -191,7 +200,9 @@ func (a *FavoritesPage) Save() SavedPage {
|
||||
sf.searchGridState = a.searchGrid.SaveToState()
|
||||
}
|
||||
if a.tracklistCtr != nil {
|
||||
sf.trackSort = a.tracklistCtr.Objects[0].(*widgets.Tracklist).Sorting()
|
||||
tl := a.tracklistCtr.Objects[0].(*widgets.Tracklist)
|
||||
sf.trackSort = tl.Sorting()
|
||||
a.pool.Release(util.WidgetTypeTracklist, tl)
|
||||
}
|
||||
return sf
|
||||
}
|
||||
@@ -277,6 +288,9 @@ func (a *FavoritesPage) onShowFavoriteArtists() {
|
||||
log.Printf("error getting starred items: %s", err.Error())
|
||||
return
|
||||
}
|
||||
if a.disposed {
|
||||
return
|
||||
}
|
||||
model := buildArtistGridViewModel(fav.Artists)
|
||||
a.artistGrid = widgets.NewFixedGridView(model, a.im, myTheme.ArtistIcon)
|
||||
a.contr.ConnectArtistGridActions(a.artistGrid)
|
||||
@@ -325,7 +339,17 @@ func (a *FavoritesPage) onShowFavoriteSongs() {
|
||||
log.Printf("error getting starred items: %s", err.Error())
|
||||
return
|
||||
}
|
||||
tracklist := widgets.NewTracklist(fav.Tracks)
|
||||
if a.disposed {
|
||||
return
|
||||
}
|
||||
var tracklist *widgets.Tracklist
|
||||
if tl := a.pool.Obtain(util.WidgetTypeTracklist); tl != nil {
|
||||
tracklist = tl.(*widgets.Tracklist)
|
||||
tracklist.Reset()
|
||||
tracklist.SetTracks(fav.Tracks)
|
||||
} else {
|
||||
tracklist = widgets.NewTracklist(fav.Tracks)
|
||||
}
|
||||
tracklist.Options = widgets.TracklistOptions{AutoNumber: true}
|
||||
tracklist.SetVisibleColumns(a.cfg.TracklistColumns)
|
||||
tracklist.SetSorting(a.trackSort)
|
||||
@@ -355,6 +379,7 @@ func (a *FavoritesPage) CreateRenderer() fyne.WidgetRenderer {
|
||||
type savedFavoritesPage struct {
|
||||
cfg *backend.FavoritesPageConfig
|
||||
contr *controller.Controller
|
||||
pool *util.WidgetPool
|
||||
pm *backend.PlaybackManager
|
||||
mp mediaprovider.MediaProvider
|
||||
im *backend.ImageManager
|
||||
|
||||
@@ -38,6 +38,7 @@ type NowPlayingPage struct {
|
||||
|
||||
type nowPlayingPageState struct {
|
||||
contr *controller.Controller
|
||||
pool *util.WidgetPool
|
||||
conf *backend.NowPlayingPageConfig
|
||||
pm *backend.PlaybackManager
|
||||
p *player.Player
|
||||
@@ -46,18 +47,26 @@ type nowPlayingPageState struct {
|
||||
func NewNowPlayingPage(
|
||||
highlightedTrackID string,
|
||||
contr *controller.Controller,
|
||||
pool *util.WidgetPool,
|
||||
conf *backend.NowPlayingPageConfig,
|
||||
pm *backend.PlaybackManager,
|
||||
p *player.Player, // TODO: once other player backends are supported (eg uPnP), refactor
|
||||
) *NowPlayingPage {
|
||||
a := &NowPlayingPage{nowPlayingPageState: nowPlayingPageState{contr: contr, conf: conf, pm: pm, p: p}}
|
||||
a := &NowPlayingPage{nowPlayingPageState: nowPlayingPageState{
|
||||
contr: contr, pool: pool, conf: conf, pm: pm, p: p,
|
||||
}}
|
||||
a.ExtendBaseWidget(a)
|
||||
|
||||
p.OnPaused(a.formatStatusLine)
|
||||
p.OnPlaying(a.formatStatusLine)
|
||||
p.OnStopped(a.formatStatusLine)
|
||||
|
||||
a.tracklist = widgets.NewTracklist(nil)
|
||||
if t := a.pool.Obtain(util.WidgetTypeTracklist); t != nil {
|
||||
a.tracklist = t.(*widgets.Tracklist)
|
||||
a.tracklist.Reset()
|
||||
} else {
|
||||
a.tracklist = widgets.NewTracklist(nil)
|
||||
}
|
||||
a.tracklist.SetVisibleColumns(conf.TracklistColumns)
|
||||
a.tracklist.OnVisibleColumnsChanged = func(cols []string) {
|
||||
a.conf.TracklistColumns = cols
|
||||
@@ -90,6 +99,7 @@ func (a *NowPlayingPage) CreateRenderer() fyne.WidgetRenderer {
|
||||
}
|
||||
|
||||
func (a *NowPlayingPage) Save() SavedPage {
|
||||
a.pool.Release(util.WidgetTypeTracklist, a.tracklist)
|
||||
nps := a.nowPlayingPageState
|
||||
return &nps
|
||||
}
|
||||
@@ -207,5 +217,5 @@ func (a *NowPlayingPage) load(highlightedTrackID string) {
|
||||
}
|
||||
|
||||
func (s *nowPlayingPageState) Restore() Page {
|
||||
return NewNowPlayingPage("", s.contr, s.conf, s.pm, s.p)
|
||||
return NewNowPlayingPage("", s.contr, s.pool, s.conf, s.pm, s.p)
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ type PlaylistPage struct {
|
||||
|
||||
playlistPageState
|
||||
|
||||
disposed bool
|
||||
header *PlaylistPageHeader
|
||||
tracklist *widgets.Tracklist
|
||||
tracks []*mediaprovider.Track
|
||||
@@ -35,6 +36,7 @@ type playlistPageState struct {
|
||||
playlistID string
|
||||
conf *backend.PlaylistPageConfig
|
||||
contr *controller.Controller
|
||||
widgetPool *util.WidgetPool
|
||||
sm *backend.ServerManager
|
||||
pm *backend.PlaybackManager
|
||||
im *backend.ImageManager
|
||||
@@ -44,27 +46,40 @@ type playlistPageState struct {
|
||||
func NewPlaylistPage(
|
||||
playlistID string,
|
||||
conf *backend.PlaylistPageConfig,
|
||||
pool *util.WidgetPool,
|
||||
contr *controller.Controller,
|
||||
sm *backend.ServerManager,
|
||||
pm *backend.PlaybackManager,
|
||||
im *backend.ImageManager,
|
||||
) *PlaylistPage {
|
||||
return newPlaylistPage(playlistID, conf, contr, sm, pm, im, widgets.TracklistSort{})
|
||||
return newPlaylistPage(playlistID, conf, contr, pool, sm, pm, im, widgets.TracklistSort{})
|
||||
}
|
||||
|
||||
func newPlaylistPage(
|
||||
playlistID string,
|
||||
conf *backend.PlaylistPageConfig,
|
||||
contr *controller.Controller,
|
||||
pool *util.WidgetPool,
|
||||
sm *backend.ServerManager,
|
||||
pm *backend.PlaybackManager,
|
||||
im *backend.ImageManager,
|
||||
trackSort widgets.TracklistSort,
|
||||
) *PlaylistPage {
|
||||
a := &PlaylistPage{playlistPageState: playlistPageState{playlistID: playlistID, conf: conf, contr: contr, sm: sm, pm: pm, im: im}}
|
||||
a := &PlaylistPage{playlistPageState: playlistPageState{playlistID: playlistID, conf: conf, contr: contr, widgetPool: pool, sm: sm, pm: pm, im: im}}
|
||||
a.ExtendBaseWidget(a)
|
||||
a.header = NewPlaylistPageHeader(a)
|
||||
a.tracklist = widgets.NewTracklist(nil)
|
||||
if h := a.widgetPool.Obtain(util.WidgetTypePlaylistPageHeader); h != nil {
|
||||
a.header = h.(*PlaylistPageHeader)
|
||||
a.header.Clear()
|
||||
} else {
|
||||
a.header = NewPlaylistPageHeader(a)
|
||||
}
|
||||
a.header.page = a
|
||||
if tl := a.widgetPool.Obtain(util.WidgetTypeTracklist); tl != nil {
|
||||
a.tracklist = tl.(*widgets.Tracklist)
|
||||
a.tracklist.Reset()
|
||||
} else {
|
||||
a.tracklist = widgets.NewTracklist(nil)
|
||||
}
|
||||
a.tracklist.SetVisibleColumns(conf.TracklistColumns)
|
||||
a.tracklist.SetSorting(trackSort)
|
||||
a.tracklist.OnVisibleColumnsChanged = func(cols []string) {
|
||||
@@ -96,8 +111,11 @@ func (a *PlaylistPage) CreateRenderer() fyne.WidgetRenderer {
|
||||
}
|
||||
|
||||
func (a *PlaylistPage) Save() SavedPage {
|
||||
a.disposed = true
|
||||
p := a.playlistPageState
|
||||
p.trackSort = a.tracklist.Sorting()
|
||||
p.widgetPool.Release(util.WidgetTypePlaylistPageHeader, a.header)
|
||||
p.widgetPool.Release(util.WidgetTypeTracklist, a.tracklist)
|
||||
return &p
|
||||
}
|
||||
|
||||
@@ -134,6 +152,9 @@ func (a *PlaylistPage) load() {
|
||||
log.Printf("Failed to get playlist: %s", err.Error())
|
||||
return
|
||||
}
|
||||
if a.disposed {
|
||||
return
|
||||
}
|
||||
renumberTracks(playlist.Tracks)
|
||||
a.tracks = playlist.Tracks
|
||||
a.tracklist.SetTracks(playlist.Tracks)
|
||||
@@ -202,7 +223,7 @@ func (a *PlaylistPage) onRemoveSelectedFromPlaylist() {
|
||||
}
|
||||
a.sm.Server.EditPlaylistTracks(a.playlistID, nil, idxs)
|
||||
a.tracklist.UnselectAll()
|
||||
go a.Reload()
|
||||
a.Reload()
|
||||
}
|
||||
|
||||
type PlaylistPageHeader struct {
|
||||
@@ -282,6 +303,14 @@ func NewPlaylistPageHeader(page *PlaylistPage) *PlaylistPageHeader {
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *PlaylistPageHeader) Clear() {
|
||||
a.titleLabel.Segments[0].(*widget.TextSegment).Text = ""
|
||||
a.createdAtLabel.Text = ""
|
||||
a.descriptionLabel.Text = ""
|
||||
a.ownerLabel.Text = ""
|
||||
a.image.SetImage(nil, false)
|
||||
}
|
||||
|
||||
func (a *PlaylistPageHeader) CreateRenderer() fyne.WidgetRenderer {
|
||||
return widget.NewSimpleRenderer(a.container)
|
||||
}
|
||||
@@ -327,5 +356,5 @@ func (a *PlaylistPageHeader) formatPlaylistTrackTimeStr(p *mediaprovider.Playlis
|
||||
}
|
||||
|
||||
func (s *playlistPageState) Restore() Page {
|
||||
return newPlaylistPage(s.playlistID, s.conf, s.contr, s.sm, s.pm, s.im, s.trackSort)
|
||||
return newPlaylistPage(s.playlistID, s.conf, s.contr, s.widgetPool, s.sm, s.pm, s.im, s.trackSort)
|
||||
}
|
||||
|
||||
+14
-14
@@ -12,18 +12,18 @@ type NavigationHandler interface {
|
||||
}
|
||||
|
||||
type Router struct {
|
||||
App *backend.App
|
||||
Controller *controller.Controller
|
||||
Nav NavigationHandler
|
||||
widgetCache util.WidgetCache
|
||||
App *backend.App
|
||||
Controller *controller.Controller
|
||||
Nav NavigationHandler
|
||||
widgetPool util.WidgetPool
|
||||
}
|
||||
|
||||
func NewRouter(app *backend.App, controller *controller.Controller, nav NavigationHandler) Router {
|
||||
r := Router{
|
||||
App: app,
|
||||
Controller: controller,
|
||||
Nav: nav,
|
||||
widgetCache: util.NewWidgetCache(),
|
||||
App: app,
|
||||
Controller: controller,
|
||||
Nav: nav,
|
||||
widgetPool: util.NewWidgetPool(),
|
||||
}
|
||||
return r
|
||||
}
|
||||
@@ -31,27 +31,27 @@ func NewRouter(app *backend.App, controller *controller.Controller, nav Navigati
|
||||
func (r Router) CreatePage(rte controller.Route) Page {
|
||||
switch rte.Page {
|
||||
case controller.Album:
|
||||
return NewAlbumPage(rte.Arg, &r.App.Config.AlbumPage, &r.widgetCache, r.App.PlaybackManager, r.App.ServerManager.Server, r.App.ImageManager, r.Controller)
|
||||
return NewAlbumPage(rte.Arg, &r.App.Config.AlbumPage, &r.widgetPool, r.App.PlaybackManager, r.App.ServerManager.Server, r.App.ImageManager, r.Controller)
|
||||
case controller.Albums:
|
||||
return NewAlbumsPage(&r.App.Config.AlbumsPage, r.Controller, r.App.PlaybackManager, r.App.ServerManager.Server, r.App.ImageManager)
|
||||
case controller.Artist:
|
||||
return NewArtistPage(rte.Arg, &r.App.Config.ArtistPage, &r.widgetCache, r.App.PlaybackManager, r.App.ServerManager.Server, r.App.ImageManager, r.Controller)
|
||||
return NewArtistPage(rte.Arg, &r.App.Config.ArtistPage, &r.widgetPool, r.App.PlaybackManager, r.App.ServerManager.Server, r.App.ImageManager, r.Controller)
|
||||
case controller.Artists:
|
||||
return NewArtistsPage(r.Controller, r.App.PlaybackManager, r.App.ServerManager.Server, r.App.ImageManager)
|
||||
case controller.Favorites:
|
||||
return NewFavoritesPage(&r.App.Config.FavoritesPage, r.Controller, r.App.ServerManager.Server, r.App.PlaybackManager, r.App.ImageManager)
|
||||
return NewFavoritesPage(&r.App.Config.FavoritesPage, &r.widgetPool, r.Controller, r.App.ServerManager.Server, r.App.PlaybackManager, r.App.ImageManager)
|
||||
case controller.Genre:
|
||||
return NewGenrePage(rte.Arg, r.Controller, r.App.PlaybackManager, r.App.ServerManager.Server, r.App.ImageManager)
|
||||
case controller.Genres:
|
||||
return NewGenresPage(r.Controller, r.App.ServerManager.Server)
|
||||
case controller.NowPlaying:
|
||||
return NewNowPlayingPage(rte.Arg, r.Controller, &r.App.Config.NowPlayingPage, r.App.PlaybackManager, r.App.Player)
|
||||
return NewNowPlayingPage(rte.Arg, r.Controller, &r.widgetPool, &r.App.Config.NowPlayingPage, r.App.PlaybackManager, r.App.Player)
|
||||
case controller.Playlist:
|
||||
return NewPlaylistPage(rte.Arg, &r.App.Config.PlaylistPage, r.Controller, r.App.ServerManager, r.App.PlaybackManager, r.App.ImageManager)
|
||||
return NewPlaylistPage(rte.Arg, &r.App.Config.PlaylistPage, &r.widgetPool, r.Controller, r.App.ServerManager, r.App.PlaybackManager, r.App.ImageManager)
|
||||
case controller.Playlists:
|
||||
return NewPlaylistsPage(r.Controller, &r.App.Config.PlaylistsPage, r.App.ServerManager.Server)
|
||||
case controller.Tracks:
|
||||
return NewTracksPage(r.Controller, &r.App.Config.TracksPage, r.App.ServerManager.Server)
|
||||
return NewTracksPage(r.Controller, &r.App.Config.TracksPage, &r.widgetPool, r.App.ServerManager.Server)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/dweymouth/supersonic/ui/controller"
|
||||
"github.com/dweymouth/supersonic/ui/layouts"
|
||||
"github.com/dweymouth/supersonic/ui/theme"
|
||||
"github.com/dweymouth/supersonic/ui/util"
|
||||
"github.com/dweymouth/supersonic/ui/widgets"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
@@ -34,16 +35,22 @@ type TracksPage struct {
|
||||
|
||||
type tracksPageState struct {
|
||||
searchText string
|
||||
widgetPool *util.WidgetPool
|
||||
contr *controller.Controller
|
||||
conf *backend.TracksPageConfig
|
||||
mp mediaprovider.MediaProvider
|
||||
}
|
||||
|
||||
func NewTracksPage(contr *controller.Controller, conf *backend.TracksPageConfig, mp mediaprovider.MediaProvider) *TracksPage {
|
||||
t := &TracksPage{tracksPageState: tracksPageState{contr: contr, conf: conf, mp: mp}}
|
||||
func NewTracksPage(contr *controller.Controller, conf *backend.TracksPageConfig, pool *util.WidgetPool, mp mediaprovider.MediaProvider) *TracksPage {
|
||||
t := &TracksPage{tracksPageState: tracksPageState{contr: contr, conf: conf, widgetPool: pool, mp: mp}}
|
||||
t.ExtendBaseWidget(t)
|
||||
|
||||
t.tracklist = widgets.NewTracklist(nil)
|
||||
if tl := t.widgetPool.Obtain(util.WidgetTypeTracklist); tl != nil {
|
||||
t.tracklist = tl.(*widgets.Tracklist)
|
||||
t.tracklist.Reset()
|
||||
} else {
|
||||
t.tracklist = widgets.NewTracklist(nil)
|
||||
}
|
||||
t.tracklist.Options = widgets.TracklistOptions{
|
||||
DisableSorting: true,
|
||||
AutoNumber: true,
|
||||
@@ -143,12 +150,14 @@ func (t *TracksPage) CreateRenderer() fyne.WidgetRenderer {
|
||||
}
|
||||
|
||||
func (t *TracksPage) Save() SavedPage {
|
||||
t.loader.Dispose()
|
||||
t.widgetPool.Release(util.WidgetTypeTracklist, t.tracklist)
|
||||
state := t.tracksPageState
|
||||
return &state
|
||||
}
|
||||
|
||||
func (s *tracksPageState) Restore() Page {
|
||||
t := NewTracksPage(s.contr, s.conf, s.mp)
|
||||
t := NewTracksPage(s.contr, s.conf, s.widgetPool, s.mp)
|
||||
t.searchText = s.searchText
|
||||
if t.searchText != "" {
|
||||
t.searcher.Entry.Text = t.searchText
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
type WidgetType string
|
||||
|
||||
const (
|
||||
WidgetTypeAlbumPageHeader WidgetType = "AlbumPageHeader"
|
||||
WidgetTypeArtistPageHeader WidgetType = "ArtistPageHeader"
|
||||
)
|
||||
|
||||
type WidgetCache struct {
|
||||
cache map[WidgetType][]cachedWidget
|
||||
}
|
||||
|
||||
type cachedWidget struct {
|
||||
widget fyne.CanvasObject
|
||||
releasedAt int64 // unixMillis
|
||||
}
|
||||
|
||||
func NewWidgetCache() WidgetCache {
|
||||
return WidgetCache{
|
||||
cache: make(map[WidgetType][]cachedWidget),
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WidgetCache) Obtain(typ WidgetType) fyne.CanvasObject {
|
||||
var widget fyne.CanvasObject
|
||||
if ws, ok := w.cache[typ]; ok && len(ws) > 0 {
|
||||
i := len(ws) - 1
|
||||
widget = ws[i].widget
|
||||
ws[i].widget = nil
|
||||
w.cache[typ] = ws[:i]
|
||||
}
|
||||
return widget
|
||||
}
|
||||
|
||||
func (w *WidgetCache) Release(typ WidgetType, wid fyne.CanvasObject) {
|
||||
if _, ok := w.cache[typ]; !ok {
|
||||
w.cache[typ] = make([]cachedWidget, 0)
|
||||
}
|
||||
w.cache[typ] = append(w.cache[typ], cachedWidget{
|
||||
widget: wid,
|
||||
releasedAt: time.Now().UnixMilli(),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
type WidgetType int
|
||||
|
||||
const (
|
||||
WidgetTypeAlbumPageHeader WidgetType = iota
|
||||
WidgetTypeArtistPageHeader
|
||||
WidgetTypePlaylistPageHeader
|
||||
WidgetTypeTracklist
|
||||
)
|
||||
|
||||
// A pool to share commonly-used widgets across pages to reduce
|
||||
// creation of new widgets and memory allocations.
|
||||
// It is not thread-safe, which is fine for its current use.
|
||||
type WidgetPool struct {
|
||||
cache map[WidgetType][]cachedWidget
|
||||
}
|
||||
|
||||
type cachedWidget struct {
|
||||
widget fyne.CanvasObject
|
||||
releasedAt int64 // unixMillis
|
||||
}
|
||||
|
||||
func NewWidgetPool() WidgetPool {
|
||||
return WidgetPool{
|
||||
cache: make(map[WidgetType][]cachedWidget),
|
||||
}
|
||||
}
|
||||
|
||||
// Obtain obtains a widget of the given type from the pool, if one exists.
|
||||
// Returns nil if there is no available widget.
|
||||
func (w *WidgetPool) Obtain(typ WidgetType) fyne.CanvasObject {
|
||||
var widget fyne.CanvasObject
|
||||
if ws, ok := w.cache[typ]; ok && len(ws) > 0 {
|
||||
i := len(ws) - 1
|
||||
widget = ws[i].widget
|
||||
ws[i].widget = nil
|
||||
w.cache[typ] = ws[:i]
|
||||
}
|
||||
return widget
|
||||
}
|
||||
|
||||
// Release releases a widget into the pool.
|
||||
// The widget must not be modified by the releaser after release,
|
||||
// since it may be Obtained for a new use at any time.
|
||||
func (w *WidgetPool) Release(typ WidgetType, wid fyne.CanvasObject) {
|
||||
if _, ok := w.cache[typ]; !ok {
|
||||
w.cache[typ] = make([]cachedWidget, 0)
|
||||
}
|
||||
w.cache[typ] = append(w.cache[typ], cachedWidget{
|
||||
widget: wid,
|
||||
releasedAt: time.Now().UnixMilli(),
|
||||
})
|
||||
}
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
// Component that manages lazily loading more tracks into a Tracklist
|
||||
// as the user scrolls near the bottom.
|
||||
type TracklistLoader struct {
|
||||
disposed bool
|
||||
|
||||
tracklist *Tracklist
|
||||
iter mediaprovider.TrackIterator
|
||||
|
||||
@@ -28,11 +30,17 @@ func NewTracklistLoader(tracklist *Tracklist, iter mediaprovider.TrackIterator)
|
||||
return t
|
||||
}
|
||||
|
||||
// Cancels all asynchronous loads so that they will no longer modify the tracklist.
|
||||
func (t *TracklistLoader) Dispose() {
|
||||
t.disposed = true
|
||||
t.tracklist.OnTrackShown = nil
|
||||
}
|
||||
|
||||
func (t *TracklistLoader) onTrackShown(tracknum int) {
|
||||
if tracknum > t.highestShown {
|
||||
t.highestShown = tracknum
|
||||
}
|
||||
if t.highestShown >= t.len-25 && !t.fetching && !t.done {
|
||||
if t.highestShown >= t.len-25 && !t.fetching && !t.done && !t.disposed {
|
||||
t.fetching = true
|
||||
go t.loadMoreTracks(25)
|
||||
}
|
||||
@@ -52,6 +60,12 @@ func (t *TracklistLoader) loadMoreTracks(num int) {
|
||||
break
|
||||
}
|
||||
t.trackBuffer = append(t.trackBuffer, tr)
|
||||
if t.disposed {
|
||||
break
|
||||
}
|
||||
}
|
||||
if t.disposed {
|
||||
return
|
||||
}
|
||||
t.tracklist.AppendTracks(t.trackBuffer)
|
||||
t.tracklist.Refresh()
|
||||
|
||||
Reference in New Issue
Block a user