From a75222d381332623a55b48c863e769efcc74a7a5 Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Sat, 2 Mar 2024 13:34:09 -0800 Subject: [PATCH] more work --- ui/util/tracklistutil.go | 11 ++- ui/widgets/playqueuelist.go | 153 ++++++++++++++++++++++++++++++++++++ ui/widgets/tracklist.go | 10 +-- 3 files changed, 165 insertions(+), 9 deletions(-) create mode 100644 ui/widgets/playqueuelist.go diff --git a/ui/util/tracklistutil.go b/ui/util/tracklistutil.go index 00f7de8..7a1b571 100644 --- a/ui/util/tracklistutil.go +++ b/ui/util/tracklistutil.go @@ -1,12 +1,21 @@ package util -import "github.com/dweymouth/supersonic/backend/mediaprovider" +import ( + "github.com/dweymouth/supersonic/backend/mediaprovider" + "github.com/dweymouth/supersonic/sharedutil" +) type TrackListModel struct { Track *mediaprovider.Track Selected bool } +func ToTrackListModels(trs []*mediaprovider.Track) []*TrackListModel { + return sharedutil.MapSlice(trs, func(tr *mediaprovider.Track) *TrackListModel { + return &TrackListModel{Track: tr, Selected: false} + }) +} + func SelectTrack(tracks []*TrackListModel, idx int) { if tracks[idx].Selected { return diff --git a/ui/widgets/playqueuelist.go b/ui/widgets/playqueuelist.go new file mode 100644 index 0000000..179a2ff --- /dev/null +++ b/ui/widgets/playqueuelist.go @@ -0,0 +1,153 @@ +package widgets + +import ( + "sync" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/driver/desktop" + "fyne.io/fyne/v2/widget" + "github.com/dweymouth/supersonic/ui/layouts" + "github.com/dweymouth/supersonic/ui/os" + "github.com/dweymouth/supersonic/ui/theme" + "github.com/dweymouth/supersonic/ui/util" +) + +type PlayQueueList struct { + FocusList + + OnShowArtistPage func(artistID string) + OnPlayTrackAt func(idx int) + + nowPlayingID string + colLayout *layouts.ColumnsLayout + + tracksMutex sync.Mutex + tracks []*util.TrackListModel +} + +func (t *PlayQueueList) onArtistTapped(artistID string) { + if t.OnShowArtistPage != nil { + t.OnShowArtistPage(artistID) + } +} + +func (p *PlayQueueList) onPlayTrackAt(idx int) { + if p.OnPlayTrackAt != nil { + p.OnPlayTrackAt(idx) + } +} + +func (p *PlayQueueList) onSelectTrack(idx int) { + if d, ok := fyne.CurrentApp().Driver().(desktop.Driver); ok { + mod := d.CurrentKeyModifiers() + if mod&os.ControlModifier != 0 { + p.selectAddOrRemove(idx) + } else if mod&fyne.KeyModifierShift != 0 { + p.selectRange(idx) + } else { + p.selectTrack(idx) + } + } else { + p.selectTrack(idx) + } + p.Refresh() +} + +func (p *PlayQueueList) selectTrack(idx int) { + p.tracksMutex.Lock() + defer p.tracksMutex.Unlock() + util.SelectTrack(p.tracks, idx) +} + +func (p *PlayQueueList) selectAddOrRemove(idx int) { + p.tracksMutex.Lock() + defer p.tracksMutex.Unlock() + p.tracks[idx].Selected = !p.tracks[idx].Selected +} + +func (p *PlayQueueList) selectRange(idx int) { + p.tracksMutex.Lock() + defer p.tracksMutex.Unlock() + util.SelectTrackRange(p.tracks, idx) +} + +type PlayQueueListRow struct { + FocusListRowBase + + playQueueList *PlayQueueList + trackID string + isPlaying bool + + playingIcon fyne.CanvasObject + num *widget.Label + cover *ImagePlaceholder + title *widget.Label + artist *MultiHyperlink + time *widget.Label +} + +func NewPlayQueueListRow(playQueueList *PlayQueueList, playingIcon fyne.CanvasObject) *PlayQueueListRow { + p := &PlayQueueListRow{ + playingIcon: playingIcon, + playQueueList: playQueueList, + num: widget.NewLabel(""), + cover: NewImagePlaceholder(theme.TracksIcon, 64), + title: util.NewTruncatingLabel(), + artist: NewMultiHyperlink(), + time: util.NewTrailingAlignLabel(), + } + p.artist.OnTapped = playQueueList.onArtistTapped + p.OnDoubleTapped = func() { + playQueueList.onPlayTrackAt(p.ItemID()) + } + p.OnTapped = func() { + playQueueList.onSelectTrack(p.ItemID()) + } + //p.title.TextStyle.Bold = true + p.ExtendBaseWidget(p) + p.Content = container.New(playQueueList.colLayout, + container.NewCenter(p.num), + p.cover, + container.New(&layouts.VboxCustomPadding{ExtraPad: -15}, + p.title, p.artist), + container.NewCenter(p.time), + ) + return p +} + +func (p *PlayQueueListRow) Update(tm *util.TrackListModel, rowNum int) { + changed := false + if tm.Selected != p.Selected { + p.Selected = tm.Selected + changed = true + } + + // Update info that can change if this row is bound to + // a new track (*mediaprovider.Track) + tr := tm.Track + if tr.ID != p.trackID { + p.EnsureUnfocused() + p.trackID = tr.ID + p.artist.BuildSegments(tr.ArtistNames, tr.ArtistIDs) + p.time.Text = util.SecondsToTimeString(float64(tr.Duration)) + changed = true + } + + // Render whether track is playing or not + if isPlaying := p.playQueueList.nowPlayingID == tr.ID; isPlaying != p.isPlaying { + p.isPlaying = isPlaying + p.title.TextStyle.Bold = isPlaying + + if isPlaying { + p.Content.(*fyne.Container).Objects[0] = p.playingIcon + } else { + p.Content.(*fyne.Container).Objects[0] = p.num + } + changed = true + } + + if changed { + p.Refresh() + } +} diff --git a/ui/widgets/tracklist.go b/ui/widgets/tracklist.go index cd052c0..1452a3b 100644 --- a/ui/widgets/tracklist.go +++ b/ui/widgets/tracklist.go @@ -323,7 +323,7 @@ func (t *Tracklist) _setTracks(trs []*mediaprovider.Track) { if t.list != nil { t.list.ClearItemForIDMap() } - t.tracksOrigOrder = toTrackModels(trs) + t.tracksOrigOrder = util.ToTrackListModels(trs) t.doSortTracks() } @@ -340,7 +340,7 @@ func (t *Tracklist) GetTracks() []*mediaprovider.Track { func (t *Tracklist) AppendTracks(trs []*mediaprovider.Track) { t.tracksMutex.Lock() defer t.tracksMutex.Unlock() - t.tracksOrigOrder = append(t.tracks, toTrackModels(trs)...) + t.tracksOrigOrder = append(t.tracks, util.ToTrackListModels(trs)...) t.doSortTracks() } @@ -388,12 +388,6 @@ func (t *Tracklist) Refresh() { t.BaseWidget.Refresh() } -func toTrackModels(trs []*mediaprovider.Track) []*util.TrackListModel { - return sharedutil.MapSlice(trs, func(tr *mediaprovider.Track) *util.TrackListModel { - return &util.TrackListModel{Track: tr, Selected: false} - }) -} - // do nothing Tapped handler so that tapping the separator between rows // doesn't fall through to the page (which calls UnselectAll on tracklist) func (t *Tracklist) Tapped(*fyne.PointEvent) {}