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

fyne-io / fyne / 13594950227

28 Feb 2025 07:16PM UTC coverage: 62.296% (-0.2%) from 62.449%
13594950227

push

github

web-flow
Merge pull request #5565 from andydotxyz/feature/bindpreferencelists

Feature/bindpreferencelists

5 of 87 new or added lines in 3 files covered. (5.75%)

11 existing lines in 3 files now uncovered.

24970 of 40083 relevant lines covered (62.3%)

717.46 hits per line

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

43.57
/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
                },
×
34
        )
35
}
36

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

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

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

71
// BindPreferenceIntList returns a bound list of int values that is managed by the application preferences.
72
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
73
//
74
// Since: 2.6
NEW
75
func BindPreferenceIntList(key string, p fyne.Preferences) IntList {
×
NEW
76
        return bindPreferenceListComparable[int](key, p,
×
NEW
77
                func(p fyne.Preferences) (func(string) []int, func(string, []int)) {
×
NEW
78
                        return p.IntList, p.SetIntList
×
NEW
79
                },
×
80
        )
81
}
82

83
// BindPreferenceString returns a bindable string value that is managed by the application preferences.
84
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
85
//
86
// Since: 2.0
87
func BindPreferenceString(key string, p fyne.Preferences) String {
5✔
88
        return bindPreferenceItem(key, p,
5✔
89
                func(p fyne.Preferences) (func(string) string, func(string, string)) {
8✔
90
                        return p.String, p.SetString
3✔
91
                })
3✔
92
}
93

94
// BindPreferenceStringList returns a bound list of string values that is managed by the application preferences.
95
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
96
//
97
// Since: 2.6
NEW
98
func BindPreferenceStringList(key string, p fyne.Preferences) StringList {
×
NEW
99
        return bindPreferenceListComparable[string](key, p,
×
NEW
100
                func(p fyne.Preferences) (func(string) []string, func(string, []string)) {
×
NEW
101
                        return p.StringList, p.SetStringList
×
NEW
102
                },
×
103
        )
104
}
105

106
func bindPreferenceItem[T bool | float64 | int | string](key string, p fyne.Preferences, setLookup preferenceLookupSetter[T]) Item[T] {
108✔
107
        if found, ok := lookupExistingBinding[T](key, p); ok {
210✔
108
                return found
102✔
109
        }
102✔
110

111
        listen := &prefBoundBase[T]{key: key, setLookup: setLookup}
6✔
112
        listen.replaceProvider(p)
6✔
113
        binds := prefBinds.ensurePreferencesAttached(p)
6✔
114
        binds.Store(key, listen)
6✔
115
        return listen
6✔
116
}
117

118
func lookupExistingBinding[T any](key string, p fyne.Preferences) (Item[T], bool) {
108✔
119
        binds := prefBinds.getBindings(p)
108✔
120
        if binds == nil {
110✔
121
                return nil, false
2✔
122
        }
2✔
123

124
        if listen, ok := binds.Load(key); listen != nil && ok {
208✔
125
                if l, ok := listen.(Item[T]); ok {
204✔
126
                        return l, ok
102✔
127
                }
102✔
128
                fyne.LogError(keyTypeMismatchError+key, nil)
×
129
        }
130

131
        return nil, false
4✔
132
}
133

NEW
134
func lookupExistingListBinding[T bool | float64 | int | string](key string, p fyne.Preferences) (*prefBoundList[T], bool) {
×
NEW
135
        binds := prefBinds.getBindings(p)
×
NEW
136
        if binds == nil {
×
NEW
137
                return nil, false
×
NEW
138
        }
×
139

NEW
140
        if listen, ok := binds.Load(key); listen != nil && ok {
×
NEW
141
                if l, ok := listen.(*prefBoundList[T]); ok {
×
NEW
142
                        return l, ok
×
NEW
143
                }
×
NEW
144
                fyne.LogError(keyTypeMismatchError+key, nil)
×
145
        }
146

NEW
147
        return nil, false
×
148
}
149

150
type prefBoundBase[T bool | float64 | int | string] struct {
151
        base
152
        key string
153

154
        get       func(string) T
155
        set       func(string, T)
156
        setLookup preferenceLookupSetter[T]
157
        cache     atomic.Pointer[T]
158
}
159

