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

fyne-io / fyne / 13571034875

27 Feb 2025 04:14PM UTC coverage: 62.226% (-0.1%) from 62.372%
13571034875

Pull #5565

github

andydotxyz
Fix a prior mistake in helper function
Pull Request #5565: Feature/bindpreferencelists

5 of 100 new or added lines in 3 files covered. (5.0%)

7 existing lines in 2 files now uncovered.

24800 of 39855 relevant lines covered (62.23%)

715.86 hits per line

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

39.87
/data/binding/preference.go
1
package binding
2

3
import (
4
        "sync/atomic"
5

6
        "fyne.io/fyne/v2"
7
)
8

9
// Work around Go not supporting generic methods on non-generic types:
10
type preferenceLookupSetter[T any] func(fyne.Preferences) (func(string) T, func(string, T))
11

12
const keyTypeMismatchError = "A previous preference binding exists with different type for key: "
13

14
// BindPreferenceBool returns a bindable bool value that is managed by the application preferences.
15
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
16
//
17
// Since: 2.0
18
func BindPreferenceBool(key string, p fyne.Preferences) Bool {
1✔
19
        return bindPreferenceItem(key, p,
1✔
20
                func(p fyne.Preferences) (func(string) bool, func(string, bool)) {
6✔
21
                        return p.Bool, p.SetBool
5✔
22
                })
5✔
23
}
24

25
// BindPreferenceBoolList returns a bound list of bool values that is managed by the application preferences.
26
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
27
//
28
// Since: 2.6
NEW
29
func BindPreferenceBoolList(key string, p fyne.Preferences) BoolList {
×
NEW
30
        return bindPreferenceListComparable[bool](key, p,
×
NEW
31
                func(p fyne.Preferences) (func(string) []bool, func(string, []bool)) {
×
NEW
32
                        return p.BoolList, p.SetBoolList
×
NEW
33
                },
×
NEW
34
                func(a, b bool) bool {
×
NEW
35
                        return a == b
×
NEW
36
                },
×
37
        )
38
}
39

40
// BindPreferenceFloat returns a bindable float64 value that is managed by the application preferences.
41
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
42
//
43
// Since: 2.0
44
func BindPreferenceFloat(key string, p fyne.Preferences) Float {
1✔
45
        return bindPreferenceItem(key, p,
1✔
46
                func(p fyne.Preferences) (func(string) float64, func(string, float64)) {
5✔
47
                        return p.Float, p.SetFloat
4✔
48
                })
4✔
49
}
50

51
// BindPreferenceFloatList returns a bound list of float64 values that is managed by the application preferences.
52
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
53
//
54
// Since: 2.6
NEW
55
func BindPreferenceFloatList(key string, p fyne.Preferences) FloatList {
×
NEW
56
        return bindPreferenceListComparable[float64](key, p,
×
NEW
57
                func(p fyne.Preferences) (func(string) []float64, func(string, []float64)) {
×
NEW
58
                        return p.FloatList, p.SetFloatList
×
NEW
59
                },
×
NEW
60
                func(a, b float64) bool {
×
NEW
61
                        return a == b
×
NEW
62
                },
×
63
        )
64
}
65

66
// BindPreferenceInt returns a bindable int value that is managed by the application preferences.
67
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
68
//
69
// Since: 2.0
70
func BindPreferenceInt(key string, p fyne.Preferences) Int {
101✔
71
        return bindPreferenceItem(key, p,
101✔
72
                func(p fyne.Preferences) (func(string) int, func(string, int)) {
110✔
73
                        return p.Int, p.SetInt
9✔
74
                })
9✔
75
}
76

