Fix #136: add context menu to album and playlist grid views

This commit is contained in:
Drew Weymouth
2023-05-02 17:50:35 -07:00
parent 118b19aa13
commit 76657b5c1c
10 changed files with 122 additions and 97 deletions
+4 -4
View File
@@ -170,8 +170,8 @@ func (p *PlaybackManager) LoadTracks(tracks []*subsonic.Child, appendToQueue, sh
return nil return nil
} }
func (p *PlaybackManager) PlayAlbum(albumID string, firstTrack int) error { func (p *PlaybackManager) PlayAlbum(albumID string, firstTrack int, shuffle bool) error {
if err := p.LoadAlbum(albumID, false, false); err != nil { if err := p.LoadAlbum(albumID, false, shuffle); err != nil {
return err return err
} }
if firstTrack <= 0 { if firstTrack <= 0 {
@@ -180,8 +180,8 @@ func (p *PlaybackManager) PlayAlbum(albumID string, firstTrack int) error {
return p.player.PlayTrackAt(firstTrack) return p.player.PlayTrackAt(firstTrack)
} }
func (p *PlaybackManager) PlayPlaylist(playlistID string, firstTrack int) error { func (p *PlaybackManager) PlayPlaylist(playlistID string, firstTrack int, shuffle bool) error {
if err := p.LoadPlaylist(playlistID, false, false); err != nil { if err := p.LoadPlaylist(playlistID, false, shuffle); err != nil {
return err return err
} }
if firstTrack <= 0 { if firstTrack <= 0 {
+1 -1
View File
@@ -168,7 +168,7 @@ func NewAlbumPageHeader(page *AlbumPage) *AlbumPageHeader {
} }
a.miscLabel = widget.NewLabel("") a.miscLabel = widget.NewLabel("")
playButton := widget.NewButtonWithIcon("Play", theme.MediaPlayIcon(), func() { playButton := widget.NewButtonWithIcon("Play", theme.MediaPlayIcon(), func() {
go page.pm.PlayAlbum(page.albumID, 0) go page.pm.PlayAlbum(page.albumID, 0, false)
}) })
shuffleBtn := widget.NewButtonWithIcon(" Shuffle", res.ResShuffleInvertSvg, func() { shuffleBtn := widget.NewButtonWithIcon(" Shuffle", res.ResShuffleInvertSvg, func() {
page.pm.LoadTracks(page.tracklist.Tracks, false, true) page.pm.LoadTracks(page.tracklist.Tracks, false, true)
+2 -18
View File
@@ -73,9 +73,7 @@ func NewAlbumsPage(cfg *backend.AlbumsPageConfig, contr *controller.Controller,
a.sortOrder.Selected = cfg.SortOrder a.sortOrder.Selected = cfg.SortOrder
iter := lm.AlbumsIter(backend.AlbumSortOrder(a.sortOrder.Selected)) iter := lm.AlbumsIter(backend.AlbumSortOrder(a.sortOrder.Selected))
a.grid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(iter), im) a.grid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(iter), im)
a.grid.OnPlay = a.onPlayAlbum contr.ConnectAlbumGridActions(a.grid)
a.grid.OnShowSecondaryPage = a.onShowArtistPage
a.grid.OnShowItemPage = a.onShowAlbumPage
a.searcher = widgets.NewSearcher() a.searcher = widgets.NewSearcher()
a.searcher.OnSearched = a.OnSearched a.searcher.OnSearched = a.OnSearched
a.createContainer(false) a.createContainer(false)
@@ -182,9 +180,7 @@ func (a *AlbumsPage) Save() SavedPage {
func (a *AlbumsPage) doSearch(query string) { func (a *AlbumsPage) doSearch(query string) {
if a.searchGrid == nil { if a.searchGrid == nil {
a.searchGrid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(a.lm.SearchIter(query)), a.im) a.searchGrid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(a.lm.SearchIter(query)), a.im)
a.searchGrid.OnPlay = a.onPlayAlbum a.contr.ConnectAlbumGridActions(a.searchGrid)
a.searchGrid.OnShowItemPage = a.onShowAlbumPage
a.searchGrid.OnShowSecondaryPage = a.onShowArtistPage
} else { } else {
a.searchGrid.Reset(widgets.NewGridViewAlbumIterator(a.lm.SearchIter(query))) a.searchGrid.Reset(widgets.NewGridViewAlbumIterator(a.lm.SearchIter(query)))
} }
@@ -192,18 +188,6 @@ func (a *AlbumsPage) doSearch(query string) {
a.Refresh() a.Refresh()
} }
func (a *AlbumsPage) onPlayAlbum(albumID string) {
go a.pm.PlayAlbum(albumID, 0)
}
func (a *AlbumsPage) onShowArtistPage(artistID string) {
a.contr.NavigateTo(controller.ArtistRoute(artistID))
}
func (a *AlbumsPage) onShowAlbumPage(albumID string) {
a.contr.NavigateTo(controller.AlbumRoute(albumID))
}
func (a *AlbumsPage) onSortOrderChanged(order string) { func (a *AlbumsPage) onSortOrderChanged(order string) {
a.cfg.SortOrder = a.sortOrder.Selected a.cfg.SortOrder = a.sortOrder.Selected
iter := a.lm.AlbumsIter(backend.AlbumSortOrder(order)) iter := a.lm.AlbumsIter(backend.AlbumSortOrder(order))
+1 -10
View File
@@ -124,10 +124,6 @@ func (a *ArtistPage) OnSongChange(track *subsonic.Child, lastScrobbledIfAny *sub
} }
} }
func (a *ArtistPage) onPlayAlbum(albumID string) {
a.pm.PlayAlbum(albumID, 0)
}
func (a *ArtistPage) playAllTracks() { func (a *ArtistPage) playAllTracks() {
if a.artistInfo != nil { // page loaded if a.artistInfo != nil { // page loaded
for i, album := range a.artistInfo.Album { for i, album := range a.artistInfo.Album {
@@ -141,10 +137,6 @@ func (a *ArtistPage) playArtistRadio() {
go a.pm.PlaySimilarSongs(a.artistID) go a.pm.PlaySimilarSongs(a.artistID)
} }
func (a *ArtistPage) onShowAlbumPage(albumID string) {
a.contr.NavigateTo(controller.AlbumRoute(albumID))
}
// should be called asynchronously // should be called asynchronously
func (a *ArtistPage) load() { func (a *ArtistPage) load() {
artist, err := a.sm.Server.GetArtist(a.artistID) artist, err := a.sm.Server.GetArtist(a.artistID)
@@ -182,8 +174,7 @@ func (a *ArtistPage) showAlbumGrid() {
} }
}) })
a.albumGrid = widgets.NewFixedGridView(model, a.im) a.albumGrid = widgets.NewFixedGridView(model, a.im)
a.albumGrid.OnPlay = a.onPlayAlbum a.contr.ConnectAlbumGridActions(a.albumGrid)
a.albumGrid.OnShowItemPage = a.onShowAlbumPage
} }
a.container.Objects[0].(*fyne.Container).Objects[0] = a.albumGrid a.container.Objects[0].(*fyne.Container).Objects[0] = a.albumGrid
a.container.Objects[0].Refresh() a.container.Objects[0].Refresh()
+2 -23
View File
@@ -54,7 +54,7 @@ func NewFavoritesPage(cfg *backend.FavoritesPageConfig, contr *controller.Contro
a.ExtendBaseWidget(a) a.ExtendBaseWidget(a)
a.createHeader(0, "") a.createHeader(0, "")
a.grid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(lm.StarredIter()), a.im) a.grid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(lm.StarredIter()), a.im)
a.connectGridActions() a.contr.ConnectAlbumGridActions(a.grid)
if cfg.InitialView == "Artists" { if cfg.InitialView == "Artists" {
a.toggleBtns.SetActivatedButton(1) a.toggleBtns.SetActivatedButton(1)
a.onShowFavoriteArtists() a.onShowFavoriteArtists()
@@ -81,12 +81,6 @@ func (a *FavoritesPage) createHeader(activeBtnIdx int, searchText string) {
a.searcher.Entry.Text = searchText a.searcher.Entry.Text = searchText
} }
func (a *FavoritesPage) connectGridActions() {
a.grid.OnPlay = a.onPlayAlbum
a.grid.OnShowItemPage = a.onShowAlbumPage
a.grid.OnShowSecondaryPage = a.onShowArtistPage
}
func (a *FavoritesPage) createContainer(initialView fyne.CanvasObject) { func (a *FavoritesPage) createContainer(initialView fyne.CanvasObject) {
searchVbox := container.NewVBox(layout.NewSpacer(), a.searcher.Entry, layout.NewSpacer()) searchVbox := container.NewVBox(layout.NewSpacer(), a.searcher.Entry, layout.NewSpacer())
a.container = container.NewBorder( a.container = container.NewBorder(
@@ -106,7 +100,6 @@ func restoreFavoritesPage(saved *savedFavoritesPage) *FavoritesPage {
a.ExtendBaseWidget(a) a.ExtendBaseWidget(a)
a.createHeader(saved.activeToggleBtn, saved.searchText) a.createHeader(saved.activeToggleBtn, saved.searchText)
a.grid = widgets.NewGridViewFromState(saved.gridState) a.grid = widgets.NewGridViewFromState(saved.gridState)
a.connectGridActions()
if saved.searchText != "" { if saved.searchText != "" {
a.searchGrid = widgets.NewGridViewFromState(saved.searchGridState) a.searchGrid = widgets.NewGridViewFromState(saved.searchGridState)
@@ -221,9 +214,7 @@ func (a *FavoritesPage) doSearchAlbums(query string) {
}) })
if a.searchGrid == nil { if a.searchGrid == nil {
a.searchGrid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(iter), a.im) a.searchGrid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(iter), a.im)
a.searchGrid.OnPlay = a.onPlayAlbum a.contr.ConnectAlbumGridActions(a.searchGrid)
a.searchGrid.OnShowItemPage = a.onShowAlbumPage
a.searchGrid.OnShowSecondaryPage = a.onShowArtistPage
} else { } else {
a.searchGrid.Reset(widgets.NewGridViewAlbumIterator(iter)) a.searchGrid.Reset(widgets.NewGridViewAlbumIterator(iter))
} }
@@ -328,18 +319,6 @@ func (a *FavoritesPage) onShowFavoriteSongs() {
} }
} }
func (a *FavoritesPage) onPlayAlbum(albumID string) {
go a.pm.PlayAlbum(albumID, 0)
}
func (a *FavoritesPage) onShowAlbumPage(albumID string) {
a.contr.NavigateTo(controller.AlbumRoute(albumID))
}
func (a *FavoritesPage) onShowArtistPage(artistID string) {
a.contr.NavigateTo(controller.ArtistRoute(artistID))
}
func (a *FavoritesPage) CreateRenderer() fyne.WidgetRenderer { func (a *FavoritesPage) CreateRenderer() fyne.WidgetRenderer {
a.ExtendBaseWidget(a) a.ExtendBaseWidget(a)
return widget.NewSimpleRenderer(a.container) return widget.NewSimpleRenderer(a.container)
+2 -18
View File
@@ -53,9 +53,7 @@ func NewGenrePage(genre string, contr *controller.Controller, pm *backend.Playba
g.playRandom = widget.NewButtonWithIcon("Play random", res.ResShuffleInvertSvg, g.playRandomSongs) g.playRandom = widget.NewButtonWithIcon("Play random", res.ResShuffleInvertSvg, g.playRandomSongs)
iter := g.lm.GenreIter(g.genre) iter := g.lm.GenreIter(g.genre)
g.grid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(iter), g.im) g.grid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(iter), g.im)
g.grid.OnPlay = g.onPlayAlbum g.contr.ConnectAlbumGridActions(g.grid)
g.grid.OnShowSecondaryPage = g.onShowArtistPage
g.grid.OnShowItemPage = g.onShowAlbumPage
g.searcher = widgets.NewSearcher() g.searcher = widgets.NewSearcher()
g.searcher.OnSearched = g.OnSearched g.searcher.OnSearched = g.OnSearched
g.createContainer(false) g.createContainer(false)
@@ -146,18 +144,6 @@ func (g *GenrePage) SearchWidget() fyne.Focusable {
return g.searcher.Entry return g.searcher.Entry
} }
func (a *GenrePage) onPlayAlbum(albumID string) {
go a.pm.PlayAlbum(albumID, 0)
}
func (a *GenrePage) onShowArtistPage(artistID string) {
a.contr.NavigateTo(controller.ArtistRoute(artistID))
}
func (a *GenrePage) onShowAlbumPage(albumID string) {
a.contr.NavigateTo(controller.AlbumRoute(albumID))
}
func (g *GenrePage) OnSearched(query string) { func (g *GenrePage) OnSearched(query string) {
g.searchText = query g.searchText = query
if query == "" { if query == "" {
@@ -177,9 +163,7 @@ func (g *GenrePage) doSearch(query string) {
}) })
if g.searchGrid == nil { if g.searchGrid == nil {
g.searchGrid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(iter), g.im) g.searchGrid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(iter), g.im)
g.searchGrid.OnPlay = g.onPlayAlbum g.contr.ConnectAlbumGridActions(g.searchGrid)
g.searchGrid.OnShowItemPage = g.onShowAlbumPage
g.searchGrid.OnShowSecondaryPage = g.onShowArtistPage
} else { } else {
g.searchGrid.Reset(widgets.NewGridViewAlbumIterator(iter)) g.searchGrid.Reset(widgets.NewGridViewAlbumIterator(iter))
} }
+15 -2
View File
@@ -94,10 +94,23 @@ func (a *PlaylistsPage) createListView() {
func (a *PlaylistsPage) createGridView(playlists []*subsonic.Playlist) { func (a *PlaylistsPage) createGridView(playlists []*subsonic.Playlist) {
model := createPlaylistGridViewModel(playlists) model := createPlaylistGridViewModel(playlists)
a.gridView = widgets.NewFixedGridView(model, a.contr.App.ImageManager) a.gridView = widgets.NewFixedGridView(model, a.contr.App.ImageManager)
a.gridView.OnPlay = func(id string) { a.gridView.OnPlay = func(id string, shuffle bool) {
a.contr.App.PlaybackManager.PlayPlaylist(id, 0) go a.contr.App.PlaybackManager.PlayPlaylist(id, 0, shuffle)
}
a.gridView.OnAddToQueue = func(id string) {
go a.contr.App.PlaybackManager.LoadPlaylist(id, true, false)
} }
a.gridView.OnShowItemPage = a.showPlaylistPage a.gridView.OnShowItemPage = a.showPlaylistPage
a.gridView.OnAddToPlaylist = func(id string) {
go func() {
pl, err := a.contr.App.ServerManager.Server.GetPlaylist(id)
if err != nil {
log.Printf("error loading playlist: %s", err.Error())
return
}
a.contr.DoAddTracksToPlaylistWorkflow(sharedutil.TracksToIDs(pl.Entry))
}()
}
} }
func (a *PlaylistsPage) showListView() { func (a *PlaylistsPage) showListView() {
+24
View File
@@ -7,6 +7,7 @@ import (
"strconv" "strconv"
"supersonic/backend" "supersonic/backend"
"supersonic/player" "supersonic/player"
"supersonic/sharedutil"
"supersonic/ui/dialogs" "supersonic/ui/dialogs"
"supersonic/ui/util" "supersonic/ui/util"
"supersonic/ui/widgets" "supersonic/ui/widgets"
@@ -125,6 +126,29 @@ func (m *Controller) ConnectTracklistActions(tracklist *widgets.Tracklist) {
} }
} }
func (m *Controller) ConnectAlbumGridActions(grid *widgets.GridView) {
grid.OnAddToQueue = func(albumID string) {
m.App.PlaybackManager.LoadAlbum(albumID, true, false)
}
grid.OnPlay = func(albumID string, shuffle bool) {
m.App.PlaybackManager.PlayAlbum(albumID, 0, shuffle)
}
grid.OnShowItemPage = func(albumID string) {
m.NavigateTo(AlbumRoute(albumID))
}
grid.OnShowSecondaryPage = func(artistID string) {
m.NavigateTo(ArtistRoute(artistID))
}
grid.OnAddToPlaylist = func(albumID string) {
album, err := m.App.ServerManager.Server.GetAlbum(albumID)
if err != nil {
log.Printf("error loading album: %s", err.Error())
return
}
m.DoAddTracksToPlaylistWorkflow(sharedutil.TracksToIDs(album.Song))
}
}
func (m *Controller) PromptForFirstServer() { func (m *Controller) PromptForFirstServer() {
d := dialogs.NewAddEditServerDialog("Connect to Server", nil) d := dialogs.NewAddEditServerDialog("Connect to Server", nil)
pop := widget.NewModalPopUp(d, m.MainWindow.Canvas()) pop := widget.NewModalPopUp(d, m.MainWindow.Canvas())
+18 -6
View File
@@ -58,14 +58,16 @@ type GridViewState struct {
items []GridViewItemModel items []GridViewItemModel
itemsMutex sync.RWMutex itemsMutex sync.RWMutex
iter GridViewIterator iter GridViewIterator
imageFetcher ImageFetcher
highestShown int highestShown int
fetching bool fetching bool
done bool done bool
imageFetcher ImageFetcher OnPlay func(id string, shuffle bool)
OnPlay func(string) OnAddToQueue func(id string)
OnShowItemPage func(string) OnAddToPlaylist func(id string)
OnShowSecondaryPage func(string) OnShowItemPage func(id string)
OnShowSecondaryPage func(id string)
scrollPos float32 scrollPos float32
} }
@@ -152,9 +154,14 @@ func (g *GridView) createGridWrapList() {
// create func // create func
func() fyne.CanvasObject { func() fyne.CanvasObject {
card := NewGridViewItem() card := NewGridViewItem()
card.OnPlay = func() { card.OnPlay = func(shuffle bool) {
if g.OnPlay != nil { if g.OnPlay != nil {
g.OnPlay(card.ItemID()) g.OnPlay(card.ItemID(), shuffle)
}
}
card.OnAddToQueue = func() {
if g.OnAddToQueue != nil {
g.OnAddToQueue(card.ItemID())
} }
} }
card.OnShowSecondaryPage = func() { card.OnShowSecondaryPage = func() {
@@ -167,6 +174,11 @@ func (g *GridView) createGridWrapList() {
g.OnShowItemPage(card.ItemID()) g.OnShowItemPage(card.ItemID())
} }
} }
card.OnAddToPlaylist = func() {
if g.OnAddToPlaylist != nil {
g.OnAddToPlaylist(card.ItemID())
}
}
return card return card
}, },
// update func // update func
+53 -15
View File
@@ -18,14 +18,16 @@ var _ fyne.Widget = (*GridViewItem)(nil)
var _ fyne.Widget = (*coverImage)(nil) var _ fyne.Widget = (*coverImage)(nil)
var _ fyne.Tappable = (*coverImage)(nil) var _ fyne.Tappable = (*coverImage)(nil)
var _ fyne.SecondaryTappable = (*coverImage)(nil)
type coverImage struct { type coverImage struct {
widget.BaseWidget widget.BaseWidget
Im *canvas.Image Im *canvas.Image
playbtn *canvas.Image playbtn *canvas.Image
OnDoubleTapped func() OnPlay func()
OnTapped func() OnShowPage func()
OnShowContextMenu func(fyne.Position)
} }
func newCoverImage() *coverImage { func newCoverImage() *coverImage {
@@ -51,13 +53,19 @@ func (c *coverImage) Cursor() desktop.Cursor {
func (c *coverImage) Tapped(e *fyne.PointEvent) { func (c *coverImage) Tapped(e *fyne.PointEvent) {
if isInside(c.center(), c.playbtn.Size().Height/2, e.Position) { if isInside(c.center(), c.playbtn.Size().Height/2, e.Position) {
if c.OnDoubleTapped != nil { if c.OnPlay != nil {
c.OnDoubleTapped() c.OnPlay()
} }
return return
} }
if c.OnTapped != nil { if c.OnShowPage != nil {
c.OnTapped() c.OnShowPage()
}
}
func (c *coverImage) TappedSecondary(e *fyne.PointEvent) {
if c.OnShowContextMenu != nil {
c.OnShowContextMenu(e.AbsolutePosition)
} }
} }
@@ -116,6 +124,7 @@ type GridViewItem struct {
secondaryID string secondaryID string
primaryText *CustomHyperlink primaryText *CustomHyperlink
secondaryText *CustomHyperlink secondaryText *CustomHyperlink
menu *widget.PopUpMenu
container *fyne.Container container *fyne.Container
// updated by GridView // updated by GridView
@@ -125,7 +134,9 @@ type GridViewItem struct {
PrevID string PrevID string
ImgLoadCancel context.CancelFunc ImgLoadCancel context.CancelFunc
OnPlay func() OnPlay func(shuffle bool)
OnAddToQueue func()
OnAddToPlaylist func()
OnShowItemPage func() OnShowItemPage func()
OnShowSecondaryPage func() OnShowSecondaryPage func()
} }
@@ -137,17 +148,14 @@ func NewGridViewItem() *GridViewItem {
Cover: newCoverImage(), Cover: newCoverImage(),
} }
g.ExtendBaseWidget(g) g.ExtendBaseWidget(g)
g.Cover.OnDoubleTapped = func() { g.Cover.OnPlay = func() { g.onPlay(false) }
if g.OnPlay != nil { g.Cover.OnShowContextMenu = g.showContextMenu
g.OnPlay()
}
}
showItemFn := func() { showItemFn := func() {
if g.OnShowItemPage != nil { if g.OnShowItemPage != nil {
g.OnShowItemPage() g.OnShowItemPage()
} }
} }
g.Cover.OnTapped = showItemFn g.Cover.OnShowPage = showItemFn
g.primaryText.OnTapped = showItemFn g.primaryText.OnTapped = showItemFn
g.secondaryText.OnTapped = func() { g.secondaryText.OnTapped = func() {
if g.OnShowSecondaryPage != nil { if g.OnShowSecondaryPage != nil {
@@ -166,6 +174,18 @@ func (g *GridViewItem) createContainer() {
g.container = container.New(pad, c) g.container = container.New(pad, c)
} }
func (g *GridViewItem) showContextMenu(pos fyne.Position) {
if g.menu == nil {
g.menu = widget.NewPopUpMenu(fyne.NewMenu("",
fyne.NewMenuItem("Play", func() { g.onPlay(false) }),
fyne.NewMenuItem("Shuffle", func() { g.onPlay(true) }),
fyne.NewMenuItem("Add to queue", g.onAddToQueue),
fyne.NewMenuItem("Add to playlist...", g.onAddToPlaylist)),
fyne.CurrentApp().Driver().CanvasForObject(g))
}
g.menu.ShowAtPosition(pos)
}
func (g *GridViewItem) Update(model GridViewItemModel) { func (g *GridViewItem) Update(model GridViewItemModel) {
g.itemID = model.ID g.itemID = model.ID
g.secondaryID = model.SecondaryID g.secondaryID = model.SecondaryID
@@ -187,3 +207,21 @@ func (g *GridViewItem) SecondaryID() string {
func (g *GridViewItem) CreateRenderer() fyne.WidgetRenderer { func (g *GridViewItem) CreateRenderer() fyne.WidgetRenderer {
return widget.NewSimpleRenderer(g.container) return widget.NewSimpleRenderer(g.container)
} }
func (g *GridViewItem) onPlay(shuffle bool) {
if g.OnPlay != nil {
g.OnPlay(shuffle)
}
}
func (g *GridViewItem) onAddToQueue() {
if g.OnAddToQueue != nil {
g.OnAddToQueue()
}
}
func (g *GridViewItem) onAddToPlaylist() {
if g.OnAddToPlaylist != nil {
g.OnAddToPlaylist()
}
}