settings.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. package app
  2. import (
  3. "bytes"
  4. "os"
  5. "path/filepath"
  6. "sync"
  7. "fyne.io/fyne/v2"
  8. "fyne.io/fyne/v2/theme"
  9. )
  10. // SettingsSchema is used for loading and storing global settings
  11. type SettingsSchema struct {
  12. // these items are used for global settings load
  13. ThemeName string `json:"theme"`
  14. Scale float32 `json:"scale"`
  15. PrimaryColor string `json:"primary_color"`
  16. CloudName string `json:"cloud_name"`
  17. CloudConfig string `json:"cloud_config"`
  18. }
  19. // StoragePath returns the location of the settings storage
  20. func (sc *SettingsSchema) StoragePath() string {
  21. return filepath.Join(rootConfigDir(), "settings.json")
  22. }
  23. // Declare conformity with Settings interface
  24. var _ fyne.Settings = (*settings)(nil)
  25. type settings struct {
  26. propertyLock sync.RWMutex
  27. theme fyne.Theme
  28. themeSpecified bool
  29. variant fyne.ThemeVariant
  30. changeListeners sync.Map // map[chan fyne.Settings]bool
  31. watcher interface{} // normally *fsnotify.Watcher or nil - avoid import in this file
  32. schema SettingsSchema
  33. }
  34. func (s *settings) BuildType() fyne.BuildType {
  35. return buildMode
  36. }
  37. func (s *settings) PrimaryColor() string {
  38. s.propertyLock.RLock()
  39. defer s.propertyLock.RUnlock()
  40. return s.schema.PrimaryColor
  41. }
  42. // OverrideTheme allows the settings app to temporarily preview different theme details.
  43. // Please make sure that you remember the original settings and call this again to revert the change.
  44. func (s *settings) OverrideTheme(theme fyne.Theme, name string) {
  45. s.propertyLock.Lock()
  46. defer s.propertyLock.Unlock()
  47. s.schema.PrimaryColor = name
  48. s.theme = theme
  49. }
  50. func (s *settings) Theme() fyne.Theme {
  51. s.propertyLock.RLock()
  52. defer s.propertyLock.RUnlock()
  53. return s.theme
  54. }
  55. func (s *settings) SetTheme(theme fyne.Theme) {
  56. s.themeSpecified = true
  57. s.applyTheme(theme, s.variant)
  58. }
  59. func (s *settings) ThemeVariant() fyne.ThemeVariant {
  60. return s.variant
  61. }
  62. func (s *settings) applyTheme(theme fyne.Theme, variant fyne.ThemeVariant) {
  63. s.propertyLock.Lock()
  64. defer s.propertyLock.Unlock()
  65. s.variant = variant
  66. s.theme = theme
  67. s.apply()
  68. }
  69. func (s *settings) Scale() float32 {
  70. s.propertyLock.RLock()
  71. defer s.propertyLock.RUnlock()
  72. if s.schema.Scale < 0.0 {
  73. return 1.0 // catching any really old data still using the `-1` value for "auto" scale
  74. }
  75. return s.schema.Scale
  76. }
  77. func (s *settings) AddChangeListener(listener chan fyne.Settings) {
  78. s.changeListeners.Store(listener, true) // the boolean is just a dummy value here.
  79. }
  80. func (s *settings) apply() {
  81. s.changeListeners.Range(func(key, _ interface{}) bool {
  82. listener := key.(chan fyne.Settings)
  83. select {
  84. case listener <- s:
  85. default:
  86. l := listener
  87. go func() { l <- s }()
  88. }
  89. return true
  90. })
  91. }
  92. func (s *settings) fileChanged() {
  93. s.load()
  94. s.apply()
  95. }
  96. func (s *settings) loadSystemTheme() fyne.Theme {
  97. path := filepath.Join(rootConfigDir(), "theme.json")
  98. data, err := fyne.LoadResourceFromPath(path)
  99. if err != nil {
  100. if !os.IsNotExist(err) {
  101. fyne.LogError("Failed to load user theme file: "+path, err)
  102. }
  103. return theme.DefaultTheme()
  104. }
  105. if data != nil && data.Content() != nil {
  106. th, err := theme.FromJSONReader(bytes.NewReader(data.Content()))
  107. if err == nil {
  108. return th
  109. }
  110. fyne.LogError("Failed to parse user theme file: "+path, err)
  111. }
  112. return theme.DefaultTheme()
  113. }
  114. func (s *settings) setupTheme() {
  115. name := s.schema.ThemeName
  116. if env := os.Getenv("FYNE_THEME"); env != "" {
  117. name = env
  118. }
  119. variant := defaultVariant()
  120. effectiveTheme := s.theme
  121. if !s.themeSpecified {
  122. effectiveTheme = s.loadSystemTheme()
  123. }
  124. switch name {
  125. case "light":
  126. variant = theme.VariantLight
  127. case "dark":
  128. variant = theme.VariantDark
  129. }
  130. s.applyTheme(effectiveTheme, variant)
  131. }
  132. func loadSettings() *settings {
  133. s := &settings{}
  134. s.load()
  135. return s
  136. }