diff --git a/ui/util/tracklistutil.go b/ui/util/tracklistutil.go index 675c6e9..f94049c 100644 --- a/ui/util/tracklistutil.go +++ b/ui/util/tracklistutil.go @@ -16,6 +16,24 @@ func ToTrackListModels(trs []*mediaprovider.Track) []*TrackListModel { }) } +func SelectedTrackModels(tracks []*TrackListModel) []*TrackListModel { + return sharedutil.FilterSlice(tracks, func(tm *TrackListModel) bool { + return tm.Selected + }) +} + +func SelectedTracks(tracks []*TrackListModel) []*mediaprovider.Track { + return sharedutil.MapSlice(SelectedTrackModels(tracks), func(tm *TrackListModel) *mediaprovider.Track { + return tm.Track + }) +} + +func SelectedTrackIDs(tracks []*TrackListModel) []string { + return sharedutil.MapSlice(SelectedTrackModels(tracks), func(tm *TrackListModel) string { + return tm.Track.ID + }) +} + func SelectTrack(tracks []*TrackListModel, idx int) { if tracks[idx].Selected { return diff --git a/ui/widgets/playqueuelist.go b/ui/widgets/playqueuelist.go index 81ac4cc..84989a6 100644 --- a/ui/widgets/playqueuelist.go +++ b/ui/widgets/playqueuelist.go @@ -14,6 +14,7 @@ import ( "fyne.io/fyne/v2/widget" "github.com/dweymouth/supersonic/backend" "github.com/dweymouth/supersonic/backend/mediaprovider" + "github.com/dweymouth/supersonic/sharedutil" "github.com/dweymouth/supersonic/ui/layouts" "github.com/dweymouth/supersonic/ui/os" myTheme "github.com/dweymouth/supersonic/ui/theme" @@ -25,10 +26,21 @@ const thumbnailSize = 52 type PlayQueueList struct { widget.BaseWidget - list *FocusList + DisableRating bool - OnShowArtistPage func(artistID string) - OnPlayTrackAt func(idx int) + // user action callbacks + OnAddToPlaylist func(trackIDs []string) + OnSetFavorite func(trackIDs []string, fav bool) + OnSetRating func(trackIDs []string, rating int) + OnRemoveFromQueue func(trackIDs []string) + OnDownload func(tracks []*mediaprovider.Track, downloadName string) + OnShowArtistPage func(artistID string) + OnPlayTrackAt func(idx int) + OnReorderTracks func(trackIDs []string, op sharedutil.TrackReorderOp) + + list *FocusList + menu *widget.PopUpMenu + ratingSubmenu *fyne.MenuItem nowPlayingID string colLayout *layouts.ColumnsLayout @@ -159,7 +171,79 @@ func (p *PlayQueueList) selectRange(idx int) { } func (p *PlayQueueList) onShowContextMenu(e *fyne.PointEvent, trackIdx int) { + p.selectTrack(trackIdx) + p.list.Refresh() + if p.menu == nil { + playlist := fyne.NewMenuItem("Add to playlist...", func() { + if p.OnAddToPlaylist != nil { + p.OnAddToPlaylist(p.selectedTrackIDs()) + } + }) + playlist.Icon = myTheme.PlaylistIcon + download := fyne.NewMenuItem("Download...", func() { + if p.OnDownload != nil { + p.OnDownload(p.selectedTracks(), "Selected tracks") + } + }) + download.Icon = theme.DownloadIcon() + favorite := fyne.NewMenuItem("Set favorite", func() { + if p.OnSetFavorite != nil { + p.OnSetFavorite(p.selectedTrackIDs(), true) + } + }) + favorite.Icon = myTheme.FavoriteIcon + unfavorite := fyne.NewMenuItem("Unset favorite", func() { + if p.OnSetFavorite != nil { + p.OnSetFavorite(p.selectedTrackIDs(), false) + } + }) + unfavorite.Icon = myTheme.NotFavoriteIcon + p.ratingSubmenu = util.NewRatingSubmenu(func(rating int) { + if p.OnSetRating != nil { + p.OnSetRating(p.selectedTrackIDs(), rating) + } + }) + remove := fyne.NewMenuItem("Remove from queue", func() { + if p.OnRemoveFromQueue != nil { + p.OnRemoveFromQueue(p.selectedTrackIDs()) + } + }) + remove.Icon = theme.ContentRemoveIcon() + reorder := util.NewReorderTracksSubmenu(func(tro sharedutil.TrackReorderOp) { + if p.OnReorderTracks != nil { + p.OnReorderTracks(p.selectedTrackIDs(), tro) + } + }) + p.menu = widget.NewPopUpMenu( + fyne.NewMenu("", + playlist, + download, + fyne.NewMenuItemSeparator(), + favorite, + unfavorite, + p.ratingSubmenu, + fyne.NewMenuItemSeparator(), + reorder, + remove, + ), + fyne.CurrentApp().Driver().CanvasForObject(p), + ) + } + p.ratingSubmenu.Disabled = p.DisableRating + p.menu.ShowAtPosition(e.AbsolutePosition) +} + +func (t *PlayQueueList) selectedTracks() []*mediaprovider.Track { + t.tracksMutex.RLock() + defer t.tracksMutex.RUnlock() + return util.SelectedTracks(t.tracks) +} + +func (t *PlayQueueList) selectedTrackIDs() []string { + t.tracksMutex.RLock() + defer t.tracksMutex.RUnlock() + return util.SelectedTrackIDs(t.tracks) } func (p *PlayQueueList) CreateRenderer() fyne.WidgetRenderer { diff --git a/ui/widgets/tracklist.go b/ui/widgets/tracklist.go index 4b17503..2a78968 100644 --- a/ui/widgets/tracklist.go +++ b/ui/widgets/tracklist.go @@ -629,24 +629,16 @@ func (t *Tracklist) onDownload(tracks []*mediaprovider.Track, downloadName strin } } -func (t *Tracklist) selectedTrackModels() []*util.TrackListModel { +func (t *Tracklist) selectedTracks() []*mediaprovider.Track { t.tracksMutex.RLock() defer t.tracksMutex.RUnlock() - return sharedutil.FilterSlice(t.tracks, func(tm *util.TrackListModel) bool { - return tm.Selected - }) -} - -func (t *Tracklist) selectedTracks() []*mediaprovider.Track { - return sharedutil.MapSlice(t.selectedTrackModels(), func(tm *util.TrackListModel) *mediaprovider.Track { - return tm.Track - }) + return util.SelectedTracks(t.tracks) } func (t *Tracklist) SelectedTrackIDs() []string { - return sharedutil.MapSlice(t.selectedTrackModels(), func(tm *util.TrackListModel) string { - return tm.Track.ID - }) + t.tracksMutex.RLock() + defer t.tracksMutex.RUnlock() + return util.SelectedTrackIDs(t.tracks) } func (t *Tracklist) lenTracks() int {