374 lines
10 KiB
Go
374 lines
10 KiB
Go
// Package theme defines how a Fyne app should look when rendered.
|
|
package theme // import "fyne.io/fyne/v2/theme"
|
|
|
|
import (
|
|
"image/color"
|
|
"os"
|
|
"strings"
|
|
|
|
"fyne.io/fyne/v2"
|
|
)
|
|
|
|
const (
|
|
// VariantDark is the version of a theme that satisfies a user preference for a dark look.
|
|
//
|
|
// Since: 2.0
|
|
VariantDark fyne.ThemeVariant = 0
|
|
|
|
// VariantLight is the version of a theme that satisfies a user preference for a light look.
|
|
//
|
|
// Since: 2.0
|
|
VariantLight fyne.ThemeVariant = 1
|
|
|
|
// potential for adding theme types such as high visibility or monochrome...
|
|
variantNameUserPreference fyne.ThemeVariant = 2 // locally used in builtinTheme for backward compatibility
|
|
)
|
|
|
|
// DarkTheme defines the built-in dark theme colors and sizes.
|
|
//
|
|
// Deprecated: This method ignores user preference and should not be used, it will be removed in v3.0.
|
|
func DarkTheme() fyne.Theme {
|
|
theme := &builtinTheme{variant: VariantDark}
|
|
|
|
theme.initFonts()
|
|
return theme
|
|
}
|
|
|
|
// DefaultTheme returns a built-in theme that can adapt to the user preference of light or dark colors.
|
|
//
|
|
// Since: 2.0
|
|
func DefaultTheme() fyne.Theme {
|
|
if defaultTheme == nil {
|
|
defaultTheme = setupDefaultTheme()
|
|
}
|
|
|
|
return defaultTheme
|
|
}
|
|
|
|
// LightTheme defines the built-in light theme colors and sizes.
|
|
//
|
|
// Deprecated: This method ignores user preference and should not be used, it will be removed in v3.0.
|
|
func LightTheme() fyne.Theme {
|
|
theme := &builtinTheme{variant: VariantLight}
|
|
|
|
theme.initFonts()
|
|
return theme
|
|
}
|
|
|
|
var (
|
|
defaultTheme fyne.Theme
|
|
)
|
|
|
|
type builtinTheme struct {
|
|
variant fyne.ThemeVariant
|
|
|
|
regular, bold, italic, boldItalic, monospace, symbol fyne.Resource
|
|
}
|
|
|
|
func (t *builtinTheme) initFonts() {
|
|
t.regular = regular
|
|
t.bold = bold
|
|
t.italic = italic
|
|
t.boldItalic = bolditalic
|
|
t.monospace = monospace
|
|
t.symbol = symbol
|
|
|
|
font := os.Getenv("FYNE_FONT")
|
|
if font != "" {
|
|
t.regular = loadCustomFont(font, "Regular", regular)
|
|
if t.regular == regular { // failed to load
|
|
t.bold = loadCustomFont(font, "Bold", bold)
|
|
t.italic = loadCustomFont(font, "Italic", italic)
|
|
t.boldItalic = loadCustomFont(font, "BoldItalic", bolditalic)
|
|
} else { // first custom font loaded, fall back to that
|
|
t.bold = loadCustomFont(font, "Bold", t.regular)
|
|
t.italic = loadCustomFont(font, "Italic", t.regular)
|
|
t.boldItalic = loadCustomFont(font, "BoldItalic", t.regular)
|
|
}
|
|
}
|
|
font = os.Getenv("FYNE_FONT_MONOSPACE")
|
|
if font != "" {
|
|
t.monospace = loadCustomFont(font, "Regular", monospace)
|
|
}
|
|
font = os.Getenv("FYNE_FONT_SYMBOL")
|
|
if font != "" {
|
|
t.symbol = loadCustomFont(font, "Regular", symbol)
|
|
}
|
|
}
|
|
|
|
func (t *builtinTheme) Color(n fyne.ThemeColorName, v fyne.ThemeVariant) color.Color {
|
|
if t.variant != variantNameUserPreference {
|
|
v = t.variant
|
|
}
|
|
|
|
primary := fyne.CurrentApp().Settings().PrimaryColor()
|
|
if n == ColorNamePrimary || n == ColorNameHyperlink {
|
|
return primaryColorNamed(primary)
|
|
} else if n == ColorNameFocus {
|
|
return focusColorNamed(primary)
|
|
} else if n == ColorNameSelection {
|
|
return selectionColorNamed(primary)
|
|
}
|
|
|
|
if v == VariantLight {
|
|
return lightPaletColorNamed(n)
|
|
}
|
|
|
|
return darkPaletColorNamed(n)
|
|
}
|
|
|
|
func (t *builtinTheme) Font(style fyne.TextStyle) fyne.Resource {
|
|
if style.Monospace {
|
|
return t.monospace
|
|
}
|
|
if style.Bold {
|
|
if style.Italic {
|
|
return t.boldItalic
|
|
}
|
|
return t.bold
|
|
}
|
|
if style.Italic {
|
|
return t.italic
|
|
}
|
|
if style.Symbol {
|
|
return t.symbol
|
|
}
|
|
return t.regular
|
|
}
|
|
|
|
func (t *builtinTheme) Size(s fyne.ThemeSizeName) float32 {
|
|
switch s {
|
|
case SizeNameSeparatorThickness:
|
|
return 1
|
|
case SizeNameInlineIcon:
|
|
return 20
|
|
case SizeNameInnerPadding:
|
|
return 8
|
|
case SizeNameLineSpacing:
|
|
return 4
|
|
case SizeNamePadding:
|
|
return 4
|
|
case SizeNameScrollBar:
|
|
return 16
|
|
case SizeNameScrollBarSmall:
|
|
return 3
|
|
case SizeNameText:
|
|
return 14
|
|
case SizeNameHeadingText:
|
|
return 24
|
|
case SizeNameSubHeadingText:
|
|
return 18
|
|
case SizeNameCaptionText:
|
|
return 11
|
|
case SizeNameInputBorder:
|
|
return 1
|
|
case SizeNameInputRadius:
|
|
return 5
|
|
case SizeNameSelectionRadius:
|
|
return 3
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func current() fyne.Theme {
|
|
app := fyne.CurrentApp()
|
|
if app == nil {
|
|
return DarkTheme()
|
|
}
|
|
currentTheme := app.Settings().Theme()
|
|
if currentTheme == nil {
|
|
return DarkTheme()
|
|
}
|
|
|
|
return currentTheme
|
|
}
|
|
|
|
func currentVariant() fyne.ThemeVariant {
|
|
if std, ok := current().(*builtinTheme); ok {
|
|
if std.variant != variantNameUserPreference {
|
|
return std.variant // override if using the old LightTheme() or DarkTheme() constructor
|
|
}
|
|
}
|
|
|
|
return fyne.CurrentApp().Settings().ThemeVariant()
|
|
}
|
|
|
|
func darkPaletColorNamed(name fyne.ThemeColorName) color.Color {
|
|
switch name {
|
|
case ColorNameBackground:
|
|
return color.NRGBA{R: 0x17, G: 0x17, B: 0x18, A: 0xff}
|
|
case ColorNameButton:
|
|
return color.NRGBA{R: 0x28, G: 0x29, B: 0x2e, A: 0xff}
|
|
case ColorNameDisabled:
|
|
return color.NRGBA{R: 0x39, G: 0x39, B: 0x3a, A: 0xff}
|
|
case ColorNameDisabledButton:
|
|
return color.NRGBA{R: 0x28, G: 0x29, B: 0x2e, A: 0xff}
|
|
case ColorNameError:
|
|
return errorColor
|
|
case ColorNameForeground:
|
|
return color.NRGBA{R: 0xf3, G: 0xf3, B: 0xf3, A: 0xff}
|
|
case ColorNameHover:
|
|
return color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0x0f}
|
|
case ColorNameHeaderBackground:
|
|
return color.NRGBA{R: 0x1b, G: 0x1b, B: 0x1b, A: 0xff}
|
|
case ColorNameInputBackground:
|
|
return color.NRGBA{R: 0x20, G: 0x20, B: 0x23, A: 0xff}
|
|
case ColorNameInputBorder:
|
|
return color.NRGBA{R: 0x39, G: 0x39, B: 0x3a, A: 0xff}
|
|
case ColorNameMenuBackground:
|
|
return color.NRGBA{R: 0x28, G: 0x29, B: 0x2e, A: 0xff}
|
|
case ColorNameOverlayBackground:
|
|
return color.NRGBA{R: 0x18, G: 0x1d, B: 0x25, A: 0xff}
|
|
case ColorNamePlaceHolder:
|
|
return color.NRGBA{R: 0xb2, G: 0xb2, B: 0xb2, A: 0xff}
|
|
case ColorNamePressed:
|
|
return color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0x66}
|
|
case ColorNameScrollBar:
|
|
return color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0x99}
|
|
case ColorNameSeparator:
|
|
return color.NRGBA{R: 0x0, G: 0x0, B: 0x0, A: 0xff}
|
|
case ColorNameShadow:
|
|
return color.NRGBA{A: 0x66}
|
|
case ColorNameSuccess:
|
|
return successColor
|
|
case ColorNameWarning:
|
|
return warningColor
|
|
}
|
|
|
|
return color.Transparent
|
|
}
|
|
|
|
func focusColorNamed(name string) color.NRGBA {
|
|
switch name {
|
|
case ColorRed:
|
|
return color.NRGBA{R: 0xf4, G: 0x43, B: 0x36, A: 0x7f}
|
|
case ColorOrange:
|
|
return color.NRGBA{R: 0xff, G: 0x98, B: 0x00, A: 0x7f}
|
|
case ColorYellow:
|
|
return color.NRGBA{R: 0xff, G: 0xeb, B: 0x3b, A: 0x7f}
|
|
case ColorGreen:
|
|
return color.NRGBA{R: 0x8b, G: 0xc3, B: 0x4a, A: 0x7f}
|
|
case ColorPurple:
|
|
return color.NRGBA{R: 0x9c, G: 0x27, B: 0xb0, A: 0x7f}
|
|
case ColorBrown:
|
|
return color.NRGBA{R: 0x79, G: 0x55, B: 0x48, A: 0x7f}
|
|
case ColorGray:
|
|
return color.NRGBA{R: 0x9e, G: 0x9e, B: 0x9e, A: 0x7f}
|
|
}
|
|
|
|
// We return the value for ColorBlue for every other value.
|
|
// There is no need to have it in the switch above.
|
|
return color.NRGBA{R: 0x00, G: 0x6C, B: 0xff, A: 0x2a}
|
|
}
|
|
|
|
func lightPaletColorNamed(name fyne.ThemeColorName) color.Color {
|
|
switch name {
|
|
case ColorNameBackground:
|
|
return color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
|
|
case ColorNameButton:
|
|
return color.NRGBA{R: 0xf5, G: 0xf5, B: 0xf5, A: 0xff}
|
|
case ColorNameDisabled:
|
|
return color.NRGBA{R: 0xe3, G: 0xe3, B: 0xe3, A: 0xff}
|
|
case ColorNameDisabledButton:
|
|
return color.NRGBA{R: 0xf5, G: 0xf5, B: 0xf5, A: 0xff}
|
|
case ColorNameError:
|
|
return errorColor
|
|
case ColorNameForeground:
|
|
return color.NRGBA{R: 0x56, G: 0x56, B: 0x56, A: 0xff}
|
|
case ColorNameHover:
|
|
return color.NRGBA{A: 0x0f}
|
|
case ColorNameHeaderBackground:
|
|
return color.NRGBA{R: 0xf9, G: 0xf9, B: 0xf9, A: 0xff}
|
|
case ColorNameInputBackground:
|
|
return color.NRGBA{R: 0xf3, G: 0xf3, B: 0xf3, A: 0xff}
|
|
case ColorNameInputBorder:
|
|
return color.NRGBA{R: 0xe3, G: 0xe3, B: 0xe3, A: 0xff}
|
|
case ColorNameMenuBackground:
|
|
return color.NRGBA{R: 0xf5, G: 0xf5, B: 0xf5, A: 0xff}
|
|
case ColorNameOverlayBackground:
|
|
return color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
|
|
case ColorNamePlaceHolder:
|
|
return color.NRGBA{R: 0x88, G: 0x88, B: 0x88, A: 0xff}
|
|
case ColorNamePressed:
|
|
return color.NRGBA{A: 0x19}
|
|
case ColorNameScrollBar:
|
|
return color.NRGBA{A: 0x99}
|
|
case ColorNameSeparator:
|
|
return color.NRGBA{R: 0xe3, G: 0xe3, B: 0xe3, A: 0xff}
|
|
case ColorNameShadow:
|
|
return color.NRGBA{A: 0x33}
|
|
case ColorNameSuccess:
|
|
return successColor
|
|
case ColorNameWarning:
|
|
return warningColor
|
|
}
|
|
|
|
return color.Transparent
|
|
}
|
|
|
|
func loadCustomFont(env, variant string, fallback fyne.Resource) fyne.Resource {
|
|
variantPath := strings.Replace(env, "Regular", variant, -1)
|
|
|
|
res, err := fyne.LoadResourceFromPath(variantPath)
|
|
if err != nil {
|
|
fyne.LogError("Error loading specified font", err)
|
|
return fallback
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
func primaryColorNamed(name string) color.NRGBA {
|
|
switch name {
|
|
case ColorRed:
|
|
return color.NRGBA{R: 0xf4, G: 0x43, B: 0x36, A: 0xff}
|
|
case ColorOrange:
|
|
return color.NRGBA{R: 0xff, G: 0x98, B: 0x00, A: 0xff}
|
|
case ColorYellow:
|
|
return color.NRGBA{R: 0xff, G: 0xeb, B: 0x3b, A: 0xff}
|
|
case ColorGreen:
|
|
return color.NRGBA{R: 0x8b, G: 0xc3, B: 0x4a, A: 0xff}
|
|
case ColorPurple:
|
|
return color.NRGBA{R: 0x9c, G: 0x27, B: 0xb0, A: 0xff}
|
|
case ColorBrown:
|
|
return color.NRGBA{R: 0x79, G: 0x55, B: 0x48, A: 0xff}
|
|
case ColorGray:
|
|
return color.NRGBA{R: 0x9e, G: 0x9e, B: 0x9e, A: 0xff}
|
|
}
|
|
|
|
// We return the value for ColorBlue for every other value.
|
|
// There is no need to have it in the switch above.
|
|
return color.NRGBA{R: 0x29, G: 0x6f, B: 0xf6, A: 0xff}
|
|
}
|
|
|
|
func selectionColorNamed(name string) color.NRGBA {
|
|
switch name {
|
|
case ColorRed:
|
|
return color.NRGBA{R: 0xf4, G: 0x43, B: 0x36, A: 0x3f}
|
|
case ColorOrange:
|
|
return color.NRGBA{R: 0xff, G: 0x98, B: 0x00, A: 0x3f}
|
|
case ColorYellow:
|
|
return color.NRGBA{R: 0xff, G: 0xeb, B: 0x3b, A: 0x3f}
|
|
case ColorGreen:
|
|
return color.NRGBA{R: 0x8b, G: 0xc3, B: 0x4a, A: 0x3f}
|
|
case ColorPurple:
|
|
return color.NRGBA{R: 0x9c, G: 0x27, B: 0xb0, A: 0x3f}
|
|
case ColorBrown:
|
|
return color.NRGBA{R: 0x79, G: 0x55, B: 0x48, A: 0x3f}
|
|
case ColorGray:
|
|
return color.NRGBA{R: 0x9e, G: 0x9e, B: 0x9e, A: 0x3f}
|
|
}
|
|
|
|
// We return the value for ColorBlue for every other value.
|
|
// There is no need to have it in the switch above.
|
|
return color.NRGBA{R: 0x00, G: 0x6C, B: 0xff, A: 0x40}
|
|
}
|
|
|
|
func setupDefaultTheme() fyne.Theme {
|
|
theme := &builtinTheme{variant: variantNameUserPreference}
|
|
|
|
theme.initFonts()
|
|
return theme
|
|
}
|