add favorite (aka starred) column to tracklist

This commit is contained in:
Drew Weymouth
2023-02-21 18:10:10 -08:00
parent c4dba8f1fb
commit ae2ff2df2f
3 changed files with 104 additions and 56 deletions
+2 -30
View File
@@ -3,7 +3,6 @@ package widgets
import ( import (
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/driver/desktop"
"fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
@@ -56,37 +55,10 @@ func (v *volumeSlider) MinSize() fyne.Size {
return fyne.NewSize(v.Width, h) 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 { type VolumeControl struct {
widget.BaseWidget widget.BaseWidget
icon *tappableIcon icon *TappableIcon
slider *volumeSlider slider *volumeSlider
OnVolumeChanged func(int) OnVolumeChanged func(int)
@@ -100,7 +72,7 @@ type VolumeControl struct {
func NewVolumeControl(initialVol int) *VolumeControl { func NewVolumeControl(initialVol int) *VolumeControl {
v := &VolumeControl{} v := &VolumeControl{}
v.ExtendBaseWidget(v) v.ExtendBaseWidget(v)
v.icon = newTappableIcon(theme.VolumeUpIcon()) v.icon = NewTappbaleIcon(theme.VolumeUpIcon())
v.icon.OnTapped = v.toggleMute v.icon.OnTapped = v.toggleMute
v.slider = NewVolumeSlider(100) v.slider = NewVolumeSlider(100)
v.lastVol = initialVol v.lastVol = initialVol
+34
View File
@@ -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
View File
@@ -4,6 +4,7 @@ import (
"log" "log"
"runtime" "runtime"
"strconv" "strconv"
"supersonic/res"
"supersonic/ui/layouts" "supersonic/ui/layouts"
"supersonic/ui/os" "supersonic/ui/os"
"supersonic/ui/util" "supersonic/ui/util"
@@ -19,12 +20,13 @@ import (
) )
const ( const (
ColumnArtist = "Artist" ColumnArtist = "Artist"
ColumnAlbum = "Album" ColumnAlbum = "Album"
ColumnTime = "Time" ColumnTime = "Time"
ColumnYear = "Year" ColumnYear = "Year"
ColumnPlays = "Plays" ColumnFavorite = "Favorite"
ColumnBitrate = "Bitrate" ColumnPlays = "Plays"
ColumnBitrate = "Bitrate"
) )
type Tracklist struct { type Tracklist struct {
@@ -41,6 +43,7 @@ type Tracklist struct {
OnPlaySelection func(tracks []*subsonic.Child) OnPlaySelection func(tracks []*subsonic.Child)
OnAddToQueue func(trackIDs []*subsonic.Child) OnAddToQueue func(trackIDs []*subsonic.Child)
OnAddToPlaylist func(trackIDs []string) OnAddToPlaylist func(trackIDs []string)
OnSetFavorite func(trackIDs []string, fav bool)
visibleColumns []bool visibleColumns []bool
@@ -54,11 +57,12 @@ type Tracklist struct {
} }
func NewTracklist(tracks []*subsonic.Child) *Tracklist { 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.ExtendBaseWidget(t)
t.selectionMgr = util.NewListSelectionManager(func() int { return len(t.Tracks) }) 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.buildHeader()
t.hdr.OnColumnVisibilityChanged = t.setColumnVisible t.hdr.OnColumnVisibilityChanged = t.setColumnVisible
playingIcon := container.NewCenter(container.NewHBox(NewHSpace(2), widget.NewIcon(theme.MediaPlayIcon()))) 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: "Album", AlignTrailing: false, CanToggleVisible: true},
{Text: "Time", AlignTrailing: true, CanToggleVisible: true}, {Text: "Time", AlignTrailing: true, CanToggleVisible: true},
{Text: "Year", AlignTrailing: true, CanToggleVisible: true}, {Text: "Year", AlignTrailing: true, CanToggleVisible: true},
{Text: "Fav.", AlignTrailing: false, CanToggleVisible: true},
{Text: "Plays", AlignTrailing: true, CanToggleVisible: true}, {Text: "Plays", AlignTrailing: true, CanToggleVisible: true},
{Text: "Bitrate", AlignTrailing: true, CanToggleVisible: true}}, {Text: "Bitrate", AlignTrailing: true, CanToggleVisible: true}},
t.colLayout) 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) 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 { func (t *Tracklist) selectedTracks() []*subsonic.Child {
sel := t.selectionMgr.GetSelection() sel := t.selectionMgr.GetSelection()
tracks := make([]*subsonic.Child, 0, len(sel)) tracks := make([]*subsonic.Child, 0, len(sel))
@@ -272,10 +283,12 @@ func ColNumber(colName string) int {
return 4 return 4
case ColumnYear: case ColumnYear:
return 5 return 5
case ColumnPlays: case ColumnFavorite:
return 6 return 6
case ColumnBitrate: case ColumnPlays:
return 7 return 7
case ColumnBitrate:
return 8
default: default:
log.Printf("error: Tracklist: invalid column name %s", colName) log.Printf("error: Tracklist: invalid column name %s", colName)
return -100 return -100
@@ -294,8 +307,10 @@ func colName(i int) string {
case 5: case 5:
return ColumnYear return ColumnYear
case 6: case 6:
return ColumnPlays return ColumnFavorite
case 7: case 7:
return ColumnPlays
case 8:
return ColumnBitrate return ColumnBitrate
default: default:
return "" return ""
@@ -306,21 +321,23 @@ type TrackRow struct {
widget.BaseWidget widget.BaseWidget
// internal state // internal state
tracklist *Tracklist tracklist *Tracklist
trackIdx int trackIdx int
trackID string trackID string
isPlaying bool isPlaying bool
playCount int64 isFavorite bool
tappedAt int64 // unixMillis playCount int64
tappedAt int64 // unixMillis
num *widget.RichText num *widget.RichText
name *widget.RichText name *widget.RichText
artist *widget.RichText artist *widget.RichText
album *widget.RichText album *widget.RichText
dur *widget.RichText dur *widget.RichText
year *widget.RichText year *widget.RichText
bitrate *widget.RichText favorite *fyne.Container
plays *widget.RichText bitrate *widget.RichText
plays *widget.RichText
OnTapped func() OnTapped func()
OnDoubleTapped 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.dur.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignTrailing
t.year = widget.NewRichTextWithText("") t.year = widget.NewRichTextWithText("")
t.year.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignTrailing 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 = widget.NewRichTextWithText("")
t.plays.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignTrailing t.plays.Segments[0].(*widget.TextSegment).Style.Alignment = fyne.TextAlignTrailing
t.bitrate = widget.NewRichTextWithText("") t.bitrate = widget.NewRichTextWithText("")
@@ -355,7 +375,7 @@ func NewTrackRow(tracklist *Tracklist, playingIcon fyne.CanvasObject) *TrackRow
t.selectionRect.Hidden = true t.selectionRect.Hidden = true
t.container = container.NewMax(t.selectionRect, t.container = container.NewMax(t.selectionRect,
container.New(tracklist.colLayout, 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 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.album.Segments[0].(*widget.TextSegment).Text = tr.Album
t.dur.Segments[0].(*widget.TextSegment).Text = util.SecondsToTimeString(float64(tr.Duration)) t.dur.Segments[0].(*widget.TextSegment).Text = util.SecondsToTimeString(float64(tr.Duration))
t.year.Segments[0].(*widget.TextSegment).Text = strconv.Itoa(tr.Year) t.year.Segments[0].(*widget.TextSegment).Text = strconv.Itoa(tr.Year)
t.plays.Segments[0].(*widget.TextSegment).Text = strconv.Itoa(int(tr.PlayCount)) t.plays.Segments[0].(*widget.TextSegment).Text = strconv.Itoa(int(tr.PlayCount))
t.bitrate.Segments[0].(*widget.TextSegment).Text = strconv.Itoa(tr.BitRate) 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.artist.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnArtist)]
t.album.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnAlbum)] t.album.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnAlbum)]
t.dur.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnTime)] t.dur.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnTime)]
t.year.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnYear)] 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.plays.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnPlays)]
t.bitrate.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnBitrate)] t.bitrate.Hidden = !t.tracklist.visibleColumns[ColNumber(ColumnBitrate)]
t.Refresh() 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 { func (t *TrackRow) CreateRenderer() fyne.WidgetRenderer {
return widget.NewSimpleRenderer(t.container) return widget.NewSimpleRenderer(t.container)
} }