Merge pull request #552 from dweymouth/fyne-2.6-migrate
Fyne 2.6 migrate
This commit is contained in:
@@ -391,14 +391,16 @@ func (g *GridView) checkFetchMoreItems(count int) {
|
||||
g.stateMutex.Lock()
|
||||
g.items = append(g.items, items...)
|
||||
g.stateMutex.Unlock()
|
||||
g.loadingDots.Stop()
|
||||
if len(items) < batchFetchSize {
|
||||
g.done = true
|
||||
}
|
||||
n += len(items)
|
||||
if len(items) > 0 {
|
||||
g.grid.Refresh()
|
||||
}
|
||||
fyne.DoAndWait(func() {
|
||||
g.loadingDots.Stop()
|
||||
if len(items) > 0 {
|
||||
g.grid.Refresh()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,16 +62,16 @@ func (l *LoadingDots) Stop() {
|
||||
func (l *LoadingDots) animate(ctx context.Context) {
|
||||
foreground := theme.ForegroundColor()
|
||||
disabled := theme.DisabledColor()
|
||||
l.doTick(foreground, disabled)
|
||||
ticker := time.NewTicker(333 * time.Millisecond)
|
||||
for {
|
||||
fyne.Do(func() { l.doTick(foreground, disabled) })
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
ticker.Stop()
|
||||
l.running.Store(false)
|
||||
return
|
||||
case <-ticker.C:
|
||||
l.doTick(foreground, disabled)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-55
@@ -5,7 +5,6 @@ import (
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"fyne.io/fyne/v2/lang"
|
||||
|
||||
@@ -91,7 +90,6 @@ type Tracklist struct {
|
||||
visibleColumns []bool
|
||||
sorting TracklistSort
|
||||
|
||||
tracksMutex sync.RWMutex
|
||||
tracks []*util.TrackListModel
|
||||
tracksOrigOrder []*util.TrackListModel
|
||||
|
||||
@@ -148,7 +146,7 @@ func NewTracklist(tracks []*mediaprovider.Track, im *backend.ImageManager, useCo
|
||||
}
|
||||
|
||||
t.list = NewFocusList(
|
||||
t.lenTracks,
|
||||
func() int { return len(t.tracks) },
|
||||
func() fyne.CanvasObject {
|
||||
var tr TracklistRow
|
||||
if t.compactRows {
|
||||
@@ -169,16 +167,13 @@ func NewTracklist(tracks []*mediaprovider.Track, im *backend.ImageManager, useCo
|
||||
return tr
|
||||
},
|
||||
func(itemID widget.ListItemID, item fyne.CanvasObject) {
|
||||
t.tracksMutex.RLock()
|
||||
// we could have removed tracks from the list in between
|
||||
// Fyne calling the length callback and this update callback
|
||||
// so the itemID may be out of bounds. if so, do nothing.
|
||||
if itemID >= len(t.tracks) {
|
||||
t.tracksMutex.RUnlock()
|
||||
return
|
||||
}
|
||||
model := t.tracks[itemID]
|
||||
t.tracksMutex.RUnlock()
|
||||
|
||||
tr := item.(TracklistRow)
|
||||
if tr.TrackID() != model.Item.Metadata().ID || tr.ItemID() != itemID {
|
||||
@@ -219,10 +214,8 @@ func (t *Tracklist) Scroll(amount float32) {
|
||||
t.list.ScrollToOffset(t.list.GetScrollOffset() + amount)
|
||||
}
|
||||
|
||||
// Gets the track at the given index. Thread-safe.
|
||||
// Gets the track at the given index.
|
||||
func (t *Tracklist) TrackAt(idx int) *mediaprovider.Track {
|
||||
t.tracksMutex.RLock()
|
||||
defer t.tracksMutex.RUnlock()
|
||||
if idx >= len(t.tracks) {
|
||||
log.Println("error: Tracklist.TrackAt: index out of range")
|
||||
return nil
|
||||
@@ -291,10 +284,8 @@ func (t *Tracklist) SetSorting(sorting TracklistSort) {
|
||||
// Sets the currently playing track ID and updates the list rendering
|
||||
func (t *Tracklist) SetNowPlaying(trackID string) {
|
||||
prevNowPlaying := t.nowPlayingID
|
||||
t.tracksMutex.RLock()
|
||||
trPrev, idxPrev := util.FindItemByID(t.tracks, prevNowPlaying)
|
||||
tr, idx := util.FindItemByID(t.tracks, trackID)
|
||||
t.tracksMutex.RUnlock()
|
||||
t.nowPlayingID = trackID
|
||||
if trPrev != nil {
|
||||
t.list.RefreshItem(idxPrev)
|
||||
@@ -306,58 +297,46 @@ func (t *Tracklist) SetNowPlaying(trackID string) {
|
||||
|
||||
// Increments the play count of the given track and updates the list rendering
|
||||
func (t *Tracklist) IncrementPlayCount(trackID string) {
|
||||
t.tracksMutex.RLock()
|
||||
tr, idx := util.FindItemByID(t.tracks, trackID)
|
||||
t.tracksMutex.RUnlock()
|
||||
if tr != nil {
|
||||
tr.(*mediaprovider.Track).PlayCount += 1
|
||||
t.list.RefreshItem(idx)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all tracks from the tracklist. Does not issue Refresh call. Thread-safe.
|
||||
// Remove all tracks from the tracklist. Does not issue Refresh call.
|
||||
func (t *Tracklist) Clear() {
|
||||
t.tracksMutex.Lock()
|
||||
defer t.tracksMutex.Unlock()
|
||||
t.tracks = nil
|
||||
t.tracksOrigOrder = nil
|
||||
}
|
||||
|
||||
// Sets the tracks in the tracklist. Thread-safe.
|
||||
// Sets the tracks in the tracklist.
|
||||
func (t *Tracklist) SetTracks(trs []*mediaprovider.Track) {
|
||||
t._setTracks(trs)
|
||||
t.Refresh()
|
||||
}
|
||||
|
||||
func (t *Tracklist) _setTracks(trs []*mediaprovider.Track) {
|
||||
t.tracksMutex.Lock()
|
||||
defer t.tracksMutex.Unlock()
|
||||
t.tracksOrigOrder = util.ToTrackListModels(trs)
|
||||
t.doSortTracks()
|
||||
}
|
||||
|
||||
// Returns the tracks in the tracklist in the current display order.
|
||||
func (t *Tracklist) GetTracks() []*mediaprovider.Track {
|
||||
t.tracksMutex.RLock()
|
||||
defer t.tracksMutex.RUnlock()
|
||||
return sharedutil.MapSlice(t.tracks, func(tm *util.TrackListModel) *mediaprovider.Track {
|
||||
return tm.Track()
|
||||
})
|
||||
}
|
||||
|
||||
// Append more tracks to the tracklist. Thread-safe.
|
||||
// Append more tracks to the tracklist.
|
||||
func (t *Tracklist) AppendTracks(trs []*mediaprovider.Track) {
|
||||
t.tracksMutex.Lock()
|
||||
t.tracksOrigOrder = append(t.tracks, util.ToTrackListModels(trs)...)
|
||||
t.doSortTracks()
|
||||
t.tracksMutex.Unlock()
|
||||
t.list.Refresh()
|
||||
}
|
||||
|
||||
func (t *Tracklist) SelectAll() {
|
||||
t.tracksMutex.RLock()
|
||||
util.SelectAllItems(t.tracks)
|
||||
t.tracksMutex.RUnlock()
|
||||
t.list.Refresh()
|
||||
}
|
||||
|
||||
@@ -367,13 +346,10 @@ func (t *Tracklist) UnselectAll() {
|
||||
}
|
||||
|
||||
func (t *Tracklist) unselectAll() {
|
||||
t.tracksMutex.RLock()
|
||||
util.UnselectAllItems(t.tracks)
|
||||
t.tracksMutex.RUnlock()
|
||||
}
|
||||
|
||||
func (t *Tracklist) SelectAndScrollToTrack(trackID string) {
|
||||
t.tracksMutex.RLock()
|
||||
idx := -1
|
||||
for i, tr := range t.tracks {
|
||||
if tr.Item.Metadata().ID == trackID {
|
||||
@@ -383,7 +359,6 @@ func (t *Tracklist) SelectAndScrollToTrack(trackID string) {
|
||||
tr.Selected = false
|
||||
}
|
||||
}
|
||||
t.tracksMutex.RUnlock()
|
||||
if idx >= 0 {
|
||||
t.list.ScrollTo(idx)
|
||||
}
|
||||
@@ -474,9 +449,7 @@ func (t *Tracklist) doSortTracks() {
|
||||
|
||||
func (t *Tracklist) onSorted(sort ListHeaderSort) {
|
||||
t.sorting = TracklistSort{ColumnName: t.colName(sort.ColNumber), SortOrder: sort.Type}
|
||||
t.tracksMutex.Lock()
|
||||
t.doSortTracks()
|
||||
t.tracksMutex.Unlock()
|
||||
t.Refresh()
|
||||
}
|
||||
|
||||
@@ -503,20 +476,14 @@ func (t *Tracklist) onSelectTrack(idx int) {
|
||||
}
|
||||
|
||||
func (t *Tracklist) selectAddOrRemove(idx int) {
|
||||
t.tracksMutex.RLock()
|
||||
defer t.tracksMutex.RUnlock()
|
||||
t.tracks[idx].Selected = !t.tracks[idx].Selected
|
||||
}
|
||||
|
||||
func (t *Tracklist) selectTrack(idx int) {
|
||||
t.tracksMutex.RLock()
|
||||
defer t.tracksMutex.RUnlock()
|
||||
util.SelectItem(t.tracks, idx)
|
||||
}
|
||||
|
||||
func (t *Tracklist) selectRange(idx int) {
|
||||
t.tracksMutex.RLock()
|
||||
defer t.tracksMutex.RUnlock()
|
||||
util.SelectItemRange(t.tracks, idx)
|
||||
}
|
||||
|
||||
@@ -606,9 +573,7 @@ func (t *Tracklist) onShowContextMenu(e *fyne.PointEvent, trackIdx int) {
|
||||
}
|
||||
|
||||
func (t *Tracklist) onSetFavorite(trackID string, fav bool) {
|
||||
t.tracksMutex.RLock()
|
||||
item, _ := util.FindItemByID(t.tracks, trackID)
|
||||
t.tracksMutex.RUnlock()
|
||||
t.onSetFavorites([]*mediaprovider.Track{item.(*mediaprovider.Track)}, fav, false)
|
||||
}
|
||||
|
||||
@@ -627,9 +592,7 @@ func (t *Tracklist) onSetFavorites(tracks []*mediaprovider.Track, fav bool, need
|
||||
|
||||
func (t *Tracklist) onSetRating(trackID string, rating int) {
|
||||
// update our own track model
|
||||
t.tracksMutex.RLock()
|
||||
item, _ := util.FindItemByID(t.tracks, trackID)
|
||||
t.tracksMutex.RUnlock()
|
||||
t.onSetRatings([]*mediaprovider.Track{item.(*mediaprovider.Track)}, rating, false)
|
||||
}
|
||||
|
||||
@@ -681,14 +644,10 @@ func (t *Tracklist) onPlaySongRadio(tracks []*mediaprovider.Track) {
|
||||
}
|
||||
|
||||
func (t *Tracklist) selectedTracks() []*mediaprovider.Track {
|
||||
t.tracksMutex.RLock()
|
||||
defer t.tracksMutex.RUnlock()
|
||||
return util.SelectedTracks(t.tracks)
|
||||
}
|
||||
|
||||
func (t *Tracklist) SelectedTrackIDs() []string {
|
||||
t.tracksMutex.RLock()
|
||||
defer t.tracksMutex.RUnlock()
|
||||
return util.SelectedItemIDs(t.tracks)
|
||||
}
|
||||
|
||||
@@ -696,9 +655,6 @@ func (t *Tracklist) SelectedTrackIDs() []string {
|
||||
// original sort order (ie if tracklist is sorted by some column), the indexes
|
||||
// returned will correspond to the order of tracks when the list was initialized.
|
||||
func (t *Tracklist) SelectedTrackIndexes() []int {
|
||||
t.tracksMutex.RLock()
|
||||
defer t.tracksMutex.RUnlock()
|
||||
|
||||
idx := -1
|
||||
return sharedutil.FilterMapSlice(t.tracksOrigOrder, func(t *util.TrackListModel) (int, bool) {
|
||||
idx++
|
||||
@@ -706,12 +662,6 @@ func (t *Tracklist) SelectedTrackIndexes() []int {
|
||||
})
|
||||
}
|
||||
|
||||
func (t *Tracklist) lenTracks() int {
|
||||
t.tracksMutex.RLock()
|
||||
defer t.tracksMutex.RUnlock()
|
||||
return len(t.tracks)
|
||||
}
|
||||
|
||||
func (t *Tracklist) ColNumber(colName string) int {
|
||||
i := slices.IndexFunc(t.columns, func(c TracklistColumn) bool {
|
||||
return c.Name == colName
|
||||
|
||||
@@ -3,6 +3,7 @@ package widgets
|
||||
import (
|
||||
"sync/atomic"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||
)
|
||||
|
||||
@@ -69,8 +70,10 @@ func (t *TracklistLoader) loadMoreTracks(num int) {
|
||||
if t.disposed.Load() {
|
||||
return
|
||||
}
|
||||
t.tracklist.AppendTracks(t.trackBuffer)
|
||||
t.len += len(t.trackBuffer)
|
||||
fyne.Do(func() {
|
||||
t.tracklist.AppendTracks(t.trackBuffer)
|
||||
t.len += len(t.trackBuffer)
|
||||
})
|
||||
}
|
||||
if t.done {
|
||||
t.trackBuffer = nil
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"image"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/canvas"
|
||||
@@ -15,10 +16,31 @@ import (
|
||||
"fyne.io/fyne/v2/widget"
|
||||
ttwidget "github.com/dweymouth/fyne-tooltip/widget"
|
||||
"github.com/dweymouth/supersonic/backend"
|
||||
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
||||
myTheme "github.com/dweymouth/supersonic/ui/theme"
|
||||
"github.com/dweymouth/supersonic/ui/util"
|
||||
)
|
||||
|
||||
var (
|
||||
tracklistUpdateCounter = util.NewEventCounter(30)
|
||||
|
||||
emptyTrack = util.TrackListModel{
|
||||
Item: &mediaprovider.Track{
|
||||
ID: "dummy",
|
||||
Title: "—",
|
||||
ArtistIDs: []string{"—"},
|
||||
ArtistNames: []string{"—"},
|
||||
Album: "—",
|
||||
Genres: []string{"—"},
|
||||
ComposerNames: []string{"—"},
|
||||
ComposerIDs: []string{"—"},
|
||||
Comment: "—",
|
||||
FilePath: "—",
|
||||
ContentType: "—",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const tracklistThumbnailSize = 48
|
||||
|
||||
type TracklistColumn struct {
|
||||
@@ -151,6 +173,9 @@ type tracklistRowBase struct {
|
||||
isFavorite bool
|
||||
playCount int
|
||||
|
||||
nextUpdateModel *util.TrackListModel
|
||||
nextUpdateRowNum int
|
||||
|
||||
num *widget.Label
|
||||
name *ttwidget.RichText
|
||||
artist *MultiHyperlink
|
||||
@@ -318,6 +343,26 @@ func (t *tracklistRowBase) TrackID() string {
|
||||
}
|
||||
|
||||
func (t *tracklistRowBase) Update(tm *util.TrackListModel, rowNum int) {
|
||||
if tracklistUpdateCounter.NumEventsSince(time.Now().Add(-150*time.Millisecond)) > 20 {
|
||||
t.doUpdate(&emptyTrack, 1)
|
||||
if t.nextUpdateModel == nil {
|
||||
// queue to run later
|
||||
fyne.Do(func() {
|
||||
if t.nextUpdateModel != nil {
|
||||
t.doUpdate(t.nextUpdateModel, t.nextUpdateRowNum)
|
||||
}
|
||||
t.nextUpdateModel = nil
|
||||
})
|
||||
}
|
||||
t.nextUpdateModel = tm
|
||||
t.nextUpdateRowNum = rowNum
|
||||
} else {
|
||||
t.nextUpdateModel = nil
|
||||
t.doUpdate(tm, rowNum)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tracklistRowBase) doUpdate(tm *util.TrackListModel, rowNum int) {
|
||||
changed := false
|
||||
if tm.Selected != t.Selected {
|
||||
t.Selected = tm.Selected
|
||||
@@ -415,6 +460,7 @@ func (t *tracklistRowBase) Update(tm *util.TrackListModel, rowNum int) {
|
||||
}
|
||||
|
||||
if changed {
|
||||
tracklistUpdateCounter.Add()
|
||||
t.Refresh()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user