-
-
Notifications
You must be signed in to change notification settings - Fork 49
/
themefile.go
153 lines (112 loc) · 3.11 KB
/
themefile.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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package theme
import (
"encoding/hex"
"errors"
"fmt"
"image/color"
"io"
"os"
"slices"
"strings"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/theme"
"github.com/pelletier/go-toml/v2"
)
var validThemeVersions = []string{"0.1", "0.2"}
type ThemeFileHeader struct {
Name string
Version string
SupportsDark bool
SupportsLight bool
}
type ThemeFile struct {
SupersonicTheme ThemeFileHeader
DarkColors ThemeColors
LightColors ThemeColors
}
type ThemeColors struct {
// Supersonic-specific colors
// Background color of the header row in list views.
ListHeader string
// Background color for each page. The main color in the app.
PageBackground string
// Color of the background rectangle around the Album, Artist, Playlist page headers.
//
// Since: Supersonic theme file version 0.2
PageHeader string
// Fyne colors
Background string
Button string
DisabledButton string
Disabled string
Error string
Focus string
Foreground string
Hover string
// since Supersonic theme file version 0.2 (Fyne version 2.4)
Hyperlink string
InputBackground string
InputBorder string
MenuBackground string
OverlayBackground string
Placeholder string
Pressed string
Primary string
ScrollBar string
Selection string
Separator string
Shadow string
Success string
Warning string
}
func ReadThemeFile(filePath string) (*ThemeFile, error) {
f, err := os.Open(filePath)
if err != nil {
return nil, err
}
defer f.Close()
return DecodeThemeFile(f)
}
func DecodeThemeFile(reader io.Reader) (*ThemeFile, error) {
theme := &ThemeFile{}
if err := toml.NewDecoder(reader).Decode(theme); err != nil {
return nil, err
}
if theme.SupersonicTheme.Name == "" || !slices.Contains(validThemeVersions, theme.SupersonicTheme.Version) {
return nil, errors.New("invalid theme file name or version")
}
if !(theme.SupersonicTheme.SupportsDark || theme.SupersonicTheme.SupportsLight) {
return nil, errors.New("invalid theme file: must support one or both of light/dark")
}
updateThemeToLatestVersion(theme)
return theme, nil
}
func (t *ThemeFile) SupportsVariant(v fyne.ThemeVariant) bool {
if v == theme.VariantDark {
return t.SupersonicTheme.SupportsDark
}
return t.SupersonicTheme.SupportsLight
}
// Parses a CSS-style #RRGGBB or #RRGGBBAA string
func ColorStringToColor(colorStr string) (color.Color, error) {
if !strings.HasPrefix(colorStr, "#") || !slices.Contains([]int{7, 9}, len(colorStr)) {
return color.Black, errors.New("invalid color string")
}
colorBytes := make([]byte, 4)
n, err := hex.Decode(colorBytes, []byte(colorStr[1:]))
if err != nil {
return color.Black, fmt.Errorf("invalid color string: %s", err.Error())
}
if n == 3 {
colorBytes[3] = 255 // opaque alpha
}
return color.RGBA{R: colorBytes[0], G: colorBytes[1], B: colorBytes[2], A: colorBytes[3]}, nil
}
func updateThemeToLatestVersion(themeFile *ThemeFile) {
switch themeFile.SupersonicTheme.Version {
case "0.1":
// in version 0.1, hyperlinks were drawn with the Primary color
themeFile.DarkColors.Hyperlink = themeFile.DarkColors.Primary
themeFile.LightColors.Hyperlink = themeFile.LightColors.Primary
}
}