fckeuspy-go/vendor/fyne.io/fyne/v2/data/binding/preference.go

262 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[bool](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[float64](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[int](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[string](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
}
if val == nil {
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
}