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
260 lines
7.5 KiB
Go
260 lines
7.5 KiB
Go
package binding
|
|
|
|
import (
|
|
"sync/atomic"
|
|
|
|
"fyne.io/fyne/v2"
|
|
)
|
|
|
|
// Work around Go not supporting generic methods on non-generic types:
|
|
type preferenceLookupSetter[T any] func(fyne.Preferences) (func(string) T, func(string, T))
|
|
|
|
const keyTypeMismatchError = "A previous preference binding exists with different type for key: "
|
|
|
|
// BindPreferenceBool returns a bindable bool value that is managed by the application preferences.
|
|
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
|
|
//
|
|
// Since: 2.0
|
|
func BindPreferenceBool(key string, p fyne.Preferences) Bool {
|
|
return bindPreferenceItem(key, p,
|
|
func(p fyne.Preferences) (func(string) bool, func(string, bool)) {
|
|
return p.Bool, p.SetBool
|
|
})
|
|
}
|
|
|
|
// BindPreferenceBoolList returns a bound list of bool values that is managed by the application preferences.
|
|
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
|
|
//
|
|
// Since: 2.6
|
|
func BindPreferenceBoolList(key string, p fyne.Preferences) BoolList {
|
|
return bindPreferenceListComparable(key, p,
|
|
func(p fyne.Preferences) (func(string) []bool, func(string, []bool)) {
|
|
return p.BoolList, p.SetBoolList
|
|
},
|
|
)
|
|
}
|
|
|
|
// BindPreferenceFloat returns a bindable float64 value that is managed by the application preferences.
|
|
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
|
|
//
|
|
// Since: 2.0
|
|
func BindPreferenceFloat(key string, p fyne.Preferences) Float {
|
|
return bindPreferenceItem(key, p,
|
|
func(p fyne.Preferences) (func(string) float64, func(string, float64)) {
|
|
return p.Float, p.SetFloat
|
|
})
|
|
}
|
|
|
|
// BindPreferenceFloatList returns a bound list of float64 values that is managed by the application preferences.
|
|
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
|
|
//
|
|
// Since: 2.6
|
|
func BindPreferenceFloatList(key string, p fyne.Preferences) FloatList {
|
|
return bindPreferenceListComparable(key, p,
|
|
func(p fyne.Preferences) (func(string) []float64, func(string, []float64)) {
|
|
return p.FloatList, p.SetFloatList
|
|
},
|
|
)
|
|
}
|
|
|
|
// BindPreferenceInt returns a bindable int value that is managed by the application preferences.
|
|
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
|
|
//
|
|
// Since: 2.0
|
|
func BindPreferenceInt(key string, p fyne.Preferences) Int {
|
|
return bindPreferenceItem(key, p,
|
|
func(p fyne.Preferences) (func(string) int, func(string, int)) {
|
|
return p.Int, p.SetInt
|
|
})
|
|
}
|
|
|
|
// BindPreferenceIntList returns a bound list of int values that is managed by the application preferences.
|
|
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
|
|
//
|
|
// Since: 2.6
|
|
func BindPreferenceIntList(key string, p fyne.Preferences) IntList {
|
|
return bindPreferenceListComparable(key, p,
|
|
func(p fyne.Preferences) (func(string) []int, func(string, []int)) {
|
|
return p.IntList, p.SetIntList
|
|
},
|
|
)
|
|
}
|
|
|
|
// BindPreferenceString returns a bindable string value that is managed by the application preferences.
|
|
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
|
|
//
|
|
// Since: 2.0
|
|
func BindPreferenceString(key string, p fyne.Preferences) String {
|
|
return bindPreferenceItem(key, p,
|
|
func(p fyne.Preferences) (func(string) string, func(string, string)) {
|
|
return p.String, p.SetString
|
|
})
|
|
}
|
|
|
|
// BindPreferenceStringList returns a bound list of string values that is managed by the application preferences.
|
|
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
|
|
//
|
|
// Since: 2.6
|
|
func BindPreferenceStringList(key string, p fyne.Preferences) StringList {
|
|
return bindPreferenceListComparable(key, p,
|
|
func(p fyne.Preferences) (func(string) []string, func(string, []string)) {
|
|
return p.StringList, p.SetStringList
|
|
},
|
|
)
|
|
}
|
|
|
|
func bindPreferenceItem[T bool | float64 | int | string](key string, p fyne.Preferences, setLookup preferenceLookupSetter[T]) Item[T] {
|
|
if found, ok := lookupExistingBinding[T](key, p); ok {
|
|
return found
|
|
}
|
|
|
|
listen := &prefBoundBase[T]{key: key, setLookup: setLookup}
|
|
listen.replaceProvider(p)
|
|
binds := prefBinds.ensurePreferencesAttached(p)
|
|
binds.Store(key, listen)
|
|
return listen
|
|
}
|
|
|
|
func lookupExistingBinding[T any](key string, p fyne.Preferences) (Item[T], bool) {
|
|
binds := prefBinds.getBindings(p)
|
|
if binds == nil {
|
|
return nil, false
|
|
}
|
|
|
|
if listen, ok := binds.Load(key); listen != nil && ok {
|
|
if l, ok := listen.(Item[T]); ok {
|
|
return l, ok
|
|
}
|
|
fyne.LogError(keyTypeMismatchError+key, nil)
|
|
}
|
|
|
|
return nil, false
|
|
}
|
|
|
|
func lookupExistingListBinding[T bool | float64 | int | string](key string, p fyne.Preferences) (*prefBoundList[T], bool) {
|
|
binds := prefBinds.getBindings(p)
|
|
if binds == nil {
|
|
return nil, false
|
|
}
|
|
|
|
if listen, ok := binds.Load(key); listen != nil && ok {
|
|
if l, ok := listen.(*prefBoundList[T]); ok {
|
|
return l, ok
|
|
}
|
|
fyne.LogError(keyTypeMismatchError+key, nil)
|
|
}
|
|
|
|
return nil, false
|
|
}
|
|
|
|
type prefBoundBase[T bool | float64 | int | string] struct {
|
|
base
|
|
key string
|
|
|
|
get func(string) T
|
|
set func(string, T)
|
|
setLookup preferenceLookupSetter[T]
|
|
cache atomic.Pointer[T]
|
|
}
|
|
|
|
func (b *prefBoundBase[T]) Get() (T, error) {
|
|
cache := b.get(b.key)
|
|
b.cache.Store(&cache)
|
|
return cache, nil
|
|
}
|
|
|
|
func (b *prefBoundBase[T]) Set(v T) error {
|
|
b.set(b.key, v)
|
|
|
|
b.lock.RLock()
|
|
defer b.lock.RUnlock()
|
|
b.trigger()
|
|
return nil
|
|
}
|
|
|
|
func (b *prefBoundBase[T]) checkForChange() {
|
|
val := b.cache.Load()
|
|
if val != nil && b.get(b.key) == *val {
|
|
return
|
|
}
|
|
b.trigger()
|
|
}
|
|
|
|
func (b *prefBoundBase[T]) replaceProvider(p fyne.Preferences) {
|
|
b.get, b.set = b.setLookup(p)
|
|
}
|
|
|
|
type prefBoundList[T bool | float64 | int | string] struct {
|
|
boundList[T]
|
|
key string
|
|
|
|
get func(string) []T
|
|
set func(string, []T)
|
|
setLookup preferenceLookupSetter[[]T]
|
|
}
|
|
|
|
func (b *prefBoundList[T]) checkForChange() {
|
|
val := *b.val
|
|
updated := b.get(b.key)
|
|
if val == nil || len(updated) != len(val) {
|
|
b.Set(updated)
|
|
return
|
|
}
|
|
|
|
// incoming changes to a preference list are not at the child level
|
|
for i, v := range val {
|
|
if i >= len(updated) {
|
|
break
|
|
}
|
|
|
|
if !b.comparator(v, updated[i]) {
|
|
_ = b.items[i].(Item[T]).Set(updated[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func (b *prefBoundList[T]) replaceProvider(p fyne.Preferences) {
|
|
b.get, b.set = b.setLookup(p)
|
|
}
|
|
|
|
type internalPrefs = interface{ WriteValues(func(map[string]any)) }
|
|
|
|
func bindPreferenceListComparable[T bool | float64 | int | string](key string, p fyne.Preferences,
|
|
setLookup preferenceLookupSetter[[]T],
|
|
) *prefBoundList[T] {
|
|
if found, ok := lookupExistingListBinding[T](key, p); ok {
|
|
return found
|
|
}
|
|
|
|
listen := &prefBoundList[T]{key: key, setLookup: setLookup}
|
|
listen.replaceProvider(p)
|
|
|
|
items := listen.get(listen.key)
|
|
listen.boundList = *bindList(nil, func(t1, t2 T) bool { return t1 == t2 })
|
|
|
|
listen.boundList.AddListener(NewDataListener(func() {
|
|
cached := *listen.val
|
|
replaced := listen.get(listen.key)
|
|
if len(cached) == len(replaced) {
|
|
return
|
|
}
|
|
|
|
listen.set(listen.key, *listen.val)
|
|
listen.trigger()
|
|
}))
|
|
|
|
listen.boundList.parentListener = func(index int) {
|
|
listen.set(listen.key, *listen.val)
|
|
|
|
// the child changes are not seen on the write end so force it
|
|
if prefs, ok := p.(internalPrefs); ok {
|
|
prefs.WriteValues(func(map[string]any) {})
|
|
}
|
|
}
|
|
listen.boundList.Set(items)
|
|
|
|
binds := prefBinds.ensurePreferencesAttached(p)
|
|
binds.Store(key, listen)
|
|
return listen
|
|
}
|