VideoTools/vendor/fyne.io/fyne/v2/widget/activity.go
Stu Leak 68df790d27 Fix player frame generation and video playback
Major improvements to UnifiedPlayer:

1. GetFrameImage() now works when paused for responsive UI updates
2. Play() method properly starts FFmpeg process
3. Frame display loop runs continuously for smooth video display
4. Disabled audio temporarily to fix video playback fundamentals
5. Simplified FFmpeg command to focus on video stream only

Player now:
- Generates video frames correctly
- Shows video when paused
- Has responsive progress tracking
- Starts playback properly

Next steps: Re-enable audio playback once video is stable
2026-01-07 22:20:00 -05:00

178 lines
3.5 KiB
Go

package widget
import (
"image/color"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/theme"
)
var _ fyne.Widget = (*Activity)(nil)
// Activity is used to indicate that something is happening that should be waited for,
// or is in the background (depending on usage).
//
// Since: 2.5
type Activity struct {
BaseWidget
started bool
}
// NewActivity returns a widget for indicating activity
//
// Since: 2.5
func NewActivity() *Activity {
a := &Activity{}
a.ExtendBaseWidget(a)
return a
}
func (a *Activity) MinSize() fyne.Size {
a.ExtendBaseWidget(a)
return a.BaseWidget.MinSize()
}
// Start the activity indicator animation
func (a *Activity) Start() {
if a.started {
return // already started
}
a.started = true
a.Refresh()
}
// Stop the activity indicator animation
func (a *Activity) Stop() {
if !a.started {
return // already stopped
}
a.started = false
a.Refresh()
}
func (a *Activity) CreateRenderer() fyne.WidgetRenderer {
dots := make([]fyne.CanvasObject, 3)
v := fyne.CurrentApp().Settings().ThemeVariant()
for i := range dots {
dots[i] = canvas.NewCircle(a.Theme().Color(theme.ColorNameForeground, v))
}
r := &activityRenderer{dots: dots, parent: a}
r.anim = &fyne.Animation{
Duration: time.Second * 2,
RepeatCount: fyne.AnimationRepeatForever,
Tick: r.animate,
}
r.updateColor()
if a.started {
r.start()
}
return r
}
var _ fyne.WidgetRenderer = (*activityRenderer)(nil)
type activityRenderer struct {
anim *fyne.Animation
dots []fyne.CanvasObject
parent *Activity
bound fyne.Size
maxCol color.NRGBA
maxRad float32
wasStarted bool
}
func (a *activityRenderer) Destroy() {
a.parent.started = false
a.stop()
}
func (a *activityRenderer) Layout(size fyne.Size) {
a.maxRad = fyne.Min(size.Width, size.Height) / 2
a.bound = size
}
func (a *activityRenderer) MinSize() fyne.Size {
return fyne.NewSquareSize(a.parent.Theme().Size(theme.SizeNameInlineIcon))
}
func (a *activityRenderer) Objects() []fyne.CanvasObject {
return a.dots
}
func (a *activityRenderer) Refresh() {
if a.parent.started {
if !a.wasStarted {
a.start()
}
} else if a.wasStarted {
a.stop()
}
a.updateColor()
}
func (a *activityRenderer) animate(done float32) {
off := done * 2
if off > 1 {
off = 2 - off
}
off1 := (done + 0.25) * 2
if done >= 0.75 {
off1 = (done - 0.75) * 2
}
if off1 > 1 {
off1 = 2 - off1
}
off2 := (done + 0.75) * 2
if done >= 0.25 {
off2 = (done - 0.25) * 2
}
if off2 > 1 {
off2 = 2 - off2
}
a.scaleDot(a.dots[0].(*canvas.Circle), off)
a.scaleDot(a.dots[1].(*canvas.Circle), off1)
a.scaleDot(a.dots[2].(*canvas.Circle), off2)
}
func (a *activityRenderer) scaleDot(dot *canvas.Circle, off float32) {
rad := a.maxRad - a.maxRad*off/1.2
mid := fyne.NewPos(a.bound.Width/2, a.bound.Height/2)
dot.Move(mid.Subtract(fyne.NewSquareOffsetPos(rad)))
dot.Resize(fyne.NewSquareSize(rad * 2))
alpha := uint8(0 + int(float32(a.maxCol.A)*off))
dot.FillColor = color.NRGBA{R: a.maxCol.R, G: a.maxCol.G, B: a.maxCol.B, A: alpha}
dot.Refresh()
}
func (a *activityRenderer) start() {
a.wasStarted = true
a.anim.Start()
}
func (a *activityRenderer) stop() {
a.wasStarted = false
a.anim.Stop()
}
func (a *activityRenderer) updateColor() {
v := fyne.CurrentApp().Settings().ThemeVariant()
rr, gg, bb, aa := a.parent.Theme().Color(theme.ColorNameForeground, v).RGBA()
a.maxCol = color.NRGBA{R: uint8(rr >> 8), G: uint8(gg >> 8), B: uint8(bb >> 8), A: uint8(aa >> 8)}
}