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
}
func (p *PlaybackManager) PlayAlbum(albumID string, firstTrack int) error {
if err := p.LoadAlbum(albumID, false, false); err != nil {
func (p *PlaybackManager) PlayAlbum(albumID string, firstTrack int, shuffle bool) error {
if err := p.LoadAlbum(albumID, false, shuffle); err != nil {
return err
}
if firstTrack <= 0 {
@@ -180,8 +180,8 @@ func (p *PlaybackManager) PlayAlbum(albumID string, firstTrack int) error {
return p.player.PlayTrackAt(firstTrack)
}
func (p *PlaybackManager) PlayPlaylist(playlistID string, firstTrack int) error {
if err := p.LoadPlaylist(playlistID, false, false); err != nil {
func (p *PlaybackManager) PlayPlaylist(playlistID string, firstTrack int, shuffle bool) error {
if err := p.LoadPlaylist(playlistID, false, shuffle); err != nil {
return err
}
if firstTrack <= 0 {
+1 -1
View File
@@ -168,7 +168,7 @@ func NewAlbumPageHeader(page *AlbumPage) *AlbumPageHeader {
}
a.miscLabel = widget.NewLabel("")
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() {
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
iter := lm.AlbumsIter(backend.AlbumSortOrder(a.sortOrder.Selected))
a.grid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(iter), im)
a.grid.OnPlay = a.onPlayAlbum
a.grid.OnShowSecondaryPage = a.onShowArtistPage
a.grid.OnShowItemPage = a.onShowAlbumPage
contr.ConnectAlbumGridActions(a.grid)
a.searcher = widgets.NewSearcher()
a.searcher.OnSearched = a.OnSearched
a.createContainer(false)
@@ -182,9 +180,7 @@ func (a *AlbumsPage) Save() SavedPage {
func (a *AlbumsPage) doSearch(query string) {
if a.searchGrid == nil {
a.searchGrid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(a.lm.SearchIter(query)), a.im)
a.searchGrid.OnPlay = a.onPlayAlbum
a.searchGrid.OnShowItemPage = a.onShowAlbumPage
a.searchGrid.OnShowSecondaryPage = a.onShowArtistPage
a.contr.ConnectAlbumGridActions(a.searchGrid)
} else {
a.searchGrid.Reset(widgets.NewGridViewAlbumIterator(a.lm.SearchIter(query)))
}
@@ -192,18 +188,6 @@ func (a *AlbumsPage) doSearch(query string) {
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) {
a.cfg.SortOrder = a.sortOrder.Selected
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() {
if a.artistInfo != nil { // page loaded
for i, album := range a.artistInfo.Album {
@@ -141,10 +137,6 @@ func (a *ArtistPage) playArtistRadio() {
go a.pm.PlaySimilarSongs(a.artistID)
}
func (a *ArtistPage) onShowAlbumPage(albumID string) {
a.contr.NavigateTo(controller.AlbumRoute(albumID))
}
// should be called asynchronously
func (a *ArtistPage) load() {
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.OnPlay = a.onPlayAlbum
a.albumGrid.OnShowItemPage = a.onShowAlbumPage
a.contr.ConnectAlbumGridActions(a.albumGrid)
}
a.container.Objects[0].(*fyne.Container).Objects[0] = a.albumGrid
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.createHeader(0, "")
a.grid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(lm.StarredIter()), a.im)
a.connectGridActions()
a.contr.ConnectAlbumGridActions(a.grid)
if cfg.InitialView == "Artists" {
a.toggleBtns.SetActivatedButton(1)
a.onShowFavoriteArtists()
@@ -81,12 +81,6 @@ func (a *FavoritesPage) createHeader(activeBtnIdx int, searchText string) {
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) {
searchVbox := container.NewVBox(layout.NewSpacer(), a.searcher.Entry, layout.NewSpacer())
a.container = container.NewBorder(
@@ -106,7 +100,6 @@ func restoreFavoritesPage(saved *savedFavoritesPage) *FavoritesPage {
a.ExtendBaseWidget(a)
a.createHeader(saved.activeToggleBtn, saved.searchText)
a.grid = widgets.NewGridViewFromState(saved.gridState)
a.connectGridActions()
if saved.searchText != "" {
a.searchGrid = widgets.NewGridViewFromState(saved.searchGridState)
@@ -221,9 +214,7 @@ func (a *FavoritesPage) doSearchAlbums(query string) {
})
if a.searchGrid == nil {
a.searchGrid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(iter), a.im)
a.searchGrid.OnPlay = a.onPlayAlbum
a.searchGrid.OnShowItemPage = a.onShowAlbumPage
a.searchGrid.OnShowSecondaryPage = a.onShowArtistPage
a.contr.ConnectAlbumGridActions(a.searchGrid)
} else {
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 {
a.ExtendBaseWidget(a)
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)
iter := g.lm.GenreIter(g.genre)
g.grid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(iter), g.im)
g.grid.OnPlay = g.onPlayAlbum
g.grid.OnShowSecondaryPage = g.onShowArtistPage
g.grid.OnShowItemPage = g.onShowAlbumPage
g.contr.ConnectAlbumGridActions(g.grid)
g.searcher = widgets.NewSearcher()
g.searcher.OnSearched = g.OnSearched
g.createContainer(false)
@@ -146,18 +144,6 @@ func (g *GenrePage) SearchWidget() fyne.Focusable {
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) {
g.searchText = query
if query == "" {
@@ -177,9 +163,7 @@ func (g *GenrePage) doSearch(query string) {
})
if g.searchGrid == nil {
g.searchGrid = widgets.NewGridView(widgets.NewGridViewAlbumIterator(iter), g.im)
g.searchGrid.OnPlay = g.onPlayAlbum
g.searchGrid.OnShowItemPage = g.onShowAlbumPage
g.searchGrid.OnShowSecondaryPage = g.onShowArtistPage
g.contr.ConnectAlbumGridActions(g.searchGrid)
} else {
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) {
model := createPlaylistGridViewModel(playlists)
a.gridView = widgets.NewFixedGridView(model, a.contr.App.ImageManager)
a.gridView.OnPlay = func(id string) {
a.contr.App.PlaybackManager.PlayPlaylist(id, 0)
a.gridView.OnPlay = func(id string, shuffle bool) {
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.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() {
+24
View File
@@ -7,6 +7,7 @@ import (
"strconv"
"supersonic/backend"
"supersonic/player"
"supersonic/sharedutil"
"supersonic/ui/dialogs"
"supersonic/ui/util"
"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() {
d := dialogs.NewAddEditServerDialog("Connect to Server", nil)
pop := widget.NewModalPopUp(d, m.MainWindow.Canvas())
+18 -6
View File
@@ -58,14 +58,16 @@ type GridViewState struct {
items []GridViewItemModel
itemsMutex sync.RWMutex
iter GridViewIterator
imageFetcher ImageFetcher
highestShown int
fetching bool
done bool
imageFetcher ImageFetcher
OnPlay func(string)
OnShowItemPage func(string)
OnShowSecondaryPage func(string)
OnPlay func(id string, shuffle bool)
OnAddToQueue func(id string)
OnAddToPlaylist func(id string)
OnShowItemPage func(id string)
OnShowSecondaryPage func(id string)
scrollPos float32
}
@@ -152,9 +154,14 @@ func (g *GridView) createGridWrapList() {
// create func
func() fyne.CanvasObject {
card := NewGridViewItem()
card.OnPlay = func() {
card.OnPlay = func(shuffle bool) {
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() {
@@ -167,6 +174,11 @@ func (g *GridView) createGridWrapList() {
g.OnShowItemPage(card.ItemID())
}
}
card.OnAddToPlaylist = func() {
if g.OnAddToPlaylist != nil {
g.OnAddToPlaylist(card.ItemID())
}
}
return card
},
// update func
+53 -15
View File
@@ -18,14 +18,16 @@ var _ fyne.Widget = (*GridViewItem)(nil)
var _ fyne.Widget = (*coverImage)(nil)
var _ fyne.Tappable = (*coverImage)(nil)
var _ fyne.SecondaryTappable = (*coverImage)(nil)
type coverImage struct {
widget.BaseWidget
Im *canvas.Image
playbtn *canvas.Image
OnDoubleTapped func()
OnTapped func()
Im *canvas.Image
playbtn *canvas.Image
OnPlay func()
OnShowPage func()
OnShowContextMenu func(fyne.Position)
}
func newCoverImage() *coverImage {
@@ -51,13 +53,19 @@ func (c *coverImage) Cursor() desktop.Cursor {
func (c *coverImage) Tapped(e *fyne.PointEvent) {
if isInside(c.center(), c.playbtn.Size().Height/2, e.Position) {
if c.OnDoubleTapped != nil {
c.OnDoubleTapped()
if c.OnPlay != nil {
c.OnPlay()
}
return
}
if c.OnTapped != nil {
c.OnTapped()
if c.OnShowPage != nil {
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
primaryText *CustomHyperlink
secondaryText *CustomHyperlink
menu *widget.PopUpMenu
container *fyne.Container
// updated by GridView
@@ -125,7 +134,9 @@ type GridViewItem struct {
PrevID string
ImgLoadCancel context.CancelFunc
OnPlay func()
OnPlay func(shuffle bool)
OnAddToQueue func()
OnAddToPlaylist func()
OnShowItemPage func()
OnShowSecondaryPage func()
}
@@ -137,17 +148,14 @@ func NewGridViewItem() *GridViewItem {
Cover: newCoverImage(),
}
g.ExtendBaseWidget(g)
g.Cover.OnDoubleTapped = func() {
if g.OnPlay != nil {
g.OnPlay()
}
}
g.Cover.OnPlay = func() { g.onPlay(false) }
g.Cover.OnShowContextMenu = g.showContextMenu
showItemFn := func() {
if g.OnShowItemPage != nil {
g.OnShowItemPage()
}
}
g.Cover.OnTapped = showItemFn
g.Cover.OnShowPage = showItemFn
g.primaryText.OnTapped = showItemFn
g.secondaryText.OnTapped = func() {
if g.OnShowSecondaryPage != nil {
@@ -166,6 +174,18 @@ func (g *GridViewItem) createContainer() {
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) {
g.itemID = model.ID
g.secondaryID = model.SecondaryID
@@ -187,3 +207,21 @@ func (g *GridViewItem) SecondaryID() string {
func (g *GridViewItem) CreateRenderer() fyne.WidgetRenderer {
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()
}
}