adam-gui/vendor/fyne.io/fyne/v2/theme/json.go

184 lines
4.0 KiB
Go
Raw Normal View History

2024-04-29 19:13:50 +02:00
package theme
import (
"encoding/hex"
"encoding/json"
"errors"
"image/color"
"io"
"strings"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/storage"
)
// FromJSON returns a Theme created from the given JSON metadata.
// Any values not present in the data will fall back to the default theme.
// If a parse error occurs it will be returned along with a default theme.
//
// Since: 2.2
func FromJSON(data string) (fyne.Theme, error) {
return FromJSONReader(strings.NewReader(data))
}
// FromJSONReader returns a Theme created from the given JSON metadata through the reader.
// Any values not present in the data will fall back to the default theme.
// If a parse error occurs it will be returned along with a default theme.
//
// Since: 2.2
func FromJSONReader(r io.Reader) (fyne.Theme, error) {
var th *schema
if err := json.NewDecoder(r).Decode(&th); err != nil {
return DefaultTheme(), err
}
return &jsonTheme{data: th, fallback: DefaultTheme()}, nil
}
type hexColor string
func (h hexColor) color() (color.Color, error) {
data := h
switch len([]rune(h)) {
case 8, 6:
case 9, 7: // remove # prefix
data = h[1:]
case 5: // remove # prefix, then double up
data = h[1:]
fallthrough
case 4: // could be rgba or #rgb
if data[0] == '#' {
v := []rune(data[1:])
data = hexColor([]rune{v[0], v[0], v[1], v[1], v[2], v[2]})
break
}
v := []rune(data)
data = hexColor([]rune{v[0], v[0], v[1], v[1], v[2], v[2], v[3], v[3]})
case 3:
v := []rune(h)
data = hexColor([]rune{v[0], v[0], v[1], v[1], v[2], v[2]})
default:
return color.Transparent, errors.New("invalid color format: " + string(h))
}
digits, err := hex.DecodeString(string(data))
if err != nil {
return nil, err
}
ret := &color.NRGBA{R: digits[0], G: digits[1], B: digits[2]}
if len(digits) == 4 {
ret.A = digits[3]
} else {
ret.A = 0xff
}
return ret, nil
}
type uriString string
func (u uriString) resource() fyne.Resource {
uri, err := storage.ParseURI(string(u))
if err != nil {
fyne.LogError("Failed to parse URI", err)
return nil
}
r, err := storage.LoadResourceFromURI(uri)
if err != nil {
fyne.LogError("Failed to load resource from URI", err)
return nil
}
return r
}
type schema struct {
Colors map[string]hexColor `json:"Colors,omitempty"`
DarkColors map[string]hexColor `json:"Colors-dark,omitempty"`
LightColors map[string]hexColor `json:"Colors-light,omitempty"`
Sizes map[string]float32 `json:"Sizes,omitempty"`
Fonts map[string]uriString `json:"Fonts,omitempty"`
Icons map[string]uriString `json:"Icons,omitempty"`
}
type jsonTheme struct {
data *schema
fallback fyne.Theme
}
func (t *jsonTheme) Color(name fyne.ThemeColorName, variant fyne.ThemeVariant) color.Color {
switch variant {
case VariantLight:
if val, ok := t.data.LightColors[string(name)]; ok {
c, err := val.color()
if err != nil {
fyne.LogError("Failed to parse color", err)
} else {
return c
}
}
case VariantDark:
if val, ok := t.data.DarkColors[string(name)]; ok {
c, err := val.color()
if err != nil {
fyne.LogError("Failed to parse color", err)
} else {
return c
}
}
}
if val, ok := t.data.Colors[string(name)]; ok {
c, err := val.color()
if err != nil {
fyne.LogError("Failed to parse color", err)
} else {
return c
}
}
return t.fallback.Color(name, variant)
}
func (t *jsonTheme) Font(style fyne.TextStyle) fyne.Resource {
if val, ok := t.data.Fonts[styleString(style)]; ok {
r := val.resource()
if r != nil {
return r
}
}
return t.fallback.Font(style)
}
func (t *jsonTheme) Icon(name fyne.ThemeIconName) fyne.Resource {
if val, ok := t.data.Icons[string(name)]; ok {
r := val.resource()
if r != nil {
return r
}
}
return t.fallback.Icon(name)
}
func (t *jsonTheme) Size(name fyne.ThemeSizeName) float32 {
if val, ok := t.data.Sizes[string(name)]; ok {
return val
}
return t.fallback.Size(name)
}
func styleString(s fyne.TextStyle) string {
if s.Bold {
if s.Italic {
return "boldItalic"
}
return "bold"
}
if s.Monospace {
return "monospace"
}
return "regular"
}