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

fyne-io / fyne / 27502714592

14 Jun 2026 07:16AM UTC coverage: 60.153% (-0.03%) from 60.178%
27502714592

Pull #6361

github

toaster
[.github/workflows] remove linters which are already part of golangci-lint
Pull Request #6361: use golangci-lint to run multiple linters

84 of 143 new or added lines in 38 files covered. (58.74%)

3 existing lines in 2 files now uncovered.

26803 of 44558 relevant lines covered (60.15%)

674.84 hits per line

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

42.66
/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(
×
31
                key, p,
×
32
                func(p fyne.Preferences) (func(string) []bool, func(string, []bool)) {
×
33
                        return p.BoolList, p.SetBoolList
×
34
                },
×
35
        )
36
}
37

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

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

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

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

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

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

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

115
        listen := &prefBoundBase[T]{key: key, setLookup: setLookup}
6✔
116
        listen.replaceProvider(p)
6✔
117
        binds := prefBinds.ensurePreferencesAttached(p)
6✔
118
        binds.Store(key, listen)
6✔
119
        return listen
6✔
120
}
121

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

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

135
        return nil, false
4✔
136
}
137

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

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

151
        return nil, false
×
152
}
153

154
type prefBoundBase[T bool | float64 | int | string] struct {
155
        base
156
        key string
157

158
        get       func(string) T
159
        set       func(string, T)
160
        setLookup preferenceLookupSetter[T]
161
        cache     atomic.Pointer[T]
162
}
163

164
func (b *prefBoundBase[T]) Get() (T, error) {
12✔
165
        cache := b.get(b.key)
12✔
166
        b.cache.Store(&cache)
12✔
167
        return cache, nil
12✔
168
}
12✔
169

170
func (b *prefBoundBase[T]) Set(v T) error {
6✔
171
        b.set(b.key, v)
6✔
172

6✔
173
        b.lock.RLock()
6✔
174
        defer b.lock.RUnlock()
6✔
175
        b.trigger()
6✔
176
        return nil
6✔
177
}
6✔
178

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

187
func (b *prefBoundBase[T]) replaceProvider(p fyne.Preferences) {
21✔
188
        b.get, b.set = b.setLookup(p)
21✔
189
}
21✔
190

191
type prefBoundList[T bool | float64 | int | string] struct {
192
        boundList[T]
193
        key string
194

195
        get       func(string) []T
196
        set       func(string, []T)
197
        setLookup preferenceLookupSetter[[]T]
198
}
199

200
func (b *prefBoundList[T]) checkForChange() {
×
201
        val := *b.val
×
202
        updated := b.get(b.key)
×
203
        if val == nil || len(updated) != len(val) {
×
NEW
204
                // #Set’s error comes via #doReload from *boundExternalListItem#setIfChanged or *boundListItem#doSet
×
NEW
205
                // which both never return an error.
×
NEW
206
                _ = b.Set(updated)
×
207
                return
×
208
        }
×
209

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

216
                if !b.comparator(v, updated[i]) {
×
217
                        _ = b.items[i].(Item[T]).Set(updated[i])
×
218
                }
×
219
        }
220
}
221

222
func (b *prefBoundList[T]) replaceProvider(p fyne.Preferences) {
×
223
        b.get, b.set = b.setLookup(p)
×
224
}
×
225

226
type internalPrefs = interface{ WriteValues(func(map[string]any)) }
227

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

235
        listen := &prefBoundList[T]{key: key, setLookup: setLookup}
×
236
        listen.replaceProvider(p)
×
237

×
238
        items := listen.get(listen.key)
×
239
        listen.boundList = *bindList(nil, func(t1, t2 T) bool { return t1 == t2 })
×
240

NEW
241
        listen.AddListener(NewDataListener(func() {
×
242
                cached := *listen.val
×
243
                replaced := listen.get(listen.key)
×
244
                if len(cached) == len(replaced) {
×
245
                        return
×
246
                }
×
247

248
                listen.set(listen.key, *listen.val)
×
249
                listen.trigger()
×
250
        }))
251

NEW
252
        listen.parentListener = func(index int) {
×
253
                listen.set(listen.key, *listen.val)
×
254

×
255
                // the child changes are not seen on the write end so force it
×
256
                if prefs, ok := p.(internalPrefs); ok {
×
257
                        prefs.WriteValues(func(map[string]any) {})
×
258
                }
259
        }
260
        // #Set’s error comes via #doReload from *boundExternalListItem#setIfChanged or *boundListItem#doSet
261
        // which both never return an error.
NEW
262
        _ = listen.Set(items)
×
263

×
264
        binds := prefBinds.ensurePreferencesAttached(p)
×
265
        binds.Store(key, listen)
×
266
        return listen
×
267
}
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