refactor 1: renaming
This commit is contained in:
+1
-1
@@ -65,7 +65,7 @@ func StartupApp(appName, appVersionTag, configFile, latestReleaseURL string) (*A
|
||||
a.LibraryManager = NewLibraryManager(a.ServerManager)
|
||||
a.ImageManager = NewImageManager(a.bgrndCtx, a.ServerManager, configdir.LocalCache(a.appName))
|
||||
a.LibraryManager.PreCacheCoverFn = func(coverID string) {
|
||||
_, _ = a.ImageManager.GetAlbumThumbnail(coverID)
|
||||
_, _ = a.ImageManager.GetCoverThumbnail(coverID)
|
||||
}
|
||||
|
||||
return a, nil
|
||||
|
||||
@@ -45,7 +45,7 @@ func NewImageManager(ctx context.Context, s *ServerManager, baseCacheDir string)
|
||||
return i
|
||||
}
|
||||
|
||||
func (i *ImageManager) GetAlbumThumbnailFromCache(coverID string) (image.Image, bool) {
|
||||
func (i *ImageManager) GetCoverThumbnailFromCache(coverID string) (image.Image, bool) {
|
||||
img, err := i.thumbnailCache.GetExtendTTL(coverID, i.thumbnailCache.DefaultTTL)
|
||||
if err == nil && img != nil {
|
||||
return img, true
|
||||
@@ -53,14 +53,14 @@ func (i *ImageManager) GetAlbumThumbnailFromCache(coverID string) (image.Image,
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (i *ImageManager) GetAlbumThumbnail(coverID string) (image.Image, error) {
|
||||
if im, ok := i.GetAlbumThumbnailFromCache(coverID); ok {
|
||||
func (i *ImageManager) GetCoverThumbnail(coverID string) (image.Image, error) {
|
||||
if im, ok := i.GetCoverThumbnailFromCache(coverID); ok {
|
||||
return im, nil
|
||||
}
|
||||
return i.fetchAndCacheCoverFromDiskOrServer(coverID, i.thumbnailCache.DefaultTTL)
|
||||
}
|
||||
|
||||
func (i *ImageManager) GetAlbumThumbnailWithTTL(coverID string, ttl time.Duration) (image.Image, error) {
|
||||
func (i *ImageManager) GetCoverThumbnailWithTTL(coverID string, ttl time.Duration) (image.Image, error) {
|
||||
// in-memory cache
|
||||
if img, err := i.thumbnailCache.GetWithNewTTL(coverID, ttl); err == nil {
|
||||
return img, nil
|
||||
@@ -68,7 +68,7 @@ func (i *ImageManager) GetAlbumThumbnailWithTTL(coverID string, ttl time.Duratio
|
||||
return i.fetchAndCacheCoverFromDiskOrServer(coverID, ttl)
|
||||
}
|
||||
|
||||
func (i *ImageManager) GetFullSizeAlbumCover(coverID string) (image.Image, error) {
|
||||
func (i *ImageManager) GetFullSizeCoverArt(coverID string) (image.Image, error) {
|
||||
if i.cachedFullSizeCoverID == coverID {
|
||||
return i.cachedFullSizeCover, nil
|
||||
}
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ func (bp *BottomPanel) onSongChange(song *subsonic.Child, _ *subsonic.Child) {
|
||||
// be in cache for the next song if it's from the same album, or
|
||||
// if the user navigates to the album page for the track
|
||||
imgTTLSec := song.Duration + 30
|
||||
im, _ = bp.ImageManager.GetAlbumThumbnailWithTTL(song.CoverArt, time.Duration(imgTTLSec)*time.Second)
|
||||
im, _ = bp.ImageManager.GetCoverThumbnailWithTTL(song.CoverArt, time.Duration(imgTTLSec)*time.Second)
|
||||
}
|
||||
bp.NowPlaying.Update(song.Title, song.Artist, song.Album, im)
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ func (a *AlbumPageHeader) Update(album *subsonic.AlbumID3, im *backend.ImageMana
|
||||
a.Refresh()
|
||||
|
||||
go func() {
|
||||
if cover, err := im.GetAlbumThumbnail(album.CoverArt); err == nil {
|
||||
if cover, err := im.GetCoverThumbnail(album.CoverArt); err == nil {
|
||||
a.cover.Image.Image = cover
|
||||
a.cover.Refresh()
|
||||
} else {
|
||||
@@ -244,7 +244,7 @@ func (a *AlbumPageHeader) toggleFavorited() {
|
||||
}
|
||||
|
||||
func (a *AlbumPageHeader) showPopUpCover() {
|
||||
cover, err := a.page.im.GetFullSizeAlbumCover(a.coverID)
|
||||
cover, err := a.page.im.GetFullSizeCoverArt(a.coverID)
|
||||
if err != nil {
|
||||
log.Printf("error getting full size album cover: %s", err.Error())
|
||||
return
|
||||
|
||||
+12
-12
@@ -24,8 +24,8 @@ type AlbumsPage struct {
|
||||
pm *backend.PlaybackManager
|
||||
im *backend.ImageManager
|
||||
lm *backend.LibraryManager
|
||||
grid *widgets.AlbumGrid
|
||||
searchGrid *widgets.AlbumGrid
|
||||
grid *widgets.GridView
|
||||
searchGrid *widgets.GridView
|
||||
searcher *widgets.Searcher
|
||||
searchText string
|
||||
titleDisp *widget.RichText
|
||||
@@ -73,9 +73,9 @@ func NewAlbumsPage(cfg *backend.AlbumsPageConfig, contr *controller.Controller,
|
||||
a.sortOrder.Selected = cfg.SortOrder
|
||||
iter := lm.AlbumsIter(backend.AlbumSortOrder(a.sortOrder.Selected))
|
||||
a.grid = widgets.NewAlbumGrid(iter, im, false /*showYear*/)
|
||||
a.grid.OnPlayAlbum = a.onPlayAlbum
|
||||
a.grid.OnShowArtistPage = a.onShowArtistPage
|
||||
a.grid.OnShowAlbumPage = a.onShowAlbumPage
|
||||
a.grid.OnPlay = a.onPlayAlbum
|
||||
a.grid.OnShowSecondaryPage = a.onShowArtistPage
|
||||
a.grid.OnShowItemPage = a.onShowAlbumPage
|
||||
a.searcher = widgets.NewSearcher()
|
||||
a.searcher.OnSearched = a.OnSearched
|
||||
a.createContainer(false)
|
||||
@@ -116,13 +116,13 @@ func restoreAlbumsPage(saved *savedAlbumsPage) *AlbumsPage {
|
||||
a.sortOrder = NewSelect(backend.AlbumSortOrders, nil)
|
||||
a.sortOrder.Selected = saved.sortOrder
|
||||
a.sortOrder.OnChanged = a.onSortOrderChanged
|
||||
a.grid = widgets.NewAlbumGridFromState(saved.gridState)
|
||||
a.grid = widgets.NewGridViewFromState(saved.gridState)
|
||||
a.searcher = widgets.NewSearcher()
|
||||
a.searcher.OnSearched = a.OnSearched
|
||||
a.searcher.Entry.Text = saved.searchText
|
||||
a.searchText = saved.searchText
|
||||
if a.searchText != "" {
|
||||
a.searchGrid = widgets.NewAlbumGridFromState(saved.searchGridState)
|
||||
a.searchGrid = widgets.NewGridViewFromState(saved.searchGridState)
|
||||
}
|
||||
a.createContainer(saved.searchText != "")
|
||||
|
||||
@@ -181,9 +181,9 @@ func (a *AlbumsPage) Save() SavedPage {
|
||||
func (a *AlbumsPage) doSearch(query string) {
|
||||
if a.searchGrid == nil {
|
||||
a.searchGrid = widgets.NewAlbumGrid(a.lm.SearchIter(query), a.im, false /*showYear*/)
|
||||
a.searchGrid.OnPlayAlbum = a.onPlayAlbum
|
||||
a.searchGrid.OnShowAlbumPage = a.onShowAlbumPage
|
||||
a.searchGrid.OnShowArtistPage = a.onShowArtistPage
|
||||
a.searchGrid.OnPlay = a.onPlayAlbum
|
||||
a.searchGrid.OnShowItemPage = a.onShowAlbumPage
|
||||
a.searchGrid.OnShowSecondaryPage = a.onShowArtistPage
|
||||
} else {
|
||||
a.searchGrid.Reset(a.lm.SearchIter(query))
|
||||
}
|
||||
@@ -225,8 +225,8 @@ type savedAlbumsPage struct {
|
||||
lm *backend.LibraryManager
|
||||
im *backend.ImageManager
|
||||
sortOrder string
|
||||
gridState widgets.AlbumGridState
|
||||
searchGridState widgets.AlbumGridState
|
||||
gridState widgets.GridViewState
|
||||
searchGridState widgets.GridViewState
|
||||
}
|
||||
|
||||
func (s *savedAlbumsPage) Restore() Page {
|
||||
|
||||
@@ -39,7 +39,7 @@ type ArtistPage struct {
|
||||
|
||||
artistInfo *subsonic.ArtistID3
|
||||
|
||||
albumGrid *widgets.AlbumGrid
|
||||
albumGrid *widgets.GridView
|
||||
tracklistCtr *fyne.Container
|
||||
nowPlayingID string
|
||||
header *ArtistPageHeader
|
||||
@@ -173,8 +173,8 @@ func (a *ArtistPage) showAlbumGrid() {
|
||||
return
|
||||
}
|
||||
a.albumGrid = widgets.NewFixedAlbumGrid(a.artistInfo.Album, a.im, true /*showYear*/)
|
||||
a.albumGrid.OnPlayAlbum = a.onPlayAlbum
|
||||
a.albumGrid.OnShowAlbumPage = a.onShowAlbumPage
|
||||
a.albumGrid.OnPlay = a.onPlayAlbum
|
||||
a.albumGrid.OnShowItemPage = a.onShowAlbumPage
|
||||
}
|
||||
a.container.Objects[0].(*fyne.Container).Objects[0] = a.albumGrid
|
||||
a.container.Objects[0].Refresh()
|
||||
|
||||
@@ -32,8 +32,8 @@ type FavoritesPage struct {
|
||||
nowPlayingID string
|
||||
pendingViewSwitch bool
|
||||
|
||||
grid *widgets.AlbumGrid
|
||||
searchGrid *widgets.AlbumGrid
|
||||
grid *widgets.GridView
|
||||
searchGrid *widgets.GridView
|
||||
artistListCtr *fyne.Container
|
||||
tracklistCtr *fyne.Container
|
||||
searcher *widgets.Searcher
|
||||
@@ -82,9 +82,9 @@ func (a *FavoritesPage) createHeader(activeBtnIdx int, searchText string) {
|
||||
}
|
||||
|
||||
func (a *FavoritesPage) connectGridActions() {
|
||||
a.grid.OnPlayAlbum = a.onPlayAlbum
|
||||
a.grid.OnShowAlbumPage = a.onShowAlbumPage
|
||||
a.grid.OnShowArtistPage = a.onShowArtistPage
|
||||
a.grid.OnPlay = a.onPlayAlbum
|
||||
a.grid.OnShowItemPage = a.onShowAlbumPage
|
||||
a.grid.OnShowSecondaryPage = a.onShowArtistPage
|
||||
}
|
||||
|
||||
func (a *FavoritesPage) createContainer(initialView fyne.CanvasObject) {
|
||||
@@ -105,11 +105,11 @@ func restoreFavoritesPage(saved *savedFavoritesPage) *FavoritesPage {
|
||||
}
|
||||
a.ExtendBaseWidget(a)
|
||||
a.createHeader(saved.activeToggleBtn, saved.searchText)
|
||||
a.grid = widgets.NewAlbumGridFromState(saved.gridState)
|
||||
a.grid = widgets.NewGridViewFromState(saved.gridState)
|
||||
a.connectGridActions()
|
||||
|
||||
if saved.searchText != "" {
|
||||
a.searchGrid = widgets.NewAlbumGridFromState(saved.searchGridState)
|
||||
a.searchGrid = widgets.NewGridViewFromState(saved.searchGridState)
|
||||
}
|
||||
|
||||
if saved.activeToggleBtn == 1 {
|
||||
@@ -221,9 +221,9 @@ func (a *FavoritesPage) doSearchAlbums(query string) {
|
||||
})
|
||||
if a.searchGrid == nil {
|
||||
a.searchGrid = widgets.NewAlbumGrid(iter, a.im, false /*showYear*/)
|
||||
a.searchGrid.OnPlayAlbum = a.onPlayAlbum
|
||||
a.searchGrid.OnShowAlbumPage = a.onShowAlbumPage
|
||||
a.searchGrid.OnShowArtistPage = a.onShowArtistPage
|
||||
a.searchGrid.OnPlay = a.onPlayAlbum
|
||||
a.searchGrid.OnShowItemPage = a.onShowAlbumPage
|
||||
a.searchGrid.OnShowSecondaryPage = a.onShowArtistPage
|
||||
} else {
|
||||
a.searchGrid.Reset(iter)
|
||||
}
|
||||
@@ -352,8 +352,8 @@ type savedFavoritesPage struct {
|
||||
sm *backend.ServerManager
|
||||
im *backend.ImageManager
|
||||
lm *backend.LibraryManager
|
||||
gridState widgets.AlbumGridState
|
||||
searchGridState widgets.AlbumGridState
|
||||
gridState widgets.GridViewState
|
||||
searchGridState widgets.GridViewState
|
||||
searchText string
|
||||
activeToggleBtn int
|
||||
}
|
||||
|
||||
+12
-12
@@ -24,8 +24,8 @@ type GenrePage struct {
|
||||
im *backend.ImageManager
|
||||
pm *backend.PlaybackManager
|
||||
lm *backend.LibraryManager
|
||||
grid *widgets.AlbumGrid
|
||||
searchGrid *widgets.AlbumGrid
|
||||
grid *widgets.GridView
|
||||
searchGrid *widgets.GridView
|
||||
searcher *widgets.Searcher
|
||||
searchText string
|
||||
titleDisp *widget.RichText
|
||||
@@ -53,9 +53,9 @@ func NewGenrePage(genre string, contr *controller.Controller, pm *backend.Playba
|
||||
g.playRandom = widget.NewButtonWithIcon("Play random", res.ResShuffleInvertSvg, g.playRandomSongs)
|
||||
iter := g.lm.GenreIter(g.genre)
|
||||
g.grid = widgets.NewAlbumGrid(iter, g.im, false)
|
||||
g.grid.OnPlayAlbum = g.onPlayAlbum
|
||||
g.grid.OnShowArtistPage = g.onShowArtistPage
|
||||
g.grid.OnShowAlbumPage = g.onShowAlbumPage
|
||||
g.grid.OnPlay = g.onPlayAlbum
|
||||
g.grid.OnShowSecondaryPage = g.onShowArtistPage
|
||||
g.grid.OnShowItemPage = g.onShowAlbumPage
|
||||
g.searcher = widgets.NewSearcher()
|
||||
g.searcher.OnSearched = g.OnSearched
|
||||
g.createContainer(false)
|
||||
@@ -94,13 +94,13 @@ func restoreGenrePage(saved *savedGenrePage) *GenrePage {
|
||||
SizeName: theme.SizeNameHeadingText,
|
||||
}
|
||||
g.playRandom = widget.NewButtonWithIcon("Play random", res.ResShuffleInvertSvg, g.playRandomSongs)
|
||||
g.grid = widgets.NewAlbumGridFromState(saved.gridState)
|
||||
g.grid = widgets.NewGridViewFromState(saved.gridState)
|
||||
g.searcher = widgets.NewSearcher()
|
||||
g.searcher.OnSearched = g.OnSearched
|
||||
g.searcher.Entry.Text = saved.searchText
|
||||
g.searchText = saved.searchText
|
||||
if g.searchText != "" {
|
||||
g.searchGrid = widgets.NewAlbumGridFromState(saved.searchGridState)
|
||||
g.searchGrid = widgets.NewGridViewFromState(saved.searchGridState)
|
||||
}
|
||||
g.createContainer(saved.searchText != "")
|
||||
|
||||
@@ -177,9 +177,9 @@ func (g *GenrePage) doSearch(query string) {
|
||||
})
|
||||
if g.searchGrid == nil {
|
||||
g.searchGrid = widgets.NewAlbumGrid(iter, g.im, false /*showYear*/)
|
||||
g.searchGrid.OnPlayAlbum = g.onPlayAlbum
|
||||
g.searchGrid.OnShowAlbumPage = g.onShowAlbumPage
|
||||
g.searchGrid.OnShowArtistPage = g.onShowArtistPage
|
||||
g.searchGrid.OnPlay = g.onPlayAlbum
|
||||
g.searchGrid.OnShowItemPage = g.onShowAlbumPage
|
||||
g.searchGrid.OnShowSecondaryPage = g.onShowArtistPage
|
||||
} else {
|
||||
g.searchGrid.Reset(iter)
|
||||
}
|
||||
@@ -198,8 +198,8 @@ type savedGenrePage struct {
|
||||
pm *backend.PlaybackManager
|
||||
lm *backend.LibraryManager
|
||||
im *backend.ImageManager
|
||||
gridState widgets.AlbumGridState
|
||||
searchGridState widgets.AlbumGridState
|
||||
gridState widgets.GridViewState
|
||||
searchGridState widgets.GridViewState
|
||||
}
|
||||
|
||||
func (s *savedGenrePage) Restore() Page {
|
||||
|
||||
@@ -233,13 +233,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 {
|
||||
if im, err := a.page.im.GetCoverThumbnail(playlist.CoverArt); err == nil && im != nil {
|
||||
a.image.SetImage(im, false /*tappable*/)
|
||||
haveCover = true
|
||||
}
|
||||
}
|
||||
if !haveCover {
|
||||
if im, err := a.page.im.GetAlbumThumbnail(playlist.ID); err == nil && im != nil {
|
||||
if im, err := a.page.im.GetCoverThumbnail(playlist.ID); err == nil && im != nil {
|
||||
a.image.SetImage(im, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
package widgets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"image"
|
||||
"strconv"
|
||||
|
||||
"supersonic/res"
|
||||
"supersonic/ui/layouts"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/canvas"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/driver/desktop"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
|
||||
"github.com/dweymouth/go-subsonic/subsonic"
|
||||
)
|
||||
|
||||
var _ fyne.Widget = (*AlbumCard)(nil)
|
||||
|
||||
var _ fyne.Widget = (*albumCover)(nil)
|
||||
var _ fyne.Tappable = (*albumCover)(nil)
|
||||
|
||||
type albumCover struct {
|
||||
widget.BaseWidget
|
||||
|
||||
Im *canvas.Image
|
||||
playbtn *canvas.Image
|
||||
OnDoubleTapped func()
|
||||
OnTapped func()
|
||||
}
|
||||
|
||||
func newAlbumCover() *albumCover {
|
||||
a := &albumCover{}
|
||||
a.ExtendBaseWidget(a)
|
||||
a.Im = &canvas.Image{FillMode: canvas.ImageFillContain, ScaleMode: canvas.ImageScaleFastest}
|
||||
a.Im.SetMinSize(fyne.NewSize(200, 200))
|
||||
a.playbtn = &canvas.Image{FillMode: canvas.ImageFillContain, Resource: res.ResPlaybuttonPng}
|
||||
a.playbtn.SetMinSize(fyne.NewSize(60, 60))
|
||||
a.playbtn.Hidden = true
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *albumCover) CreateRenderer() fyne.WidgetRenderer {
|
||||
return widget.NewSimpleRenderer(
|
||||
container.NewMax(a.Im, container.NewCenter(a.playbtn)),
|
||||
)
|
||||
}
|
||||
|
||||
func (a *albumCover) Cursor() desktop.Cursor {
|
||||
return desktop.PointerCursor
|
||||
}
|
||||
|
||||
func (a *albumCover) Tapped(e *fyne.PointEvent) {
|
||||
if isInside(a.center(), a.playbtn.Size().Height/2, e.Position) {
|
||||
if a.OnDoubleTapped != nil {
|
||||
a.OnDoubleTapped()
|
||||
}
|
||||
return
|
||||
}
|
||||
if a.OnTapped != nil {
|
||||
a.OnTapped()
|
||||
}
|
||||
}
|
||||
|
||||
func (a *albumCover) MouseIn(*desktop.MouseEvent) {
|
||||
a.playbtn.Hidden = false
|
||||
a.Refresh()
|
||||
}
|
||||
|
||||
func (a *albumCover) MouseOut() {
|
||||
a.playbtn.Hidden = true
|
||||
a.Refresh()
|
||||
}
|
||||
|
||||
// TODO: figure out why circle around play button isn't being displayed
|
||||
func (a *albumCover) MouseMoved(e *desktop.MouseEvent) {
|
||||
if isInside(a.center(), a.playbtn.MinSize().Height/2, e.Position) {
|
||||
a.playbtn.SetMinSize(fyne.NewSize(65, 65))
|
||||
} else {
|
||||
a.playbtn.SetMinSize(fyne.NewSize(60, 60))
|
||||
}
|
||||
a.Refresh()
|
||||
}
|
||||
|
||||
func (a *albumCover) center() fyne.Position {
|
||||
return fyne.NewPos(a.Size().Width/2, a.Size().Height/2)
|
||||
}
|
||||
|
||||
func (a *albumCover) SetImage(im image.Image) {
|
||||
a.Im.Resource = nil
|
||||
a.Im.Image = im
|
||||
a.Refresh()
|
||||
}
|
||||
|
||||
func (a *albumCover) SetImageResource(res *fyne.StaticResource) {
|
||||
a.Im.Image = nil
|
||||
a.Im.Resource = res
|
||||
a.Refresh()
|
||||
}
|
||||
|
||||
func isInside(origin fyne.Position, radius float32, point fyne.Position) bool {
|
||||
x, y := (point.X - origin.X), (point.Y - origin.Y)
|
||||
return x*x+y*y <= radius*radius
|
||||
}
|
||||
|
||||
type AlbumCard struct {
|
||||
widget.BaseWidget
|
||||
|
||||
albumID string
|
||||
artistID string
|
||||
title *CustomHyperlink
|
||||
artist *CustomHyperlink
|
||||
year *widget.Label
|
||||
container *fyne.Container
|
||||
|
||||
showYear bool
|
||||
|
||||
// updated by AlbumGrid
|
||||
Cover *albumCover
|
||||
|
||||
// these fields are used by AlbumGrid to track async update tasks
|
||||
PrevAlbumID string
|
||||
ImgLoadCancel context.CancelFunc
|
||||
|
||||
OnPlay func()
|
||||
OnShowAlbumPage func()
|
||||
OnShowArtistPage func()
|
||||
}
|
||||
|
||||
func NewAlbumCard(showYear bool) *AlbumCard {
|
||||
a := &AlbumCard{
|
||||
title: NewCustomHyperlink(),
|
||||
artist: NewCustomHyperlink(),
|
||||
year: widget.NewLabel(""),
|
||||
Cover: newAlbumCover(),
|
||||
showYear: showYear,
|
||||
}
|
||||
a.ExtendBaseWidget(a)
|
||||
a.Cover.OnDoubleTapped = func() {
|
||||
if a.OnPlay != nil {
|
||||
a.OnPlay()
|
||||
}
|
||||
}
|
||||
showAlbumFn := func() {
|
||||
if a.OnShowAlbumPage != nil {
|
||||
a.OnShowAlbumPage()
|
||||
}
|
||||
}
|
||||
a.Cover.OnTapped = showAlbumFn
|
||||
a.title.OnTapped = showAlbumFn
|
||||
a.artist.OnTapped = func() {
|
||||
if a.OnShowArtistPage != nil {
|
||||
a.OnShowArtistPage()
|
||||
}
|
||||
}
|
||||
|
||||
a.createContainer()
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *AlbumCard) createContainer() {
|
||||
var secondLabel fyne.Widget = a.artist
|
||||
if a.showYear {
|
||||
secondLabel = a.year
|
||||
}
|
||||
info := container.New(&layouts.VboxCustomPadding{ExtraPad: -16}, a.title, secondLabel)
|
||||
c := container.New(&layouts.VboxCustomPadding{ExtraPad: -5}, a.Cover, info)
|
||||
pad := &layouts.CenterPadLayout{PadLeftRight: 20, PadTopBottom: 10}
|
||||
a.container = container.New(pad, c)
|
||||
}
|
||||
|
||||
func (a *AlbumCard) Update(al *subsonic.AlbumID3) {
|
||||
a.title.SetText(al.Name)
|
||||
a.artist.SetText(al.Artist)
|
||||
a.year.SetText(strconv.Itoa(al.Year))
|
||||
a.albumID = al.ID
|
||||
a.artistID = al.ArtistID
|
||||
a.Cover.playbtn.Hidden = true
|
||||
}
|
||||
|
||||
func (a *AlbumCard) AlbumID() string {
|
||||
return a.albumID
|
||||
}
|
||||
|
||||
func (a *AlbumCard) ArtistID() string {
|
||||
return a.artistID
|
||||
}
|
||||
|
||||
func (a *AlbumCard) CreateRenderer() fyne.WidgetRenderer {
|
||||
return widget.NewSimpleRenderer(a.container)
|
||||
}
|
||||
@@ -1,230 +0,0 @@
|
||||
package widgets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"image"
|
||||
"log"
|
||||
"supersonic/backend"
|
||||
"supersonic/res"
|
||||
"sync"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
"github.com/dweymouth/go-subsonic/subsonic"
|
||||
)
|
||||
|
||||
const albumFetchBatchSize = 6
|
||||
|
||||
type ImageFetcher interface {
|
||||
GetAlbumThumbnailFromCache(string) (image.Image, bool)
|
||||
GetAlbumThumbnail(string) (image.Image, error)
|
||||
}
|
||||
|
||||
type AlbumGrid struct {
|
||||
widget.BaseWidget
|
||||
|
||||
AlbumGridState
|
||||
|
||||
grid *widget.GridWrapList
|
||||
}
|
||||
|
||||
type AlbumGridState struct {
|
||||
albums []*subsonic.AlbumID3
|
||||
albumsMutex sync.RWMutex
|
||||
iter *backend.BatchingIterator
|
||||
highestShown int
|
||||
fetching bool
|
||||
done bool
|
||||
showYear bool
|
||||
|
||||
imageFetcher ImageFetcher
|
||||
OnPlayAlbum func(string)
|
||||
OnShowAlbumPage func(string)
|
||||
OnShowArtistPage func(string)
|
||||
|
||||
scrollPos float32
|
||||
}
|
||||
|
||||
var _ fyne.Widget = (*AlbumGrid)(nil)
|
||||
|
||||
func NewFixedAlbumGrid(albums []*subsonic.AlbumID3, fetch ImageFetcher, showYear bool) *AlbumGrid {
|
||||
ag := &AlbumGrid{
|
||||
AlbumGridState: AlbumGridState{
|
||||
albums: albums,
|
||||
done: true,
|
||||
imageFetcher: fetch,
|
||||
showYear: showYear,
|
||||
},
|
||||
}
|
||||
ag.ExtendBaseWidget(ag)
|
||||
ag.createGridWrapList()
|
||||
return ag
|
||||
}
|
||||
|
||||
func NewAlbumGrid(iter backend.AlbumIterator, fetch ImageFetcher, showYear bool) *AlbumGrid {
|
||||
ag := &AlbumGrid{
|
||||
AlbumGridState: AlbumGridState{
|
||||
iter: backend.NewBatchingIterator(iter),
|
||||
imageFetcher: fetch,
|
||||
},
|
||||
}
|
||||
ag.ExtendBaseWidget(ag)
|
||||
|
||||
ag.createGridWrapList()
|
||||
|
||||
// fetch initial albums
|
||||
ag.fetchMoreAlbums(36)
|
||||
return ag
|
||||
}
|
||||
|
||||
func (ag *AlbumGrid) SaveToState() AlbumGridState {
|
||||
s := ag.AlbumGridState
|
||||
s.scrollPos = ag.grid.GetScrollOffset()
|
||||
return s
|
||||
}
|
||||
|
||||
func NewAlbumGridFromState(state AlbumGridState) *AlbumGrid {
|
||||
ag := &AlbumGrid{AlbumGridState: state}
|
||||
ag.ExtendBaseWidget(ag)
|
||||
ag.createGridWrapList()
|
||||
ag.Refresh() // needed to initialize the widget
|
||||
ag.grid.ScrollToOffset(state.scrollPos)
|
||||
return ag
|
||||
}
|
||||
|
||||
func (ag *AlbumGrid) Clear() {
|
||||
ag.albumsMutex.Lock()
|
||||
defer ag.albumsMutex.Unlock()
|
||||
ag.albums = nil
|
||||
ag.done = true
|
||||
}
|
||||
|
||||
func (ag *AlbumGrid) Reset(iter backend.AlbumIterator) {
|
||||
ag.albumsMutex.Lock()
|
||||
ag.albums = nil
|
||||
ag.albumsMutex.Unlock()
|
||||
ag.fetching = false
|
||||
ag.done = false
|
||||
ag.highestShown = 0
|
||||
ag.iter = backend.NewBatchingIterator(iter)
|
||||
ag.fetchMoreAlbums(36)
|
||||
}
|
||||
|
||||
func (ag *AlbumGrid) createGridWrapList() {
|
||||
g := widget.NewGridWrapList(
|
||||
func() int {
|
||||
return ag.lenAlbums()
|
||||
},
|
||||
// create func
|
||||
func() fyne.CanvasObject {
|
||||
ac := NewAlbumCard(ag.showYear)
|
||||
ac.OnPlay = func() {
|
||||
if ag.OnPlayAlbum != nil {
|
||||
ag.OnPlayAlbum(ac.AlbumID())
|
||||
}
|
||||
}
|
||||
ac.OnShowArtistPage = func() {
|
||||
if ag.OnShowArtistPage != nil {
|
||||
ag.OnShowArtistPage(ac.ArtistID())
|
||||
}
|
||||
}
|
||||
ac.OnShowAlbumPage = func() {
|
||||
if ag.OnShowAlbumPage != nil {
|
||||
ag.OnShowAlbumPage(ac.AlbumID())
|
||||
}
|
||||
}
|
||||
return ac
|
||||
},
|
||||
// update func
|
||||
func(itemID int, obj fyne.CanvasObject) {
|
||||
ac := obj.(*AlbumCard)
|
||||
ag.doUpdateAlbumCard(itemID, ac)
|
||||
},
|
||||
)
|
||||
ag.grid = g
|
||||
}
|
||||
|
||||
func (ag *AlbumGrid) doUpdateAlbumCard(albumIdx int, ac *AlbumCard) {
|
||||
if albumIdx > ag.highestShown {
|
||||
ag.highestShown = albumIdx
|
||||
}
|
||||
ag.albumsMutex.RLock()
|
||||
album := ag.albums[albumIdx]
|
||||
ag.albumsMutex.RUnlock()
|
||||
if ac.PrevAlbumID == album.ID {
|
||||
// nothing to do
|
||||
return
|
||||
}
|
||||
ac.Update(album)
|
||||
ac.PrevAlbumID = album.ID
|
||||
// cancel any previous image fetch
|
||||
if ac.ImgLoadCancel != nil {
|
||||
ac.ImgLoadCancel()
|
||||
ac.ImgLoadCancel = nil
|
||||
}
|
||||
if img, ok := ag.imageFetcher.GetAlbumThumbnailFromCache(album.CoverArt); ok {
|
||||
ac.Cover.SetImage(img)
|
||||
} else {
|
||||
ac.Cover.SetImageResource(res.ResAlbumplaceholderPng)
|
||||
// asynchronously fetch cover image
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ac.ImgLoadCancel = cancel
|
||||
go func(ctx context.Context) {
|
||||
i, err := ag.imageFetcher.GetAlbumThumbnail(album.CoverArt)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
if err == nil {
|
||||
ac.Cover.SetImage(i)
|
||||
} else {
|
||||
log.Printf("error fetching image: %s", err.Error())
|
||||
}
|
||||
}
|
||||
}(ctx)
|
||||
}
|
||||
|
||||
// if user has scrolled near the bottom, fetch more
|
||||
if !ag.done && !ag.fetching && albumIdx > ag.lenAlbums()-10 {
|
||||
ag.fetchMoreAlbums(20)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AlbumGrid) lenAlbums() int {
|
||||
a.albumsMutex.RLock()
|
||||
defer a.albumsMutex.RUnlock()
|
||||
return len(a.albums)
|
||||
}
|
||||
|
||||
// fetches at least count more albums
|
||||
func (a *AlbumGrid) fetchMoreAlbums(count int) {
|
||||
if a.iter == nil {
|
||||
a.done = true
|
||||
}
|
||||
a.fetching = true
|
||||
go func() {
|
||||
// keep repeating the fetch task as long as the user
|
||||
// has scrolled near the bottom
|
||||
for !a.done && a.highestShown >= a.lenAlbums()-10 {
|
||||
n := 0
|
||||
for !a.done && n < count {
|
||||
albums := a.iter.NextN(albumFetchBatchSize)
|
||||
a.albumsMutex.Lock()
|
||||
a.albums = append(a.albums, albums...)
|
||||
a.albumsMutex.Unlock()
|
||||
if len(albums) < albumFetchBatchSize {
|
||||
a.done = true
|
||||
}
|
||||
n += len(albums)
|
||||
if len(albums) > 0 {
|
||||
a.Refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
a.fetching = false
|
||||
}()
|
||||
}
|
||||
|
||||
func (a *AlbumGrid) CreateRenderer() fyne.WidgetRenderer {
|
||||
return widget.NewSimpleRenderer(a.grid)
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
package widgets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"image"
|
||||
"log"
|
||||
"supersonic/backend"
|
||||
"supersonic/res"
|
||||
"sync"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
"github.com/dweymouth/go-subsonic/subsonic"
|
||||
)
|
||||
|
||||
const albumFetchBatchSize = 6
|
||||
|
||||
type ImageFetcher interface {
|
||||
GetCoverThumbnailFromCache(string) (image.Image, bool)
|
||||
GetCoverThumbnail(string) (image.Image, error)
|
||||
}
|
||||
|
||||
type GridView struct {
|
||||
widget.BaseWidget
|
||||
|
||||
GridViewState
|
||||
|
||||
grid *widget.GridWrapList
|
||||
}
|
||||
|
||||
type GridViewState struct {
|
||||
items []*subsonic.AlbumID3
|
||||
itemsMutex sync.RWMutex
|
||||
iter *backend.BatchingIterator
|
||||
highestShown int
|
||||
fetching bool
|
||||
done bool
|
||||
showYear bool
|
||||
|
||||
imageFetcher ImageFetcher
|
||||
OnPlay func(string)
|
||||
OnShowItemPage func(string)
|
||||
OnShowSecondaryPage func(string)
|
||||
|
||||
scrollPos float32
|
||||
}
|
||||
|
||||
var _ fyne.Widget = (*GridView)(nil)
|
||||
|
||||
func NewFixedAlbumGrid(albums []*subsonic.AlbumID3, fetch ImageFetcher, showYear bool) *GridView {
|
||||
g := &GridView{
|
||||
GridViewState: GridViewState{
|
||||
items: albums,
|
||||
done: true,
|
||||
imageFetcher: fetch,
|
||||
showYear: showYear,
|
||||
},
|
||||
}
|
||||
g.ExtendBaseWidget(g)
|
||||
g.createGridWrapList()
|
||||
return g
|
||||
}
|
||||
|
||||
func NewAlbumGrid(iter backend.AlbumIterator, fetch ImageFetcher, showYear bool) *GridView {
|
||||
g := &GridView{
|
||||
GridViewState: GridViewState{
|
||||
iter: backend.NewBatchingIterator(iter),
|
||||
imageFetcher: fetch,
|
||||
},
|
||||
}
|
||||
g.ExtendBaseWidget(g)
|
||||
|
||||
g.createGridWrapList()
|
||||
|
||||
// fetch initial items
|
||||
g.fetchMoreItems(36)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GridView) SaveToState() GridViewState {
|
||||
s := g.GridViewState
|
||||
s.scrollPos = g.grid.GetScrollOffset()
|
||||
return s
|
||||
}
|
||||
|
||||
func NewGridViewFromState(state GridViewState) *GridView {
|
||||
g := &GridView{GridViewState: state}
|
||||
g.ExtendBaseWidget(g)
|
||||
g.createGridWrapList()
|
||||
g.Refresh() // needed to initialize the widget
|
||||
g.grid.ScrollToOffset(state.scrollPos)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GridView) Clear() {
|
||||
g.itemsMutex.Lock()
|
||||
defer g.itemsMutex.Unlock()
|
||||
g.items = nil
|
||||
g.done = true
|
||||
}
|
||||
|
||||
func (g *GridView) Reset(iter backend.AlbumIterator) {
|
||||
g.itemsMutex.Lock()
|
||||
g.items = nil
|
||||
g.itemsMutex.Unlock()
|
||||
g.fetching = false
|
||||
g.done = false
|
||||
g.highestShown = 0
|
||||
g.iter = backend.NewBatchingIterator(iter)
|
||||
g.fetchMoreItems(36)
|
||||
}
|
||||
|
||||
func (g *GridView) createGridWrapList() {
|
||||
g.grid = widget.NewGridWrapList(
|
||||
func() int {
|
||||
return g.lenItems()
|
||||
},
|
||||
// create func
|
||||
func() fyne.CanvasObject {
|
||||
card := NewGridViewCard(g.showYear)
|
||||
card.OnPlay = func() {
|
||||
if g.OnPlay != nil {
|
||||
g.OnPlay(card.ItemID())
|
||||
}
|
||||
}
|
||||
card.OnShowSecondaryPage = func() {
|
||||
if g.OnShowSecondaryPage != nil {
|
||||
g.OnShowSecondaryPage(card.SecondaryID())
|
||||
}
|
||||
}
|
||||
card.OnShowItemPage = func() {
|
||||
if g.OnShowItemPage != nil {
|
||||
g.OnShowItemPage(card.ItemID())
|
||||
}
|
||||
}
|
||||
return card
|
||||
},
|
||||
// update func
|
||||
func(itemID int, obj fyne.CanvasObject) {
|
||||
ac := obj.(*GridViewCard)
|
||||
g.doUpdateItemCard(itemID, ac)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (g *GridView) doUpdateItemCard(itemIdx int, card *GridViewCard) {
|
||||
if itemIdx > g.highestShown {
|
||||
g.highestShown = itemIdx
|
||||
}
|
||||
g.itemsMutex.RLock()
|
||||
album := g.items[itemIdx]
|
||||
g.itemsMutex.RUnlock()
|
||||
if card.PrevID == album.ID {
|
||||
// nothing to do
|
||||
return
|
||||
}
|
||||
card.Update(album)
|
||||
card.PrevID = album.ID
|
||||
// cancel any previous image fetch
|
||||
if card.ImgLoadCancel != nil {
|
||||
card.ImgLoadCancel()
|
||||
card.ImgLoadCancel = nil
|
||||
}
|
||||
if img, ok := g.imageFetcher.GetCoverThumbnailFromCache(album.CoverArt); ok {
|
||||
card.Cover.SetImage(img)
|
||||
} else {
|
||||
card.Cover.SetImageResource(res.ResAlbumplaceholderPng)
|
||||
// asynchronously fetch cover image
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
card.ImgLoadCancel = cancel
|
||||
go func(ctx context.Context) {
|
||||
i, err := g.imageFetcher.GetCoverThumbnail(album.CoverArt)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
if err == nil {
|
||||
card.Cover.SetImage(i)
|
||||
} else {
|
||||
log.Printf("error fetching image: %s", err.Error())
|
||||
}
|
||||
}
|
||||
}(ctx)
|
||||
}
|
||||
|
||||
// if user has scrolled near the bottom, fetch more
|
||||
if !g.done && !g.fetching && itemIdx > g.lenItems()-10 {
|
||||
g.fetchMoreItems(20)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GridView) lenItems() int {
|
||||
g.itemsMutex.RLock()
|
||||
defer g.itemsMutex.RUnlock()
|
||||
return len(g.items)
|
||||
}
|
||||
|
||||
// fetches at least count more items
|
||||
func (g *GridView) fetchMoreItems(count int) {
|
||||
if g.iter == nil {
|
||||
g.done = true
|
||||
}
|
||||
g.fetching = true
|
||||
go func() {
|
||||
// keep repeating the fetch task as long as the user
|
||||
// has scrolled near the bottom
|
||||
for !g.done && g.highestShown >= g.lenItems()-10 {
|
||||
n := 0
|
||||
for !g.done && n < count {
|
||||
albums := g.iter.NextN(albumFetchBatchSize)
|
||||
g.itemsMutex.Lock()
|
||||
g.items = append(g.items, albums...)
|
||||
g.itemsMutex.Unlock()
|
||||
if len(albums) < albumFetchBatchSize {
|
||||
g.done = true
|
||||
}
|
||||
n += len(albums)
|
||||
if len(albums) > 0 {
|
||||
g.Refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
g.fetching = false
|
||||
}()
|
||||
}
|
||||
|
||||
func (g *GridView) CreateRenderer() fyne.WidgetRenderer {
|
||||
return widget.NewSimpleRenderer(g.grid)
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package widgets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"image"
|
||||
"strconv"
|
||||
|
||||
"supersonic/res"
|
||||
"supersonic/ui/layouts"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/canvas"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/driver/desktop"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
|
||||
"github.com/dweymouth/go-subsonic/subsonic"
|
||||
)
|
||||
|
||||
var _ fyne.Widget = (*GridViewCard)(nil)
|
||||
|
||||
var _ fyne.Widget = (*coverImage)(nil)
|
||||
var _ fyne.Tappable = (*coverImage)(nil)
|
||||
|
||||
type coverImage struct {
|
||||
widget.BaseWidget
|
||||
|
||||
Im *canvas.Image
|
||||
playbtn *canvas.Image
|
||||
OnDoubleTapped func()
|
||||
OnTapped func()
|
||||
}
|
||||
|
||||
func newCoverImage() *coverImage {
|
||||
c := &coverImage{}
|
||||
c.ExtendBaseWidget(c)
|
||||
c.Im = &canvas.Image{FillMode: canvas.ImageFillContain, ScaleMode: canvas.ImageScaleFastest}
|
||||
c.Im.SetMinSize(fyne.NewSize(200, 200))
|
||||
c.playbtn = &canvas.Image{FillMode: canvas.ImageFillContain, Resource: res.ResPlaybuttonPng}
|
||||
c.playbtn.SetMinSize(fyne.NewSize(60, 60))
|
||||
c.playbtn.Hidden = true
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *coverImage) CreateRenderer() fyne.WidgetRenderer {
|
||||
return widget.NewSimpleRenderer(
|
||||
container.NewMax(c.Im, container.NewCenter(c.playbtn)),
|
||||
)
|
||||
}
|
||||
|
||||
func (c *coverImage) Cursor() desktop.Cursor {
|
||||
return desktop.PointerCursor
|
||||
}
|
||||
|
||||
func (c *coverImage) Tapped(e *fyne.PointEvent) {
|
||||
if isInside(c.center(), c.playbtn.Size().Height/2, e.Position) {
|
||||
if c.OnDoubleTapped != nil {
|
||||
c.OnDoubleTapped()
|
||||
}
|
||||
return
|
||||
}
|
||||
if c.OnTapped != nil {
|
||||
c.OnTapped()
|
||||
}
|
||||
}
|
||||
|
||||
func (a *coverImage) MouseIn(*desktop.MouseEvent) {
|
||||
a.playbtn.Hidden = false
|
||||
a.Refresh()
|
||||
}
|
||||
|
||||
func (a *coverImage) MouseOut() {
|
||||
a.playbtn.Hidden = true
|
||||
a.Refresh()
|
||||
}
|
||||
|
||||
func (a *coverImage) MouseMoved(e *desktop.MouseEvent) {
|
||||
if isInside(a.center(), a.playbtn.MinSize().Height/2, e.Position) {
|
||||
a.playbtn.SetMinSize(fyne.NewSize(65, 65))
|
||||
} else {
|
||||
a.playbtn.SetMinSize(fyne.NewSize(60, 60))
|
||||
}
|
||||
a.Refresh()
|
||||
}
|
||||
|
||||
func (a *coverImage) center() fyne.Position {
|
||||
return fyne.NewPos(a.Size().Width/2, a.Size().Height/2)
|
||||
}
|
||||
|
||||
func (a *coverImage) SetImage(im image.Image) {
|
||||
a.Im.Resource = nil
|
||||
a.Im.Image = im
|
||||
a.Refresh()
|
||||
}
|
||||
|
||||
func (a *coverImage) SetImageResource(res *fyne.StaticResource) {
|
||||
a.Im.Image = nil
|
||||
a.Im.Resource = res
|
||||
a.Refresh()
|
||||
}
|
||||
|
||||
func isInside(origin fyne.Position, radius float32, point fyne.Position) bool {
|
||||
x, y := (point.X - origin.X), (point.Y - origin.Y)
|
||||
return x*x+y*y <= radius*radius
|
||||
}
|
||||
|
||||
type GridViewCardModel struct {
|
||||
Name string
|
||||
ID string
|
||||
CoverArtID string
|
||||
Secondary string
|
||||
SecondaryID string
|
||||
}
|
||||
|
||||
type GridViewCard struct {
|
||||
widget.BaseWidget
|
||||
|
||||
albumID string
|
||||
artistID string
|
||||
title *CustomHyperlink
|
||||
artist *CustomHyperlink
|
||||
year *widget.Label
|
||||
container *fyne.Container
|
||||
|
||||
showYear bool
|
||||
|
||||
// updated by GridView
|
||||
Cover *coverImage
|
||||
|
||||
// these fields are used by GridView to track async update tasks
|
||||
PrevID string
|
||||
ImgLoadCancel context.CancelFunc
|
||||
|
||||
OnPlay func()
|
||||
OnShowItemPage func()
|
||||
OnShowSecondaryPage func()
|
||||
}
|
||||
|
||||
func NewGridViewCard(showYear bool) *GridViewCard {
|
||||
g := &GridViewCard{
|
||||
title: NewCustomHyperlink(),
|
||||
artist: NewCustomHyperlink(),
|
||||
year: widget.NewLabel(""),
|
||||
Cover: newCoverImage(),
|
||||
showYear: showYear,
|
||||
}
|
||||
g.ExtendBaseWidget(g)
|
||||
g.Cover.OnDoubleTapped = func() {
|
||||
if g.OnPlay != nil {
|
||||
g.OnPlay()
|
||||
}
|
||||
}
|
||||
showItemFn := func() {
|
||||
if g.OnShowItemPage != nil {
|
||||
g.OnShowItemPage()
|
||||
}
|
||||
}
|
||||
g.Cover.OnTapped = showItemFn
|
||||
g.title.OnTapped = showItemFn
|
||||
g.artist.OnTapped = func() {
|
||||
if g.OnShowSecondaryPage != nil {
|
||||
g.OnShowSecondaryPage()
|
||||
}
|
||||
}
|
||||
|
||||
g.createContainer()
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GridViewCard) createContainer() {
|
||||
var secondLabel fyne.Widget = g.artist
|
||||
if g.showYear {
|
||||
secondLabel = g.year
|
||||
}
|
||||
info := container.New(&layouts.VboxCustomPadding{ExtraPad: -16}, g.title, secondLabel)
|
||||
c := container.New(&layouts.VboxCustomPadding{ExtraPad: -5}, g.Cover, info)
|
||||
pad := &layouts.CenterPadLayout{PadLeftRight: 20, PadTopBottom: 10}
|
||||
g.container = container.New(pad, c)
|
||||
}
|
||||
|
||||
func (g *GridViewCard) Update(al *subsonic.AlbumID3) {
|
||||
g.title.SetText(al.Name)
|
||||
g.artist.SetText(al.Artist)
|
||||
g.year.SetText(strconv.Itoa(al.Year))
|
||||
g.albumID = al.ID
|
||||
g.artistID = al.ArtistID
|
||||
g.Cover.playbtn.Hidden = true
|
||||
}
|
||||
|
||||
func (g *GridViewCard) ItemID() string {
|
||||
return g.albumID
|
||||
}
|
||||
|
||||
func (g *GridViewCard) SecondaryID() string {
|
||||
return g.artistID
|
||||
}
|
||||
|
||||
func (g *GridViewCard) CreateRenderer() fyne.WidgetRenderer {
|
||||
return widget.NewSimpleRenderer(g.container)
|
||||
}
|
||||
Reference in New Issue
Block a user