add context menu to tracklist

This commit is contained in:
Drew Weymouth
2023-02-08 16:49:01 -08:00
parent 6963a5de8e
commit edea337e7f
5 changed files with 113 additions and 31 deletions
+4
View File
@@ -157,6 +157,10 @@ func (p *PlaybackManager) PlayPlaylist(playlistID string, firstTrack int) error
return p.player.PlayTrackAt(firstTrack)
}
func (p *PlaybackManager) PlayFromBeginning() error {
return p.player.PlayFromBeginning()
}
func (p *PlaybackManager) checkScrobble(playDur time.Duration) {
if playDur.Seconds() < 0.1 || p.curTrackTime < 0.1 {
return
+18 -2
View File
@@ -21,6 +21,7 @@ type AlbumPage struct {
albumID string
sm *backend.ServerManager
pm *backend.PlaybackManager
im *backend.ImageManager
lm *backend.LibraryManager
nav func(Route)
@@ -36,19 +37,28 @@ type AlbumPage struct {
func NewAlbumPage(
albumID string,
sm *backend.ServerManager,
pm *backend.PlaybackManager,
lm *backend.LibraryManager,
im *backend.ImageManager,
popUpProvider util.PopUpProvider,
nav func(Route),
) *AlbumPage {
a := &AlbumPage{albumID: albumID, sm: sm, lm: lm, im: im, nav: nav, popUpProvider: popUpProvider}
a := &AlbumPage{albumID: albumID, sm: sm, pm: pm, lm: lm, im: im, nav: nav, popUpProvider: popUpProvider}
a.ExtendBaseWidget(a)
a.header = NewAlbumPageHeader(a)
a.tracklist = widgets.NewTracklist(nil)
// connect tracklist actions
a.tracklist.OnPlayTrackAt = a.onPlayTrackAt
a.tracklist.OnAddToQueue = func(tracks []*subsonic.Child) { a.pm.LoadTracks(tracks, true) }
a.tracklist.OnPlaySelection = func(tracks []*subsonic.Child) {
a.pm.LoadTracks(tracks, false)
a.pm.PlayFromBeginning()
}
a.container = container.NewBorder(
container.New(&layouts.MaxPadLayout{PadLeft: 15, PadRight: 15, PadTop: 15, PadBottom: 10}, a.header),
nil, nil, nil, container.New(&layouts.MaxPadLayout{PadLeft: 15, PadRight: 15, PadBottom: 15}, a.tracklist))
a.loadAsync()
return a
}
@@ -65,6 +75,7 @@ func (a *AlbumPage) Save() SavedPage {
return &savedAlbumPage{
albumID: a.albumID,
lm: a.lm,
pm: a.pm,
im: a.im,
nav: a.nav,
popUpProvider: a.popUpProvider,
@@ -88,6 +99,10 @@ func (a *AlbumPage) Reload() {
a.loadAsync()
}
func (a *AlbumPage) Tapped(*fyne.PointEvent) {
a.tracklist.UnselectAll()
}
func (a *AlbumPage) onPlayTrackAt(tracknum int) {
if a.OnPlayAlbum != nil {
a.OnPlayAlbum(a.albumID, tracknum)
@@ -227,6 +242,7 @@ func formatMiscLabelStr(a *subsonic.AlbumID3) string {
type savedAlbumPage struct {
albumID string
lm *backend.LibraryManager
pm *backend.PlaybackManager
im *backend.ImageManager
sm *backend.ServerManager
popUpProvider util.PopUpProvider
@@ -234,5 +250,5 @@ type savedAlbumPage struct {
}
func (s *savedAlbumPage) Restore() Page {
return NewAlbumPage(s.albumID, s.sm, s.lm, s.im, s.popUpProvider, s.nav)
return NewAlbumPage(s.albumID, s.sm, s.pm, s.lm, s.im, s.popUpProvider, s.nav)
}
+11
View File
@@ -42,7 +42,14 @@ func NewPlaylistPage(
a.header = NewPlaylistPageHeader(a)
a.tracklist = widgets.NewTracklist(nil)
a.tracklist.AutoNumber = true
// connect tracklist actions
a.tracklist.OnPlayTrackAt = a.onPlayTrackAt
a.tracklist.OnAddToQueue = func(tracks []*subsonic.Child) { a.pm.LoadTracks(tracks, true) }
a.tracklist.OnPlaySelection = func(tracks []*subsonic.Child) {
a.pm.LoadTracks(tracks, false)
a.pm.PlayFromBeginning()
}
a.container = container.NewBorder(
container.New(&layouts.MaxPadLayout{PadLeft: 15, PadRight: 15, PadTop: 15, PadBottom: 10}, a.header),
nil, nil, nil, container.New(&layouts.MaxPadLayout{PadLeft: 15, PadRight: 15, PadBottom: 15}, a.tracklist))
@@ -81,6 +88,10 @@ func (a *PlaylistPage) Reload() {
a.loadAsync()
}
func (a *PlaylistPage) Tapped(*fyne.PointEvent) {
a.tracklist.UnselectAll()
}
func (a *PlaylistPage) onPlayTrackAt(tracknum int) {
a.pm.PlayPlaylist(a.playlistID, tracknum)
}
+1 -1
View File
@@ -100,7 +100,7 @@ func (p *popUpProvider) WindowSize() fyne.Size {
func (r Router) CreatePage(rte Route) Page {
switch rte.Page {
case Album:
return NewAlbumPage(rte.Arg, r.App.ServerManager, r.App.LibraryManager, r.App.ImageManager, r.pop, r.OpenRoute)
return NewAlbumPage(rte.Arg, r.App.ServerManager, r.App.PlaybackManager, r.App.LibraryManager, r.App.ImageManager, r.pop, r.OpenRoute)
case Albums:
return NewAlbumsPage("Albums", rte.Arg, r.App.LibraryManager, r.App.ImageManager, r.OpenRoute)
case Artist:
+79 -28
View File
@@ -19,23 +19,23 @@ import (
type TrackRow struct {
widget.BaseWidget
trackID string
prevTrackID string
prevIsPlaying bool
tappedAt int64 // unixMillis
// internal state
trackIdx int
trackID string
isPlaying bool
tappedAt int64 // unixMillis
num *widget.RichText
name *widget.RichText
artist *widget.RichText
dur *widget.RichText
OnTapped func()
OnDoubleTapped func()
OnTapped func()
OnDoubleTapped func()
OnTappedSecondary func(e *fyne.PointEvent, trackIdx int)
selectionRect *canvas.Rectangle
container *fyne.Container
container *fyne.Container
}
func NewTrackRow(layout *layouts.ColumnsLayout) *TrackRow {
@@ -59,11 +59,10 @@ func NewTrackRow(layout *layouts.ColumnsLayout) *TrackRow {
}
func (t *TrackRow) Update(tr *subsonic.Child, isPlaying bool, rowNum int) {
if tr.ID == t.prevTrackID && isPlaying == t.prevIsPlaying {
if tr.ID == t.trackID && isPlaying == t.isPlaying {
return
}
t.prevTrackID = t.trackID
t.prevIsPlaying = isPlaying
t.isPlaying = isPlaying
t.trackID = tr.ID
if rowNum < 0 {
@@ -102,38 +101,55 @@ func (t *TrackRow) Tapped(*fyne.PointEvent) {
}
}
func (t *TrackRow) TappedSecondary(e *fyne.PointEvent) {
if t.OnTappedSecondary != nil {
t.OnTappedSecondary(e, t.trackIdx)
}
}
type Tracklist struct {
widget.BaseWidget
Tracks []*subsonic.Child
AutoNumber bool
OnPlayTrackAt func(int)
SelectionMgr util.ListSelectionManager
Tracks []*subsonic.Child
AutoNumber bool
// user action callbacks
OnPlayTrackAt func(int)
OnPlaySelection func(tracks []*subsonic.Child)
OnAddToQueue func(trackIDs []*subsonic.Child)
OnAddToPlaylist func(trackIDs []*subsonic.Child)
selectionMgr util.ListSelectionManager
nowPlayingIdx int
colLayout *layouts.ColumnsLayout
hdr *ListHeader
list *widget.List
ctxMenu *fyne.Menu
container *fyne.Container
}
func NewTracklist(tracks []*subsonic.Child) *Tracklist {
t := &Tracklist{Tracks: tracks, nowPlayingIdx: -1}
t.ExtendBaseWidget(t)
t.SelectionMgr = util.NewListSelectionManager(func() int { return len(t.Tracks) })
t.selectionMgr = util.NewListSelectionManager(func() int { return len(t.Tracks) })
t.colLayout = layouts.NewColumnsLayout([]float32{35, -1, -1, 60})
t.hdr = NewListHeader([]ListColumn{{"#", true}, {"Title", false}, {"Artist", false}, {"Time", true}}, t.colLayout)
t.list = widget.NewList(
func() int { return len(t.Tracks) },
func() fyne.CanvasObject { return NewTrackRow(t.colLayout) },
func() fyne.CanvasObject {
tr := NewTrackRow(t.colLayout)
tr.OnTapped = func() { t.onSelectTrack(tr.trackIdx) }
tr.OnTappedSecondary = t.onShowContextMenu
tr.OnDoubleTapped = func() { t.onPlayTrackAt(tr.trackIdx) }
return tr
},
func(itemID widget.ListItemID, item fyne.CanvasObject) {
tr := item.(*TrackRow)
tr.OnTapped = func() { t.onSelectTrack(itemID) }
tr.OnDoubleTapped = func() { t.onPlayTrackAt(itemID) }
tr.selectionRect.Hidden = !t.SelectionMgr.IsSelected(itemID)
i := itemID + 1
if !t.AutoNumber {
i = -1 // signal that we want to use the track num.
tr.trackIdx = itemID
tr.selectionRect.Hidden = !t.selectionMgr.IsSelected(itemID)
i := -1 // signal that we want to display the actual track num.
if t.AutoNumber {
i = itemID + 1
}
tr.Update(t.Tracks[itemID], itemID == t.nowPlayingIdx, i)
})
@@ -152,6 +168,11 @@ func (t *Tracklist) SetNowPlaying(trackID string) {
t.list.Refresh()
}
func (t *Tracklist) UnselectAll() {
t.selectionMgr.UnselectAll()
t.Refresh()
}
func (t *Tracklist) CreateRenderer() fyne.WidgetRenderer {
return widget.NewSimpleRenderer(t.container)
}
@@ -165,14 +186,44 @@ func (t *Tracklist) onPlayTrackAt(idx int) {
func (t *Tracklist) onSelectTrack(idx int) {
if d, ok := fyne.CurrentApp().Driver().(desktop.Driver); ok {
if d.ActiveKeyModifiers()&os.ControlModifier != 0 {
t.SelectionMgr.SelectAddOrRemove(idx)
t.selectionMgr.SelectAddOrRemove(idx)
} else if (d.ActiveKeyModifiers() & fyne.KeyModifierShift) != 0 {
t.SelectionMgr.SelectRange(idx)
t.selectionMgr.SelectRange(idx)
} else {
t.SelectionMgr.Select(idx)
t.selectionMgr.Select(idx)
}
} else {
t.SelectionMgr.Select(idx)
t.selectionMgr.Select(idx)
}
t.list.Refresh()
}
func (t *Tracklist) onShowContextMenu(e *fyne.PointEvent, trackIdx int) {
t.selectionMgr.Select(trackIdx)
t.Refresh()
if t.ctxMenu == nil {
t.ctxMenu = fyne.NewMenu("",
fyne.NewMenuItem("Play", func() {
if t.OnPlaySelection != nil {
t.OnPlaySelection(t.selectedTracks())
}
}),
fyne.NewMenuItem("Add to queue", func() {
if t.OnPlaySelection != nil {
t.OnAddToQueue(t.selectedTracks())
}
}),
//fyne.NewMenuItem("Add to playlist...", func() {}),
)
}
widget.ShowPopUpMenuAtPosition(t.ctxMenu, fyne.CurrentApp().Driver().CanvasForObject(t), e.AbsolutePosition)
}
func (t *Tracklist) selectedTracks() []*subsonic.Child {
sel := t.selectionMgr.GetSelection()
tracks := make([]*subsonic.Child, 0, len(sel))
for _, idx := range sel {
tracks = append(tracks, t.Tracks[idx])
}
return tracks
}