| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522 |
- 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}
- }
|