Skip to content

Commit 739f0e6

Browse files
authored
Merge pull request #5431 from dweymouth/settings-callback
Add new callback-based settings listener API
2 parents 221abeb + 4f3ca6a commit 739f0e6

File tree

12 files changed

+84
-63
lines changed

12 files changed

+84
-63
lines changed

app/app_windows.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -90,5 +90,7 @@ func runScript(name, script string) {
9090
}
9191

9292
func watchTheme(s *settings) {
93-
go internalapp.WatchTheme(s.setupTheme)
93+
go internalapp.WatchTheme(func() {
94+
fyne.Do(s.setupTheme)
95+
})
9496
}

app/app_xdg.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,13 @@ func watchTheme(s *settings) {
118118
// Theme lookup hangs on some desktops. Update theme variant cache from within goroutine.
119119
themeVariant := findFreedesktopColorScheme()
120120
internalapp.CurrentVariant.Store(uint64(themeVariant))
121-
s.applyVariant(themeVariant)
121+
fyne.Do(func() { s.applyVariant(themeVariant) })
122122

123123
portalSettings.OnSignalSettingChanged(func(changed portalSettings.Changed) {
124124
if changed.Namespace == appearance.Namespace && changed.Key == "color-scheme" {
125125
themeVariant := colorSchemeToThemeVariant(appearance.ColorScheme(changed.Value.(uint32)))
126126
internalapp.CurrentVariant.Store(uint64(themeVariant))
127-
s.applyVariant(themeVariant)
127+
fyne.Do(func() { s.applyVariant(themeVariant) })
128128
}
129129
})
130130
}()

app/cloud_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ func TestFyneApp_transitionCloud(t *testing.T) {
4545
a.Preferences().AddChangeListener(func() {
4646
preferenceChanged = true
4747
})
48-
a.Settings().AddChangeListener(settingsChan)
48+
a.Settings().AddListener(func(s fyne.Settings) {
49+
go func() { settingsChan <- s }()
50+
})
4951

5052
done := make(chan struct{})
5153
go func() {

app/settings.go

+9-14
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"bytes"
55
"os"
66
"path/filepath"
7-
"sync"
87

98
"fyne.io/fyne/v2"
109
"fyne.io/fyne/v2/internal/app"
@@ -33,11 +32,11 @@ func (sc *SettingsSchema) StoragePath() string {
3332
var _ fyne.Settings = (*settings)(nil)
3433

3534
type settings struct {
36-
propertyLock sync.RWMutex
3735
theme fyne.Theme
3836
themeSpecified bool
3937
variant fyne.ThemeVariant
4038

39+
listeners []func(fyne.Settings)
4140
changeListeners async.Map[chan fyne.Settings, bool]
4241
watcher any // normally *fsnotify.Watcher or nil - avoid import in this file
4342

@@ -49,8 +48,6 @@ func (s *settings) BuildType() fyne.BuildType {
4948
}
5049

5150
func (s *settings) PrimaryColor() string {
52-
s.propertyLock.RLock()
53-
defer s.propertyLock.RUnlock()
5451
return s.schema.PrimaryColor
5552
}
5653

@@ -59,8 +56,6 @@ func (s *settings) PrimaryColor() string {
5956
//
6057
// Deprecated: Use container.NewThemeOverride to change the appearance of part of your application.
6158
func (s *settings) OverrideTheme(theme fyne.Theme, name string) {
62-
s.propertyLock.Lock()
63-
defer s.propertyLock.Unlock()
6459
s.schema.PrimaryColor = name
6560
s.theme = theme
6661
}
@@ -70,8 +65,6 @@ func (s *settings) Theme() fyne.Theme {
7065
fyne.LogError("Attempt to access current Fyne theme when no app is started", nil)
7166
return nil
7267
}
73-
s.propertyLock.RLock()
74-
defer s.propertyLock.RUnlock()
7568
return s.theme
7669
}
7770

@@ -89,23 +82,17 @@ func (s *settings) ThemeVariant() fyne.ThemeVariant {
8982
}
9083

9184
func (s *settings) applyTheme(theme fyne.Theme, variant fyne.ThemeVariant) {
92-
s.propertyLock.Lock()
93-
defer s.propertyLock.Unlock()
9485
s.variant = variant
9586
s.theme = theme
9687
s.apply()
9788
}
9889

9990
func (s *settings) applyVariant(variant fyne.ThemeVariant) {
100-
s.propertyLock.Lock()
101-
defer s.propertyLock.Unlock()
10291
s.variant = variant
10392
s.apply()
10493
}
10594

10695
func (s *settings) Scale() float32 {
107-
s.propertyLock.RLock()
108-
defer s.propertyLock.RUnlock()
10996
if s.schema.Scale < 0.0 {
11097
return 1.0 // catching any really old data still using the `-1` value for "auto" scale
11198
}
@@ -116,6 +103,10 @@ func (s *settings) AddChangeListener(listener chan fyne.Settings) {
116103
s.changeListeners.Store(listener, true) // the boolean is just a dummy value here.
117104
}
118105

106+
func (s *settings) AddListener(listener func(fyne.Settings)) {
107+
s.listeners = append(s.listeners, listener)
108+
}
109+
119110
func (s *settings) apply() {
120111
s.changeListeners.Range(func(listener chan fyne.Settings, _ bool) bool {
121112
select {
@@ -126,6 +117,10 @@ func (s *settings) apply() {
126117
}
127118
return true
128119
})
120+
121+
for _, l := range s.listeners {
122+
l(s)
123+
}
129124
}
130125

131126
func (s *settings) fileChanged() {

cmd/fyne_demo/tutorials/welcome.go

+9-15
Original file line numberDiff line numberDiff line change
@@ -59,21 +59,15 @@ func welcomeScreen(_ fyne.Window) fyne.CanvasObject {
5959
slideBG := container.New(underlayer, underlay)
6060
footerBG := canvas.NewRectangle(shadowColor)
6161

62-
listen := make(chan fyne.Settings)
63-
fyne.CurrentApp().Settings().AddChangeListener(listen)
64-
go func() {
65-
for range listen {
66-
fyne.Do(func() {
67-
bgColor = withAlpha(theme.Color(theme.ColorNameBackground), 0xe0)
68-
bg.FillColor = bgColor
69-
bg.Refresh()
70-
71-
shadowColor = withAlpha(theme.Color(theme.ColorNameBackground), 0x33)
72-
footerBG.FillColor = bgColor
73-
footer.Refresh()
74-
})
75-
}
76-
}()
62+
fyne.CurrentApp().Settings().AddListener(func(fyne.Settings) {
63+
bgColor = withAlpha(theme.Color(theme.ColorNameBackground), 0xe0)
64+
bg.FillColor = bgColor
65+
bg.Refresh()
66+
67+
shadowColor = withAlpha(theme.Color(theme.ColorNameBackground), 0x33)
68+
footerBG.FillColor = bgColor
69+
footer.Refresh()
70+
})
7771

7872
underlay.Resize(fyne.NewSize(1024, 1024))
7973
scroll.OnScrolled = func(p fyne.Position) {

internal/driver/glfw/canvas_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,9 @@ func TestGlCanvas_Scale(t *testing.T) {
539539
}
540540

541541
func TestGlCanvas_SetContent(t *testing.T) {
542-
fyne.CurrentApp().Settings().SetTheme(internalTest.DarkTheme(theme.DefaultTheme()))
542+
runOnMain(func() {
543+
fyne.CurrentApp().Settings().SetTheme(internalTest.DarkTheme(theme.DefaultTheme()))
544+
})
543545
var menuHeight float32
544546
if build.HasNativeMenu {
545547
menuHeight = 0

internal/driver/glfw/loop.go

+14-16
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,24 @@ func (d *gLDriver) runGL() {
116116
if d.trayStart != nil {
117117
d.trayStart()
118118
}
119+
120+
fyne.CurrentApp().Settings().AddListener(func(set fyne.Settings) {
121+
painter.ClearFontCache()
122+
cache.ResetThemeCaches()
123+
app.ApplySettingsWithCallback(set, fyne.CurrentApp(), func(w fyne.Window) {
124+
c, ok := w.Canvas().(*glCanvas)
125+
if !ok {
126+
return
127+
}
128+
c.applyThemeOutOfTreeObjects()
129+
c.reloadScale()
130+
})
131+
})
132+
119133
if f := fyne.CurrentApp().Lifecycle().(*app.Lifecycle).OnStarted(); f != nil {
120134
f()
121135
}
122136

123-
settingsChange := make(chan fyne.Settings)
124-
fyne.CurrentApp().Settings().AddChangeListener(settingsChange)
125-
126137
eventTick := time.NewTicker(time.Second / 60)
127138
for {
128139
select {
@@ -166,23 +177,10 @@ func (d *gLDriver) runGL() {
166177
view.SetSize(w.shouldWidth, w.shouldHeight)
167178
}
168179
}
169-
170180
}
171181

172182
d.animation.TickAnimations()
173183
d.drawSingleFrame()
174-
case set := <-settingsChange:
175-
painter.ClearFontCache()
176-
cache.ResetThemeCaches()
177-
app.ApplySettingsWithCallback(set, fyne.CurrentApp(), func(w fyne.Window) {
178-
c, ok := w.Canvas().(*glCanvas)
179-
if !ok {
180-
return
181-
}
182-
c.applyThemeOutOfTreeObjects()
183-
c.reloadScale()
184-
})
185-
186184
}
187185
}
188186
}

internal/driver/mobile/driver.go

+13-12
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,20 @@ func (d *driver) Run() {
173173
app.Main(func(a app.App) {
174174
async.SetMainGoroutine()
175175
d.app = a
176-
settingsChange := make(chan fyne.Settings)
177176
d.queuedFuncs = async.NewUnboundedChan[func()]()
178-
fyne.CurrentApp().Settings().AddChangeListener(settingsChange)
177+
178+
fyne.CurrentApp().Settings().AddListener(func(s fyne.Settings) {
179+
painter.ClearFontCache()
180+
cache.ResetThemeCaches()
181+
intapp.ApplySettingsWithCallback(s, fyne.CurrentApp(), func(w fyne.Window) {
182+
c, ok := w.Canvas().(*canvas)
183+
if !ok {
184+
return
185+
}
186+
c.applyThemeOutOfTreeObjects()
187+
})
188+
})
189+
179190
draw := time.NewTicker(time.Second / 60)
180191
defer func() {
181192
l := fyne.CurrentApp().Lifecycle().(*intapp.Lifecycle)
@@ -196,16 +207,6 @@ func (d *driver) Run() {
196207
select {
197208
case <-draw.C:
198209
d.sendPaintEvent()
199-
case set := <-settingsChange:
200-
painter.ClearFontCache()
201-
cache.ResetThemeCaches()
202-
intapp.ApplySettingsWithCallback(set, fyne.CurrentApp(), func(w fyne.Window) {
203-
c, ok := w.Canvas().(*canvas)
204-
if !ok {
205-
return
206-
}
207-
c.applyThemeOutOfTreeObjects()
208-
})
209210
case fn := <-d.queuedFuncs.Out():
210211
fn()
211212
case e, ok := <-a.Events():

settings.go

+10
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,17 @@ type Settings interface {
2727
// Since: 1.4
2828
PrimaryColor() string
2929

30+
// AddChangeListener subscribes to settings change events over a channel.
31+
//
32+
// Deprecated: Use AddListener instead, which uses a callback-based API
33+
// with the callback guaranteed to be invoked on the app goroutine.
3034
AddChangeListener(chan Settings)
35+
36+
// AddListener registers a callback that is invoked whenever the settings change.
37+
//
38+
// Since: 2.6
39+
AddListener(func(Settings))
40+
3141
BuildType() BuildType
3242

3343
ShowAnimations() bool

test/app.go

+12
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ type testSettings struct {
182182
scale float32
183183
theme fyne.Theme
184184

185+
listeners []func(fyne.Settings)
185186
changeListeners []chan fyne.Settings
186187
propertyLock sync.RWMutex
187188
app *app
@@ -193,6 +194,12 @@ func (s *testSettings) AddChangeListener(listener chan fyne.Settings) {
193194
s.changeListeners = append(s.changeListeners, listener)
194195
}
195196

197+
func (s *testSettings) AddListener(listener func(fyne.Settings)) {
198+
s.propertyLock.Lock()
199+
defer s.propertyLock.Unlock()
200+
s.listeners = append(s.listeners, listener)
201+
}
202+
196203
func (s *testSettings) BuildType() fyne.BuildType {
197204
return fyne.BuildStandard
198205
}
@@ -241,6 +248,7 @@ func (s *testSettings) Scale() float32 {
241248
func (s *testSettings) apply() {
242249
s.propertyLock.RLock()
243250
listeners := s.changeListeners
251+
listenersFns := s.listeners
244252
s.propertyLock.RUnlock()
245253

246254
for _, listener := range listeners {
@@ -253,6 +261,10 @@ func (s *testSettings) apply() {
253261
cache.ResetThemeCaches()
254262
intapp.ApplySettings(s, s.app)
255263
s.app.propertyLock.Unlock()
264+
265+
for _, l := range listenersFns {
266+
l(s)
267+
}
256268
}, false)
257269

258270
s.app.propertyLock.Lock()

test/app_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ func TestFyneApp_transitionCloud(t *testing.T) {
2323
a.Preferences().AddChangeListener(func() {
2424
preferenceChanged = true
2525
})
26-
a.Settings().AddChangeListener(settingsChan)
26+
a.Settings().AddListener(func(s fyne.Settings) {
27+
go func() { settingsChan <- s }()
28+
})
2729
a.SetCloudProvider(p)
2830

2931
<-settingsChan // settings were updated

theme/themedtestapp_test.go

+3
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ func (t *themedApp) ShowAnimations() bool {
107107
func (t *themedApp) AddChangeListener(chan fyne.Settings) {
108108
}
109109

110+
func (t *themedApp) AddListener(func(fyne.Settings)) {
111+
}
112+
110113
func (t *themedApp) Clipboard() fyne.Clipboard {
111114
return nil
112115
}

0 commit comments

Comments
 (0)