adam-gui/vendor/fyne.io/fyne/v2/data/binding/mapbinding.go

523 lines
9.8 KiB
Go
Raw Permalink Normal View History

2024-04-29 19:13:50 +02:00
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 (interface{}), 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 (interface{}).
//
// Since: 2.0
type UntypedMap interface {
DataMap
Delete(string)
Get() (map[string]interface{}, error)
GetValue(string) (interface{}, error)
Set(map[string]interface{}) error
SetValue(string, interface{}) error
}
// NewUntypedMap creates a new, empty map binding of string to interface{}.
//
// Since: 2.0
func NewUntypedMap() UntypedMap {
return &mapBase{items: make(map[string]reflectUntyped), val: &map[string]interface{}{}}
}
// BindUntypedMap creates a new map binding of string to interface{} 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]interface{}) 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) (interface{}, error)
SetValue(string, interface{}) error
Reload() error
}
// BindStruct creates a new map binding of string to interface{} 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 interface{}.
// Only exported fields are included.
//
// Since: 2.0
func BindStruct(i interface{}) 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]interface{}{}
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() (interface{}, error)
set(interface{}) error
}
type mapBase struct {
base
updateExternal bool
items map[string]reflectUntyped
val *map[string]interface{}
}
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]interface{}, error) {
b.lock.RLock()
defer b.lock.RUnlock()
if b.val == nil {
return map[string]interface{}{}, nil
}
return *b.val, nil
}
func (b *mapBase) GetValue(key string) (interface{}, 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]interface{}) 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 interface{}) 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
}
func (b *mapBase) setItem(key string, d reflectUntyped) {
b.items[key] = d
b.trigger()
}
type boundStruct struct {
mapBase
orig interface{}
}
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].(*reflectBool).Set(f.Bool())
case reflect.Float32, reflect.Float64:
err = b.items[key].(*reflectFloat).Set(f.Float())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
err = b.items[key].(*reflectInt).Set(int(f.Int()))
case reflect.String:
err = b.items[key].(*reflectString).Set(f.String())
}
if err != nil {
retErr = err
}
(*b.val)[key] = f.Interface()
}
return
}
func bindUntypedMapValue(m *map[string]interface{}, 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]interface{}
key string
}
func (b *boundMapValue) get() (interface{}, error) {
if v, ok := (*b.val)[b.key]; ok {
return v, nil
}
return nil, errKeyNotFound
}
func (b *boundMapValue) set(val interface{}) error {
(*b.val)[b.key] = val
b.trigger()
return nil
}
type boundExternalMapValue struct {
boundMapValue
old interface{}
}
func (b *boundExternalMapValue) setIfChanged(val interface{}) error {
if val == b.old {
return nil
}
b.old = val
return b.set(val)
}
type boundReflect struct {
base
val reflect.Value
}
func (b *boundReflect) get() (interface{}, error) {
return b.val.Interface(), nil
}
func (b *boundReflect) set(val interface{}) (err error) {
defer func() {
if r := recover(); r != nil {
err = errors.New("unable to set bool in data binding")
}
}()
b.val.Set(reflect.ValueOf(val))
b.trigger()
return nil
}
type reflectBool struct {
boundReflect
}
func (r *reflectBool) Get() (val bool, err error) {
defer func() {
if r := recover(); r != nil {
err = errors.New("invalid bool value in data binding")
}
}()
val = r.val.Bool()
return
}
func (r *reflectBool) Set(b bool) (err error) {
defer func() {
if r := recover(); r != nil {
err = errors.New("unable to set bool in data binding")
}
}()
r.val.SetBool(b)
r.trigger()
return
}
func bindReflectBool(f reflect.Value) reflectUntyped {
r := &reflectBool{}
r.val = f
return r
}
type reflectFloat struct {
boundReflect
}
func (r *reflectFloat) Get() (val float64, err error) {
defer func() {
if r := recover(); r != nil {
err = errors.New("invalid float64 value in data binding")
}
}()
val = r.val.Float()
return
}
func (r *reflectFloat) Set(f float64) (err error) {
defer func() {
if r := recover(); r != nil {
err = errors.New("unable to set float64 in data binding")
}
}()
r.val.SetFloat(f)
r.trigger()
return
}
func bindReflectFloat(f reflect.Value) reflectUntyped {
r := &reflectFloat{}
r.val = f
return r
}
type reflectInt struct {
boundReflect
}
func (r *reflectInt) Get() (val int, err error) {
defer func() {
if r := recover(); r != nil {
err = errors.New("invalid int value in data binding")
}
}()
val = int(r.val.Int())
return
}
func (r *reflectInt) Set(i int) (err error) {
defer func() {
if r := recover(); r != nil {
err = errors.New("unable to set int in data binding")
}
}()
r.val.SetInt(int64(i))
r.trigger()
return
}
func bindReflectInt(f reflect.Value) reflectUntyped {
r := &reflectInt{}
r.val = f
return r
}
type reflectString struct {
boundReflect
}
func (r *reflectString) Get() (val string, err error) {
defer func() {
if r := recover(); r != nil {
err = errors.New("invalid string value in data binding")
}
}()
val = r.val.String()
return
}
func (r *reflectString) Set(s string) (err error) {
defer func() {
if r := recover(); r != nil {
err = errors.New("unable to set string in data binding")
}
}()
r.val.SetString(s)
r.trigger()
return
}
func bindReflectString(f reflect.Value) reflectUntyped {
r := &reflectString{}
r.val = f
return r
}
func bindReflect(field reflect.Value) reflectUntyped {
switch field.Kind() {
case reflect.Bool:
return bindReflectBool(field)
case reflect.Float32, reflect.Float64:
return bindReflectFloat(field)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return bindReflectInt(field)
case reflect.String:
return bindReflectString(field)
}
return &boundReflect{val: field}
}