refactor a bit and have tracklist respond to set sorting calls

This commit is contained in:
Drew Weymouth
2023-05-23 11:10:43 -07:00
parent 84e1f01a68
commit 71ce5043b7
2 changed files with 112 additions and 89 deletions
+28 -13
View File
@@ -15,14 +15,19 @@ import (
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
) )
type ColumnSort int type SortType int
const ( const (
SortNone ColumnSort = iota SortNone SortType = iota
SortAscending SortAscending
SortDescending SortDescending
) )
type ListHeaderSort struct {
ColNumber int
Type SortType
}
type ListColumn struct { type ListColumn struct {
Text string Text string
AlignTrailing bool AlignTrailing bool
@@ -34,10 +39,11 @@ type ListHeader struct {
DisableSorting bool DisableSorting bool
OnColumnSortChanged func(int, ColumnSort) OnColumnSortChanged func(ListHeaderSort)
OnColumnVisibilityChanged func(int, bool) OnColumnVisibilityChanged func(int, bool)
OnColumnVisibilityMenuShown func(*widget.PopUp) OnColumnVisibilityMenuShown func(*widget.PopUp)
sort ListHeaderSort
columns []ListColumn columns []ListColumn
columnVisible []bool columnVisible []bool
columnsLayout *layouts.ColumnsLayout columnsLayout *layouts.ColumnsLayout
@@ -80,8 +86,8 @@ func (l *ListHeader) SetColumnVisible(colNum int, visible bool) {
func (l *ListHeader) buildColumns() { func (l *ListHeader) buildColumns() {
for i, c := range l.columns { for i, c := range l.columns {
hdr := newColHeader(c, &l.DisableSorting) hdr := newColHeader(c, &l.DisableSorting)
hdr.OnSortChanged = func(i int) func(ColumnSort) { hdr.OnSortChanged = func(i int) func(SortType) {
return func(sort ColumnSort) { l.onSortChanged(i, sort) } return func(sort SortType) { l.SetSorting(ListHeaderSort{ColNumber: i, Type: sort}) }
}(i) }(i)
hdr.OnTappedSecondary = l.TappedSecondary hdr.OnTappedSecondary = l.TappedSecondary
l.columnsContainer.Add( l.columnsContainer.Add(
@@ -95,17 +101,26 @@ func (l *ListHeader) buildColumns() {
} }
} }
func (l *ListHeader) onSortChanged(colNum int, sort ColumnSort) { // Sets the sorting for the ListHeader. Will invoke
// OnColumnSortChanged if set.
func (l *ListHeader) SetSorting(sort ListHeaderSort) {
if l.sort == sort {
return
}
l.sort = sort
for i, c := range l.columnsContainer.Objects { for i, c := range l.columnsContainer.Objects {
if i != colNum { // TODO
c.(*fyne.Container).Objects[1].(*colHeader).Sort = SortNone //c.(*colHeader).Sort = sortNone
// TODO hdr := c.(*fyne.Container).Objects[1].(*colHeader)
//c.(*colHeader).Sort = sortNone if i == sort.ColNumber {
hdr.Sort = sort.Type
} else {
hdr.Sort = SortNone
} }
} }
l.Refresh() l.Refresh()
if l.OnColumnSortChanged != nil { if l.OnColumnSortChanged != nil {
l.OnColumnSortChanged(colNum, sort) l.OnColumnSortChanged(sort)
} }
} }
@@ -156,8 +171,8 @@ func (l *ListHeader) createOnChangedCallbk(colNum int) func(bool) {
type colHeader struct { type colHeader struct {
widget.BaseWidget widget.BaseWidget
Sort ColumnSort Sort SortType
OnSortChanged func(ColumnSort) OnSortChanged func(SortType)
OnTappedSecondary func(*fyne.PointEvent) OnTappedSecondary func(*fyne.PointEvent)
sortDisabled *bool sortDisabled *bool
+84 -76
View File
@@ -43,7 +43,7 @@ var columns = []string{
} }
type TracklistSort struct { type TracklistSort struct {
SortOrder ColumnSort SortOrder SortType
ColumnName string ColumnName string
} }
@@ -108,9 +108,7 @@ func NewTracklist(tracks []*mediaprovider.Track) *Tracklist {
// #, Title, Artist, Album, Time, Year, Favorite, Rating, Plays, Bitrate, Size, Path // #, Title, Artist, Album, Time, Year, Favorite, Rating, Plays, Bitrate, Size, Path
t.colLayout = layouts.NewColumnsLayout([]float32{40, -1, -1, -1, 60, 60, 55, 100, 65, 75, 75, -1}) t.colLayout = layouts.NewColumnsLayout([]float32{40, -1, -1, -1, 60, 60, 55, 100, 65, 75, 75, -1})
t.buildHeader() t.buildHeader()
t.hdr.OnColumnSortChanged = func(idx int, sort ColumnSort) { t.hdr.OnColumnSortChanged = t.onSorted
t.SetSorting(TracklistSort{ColumnName: colName(idx), SortOrder: sort})
}
t.hdr.OnColumnVisibilityChanged = t.setColumnVisible t.hdr.OnColumnVisibilityChanged = t.setColumnVisible
t.hdr.OnColumnVisibilityMenuShown = func(pop *widget.PopUp) { t.hdr.OnColumnVisibilityMenuShown = func(pop *widget.PopUp) {
if t.OnColumnVisibilityMenuShown != nil { if t.OnColumnVisibilityMenuShown != nil {
@@ -210,81 +208,16 @@ func (t *Tracklist) setColumnVisible(colNum int, vis bool) {
} }
} }
func (t *Tracklist) stringSort(fieldFn func(*mediaprovider.Track) string, sortOrder ColumnSort) {
new := make([]*mediaprovider.Track, len(t.tracksOrigOrder))
copy(new, t.tracksOrigOrder)
sort.SliceStable(new, func(i, j int) bool {
cmp := strings.Compare(fieldFn(new[i]), fieldFn(new[j]))
if sortOrder == SortDescending {
return cmp > 0
}
return cmp < 0
})
t.tracks = new
}
func (t *Tracklist) intSort(fieldFn func(*mediaprovider.Track) int64, sortOrder ColumnSort) {
new := make([]*mediaprovider.Track, len(t.tracksOrigOrder))
copy(new, t.tracksOrigOrder)
sort.SliceStable(new, func(i, j int) bool {
if sortOrder == SortDescending {
return fieldFn(new[i]) > fieldFn(new[j])
}
return fieldFn(new[i]) < fieldFn(new[j])
})
t.tracks = new
}
func (t *Tracklist) Sorting() TracklistSort { func (t *Tracklist) Sorting() TracklistSort {
return t.sorting return t.sorting
} }
func (t *Tracklist) SetSorting(sorting TracklistSort) { func (t *Tracklist) SetSorting(sorting TracklistSort) {
t.sorting = sorting if sorting.ColumnName == "" {
t.tracksMutex.Lock()
if sorting.SortOrder == SortNone {
t.tracks = t.tracksOrigOrder
t.tracksMutex.Unlock()
t.Refresh()
return return
} }
switch sorting.ColumnName { // actual sorting will be handled in callback from header
case ColumnNum: t.hdr.SetSorting(ListHeaderSort{ColNumber: ColNumber(sorting.ColumnName), Type: sorting.SortOrder})
if sorting.SortOrder == SortDescending {
t.tracks = sharedutil.Reversed(t.tracksOrigOrder)
} else {
t.tracks = t.tracksOrigOrder
}
case ColumnTitle:
t.stringSort(func(tr *mediaprovider.Track) string { return tr.Name }, sorting.SortOrder)
case ColumnArtist:
t.stringSort(func(tr *mediaprovider.Track) string { return tr.ArtistNames[0] }, sorting.SortOrder)
case ColumnAlbum:
t.stringSort(func(tr *mediaprovider.Track) string { return tr.Album }, sorting.SortOrder)
case ColumnPath:
t.stringSort(func(tr *mediaprovider.Track) string { return tr.FilePath }, sorting.SortOrder)
case ColumnRating:
t.intSort(func(tr *mediaprovider.Track) int64 { return int64(tr.Rating) }, sorting.SortOrder)
case ColumnTime:
t.intSort(func(tr *mediaprovider.Track) int64 { return int64(tr.Duration) }, sorting.SortOrder)
case ColumnYear:
t.intSort(func(tr *mediaprovider.Track) int64 { return int64(tr.Year) }, sorting.SortOrder)
case ColumnSize:
t.intSort(func(tr *mediaprovider.Track) int64 { return tr.Size }, sorting.SortOrder)
case ColumnPlays:
t.intSort(func(tr *mediaprovider.Track) int64 { return int64(tr.PlayCount) }, sorting.SortOrder)
case ColumnBitrate:
t.intSort(func(tr *mediaprovider.Track) int64 { return int64(tr.BitRate) }, sorting.SortOrder)
case ColumnFavorite:
t.intSort(func(tr *mediaprovider.Track) int64 {
if tr.Favorite {
return 1
}
return 0
}, sorting.SortOrder)
}
t.tracksMutex.Unlock()
t.Refresh()
} }
func (t *Tracklist) SetNowPlaying(trackID string) { func (t *Tracklist) SetNowPlaying(trackID string) {
@@ -302,7 +235,7 @@ func (t *Tracklist) IncrementPlayCount(trackID string) {
} }
} }
// Remove all tracks from the tracklist. Thread-safe. // Remove all tracks from the tracklist. Does not issue Refresh call. Thread-safe.
func (t *Tracklist) Clear() { func (t *Tracklist) Clear() {
t.selectionMgr.UnselectAll() t.selectionMgr.UnselectAll()
t.tracksMutex.Lock() t.tracksMutex.Lock()
@@ -310,12 +243,12 @@ func (t *Tracklist) Clear() {
t.tracks = nil t.tracks = nil
} }
// Sets the tracks in the tracklist. Thread-safe. // Sets the tracks in the tracklist. Does not issue Refresh call. Thread-safe.
func (t *Tracklist) SetTracks(trs []*mediaprovider.Track) { func (t *Tracklist) SetTracks(trs []*mediaprovider.Track) {
t.tracksMutex.Lock() t.tracksMutex.Lock()
defer t.tracksMutex.Unlock() defer t.tracksMutex.Unlock()
t.tracks = trs
t.tracksOrigOrder = trs t.tracksOrigOrder = trs
t.doSortTracks()
} }
// Returns the tracks in the tracklist in the current display order. // Returns the tracks in the tracklist in the current display order.
@@ -325,7 +258,7 @@ func (t *Tracklist) GetTracks() []*mediaprovider.Track {
return t.tracks return t.tracks
} }
// Append more tracks to the tracklist. Thread-safe. // Append more tracks to the tracklist. Does not issue Refresh call. Thread-safe.
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()
@@ -367,6 +300,81 @@ func (t *Tracklist) Refresh() {
t.BaseWidget.Refresh() t.BaseWidget.Refresh()
} }
func (t *Tracklist) stringSort(fieldFn func(*mediaprovider.Track) string) {
new := make([]*mediaprovider.Track, len(t.tracksOrigOrder))
copy(new, t.tracksOrigOrder)
sort.SliceStable(new, func(i, j int) bool {
cmp := strings.Compare(fieldFn(new[i]), fieldFn(new[j]))
if t.sorting.SortOrder == SortDescending {
return cmp > 0
}
return cmp < 0
})
t.tracks = new
}
func (t *Tracklist) intSort(fieldFn func(*mediaprovider.Track) int64) {
new := make([]*mediaprovider.Track, len(t.tracksOrigOrder))
copy(new, t.tracksOrigOrder)
sort.SliceStable(new, func(i, j int) bool {
if t.sorting.SortOrder == SortDescending {
return fieldFn(new[i]) > fieldFn(new[j])
}
return fieldFn(new[i]) < fieldFn(new[j])
})
t.tracks = new
}
func (t *Tracklist) doSortTracks() {
if t.sorting.SortOrder == SortNone {
t.tracks = t.tracksOrigOrder
return
}
switch t.sorting.ColumnName {
case ColumnNum:
if t.sorting.SortOrder == SortDescending {
t.tracks = sharedutil.Reversed(t.tracksOrigOrder)
} else {
t.tracks = t.tracksOrigOrder
}
case ColumnTitle:
t.stringSort(func(tr *mediaprovider.Track) string { return tr.Name })
case ColumnArtist:
t.stringSort(func(tr *mediaprovider.Track) string { return tr.ArtistNames[0] })
case ColumnAlbum:
t.stringSort(func(tr *mediaprovider.Track) string { return tr.Album })
case ColumnPath:
t.stringSort(func(tr *mediaprovider.Track) string { return tr.FilePath })
case ColumnRating:
t.intSort(func(tr *mediaprovider.Track) int64 { return int64(tr.Rating) })
case ColumnTime:
t.intSort(func(tr *mediaprovider.Track) int64 { return int64(tr.Duration) })
case ColumnYear:
t.intSort(func(tr *mediaprovider.Track) int64 { return int64(tr.Year) })
case ColumnSize:
t.intSort(func(tr *mediaprovider.Track) int64 { return tr.Size })
case ColumnPlays:
t.intSort(func(tr *mediaprovider.Track) int64 { return int64(tr.PlayCount) })
case ColumnBitrate:
t.intSort(func(tr *mediaprovider.Track) int64 { return int64(tr.BitRate) })
case ColumnFavorite:
t.intSort(func(tr *mediaprovider.Track) int64 {
if tr.Favorite {
return 1
}
return 0
})
}
}
func (t *Tracklist) onSorted(sort ListHeaderSort) {
t.sorting = TracklistSort{ColumnName: colName(sort.ColNumber), SortOrder: sort.Type}
t.tracksMutex.Lock()
t.doSortTracks()
t.tracksMutex.Unlock()
t.Refresh()
}
func (t *Tracklist) onPlayTrackAt(idx int) { func (t *Tracklist) onPlayTrackAt(idx int) {
if t.OnPlayTrackAt != nil { if t.OnPlayTrackAt != nil {
t.OnPlayTrackAt(idx) t.OnPlayTrackAt(idx)