clicking album thumbnail on album page shows full size cover popup

This commit is contained in:
Drew Weymouth
2023-01-22 13:21:25 -08:00
parent c1aaed4b3e
commit c9bd3b5079
5 changed files with 146 additions and 33 deletions
+16
View File
@@ -18,6 +18,9 @@ type ImageManager struct {
s *ServerManager s *ServerManager
baseCacheDir string baseCacheDir string
thumbnailCache gcache.Cache thumbnailCache gcache.Cache
cachedFullSizeCover image.Image
cachedFullSizeCoverID string
} }
func NewImageManager(s *ServerManager, baseCacheDir string) *ImageManager { func NewImageManager(s *ServerManager, baseCacheDir string) *ImageManager {
@@ -66,6 +69,19 @@ func (i *ImageManager) GetAlbumThumbnail(albumID string) (image.Image, error) {
return i.fetchAndCacheCoverFromServer(albumID) return i.fetchAndCacheCoverFromServer(albumID)
} }
func (i *ImageManager) GetFullSizeAlbumCover(albumID string) (image.Image, error) {
if i.cachedFullSizeCoverID == albumID {
return i.cachedFullSizeCover, nil
}
im, err := i.s.Server.GetCoverArt(albumID, nil)
if err != nil {
return nil, err
}
i.cachedFullSizeCover = im
i.cachedFullSizeCoverID = albumID
return im, nil
}
func (i *ImageManager) ensureCoverCacheDir() string { func (i *ImageManager) ensureCoverCacheDir() string {
path := path.Join(i.baseCacheDir, i.s.ServerID.String(), "covers") path := path.Join(i.baseCacheDir, i.s.ServerID.String(), "covers")
configdir.MakePath(path) configdir.MakePath(path)
+39 -8
View File
@@ -28,12 +28,25 @@ type AlbumPage struct {
tracklist *widgets.Tracklist tracklist *widgets.Tracklist
nowPlayingID string nowPlayingID string
container *fyne.Container container *fyne.Container
popUpProvider PopUpProvider
OnPlayAlbum func(string, int) OnPlayAlbum func(string, int)
} }
func NewAlbumPage(albumID string, sm *backend.ServerManager, lm *backend.LibraryManager, im *backend.ImageManager, nav func(Route)) *AlbumPage { type PopUpProvider interface {
a := &AlbumPage{albumID: albumID, sm: sm, lm: lm, im: im, nav: nav} CreatePopUp(fyne.CanvasObject) *widget.PopUp
WindowSize() fyne.Size
}
func NewAlbumPage(
albumID string,
sm *backend.ServerManager,
lm *backend.LibraryManager,
im *backend.ImageManager,
popUpProvider PopUpProvider,
nav func(Route),
) *AlbumPage {
a := &AlbumPage{albumID: albumID, sm: sm, lm: lm, im: im, nav: nav, popUpProvider: popUpProvider}
a.ExtendBaseWidget(a) a.ExtendBaseWidget(a)
a.header = NewAlbumPageHeader(a) a.header = NewAlbumPageHeader(a)
a.tracklist = widgets.NewTracklist(nil) a.tracklist = widgets.NewTracklist(nil)
@@ -59,6 +72,7 @@ func (a *AlbumPage) Save() SavedPage {
lm: a.lm, lm: a.lm,
im: a.im, im: a.im,
nav: a.nav, nav: a.nav,
popUpProvider: a.popUpProvider,
} }
} }
@@ -107,7 +121,7 @@ type AlbumPageHeader struct {
page *AlbumPage page *AlbumPage
cover *canvas.Image cover *widgets.TappableImage
titleLabel *widget.RichText titleLabel *widget.RichText
artistLabel *widgets.CustomHyperlink artistLabel *widgets.CustomHyperlink
genreLabel *widgets.CustomHyperlink genreLabel *widgets.CustomHyperlink
@@ -122,12 +136,14 @@ type AlbumPageHeader struct {
func NewAlbumPageHeader(page *AlbumPage) *AlbumPageHeader { func NewAlbumPageHeader(page *AlbumPage) *AlbumPageHeader {
a := &AlbumPageHeader{page: page} a := &AlbumPageHeader{page: page}
a.ExtendBaseWidget(a) a.ExtendBaseWidget(a)
a.cover = &canvas.Image{FillMode: canvas.ImageFillContain} a.cover = widgets.NewTappableImage()
a.cover.FillMode = canvas.ImageFillContain
a.cover.OnTapped = a.showPopUpCover
a.cover.SetMinSize(fyne.NewSize(225, 225)) a.cover.SetMinSize(fyne.NewSize(225, 225))
// due to cache warming we can probably immediately set the cover // due to cache warming we can probably immediately set the cover
// and not have to set it asynchronously in the Update function // and not have to set it asynchronously in the Update function
if im, ok := page.im.GetAlbumThumbnailFromCache(page.albumID); ok { if im, ok := page.im.GetAlbumThumbnailFromCache(page.albumID); ok {
a.cover.Image = im a.cover.Image.Image = im
} }
a.titleLabel = widget.NewRichTextWithText("") a.titleLabel = widget.NewRichTextWithText("")
a.titleLabel.Wrapping = fyne.TextTruncate a.titleLabel.Wrapping = fyne.TextTruncate
@@ -180,12 +196,12 @@ func (a *AlbumPageHeader) Update(album *subsonic.AlbumID3, im *backend.ImageMana
a.Refresh() a.Refresh()
// cover image was already loaded from cache in consructor // cover image was already loaded from cache in consructor
if a.albumID == album.ID && a.cover.Image != nil { if a.albumID == album.ID && a.cover.Image.Image != nil {
return return
} }
go func() { go func() {
if cover, err := im.GetAlbumThumbnail(album.ID); err == nil { if cover, err := im.GetAlbumThumbnail(album.ID); err == nil {
a.cover.Image = cover a.cover.Image.Image = cover
a.cover.Refresh() a.cover.Refresh()
} else { } else {
log.Printf("error fetching cover: %v", err) log.Printf("error fetching cover: %v", err)
@@ -201,6 +217,20 @@ func (a *AlbumPageHeader) toggleFavorited() {
} }
} }
func (a *AlbumPageHeader) showPopUpCover() {
cover, err := a.page.im.GetFullSizeAlbumCover(a.albumID)
if err != nil {
log.Printf("error getting full size album cover: %s", err.Error())
return
}
im := canvas.NewImageFromImage(cover)
im.FillMode = canvas.ImageFillContain
pop := a.page.popUpProvider.CreatePopUp(im)
s := a.page.popUpProvider.WindowSize()
pop.Resize(fyne.NewSize(s.Width*0.8, s.Height*0.8))
pop.ShowAtPosition(fyne.NewPos(0.1*s.Width, 0.1*s.Height))
}
func formatMiscLabelStr(a *subsonic.AlbumID3) string { func formatMiscLabelStr(a *subsonic.AlbumID3) string {
return fmt.Sprintf("%d · %d tracks · %s", a.Year, a.SongCount, util.SecondsToTimeString(float64(a.Duration))) return fmt.Sprintf("%d · %d tracks · %s", a.Year, a.SongCount, util.SecondsToTimeString(float64(a.Duration)))
} }
@@ -210,9 +240,10 @@ type savedAlbumPage struct {
lm *backend.LibraryManager lm *backend.LibraryManager
im *backend.ImageManager im *backend.ImageManager
sm *backend.ServerManager sm *backend.ServerManager
popUpProvider PopUpProvider
nav func(Route) nav func(Route)
} }
func (s *savedAlbumPage) Restore() Page { func (s *savedAlbumPage) Restore() Page {
return NewAlbumPage(s.albumID, s.sm, s.lm, s.im, s.nav) return NewAlbumPage(s.albumID, s.sm, s.lm, s.im, s.popUpProvider, s.nav)
} }
+23 -2
View File
@@ -2,6 +2,9 @@ package browsing
import ( import (
"supersonic/backend" "supersonic/backend"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/widget"
) )
type PageName int type PageName int
@@ -58,20 +61,38 @@ type NavigationHandler interface {
type Router struct { type Router struct {
App *backend.App App *backend.App
MainWindow fyne.Window
Nav NavigationHandler Nav NavigationHandler
} }
func NewRouter(app *backend.App, nav NavigationHandler) Router { func NewRouter(app *backend.App, mainWindow fyne.Window, nav NavigationHandler) Router {
return Router{ return Router{
App: app, App: app,
MainWindow: mainWindow,
Nav: nav, Nav: nav,
} }
} }
type popUpProvider struct {
window fyne.Window
}
func (p *popUpProvider) CreatePopUp(obj fyne.CanvasObject) *widget.PopUp {
return widget.NewPopUp(obj, p.window.Canvas())
}
func (p *popUpProvider) WindowSize() fyne.Size {
return p.window.Canvas().Size()
}
func (r Router) pop() *popUpProvider {
return &popUpProvider{window: r.MainWindow}
}
func (r Router) CreatePage(rte Route) Page { func (r Router) CreatePage(rte Route) Page {
switch rte.Page { switch rte.Page {
case Album: case Album:
return NewAlbumPage(rte.Arg, r.App.ServerManager, r.App.LibraryManager, r.App.ImageManager, r.OpenRoute) return NewAlbumPage(rte.Arg, r.App.ServerManager, r.App.LibraryManager, r.App.ImageManager, r.pop(), r.OpenRoute)
case Albums: case Albums:
return NewAlbumsPage("Albums", rte.Arg, r.App.LibraryManager, r.App.ImageManager, r.OpenRoute) return NewAlbumsPage("Albums", rte.Arg, r.App.LibraryManager, r.App.ImageManager, r.OpenRoute)
case Artist: case Artist:
+1 -1
View File
@@ -42,7 +42,7 @@ func NewMainWindow(fyneApp fyne.App, appName string, app *backend.App) MainWindo
BrowsingPane: browsing.NewBrowsingPane(app), BrowsingPane: browsing.NewBrowsingPane(app),
} }
m.Router = browsing.NewRouter(app, m.BrowsingPane) m.Router = browsing.NewRouter(app, m.Window, m.BrowsingPane)
m.BottomPanel = NewBottomPanel(app.Player, m.Router.OpenRoute) m.BottomPanel = NewBottomPanel(app.Player, m.Router.OpenRoute)
m.BottomPanel.SetPlaybackManager(app.PlaybackManager) m.BottomPanel.SetPlaybackManager(app.PlaybackManager)
m.BottomPanel.ImageManager = app.ImageManager m.BottomPanel.ImageManager = app.ImageManager
+45
View File
@@ -0,0 +1,45 @@
package widgets
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/driver/desktop"
"fyne.io/fyne/v2/widget"
)
type TappableImage struct {
widget.BaseWidget
canvas.Image
OnTapped func()
}
func NewTappableImage() *TappableImage {
t := &TappableImage{}
t.ExtendBaseWidget(t)
return t
}
func (t *TappableImage) Cursor() desktop.Cursor {
return desktop.PointerCursor
}
func (t *TappableImage) Tapped(e *fyne.PointEvent) {
if t.OnTapped != nil {
t.OnTapped()
}
}
func (t *TappableImage) Refresh() {
t.Image.Refresh()
t.BaseWidget.Refresh()
}
func (t *TappableImage) Resize(size fyne.Size) {
t.Image.Resize(size)
t.BaseWidget.Resize(size)
}
func (t *TappableImage) CreateRenderer() fyne.WidgetRenderer {
return widget.NewSimpleRenderer(&t.Image)
}