more work
This commit is contained in:
@@ -1,12 +1,21 @@
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import "github.com/dweymouth/supersonic/backend/mediaprovider"
|
import (
|
||||||
|
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||||
|
"github.com/dweymouth/supersonic/sharedutil"
|
||||||
|
)
|
||||||
|
|
||||||
type TrackListModel struct {
|
type TrackListModel struct {
|
||||||
Track *mediaprovider.Track
|
Track *mediaprovider.Track
|
||||||
Selected bool
|
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) {
|
func SelectTrack(tracks []*TrackListModel, idx int) {
|
||||||
if tracks[idx].Selected {
|
if tracks[idx].Selected {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -323,7 +323,7 @@ func (t *Tracklist) _setTracks(trs []*mediaprovider.Track) {
|
|||||||
if t.list != nil {
|
if t.list != nil {
|
||||||
t.list.ClearItemForIDMap()
|
t.list.ClearItemForIDMap()
|
||||||
}
|
}
|
||||||
t.tracksOrigOrder = toTrackModels(trs)
|
t.tracksOrigOrder = util.ToTrackListModels(trs)
|
||||||
t.doSortTracks()
|
t.doSortTracks()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,7 +340,7 @@ func (t *Tracklist) GetTracks() []*mediaprovider.Track {
|
|||||||
func (t *Tracklist) AppendTracks(trs []*mediaprovider.Track) {
|
func (t *Tracklist) AppendTracks(trs []*mediaprovider.Track) {
|
||||||
t.tracksMutex.Lock()
|
t.tracksMutex.Lock()
|
||||||
defer t.tracksMutex.Unlock()
|
defer t.tracksMutex.Unlock()
|
||||||
t.tracksOrigOrder = append(t.tracks, toTrackModels(trs)...)
|
t.tracksOrigOrder = append(t.tracks, util.ToTrackListModels(trs)...)
|
||||||
t.doSortTracks()
|
t.doSortTracks()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,12 +388,6 @@ func (t *Tracklist) Refresh() {
|
|||||||
t.BaseWidget.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
|
// do nothing Tapped handler so that tapping the separator between rows
|
||||||
// doesn't fall through to the page (which calls UnselectAll on tracklist)
|
// doesn't fall through to the page (which calls UnselectAll on tracklist)
|
||||||
func (t *Tracklist) Tapped(*fyne.PointEvent) {}
|
func (t *Tracklist) Tapped(*fyne.PointEvent) {}
|
||||||
|
|||||||
Reference in New Issue
Block a user