77
// BindPreferenceIntList returns a bound list of int values that is managed by the application preferences.
78
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
79
//
80
// Since: 2.6
NEW
81
func BindPreferenceIntList(key string, p fyne.Preferences) IntList {
×
NEW
82
        return bindPreferenceListComparable[int](key, p,
×
NEW
83
                func(p fyne.Preferences) (func(string) []int, func(string, []int)) {
×
NEW
84
                        return p.IntList, p.SetIntList
×
NEW
85
                },
×
NEW
86
                func(a, b int) bool {
×
NEW
87
                        return a == b
×
NEW
88
                },
×
89
        )
90
}
91

92
// BindPreferenceString returns a bindable string value that is managed by the application preferences.
93
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
94
//
95
// Since: 2.0
96
func BindPreferenceString(key string, p fyne.Preferences) String {
5✔
97
        return bindPreferenceItem(key, p,
5✔
98
                func(p fyne.Preferences) (func(string) string, func(string, string)) {
8✔
99
                        return p.String, p.SetString
3✔
100
                })
3✔
101
}
102

103
// BindPreferenceStringList returns a bound list of string values that is managed by the application preferences.
104
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
105
//
106
// Since: 2.6
NEW
107
func BindPreferenceStringList(key string, p fyne.Preferences) StringList {
×
NEW
108
        return bindPreferenceListComparable[string](key, p,
×
NEW
109
                func(p fyne.Preferences) (func(string) []string, func(string, []string)) {
×
NEW
110
                        return p.StringList, p.SetStringList
×
NEW
111
                },
×
NEW
112
                func(a, b string) bool {
×
NEW
113
                        return a == b
×
NEW
114
                },
×
115
        )
116
}
117

118
func bindPreferenceItem[T bool | float64 | int | string](key string, p fyne.Preferences, setLookup preferenceLookupSetter[T]) Item[T] {
108✔
119
        if found, ok := lookupExistingBinding[T](key, p); ok {
210✔
120
                return found
102✔
121
        }
102✔
122

123
        listen := &prefBoundBase[T]{key: key, setLookup: setLookup}
6✔
124
        listen.replaceProvider(p)
6✔
125
        binds := prefBinds.ensurePreferencesAttached(p)
6✔
126
        binds.Store(key, listen)
6✔
127
        return listen
6✔
128
}
129

130
func lookupExistingBinding[T any](key string, p fyne.Preferences) (Item[T], bool) {
108✔
131
        binds := prefBinds.getBindings(p)
108✔
132
        if binds == nil {
110✔
133
                return nil, false
2✔
134
        }
2✔
135

136
        if listen, ok := binds.Load(key); listen != nil && ok {
208✔
137
                if l, ok := listen.(Item[T]); ok {
204✔
138
                        return l, ok
102✔
139
                }
102✔
140
                fyne.LogError(keyTypeMismatchError+key, nil)
×
141
        }
142

143
        return nil, false
4✔
144
}
145

NEW
146
func lookupExistingListBinding[T bool | float64 | int | string](key string, p fyne.Preferences) (*prefBoundList[T], bool) {
×
NEW
147
        binds := prefBinds.getBindings(p)
×
NEW
148
        if binds == nil {
×
NEW
149
                return nil, false
×
NEW
150
        }
×
151

NEW
152
        if listen, ok := binds.Load(key); listen != nil && ok {
×
NEW
153
                if l, ok := listen.(*prefBoundList[T]); ok {
×
NEW
154
                        return l, ok
×
NEW
155
                }
×
NEW
156
                fyne.LogError(keyTypeMismatchError+key, nil)
×
157
        }
158

NEW
159
        return nil, false
×
160
}
161

162
type prefBoundBase[T bool | float64 | int | string] struct {
163
        base
164
        key string
165

166
        get       func(string) T
167
        set       func(string, T)
168
        setLookup preferenceLookupSetter[T]
169
        cache     atomic.Pointer[T]
170
}
171

172
func (b *prefBoundBase[T]) Get() (T, error) {
12✔
173
        cache := b.get(b.key)
12✔
174
        b.cache.Store(&cache)
12✔
175
        return cache, nil
12✔
176
}
12✔
177

