refactor UI for better separation
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
|||||||
"supersonic/backend"
|
"supersonic/backend"
|
||||||
"supersonic/player"
|
"supersonic/player"
|
||||||
"supersonic/ui"
|
"supersonic/ui"
|
||||||
|
"supersonic/ui/widgets"
|
||||||
|
|
||||||
"fyne.io/fyne/v2/app"
|
"fyne.io/fyne/v2/app"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
@@ -50,7 +51,7 @@ func main() {
|
|||||||
mainWindow := ui.NewMainWindow(myApp, appname, p)
|
mainWindow := ui.NewMainWindow(myApp, appname, p)
|
||||||
|
|
||||||
if server == nil {
|
if server == nil {
|
||||||
d := ui.NewAddServerDialog("Connect to Server")
|
d := widgets.NewAddServerForm("Connect to Server")
|
||||||
pop := widget.NewModalPopUp(d, mainWindow.Canvas())
|
pop := widget.NewModalPopUp(d, mainWindow.Canvas())
|
||||||
d.OnSubmit = func() {
|
d.OnSubmit = func() {
|
||||||
pop.Hide()
|
pop.Hide()
|
||||||
|
|||||||
+4
-3
@@ -3,6 +3,7 @@ package ui
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"supersonic/backend"
|
"supersonic/backend"
|
||||||
|
"supersonic/ui/widgets"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
@@ -35,7 +36,7 @@ func NewAlbumGrid(iter backend.AlbumIterator, pm *backend.PlaybackManager, im *b
|
|||||||
},
|
},
|
||||||
// create func
|
// create func
|
||||||
func() fyne.CanvasObject {
|
func() fyne.CanvasObject {
|
||||||
ac := NewAlbumCard()
|
ac := widgets.NewAlbumCard()
|
||||||
ac.OnPlay = func() {
|
ac.OnPlay = func() {
|
||||||
pm.PlayAlbum(ac.AlbumID())
|
pm.PlayAlbum(ac.AlbumID())
|
||||||
}
|
}
|
||||||
@@ -43,7 +44,7 @@ func NewAlbumGrid(iter backend.AlbumIterator, pm *backend.PlaybackManager, im *b
|
|||||||
},
|
},
|
||||||
// update func
|
// update func
|
||||||
func(itemID int, obj fyne.CanvasObject) {
|
func(itemID int, obj fyne.CanvasObject) {
|
||||||
ac := obj.(*AlbumCard)
|
ac := obj.(*widgets.AlbumCard)
|
||||||
ag.doUpdateAlbumCard(itemID, ac)
|
ag.doUpdateAlbumCard(itemID, ac)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -54,7 +55,7 @@ func NewAlbumGrid(iter backend.AlbumIterator, pm *backend.PlaybackManager, im *b
|
|||||||
return ag
|
return ag
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ag *AlbumGrid) doUpdateAlbumCard(albumIdx int, ac *AlbumCard) {
|
func (ag *AlbumGrid) doUpdateAlbumCard(albumIdx int, ac *widgets.AlbumCard) {
|
||||||
album := ag.albums[albumIdx]
|
album := ag.albums[albumIdx]
|
||||||
if ac.PrevAlbumID == album.ID {
|
if ac.PrevAlbumID == album.ID {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
|
|||||||
+32
-7
@@ -1,9 +1,11 @@
|
|||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"supersonic/backend"
|
"supersonic/backend"
|
||||||
"supersonic/player"
|
"supersonic/player"
|
||||||
|
"supersonic/ui/widgets"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
@@ -57,8 +59,8 @@ type BottomPanel struct {
|
|||||||
|
|
||||||
playbackManager *backend.PlaybackManager
|
playbackManager *backend.PlaybackManager
|
||||||
|
|
||||||
nowPlaying *NowPlayingCard
|
nowPlaying *widgets.NowPlayingCard
|
||||||
controls *PlayerControls
|
controls *widgets.PlayerControls
|
||||||
container *fyne.Container
|
container *fyne.Container
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,9 +69,30 @@ var _ fyne.Widget = (*BottomPanel)(nil)
|
|||||||
func NewBottomPanel(p *player.Player) *BottomPanel {
|
func NewBottomPanel(p *player.Player) *BottomPanel {
|
||||||
bp := &BottomPanel{}
|
bp := &BottomPanel{}
|
||||||
bp.ExtendBaseWidget(bp)
|
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.nowPlaying = widgets.NewNowPlayingCard()
|
||||||
bp.controls = NewPlayerControls(p)
|
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)
|
bp.container = container.New(newBottomPanelLayout(500, bp.nowPlaying, bp.controls, nil), bp.nowPlaying, bp.controls)
|
||||||
return bp
|
return bp
|
||||||
@@ -77,9 +100,11 @@ func NewBottomPanel(p *player.Player) *BottomPanel {
|
|||||||
|
|
||||||
func (bp *BottomPanel) SetPlaybackManager(pm *backend.PlaybackManager) {
|
func (bp *BottomPanel) SetPlaybackManager(pm *backend.PlaybackManager) {
|
||||||
bp.playbackManager = pm
|
bp.playbackManager = pm
|
||||||
bp.controls.SetPlaybackManager(pm)
|
pm.OnSongChange(bp.onSongChange)
|
||||||
pm.OnSongChange(func(song *subsonic.Child) {
|
pm.OnPlayTimeUpdate(func(cur, total float64) {
|
||||||
bp.onSongChange(song)
|
if !pm.IsSeeking() {
|
||||||
|
bp.controls.UpdatePlayTime(cur, total)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package ui
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ui
|
package widgets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AddServerDialog struct {
|
type AddServerForm struct {
|
||||||
widget.BaseWidget
|
widget.BaseWidget
|
||||||
|
|
||||||
Nickname string
|
Nickname string
|
||||||
@@ -20,10 +20,10 @@ type AddServerDialog struct {
|
|||||||
container *fyne.Container
|
container *fyne.Container
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ fyne.Widget = (*AddServerDialog)(nil)
|
var _ fyne.Widget = (*AddServerForm)(nil)
|
||||||
|
|
||||||
func NewAddServerDialog(title string) *AddServerDialog {
|
func NewAddServerForm(title string) *AddServerForm {
|
||||||
a := &AddServerDialog{}
|
a := &AddServerForm{}
|
||||||
a.ExtendBaseWidget(a)
|
a.ExtendBaseWidget(a)
|
||||||
titleLabel := widget.NewLabel(title)
|
titleLabel := widget.NewLabel(title)
|
||||||
titleLabel.TextStyle.Bold = true
|
titleLabel.TextStyle.Bold = true
|
||||||
@@ -59,11 +59,11 @@ func NewAddServerDialog(title string) *AddServerDialog {
|
|||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AddServerDialog) MinSize() fyne.Size {
|
func (a *AddServerForm) MinSize() fyne.Size {
|
||||||
a.ExtendBaseWidget(a)
|
a.ExtendBaseWidget(a)
|
||||||
return fyne.NewSize(300, a.container.MinSize().Height)
|
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)
|
return widget.NewSimpleRenderer(a.container)
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ui
|
package widgets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
"fyne.io/fyne/v2/driver/desktop"
|
"fyne.io/fyne/v2/driver/desktop"
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
|
|
||||||
"github.com/dweymouth/go-subsonic"
|
"github.com/dweymouth/go-subsonic"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ui
|
package widgets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"image"
|
"image"
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
package ui
|
package widgets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"supersonic/ui/util"
|
||||||
"supersonic/backend"
|
|
||||||
"supersonic/player"
|
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
@@ -67,48 +65,27 @@ type PlayerControls struct {
|
|||||||
container *fyne.Container
|
container *fyne.Container
|
||||||
|
|
||||||
totalTime float64
|
totalTime float64
|
||||||
player *player.Player
|
|
||||||
playbackManager *backend.PlaybackManager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ fyne.Widget = (*PlayerControls)(nil)
|
var _ fyne.Widget = (*PlayerControls)(nil)
|
||||||
|
|
||||||
// NewPlayerControls sets up the seek bar, and transport buttons, and returns the encompassing Container.
|
// NewPlayerControls sets up the seek bar, and transport buttons.
|
||||||
func NewPlayerControls(p *player.Player) *PlayerControls {
|
func NewPlayerControls() *PlayerControls {
|
||||||
pc := &PlayerControls{player: p}
|
pc := &PlayerControls{}
|
||||||
pc.ExtendBaseWidget(pc)
|
pc.ExtendBaseWidget(pc)
|
||||||
|
|
||||||
pc.slider = NewTrackPosSlider()
|
pc.slider = NewTrackPosSlider()
|
||||||
pc.curTimeLabel = widget.NewLabel("0:00")
|
pc.curTimeLabel = widget.NewLabel("0:00")
|
||||||
pc.totalTimeLabel = 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) {
|
pc.slider.OnChanged = func(f float64) {
|
||||||
time := f * pc.totalTime
|
time := f * pc.totalTime
|
||||||
pc.curTimeLabel.SetText(SecondsToTimeString(time))
|
pc.curTimeLabel.SetText(util.SecondsToTimeString(time))
|
||||||
}
|
}
|
||||||
|
|
||||||
pc.prev = widget.NewButtonWithIcon("", theme.MediaSkipPreviousIcon(), func() {
|
pc.prev = widget.NewButtonWithIcon("", theme.MediaSkipPreviousIcon(), func() {})
|
||||||
p.SeekBackOrPrevious()
|
pc.next = widget.NewButtonWithIcon("", theme.MediaSkipNextIcon(), func() {})
|
||||||
})
|
pc.playpause = widget.NewButtonWithIcon("", theme.MediaPlayIcon(), func() {})
|
||||||
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())
|
|
||||||
})
|
|
||||||
|
|
||||||
buttons := container.NewHBox(pc.prev, pc.playpause, pc.next)
|
buttons := container.NewHBox(pc.prev, pc.playpause, pc.next)
|
||||||
b := container.New(layout.NewCenterLayout(), buttons)
|
b := container.New(layout.NewCenterLayout(), buttons)
|
||||||
@@ -119,31 +96,47 @@ func NewPlayerControls(p *player.Player) *PlayerControls {
|
|||||||
return pc
|
return pc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pc *PlayerControls) SetPlaybackManager(pm *backend.PlaybackManager) {
|
func (pc *PlayerControls) OnSeek(f func(float64)) {
|
||||||
pc.playbackManager = pm
|
pc.slider.OnDragEnd = f
|
||||||
pm.OnPlayTimeUpdate(func(curTime float64, totalTime float64) {
|
|
||||||
pc.doPlayTimeUpdate(curTime, totalTime)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// TODO: there is a bug with very long tracks (~20min) where the
|
||||||
// curtime label will bounce back and forth +- 1sec (rounding issue?)
|
// curtime label will bounce back and forth +- 1sec (rounding issue?)
|
||||||
pc.totalTime = totalTime
|
pc.totalTime = totalTime
|
||||||
if !pc.playbackManager.IsSeeking() {
|
|
||||||
v := 0.0
|
v := 0.0
|
||||||
if totalTime > 0 {
|
if totalTime > 0 {
|
||||||
v = curTime / totalTime
|
v = curTime / totalTime
|
||||||
}
|
}
|
||||||
|
|
||||||
updated := false
|
updated := false
|
||||||
tt := SecondsToTimeString(totalTime)
|
tt := util.SecondsToTimeString(totalTime)
|
||||||
if tt != pc.totalTimeLabel.Text {
|
if tt != pc.totalTimeLabel.Text {
|
||||||
pc.totalTimeLabel.SetText(tt)
|
pc.totalTimeLabel.SetText(tt)
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
if !pc.slider.IsDragging() {
|
if !pc.slider.IsDragging() {
|
||||||
ct := SecondsToTimeString(curTime)
|
ct := util.SecondsToTimeString(curTime)
|
||||||
if ct != pc.curTimeLabel.Text {
|
if ct != pc.curTimeLabel.Text {
|
||||||
pc.curTimeLabel.SetText(ct)
|
pc.curTimeLabel.SetText(ct)
|
||||||
updated = true
|
updated = true
|
||||||
@@ -154,7 +147,6 @@ func (pc *PlayerControls) doPlayTimeUpdate(curTime, totalTime float64) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PlayerControls) CreateRenderer() fyne.WidgetRenderer {
|
func (p *PlayerControls) CreateRenderer() fyne.WidgetRenderer {
|
||||||
return widget.NewSimpleRenderer(p.container)
|
return widget.NewSimpleRenderer(p.container)
|
||||||
Reference in New Issue
Block a user