169 lines
3.9 KiB
Go
169 lines
3.9 KiB
Go
|
package app
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"sync"
|
||
|
|
||
|
"fyne.io/fyne/v2"
|
||
|
"fyne.io/fyne/v2/theme"
|
||
|
)
|
||
|
|
||
|
var noAnimations bool // set to true at compile time if no_animations tag is passed
|
||
|
|
||
|
// 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"`
|
||
|
PrimaryColor string `json:"primary_color"`
|
||
|
CloudName string `json:"cloud_name"`
|
||
|
CloudConfig string `json:"cloud_config"`
|
||
|
DisableAnimations bool `json:"no_animations"`
|
||
|
}
|
||
|
|
||
|
// 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
|
||
|
variant fyne.ThemeVariant
|
||
|
|
||
|
changeListeners sync.Map // map[chan fyne.Settings]bool
|
||
|
watcher interface{} // normally *fsnotify.Watcher or nil - avoid import in this file
|
||
|
|
||
|
schema SettingsSchema
|
||
|
}
|
||
|
|
||
|
func (s *settings) BuildType() fyne.BuildType {
|
||
|
return buildMode
|
||
|
}
|
||
|
|
||
|
func (s *settings) PrimaryColor() string {
|
||
|
s.propertyLock.RLock()
|
||
|
defer s.propertyLock.RUnlock()
|
||
|
return s.schema.PrimaryColor
|
||
|
}
|
||
|
|
||
|
// OverrideTheme allows the settings app to temporarily preview different theme details.
|
||
|
// Please make sure that you remember the original settings and call this again to revert the change.
|
||
|
func (s *settings) OverrideTheme(theme fyne.Theme, name string) {
|
||
|
s.propertyLock.Lock()
|
||
|
defer s.propertyLock.Unlock()
|
||
|
s.schema.PrimaryColor = name
|
||
|
s.theme = theme
|
||
|
}
|
||
|
|
||
|
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, s.variant)
|
||
|
}
|
||
|
|
||
|
func (s *settings) ShowAnimations() bool {
|
||
|
return !s.schema.DisableAnimations && !noAnimations
|
||
|
}
|
||
|
|
||
|
func (s *settings) ThemeVariant() fyne.ThemeVariant {
|
||
|
return s.variant
|
||
|
}
|
||
|
|
||
|
func (s *settings) applyTheme(theme fyne.Theme, variant fyne.ThemeVariant) {
|
||
|
s.propertyLock.Lock()
|
||
|
defer s.propertyLock.Unlock()
|
||
|
s.variant = variant
|
||
|
s.theme = theme
|
||
|
s.apply()
|
||
|
}
|
||
|
|
||
|
func (s *settings) Scale() float32 {
|
||
|
s.propertyLock.RLock()
|
||
|
defer s.propertyLock.RUnlock()
|
||
|
if s.schema.Scale < 0.0 {
|
||
|
return 1.0 // catching any really old data still using the `-1` value for "auto" scale
|
||
|
}
|
||
|
return s.schema.Scale
|
||
|
}
|
||
|
|
||
|
func (s *settings) AddChangeListener(listener chan fyne.Settings) {
|
||
|
s.changeListeners.Store(listener, true) // the boolean is just a dummy value here.
|
||
|
}
|
||
|
|
||
|
func (s *settings) apply() {
|
||
|
s.changeListeners.Range(func(key, _ interface{}) bool {
|
||
|
listener := key.(chan fyne.Settings)
|
||
|
select {
|
||
|
case listener <- s:
|
||
|
default:
|
||
|
l := listener
|
||
|
go func() { l <- s }()
|
||
|
}
|
||
|
return true
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func (s *settings) fileChanged() {
|
||
|
s.load()
|
||
|
s.apply()
|
||
|
}
|
||
|
|
||
|
func (s *settings) loadSystemTheme() fyne.Theme {
|
||
|
path := filepath.Join(rootConfigDir(), "theme.json")
|
||
|
data, err := fyne.LoadResourceFromPath(path)
|
||
|
if err != nil {
|
||
|
if !os.IsNotExist(err) {
|
||
|
fyne.LogError("Failed to load user theme file: "+path, err)
|
||
|
}
|
||
|
return theme.DefaultTheme()
|
||
|
}
|
||
|
if data != nil && data.Content() != nil {
|
||
|
th, err := theme.FromJSONReader(bytes.NewReader(data.Content()))
|
||
|
if err == nil {
|
||
|
return th
|
||
|
}
|
||
|
fyne.LogError("Failed to parse user theme file: "+path, err)
|
||
|
}
|
||
|
return theme.DefaultTheme()
|
||
|
}
|
||
|
|
||
|
func (s *settings) setupTheme() {
|
||
|
name := s.schema.ThemeName
|
||
|
if env := os.Getenv("FYNE_THEME"); env != "" {
|
||
|
name = env
|
||
|
}
|
||
|
|
||
|
variant := defaultVariant()
|
||
|
effectiveTheme := s.theme
|
||
|
if !s.themeSpecified {
|
||
|
effectiveTheme = s.loadSystemTheme()
|
||
|
}
|
||
|
switch name {
|
||
|
case "light":
|
||
|
variant = theme.VariantLight
|
||
|
case "dark":
|
||
|
variant = theme.VariantDark
|
||
|
}
|
||
|
|
||
|
s.applyTheme(effectiveTheme, variant)
|
||
|
}
|
||
|
|
||
|
func loadSettings() *settings {
|
||
|
s := &settings{}
|
||
|
s.load()
|
||
|
|
||
|
return s
|
||
|
}
|