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

fyne-io / fyne / 25506274135

07 May 2026 03:43PM UTC coverage: 60.303% (-0.06%) from 60.359%
25506274135

push

github

web-flow
Merge pull request #6291 from andydotxyz/fix/darwinscreen

Fix issue with window placement and fullscreen for macOS 26

17 of 102 new or added lines in 23 files covered. (16.67%)

4 existing lines in 2 files now uncovered.

26301 of 43615 relevant lines covered (60.3%)

678.64 hits per line

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

43.26
/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 {
×
NEW
30
        return bindPreferenceListComparable(
×
NEW
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 {
×
NEW
54
        return bindPreferenceListComparable(
×
NEW
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 {
×
NEW
78
        return bindPreferenceListComparable(
×
NEW
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 {
×
NEW
102
        return bindPreferenceListComparable(
×
NEW
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) {
×
204
                b.Set(updated)
×
205
                return
×
206
        }
×
207

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

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

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

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

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

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

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

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

246
                listen.set(listen.key, *listen.val)
×
247
                listen.trigger()
×
248
        }))
249

250
        listen.boundList.parentListener = func(index int) {
×
251
                listen.set(listen.key, *listen.val)
×
252

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

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