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
618 lines
14 KiB
Go
618 lines
14 KiB
Go
package binding
|
|
|
|
import (
|
|
"bytes"
|
|
|
|
"fyne.io/fyne/v2"
|
|
"fyne.io/fyne/v2/storage"
|
|
)
|
|
|
|
// DataTreeRootID const is the value used as ID for the root of any tree binding.
|
|
const DataTreeRootID = ""
|
|
|
|
// Tree supports binding a tree of values with type T.
|
|
//
|
|
// Since: 2.7
|
|
type Tree[T any] interface {
|
|
DataTree
|
|
|
|
Append(parent, id string, value T) error
|
|
Get() (map[string][]string, map[string]T, error)
|
|
GetValue(id string) (T, error)
|
|
Prepend(parent, id string, value T) error
|
|
Remove(id string) error
|
|
Set(ids map[string][]string, values map[string]T) error
|
|
SetValue(id string, value T) error
|
|
}
|
|
|
|
// ExternalTree supports binding a tree of values, of type T, from an external variable.
|
|
//
|
|
// Since: 2.7
|
|
type ExternalTree[T any] interface {
|
|
Tree[T]
|
|
|
|
Reload() error
|
|
}
|
|
|
|
// NewTree returns a bindable tree of values with type T.
|
|
//
|
|
// Since: 2.7
|
|
func NewTree[T any](comparator func(T, T) bool) Tree[T] {
|
|
return newTree[T](comparator)
|
|
}
|
|
|
|
// BindTree returns a bound tree of values with type T, based on the contents of the passed values.
|
|
// The ids map specifies how each item relates to its parent (with id ""), with the values being in the v map.
|
|
// If your code changes the content of the maps this refers to you should call Reload() to inform the bindings.
|
|
//
|
|
// Since: 2.7
|
|
func BindTree[T any](ids *map[string][]string, v *map[string]T, comparator func(T, T) bool) ExternalTree[T] {
|
|
return bindTree(ids, v, comparator)
|
|
}
|
|
|
|
// DataTree is the base interface for all bindable data trees.
|
|
//
|
|
// Since: 2.4
|
|
type DataTree interface {
|
|
DataItem
|
|
GetItem(id string) (DataItem, error)
|
|
ChildIDs(string) []string
|
|
}
|
|
|
|
// BoolTree supports binding a tree of bool values.
|
|
//
|
|
// Since: 2.4
|
|
type BoolTree = Tree[bool]
|
|
|
|
// ExternalBoolTree supports binding a tree of bool values from an external variable.
|
|
//
|
|
// Since: 2.4
|
|
type ExternalBoolTree = ExternalTree[bool]
|
|
|
|
// NewBoolTree returns a bindable tree of bool values.
|
|
//
|
|
// Since: 2.4
|
|
func NewBoolTree() Tree[bool] {
|
|
return newTreeComparable[bool]()
|
|
}
|
|
|
|
// BindBoolTree returns a bound tree of bool values, based on the contents of the passed values.
|
|
// The ids map specifies how each item relates to its parent (with id ""), with the values being in the v map.
|
|
// If your code changes the content of the maps this refers to you should call Reload() to inform the bindings.
|
|
//
|
|
// Since: 2.4
|
|
func BindBoolTree(ids *map[string][]string, v *map[string]bool) ExternalTree[bool] {
|
|
return bindTreeComparable(ids, v)
|
|
}
|
|
|
|
// BytesTree supports binding a tree of []byte values.
|
|
//
|
|
// Since: 2.4
|
|
type BytesTree = Tree[[]byte]
|
|
|
|
// ExternalBytesTree supports binding a tree of []byte values from an external variable.
|
|
//
|
|
// Since: 2.4
|
|
type ExternalBytesTree = ExternalTree[[]byte]
|
|
|
|
// NewBytesTree returns a bindable tree of []byte values.
|
|
//
|
|
// Since: 2.4
|
|
func NewBytesTree() Tree[[]byte] {
|
|
return newTree(bytes.Equal)
|
|
}
|
|
|
|
// BindBytesTree returns a bound tree of []byte values, based on the contents of the passed values.
|
|
// The ids map specifies how each item relates to its parent (with id ""), with the values being in the v map.
|
|
// If your code changes the content of the maps this refers to you should call Reload() to inform the bindings.
|
|
//
|
|
// Since: 2.4
|
|
func BindBytesTree(ids *map[string][]string, v *map[string][]byte) ExternalTree[[]byte] {
|
|
return bindTree(ids, v, bytes.Equal)
|
|
}
|
|
|
|
// FloatTree supports binding a tree of float64 values.
|
|
//
|
|
// Since: 2.4
|
|
type FloatTree = Tree[float64]
|
|
|
|
// ExternalFloatTree supports binding a tree of float64 values from an external variable.
|
|
//
|
|
// Since: 2.4
|
|
type ExternalFloatTree = ExternalTree[float64]
|
|
|
|
// NewFloatTree returns a bindable tree of float64 values.
|
|
//
|
|
// Since: 2.4
|
|
func NewFloatTree() Tree[float64] {
|
|
return newTreeComparable[float64]()
|
|
}
|
|
|
|
// BindFloatTree returns a bound tree of float64 values, based on the contents of the passed values.
|
|
// The ids map specifies how each item relates to its parent (with id ""), with the values being in the v map.
|
|
// If your code changes the content of the maps this refers to you should call Reload() to inform the bindings.
|
|
//
|
|
// Since: 2.4
|
|
func BindFloatTree(ids *map[string][]string, v *map[string]float64) ExternalTree[float64] {
|
|
return bindTreeComparable(ids, v)
|
|
}
|
|
|
|
// IntTree supports binding a tree of int values.
|
|
//
|
|
// Since: 2.4
|
|
type IntTree = Tree[int]
|
|
|
|
// ExternalIntTree supports binding a tree of int values from an external variable.
|
|
//
|
|
// Since: 2.4
|
|
type ExternalIntTree = ExternalTree[int]
|
|
|
|
// NewIntTree returns a bindable tree of int values.
|
|
//
|
|
// Since: 2.4
|
|
func NewIntTree() Tree[int] {
|
|
return newTreeComparable[int]()
|
|
}
|
|
|
|
// BindIntTree returns a bound tree of int values, based on the contents of the passed values.
|
|
// The ids map specifies how each item relates to its parent (with id ""), with the values being in the v map.
|
|
// If your code changes the content of the maps this refers to you should call Reload() to inform the bindings.
|
|
//
|
|
// Since: 2.4
|
|
func BindIntTree(ids *map[string][]string, v *map[string]int) ExternalTree[int] {
|
|
return bindTreeComparable(ids, v)
|
|
}
|
|
|
|
// RuneTree supports binding a tree of rune values.
|
|
//
|
|
// Since: 2.4
|
|
type RuneTree = Tree[rune]
|
|
|
|
// ExternalRuneTree supports binding a tree of rune values from an external variable.
|
|
//
|
|
// Since: 2.4
|
|
type ExternalRuneTree = ExternalTree[rune]
|
|
|
|
// NewRuneTree returns a bindable tree of rune values.
|
|
//
|
|
// Since: 2.4
|
|
func NewRuneTree() Tree[rune] {
|
|
return newTreeComparable[rune]()
|
|
}
|
|
|
|
// BindRuneTree returns a bound tree of rune values, based on the contents of the passed values.
|
|
// The ids map specifies how each item relates to its parent (with id ""), with the values being in the v map.
|
|
// If your code changes the content of the maps this refers to you should call Reload() to inform the bindings.
|
|
//
|
|
// Since: 2.4
|
|
func BindRuneTree(ids *map[string][]string, v *map[string]rune) ExternalTree[rune] {
|
|
return bindTreeComparable(ids, v)
|
|
}
|
|
|
|
// StringTree supports binding a tree of string values.
|
|
//
|
|
// Since: 2.4
|
|
type StringTree = Tree[string]
|
|
|
|
// ExternalStringTree supports binding a tree of string values from an external variable.
|
|
//
|
|
// Since: 2.4
|
|
type ExternalStringTree = ExternalTree[string]
|
|
|
|
// NewStringTree returns a bindable tree of string values.
|
|
//
|
|
// Since: 2.4
|
|
func NewStringTree() Tree[string] {
|
|
return newTreeComparable[string]()
|
|
}
|
|
|
|
// BindStringTree returns a bound tree of string values, based on the contents of the passed values.
|
|
// The ids map specifies how each item relates to its parent (with id ""), with the values being in the v map.
|
|
// If your code changes the content of the maps this refers to you should call Reload() to inform the bindings.
|
|
//
|
|
// Since: 2.4
|
|
func BindStringTree(ids *map[string][]string, v *map[string]string) ExternalTree[string] {
|
|
return bindTreeComparable(ids, v)
|
|
}
|
|
|
|
// UntypedTree supports binding a tree of any values.
|
|
//
|
|
// Since: 2.5
|
|
type UntypedTree = Tree[any]
|
|
|
|
// ExternalUntypedTree supports binding a tree of any values from an external variable.
|
|
//
|
|
// Since: 2.5
|
|
type ExternalUntypedTree = ExternalTree[any]
|
|
|
|
// NewUntypedTree returns a bindable tree of any values.
|
|
//
|
|
// Since: 2.5
|
|
func NewUntypedTree() Tree[any] {
|
|
return newTree(func(a1, a2 any) bool { return a1 == a2 })
|
|
}
|
|
|
|
// BindUntypedTree returns a bound tree of any values, based on the contents of the passed values.
|
|
// The ids map specifies how each item relates to its parent (with id ""), with the values being in the v map.
|
|
// If your code changes the content of the maps this refers to you should call Reload() to inform the bindings.
|
|
//
|
|
// Since: 2.4
|
|
func BindUntypedTree(ids *map[string][]string, v *map[string]any) ExternalTree[any] {
|
|
return bindTree(ids, v, func(a1, a2 any) bool { return a1 == a2 })
|
|
}
|
|
|
|
// URITree supports binding a tree of fyne.URI values.
|
|
//
|
|
// Since: 2.4
|
|
type URITree = Tree[fyne.URI]
|
|
|
|
// ExternalURITree supports binding a tree of fyne.URI values from an external variable.
|
|
//
|
|
// Since: 2.4
|
|
type ExternalURITree = ExternalTree[fyne.URI]
|
|
|
|
// NewURITree returns a bindable tree of fyne.URI values.
|
|
//
|
|
// Since: 2.4
|
|
func NewURITree() Tree[fyne.URI] {
|
|
return newTree(storage.EqualURI)
|
|
}
|
|
|
|
// BindURITree returns a bound tree of fyne.URI values, based on the contents of the passed values.
|
|
// The ids map specifies how each item relates to its parent (with id ""), with the values being in the v map.
|
|
// If your code changes the content of the maps this refers to you should call Reload() to inform the bindings.
|
|
//
|
|
// Since: 2.4
|
|
func BindURITree(ids *map[string][]string, v *map[string]fyne.URI) ExternalTree[fyne.URI] {
|
|
return bindTree(ids, v, storage.EqualURI)
|
|
}
|
|
|
|
type treeBase struct {
|
|
base
|
|
|
|
ids map[string][]string
|
|
items map[string]DataItem
|
|
}
|
|
|
|
// GetItem returns the DataItem at the specified id.
|
|
func (t *treeBase) GetItem(id string) (DataItem, error) {
|
|
t.lock.RLock()
|
|
defer t.lock.RUnlock()
|
|
|
|
if item, ok := t.items[id]; ok {
|
|
return item, nil
|
|
}
|
|
|
|
return nil, errOutOfBounds
|
|
}
|
|
|
|
// ChildIDs returns the ordered IDs of items in this data tree that are children of the specified ID.
|
|
func (t *treeBase) ChildIDs(id string) []string {
|
|
t.lock.RLock()
|
|
defer t.lock.RUnlock()
|
|
|
|
if ids, ok := t.ids[id]; ok {
|
|
return ids
|
|
}
|
|
|
|
return []string{}
|
|
}
|
|
|
|
func (t *treeBase) appendItem(i DataItem, id, parent string) {
|
|
t.items[id] = i
|
|
|
|
ids := t.ids[parent]
|
|
for _, in := range ids {
|
|
if in == id {
|
|
return
|
|
}
|
|
}
|
|
t.ids[parent] = append(ids, id)
|
|
}
|
|
|
|
func (t *treeBase) deleteItem(id, parent string) {
|
|
delete(t.items, id)
|
|
|
|
ids, ok := t.ids[parent]
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
off := -1
|
|
for i, id2 := range ids {
|
|
if id2 == id {
|
|
off = i
|
|
break
|
|
}
|
|
}
|
|
if off == -1 {
|
|
return
|
|
}
|
|
t.ids[parent] = append(ids[:off], ids[off+1:]...)
|
|
}
|
|
|
|
func parentIDFor(id string, ids map[string][]string) string {
|
|
for parent, list := range ids {
|
|
for _, child := range list {
|
|
if child == id {
|
|
return parent
|
|
}
|
|
}
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func newTree[T any](comparator func(T, T) bool) *boundTree[T] {
|
|
t := &boundTree[T]{val: &map[string]T{}, comparator: comparator}
|
|
t.ids = make(map[string][]string)
|
|
t.items = make(map[string]DataItem)
|
|
return t
|
|
}
|
|
|
|
func newTreeComparable[T comparable]() *boundTree[T] {
|
|
return newTree(func(t1, t2 T) bool { return t1 == t2 })
|
|
}
|
|
|
|
func bindTree[T any](ids *map[string][]string, v *map[string]T, comparator func(T, T) bool) *boundTree[T] {
|
|
if v == nil {
|
|
return newTree(comparator)
|
|
}
|
|
|
|
t := &boundTree[T]{val: v, updateExternal: true, comparator: comparator}
|
|
t.ids = make(map[string][]string)
|
|
t.items = make(map[string]DataItem)
|
|
|
|
for parent, children := range *ids {
|
|
for _, leaf := range children {
|
|
t.appendItem(bindTreeItem(v, leaf, t.updateExternal, t.comparator), leaf, parent)
|
|
}
|
|
}
|
|
|
|
return t
|
|
}
|
|
|
|
func bindTreeComparable[T comparable](ids *map[string][]string, v *map[string]T) *boundTree[T] {
|
|
return bindTree(ids, v, func(t1, t2 T) bool { return t1 == t2 })
|
|
}
|
|
|
|
type boundTree[T any] struct {
|
|
treeBase
|
|
|
|
comparator func(T, T) bool
|
|
val *map[string]T
|
|
updateExternal bool
|
|
}
|
|
|
|
func (t *boundTree[T]) Append(parent, id string, val T) error {
|
|
t.lock.Lock()
|
|
|
|
t.ids[parent] = append(t.ids[parent], id)
|
|
v := *t.val
|
|
v[id] = val
|
|
|
|
trigger, err := t.doReload()
|
|
t.lock.Unlock()
|
|
|
|
if trigger {
|
|
t.trigger()
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (t *boundTree[T]) Get() (map[string][]string, map[string]T, error) {
|
|
t.lock.RLock()
|
|
defer t.lock.RUnlock()
|
|
|
|
return t.ids, *t.val, nil
|
|
}
|
|
|
|
func (t *boundTree[T]) GetValue(id string) (T, error) {
|
|
t.lock.RLock()
|
|
defer t.lock.RUnlock()
|
|
|
|
if item, ok := (*t.val)[id]; ok {
|
|
return item, nil
|
|
}
|
|
|
|
return *new(T), errOutOfBounds
|
|
}
|
|
|
|
func (t *boundTree[T]) Prepend(parent, id string, val T) error {
|
|
t.lock.Lock()
|
|
|
|
t.ids[parent] = append([]string{id}, t.ids[parent]...)
|
|
v := *t.val
|
|
v[id] = val
|
|
|
|
trigger, err := t.doReload()
|
|
t.lock.Unlock()
|
|
|
|
if trigger {
|
|
t.trigger()
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (t *boundTree[T]) Remove(id string) error {
|
|
t.lock.Lock()
|
|
t.removeChildren(id)
|
|
delete(t.ids, id)
|
|
v := *t.val
|
|
delete(v, id)
|
|
|
|
trigger, err := t.doReload()
|
|
t.lock.Unlock()
|
|
|
|
if trigger {
|
|
t.trigger()
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (t *boundTree[T]) removeChildren(id string) {
|
|
for _, cid := range t.ids[id] {
|
|
t.removeChildren(cid)
|
|
|
|
delete(t.ids, cid)
|
|
v := *t.val
|
|
delete(v, cid)
|
|
}
|
|
}
|
|
|
|
func (t *boundTree[T]) Reload() error {
|
|
t.lock.Lock()
|
|
trigger, err := t.doReload()
|
|
t.lock.Unlock()
|
|
|
|
if trigger {
|
|
t.trigger()
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (t *boundTree[T]) Set(ids map[string][]string, v map[string]T) error {
|
|
t.lock.Lock()
|
|
t.ids = ids
|
|
*t.val = v
|
|
|
|
trigger, err := t.doReload()
|
|
t.lock.Unlock()
|
|
|
|
if trigger {
|
|
t.trigger()
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (t *boundTree[T]) doReload() (fire bool, retErr error) {
|
|
updated := []string{}
|
|
for id := range *t.val {
|
|
found := false
|
|
for child := range t.items {
|
|
if child == id { // update existing
|
|
updated = append(updated, id)
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if found {
|
|
continue
|
|
}
|
|
|
|
// append new
|
|
t.appendItem(bindTreeItem(t.val, id, t.updateExternal, t.comparator), id, parentIDFor(id, t.ids))
|
|
updated = append(updated, id)
|
|
fire = true
|
|
}
|
|
|
|
for id := range t.items {
|
|
remove := true
|
|
for _, done := range updated {
|
|
if done == id {
|
|
remove = false
|
|
break
|
|
}
|
|
}
|
|
|
|
if remove { // remove item no longer present
|
|
fire = true
|
|
t.deleteItem(id, parentIDFor(id, t.ids))
|
|
}
|
|
}
|
|
|
|
for id, item := range t.items {
|
|
var err error
|
|
if t.updateExternal {
|
|
err = item.(*boundExternalTreeItem[T]).setIfChanged((*t.val)[id])
|
|
} else {
|
|
err = item.(*boundTreeItem[T]).doSet((*t.val)[id])
|
|
}
|
|
if err != nil {
|
|
retErr = err
|
|
}
|
|
}
|
|
return fire, retErr
|
|
}
|
|
|
|
func (t *boundTree[T]) SetValue(id string, v T) error {
|
|
t.lock.Lock()
|
|
(*t.val)[id] = v
|
|
t.lock.Unlock()
|
|
|
|
item, err := t.GetItem(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return item.(Item[T]).Set(v)
|
|
}
|
|
|
|
func bindTreeItem[T any](v *map[string]T, id string, external bool, comparator func(T, T) bool) Item[T] {
|
|
if external {
|
|
ret := &boundExternalTreeItem[T]{old: (*v)[id], comparator: comparator}
|
|
ret.val = v
|
|
ret.id = id
|
|
return ret
|
|
}
|
|
|
|
return &boundTreeItem[T]{id: id, val: v}
|
|
}
|
|
|
|
type boundTreeItem[T any] struct {
|
|
base
|
|
|
|
val *map[string]T
|
|
id string
|
|
}
|
|
|
|
func (t *boundTreeItem[T]) Get() (T, error) {
|
|
t.lock.Lock()
|
|
defer t.lock.Unlock()
|
|
|
|
v := *t.val
|
|
if item, ok := v[t.id]; ok {
|
|
return item, nil
|
|
}
|
|
|
|
return *new(T), errOutOfBounds
|
|
}
|
|
|
|
func (t *boundTreeItem[T]) Set(val T) error {
|
|
return t.doSet(val)
|
|
}
|
|
|
|
func (t *boundTreeItem[T]) doSet(val T) error {
|
|
t.lock.Lock()
|
|
(*t.val)[t.id] = val
|
|
t.lock.Unlock()
|
|
|
|
t.trigger()
|
|
return nil
|
|
}
|
|
|
|
type boundExternalTreeItem[T any] struct {
|
|
boundTreeItem[T]
|
|
|
|
comparator func(T, T) bool
|
|
old T
|
|
}
|
|
|
|
func (t *boundExternalTreeItem[T]) setIfChanged(val T) error {
|
|
t.lock.Lock()
|
|
if t.comparator(val, t.old) {
|
|
t.lock.Unlock()
|
|
return nil
|
|
}
|
|
(*t.val)[t.id] = val
|
|
t.old = val
|
|
t.lock.Unlock()
|
|
|
|
t.trigger()
|
|
return nil
|
|
}
|