178
func (b *prefBoundBase[T]) Set(v T) error {
6✔
179
        b.set(b.key, v)
6✔
180

6✔
181
        b.lock.RLock()
6✔
182
        defer b.lock.RUnlock()
6✔
183
        b.trigger()
6✔
184
        return nil
6✔
185
}
6✔
186

187
func (b *prefBoundBase[T]) checkForChange() {
17✔
188
        val := b.cache.Load()
17✔
189
        if val != nil && b.get(b.key) == *val {
21✔
190
                return
4✔
191
        }
4✔
192
        b.trigger()
13✔
193
}
194

195
func (b *prefBoundBase[T]) replaceProvider(p fyne.Preferences) {
21✔
196
        b.get, b.set = b.setLookup(p)
21✔
197
}
21✔
198

199
type prefBoundList[T bool | float64 | int | string] struct {
200
        boundList[T]
201
        key string
202

203
        get       func(string) []T
204
        set       func(string, []T)
205
        setLookup preferenceLookupSetter[[]T]
206
}
207

NEW
208
func (b *prefBoundList[T]) checkForChange() {
×
NEW
209
        val := *b.val
×
NEW
210
        updated := b.get(b.key)
×
NEW
211
        if val == nil || len(updated) != len(val) {
×
NEW
212
                b.Set(updated)
×
NEW
213
                return
×
NEW
214
        }
×
NEW
215
        if val == nil {
×
NEW
216
                return
×
NEW
217
        }
×
218

219
        // incoming changes to a preference list are not at the child level
NEW
220
        for i, v := range val {
×
NEW
221
                if i >= len(updated) {
×
NEW
222
                        break
×
223
                }
224

NEW
225
                if !b.comparator(v, updated[i]) {
×
NEW
226
                        _ = b.items[i].(Item[T]).Set(updated[i])
×
NEW
227
                }
×
228
        }
229
}
230

NEW
231
func (b *prefBoundList[T]) replaceProvider(p fyne.Preferences) {
×
NEW
232
        b.get, b.set = b.setLookup(p)
×
NEW
233
}
×
234

235
type internalPrefs = interface{ WriteValues(func(map[string]any)) }
236

237
func bindPreferenceListComparable[T bool | float64 | int | string](key string, p fyne.Preferences,
NEW
238
        setLookup preferenceLookupSetter[[]T], compare func(T, T) bool) *prefBoundList[T] {
×
NEW
239
        if found, ok := lookupExistingListBinding[T](key, p); ok {
×
NEW
240
                return found
×
NEW
241
        }
×
242

NEW
243
        listen := &prefBoundList[T]{key: key, setLookup: setLookup}
×
NEW
244
        listen.replaceProvider(p)
×
NEW
245

×
NEW
246
        items := listen.get(listen.key)
×
NEW
247
        listen.boundList = *bindList(nil, compare)
×
NEW
248

×
NEW
249
        listen.boundList.AddListener(NewDataListener(func() {
×
NEW
250
                cached := *listen.val
×
NEW
251
                replaced := listen.get(listen.key)
×
NEW
252
                if len(cached) == len(replaced) {
×
NEW
253
                        return
×
NEW
254
                }
×
255

NEW
256
                listen.set(listen.key, *listen.val)
×
NEW
257
                listen.trigger()
×
258
        }))
259

NEW
260
        listen.boundList.parentListener = func(index int) {
×
NEW
261
                listen.set(listen.key, *listen.val)
×
NEW
262

×
NEW
263
                // the child changes are not seen on the write end so force it
×
NEW
264
                if prefs, ok := p.(internalPrefs); ok {
×
NEW
265
                        prefs.WriteValues(func(map[string]any) {})
×
266
                }
267
        }
NEW
268
        listen.boundList.Set(items)
×
NEW
269

×
NEW
270
        binds := prefBinds.ensurePreferencesAttached(p)
×
NEW
271
        binds.Store(key, listen)
×
NEW
272
        return listen
×
273
}
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