Files
supersonic/ui/widgets/playercontrols.go
T
2023-01-03 16:43:53 -08:00

154 lines
3.5 KiB
Go

package widgets
import (
"supersonic/ui/util"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
// TrackPosSlider is a custom slider that doesn't trigger
// the seek action until drag end.
type TrackPosSlider struct {
widget.Slider
OnDragEnd func(float64)
isDragging bool
lastDraggedValue float64
}
func NewTrackPosSlider() *TrackPosSlider {
slider := &TrackPosSlider{
Slider: widget.Slider{
Value: 0,
Min: 0,
Max: 1,
Step: 0.002,
Orientation: widget.Horizontal,
},
}
slider.ExtendBaseWidget(slider)
return slider
}
func (t *TrackPosSlider) DragEnd() {
t.isDragging = false
t.Slider.DragEnd()
if t.OnDragEnd != nil {
t.OnDragEnd(t.lastDraggedValue)
}
}
func (t *TrackPosSlider) Dragged(e *fyne.DragEvent) {
t.isDragging = true
t.Slider.Dragged(e)
t.lastDraggedValue = t.Value
}
func (t *TrackPosSlider) IsDragging() bool {
return t.isDragging
}
type PlayerControls struct {
widget.BaseWidget
slider *TrackPosSlider
curTimeLabel *widget.Label
totalTimeLabel *widget.Label
prev *widget.Button
playpause *widget.Button
next *widget.Button
container *fyne.Container
totalTime float64
}
var _ fyne.Widget = (*PlayerControls)(nil)
// NewPlayerControls sets up the seek bar, and transport buttons.
func NewPlayerControls() *PlayerControls {
pc := &PlayerControls{}
pc.ExtendBaseWidget(pc)
pc.slider = NewTrackPosSlider()
pc.curTimeLabel = widget.NewLabel(util.SecondsToTimeString(0))
pc.totalTimeLabel = widget.NewLabel(util.SecondsToTimeString(0))
pc.slider.OnChanged = func(f float64) {
time := f * pc.totalTime
pc.curTimeLabel.SetText(util.SecondsToTimeString(time))
}
pc.prev = widget.NewButtonWithIcon("", theme.MediaSkipPreviousIcon(), func() {})
pc.next = widget.NewButtonWithIcon("", theme.MediaSkipNextIcon(), func() {})
pc.playpause = widget.NewButtonWithIcon("", theme.MediaPlayIcon(), func() {})
buttons := container.NewHBox(pc.prev, pc.playpause, pc.next)
b := container.New(layout.NewCenterLayout(), buttons)
c := container.NewBorder(nil, nil, pc.curTimeLabel, pc.totalTimeLabel, pc.slider)
pc.container = container.NewVBox(c, b)
return pc
}
func (pc *PlayerControls) OnSeek(f func(float64)) {
pc.slider.OnDragEnd = f
}
func (pc *PlayerControls) OnSeekPrevious(f func()) {
pc.prev.OnTapped = f
}
func (pc *PlayerControls) OnSeekNext(f func()) {
pc.next.OnTapped = f
}
func (pc *PlayerControls) OnPlayPause(f func()) {
pc.playpause.OnTapped = f
}
func (pc *PlayerControls) SetPlaying(playing bool) {
if playing {
pc.playpause.SetIcon(theme.MediaPauseIcon())
} else {
pc.playpause.SetIcon(theme.MediaPlayIcon())
}
}
func (pc *PlayerControls) UpdatePlayTime(curTime, totalTime float64) {
// TODO: there is a bug with very long tracks (~20min) where the
// curtime label will bounce back and forth +- 1sec (rounding issue?)
pc.totalTime = totalTime
v := 0.0
if totalTime > 0 {
v = curTime / totalTime
}
updated := false
tt := util.SecondsToTimeString(totalTime)
if tt != pc.totalTimeLabel.Text {
pc.totalTimeLabel.SetText(tt)
updated = true
}
if !pc.slider.IsDragging() {
ct := util.SecondsToTimeString(curTime)
if ct != pc.curTimeLabel.Text {
pc.curTimeLabel.SetText(ct)
updated = true
}
if updated {
// Only update slider once a second when time label changes
pc.slider.SetValue(v)
}
}
}
func (p *PlayerControls) CreateRenderer() fyne.WidgetRenderer {
return widget.NewSimpleRenderer(p.container)
}