VideoTools/vendor/fyne.io/fyne/v2/data/binding/maps.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

412 lines
7.8 KiB
Go

package binding
import (
"errors"
"reflect"
"fyne.io/fyne/v2"
)
// DataMap is the base interface for all bindable data maps.
//
// Since: 2.0
type DataMap interface {
DataItem
GetItem(string) (DataItem, error)
Keys() []string
}
// ExternalUntypedMap is a map data binding with all values untyped (any), connected to an external data source.
//
// Since: 2.0
type ExternalUntypedMap interface {
UntypedMap
Reload() error
}
// UntypedMap is a map data binding with all values Untyped (any).
//
// Since: 2.0
type UntypedMap interface {
DataMap
Delete(string)
Get() (map[string]any, error)
GetValue(string) (any, error)
Set(map[string]any) error
SetValue(string, any) error
}
// NewUntypedMap creates a new, empty map binding of string to any.
//
// Since: 2.0
func NewUntypedMap() UntypedMap {
return &mapBase{items: make(map[string]reflectUntyped), val: &map[string]any{}}
}
// BindUntypedMap creates a new map binding of string to any based on the data passed.
// If your code changes the content of the map this refers to you should call Reload() to inform the bindings.
//
// Since: 2.0
func BindUntypedMap(d *map[string]any) ExternalUntypedMap {
if d == nil {
return NewUntypedMap().(ExternalUntypedMap)
}
m := &mapBase{items: make(map[string]reflectUntyped), val: d, updateExternal: true}
for k := range *d {
m.setItem(k, bindUntypedMapValue(d, k, m.updateExternal))
}
return m
}
// Struct is the base interface for a bound struct type.
//
// Since: 2.0
type Struct interface {
DataMap
GetValue(string) (any, error)
SetValue(string, any) error
Reload() error
}
// BindStruct creates a new map binding of string to any using the struct passed as data.
// The key for each item is a string representation of each exported field with the value set as an any.
// Only exported fields are included.
//
// Since: 2.0
func BindStruct(i any) Struct {
if i == nil {
return NewUntypedMap().(Struct)
}
t := reflect.TypeOf(i)
if t.Kind() != reflect.Ptr ||
(reflect.TypeOf(reflect.ValueOf(i).Elem()).Kind() != reflect.Struct) {
fyne.LogError("Invalid type passed to BindStruct, must be pointer to struct", nil)
return NewUntypedMap().(Struct)
}
s := &boundStruct{orig: i}
s.items = make(map[string]reflectUntyped)
s.val = &map[string]any{}
s.updateExternal = true
v := reflect.ValueOf(i).Elem()
t = v.Type()
for j := 0; j < v.NumField(); j++ {
f := v.Field(j)
if !f.CanSet() {
continue
}
key := t.Field(j).Name
s.items[key] = bindReflect(f)
(*s.val)[key] = f.Interface()
}
return s
}
type reflectUntyped interface {
DataItem
get() (any, error)
set(any) error
}
type mapBase struct {
base
updateExternal bool
items map[string]reflectUntyped
val *map[string]any
}
func (b *mapBase) GetItem(key string) (DataItem, error) {
b.lock.RLock()
defer b.lock.RUnlock()
if v, ok := b.items[key]; ok {
return v, nil
}
return nil, errKeyNotFound
}
func (b *mapBase) Keys() []string {
b.lock.Lock()
defer b.lock.Unlock()
ret := make([]string, len(b.items))
i := 0
for k := range b.items {
ret[i] = k
i++
}
return ret
}
func (b *mapBase) Delete(key string) {
b.lock.Lock()
defer b.lock.Unlock()
delete(b.items, key)
b.trigger()
}
func (b *mapBase) Get() (map[string]any, error) {
b.lock.RLock()
defer b.lock.RUnlock()
if b.val == nil {
return map[string]any{}, nil
}
return *b.val, nil
}
func (b *mapBase) GetValue(key string) (any, error) {
b.lock.RLock()
defer b.lock.RUnlock()
if i, ok := b.items[key]; ok {
return i.get()
}
return nil, errKeyNotFound
}
func (b *mapBase) Reload() error {
b.lock.Lock()
defer b.lock.Unlock()
return b.doReload()
}
func (b *mapBase) Set(v map[string]any) error {
b.lock.Lock()
defer b.lock.Unlock()
if b.val == nil { // was not initialized with a blank value, recover
b.val = &v
b.trigger()
return nil
}
*b.val = v
return b.doReload()
}
func (b *mapBase) SetValue(key string, d any) error {
b.lock.Lock()
defer b.lock.Unlock()
if i, ok := b.items[key]; ok {
return i.set(d)
}
(*b.val)[key] = d
item := bindUntypedMapValue(b.val, key, b.updateExternal)
b.setItem(key, item)
return nil
}
func (b *mapBase) doReload() (retErr error) {
changed := false
// add new
for key := range *b.val {
_, found := b.items[key]
if !found {
b.setItem(key, bindUntypedMapValue(b.val, key, b.updateExternal))
changed = true
}
}
// remove old
for key := range b.items {
_, found := (*b.val)[key]
if !found {
delete(b.items, key)
changed = true
}
}
if changed {
b.trigger()
}
for k, item := range b.items {
var err error
if b.updateExternal {
err = item.(*boundExternalMapValue).setIfChanged((*b.val)[k])
} else {
err = item.(*boundMapValue).set((*b.val)[k])
}
if err != nil {
retErr = err
}
}
return retErr
}
func (b *mapBase) setItem(key string, d reflectUntyped) {
b.items[key] = d
b.trigger()
}
type boundStruct struct {
mapBase
orig any
}
func (b *boundStruct) Reload() (retErr error) {
b.lock.Lock()
defer b.lock.Unlock()
v := reflect.ValueOf(b.orig).Elem()
t := v.Type()
for j := 0; j < v.NumField(); j++ {
f := v.Field(j)
if !f.CanSet() {
continue
}
kind := f.Kind()
if kind == reflect.Slice || kind == reflect.Struct {
fyne.LogError("Data binding does not yet support slice or struct elements in a struct", nil)
continue
}
key := t.Field(j).Name
old := (*b.val)[key]
if f.Interface() == old {
continue
}
var err error
switch kind {
case reflect.Bool:
err = b.items[key].(*boundReflect[bool]).Set(f.Bool())
case reflect.Float32, reflect.Float64:
err = b.items[key].(*boundReflect[float64]).Set(f.Float())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
err = b.items[key].(*boundReflect[int]).Set(int(f.Int()))
case reflect.String:
err = b.items[key].(*boundReflect[string]).Set(f.String())
}
if err != nil {
retErr = err
}
(*b.val)[key] = f.Interface()
}
return retErr
}
func bindUntypedMapValue(m *map[string]any, k string, external bool) reflectUntyped {
if external {
ret := &boundExternalMapValue{old: (*m)[k]}
ret.val = m
ret.key = k
return ret
}
return &boundMapValue{val: m, key: k}
}
type boundMapValue struct {
base
val *map[string]any
key string
}
func (b *boundMapValue) get() (any, error) {
if v, ok := (*b.val)[b.key]; ok {
return v, nil
}
return nil, errKeyNotFound
}
func (b *boundMapValue) set(val any) error {
(*b.val)[b.key] = val
b.trigger()
return nil
}
type boundExternalMapValue struct {
boundMapValue
old any
}
func (b *boundExternalMapValue) setIfChanged(val any) error {
if val == b.old {
return nil
}
b.old = val
return b.set(val)
}
type boundReflect[T any] struct {
base
val reflect.Value
}
func (b *boundReflect[T]) Get() (T, error) {
var zero T
val, err := b.get()
if err != nil {
return zero, err
}
casted, ok := val.(T)
if !ok {
return zero, errors.New("unable to convert value to type")
}
return casted, nil
}
func (b *boundReflect[T]) Set(val T) error {
return b.set(val)
}
func (b *boundReflect[T]) get() (any, error) {
if !b.val.CanInterface() {
return nil, errors.New("unable to get value from data binding")
}
return b.val.Interface(), nil
}
func (b *boundReflect[T]) set(val any) error {
if !b.val.CanSet() {
return errors.New("unable to set value in data binding")
}
b.val.Set(reflect.ValueOf(val))
b.trigger()
return nil
}
func bindReflect(field reflect.Value) reflectUntyped {
switch field.Kind() {
case reflect.Bool:
return &boundReflect[bool]{val: field}
case reflect.Float32, reflect.Float64:
return &boundReflect[float64]{val: field}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return &boundReflect[int]{val: field}
case reflect.String:
return &boundReflect[string]{val: field}
}
return &boundReflect[any]{val: field}
}