diff --git a/ui/browsing/albumpage.go b/ui/browsing/albumpage.go index 5c343ad..4677074 100644 --- a/ui/browsing/albumpage.go +++ b/ui/browsing/albumpage.go @@ -28,22 +28,17 @@ type AlbumPage struct { tracklist *widgets.Tracklist nowPlayingID string container *fyne.Container - popUpProvider PopUpProvider + popUpProvider util.PopUpProvider OnPlayAlbum func(string, int) } -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, + popUpProvider util.PopUpProvider, nav func(Route), ) *AlbumPage { 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 { a := &AlbumPageHeader{page: page} a.ExtendBaseWidget(a) - a.cover = widgets.NewTappableImage() + a.cover = widgets.NewTappableImage(a.showPopUpCover) 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 @@ -223,24 +217,7 @@ func (a *AlbumPageHeader) showPopUpCover() { 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() - 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, - )) + util.ShowPopUpImage(cover, a.page.popUpProvider) } func formatMiscLabelStr(a *subsonic.AlbumID3) string { @@ -252,7 +229,7 @@ type savedAlbumPage struct { lm *backend.LibraryManager im *backend.ImageManager sm *backend.ServerManager - popUpProvider PopUpProvider + popUpProvider util.PopUpProvider nav func(Route) } diff --git a/ui/browsing/artistpage.go b/ui/browsing/artistpage.go index aaf8955..765e3b2 100644 --- a/ui/browsing/artistpage.go +++ b/ui/browsing/artistpage.go @@ -24,10 +24,12 @@ var _ fyne.Widget = (*ArtistPage)(nil) type ArtistPage struct { widget.BaseWidget - artistID string - im *backend.ImageManager - sm *backend.ServerManager - nav func(Route) + artistID string + im *backend.ImageManager + sm *backend.ServerManager + nav func(Route) + popUpProvider util.PopUpProvider + grid *widgets.AlbumGrid header *ArtistPageHeader container *fyne.Container @@ -35,15 +37,16 @@ type ArtistPage struct { 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{ - artistID: artistID, - sm: sm, - im: im, - nav: nav, + artistID: artistID, + sm: sm, + im: im, + nav: nav, + popUpProvider: popUp, } a.ExtendBaseWidget(a) - a.header = NewArtistPageHeader(nav) + a.header = NewArtistPageHeader(a, nav) a.container = container.NewBorder( container.New(&layouts.MaxPadLayout{PadLeft: 15, PadRight: 15, PadTop: 15, PadBottom: 10}, a.header), nil, nil, nil, layout.NewSpacer()) @@ -65,10 +68,11 @@ func (a *ArtistPage) Reload() { func (a *ArtistPage) Save() SavedPage { return &savedArtistPage{ - artistID: a.artistID, - sm: a.sm, - im: a.im, - nav: a.nav, + artistID: a.artistID, + sm: a.sm, + im: a.im, + nav: a.nav, + popUpProvider: a.popUpProvider, } } @@ -109,14 +113,15 @@ func (a *ArtistPage) CreateRenderer() fyne.WidgetRenderer { } type savedArtistPage struct { - artistID string - sm *backend.ServerManager - im *backend.ImageManager - nav func(Route) + artistID string + sm *backend.ServerManager + im *backend.ImageManager + nav func(Route) + popUpProvider util.PopUpProvider } 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 { @@ -124,6 +129,7 @@ type ArtistPageHeader struct { nav func(Route) artistID string + artistPage *ArtistPage artistImage *widgets.ImagePlaceholder titleDisp *widget.RichText biographyDisp *widget.RichText @@ -131,9 +137,10 @@ type ArtistPageHeader struct { container *fyne.Container } -func NewArtistPageHeader(nav func(Route)) *ArtistPageHeader { +func NewArtistPageHeader(page *ArtistPage, nav func(Route)) *ArtistPageHeader { a := &ArtistPageHeader{ nav: nav, + artistPage: page, titleDisp: widget.NewRichTextWithText(""), biographyDisp: widget.NewRichTextWithText("Artist biography not available."), similarArtists: container.NewHBox(), @@ -192,7 +199,10 @@ func (a *ArtistPageHeader) UpdateInfo(info *subsonic.ArtistInfo2) { if err != nil { return } - a.artistImage.SetImage(im) + a.artistImage.OnTapped = func() { + util.ShowPopUpImage(im, a.artistPage.popUpProvider) + } + a.artistImage.SetImage(im, true /*tappable*/) } } } diff --git a/ui/browsing/playlistpage.go b/ui/browsing/playlistpage.go index 010154d..ea01e9a 100644 --- a/ui/browsing/playlistpage.go +++ b/ui/browsing/playlistpage.go @@ -19,16 +19,15 @@ import ( type PlaylistPage struct { widget.BaseWidget - playlistID string - sm *backend.ServerManager - pm *backend.PlaybackManager - im *backend.ImageManager - nav func(Route) - header *PlaylistPageHeader - tracklist *widgets.Tracklist - nowPlayingID string - container *fyne.Container - popUpProvider PopUpProvider + playlistID string + sm *backend.ServerManager + pm *backend.PlaybackManager + im *backend.ImageManager + nav func(Route) + header *PlaylistPageHeader + tracklist *widgets.Tracklist + nowPlayingID string + container *fyne.Container } func NewPlaylistPage( @@ -160,13 +159,13 @@ func (a *PlaylistPageHeader) Update(playlist *subsonic.Playlist) { var haveCover bool if playlist.CoverArt != "" { 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 } } if !haveCover { if im, err := a.page.im.GetAlbumThumbnail(playlist.ID); err == nil && im != nil { - a.image.SetImage(im) + a.image.SetImage(im, false) } } a.Refresh() diff --git a/ui/browsing/router.go b/ui/browsing/router.go index ca7f5a0..4ec1f9e 100644 --- a/ui/browsing/router.go +++ b/ui/browsing/router.go @@ -2,6 +2,7 @@ package browsing import ( "supersonic/backend" + "supersonic/ui/util" "fyne.io/fyne/v2" "fyne.io/fyne/v2/widget" @@ -70,14 +71,18 @@ type Router struct { App *backend.App MainWindow fyne.Window Nav NavigationHandler + + pop util.PopUpProvider } func NewRouter(app *backend.App, mainWindow fyne.Window, nav NavigationHandler) Router { - return Router{ + r := Router{ App: app, MainWindow: mainWindow, Nav: nav, } + r.pop = &popUpProvider{window: r.MainWindow} + return r } type popUpProvider struct { @@ -92,18 +97,14 @@ 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.pop(), 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: - 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: return NewArtistsGenresPage(false, r.App.ServerManager, r.OpenRoute) case Favorites: diff --git a/ui/util/util.go b/ui/util/util.go index d1db088..dee6e3c 100644 --- a/ui/util/util.go +++ b/ui/util/util.go @@ -6,6 +6,8 @@ import ( "math" "strings" + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/widget" "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) } +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 { tokr := html.NewTokenizer(strings.NewReader(s)) var segs []widget.RichTextSegment diff --git a/ui/widgets/imageplaceholder.go b/ui/widgets/imageplaceholder.go index 1e3a6e4..aa1b198 100644 --- a/ui/widgets/imageplaceholder.go +++ b/ui/widgets/imageplaceholder.go @@ -15,6 +15,8 @@ type ImagePlaceholder struct { widget.BaseWidget container *fyne.Container minSize float32 + + OnTapped func() } func NewImagePlaceholder(centerIcon fyne.Resource, minSize float32) *ImagePlaceholder { @@ -34,15 +36,36 @@ func NewImagePlaceholder(centerIcon fyne.Resource, minSize float32) *ImagePlaceh return m } -func (i *ImagePlaceholder) SetImage(img image.Image) { - cImg := canvas.NewImageFromImage(img) - cImg.FillMode = canvas.ImageFillContain - cImg.SetMinSize(fyne.NewSize(i.minSize, i.minSize)) +type CanvasImage interface { + fyne.CanvasObject + + 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.Add(cImg) + i.container.Add(cIm) i.container.Refresh() } +func (i *ImagePlaceholder) onTapped() { + if i.OnTapped != nil { + i.OnTapped() + } +} + func (i *ImagePlaceholder) CreateRenderer() fyne.WidgetRenderer { return widget.NewSimpleRenderer(i.container) } diff --git a/ui/widgets/tappableimage.go b/ui/widgets/tappableimage.go index a8a9608..d17d173 100644 --- a/ui/widgets/tappableimage.go +++ b/ui/widgets/tappableimage.go @@ -14,8 +14,8 @@ type TappableImage struct { OnTapped func() } -func NewTappableImage() *TappableImage { - t := &TappableImage{} +func NewTappableImage(onTapped func()) *TappableImage { + t := &TappableImage{OnTapped: onTapped} t.ExtendBaseWidget(t) return t }