diff --git a/main.go b/main.go index d0e556b..2d4886b 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,7 @@ import ( "supersonic/backend" "supersonic/player" "supersonic/ui" + "supersonic/ui/widgets" "fyne.io/fyne/v2/app" "fyne.io/fyne/v2/container" @@ -50,7 +51,7 @@ func main() { mainWindow := ui.NewMainWindow(myApp, appname, p) if server == nil { - d := ui.NewAddServerDialog("Connect to Server") + d := widgets.NewAddServerForm("Connect to Server") pop := widget.NewModalPopUp(d, mainWindow.Canvas()) d.OnSubmit = func() { pop.Hide() diff --git a/ui/albumgrid.go b/ui/albumgrid.go index 1ff9fd9..5cb3933 100644 --- a/ui/albumgrid.go +++ b/ui/albumgrid.go @@ -3,6 +3,7 @@ package ui import ( "context" "supersonic/backend" + "supersonic/ui/widgets" "fyne.io/fyne/v2" "fyne.io/fyne/v2/widget" @@ -35,7 +36,7 @@ func NewAlbumGrid(iter backend.AlbumIterator, pm *backend.PlaybackManager, im *b }, // create func func() fyne.CanvasObject { - ac := NewAlbumCard() + ac := widgets.NewAlbumCard() ac.OnPlay = func() { pm.PlayAlbum(ac.AlbumID()) } @@ -43,7 +44,7 @@ func NewAlbumGrid(iter backend.AlbumIterator, pm *backend.PlaybackManager, im *b }, // update func func(itemID int, obj fyne.CanvasObject) { - ac := obj.(*AlbumCard) + ac := obj.(*widgets.AlbumCard) ag.doUpdateAlbumCard(itemID, ac) }, ) @@ -54,7 +55,7 @@ func NewAlbumGrid(iter backend.AlbumIterator, pm *backend.PlaybackManager, im *b return ag } -func (ag *AlbumGrid) doUpdateAlbumCard(albumIdx int, ac *AlbumCard) { +func (ag *AlbumGrid) doUpdateAlbumCard(albumIdx int, ac *widgets.AlbumCard) { album := ag.albums[albumIdx] if ac.PrevAlbumID == album.ID { // nothing to do diff --git a/ui/bottompanel.go b/ui/bottompanel.go index ce1ea2e..74d85df 100644 --- a/ui/bottompanel.go +++ b/ui/bottompanel.go @@ -1,9 +1,11 @@ package ui import ( + "fmt" "image" "supersonic/backend" "supersonic/player" + "supersonic/ui/widgets" "fyne.io/fyne/v2" "fyne.io/fyne/v2/container" @@ -57,8 +59,8 @@ type BottomPanel struct { playbackManager *backend.PlaybackManager - nowPlaying *NowPlayingCard - controls *PlayerControls + nowPlaying *widgets.NowPlayingCard + controls *widgets.PlayerControls container *fyne.Container } @@ -67,9 +69,30 @@ var _ fyne.Widget = (*BottomPanel)(nil) func NewBottomPanel(p *player.Player) *BottomPanel { bp := &BottomPanel{} bp.ExtendBaseWidget(bp) + p.OnPaused(func() { + bp.controls.SetPlaying(false) + }) + p.OnPlaying(func() { + bp.controls.SetPlaying(true) + }) + p.OnStopped(func() { + bp.controls.SetPlaying(false) + }) - bp.nowPlaying = NewNowPlayingCard() - bp.controls = NewPlayerControls(p) + bp.nowPlaying = widgets.NewNowPlayingCard() + bp.controls = widgets.NewPlayerControls() + bp.controls.OnPlayPause(func() { + p.PlayPause() + }) + bp.controls.OnSeekNext(func() { + p.SeekNext() + }) + bp.controls.OnSeekPrevious(func() { + p.SeekBackOrPrevious() + }) + bp.controls.OnSeek(func(f float64) { + p.Seek(fmt.Sprintf("%d", int(f*100)), player.SeekAbsolutePercent) + }) bp.container = container.New(newBottomPanelLayout(500, bp.nowPlaying, bp.controls, nil), bp.nowPlaying, bp.controls) return bp @@ -77,9 +100,11 @@ func NewBottomPanel(p *player.Player) *BottomPanel { func (bp *BottomPanel) SetPlaybackManager(pm *backend.PlaybackManager) { bp.playbackManager = pm - bp.controls.SetPlaybackManager(pm) - pm.OnSongChange(func(song *subsonic.Child) { - bp.onSongChange(song) + pm.OnSongChange(bp.onSongChange) + pm.OnPlayTimeUpdate(func(cur, total float64) { + if !pm.IsSeeking() { + bp.controls.UpdatePlayTime(cur, total) + } }) } diff --git a/ui/util.go b/ui/util/util.go similarity index 94% rename from ui/util.go rename to ui/util/util.go index c9adcdc..c7ba22b 100644 --- a/ui/util.go +++ b/ui/util/util.go @@ -1,4 +1,4 @@ -package ui +package util import ( "fmt" diff --git a/ui/addserverdialog.go b/ui/widgets/addserverform.go similarity index 83% rename from ui/addserverdialog.go rename to ui/widgets/addserverform.go index 6b1e741..6c08579 100644 --- a/ui/addserverdialog.go +++ b/ui/widgets/addserverform.go @@ -1,4 +1,4 @@ -package ui +package widgets import ( "fyne.io/fyne/v2" @@ -8,7 +8,7 @@ import ( "fyne.io/fyne/v2/widget" ) -type AddServerDialog struct { +type AddServerForm struct { widget.BaseWidget Nickname string @@ -20,10 +20,10 @@ type AddServerDialog struct { container *fyne.Container } -var _ fyne.Widget = (*AddServerDialog)(nil) +var _ fyne.Widget = (*AddServerForm)(nil) -func NewAddServerDialog(title string) *AddServerDialog { - a := &AddServerDialog{} +func NewAddServerForm(title string) *AddServerForm { + a := &AddServerForm{} a.ExtendBaseWidget(a) titleLabel := widget.NewLabel(title) titleLabel.TextStyle.Bold = true @@ -59,11 +59,11 @@ func NewAddServerDialog(title string) *AddServerDialog { return a } -func (a *AddServerDialog) MinSize() fyne.Size { +func (a *AddServerForm) MinSize() fyne.Size { a.ExtendBaseWidget(a) return fyne.NewSize(300, a.container.MinSize().Height) } -func (a *AddServerDialog) CreateRenderer() fyne.WidgetRenderer { +func (a *AddServerForm) CreateRenderer() fyne.WidgetRenderer { return widget.NewSimpleRenderer(a.container) } diff --git a/ui/albumcard.go b/ui/widgets/albumcard.go similarity index 99% rename from ui/albumcard.go rename to ui/widgets/albumcard.go index 37d7b59..354dce8 100644 --- a/ui/albumcard.go +++ b/ui/widgets/albumcard.go @@ -1,4 +1,4 @@ -package ui +package widgets import ( "context" @@ -11,6 +11,7 @@ import ( "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/driver/desktop" "fyne.io/fyne/v2/widget" + "github.com/dweymouth/go-subsonic" ) diff --git a/ui/nowplayingcard.go b/ui/widgets/nowplayingcard.go similarity index 98% rename from ui/nowplayingcard.go rename to ui/widgets/nowplayingcard.go index 4220939..f87a718 100644 --- a/ui/nowplayingcard.go +++ b/ui/widgets/nowplayingcard.go @@ -1,4 +1,4 @@ -package ui +package widgets import ( "image" diff --git a/ui/playercontrols.go b/ui/widgets/playercontrols.go similarity index 62% rename from ui/playercontrols.go rename to ui/widgets/playercontrols.go index ee20d09..1b43941 100644 --- a/ui/playercontrols.go +++ b/ui/widgets/playercontrols.go @@ -1,9 +1,7 @@ -package ui +package widgets import ( - "fmt" - "supersonic/backend" - "supersonic/player" + "supersonic/ui/util" "fyne.io/fyne/v2" "fyne.io/fyne/v2/container" @@ -66,49 +64,28 @@ type PlayerControls struct { next *widget.Button container *fyne.Container - totalTime float64 - player *player.Player - playbackManager *backend.PlaybackManager + totalTime float64 } var _ fyne.Widget = (*PlayerControls)(nil) -// NewPlayerControls sets up the seek bar, and transport buttons, and returns the encompassing Container. -func NewPlayerControls(p *player.Player) *PlayerControls { - pc := &PlayerControls{player: p} +// NewPlayerControls sets up the seek bar, and transport buttons. +func NewPlayerControls() *PlayerControls { + pc := &PlayerControls{} pc.ExtendBaseWidget(pc) pc.slider = NewTrackPosSlider() pc.curTimeLabel = widget.NewLabel("0:00") pc.totalTimeLabel = widget.NewLabel("0:00") - pc.slider.OnDragEnd = func(f float64) { - p.Seek(fmt.Sprintf("%d", int(f*100)), player.SeekAbsolutePercent) - } pc.slider.OnChanged = func(f float64) { time := f * pc.totalTime - pc.curTimeLabel.SetText(SecondsToTimeString(time)) + pc.curTimeLabel.SetText(util.SecondsToTimeString(time)) } - pc.prev = widget.NewButtonWithIcon("", theme.MediaSkipPreviousIcon(), func() { - p.SeekBackOrPrevious() - }) - pc.next = widget.NewButtonWithIcon("", theme.MediaSkipNextIcon(), func() { - p.SeekNext() - }) - pc.playpause = widget.NewButtonWithIcon("", theme.MediaPlayIcon(), func() { - p.PlayPause() - }) - - p.OnPaused(func() { - pc.playpause.SetIcon(theme.MediaPlayIcon()) - }) - p.OnPlaying(func() { - pc.playpause.SetIcon(theme.MediaPauseIcon()) - }) - p.OnStopped(func() { - pc.playpause.SetIcon(theme.MediaPlayIcon()) - }) + 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) @@ -119,39 +96,54 @@ func NewPlayerControls(p *player.Player) *PlayerControls { return pc } -func (pc *PlayerControls) SetPlaybackManager(pm *backend.PlaybackManager) { - pc.playbackManager = pm - pm.OnPlayTimeUpdate(func(curTime float64, totalTime float64) { - pc.doPlayTimeUpdate(curTime, totalTime) - }) +func (pc *PlayerControls) OnSeek(f func(float64)) { + pc.slider.OnDragEnd = f } -func (pc *PlayerControls) doPlayTimeUpdate(curTime, totalTime float64) { +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 - if !pc.playbackManager.IsSeeking() { - v := 0.0 - if totalTime > 0 { - v = curTime / totalTime - } + v := 0.0 + if totalTime > 0 { + v = curTime / totalTime + } - updated := false - tt := SecondsToTimeString(totalTime) - if tt != pc.totalTimeLabel.Text { - pc.totalTimeLabel.SetText(tt) + 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 !pc.slider.IsDragging() { - ct := 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) - } + if updated { + // Only update slider once a second when time label changes + pc.slider.SetValue(v) } } }