• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

fyne-io / fyne / 13246048236

10 Feb 2025 04:50PM UTC coverage: 62.558% (-0.03%) from 62.586%
13246048236

push

github

web-flow
Merge pull request #5505 from Jacalz/generic-binding-item

48 of 55 new or added lines in 6 files covered. (87.27%)

4 existing lines in 1 file now uncovered.

24851 of 39725 relevant lines covered (62.56%)

828.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.69
/data/binding/binding.go
1
//go:generate go run gen.go
2

3
// Package binding provides support for binding data to widgets.
4
// All APIs in the binding package are safe to invoke directly from any goroutine.
5
package binding
6

7
import (
8
        "errors"
9
        "reflect"
10
        "sync"
11

12
        "fyne.io/fyne/v2"
13
)
14

15
var (
16
        errKeyNotFound = errors.New("key not found")
17
        errOutOfBounds = errors.New("index out of bounds")
18
        errParseFailed = errors.New("format did not match 1 value")
19

20
        // As an optimisation we connect any listeners asking for the same key, so that there is only 1 per preference item.
21
        prefBinds = newPreferencesMap()
22
)
23

24
// DataItem is the base interface for all bindable data items.
25
// All APIs on bindable data items are safe to invoke directly fron any goroutine.
26
//
27
// Since: 2.0
28
type DataItem interface {
29
        // AddListener attaches a new change listener to this DataItem.
30
        // Listeners are called each time the data inside this DataItem changes.
31
        // Additionally, the listener will be triggered upon successful connection to get the current value.
32
        AddListener(DataListener)
33
        // RemoveListener will detach the specified change listener from the DataItem.
34
        // Disconnected listener will no longer be triggered when changes occur.
35
        RemoveListener(DataListener)
36
}
37

38
// DataListener is any object that can register for changes in a bindable DataItem.
39
// See NewDataListener to define a new listener using just an inline function.
40
//
41
// Since: 2.0
42
type DataListener interface {
43
        DataChanged()
44
}
45

46
// NewDataListener is a helper function that creates a new listener type from a simple callback function.
47
//
48
// Since: 2.0
49
func NewDataListener(fn func()) DataListener {
27✔
50
        return &listener{fn}
27✔
51
}
27✔
52

53
type listener struct {
54
        callback func()
55
}
56

57
func (l *listener) DataChanged() {
76✔
58
        l.callback()
76✔
59
}
76✔
60

61
type base struct {
62
        listeners []DataListener
63

64
        lock sync.RWMutex
65
}
66

67
// AddListener allows a data listener to be informed of changes to this item.
68
func (b *base) AddListener(l DataListener) {
52✔
69
        fyne.Do(func() {
104✔
70
                b.listeners = append(b.listeners, l)
52✔
71
                l.DataChanged()
52✔
72
        })
52✔
73
}
74

75
// RemoveListener should be called if the listener is no longer interested in being informed of data change events.
76
func (b *base) RemoveListener(l DataListener) {
3✔
77
        fyne.Do(func() {
6✔
78
                for i, listener := range b.listeners {
6✔
79
                        if listener == l {
6✔
80
                                // Delete without preserving order:
3✔
81
                                lastIndex := len(b.listeners) - 1
3✔
82
                                b.listeners[i] = b.listeners[lastIndex]
3✔
83
                                b.listeners[lastIndex] = nil
3✔
84
                                b.listeners = b.listeners[:lastIndex]
3✔
85
                                return
3✔
86
                        }
3✔
87
                }
88
        })
89
}
90

91
func (b *base) trigger() {
378✔
92
        fyne.Do(b.triggerFromMain)
378✔
93
}
378✔
94

95
func (b *base) triggerFromMain() {
431✔
96
        for _, listen := range b.listeners {
532✔
97
                listen.DataChanged()
101✔
98
        }
101✔
99
}
100

101
// Untyped supports binding an any value.
102
//
103
// Since: 2.1
104
type Untyped = Item[any]
105

106
// NewUntyped returns a bindable any value that is managed internally.
107
//
108
// Since: 2.1
109
func NewUntyped() Untyped {
2✔
110
        return NewItem(func(a1, a2 any) bool { return a1 == a2 })
9✔
111
}
112

113
// ExternalUntyped supports binding a any value to an external value.
114
//
115
// Since: 2.1
116
type ExternalUntyped = ExternalItem[any]
117

118
// BindUntyped returns a bindable any value that is bound to an external type.
119
// The parameter must be a pointer to the type you wish to bind.
120
//
121
// Since: 2.1
122
func BindUntyped(v any) ExternalUntyped {
2✔
123
        t := reflect.TypeOf(v)
2✔
124
        if t.Kind() != reflect.Ptr {
2✔
125
                fyne.LogError("Invalid type passed to BindUntyped, must be a pointer", nil)
×
126
                v = nil
×
127
        }
×
128

129
        if v == nil {
2✔
NEW
130
                v = new(any) // never allow a nil value pointer
×
131
        }
×
132

133
        b := &boundExternalUntyped{}
2✔
134
        b.val = reflect.ValueOf(v).Elem()
2✔
135
        b.old = b.val.Interface()
2✔
136
        return b
2✔
137
}
138

139
type boundExternalUntyped struct {
140
        base
141

142
        val reflect.Value
143
        old any
144
}
145

146
func (b *boundExternalUntyped) Get() (any, error) {
5✔
147
        b.lock.RLock()
5✔
148
        defer b.lock.RUnlock()
5✔
149

5✔
150
        return b.val.Interface(), nil
5✔
151
}
5✔
152

153
func (b *boundExternalUntyped) Set(val any) error {
5✔
154
        b.lock.Lock()
5✔
155
        if b.old == val {
5✔
156
                b.lock.Unlock()
×
157
                return nil
×
158
        }
×
159
        b.val.Set(reflect.ValueOf(val))
5✔
160
        b.old = val
5✔
161
        b.lock.Unlock()
5✔
162

5✔
163
        b.trigger()
5✔
164
        return nil
5✔
165
}
166

167
func (b *boundExternalUntyped) Reload() error {
3✔
168
        return b.Set(b.val.Interface())
3✔
169
}
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc