/
settings.go
136 lines (112 loc) · 2.64 KB
/
settings.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package app
import (
"encoding/json"
"io"
"os"
"path/filepath"
"sync"
"fyne.io/fyne"
"fyne.io/fyne/theme"
)
// SettingsSchema is used for loading and storing global settings
type SettingsSchema struct {
// these items are used for global settings load
ThemeName string `json:"theme"`
Scale float32 `json:"scale"`
}
// StoragePath returns the location of the settings storage
func (sc *SettingsSchema) StoragePath() string {
return filepath.Join(rootConfigDir(), "settings.json")
}
// Declare conformity with Settings interface
var _ fyne.Settings = (*settings)(nil)
type settings struct {
propertyLock sync.RWMutex
theme fyne.Theme
themeSpecified bool
listenerLock sync.Mutex
changeListeners []chan fyne.Settings
watcher interface{} // normally *fsnotify.Watcher or nil - avoid import in this file
schema SettingsSchema
}
func (s *settings) Theme() fyne.Theme {
s.propertyLock.RLock()
defer s.propertyLock.RUnlock()
return s.theme
}
func (s *settings) SetTheme(theme fyne.Theme) {
s.themeSpecified = true
s.applyTheme(theme)
}
func (s *settings) applyTheme(theme fyne.Theme) {
s.propertyLock.Lock()
defer s.propertyLock.Unlock()
s.theme = theme
s.apply()
}
func (s *settings) Scale() float32 {
s.propertyLock.RLock()
defer s.propertyLock.RUnlock()
return s.schema.Scale
}
func (s *settings) AddChangeListener(listener chan fyne.Settings) {
s.listenerLock.Lock()
defer s.listenerLock.Unlock()
s.changeListeners = append(s.changeListeners, listener)
}
func (s *settings) apply() {
s.listenerLock.Lock()
defer s.listenerLock.Unlock()
for _, listener := range s.changeListeners {
select {
case listener <- s:
default:
l := listener
go func() { l <- s }()
}
}
}
func (s *settings) load() {
err := s.loadFromFile(s.schema.StoragePath())
if err != nil && err != io.EOF { // we can get an EOF in windows settings writes
fyne.LogError("Settings load error:", err)
}
s.setupTheme()
}
func (s *settings) loadFromFile(path string) error {
file, err := os.Open(path) // #nosec
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
decode := json.NewDecoder(file)
return decode.Decode(&s.schema)
}
func (s *settings) fileChanged() {
s.load()
s.apply()
}
func (s *settings) setupTheme() {
if s.themeSpecified {
return
}
name := s.schema.ThemeName
if env := os.Getenv("FYNE_THEME"); env != "" {
s.themeSpecified = true
name = env
}
if name == "light" {
s.applyTheme(theme.LightTheme())
} else if name == "dark" {
s.applyTheme(theme.DarkTheme())
} else {
s.applyTheme(defaultTheme())
}
}
func loadSettings() *settings {
s := &settings{}
s.load()
return s
}