160
func (b *prefBoundBase[T]) Get() (T, error) {
12✔
161
        cache := b.get(b.key)
12✔
162
        b.cache.Store(&cache)
12✔
163
        return cache, nil
12✔
164
}
12✔
165

166
func (b *prefBoundBase[T]) Set(v T) error {
6✔
167
        b.set(b.key, v)
6✔
168

6✔
169
        b.lock.RLock()
6✔
170
        defer b.lock.RUnlock()
6✔
171
        b.trigger()
6✔
172
        return nil
6✔
173
}
6✔
174

175
func (b *prefBoundBase[T]) checkForChange() {
17✔
176
        val := b.cache.Load()
17✔
177
        if val != nil && b.get(b.key) == *val {
21✔
178
                return
4✔
179
        }
4✔
180
        b.trigger()
13✔
181
}
182

183
func (b *prefBoundBase[T]) replaceProvider(p fyne.Preferences) {
21✔
184
        b.get, b.set = b.setLookup(p)
21✔
185
}
21✔
186

187
type prefBoundList[T bool | float64 | int | string] struct {
188
        boundList[T]
189
        key string
190

191
        get       func(string) []T
192
        set       func(string, []T)
193
        setLookup preferenceLookupSetter[[]T]
194
}
195

NEW
196
func (b *prefBoundList[T]) checkForChange() {
×
NEW
197
        val := *b.val
×
NEW
198
        updated := b.get(b.key)
×
NEW
199
        if val == nil || len(updated) != len(val) {
×
NEW
200
                b.Set(updated)
×
NEW
201
                return
×
NEW
202
        }
×
NEW
203
        if val == nil {
×
NEW
204
                return
×
NEW
205
        }
×
206

207
        // incoming changes to a preference list are not at the child level
NEW
208
        for i, v := range val {
×
NEW
209
                if i >= len(updated) {
×
NEW
210
                        break
×
211
                }
212

NEW
213
                if !b.comparator(v, updated[i]) {
×
NEW
214
                        _ = b.items[i].(Item[T]).Set(updated[i])
×
NEW
215
                }
×
216
        }
217
}
218

NEW
219
func (b *prefBoundList[T]) replaceProvider(p fyne.Preferences) {
×
NEW
220
        b.get, b.set = b.setLookup(p)
×
NEW
221
}
×
222

223
type internalPrefs = interface{ WriteValues(func(map[string]any)) }
224

225
func bindPreferenceListComparable[T bool | float64 | int | string](key string, p fyne.Preferences,
NEW
226
        setLookup preferenceLookupSetter[[]T]) *prefBoundList[T] {
×
NEW
227
        if found, ok := lookupExistingListBinding[T](key, p); ok {
×
NEW
228
                return found
×
NEW
229
        }
×
230

NEW
231
        listen := &prefBoundList[T]{key: key, setLookup: setLookup}
×
NEW
232
        listen.replaceProvider(p)
×
NEW
233

×
NEW
234
        items := listen.get(listen.key)
×
NEW
235
        listen.boundList = *bindList(nil, func(t1, t2 T) bool { return t1 == t2 })
×
236

NEW
237
        listen.boundList.AddListener(NewDataListener(func() {
×
NEW
238
                cached := *listen.val
×
NEW
239
                replaced := listen.get(listen.key)
×
NEW
240
                if len(cached) == len(replaced) {
×
NEW
241
                        return
×
NEW
242
                }
×
243

NEW
244
                listen.set(listen.key, *listen.val)
×
NEW
245
                listen.trigger()
×
246
        }))
247

NEW
248
        listen.boundList.parentListener = func(index int) {
×
NEW
249
                listen.set(listen.key, *listen.val)
×
NEW
250

×
NEW
251
                // the child changes are not seen on the write end so force it
×
NEW
252
                if prefs, ok := p.(internalPrefs); ok {
×
NEW
253
                        prefs.WriteValues(func(map[string]any) {})
×
254
                }
255
        }
NEW
256
        listen.boundList.Set(items)
×
NEW
257

×
NEW
258
        binds := prefBinds.ensurePreferencesAttached(p)
×
NEW
259
        binds.Store(key, listen)
×
NEW
260
        return listen
×
261
}
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