move shuffle and repeat controls into main player controls (#862)
Shuffle moves to the far left of the transport buttons and repeat to the far right, flanking prev/play/next. Both use a new IconButtonSizeSmallest (10% smaller than IconButtonSizeSmaller) with 2dp spacing separating them from the adjacent transport buttons. Wiring updated in bottompanel.go accordingly. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
e31f546e09
commit
b40c154f7e
+12
-12
@@ -88,7 +88,7 @@ func NewBottomPanel(pm *backend.PlaybackManager, im *backend.ImageManager, contr
|
|||||||
contr.ShowShareDialog(tr.ID)
|
contr.ShowShareDialog(tr.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bp.Controls = widgets.NewPlayerControls(cfg.Playback.UseWaveformSeekbar)
|
bp.Controls = widgets.NewPlayerControls(cfg.Playback.UseWaveformSeekbar, pm.GetLoopMode(), pm.IsShuffle())
|
||||||
bp.Controls.OnPlayPause(func() {
|
bp.Controls.OnPlayPause(func() {
|
||||||
pm.PlayPause()
|
pm.PlayPause()
|
||||||
})
|
})
|
||||||
@@ -101,14 +101,20 @@ func NewBottomPanel(pm *backend.PlaybackManager, im *backend.ImageManager, contr
|
|||||||
bp.Controls.OnSeek(func(f float64) {
|
bp.Controls.OnSeek(func(f float64) {
|
||||||
pm.SeekFraction(f)
|
pm.SeekFraction(f)
|
||||||
})
|
})
|
||||||
|
bp.Controls.OnChangeLoopMode(func() {
|
||||||
bp.AuxControls = widgets.NewAuxControls(pm.Volume(), pm.GetLoopMode(), pm.IsAutoplay(), pm.IsShuffle())
|
pm.SetNextLoopMode()
|
||||||
|
})
|
||||||
|
bp.Controls.OnChangeShuffle = func(shuffle bool) {
|
||||||
|
pm.SetShuffle(shuffle)
|
||||||
|
}
|
||||||
pm.OnLoopModeChange(func(lm backend.LoopMode) {
|
pm.OnLoopModeChange(func(lm backend.LoopMode) {
|
||||||
fyne.Do(func() { bp.AuxControls.SetLoopMode(lm) })
|
fyne.Do(func() { bp.Controls.SetLoopMode(lm) })
|
||||||
})
|
})
|
||||||
pm.OnShuffleChange(func(lm bool) {
|
pm.OnShuffleChange(func(sh bool) {
|
||||||
fyne.Do(func() { bp.AuxControls.SetShuffle(lm) })
|
fyne.Do(func() { bp.Controls.SetShuffle(sh) })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
bp.AuxControls = widgets.NewAuxControls(pm.Volume(), pm.IsAutoplay())
|
||||||
pm.OnVolumeChange(func(vol int) {
|
pm.OnVolumeChange(func(vol int) {
|
||||||
fyne.Do(func() { bp.AuxControls.VolumeControl.SetVolume(vol) })
|
fyne.Do(func() { bp.AuxControls.VolumeControl.SetVolume(vol) })
|
||||||
})
|
})
|
||||||
@@ -119,15 +125,9 @@ func NewBottomPanel(pm *backend.PlaybackManager, im *backend.ImageManager, contr
|
|||||||
bp.AuxControls.VolumeControl.OnSetVolume = func(v int) {
|
bp.AuxControls.VolumeControl.OnSetVolume = func(v int) {
|
||||||
pm.SetVolume(v)
|
pm.SetVolume(v)
|
||||||
}
|
}
|
||||||
bp.AuxControls.OnChangeLoopMode(func() {
|
|
||||||
pm.SetNextLoopMode()
|
|
||||||
})
|
|
||||||
bp.AuxControls.OnChangeAutoplay = func(autoplay bool) {
|
bp.AuxControls.OnChangeAutoplay = func(autoplay bool) {
|
||||||
pm.SetAutoplay(autoplay)
|
pm.SetAutoplay(autoplay)
|
||||||
}
|
}
|
||||||
bp.AuxControls.OnChangeShuffle = func(shuffle bool) {
|
|
||||||
pm.SetShuffle(shuffle)
|
|
||||||
}
|
|
||||||
bp.AuxControls.OnShowPlayQueue(contr.ShowPopUpPlayQueue)
|
bp.AuxControls.OnShowPlayQueue(contr.ShowPopUpPlayQueue)
|
||||||
bp.AuxControls.OnShowCastMenu(contr.ShowCastMenu)
|
bp.AuxControls.OnShowCastMenu(contr.ShowCastMenu)
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"fyne.io/fyne/v2/theme"
|
"fyne.io/fyne/v2/theme"
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
|
|
||||||
"github.com/dweymouth/supersonic/backend"
|
|
||||||
myTheme "github.com/dweymouth/supersonic/ui/theme"
|
myTheme "github.com/dweymouth/supersonic/ui/theme"
|
||||||
"github.com/dweymouth/supersonic/ui/util"
|
"github.com/dweymouth/supersonic/ui/util"
|
||||||
)
|
)
|
||||||
@@ -22,42 +21,23 @@ type AuxControls struct {
|
|||||||
widget.BaseWidget
|
widget.BaseWidget
|
||||||
|
|
||||||
OnChangeAutoplay func(autoplay bool)
|
OnChangeAutoplay func(autoplay bool)
|
||||||
OnChangeShuffle func(shuffle bool)
|
|
||||||
|
|
||||||
VolumeControl *VolumeControl
|
VolumeControl *VolumeControl
|
||||||
shuffle *IconButton
|
|
||||||
autoplay *IconButton
|
autoplay *IconButton
|
||||||
loop *IconButton
|
|
||||||
cast *IconButton
|
cast *IconButton
|
||||||
showQueue *IconButton
|
showQueue *IconButton
|
||||||
|
|
||||||
container *fyne.Container
|
container *fyne.Container
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuxControls(initialVolume int, initialLoopMode backend.LoopMode, initialAutoplay bool, initialShuffle bool) *AuxControls {
|
func NewAuxControls(initialVolume int, initialAutoplay bool) *AuxControls {
|
||||||
a := &AuxControls{
|
a := &AuxControls{
|
||||||
VolumeControl: NewVolumeControl(initialVolume),
|
VolumeControl: NewVolumeControl(initialVolume),
|
||||||
shuffle: NewIconButton(myTheme.ShuffleIcon, nil),
|
|
||||||
autoplay: NewIconButton(myTheme.AutoplayIcon, nil),
|
autoplay: NewIconButton(myTheme.AutoplayIcon, nil),
|
||||||
loop: NewIconButton(myTheme.RepeatIcon, nil),
|
|
||||||
cast: NewIconButton(myTheme.CastIcon, nil),
|
cast: NewIconButton(myTheme.CastIcon, nil),
|
||||||
showQueue: NewIconButton(myTheme.PlayQueueIcon, nil),
|
showQueue: NewIconButton(myTheme.PlayQueueIcon, nil),
|
||||||
}
|
}
|
||||||
|
|
||||||
a.shuffle.Highlighted = initialShuffle
|
|
||||||
a.shuffle.IconSize = IconButtonSizeSmaller
|
|
||||||
a.shuffle.SetToolTip(lang.L("Shuffle"))
|
|
||||||
a.shuffle.OnTapped = func() {
|
|
||||||
a.SetShuffle(!a.shuffle.Highlighted)
|
|
||||||
if a.OnChangeShuffle != nil {
|
|
||||||
a.OnChangeShuffle(a.shuffle.Highlighted)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a.loop.IconSize = IconButtonSizeSmaller
|
|
||||||
a.loop.SetToolTip(lang.L("Repeat"))
|
|
||||||
a.SetLoopMode(initialLoopMode)
|
|
||||||
|
|
||||||
a.cast.IconSize = IconButtonSizeSmaller
|
a.cast.IconSize = IconButtonSizeSmaller
|
||||||
a.cast.SetToolTip(lang.L("Cast to device"))
|
a.cast.SetToolTip(lang.L("Cast to device"))
|
||||||
|
|
||||||
@@ -81,7 +61,7 @@ func NewAuxControls(initialVolume int, initialLoopMode backend.LoopMode, initial
|
|||||||
a.VolumeControl,
|
a.VolumeControl,
|
||||||
container.New(
|
container.New(
|
||||||
layout.NewCustomPaddedHBoxLayout(theme.Padding()*1.5),
|
layout.NewCustomPaddedHBoxLayout(theme.Padding()*1.5),
|
||||||
layout.NewSpacer(), a.autoplay, a.shuffle, a.loop, a.cast, a.showQueue, util.NewHSpace(5)),
|
layout.NewSpacer(), a.autoplay, a.cast, a.showQueue, util.NewHSpace(5)),
|
||||||
layout.NewSpacer(),
|
layout.NewSpacer(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -93,32 +73,6 @@ func (a *AuxControls) CreateRenderer() fyne.WidgetRenderer {
|
|||||||
return widget.NewSimpleRenderer(a.container)
|
return widget.NewSimpleRenderer(a.container)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AuxControls) OnChangeLoopMode(f func()) {
|
|
||||||
a.loop.OnTapped = f
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AuxControls) SetLoopMode(mode backend.LoopMode) {
|
|
||||||
switch mode {
|
|
||||||
case backend.LoopAll:
|
|
||||||
a.loop.Highlighted = true
|
|
||||||
a.loop.SetIcon(myTheme.RepeatIcon)
|
|
||||||
case backend.LoopOne:
|
|
||||||
a.loop.Highlighted = true
|
|
||||||
a.loop.SetIcon(myTheme.RepeatOneIcon)
|
|
||||||
case backend.LoopNone:
|
|
||||||
a.loop.Highlighted = false
|
|
||||||
a.loop.SetIcon(myTheme.RepeatIcon)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AuxControls) SetShuffle(isShuffle bool) {
|
|
||||||
if isShuffle == a.shuffle.Highlighted {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
a.shuffle.Highlighted = isShuffle
|
|
||||||
a.shuffle.Refresh()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AuxControls) DisableCastButton() {
|
func (a *AuxControls) DisableCastButton() {
|
||||||
a.cast.Disable()
|
a.cast.Disable()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ type IconButtonSize int
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
IconButtonSizeNormal IconButtonSize = iota
|
IconButtonSizeNormal IconButtonSize = iota
|
||||||
|
IconButtonSizeSlightlyBigger
|
||||||
IconButtonSizeBigger
|
IconButtonSizeBigger
|
||||||
IconButtonSizeSmaller
|
IconButtonSizeSmaller
|
||||||
|
IconButtonSizeSmallest
|
||||||
)
|
)
|
||||||
|
|
||||||
type IconButton struct {
|
type IconButton struct {
|
||||||
@@ -141,8 +143,12 @@ func (i *IconButton) iconSize() fyne.Size {
|
|||||||
switch i.IconSize {
|
switch i.IconSize {
|
||||||
case IconButtonSizeBigger:
|
case IconButtonSizeBigger:
|
||||||
return fyne.NewSquareSize(theme.IconInlineSize() * 2)
|
return fyne.NewSquareSize(theme.IconInlineSize() * 2)
|
||||||
|
case IconButtonSizeSlightlyBigger:
|
||||||
|
return fyne.NewSquareSize(theme.IconInlineSize() * 1.37)
|
||||||
case IconButtonSizeSmaller:
|
case IconButtonSizeSmaller:
|
||||||
return fyne.NewSquareSize(theme.IconInlineSize())
|
return fyne.NewSquareSize(theme.IconInlineSize())
|
||||||
|
case IconButtonSizeSmallest:
|
||||||
|
return fyne.NewSquareSize(theme.IconInlineSize() * 0.9)
|
||||||
default:
|
default:
|
||||||
return fyne.NewSquareSize(theme.IconInlineSize() * 1.3333)
|
return fyne.NewSquareSize(theme.IconInlineSize() * 1.3333)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package widgets
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/dweymouth/supersonic/backend"
|
"github.com/dweymouth/supersonic/backend"
|
||||||
|
myTheme "github.com/dweymouth/supersonic/ui/theme"
|
||||||
"github.com/dweymouth/supersonic/ui/util"
|
"github.com/dweymouth/supersonic/ui/util"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
@@ -84,13 +85,17 @@ type PlayerControls struct {
|
|||||||
|
|
||||||
UseWaveformSeekbar bool
|
UseWaveformSeekbar bool
|
||||||
|
|
||||||
|
OnChangeShuffle func(shuffle bool)
|
||||||
|
|
||||||
slider *TrackPosSlider
|
slider *TrackPosSlider
|
||||||
waveform *WaveformSeekbar
|
waveform *WaveformSeekbar
|
||||||
curTimeLabel *labelMinSize
|
curTimeLabel *labelMinSize
|
||||||
totalTimeLabel *labelMinSize
|
totalTimeLabel *labelMinSize
|
||||||
|
shuffle *IconButton
|
||||||
prev *IconButton
|
prev *IconButton
|
||||||
playpause *IconButton
|
playpause *IconButton
|
||||||
next *IconButton
|
next *IconButton
|
||||||
|
loop *IconButton
|
||||||
container *fyne.Container
|
container *fyne.Container
|
||||||
|
|
||||||
totalTime float64
|
totalTime float64
|
||||||
@@ -114,7 +119,7 @@ func NewLabelMinSize(text string, minWidth float32) *labelMinSize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewPlayerControls sets up the seek bar, and transport buttons.
|
// NewPlayerControls sets up the seek bar, and transport buttons.
|
||||||
func NewPlayerControls(useWaveformSeekbar bool) *PlayerControls {
|
func NewPlayerControls(useWaveformSeekbar bool, initialLoopMode backend.LoopMode, initialShuffle bool) *PlayerControls {
|
||||||
pc := &PlayerControls{UseWaveformSeekbar: useWaveformSeekbar}
|
pc := &PlayerControls{UseWaveformSeekbar: useWaveformSeekbar}
|
||||||
pc.ExtendBaseWidget(pc)
|
pc.ExtendBaseWidget(pc)
|
||||||
|
|
||||||
@@ -139,15 +144,34 @@ func NewPlayerControls(useWaveformSeekbar bool) *PlayerControls {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pc.shuffle = NewIconButton(myTheme.ShuffleIcon, nil)
|
||||||
|
pc.shuffle.IconSize = IconButtonSizeSmallest
|
||||||
|
pc.shuffle.SetToolTip(lang.L("Shuffle"))
|
||||||
|
pc.shuffle.OnTapped = func() {
|
||||||
|
pc.SetShuffle(!pc.shuffle.Highlighted)
|
||||||
|
if pc.OnChangeShuffle != nil {
|
||||||
|
pc.OnChangeShuffle(pc.shuffle.Highlighted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pc.loop = NewIconButton(myTheme.RepeatIcon, nil)
|
||||||
|
pc.loop.IconSize = IconButtonSizeSmallest
|
||||||
|
pc.loop.SetToolTip(lang.L("Repeat"))
|
||||||
|
|
||||||
|
pc.shuffle.Highlighted = initialShuffle
|
||||||
|
pc.SetLoopMode(initialLoopMode)
|
||||||
|
|
||||||
pc.prev = NewIconButton(theme.MediaSkipPreviousIcon(), func() {})
|
pc.prev = NewIconButton(theme.MediaSkipPreviousIcon(), func() {})
|
||||||
pc.prev.SetToolTip(lang.L("Previous"))
|
pc.prev.SetToolTip(lang.L("Previous"))
|
||||||
|
pc.prev.IconSize = IconButtonSizeSlightlyBigger
|
||||||
pc.next = NewIconButton(theme.MediaSkipNextIcon(), func() {})
|
pc.next = NewIconButton(theme.MediaSkipNextIcon(), func() {})
|
||||||
pc.next.SetToolTip(lang.L("Next"))
|
pc.next.SetToolTip(lang.L("Next"))
|
||||||
|
pc.next.IconSize = IconButtonSizeSlightlyBigger
|
||||||
pc.playpause = NewIconButton(theme.MediaPlayIcon(), func() {})
|
pc.playpause = NewIconButton(theme.MediaPlayIcon(), func() {})
|
||||||
pc.playpause.SetToolTip(lang.L("Play"))
|
pc.playpause.SetToolTip(lang.L("Play"))
|
||||||
pc.playpause.IconSize = IconButtonSizeBigger
|
pc.playpause.IconSize = IconButtonSizeBigger
|
||||||
|
|
||||||
buttons := container.NewHBox(layout.NewSpacer(), pc.prev, pc.playpause, pc.next, layout.NewSpacer())
|
buttons := container.NewHBox(layout.NewSpacer(), pc.shuffle, util.NewHSpace(2), pc.prev, pc.playpause, pc.next, util.NewHSpace(2), pc.loop, layout.NewSpacer())
|
||||||
|
|
||||||
seekCtrl := container.NewStack(
|
seekCtrl := container.NewStack(
|
||||||
pc.slider,
|
pc.slider,
|
||||||
@@ -174,6 +198,32 @@ func (pc *PlayerControls) OnSeekPrevious(f func()) {
|
|||||||
pc.prev.OnTapped = f
|
pc.prev.OnTapped = f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pc *PlayerControls) OnChangeLoopMode(f func()) {
|
||||||
|
pc.loop.OnTapped = f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc *PlayerControls) SetLoopMode(mode backend.LoopMode) {
|
||||||
|
switch mode {
|
||||||
|
case backend.LoopAll:
|
||||||
|
pc.loop.Highlighted = true
|
||||||
|
pc.loop.SetIcon(myTheme.RepeatIcon)
|
||||||
|
case backend.LoopOne:
|
||||||
|
pc.loop.Highlighted = true
|
||||||
|
pc.loop.SetIcon(myTheme.RepeatOneIcon)
|
||||||
|
case backend.LoopNone:
|
||||||
|
pc.loop.Highlighted = false
|
||||||
|
pc.loop.SetIcon(myTheme.RepeatIcon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pc *PlayerControls) SetShuffle(isShuffle bool) {
|
||||||
|
if isShuffle == pc.shuffle.Highlighted {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pc.shuffle.Highlighted = isShuffle
|
||||||
|
pc.shuffle.Refresh()
|
||||||
|
}
|
||||||
|
|
||||||
func (pc *PlayerControls) OnSeekNext(f func()) {
|
func (pc *PlayerControls) OnSeekNext(f func()) {
|
||||||
pc.next.OnTapped = f
|
pc.next.OnTapped = f
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user