clicking album thumbnail on album page shows full size cover popup
This commit is contained in:
@@ -18,6 +18,9 @@ type ImageManager struct {
|
||||
s *ServerManager
|
||||
baseCacheDir string
|
||||
thumbnailCache gcache.Cache
|
||||
|
||||
cachedFullSizeCover image.Image
|
||||
cachedFullSizeCoverID string
|
||||
}
|
||||
|
||||
func NewImageManager(s *ServerManager, baseCacheDir string) *ImageManager {
|
||||
@@ -66,6 +69,19 @@ func (i *ImageManager) GetAlbumThumbnail(albumID string) (image.Image, error) {
|
||||
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 {
|
||||
path := path.Join(i.baseCacheDir, i.s.ServerID.String(), "covers")
|
||||
configdir.MakePath(path)
|
||||
|
||||
+57
-26
@@ -19,21 +19,34 @@ import (
|
||||
type AlbumPage struct {
|
||||
widget.BaseWidget
|
||||
|
||||
albumID string
|
||||
sm *backend.ServerManager
|
||||
im *backend.ImageManager
|
||||
lm *backend.LibraryManager
|
||||
nav func(Route)
|
||||
header *AlbumPageHeader
|
||||
tracklist *widgets.Tracklist
|
||||
nowPlayingID string
|
||||
container *fyne.Container
|
||||
albumID string
|
||||
sm *backend.ServerManager
|
||||
im *backend.ImageManager
|
||||
lm *backend.LibraryManager
|
||||
nav func(Route)
|
||||
header *AlbumPageHeader
|
||||
tracklist *widgets.Tracklist
|
||||
nowPlayingID string
|
||||
container *fyne.Container
|
||||
popUpProvider PopUpProvider
|
||||
|
||||
OnPlayAlbum func(string, int)
|
||||
}
|
||||
|
||||
func NewAlbumPage(albumID string, sm *backend.ServerManager, lm *backend.LibraryManager, im *backend.ImageManager, nav func(Route)) *AlbumPage {
|
||||
a := &AlbumPage{albumID: albumID, sm: sm, lm: lm, im: im, nav: nav}
|
||||
type PopUpProvider interface {
|
||||
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.header = NewAlbumPageHeader(a)
|
||||
a.tracklist = widgets.NewTracklist(nil)
|
||||
@@ -55,10 +68,11 @@ func (a *AlbumPage) SetPlayAlbumCallback(cb func(string, int)) {
|
||||
|
||||
func (a *AlbumPage) Save() SavedPage {
|
||||
return &savedAlbumPage{
|
||||
albumID: a.albumID,
|
||||
lm: a.lm,
|
||||
im: a.im,
|
||||
nav: a.nav,
|
||||
albumID: a.albumID,
|
||||
lm: a.lm,
|
||||
im: a.im,
|
||||
nav: a.nav,
|
||||
popUpProvider: a.popUpProvider,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +121,7 @@ type AlbumPageHeader struct {
|
||||
|
||||
page *AlbumPage
|
||||
|
||||
cover *canvas.Image
|
||||
cover *widgets.TappableImage
|
||||
titleLabel *widget.RichText
|
||||
artistLabel *widgets.CustomHyperlink
|
||||
genreLabel *widgets.CustomHyperlink
|
||||
@@ -122,12 +136,14 @@ type AlbumPageHeader struct {
|
||||
func NewAlbumPageHeader(page *AlbumPage) *AlbumPageHeader {
|
||||
a := &AlbumPageHeader{page: page}
|
||||
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))
|
||||
// due to cache warming we can probably immediately set the cover
|
||||
// and not have to set it asynchronously in the Update function
|
||||
if im, ok := page.im.GetAlbumThumbnailFromCache(page.albumID); ok {
|
||||
a.cover.Image = im
|
||||
a.cover.Image.Image = im
|
||||
}
|
||||
a.titleLabel = widget.NewRichTextWithText("")
|
||||
a.titleLabel.Wrapping = fyne.TextTruncate
|
||||
@@ -180,12 +196,12 @@ func (a *AlbumPageHeader) Update(album *subsonic.AlbumID3, im *backend.ImageMana
|
||||
a.Refresh()
|
||||
|
||||
// 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
|
||||
}
|
||||
go func() {
|
||||
if cover, err := im.GetAlbumThumbnail(album.ID); err == nil {
|
||||
a.cover.Image = cover
|
||||
a.cover.Image.Image = cover
|
||||
a.cover.Refresh()
|
||||
} else {
|
||||
log.Printf("error fetching cover: %v", err)
|
||||
@@ -201,18 +217,33 @@ 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 {
|
||||
return fmt.Sprintf("%d · %d tracks · %s", a.Year, a.SongCount, util.SecondsToTimeString(float64(a.Duration)))
|
||||
}
|
||||
|
||||
type savedAlbumPage struct {
|
||||
albumID string
|
||||
lm *backend.LibraryManager
|
||||
im *backend.ImageManager
|
||||
sm *backend.ServerManager
|
||||
nav func(Route)
|
||||
albumID string
|
||||
lm *backend.LibraryManager
|
||||
im *backend.ImageManager
|
||||
sm *backend.ServerManager
|
||||
popUpProvider PopUpProvider
|
||||
nav func(Route)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
+27
-6
@@ -2,6 +2,9 @@ package browsing
|
||||
|
||||
import (
|
||||
"supersonic/backend"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
type PageName int
|
||||
@@ -57,21 +60,39 @@ type NavigationHandler interface {
|
||||
}
|
||||
|
||||
type Router struct {
|
||||
App *backend.App
|
||||
Nav NavigationHandler
|
||||
App *backend.App
|
||||
MainWindow fyne.Window
|
||||
Nav NavigationHandler
|
||||
}
|
||||
|
||||
func NewRouter(app *backend.App, nav NavigationHandler) Router {
|
||||
func NewRouter(app *backend.App, mainWindow fyne.Window, nav NavigationHandler) Router {
|
||||
return Router{
|
||||
App: app,
|
||||
Nav: nav,
|
||||
App: app,
|
||||
MainWindow: mainWindow,
|
||||
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 {
|
||||
switch rte.Page {
|
||||
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:
|
||||
return NewAlbumsPage("Albums", rte.Arg, r.App.LibraryManager, r.App.ImageManager, r.OpenRoute)
|
||||
case Artist:
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ func NewMainWindow(fyneApp fyne.App, appName string, app *backend.App) MainWindo
|
||||
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.SetPlaybackManager(app.PlaybackManager)
|
||||
m.BottomPanel.ImageManager = app.ImageManager
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user