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 } func (v *View) PopupError(err error) { dialog.ShowError(err, v.Window) } 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) }