fcaster/app/view.go
MassiveBox 4cf9a2a58f
All checks were successful
Build on Push and create Release on Tag / build (push) Successful in 15m49s
Improve casting resume behavior
2025-06-07 22:43:22 +02:00

160 lines
4.4 KiB
Go

package main
import (
"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
type CastingScreenElements struct {
TimestampLabel, ProgressBar, DurationLabel fyne.CanvasObject
}
type View struct {
Controller *Controller
Window fyne.Window
CastingScreenElements CastingScreenElements
ReconnectingDialog *dialog.CustomDialog
}
func (v *View) PopupError(err error) {
fyne.Do(func() {
dialog.ShowError(err, v.Window)
})
}
func (v *View) PopupReconnecting(show bool) {
if v.ReconnectingDialog != nil && !show {
fyne.DoAndWait(func() {
v.ReconnectingDialog.Dismiss()
v.ReconnectingDialog = nil
})
return
}
if v.ReconnectingDialog == nil && show {
v.ReconnectingDialog = dialog.NewCustomWithoutButtons("Reconnecting to stream...", widget.NewProgressBarInfinite(), v.Window)
fyne.Do(func() {
v.ReconnectingDialog.Show()
})
}
}
func NewView(controller *Controller) *View {
v := &View{
Controller: controller,
Window: app.New().NewWindow("FCaster"),
}
v.Window.Resize(fyne.NewSize(300, 300))
return v
}
func (v *View) Start() {
v.Window.ShowAndRun()
}
func (v *View) LoadingScreen() {
loading := container.NewVBox(
widget.NewLabel("Discovering devices..."),
widget.NewProgressBarInfinite(),
widget.NewButton("Refresh", func() { v.Controller.DiscoverDevices() }),
)
fyne.Do(func() {
v.Window.SetContent(loading)
})
}
func (v *View) MainScreen(deviceNames []string) {
selector := widget.NewSelect(deviceNames, nil)
reloadBtn := widget.NewButtonWithIcon("", theme.ViewRefreshIcon(), func() {
v.Controller.DiscoverDevices()
})
entry := widget.NewEntry()
fyne.Do(func() {
v.Window.SetContent(container.NewVBox(
widget.NewLabel("Select the device:"),
container.NewBorder(
nil, nil, nil,
container.NewVBox(reloadBtn),
selector,
),
widget.NewLabel("Enter the media URL:"),
entry,
widget.NewButton("Start Casting", func() {
v.Controller.StartCasting(selector.Selected, entry.Text)
}),
widget.NewButton("Join Existing Casting", func() {
v.Controller.StartCasting(selector.Selected, "")
}),
))
})
}
func (v *View) CastingScreen(deviceName string) {
v.CastingScreenElements = CastingScreenElements{
TimestampLabel: widget.NewLabel("00:00"),
DurationLabel: widget.NewLabel("00:00"),
ProgressBar: widget.NewSlider(0, 100),
}
el := v.CastingScreenElements
el.ProgressBar.(*widget.Slider).OnChanged = func(f float64) {
fyne.Do(func() {
el.TimestampLabel.(*widget.Label).SetText(formatTime(int(f)))
})
}
el.ProgressBar.(*widget.Slider).OnChangeEnded = func(f float64) {
v.Controller.PlayerAction(ArgsActionSeek, float32(f))
}
v.Window.SetContent(container.NewVBox(
widget.NewLabel(deviceName),
container.NewBorder(
nil, nil, el.TimestampLabel, el.DurationLabel,
el.ProgressBar,
),
widget.NewButton("Exit", func() { v.Controller.ExitCasting() }),
container.NewHBox(
widget.NewButtonWithIcon("", theme.MediaPlayIcon(), func() { v.Controller.PlayerAction(ActionPlay) }),
widget.NewButtonWithIcon("", theme.MediaPauseIcon(), func() { v.Controller.PlayerAction(ActionPause) }),
widget.NewButtonWithIcon("", theme.MediaFastRewindIcon(), func() { v.Controller.PlayerAction(ActionSkipBack) }),
widget.NewButtonWithIcon("", theme.MediaFastForwardIcon(), func() { v.Controller.PlayerAction(ActionSkipForward) }),
widget.NewButtonWithIcon("", theme.MediaStopIcon(), func() { v.Controller.PlayerAction(ActionStop) }),
widget.NewButtonWithIcon("", theme.VolumeDownIcon(), func() { v.Controller.PlayerAction(ActionVolumeDown) }),
widget.NewButtonWithIcon("", theme.VolumeUpIcon(), func() { v.Controller.PlayerAction(ActionVolumeUp) }),
),
))
}
func (v *View) UpdateCastingScreen(action int, args ...float32) {
fyne.Do(func() {
switch action {
case RespPlaybackUpdate:
v.CastingScreenElements.ProgressBar.(*widget.Slider).Max = float64(args[0])
v.CastingScreenElements.ProgressBar.(*widget.Slider).SetValue(float64(args[1]))
// timestamp label is updated from ProgressBar OnChange listener
v.CastingScreenElements.DurationLabel.(*widget.Label).SetText(formatTime(int(args[0])))
return
}
})
}
func formatTime(seconds int) string {
hours := seconds / 3600
minutes := (seconds % 3600) / 60
sec := seconds % 60
if hours == 0 {
return fmt.Sprintf("%02d:%02d", minutes, sec)
}
return fmt.Sprintf("%02d:%02d:%02d", hours, minutes, sec)
}