diff --git a/ui/browsing/artistpage.go b/ui/browsing/artistpage.go index feb1eb3..aaf8955 100644 --- a/ui/browsing/artistpage.go +++ b/ui/browsing/artistpage.go @@ -3,7 +3,6 @@ package browsing import ( "bytes" "image" - "image/color" "log" "strings" "supersonic/backend" @@ -13,7 +12,6 @@ import ( "supersonic/ui/widgets" "fyne.io/fyne/v2" - "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/theme" @@ -46,7 +44,9 @@ func NewArtistPage(artistID string, sm *backend.ServerManager, im *backend.Image } a.ExtendBaseWidget(a) a.header = NewArtistPageHeader(nav) - a.container = container.NewBorder(a.header, nil, nil, nil, layout.NewSpacer()) + a.container = container.NewBorder( + container.New(&layouts.MaxPadLayout{PadLeft: 15, PadRight: 15, PadTop: 15, PadBottom: 10}, a.header), + nil, nil, nil, layout.NewSpacer()) a.loadAsync() return a } @@ -124,7 +124,7 @@ type ArtistPageHeader struct { nav func(Route) artistID string - artistImageCtr *fyne.Container + artistImage *widgets.ImagePlaceholder titleDisp *widget.RichText biographyDisp *widget.RichText similarArtists *fyne.Container @@ -141,8 +141,7 @@ func NewArtistPageHeader(nav func(Route)) *ArtistPageHeader { a.titleDisp.Segments[0].(*widget.TextSegment).Style = widget.RichTextStyle{ SizeName: theme.SizeNameHeadingText, } - a.artistImageCtr = container.New(&layouts.CenterPadLayout{PadLeftRight: 10, PadTopBottom: 10}, - NewMissingArtistImage()) + a.artistImage = widgets.NewImagePlaceholder(res.ResPeopleInvertPng, 225) a.biographyDisp.Wrapping = fyne.TextWrapWord a.ExtendBaseWidget(a) a.createContainer() @@ -193,47 +192,16 @@ func (a *ArtistPageHeader) UpdateInfo(info *subsonic.ArtistInfo2) { if err != nil { return } - img := canvas.NewImageFromImage(im) - img.FillMode = canvas.ImageFillContain - img.SetMinSize(fyne.NewSize(225, 225)) - a.artistImageCtr.RemoveAll() - a.artistImageCtr.Add(img) - a.artistImageCtr.Refresh() + a.artistImage.SetImage(im) } } } func (a *ArtistPageHeader) createContainer() { - a.container = container.NewBorder(nil, nil, a.artistImageCtr, nil, + a.container = container.NewBorder(nil, nil, a.artistImage, nil, container.NewBorder(a.titleDisp, nil, nil, nil, container.NewVBox(a.biographyDisp, a.similarArtists))) } func (a *ArtistPageHeader) CreateRenderer() fyne.WidgetRenderer { return widget.NewSimpleRenderer(a.container) } - -type MissingArtistImage struct { - widget.BaseWidget - container *fyne.Container -} - -func NewMissingArtistImage() *MissingArtistImage { - m := &MissingArtistImage{} - m.ExtendBaseWidget(m) - img := canvas.NewImageFromResource(res.ResPeopleInvertPng) - img.FillMode = canvas.ImageFillContain - img.SetMinSize(fyne.NewSize(64, 64)) - rect := canvas.NewRectangle(theme.BackgroundColor()) - rect.StrokeColor = color.Black - rect.StrokeWidth = 3 - rect.SetMinSize(fyne.NewSize(225, 225)) - m.container = container.NewMax( - rect, - container.NewCenter(img), - ) - return m -} - -func (m *MissingArtistImage) CreateRenderer() fyne.WidgetRenderer { - return widget.NewSimpleRenderer(m.container) -} diff --git a/ui/browsing/playlistpage.go b/ui/browsing/playlistpage.go index 87fd7b1..010154d 100644 --- a/ui/browsing/playlistpage.go +++ b/ui/browsing/playlistpage.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "supersonic/backend" + "supersonic/res" "supersonic/ui/layouts" "supersonic/ui/util" "supersonic/ui/widgets" @@ -21,6 +22,7 @@ type PlaylistPage struct { playlistID string sm *backend.ServerManager pm *backend.PlaybackManager + im *backend.ImageManager nav func(Route) header *PlaylistPageHeader tracklist *widgets.Tracklist @@ -33,9 +35,10 @@ func NewPlaylistPage( playlistID string, sm *backend.ServerManager, pm *backend.PlaybackManager, + im *backend.ImageManager, nav func(Route), ) *PlaylistPage { - a := &PlaylistPage{playlistID: playlistID, sm: sm, pm: pm} + a := &PlaylistPage{playlistID: playlistID, sm: sm, pm: pm, im: im} a.ExtendBaseWidget(a) a.header = NewPlaylistPageHeader(a) a.tracklist = widgets.NewTracklist(nil) @@ -57,6 +60,7 @@ func (a *PlaylistPage) Save() SavedPage { playlistID: a.playlistID, sm: a.sm, pm: a.pm, + im: a.im, nav: a.nav, } } @@ -89,9 +93,10 @@ func (a *PlaylistPage) loadAsync() { log.Printf("Failed to get playlist: %s", err.Error()) return } - a.header.Update(playlist) a.tracklist.Tracks = playlist.Entry a.tracklist.SetNowPlaying(a.nowPlayingID) + a.tracklist.Refresh() + a.header.Update(playlist) }() } @@ -100,6 +105,8 @@ type PlaylistPageHeader struct { page *PlaylistPage + image *widgets.ImagePlaceholder + titleLabel *widget.RichText descriptionLabel *widget.Label createdAtLabel *widget.Label @@ -115,6 +122,7 @@ func NewPlaylistPageHeader(page *PlaylistPage) *PlaylistPageHeader { a := &PlaylistPageHeader{page: page} a.ExtendBaseWidget(a) + a.image = widgets.NewImagePlaceholder(res.ResPlaylistInvertPng, 225) a.titleLabel = widget.NewRichTextWithText("") a.titleLabel.Wrapping = fyne.TextTruncate a.titleLabel.Segments[0].(*widget.TextSegment).Style = widget.RichTextStyle{ @@ -128,12 +136,13 @@ func NewPlaylistPageHeader(page *PlaylistPage) *PlaylistPageHeader { page.onPlayTrackAt(0) }) - a.container = container.NewVBox(a.titleLabel, container.New(&layouts.VboxCustomPadding{ExtraPad: -10}, - a.descriptionLabel, - a.ownerLabel, - a.trackTimeLabel), - container.NewHBox(a.playButton), - ) + a.container = container.NewBorder(nil, nil, a.image, nil, + container.NewVBox(a.titleLabel, container.New(&layouts.VboxCustomPadding{ExtraPad: -10}, + a.descriptionLabel, + a.ownerLabel, + a.trackTimeLabel), + container.NewHBox(a.playButton), + )) return a } @@ -147,6 +156,19 @@ func (a *PlaylistPageHeader) Update(playlist *subsonic.Playlist) { a.ownerLabel.SetText(a.formatPlaylistOwnerStr(playlist)) a.trackTimeLabel.SetText(a.formatPlaylistTrackTimeStr(playlist)) a.createdAtLabel.SetText("created at TODO") + + var haveCover bool + if playlist.CoverArt != "" { + if im, err := a.page.im.GetAlbumThumbnail(playlist.CoverArt); err == nil && im != nil { + a.image.SetImage(im) + haveCover = true + } + } + if !haveCover { + if im, err := a.page.im.GetAlbumThumbnail(playlist.ID); err == nil && im != nil { + a.image.SetImage(im) + } + } a.Refresh() } @@ -166,9 +188,10 @@ type savedPlaylistPage struct { playlistID string sm *backend.ServerManager pm *backend.PlaybackManager + im *backend.ImageManager nav func(Route) } func (s *savedPlaylistPage) Restore() Page { - return NewPlaylistPage(s.playlistID, s.sm, s.pm, s.nav) + return NewPlaylistPage(s.playlistID, s.sm, s.pm, s.im, s.nav) } diff --git a/ui/browsing/router.go b/ui/browsing/router.go index 8eab236..ca7f5a0 100644 --- a/ui/browsing/router.go +++ b/ui/browsing/router.go @@ -113,7 +113,7 @@ func (r Router) CreatePage(rte Route) Page { case Genres: return NewArtistsGenresPage(true, r.App.ServerManager, r.OpenRoute) case Playlist: - return NewPlaylistPage(rte.Arg, r.App.ServerManager, r.App.PlaybackManager, r.OpenRoute) + return NewPlaylistPage(rte.Arg, r.App.ServerManager, r.App.PlaybackManager, r.App.ImageManager, r.OpenRoute) case Playlists: return NewPlaylistsPage(r.App.ServerManager, r.OpenRoute) } diff --git a/ui/widgets/imageplaceholder.go b/ui/widgets/imageplaceholder.go new file mode 100644 index 0000000..1e3a6e4 --- /dev/null +++ b/ui/widgets/imageplaceholder.go @@ -0,0 +1,48 @@ +package widgets + +import ( + "image" + "image/color" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" +) + +type ImagePlaceholder struct { + widget.BaseWidget + container *fyne.Container + minSize float32 +} + +func NewImagePlaceholder(centerIcon fyne.Resource, minSize float32) *ImagePlaceholder { + m := &ImagePlaceholder{minSize: minSize} + m.ExtendBaseWidget(m) + img := canvas.NewImageFromResource(centerIcon) + img.FillMode = canvas.ImageFillContain + img.SetMinSize(fyne.NewSize(minSize/4, minSize/4)) + rect := canvas.NewRectangle(theme.BackgroundColor()) + rect.StrokeColor = color.Black + rect.StrokeWidth = 3 + rect.SetMinSize(fyne.NewSize(minSize, minSize)) + m.container = container.NewMax( + rect, + container.NewCenter(img), + ) + 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)) + i.container.RemoveAll() + i.container.Add(cImg) + i.container.Refresh() +} + +func (i *ImagePlaceholder) CreateRenderer() fyne.WidgetRenderer { + return widget.NewSimpleRenderer(i.container) +}