Merge pull request #147 from dweymouth/feature/rating
Add 5-star rating column to tracklist
This commit is contained in:
+2
-2
@@ -107,14 +107,14 @@ func DefaultConfig(appVersionTag string) *Config {
|
||||
CloseToSystemTray: false,
|
||||
},
|
||||
AlbumPage: AlbumPageConfig{
|
||||
TracklistColumns: []string{"Artist", "Time", "Plays", "Favorite"},
|
||||
TracklistColumns: []string{"Artist", "Time", "Plays", "Favorite", "Rating"},
|
||||
},
|
||||
AlbumsPage: AlbumsPageConfig{
|
||||
SortOrder: string(AlbumSortRecentlyAdded),
|
||||
},
|
||||
ArtistPage: ArtistPageConfig{
|
||||
InitialView: "Discography",
|
||||
TracklistColumns: []string{"Album", "Time", "Plays", "Favorite"},
|
||||
TracklistColumns: []string{"Album", "Time", "Plays", "Favorite", "Rating"},
|
||||
},
|
||||
FavoritesPage: FavoritesPageConfig{
|
||||
TracklistColumns: []string{"Artist", "Album", "Time", "Plays"},
|
||||
|
||||
@@ -242,6 +242,14 @@ func (p *PlaybackManager) OnTrackFavoriteStatusChanged(id string, fav bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// Any time the user changes the rating of a track elsewhere in the app,
|
||||
// this should be called to ensure the in-memory track model is updated.
|
||||
func (p *PlaybackManager) OnTrackRatingChanged(id string, rating int) {
|
||||
if tr := sharedutil.FindTrackByID(id, p.playQueue); tr != nil {
|
||||
tr.UserRating = rating
|
||||
}
|
||||
}
|
||||
|
||||
// trackIdxs must be sorted
|
||||
func (p *PlaybackManager) RemoveTracksFromQueue(trackIdxs []int) {
|
||||
newQueue := make([]*subsonic.Child, 0, len(p.playQueue)-len(trackIdxs))
|
||||
|
||||
+10
-20
File diff suppressed because one or more lines are too long
+2
-4
@@ -19,14 +19,12 @@ fyne bundle -append -prefix Res icons/freepik/playlist.png >> bundled.go
|
||||
fyne bundle -append -prefix Res icons/freepik/playlist-invert.png >> bundled.go
|
||||
fyne bundle -append -prefix Res icons/freepik/podcast.png >> bundled.go
|
||||
fyne bundle -append -prefix Res icons/freepik/podcast-invert.png >> bundled.go
|
||||
fyne bundle -append -prefix Res icons/freepik/star-filled.png >> bundled.go
|
||||
fyne bundle -append -prefix Res icons/freepik/star-filled-invert.png >> bundled.go
|
||||
fyne bundle -append -prefix Res icons/freepik/star-outline.png >> bundled.go
|
||||
fyne bundle -append -prefix Res icons/freepik/star-outline-invert.png >> bundled.go
|
||||
fyne bundle -append -prefix Res icons/freepik/theatermasks.png >> bundled.go
|
||||
fyne bundle -append -prefix Res icons/freepik/theatermasks-invert.png >> bundled.go
|
||||
fyne bundle -append -prefix Res icons/publicdomain/shuffle.svg >> bundled.go
|
||||
fyne bundle -append -prefix Res icons/publicdomain/shuffle-invert.svg >> bundled.go
|
||||
fyne bundle -append -prefix Res icons/publicdomain/star-outline.svg >> bundled.go
|
||||
fyne bundle -append -prefix Res icons/publicdomain/star-filled.svg >> bundled.go
|
||||
|
||||
fyne bundle -append -prefix Res ../LICENSE >> bundled.go
|
||||
fyne bundle -append -prefix Res licenses/BSDLICENSE >> bundled.go
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="2 2 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21.12 9.88005C21.0781 9.74719 20.9996 9.62884 20.8935 9.53862C20.7873 9.4484 20.6579 9.38997 20.52 9.37005L15.1 8.58005L12.67 3.67005C12.6008 3.55403 12.5027 3.45795 12.3853 3.39123C12.2678 3.32451 12.1351 3.28943 12 3.28943C11.8649 3.28943 11.7322 3.32451 11.6147 3.39123C11.4973 3.45795 11.3991 3.55403 11.33 3.67005L8.89999 8.58005L3.47999 9.37005C3.34211 9.38997 3.21266 9.4484 3.10652 9.53862C3.00038 9.62884 2.92186 9.74719 2.87999 9.88005C2.83529 10.0124 2.82846 10.1547 2.86027 10.2907C2.89207 10.4268 2.96124 10.5512 3.05999 10.6501L6.99999 14.4701L6.06999 19.8701C6.04642 20.0091 6.06199 20.1519 6.11497 20.2826C6.16796 20.4133 6.25625 20.5267 6.36999 20.6101C6.48391 20.6912 6.61825 20.7389 6.75785 20.7478C6.89746 20.7566 7.03675 20.7262 7.15999 20.6601L12 18.1101L16.85 20.6601C16.9573 20.7189 17.0776 20.7499 17.2 20.7501C17.3573 20.7482 17.5105 20.6995 17.64 20.6101C17.7537 20.5267 17.842 20.4133 17.895 20.2826C17.948 20.1519 17.9636 20.0091 17.94 19.8701L17 14.4701L20.93 10.6501C21.0305 10.5523 21.1015 10.4283 21.1351 10.2922C21.1687 10.1561 21.1634 10.0133 21.12 9.88005Z" fill="#000000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="2 2 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.2 20.7501C17.0776 20.7499 16.9573 20.7189 16.85 20.6601L12 18.1101L7.14999 20.6601C7.02675 20.7262 6.88746 20.7566 6.74786 20.7478C6.60825 20.7389 6.47391 20.6912 6.35999 20.6101C6.24625 20.5267 6.15796 20.4133 6.10497 20.2826C6.05199 20.1519 6.03642 20.0091 6.05999 19.8701L6.99999 14.4701L3.05999 10.6501C2.96124 10.5512 2.89207 10.4268 2.86027 10.2907C2.82846 10.1547 2.83529 10.0124 2.87999 9.88005C2.92186 9.74719 3.00038 9.62884 3.10652 9.53862C3.21266 9.4484 3.34211 9.38997 3.47999 9.37005L8.89999 8.58005L11.33 3.67005C11.3991 3.55403 11.4973 3.45795 11.6147 3.39123C11.7322 3.32451 11.8649 3.28943 12 3.28943C12.1351 3.28943 12.2678 3.32451 12.3853 3.39123C12.5027 3.45795 12.6008 3.55403 12.67 3.67005L15.1 8.58005L20.52 9.37005C20.6579 9.38997 20.7873 9.4484 20.8935 9.53862C20.9996 9.62884 21.0781 9.74719 21.12 9.88005C21.1647 10.0124 21.1715 10.1547 21.1397 10.2907C21.1079 10.4268 21.0387 10.5512 20.94 10.6501L17 14.4701L17.93 19.8701C17.9536 20.0091 17.938 20.1519 17.885 20.2826C17.832 20.4133 17.7437 20.5267 17.63 20.6101C17.5034 20.6976 17.3539 20.7463 17.2 20.7501ZM12 16.5201C12.121 16.5215 12.2403 16.5488 12.35 16.6001L16.2 18.6001L15.47 14.3101C15.4502 14.1897 15.4589 14.0664 15.4953 13.9501C15.5318 13.8337 15.595 13.7275 15.68 13.6401L18.8 10.6401L14.49 10.0001C14.3708 9.98109 14.2578 9.93401 14.1605 9.86271C14.0631 9.79141 13.9841 9.69795 13.93 9.59005L12 5.69005L10.07 9.60005C10.0159 9.70795 9.9369 9.80141 9.83952 9.87271C9.74214 9.94401 9.62918 9.99109 9.50999 10.0101L5.19999 10.6401L8.31999 13.6401C8.40493 13.7275 8.46817 13.8337 8.50464 13.9501C8.54111 14.0664 8.54979 14.1897 8.52999 14.3101L7.79999 18.6301L11.65 16.6301C11.7573 16.5683 11.8767 16.5308 12 16.5201Z" fill="#000000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -3,12 +3,14 @@ package controller
|
||||
import (
|
||||
"image"
|
||||
"log"
|
||||
"math"
|
||||
"strconv"
|
||||
"supersonic/backend"
|
||||
"supersonic/player"
|
||||
"supersonic/ui/dialogs"
|
||||
"supersonic/ui/util"
|
||||
"supersonic/ui/widgets"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
@@ -111,6 +113,7 @@ func (m *Controller) ConnectTracklistActions(tracklist *widgets.Tracklist) {
|
||||
m.App.PlaybackManager.OnTrackFavoriteStatusChanged(id, fav)
|
||||
}
|
||||
}
|
||||
tracklist.OnSetRating = m.setTrackRatings
|
||||
tracklist.OnShowAlbumPage = func(albumID string) {
|
||||
m.NavigateTo(AlbumRoute(albumID))
|
||||
}
|
||||
@@ -386,3 +389,43 @@ func (c *Controller) doModalClosed() {
|
||||
c.runOnModalClosed = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Controller) setTrackRatings(trackIDs []string, rating int) {
|
||||
// Subsonic doesn't allow bulk setting ratings.
|
||||
// To not overwhelm the server with requests, set rating for
|
||||
// only 5 tracks at a time concurrently
|
||||
batchSize := 5
|
||||
batchSetRating := func(offs int, wg *sync.WaitGroup) {
|
||||
for i := 0; i < batchSize && offs+i < len(trackIDs); i++ {
|
||||
if wg != nil {
|
||||
wg.Add(1)
|
||||
}
|
||||
go func(idx int) {
|
||||
c.App.ServerManager.Server.SetRating(trackIDs[idx], rating)
|
||||
if wg != nil {
|
||||
wg.Done()
|
||||
}
|
||||
}(offs + i)
|
||||
}
|
||||
}
|
||||
|
||||
if len(trackIDs) <= 5 {
|
||||
// one batch only - no need to use wait group
|
||||
batchSetRating(0, nil)
|
||||
} else {
|
||||
go func() {
|
||||
numBatches := int(math.Ceil(float64(len(trackIDs)) / float64(batchSize)))
|
||||
for i := 0; i < numBatches; i++ {
|
||||
var wg sync.WaitGroup
|
||||
batchSetRating(i*batchSize, &wg)
|
||||
wg.Wait()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Notify PlaybackManager of rating change to update
|
||||
// the in-memory track models
|
||||
for _, id := range trackIDs {
|
||||
c.App.PlaybackManager.OnTrackRatingChanged(id, rating)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
package widgets
|
||||
|
||||
import (
|
||||
"math"
|
||||
"supersonic/res"
|
||||
"supersonic/ui/layouts"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/canvas"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/driver/desktop"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
var (
|
||||
themedResStarFilled = theme.NewThemedResource(res.ResStarFilledSvg)
|
||||
themedResStarOutline = theme.NewThemedResource(res.ResStarOutlineSvg)
|
||||
)
|
||||
|
||||
type StarRating struct {
|
||||
widget.BaseWidget
|
||||
|
||||
Rating int
|
||||
StarSize float32
|
||||
|
||||
OnRatingChanged func(int)
|
||||
|
||||
holdRating bool // don't render mouseHoverRating
|
||||
mouseHoverRating int // zero if not hovered
|
||||
container *fyne.Container
|
||||
}
|
||||
|
||||
func NewStarRating() *StarRating {
|
||||
s := &StarRating{}
|
||||
s.ExtendBaseWidget(s)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StarRating) createContainer() {
|
||||
s.container = container.New(&layouts.HboxCustomPadding{
|
||||
DisableThemePad: true,
|
||||
})
|
||||
var im *canvas.Image
|
||||
for i := 0; i < 5; i++ {
|
||||
if s.Rating > i {
|
||||
im = canvas.NewImageFromResource(themedResStarFilled)
|
||||
} else {
|
||||
im = canvas.NewImageFromResource(themedResStarOutline)
|
||||
}
|
||||
im.SetMinSize(fyne.NewSize(s.StarSize, s.StarSize))
|
||||
s.container.Add(im)
|
||||
}
|
||||
}
|
||||
|
||||
var _ desktop.Hoverable = (*StarRating)(nil)
|
||||
|
||||
func (s *StarRating) MouseIn(e *desktop.MouseEvent) {
|
||||
s.MouseMoved(e)
|
||||
}
|
||||
|
||||
func (s *StarRating) MouseMoved(e *desktop.MouseEvent) {
|
||||
hoverRating := int(math.Ceil(5 * float64(e.Position.X/s.Size().Width)))
|
||||
if s.mouseHoverRating != hoverRating {
|
||||
s.holdRating = false
|
||||
s.mouseHoverRating = hoverRating
|
||||
s.Refresh()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StarRating) MouseOut() {
|
||||
s.mouseHoverRating = 0
|
||||
s.holdRating = false
|
||||
s.Refresh()
|
||||
}
|
||||
|
||||
var _ fyne.Tappable = (*StarRating)(nil)
|
||||
|
||||
func (s *StarRating) Tapped(*fyne.PointEvent) {
|
||||
if s.mouseHoverRating <= 0 {
|
||||
return //shouldn't happen
|
||||
}
|
||||
if s.Rating == s.mouseHoverRating {
|
||||
s.Rating = 0
|
||||
} else {
|
||||
s.Rating = s.mouseHoverRating
|
||||
}
|
||||
s.holdRating = true
|
||||
s.Refresh()
|
||||
if s.OnRatingChanged != nil {
|
||||
s.OnRatingChanged(s.Rating)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StarRating) Refresh() {
|
||||
// widget has not had renderer created yet
|
||||
if s.container == nil {
|
||||
return
|
||||
}
|
||||
rating := s.Rating
|
||||
if !s.holdRating && s.mouseHoverRating > 0 {
|
||||
rating = s.mouseHoverRating
|
||||
}
|
||||
for i := 0; i < 5; i++ {
|
||||
im := s.container.Objects[i].(*canvas.Image)
|
||||
im.SetMinSize(fyne.NewSize(s.StarSize, s.StarSize))
|
||||
if rating > i {
|
||||
im.Resource = themedResStarFilled
|
||||
} else {
|
||||
im.Resource = themedResStarOutline
|
||||
}
|
||||
}
|
||||
s.BaseWidget.Refresh()
|
||||
}
|
||||
|
||||
func (s *StarRating) MinSize() fyne.Size {
|
||||
return fyne.NewSize(s.StarSize*5, s.StarSize)
|
||||
}
|
||||
|
||||
func (s *StarRating) CreateRenderer() fyne.WidgetRenderer {
|
||||
if s.container == nil {
|
||||
s.createContainer()
|
||||
}
|
||||
return widget.NewSimpleRenderer(container.NewCenter(s.container))
|
||||
}
|
||||
+41
-11
@@ -26,6 +26,7 @@ const (
|
||||
ColumnTime = "Time"
|
||||
ColumnYear = "Year"
|
||||
ColumnFavorite = "Favorite"
|
||||
ColumnRating = "Rating"
|
||||
ColumnPlays = "Plays"
|
||||
ColumnBitrate = "Bitrate"
|
||||
ColumnSize = "Size"
|
||||
@@ -62,6 +63,7 @@ type Tracklist struct {
|
||||
OnAddToQueue func(trackIDs []*subsonic.Child)
|
||||
OnAddToPlaylist func(trackIDs []string)
|
||||
OnSetFavorite func(trackIDs []string, fav bool)
|
||||
OnSetRating func(trackIDs []string, rating int)
|
||||
|
||||
OnShowArtistPage func(artistID string)
|
||||
OnShowAlbumPage func(albumID string)
|
||||
@@ -83,12 +85,12 @@ type Tracklist struct {
|
||||
}
|
||||
|
||||
func NewTracklist(tracks []*subsonic.Child) *Tracklist {
|
||||
t := &Tracklist{Tracks: tracks, visibleColumns: make([]bool, 11)}
|
||||
t := &Tracklist{Tracks: tracks, visibleColumns: make([]bool, 12)}
|
||||
|
||||
t.ExtendBaseWidget(t)
|
||||
t.selectionMgr = util.NewListSelectionManager(t.lenTracks)
|
||||
// #, Title, Artist, Album, Time, Year, Favorite, Plays, Bitrate, Size, Path
|
||||
t.colLayout = layouts.NewColumnsLayout([]float32{40, -1, -1, -1, 60, 60, 47, 65, 75, 70, -1})
|
||||
// #, Title, Artist, Album, Time, Year, Favorite, Rating, Plays, Bitrate, Size, Path
|
||||
t.colLayout = layouts.NewColumnsLayout([]float32{40, -1, -1, -1, 50, 50, 45, 95, 55, 65, 70, -1})
|
||||
t.buildHeader()
|
||||
t.hdr.OnColumnVisibilityChanged = t.setColumnVisible
|
||||
t.hdr.OnColumnVisibilityMenuShown = func(pop *widget.PopUp) {
|
||||
@@ -132,6 +134,7 @@ func (t *Tracklist) buildHeader() {
|
||||
{Text: "Time", AlignTrailing: true, CanToggleVisible: true},
|
||||
{Text: "Year", AlignTrailing: true, CanToggleVisible: true},
|
||||
{Text: "Fav.", AlignTrailing: false, CanToggleVisible: true},
|
||||
{Text: "Rating", AlignTrailing: false, CanToggleVisible: true},
|
||||
{Text: "Plays", AlignTrailing: true, CanToggleVisible: true},
|
||||
{Text: "Bitrate", AlignTrailing: true, CanToggleVisible: true},
|
||||
{Text: "Size", AlignTrailing: true, CanToggleVisible: true},
|
||||
@@ -303,6 +306,18 @@ func (t *Tracklist) onSetFavorite(trackID string, fav bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tracklist) onSetRating(trackID string, rating int) {
|
||||
// update our own track model
|
||||
t.tracksMutex.RLock()
|
||||
tr := sharedutil.FindTrackByID(trackID, t.Tracks)
|
||||
t.tracksMutex.RUnlock()
|
||||
tr.UserRating = rating
|
||||
// notify listener
|
||||
if t.OnSetRating != nil {
|
||||
t.OnSetRating([]string{trackID}, rating)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tracklist) onArtistTapped(artistID string) {
|
||||
if t.OnShowArtistPage != nil {
|
||||
t.OnShowArtistPage(artistID)
|
||||
@@ -360,14 +375,16 @@ func ColNumber(colName string) int {
|
||||
return 5
|
||||
case ColumnFavorite:
|
||||
return 6
|
||||
case ColumnPlays:
|
||||
case ColumnRating:
|
||||
return 7
|
||||
case ColumnBitrate:
|
||||
case ColumnPlays:
|
||||
return 8
|
||||
case ColumnSize:
|
||||
case ColumnBitrate:
|
||||
return 9
|
||||
case ColumnPath:
|
||||
case ColumnSize:
|
||||
return 10
|
||||
case ColumnPath:
|
||||
return 11
|
||||
default:
|
||||
log.Printf("error: Tracklist: invalid column name %s", colName)
|
||||
return -100
|
||||
@@ -388,12 +405,14 @@ func colName(i int) string {
|
||||
case 6:
|
||||
return ColumnFavorite
|
||||
case 7:
|
||||
return ColumnPlays
|
||||
return ColumnRating
|
||||
case 8:
|
||||
return ColumnBitrate
|
||||
return ColumnPlays
|
||||
case 9:
|
||||
return ColumnSize
|
||||
return ColumnBitrate
|
||||
case 10:
|
||||
return ColumnSize
|
||||
case 11:
|
||||
return ColumnPath
|
||||
default:
|
||||
return ""
|
||||
@@ -421,6 +440,7 @@ type TrackRow struct {
|
||||
dur *widget.RichText
|
||||
year *widget.RichText
|
||||
favorite *fyne.Container
|
||||
rating *StarRating
|
||||
bitrate *widget.RichText
|
||||
plays *widget.RichText
|
||||
size *widget.RichText
|
||||
@@ -449,6 +469,9 @@ func NewTrackRow(tracklist *Tracklist, playingIcon fyne.CanvasObject) *TrackRow
|
||||
favorite := NewTappbaleIcon(res.ResHeartOutlineInvertPng)
|
||||
favorite.OnTapped = t.toggleFavorited
|
||||
t.favorite = container.NewCenter(favorite)
|
||||
t.rating = NewStarRating()
|
||||
t.rating.StarSize = 16
|
||||
t.rating.OnRatingChanged = t.setTrackRating
|
||||
t.plays = widget.NewRichTextWithText("")
|
||||
t.plays.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignTrailing
|
||||
t.bitrate = widget.NewRichTextWithText("")
|
||||
@@ -459,7 +482,7 @@ func NewTrackRow(tracklist *Tracklist, playingIcon fyne.CanvasObject) *TrackRow
|
||||
t.path.Wrapping = fyne.TextTruncate
|
||||
|
||||
t.Content = container.New(tracklist.colLayout,
|
||||
t.num, t.name, t.artist, t.album, t.dur, t.year, t.favorite, t.plays, t.bitrate, t.size, t.path)
|
||||
t.num, t.name, t.artist, t.album, t.dur, t.year, t.favorite, t.rating, t.plays, t.bitrate, t.size, t.path)
|
||||
return t
|
||||
}
|
||||
|
||||
@@ -546,12 +569,15 @@ func (t *TrackRow) Update(tr *subsonic.Child, rowNum int) {
|
||||
t.favorite.Objects[0].(*TappableIcon).Resource = res.ResHeartFilledInvertPng
|
||||
}
|
||||
|
||||
t.rating.Rating = tr.UserRating
|
||||
|
||||
// Show only columns configured to be visible
|
||||
t.artist.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnArtist)]
|
||||
t.album.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnAlbum)]
|
||||
t.dur.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnTime)]
|
||||
t.year.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnYear)]
|
||||
t.favorite.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnFavorite)]
|
||||
t.rating.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnRating)]
|
||||
t.plays.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnPlays)]
|
||||
t.bitrate.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnBitrate)]
|
||||
t.size.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnSize)]
|
||||
@@ -574,6 +600,10 @@ func (t *TrackRow) toggleFavorited() {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TrackRow) setTrackRating(rating int) {
|
||||
t.tracklist.onSetRating(t.trackID, rating)
|
||||
}
|
||||
|
||||
func (t *TrackRow) TappedSecondary(e *fyne.PointEvent) {
|
||||
if t.OnTappedSecondary != nil {
|
||||
t.OnTappedSecondary(e, t.trackIdx)
|
||||
|
||||
Reference in New Issue
Block a user