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

fyne-io / fyne / 17516456766

06 Sep 2025 03:47PM UTC coverage: 62.317% (+0.02%) from 62.295%
17516456766

push

github

web-flow
Merge pull request #5933 from Jacalz/moduke-update

25338 of 40660 relevant lines covered (62.32%)

714.01 hits per line

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

44.53
/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
29
func BindPreferenceBoolList(key string, p fyne.Preferences) BoolList {
×
30
        return bindPreferenceListComparable(key, p,
×
31
                func(p fyne.Preferences) (func(string) []bool, func(string, []bool)) {
×
32
                        return p.BoolList, p.SetBoolList
×
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
52
func BindPreferenceFloatList(key string, p fyne.Preferences) FloatList {
×
53
        return bindPreferenceListComparable(key, p,
×
54
                func(p fyne.Preferences) (func(string) []float64, func(string, []float64)) {
×
55
                        return p.FloatList, p.SetFloatList
×
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
75
func BindPreferenceIntList(key string, p fyne.Preferences) IntList {
×
76
        return bindPreferenceListComparable(key, p,
×
77
                func(p fyne.Preferences) (func(string) []int, func(string, []int)) {
×
78
                        return p.IntList, p.SetIntList
×
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
98
func BindPreferenceStringList(key string, p fyne.Preferences) StringList {
×
99
        return bindPreferenceListComparable(key, p,
×
100
                func(p fyne.Preferences) (func(string) []string, func(string, []string)) {
×
101
                        return p.StringList, p.SetStringList
×
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

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

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

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

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

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

210
                if !b.comparator(v, updated[i]) {
×
211
                        _ = b.items[i].(Item[T]).Set(updated[i])
×
212
                }
×
213
        }
214
}
215

216
func (b *prefBoundList[T]) replaceProvider(p fyne.Preferences) {
×
217
        b.get, b.set = b.setLookup(p)
×
218
}
×
219

220
type internalPrefs = interface{ WriteValues(func(map[string]any)) }
221

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

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

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

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

242
                listen.set(listen.key, *listen.val)
×
243
                listen.trigger()
×
244
        }))
245

246
        listen.boundList.parentListener = func(index int) {
×
247
                listen.set(listen.key, *listen.val)
×
248

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

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