Fix #136: add context menu to album and playlist grid views
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
@@ -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
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user