make artist image displayable in pop-up

This commit is contained in:
Drew Weymouth
2023-02-07 09:31:49 -08:00
parent 87ae3e940f
commit 9fd6508af0
7 changed files with 113 additions and 75 deletions
+5 -28
View File
@@ -28,22 +28,17 @@ type AlbumPage struct {
tracklist *widgets.Tracklist tracklist *widgets.Tracklist
nowPlayingID string nowPlayingID string
container *fyne.Container container *fyne.Container
popUpProvider PopUpProvider popUpProvider util.PopUpProvider
OnPlayAlbum func(string, int) OnPlayAlbum func(string, int)
} }
type PopUpProvider interface {
CreatePopUp(fyne.CanvasObject) *widget.PopUp
WindowSize() fyne.Size
}
func NewAlbumPage( func NewAlbumPage(
albumID string, albumID string,
sm *backend.ServerManager, sm *backend.ServerManager,
lm *backend.LibraryManager, lm *backend.LibraryManager,
im *backend.ImageManager, im *backend.ImageManager,
popUpProvider PopUpProvider, popUpProvider util.PopUpProvider,
nav func(Route), nav func(Route),
) *AlbumPage { ) *AlbumPage {
a := &AlbumPage{albumID: albumID, sm: sm, lm: lm, im: im, nav: nav, popUpProvider: popUpProvider} a := &AlbumPage{albumID: albumID, sm: sm, lm: lm, im: im, nav: nav, popUpProvider: popUpProvider}
@@ -136,9 +131,8 @@ 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 = widgets.NewTappableImage() a.cover = widgets.NewTappableImage(a.showPopUpCover)
a.cover.FillMode = canvas.ImageFillContain 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
@@ -223,24 +217,7 @@ func (a *AlbumPageHeader) showPopUpCover() {
log.Printf("error getting full size album cover: %s", err.Error()) log.Printf("error getting full size album cover: %s", err.Error())
return return
} }
im := canvas.NewImageFromImage(cover) util.ShowPopUpImage(cover, a.page.popUpProvider)
im.FillMode = canvas.ImageFillContain
pop := a.page.popUpProvider.CreatePopUp(im)
s := a.page.popUpProvider.WindowSize()
var popS fyne.Size
if asp := util.ImageAspect(cover); s.Width/s.Height > asp {
// window height is limiting factor
h := s.Height * 0.8
popS = fyne.NewSize(h*asp, h)
} else {
w := s.Width * 0.8
popS = fyne.NewSize(w, w*(1/asp))
}
pop.Resize(popS)
pop.ShowAtPosition(fyne.NewPos(
(s.Width-popS.Width)/2,
(s.Height-popS.Height)/2,
))
} }
func formatMiscLabelStr(a *subsonic.AlbumID3) string { func formatMiscLabelStr(a *subsonic.AlbumID3) string {
@@ -252,7 +229,7 @@ type savedAlbumPage struct {
lm *backend.LibraryManager lm *backend.LibraryManager
im *backend.ImageManager im *backend.ImageManager
sm *backend.ServerManager sm *backend.ServerManager
popUpProvider PopUpProvider popUpProvider util.PopUpProvider
nav func(Route) nav func(Route)
} }
+31 -21
View File
@@ -24,10 +24,12 @@ var _ fyne.Widget = (*ArtistPage)(nil)
type ArtistPage struct { type ArtistPage struct {
widget.BaseWidget widget.BaseWidget
artistID string artistID string
im *backend.ImageManager im *backend.ImageManager
sm *backend.ServerManager sm *backend.ServerManager
nav func(Route) nav func(Route)
popUpProvider util.PopUpProvider
grid *widgets.AlbumGrid grid *widgets.AlbumGrid
header *ArtistPageHeader header *ArtistPageHeader
container *fyne.Container container *fyne.Container
@@ -35,15 +37,16 @@ type ArtistPage struct {
OnPlayAlbum func(string, int) OnPlayAlbum func(string, int)
} }
func NewArtistPage(artistID string, sm *backend.ServerManager, im *backend.ImageManager, nav func(Route)) *ArtistPage { func NewArtistPage(artistID string, sm *backend.ServerManager, im *backend.ImageManager, popUp util.PopUpProvider, nav func(Route)) *ArtistPage {
a := &ArtistPage{ a := &ArtistPage{
artistID: artistID, artistID: artistID,
sm: sm, sm: sm,
im: im, im: im,
nav: nav, nav: nav,
popUpProvider: popUp,
} }
a.ExtendBaseWidget(a) a.ExtendBaseWidget(a)
a.header = NewArtistPageHeader(nav) a.header = NewArtistPageHeader(a, nav)
a.container = container.NewBorder( a.container = container.NewBorder(
container.New(&layouts.MaxPadLayout{PadLeft: 15, PadRight: 15, PadTop: 15, PadBottom: 10}, a.header), container.New(&layouts.MaxPadLayout{PadLeft: 15, PadRight: 15, PadTop: 15, PadBottom: 10}, a.header),
nil, nil, nil, layout.NewSpacer()) nil, nil, nil, layout.NewSpacer())
@@ -65,10 +68,11 @@ func (a *ArtistPage) Reload() {
func (a *ArtistPage) Save() SavedPage { func (a *ArtistPage) Save() SavedPage {
return &savedArtistPage{ return &savedArtistPage{
artistID: a.artistID, artistID: a.artistID,
sm: a.sm, sm: a.sm,
im: a.im, im: a.im,
nav: a.nav, nav: a.nav,
popUpProvider: a.popUpProvider,
} }
} }
@@ -109,14 +113,15 @@ func (a *ArtistPage) CreateRenderer() fyne.WidgetRenderer {
} }
type savedArtistPage struct { type savedArtistPage struct {
artistID string artistID string
sm *backend.ServerManager sm *backend.ServerManager
im *backend.ImageManager im *backend.ImageManager
nav func(Route) nav func(Route)
popUpProvider util.PopUpProvider
} }
func (s *savedArtistPage) Restore() Page { func (s *savedArtistPage) Restore() Page {
return NewArtistPage(s.artistID, s.sm, s.im, s.nav) return NewArtistPage(s.artistID, s.sm, s.im, s.popUpProvider, s.nav)
} }
type ArtistPageHeader struct { type ArtistPageHeader struct {
@@ -124,6 +129,7 @@ type ArtistPageHeader struct {
nav func(Route) nav func(Route)
artistID string artistID string
artistPage *ArtistPage
artistImage *widgets.ImagePlaceholder artistImage *widgets.ImagePlaceholder
titleDisp *widget.RichText titleDisp *widget.RichText
biographyDisp *widget.RichText biographyDisp *widget.RichText
@@ -131,9 +137,10 @@ type ArtistPageHeader struct {
container *fyne.Container container *fyne.Container
} }
func NewArtistPageHeader(nav func(Route)) *ArtistPageHeader { func NewArtistPageHeader(page *ArtistPage, nav func(Route)) *ArtistPageHeader {
a := &ArtistPageHeader{ a := &ArtistPageHeader{
nav: nav, nav: nav,
artistPage: page,
titleDisp: widget.NewRichTextWithText(""), titleDisp: widget.NewRichTextWithText(""),
biographyDisp: widget.NewRichTextWithText("Artist biography not available."), biographyDisp: widget.NewRichTextWithText("Artist biography not available."),
similarArtists: container.NewHBox(), similarArtists: container.NewHBox(),
@@ -192,7 +199,10 @@ func (a *ArtistPageHeader) UpdateInfo(info *subsonic.ArtistInfo2) {
if err != nil { if err != nil {
return return
} }
a.artistImage.SetImage(im) a.artistImage.OnTapped = func() {
util.ShowPopUpImage(im, a.artistPage.popUpProvider)
}
a.artistImage.SetImage(im, true /*tappable*/)
} }
} }
} }
+11 -12
View File
@@ -19,16 +19,15 @@ import (
type PlaylistPage struct { type PlaylistPage struct {
widget.BaseWidget widget.BaseWidget
playlistID string playlistID string
sm *backend.ServerManager sm *backend.ServerManager
pm *backend.PlaybackManager pm *backend.PlaybackManager
im *backend.ImageManager im *backend.ImageManager
nav func(Route) nav func(Route)
header *PlaylistPageHeader header *PlaylistPageHeader
tracklist *widgets.Tracklist tracklist *widgets.Tracklist
nowPlayingID string nowPlayingID string
container *fyne.Container container *fyne.Container
popUpProvider PopUpProvider
} }
func NewPlaylistPage( func NewPlaylistPage(
@@ -160,13 +159,13 @@ func (a *PlaylistPageHeader) Update(playlist *subsonic.Playlist) {
var haveCover bool var haveCover bool
if playlist.CoverArt != "" { if playlist.CoverArt != "" {
if im, err := a.page.im.GetAlbumThumbnail(playlist.CoverArt); err == nil && im != nil { if im, err := a.page.im.GetAlbumThumbnail(playlist.CoverArt); err == nil && im != nil {
a.image.SetImage(im) a.image.SetImage(im, false /*tappable*/)
haveCover = true haveCover = true
} }
} }
if !haveCover { if !haveCover {
if im, err := a.page.im.GetAlbumThumbnail(playlist.ID); err == nil && im != nil { if im, err := a.page.im.GetAlbumThumbnail(playlist.ID); err == nil && im != nil {
a.image.SetImage(im) a.image.SetImage(im, false)
} }
} }
a.Refresh() a.Refresh()
+8 -7
View File
@@ -2,6 +2,7 @@ package browsing
import ( import (
"supersonic/backend" "supersonic/backend"
"supersonic/ui/util"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
@@ -70,14 +71,18 @@ type Router struct {
App *backend.App App *backend.App
MainWindow fyne.Window MainWindow fyne.Window
Nav NavigationHandler Nav NavigationHandler
pop util.PopUpProvider
} }
func NewRouter(app *backend.App, mainWindow fyne.Window, nav NavigationHandler) Router { func NewRouter(app *backend.App, mainWindow fyne.Window, nav NavigationHandler) Router {
return Router{ r := Router{
App: app, App: app,
MainWindow: mainWindow, MainWindow: mainWindow,
Nav: nav, Nav: nav,
} }
r.pop = &popUpProvider{window: r.MainWindow}
return r
} }
type popUpProvider struct { type popUpProvider struct {
@@ -92,18 +97,14 @@ func (p *popUpProvider) WindowSize() fyne.Size {
return p.window.Canvas().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.pop(), 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:
return NewArtistPage(rte.Arg, r.App.ServerManager, r.App.ImageManager, r.OpenRoute) return NewArtistPage(rte.Arg, r.App.ServerManager, r.App.ImageManager, r.pop, r.OpenRoute)
case Artists: case Artists:
return NewArtistsGenresPage(false, r.App.ServerManager, r.OpenRoute) return NewArtistsGenresPage(false, r.App.ServerManager, r.OpenRoute)
case Favorites: case Favorites:
+28
View File
@@ -6,6 +6,8 @@ import (
"math" "math"
"strings" "strings"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
"golang.org/x/net/html" "golang.org/x/net/html"
) )
@@ -26,6 +28,32 @@ func ImageAspect(im image.Image) float32 {
return float32(b.Max.X-b.Min.X) / float32(b.Max.Y-b.Min.Y) return float32(b.Max.X-b.Min.X) / float32(b.Max.Y-b.Min.Y)
} }
type PopUpProvider interface {
CreatePopUp(fyne.CanvasObject) *widget.PopUp
WindowSize() fyne.Size
}
func ShowPopUpImage(img image.Image, popUpProvider PopUpProvider) {
im := canvas.NewImageFromImage(img)
im.FillMode = canvas.ImageFillContain
pop := popUpProvider.CreatePopUp(im)
s := popUpProvider.WindowSize()
var popS fyne.Size
if asp := ImageAspect(img); s.Width/s.Height > asp {
// window height is limiting factor
h := s.Height * 0.8
popS = fyne.NewSize(h*asp, h)
} else {
w := s.Width * 0.8
popS = fyne.NewSize(w, w*(1/asp))
}
pop.Resize(popS)
pop.ShowAtPosition(fyne.NewPos(
(s.Width-popS.Width)/2,
(s.Height-popS.Height)/2,
))
}
func RichTextSegsFromHTMLString(s string) []widget.RichTextSegment { func RichTextSegsFromHTMLString(s string) []widget.RichTextSegment {
tokr := html.NewTokenizer(strings.NewReader(s)) tokr := html.NewTokenizer(strings.NewReader(s))
var segs []widget.RichTextSegment var segs []widget.RichTextSegment
+28 -5
View File
@@ -15,6 +15,8 @@ type ImagePlaceholder struct {
widget.BaseWidget widget.BaseWidget
container *fyne.Container container *fyne.Container
minSize float32 minSize float32
OnTapped func()
} }
func NewImagePlaceholder(centerIcon fyne.Resource, minSize float32) *ImagePlaceholder { func NewImagePlaceholder(centerIcon fyne.Resource, minSize float32) *ImagePlaceholder {
@@ -34,15 +36,36 @@ func NewImagePlaceholder(centerIcon fyne.Resource, minSize float32) *ImagePlaceh
return m return m
} }
func (i *ImagePlaceholder) SetImage(img image.Image) { type CanvasImage interface {
cImg := canvas.NewImageFromImage(img) fyne.CanvasObject
cImg.FillMode = canvas.ImageFillContain
cImg.SetMinSize(fyne.NewSize(i.minSize, i.minSize)) SetMinSize(fyne.Size)
}
func (i *ImagePlaceholder) SetImage(img image.Image, tappable bool) {
var cIm CanvasImage
if tappable {
cImg := NewTappableImage(i.onTapped)
cImg.Image.Image = img
cImg.FillMode = canvas.ImageFillContain
cIm = cImg
} else {
cImg := canvas.NewImageFromImage(img)
cImg.FillMode = canvas.ImageFillContain
cIm = cImg
}
cIm.SetMinSize(fyne.NewSize(i.minSize, i.minSize))
i.container.RemoveAll() i.container.RemoveAll()
i.container.Add(cImg) i.container.Add(cIm)
i.container.Refresh() i.container.Refresh()
} }
func (i *ImagePlaceholder) onTapped() {
if i.OnTapped != nil {
i.OnTapped()
}
}
func (i *ImagePlaceholder) CreateRenderer() fyne.WidgetRenderer { func (i *ImagePlaceholder) CreateRenderer() fyne.WidgetRenderer {
return widget.NewSimpleRenderer(i.container) return widget.NewSimpleRenderer(i.container)
} }
+2 -2
View File
@@ -14,8 +14,8 @@ type TappableImage struct {
OnTapped func() OnTapped func()
} }
func NewTappableImage() *TappableImage { func NewTappableImage(onTapped func()) *TappableImage {
t := &TappableImage{} t := &TappableImage{OnTapped: onTapped}
t.ExtendBaseWidget(t) t.ExtendBaseWidget(t)
return t return t
} }