preferences.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. package internal
  2. import (
  3. "reflect"
  4. "sync"
  5. "fyne.io/fyne/v2"
  6. )
  7. // InMemoryPreferences provides an implementation of the fyne.Preferences API that is stored in memory.
  8. type InMemoryPreferences struct {
  9. values map[string]interface{}
  10. lock sync.RWMutex
  11. changeListeners []func()
  12. }
  13. // Declare conformity with Preferences interface
  14. var _ fyne.Preferences = (*InMemoryPreferences)(nil)
  15. // AddChangeListener allows code to be notified when some preferences change. This will fire on any update.
  16. // The passed 'listener' should not try to write values.
  17. func (p *InMemoryPreferences) AddChangeListener(listener func()) {
  18. p.lock.Lock()
  19. defer p.lock.Unlock()
  20. p.changeListeners = append(p.changeListeners, listener)
  21. }
  22. // Bool looks up a boolean value for the key
  23. func (p *InMemoryPreferences) Bool(key string) bool {
  24. return p.BoolWithFallback(key, false)
  25. }
  26. func (p *InMemoryPreferences) BoolList(key string) []bool {
  27. return p.BoolListWithFallback(key, []bool{})
  28. }
  29. func (p *InMemoryPreferences) BoolListWithFallback(key string, fallback []bool) []bool {
  30. value, ok := p.get(key)
  31. if !ok {
  32. return fallback
  33. }
  34. valb, ok := value.([]bool)
  35. if !ok {
  36. return fallback
  37. }
  38. return valb
  39. }
  40. // BoolWithFallback looks up a boolean value and returns the given fallback if not found
  41. func (p *InMemoryPreferences) BoolWithFallback(key string, fallback bool) bool {
  42. value, ok := p.get(key)
  43. if !ok {
  44. return fallback
  45. }
  46. valb, ok := value.(bool)
  47. if !ok {
  48. return fallback
  49. }
  50. return valb
  51. }
  52. // ChangeListeners returns the list of listeners registered for this set of preferences.
  53. func (p *InMemoryPreferences) ChangeListeners() []func() {
  54. return p.changeListeners
  55. }
  56. // Float looks up a float64 value for the key
  57. func (p *InMemoryPreferences) Float(key string) float64 {
  58. return p.FloatWithFallback(key, 0.0)
  59. }
  60. func (p *InMemoryPreferences) FloatList(key string) []float64 {
  61. return p.FloatListWithFallback(key, []float64{})
  62. }
  63. func (p *InMemoryPreferences) FloatListWithFallback(key string, fallback []float64) []float64 {
  64. value, ok := p.get(key)
  65. if !ok {
  66. return fallback
  67. }
  68. valf, ok := value.([]float64)
  69. if ok {
  70. return valf
  71. }
  72. vali, ok := value.([]int)
  73. if ok {
  74. flts := make([]float64, len(vali))
  75. for i, f := range vali {
  76. flts[i] = float64(f)
  77. }
  78. return flts
  79. }
  80. return fallback
  81. }
  82. // FloatWithFallback looks up a float64 value and returns the given fallback if not found
  83. func (p *InMemoryPreferences) FloatWithFallback(key string, fallback float64) float64 {
  84. value, ok := p.get(key)
  85. if !ok {
  86. return fallback
  87. }
  88. valf, ok := value.(float64)
  89. if ok {
  90. return valf
  91. }
  92. vali, ok := value.(int)
  93. if ok {
  94. return float64(vali)
  95. }
  96. return fallback
  97. }
  98. // Int looks up an integer value for the key
  99. func (p *InMemoryPreferences) Int(key string) int {
  100. return p.IntWithFallback(key, 0)
  101. }
  102. func (p *InMemoryPreferences) IntList(key string) []int {
  103. return p.IntListWithFallback(key, []int{})
  104. }
  105. func (p *InMemoryPreferences) IntListWithFallback(key string, fallback []int) []int {
  106. value, ok := p.get(key)
  107. if !ok {
  108. return fallback
  109. }
  110. vali, ok := value.([]int)
  111. if ok {
  112. return vali
  113. }
  114. // integers can be de-serialised as floats, so support both
  115. valf, ok := value.([]float64)
  116. if ok {
  117. ints := make([]int, len(valf))
  118. for i, f := range valf {
  119. ints[i] = int(f)
  120. }
  121. return ints
  122. }
  123. return fallback
  124. }
  125. // IntWithFallback looks up an integer value and returns the given fallback if not found
  126. func (p *InMemoryPreferences) IntWithFallback(key string, fallback int) int {
  127. value, ok := p.get(key)
  128. if !ok {
  129. return fallback
  130. }
  131. vali, ok := value.(int)
  132. if ok {
  133. return vali
  134. }
  135. // integers can be de-serialised as floats, so support both
  136. valf, ok := value.(float64)
  137. if !ok {
  138. return fallback
  139. }
  140. return int(valf)
  141. }
  142. // ReadValues provides read access to the underlying value map - for internal use only...
  143. // You should not retain a reference to the map nor write to the values in the callback function
  144. func (p *InMemoryPreferences) ReadValues(fn func(map[string]interface{})) {
  145. p.lock.RLock()
  146. fn(p.values)
  147. p.lock.RUnlock()
  148. }
  149. // RemoveValue deletes a value on the given key
  150. func (p *InMemoryPreferences) RemoveValue(key string) {
  151. p.remove(key)
  152. }
  153. // SetBool saves a boolean value for the given key
  154. func (p *InMemoryPreferences) SetBool(key string, value bool) {
  155. p.set(key, value)
  156. }
  157. func (p *InMemoryPreferences) SetBoolList(key string, value []bool) {
  158. p.set(key, value)
  159. }
  160. // SetFloat saves a float64 value for the given key
  161. func (p *InMemoryPreferences) SetFloat(key string, value float64) {
  162. p.set(key, value)
  163. }
  164. func (p *InMemoryPreferences) SetFloatList(key string, value []float64) {
  165. p.set(key, value)
  166. }
  167. // SetInt saves an integer value for the given key
  168. func (p *InMemoryPreferences) SetInt(key string, value int) {
  169. p.set(key, value)
  170. }
  171. func (p *InMemoryPreferences) SetIntList(key string, value []int) {
  172. p.set(key, value)
  173. }
  174. // SetString saves a string value for the given key
  175. func (p *InMemoryPreferences) SetString(key string, value string) {
  176. p.set(key, value)
  177. }
  178. func (p *InMemoryPreferences) SetStringList(key string, value []string) {
  179. p.set(key, value)
  180. }
  181. // String looks up a string value for the key
  182. func (p *InMemoryPreferences) String(key string) string {
  183. return p.StringWithFallback(key, "")
  184. }
  185. func (p *InMemoryPreferences) StringList(key string) []string {
  186. return p.StringListWithFallback(key, []string{})
  187. }
  188. func (p *InMemoryPreferences) StringListWithFallback(key string, fallback []string) []string {
  189. value, ok := p.get(key)
  190. if !ok {
  191. return fallback
  192. }
  193. vals, ok := value.([]string)
  194. if !ok {
  195. return fallback
  196. }
  197. return vals
  198. }
  199. // StringWithFallback looks up a string value and returns the given fallback if not found
  200. func (p *InMemoryPreferences) StringWithFallback(key, fallback string) string {
  201. value, ok := p.get(key)
  202. if !ok {
  203. return fallback
  204. }
  205. vals, ok := value.(string)
  206. if !ok {
  207. return fallback
  208. }
  209. return vals
  210. }
  211. // WriteValues provides write access to the underlying value map - for internal use only...
  212. // You should not retain a reference to the map passed to the callback function
  213. func (p *InMemoryPreferences) WriteValues(fn func(map[string]interface{})) {
  214. p.lock.Lock()
  215. fn(p.values)
  216. p.lock.Unlock()
  217. p.fireChange()
  218. }
  219. // NewInMemoryPreferences creates a new preferences implementation stored in memory
  220. func NewInMemoryPreferences() *InMemoryPreferences {
  221. return &InMemoryPreferences{values: make(map[string]interface{})}
  222. }
  223. func (p *InMemoryPreferences) fireChange() {
  224. p.lock.RLock()
  225. listeners := p.changeListeners
  226. p.lock.RUnlock()
  227. var wg sync.WaitGroup
  228. for _, l := range listeners {
  229. wg.Add(1)
  230. go func(listener func()) {
  231. defer wg.Done()
  232. listener()
  233. }(l)
  234. }
  235. wg.Wait()
  236. }
  237. func (p *InMemoryPreferences) get(key string) (interface{}, bool) {
  238. p.lock.RLock()
  239. defer p.lock.RUnlock()
  240. v, err := p.values[key]
  241. return v, err
  242. }
  243. func (p *InMemoryPreferences) remove(key string) {
  244. p.lock.Lock()
  245. delete(p.values, key)
  246. p.lock.Unlock()
  247. p.fireChange()
  248. }
  249. func (p *InMemoryPreferences) set(key string, value interface{}) {
  250. p.lock.Lock()
  251. if reflect.TypeOf(value).Kind() == reflect.Slice {
  252. s := reflect.ValueOf(value)
  253. old := reflect.ValueOf(p.values[key])
  254. if p.values[key] != nil && s.Len() == old.Len() {
  255. changed := false
  256. for i := 0; i < s.Len(); i++ {
  257. if s.Index(i).Interface() != old.Index(i).Interface() {
  258. changed = true
  259. break
  260. }
  261. }
  262. if !changed {
  263. p.lock.Unlock()
  264. return
  265. }
  266. }
  267. } else {
  268. if stored, ok := p.values[key]; ok && stored == value {
  269. p.lock.Unlock()
  270. return
  271. }
  272. }
  273. p.values[key] = value
  274. p.lock.Unlock()
  275. p.fireChange()
  276. }