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
|
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)
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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