add favorite (aka starred) column to tracklist
This commit is contained in:
@@ -3,7 +3,6 @@ package widgets
|
||||
import (
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/driver/desktop"
|
||||
"fyne.io/fyne/v2/layout"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
@@ -56,37 +55,10 @@ func (v *volumeSlider) MinSize() fyne.Size {
|
||||
return fyne.NewSize(v.Width, h)
|
||||
}
|
||||
|
||||
type tappableIcon struct {
|
||||
widget.Icon
|
||||
|
||||
OnTapped func()
|
||||
}
|
||||
|
||||
func newTappableIcon(res fyne.Resource) *tappableIcon {
|
||||
icon := &tappableIcon{}
|
||||
icon.ExtendBaseWidget(icon)
|
||||
icon.SetResource(res)
|
||||
|
||||
return icon
|
||||
}
|
||||
|
||||
func (t *tappableIcon) Tapped(_ *fyne.PointEvent) {
|
||||
if t.OnTapped != nil {
|
||||
t.OnTapped()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tappableIcon) TappedSecondary(_ *fyne.PointEvent) {
|
||||
}
|
||||
|
||||
func (t *tappableIcon) Cursor() desktop.Cursor {
|
||||
return desktop.PointerCursor
|
||||
}
|
||||
|
||||
type VolumeControl struct {
|
||||
widget.BaseWidget
|
||||
|
||||
icon *tappableIcon
|
||||
icon *TappableIcon
|
||||
slider *volumeSlider
|
||||
|
||||
OnVolumeChanged func(int)
|
||||
@@ -100,7 +72,7 @@ type VolumeControl struct {
|
||||
func NewVolumeControl(initialVol int) *VolumeControl {
|
||||
v := &VolumeControl{}
|
||||
v.ExtendBaseWidget(v)
|
||||
v.icon = newTappableIcon(theme.VolumeUpIcon())
|
||||
v.icon = NewTappbaleIcon(theme.VolumeUpIcon())
|
||||
v.icon.OnTapped = v.toggleMute
|
||||
v.slider = NewVolumeSlider(100)
|
||||
v.lastVol = initialVol
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package widgets
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/driver/desktop"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
type TappableIcon struct {
|
||||
widget.Icon
|
||||
|
||||
OnTapped func()
|
||||
}
|
||||
|
||||
func NewTappbaleIcon(res fyne.Resource) *TappableIcon {
|
||||
icon := &TappableIcon{}
|
||||
icon.ExtendBaseWidget(icon)
|
||||
icon.SetResource(res)
|
||||
|
||||
return icon
|
||||
}
|
||||
|
||||
func (t *TappableIcon) Tapped(_ *fyne.PointEvent) {
|
||||
if t.OnTapped != nil {
|
||||
t.OnTapped()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TappableIcon) TappedSecondary(_ *fyne.PointEvent) {
|
||||
}
|
||||
|
||||
func (t *TappableIcon) Cursor() desktop.Cursor {
|
||||
return desktop.PointerCursor
|
||||
}
|
||||
+68
-26
@@ -4,6 +4,7 @@ import (
|
||||
"log"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"supersonic/res"
|
||||
"supersonic/ui/layouts"
|
||||
"supersonic/ui/os"
|
||||
"supersonic/ui/util"
|
||||
@@ -19,12 +20,13 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
ColumnArtist = "Artist"
|
||||
ColumnAlbum = "Album"
|
||||
ColumnTime = "Time"
|
||||
ColumnYear = "Year"
|
||||
ColumnPlays = "Plays"
|
||||
ColumnBitrate = "Bitrate"
|
||||
ColumnArtist = "Artist"
|
||||
ColumnAlbum = "Album"
|
||||
ColumnTime = "Time"
|
||||
ColumnYear = "Year"
|
||||
ColumnFavorite = "Favorite"
|
||||
ColumnPlays = "Plays"
|
||||
ColumnBitrate = "Bitrate"
|
||||
)
|
||||
|
||||
type Tracklist struct {
|
||||
@@ -41,6 +43,7 @@ type Tracklist struct {
|
||||
OnPlaySelection func(tracks []*subsonic.Child)
|
||||
OnAddToQueue func(trackIDs []*subsonic.Child)
|
||||
OnAddToPlaylist func(trackIDs []string)
|
||||
OnSetFavorite func(trackIDs []string, fav bool)
|
||||
|
||||
visibleColumns []bool
|
||||
|
||||
@@ -54,11 +57,12 @@ type Tracklist struct {
|
||||
}
|
||||
|
||||
func NewTracklist(tracks []*subsonic.Child) *Tracklist {
|
||||
t := &Tracklist{Tracks: tracks, nowPlayingIdx: -1, visibleColumns: make([]bool, 8)}
|
||||
t := &Tracklist{Tracks: tracks, nowPlayingIdx: -1, visibleColumns: make([]bool, 9)}
|
||||
|
||||
t.ExtendBaseWidget(t)
|
||||
t.selectionMgr = util.NewListSelectionManager(func() int { return len(t.Tracks) })
|
||||
t.colLayout = layouts.NewColumnsLayout([]float32{35, -1, -1, -1, 60, 60, 65, 75})
|
||||
// #, Title, Artist, Album, Time, Year, Favorite, Plays, Bitrate
|
||||
t.colLayout = layouts.NewColumnsLayout([]float32{35, -1, -1, -1, 60, 60, 47, 65, 75})
|
||||
t.buildHeader()
|
||||
t.hdr.OnColumnVisibilityChanged = t.setColumnVisible
|
||||
playingIcon := container.NewCenter(container.NewHBox(NewHSpace(2), widget.NewIcon(theme.MediaPlayIcon())))
|
||||
@@ -93,6 +97,7 @@ func (t *Tracklist) buildHeader() {
|
||||
{Text: "Album", AlignTrailing: false, CanToggleVisible: true},
|
||||
{Text: "Time", AlignTrailing: true, CanToggleVisible: true},
|
||||
{Text: "Year", AlignTrailing: true, CanToggleVisible: true},
|
||||
{Text: "Fav.", AlignTrailing: false, CanToggleVisible: true},
|
||||
{Text: "Plays", AlignTrailing: true, CanToggleVisible: true},
|
||||
{Text: "Bitrate", AlignTrailing: true, CanToggleVisible: true}},
|
||||
t.colLayout)
|
||||
@@ -239,6 +244,12 @@ func (t *Tracklist) onShowContextMenu(e *fyne.PointEvent, trackIdx int) {
|
||||
widget.ShowPopUpMenuAtPosition(t.ctxMenu, fyne.CurrentApp().Driver().CanvasForObject(t), e.AbsolutePosition)
|
||||
}
|
||||
|
||||
func (t *Tracklist) onSetFavorite(trackID string, fav bool) {
|
||||
if t.OnSetFavorite != nil {
|
||||
t.OnSetFavorite([]string{trackID}, fav)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tracklist) selectedTracks() []*subsonic.Child {
|
||||
sel := t.selectionMgr.GetSelection()
|
||||
tracks := make([]*subsonic.Child, 0, len(sel))
|
||||
@@ -272,10 +283,12 @@ func ColNumber(colName string) int {
|
||||
return 4
|
||||
case ColumnYear:
|
||||
return 5
|
||||
case ColumnPlays:
|
||||
case ColumnFavorite:
|
||||
return 6
|
||||
case ColumnBitrate:
|
||||
case ColumnPlays:
|
||||
return 7
|
||||
case ColumnBitrate:
|
||||
return 8
|
||||
default:
|
||||
log.Printf("error: Tracklist: invalid column name %s", colName)
|
||||
return -100
|
||||
@@ -294,8 +307,10 @@ func colName(i int) string {
|
||||
case 5:
|
||||
return ColumnYear
|
||||
case 6:
|
||||
return ColumnPlays
|
||||
return ColumnFavorite
|
||||
case 7:
|
||||
return ColumnPlays
|
||||
case 8:
|
||||
return ColumnBitrate
|
||||
default:
|
||||
return ""
|
||||
@@ -306,21 +321,23 @@ type TrackRow struct {
|
||||
widget.BaseWidget
|
||||
|
||||
// internal state
|
||||
tracklist *Tracklist
|
||||
trackIdx int
|
||||
trackID string
|
||||
isPlaying bool
|
||||
playCount int64
|
||||
tappedAt int64 // unixMillis
|
||||
tracklist *Tracklist
|
||||
trackIdx int
|
||||
trackID string
|
||||
isPlaying bool
|
||||
isFavorite bool
|
||||
playCount int64
|
||||
tappedAt int64 // unixMillis
|
||||
|
||||
num *widget.RichText
|
||||
name *widget.RichText
|
||||
artist *widget.RichText
|
||||
album *widget.RichText
|
||||
dur *widget.RichText
|
||||
year *widget.RichText
|
||||
bitrate *widget.RichText
|
||||
plays *widget.RichText
|
||||
num *widget.RichText
|
||||
name *widget.RichText
|
||||
artist *widget.RichText
|
||||
album *widget.RichText
|
||||
dur *widget.RichText
|
||||
year *widget.RichText
|
||||
favorite *fyne.Container
|
||||
bitrate *widget.RichText
|
||||
plays *widget.RichText
|
||||
|
||||
OnTapped func()
|
||||
OnDoubleTapped func()
|
||||
@@ -346,6 +363,9 @@ func NewTrackRow(tracklist *Tracklist, playingIcon fyne.CanvasObject) *TrackRow
|
||||
t.dur.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignTrailing
|
||||
t.year = widget.NewRichTextWithText("")
|
||||
t.year.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignTrailing
|
||||
favorite := NewTappbaleIcon(res.ResHeartOutlineInvertPng)
|
||||
favorite.OnTapped = t.toggleFavorited
|
||||
t.favorite = container.NewCenter(favorite)
|
||||
t.plays = widget.NewRichTextWithText("")
|
||||
t.plays.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignTrailing
|
||||
t.bitrate = widget.NewRichTextWithText("")
|
||||
@@ -355,7 +375,7 @@ func NewTrackRow(tracklist *Tracklist, playingIcon fyne.CanvasObject) *TrackRow
|
||||
t.selectionRect.Hidden = true
|
||||
t.container = container.NewMax(t.selectionRect,
|
||||
container.New(tracklist.colLayout,
|
||||
t.num, t.name, t.artist, t.album, t.dur, t.year, t.plays, t.bitrate))
|
||||
t.num, t.name, t.artist, t.album, t.dur, t.year, t.favorite, t.plays, t.bitrate))
|
||||
return t
|
||||
}
|
||||
|
||||
@@ -374,6 +394,7 @@ func (t *TrackRow) Update(tr *subsonic.Child, isPlaying bool, rowNum int) {
|
||||
t.album.Segments[0].(*widget.TextSegment).Text = tr.Album
|
||||
t.dur.Segments[0].(*widget.TextSegment).Text = util.SecondsToTimeString(float64(tr.Duration))
|
||||
t.year.Segments[0].(*widget.TextSegment).Text = strconv.Itoa(tr.Year)
|
||||
|
||||
t.plays.Segments[0].(*widget.TextSegment).Text = strconv.Itoa(int(tr.PlayCount))
|
||||
t.bitrate.Segments[0].(*widget.TextSegment).Text = strconv.Itoa(tr.BitRate)
|
||||
|
||||
@@ -392,16 +413,37 @@ func (t *TrackRow) Update(tr *subsonic.Child, isPlaying bool, rowNum int) {
|
||||
}
|
||||
}
|
||||
|
||||
if tr.Starred.IsZero() && t.isFavorite {
|
||||
t.isFavorite = false
|
||||
t.favorite.Objects[0].(*TappableIcon).Resource = res.ResHeartOutlineInvertPng
|
||||
} else if !t.isFavorite {
|
||||
t.isFavorite = true
|
||||
t.favorite.Objects[0].(*TappableIcon).Resource = res.ResHeartFilledInvertPng
|
||||
}
|
||||
|
||||
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.plays.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnPlays)]
|
||||
t.bitrate.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnBitrate)]
|
||||
|
||||
t.Refresh()
|
||||
}
|
||||
|
||||
func (t *TrackRow) toggleFavorited() {
|
||||
if t.isFavorite {
|
||||
t.favorite.Objects[0].(*TappableIcon).Resource = res.ResHeartOutlineInvertPng
|
||||
t.favorite.Refresh()
|
||||
t.tracklist.onSetFavorite(t.trackID, false)
|
||||
} else {
|
||||
t.favorite.Objects[0].(*TappableIcon).Resource = res.ResHeartFilledInvertPng
|
||||
t.favorite.Refresh()
|
||||
t.tracklist.onSetFavorite(t.trackID, true)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TrackRow) CreateRenderer() fyne.WidgetRenderer {
|
||||
return widget.NewSimpleRenderer(t.container)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user