theme.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. // Package theme defines how a Fyne app should look when rendered.
  2. package theme // import "fyne.io/fyne/v2/theme"
  3. import (
  4. "image/color"
  5. "os"
  6. "strings"
  7. "fyne.io/fyne/v2"
  8. )
  9. const (
  10. // VariantDark is the version of a theme that satisfies a user preference for a dark look.
  11. //
  12. // Since: 2.0
  13. VariantDark fyne.ThemeVariant = 0
  14. // VariantLight is the version of a theme that satisfies a user preference for a light look.
  15. //
  16. // Since: 2.0
  17. VariantLight fyne.ThemeVariant = 1
  18. // potential for adding theme types such as high visibility or monochrome...
  19. variantNameUserPreference fyne.ThemeVariant = 2 // locally used in builtinTheme for backward compatibility
  20. )
  21. // DarkTheme defines the built-in dark theme colors and sizes.
  22. //
  23. // Deprecated: This method ignores user preference and should not be used, it will be removed in v3.0.
  24. func DarkTheme() fyne.Theme {
  25. theme := &builtinTheme{variant: VariantDark}
  26. theme.initFonts()
  27. return theme
  28. }
  29. // DefaultTheme returns a built-in theme that can adapt to the user preference of light or dark colors.
  30. //
  31. // Since: 2.0
  32. func DefaultTheme() fyne.Theme {
  33. if defaultTheme == nil {
  34. defaultTheme = setupDefaultTheme()
  35. }
  36. return defaultTheme
  37. }
  38. // LightTheme defines the built-in light theme colors and sizes.
  39. //
  40. // Deprecated: This method ignores user preference and should not be used, it will be removed in v3.0.
  41. func LightTheme() fyne.Theme {
  42. theme := &builtinTheme{variant: VariantLight}
  43. theme.initFonts()
  44. return theme
  45. }
  46. var (
  47. defaultTheme fyne.Theme
  48. )
  49. type builtinTheme struct {
  50. variant fyne.ThemeVariant
  51. regular, bold, italic, boldItalic, monospace, symbol fyne.Resource
  52. }
  53. func (t *builtinTheme) initFonts() {
  54. t.regular = regular
  55. t.bold = bold
  56. t.italic = italic
  57. t.boldItalic = bolditalic
  58. t.monospace = monospace
  59. t.symbol = symbol
  60. font := os.Getenv("FYNE_FONT")
  61. if font != "" {
  62. t.regular = loadCustomFont(font, "Regular", regular)
  63. if t.regular == regular { // failed to load
  64. t.bold = loadCustomFont(font, "Bold", bold)
  65. t.italic = loadCustomFont(font, "Italic", italic)
  66. t.boldItalic = loadCustomFont(font, "BoldItalic", bolditalic)
  67. } else { // first custom font loaded, fall back to that
  68. t.bold = loadCustomFont(font, "Bold", t.regular)
  69. t.italic = loadCustomFont(font, "Italic", t.regular)
  70. t.boldItalic = loadCustomFont(font, "BoldItalic", t.regular)
  71. }
  72. }
  73. font = os.Getenv("FYNE_FONT_MONOSPACE")
  74. if font != "" {
  75. t.monospace = loadCustomFont(font, "Regular", monospace)
  76. }
  77. font = os.Getenv("FYNE_FONT_SYMBOL")
  78. if font != "" {
  79. t.symbol = loadCustomFont(font, "Regular", symbol)
  80. }
  81. }
  82. func (t *builtinTheme) Color(n fyne.ThemeColorName, v fyne.ThemeVariant) color.Color {
  83. if t.variant != variantNameUserPreference {
  84. v = t.variant
  85. }
  86. primary := fyne.CurrentApp().Settings().PrimaryColor()
  87. if n == ColorNamePrimary || n == ColorNameHyperlink {
  88. return primaryColorNamed(primary)
  89. } else if n == ColorNameFocus {
  90. return focusColorNamed(primary)
  91. } else if n == ColorNameSelection {
  92. return selectionColorNamed(primary)
  93. }
  94. if v == VariantLight {
  95. return lightPaletColorNamed(n)
  96. }
  97. return darkPaletColorNamed(n)
  98. }
  99. func (t *builtinTheme) Font(style fyne.TextStyle) fyne.Resource {
  100. if style.Monospace {
  101. return t.monospace
  102. }
  103. if style.Bold {
  104. if style.Italic {
  105. return t.boldItalic
  106. }
  107. return t.bold
  108. }
  109. if style.Italic {
  110. return t.italic
  111. }
  112. if style.Symbol {
  113. return t.symbol
  114. }
  115. return t.regular
  116. }
  117. func (t *builtinTheme) Size(s fyne.ThemeSizeName) float32 {
  118. switch s {
  119. case SizeNameSeparatorThickness:
  120. return 1
  121. case SizeNameInlineIcon:
  122. return 20
  123. case SizeNameInnerPadding:
  124. return 8
  125. case SizeNameLineSpacing:
  126. return 4
  127. case SizeNamePadding:
  128. return 4
  129. case SizeNameScrollBar:
  130. return 16
  131. case SizeNameScrollBarSmall:
  132. return 3
  133. case SizeNameText:
  134. return 14
  135. case SizeNameHeadingText:
  136. return 24
  137. case SizeNameSubHeadingText:
  138. return 18
  139. case SizeNameCaptionText:
  140. return 11
  141. case SizeNameInputBorder:
  142. return 1
  143. case SizeNameInputRadius:
  144. return 5
  145. case SizeNameSelectionRadius:
  146. return 3
  147. default:
  148. return 0
  149. }
  150. }
  151. func current() fyne.Theme {
  152. app := fyne.CurrentApp()
  153. if app == nil {
  154. return DarkTheme()
  155. }
  156. currentTheme := app.Settings().Theme()
  157. if currentTheme == nil {
  158. return DarkTheme()
  159. }
  160. return currentTheme
  161. }
  162. func currentVariant() fyne.ThemeVariant {
  163. if std, ok := current().(*builtinTheme); ok {
  164. if std.variant != variantNameUserPreference {
  165. return std.variant // override if using the old LightTheme() or DarkTheme() constructor
  166. }
  167. }
  168. return fyne.CurrentApp().Settings().ThemeVariant()
  169. }
  170. func darkPaletColorNamed(name fyne.ThemeColorName) color.Color {
  171. switch name {
  172. case ColorNameBackground:
  173. return color.NRGBA{R: 0x17, G: 0x17, B: 0x18, A: 0xff}
  174. case ColorNameButton:
  175. return color.NRGBA{R: 0x28, G: 0x29, B: 0x2e, A: 0xff}
  176. case ColorNameDisabled:
  177. return color.NRGBA{R: 0x39, G: 0x39, B: 0x3a, A: 0xff}
  178. case ColorNameDisabledButton:
  179. return color.NRGBA{R: 0x28, G: 0x29, B: 0x2e, A: 0xff}
  180. case ColorNameError:
  181. return errorColor
  182. case ColorNameForeground:
  183. return color.NRGBA{R: 0xf3, G: 0xf3, B: 0xf3, A: 0xff}
  184. case ColorNameHover:
  185. return color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0x0f}
  186. case ColorNameHeaderBackground:
  187. return color.NRGBA{R: 0x1b, G: 0x1b, B: 0x1b, A: 0xff}
  188. case ColorNameInputBackground:
  189. return color.NRGBA{R: 0x20, G: 0x20, B: 0x23, A: 0xff}
  190. case ColorNameInputBorder:
  191. return color.NRGBA{R: 0x39, G: 0x39, B: 0x3a, A: 0xff}
  192. case ColorNameMenuBackground:
  193. return color.NRGBA{R: 0x28, G: 0x29, B: 0x2e, A: 0xff}
  194. case ColorNameOverlayBackground:
  195. return color.NRGBA{R: 0x18, G: 0x1d, B: 0x25, A: 0xff}
  196. case ColorNamePlaceHolder:
  197. return color.NRGBA{R: 0xb2, G: 0xb2, B: 0xb2, A: 0xff}
  198. case ColorNamePressed:
  199. return color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0x66}
  200. case ColorNameScrollBar:
  201. return color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0x99}
  202. case ColorNameSeparator:
  203. return color.NRGBA{R: 0x0, G: 0x0, B: 0x0, A: 0xff}
  204. case ColorNameShadow:
  205. return color.NRGBA{A: 0x66}
  206. case ColorNameSuccess:
  207. return successColor
  208. case ColorNameWarning:
  209. return warningColor
  210. }
  211. return color.Transparent
  212. }
  213. func focusColorNamed(name string) color.NRGBA {
  214. switch name {
  215. case ColorRed:
  216. return color.NRGBA{R: 0xf4, G: 0x43, B: 0x36, A: 0x7f}
  217. case ColorOrange:
  218. return color.NRGBA{R: 0xff, G: 0x98, B: 0x00, A: 0x7f}
  219. case ColorYellow:
  220. return color.NRGBA{R: 0xff, G: 0xeb, B: 0x3b, A: 0x7f}
  221. case ColorGreen:
  222. return color.NRGBA{R: 0x8b, G: 0xc3, B: 0x4a, A: 0x7f}
  223. case ColorPurple:
  224. return color.NRGBA{R: 0x9c, G: 0x27, B: 0xb0, A: 0x7f}
  225. case ColorBrown:
  226. return color.NRGBA{R: 0x79, G: 0x55, B: 0x48, A: 0x7f}
  227. case ColorGray:
  228. return color.NRGBA{R: 0x9e, G: 0x9e, B: 0x9e, A: 0x7f}
  229. }
  230. // We return the value for ColorBlue for every other value.
  231. // There is no need to have it in the switch above.
  232. return color.NRGBA{R: 0x00, G: 0x6C, B: 0xff, A: 0x2a}
  233. }
  234. func lightPaletColorNamed(name fyne.ThemeColorName) color.Color {
  235. switch name {
  236. case ColorNameBackground:
  237. return color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
  238. case ColorNameButton:
  239. return color.NRGBA{R: 0xf5, G: 0xf5, B: 0xf5, A: 0xff}
  240. case ColorNameDisabled:
  241. return color.NRGBA{R: 0xe3, G: 0xe3, B: 0xe3, A: 0xff}
  242. case ColorNameDisabledButton:
  243. return color.NRGBA{R: 0xf5, G: 0xf5, B: 0xf5, A: 0xff}
  244. case ColorNameError:
  245. return errorColor
  246. case ColorNameForeground:
  247. return color.NRGBA{R: 0x56, G: 0x56, B: 0x56, A: 0xff}
  248. case ColorNameHover:
  249. return color.NRGBA{A: 0x0f}
  250. case ColorNameHeaderBackground:
  251. return color.NRGBA{R: 0xf9, G: 0xf9, B: 0xf9, A: 0xff}
  252. case ColorNameInputBackground:
  253. return color.NRGBA{R: 0xf3, G: 0xf3, B: 0xf3, A: 0xff}
  254. case ColorNameInputBorder:
  255. return color.NRGBA{R: 0xe3, G: 0xe3, B: 0xe3, A: 0xff}
  256. case ColorNameMenuBackground:
  257. return color.NRGBA{R: 0xf5, G: 0xf5, B: 0xf5, A: 0xff}
  258. case ColorNameOverlayBackground:
  259. return color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
  260. case ColorNamePlaceHolder:
  261. return color.NRGBA{R: 0x88, G: 0x88, B: 0x88, A: 0xff}
  262. case ColorNamePressed:
  263. return color.NRGBA{A: 0x19}
  264. case ColorNameScrollBar:
  265. return color.NRGBA{A: 0x99}
  266. case ColorNameSeparator:
  267. return color.NRGBA{R: 0xe3, G: 0xe3, B: 0xe3, A: 0xff}
  268. case ColorNameShadow:
  269. return color.NRGBA{A: 0x33}
  270. case ColorNameSuccess:
  271. return successColor
  272. case ColorNameWarning:
  273. return warningColor
  274. }
  275. return color.Transparent
  276. }
  277. func loadCustomFont(env, variant string, fallback fyne.Resource) fyne.Resource {
  278. variantPath := strings.Replace(env, "Regular", variant, -1)
  279. res, err := fyne.LoadResourceFromPath(variantPath)
  280. if err != nil {
  281. fyne.LogError("Error loading specified font", err)
  282. return fallback
  283. }
  284. return res
  285. }
  286. func primaryColorNamed(name string) color.NRGBA {
  287. switch name {
  288. case ColorRed:
  289. return color.NRGBA{R: 0xf4, G: 0x43, B: 0x36, A: 0xff}
  290. case ColorOrange:
  291. return color.NRGBA{R: 0xff, G: 0x98, B: 0x00, A: 0xff}
  292. case ColorYellow:
  293. return color.NRGBA{R: 0xff, G: 0xeb, B: 0x3b, A: 0xff}
  294. case ColorGreen:
  295. return color.NRGBA{R: 0x8b, G: 0xc3, B: 0x4a, A: 0xff}
  296. case ColorPurple:
  297. return color.NRGBA{R: 0x9c, G: 0x27, B: 0xb0, A: 0xff}
  298. case ColorBrown:
  299. return color.NRGBA{R: 0x79, G: 0x55, B: 0x48, A: 0xff}
  300. case ColorGray:
  301. return color.NRGBA{R: 0x9e, G: 0x9e, B: 0x9e, A: 0xff}
  302. }
  303. // We return the value for ColorBlue for every other value.
  304. // There is no need to have it in the switch above.
  305. return color.NRGBA{R: 0x29, G: 0x6f, B: 0xf6, A: 0xff}
  306. }
  307. func selectionColorNamed(name string) color.NRGBA {
  308. switch name {
  309. case ColorRed:
  310. return color.NRGBA{R: 0xf4, G: 0x43, B: 0x36, A: 0x3f}
  311. case ColorOrange:
  312. return color.NRGBA{R: 0xff, G: 0x98, B: 0x00, A: 0x3f}
  313. case ColorYellow:
  314. return color.NRGBA{R: 0xff, G: 0xeb, B: 0x3b, A: 0x3f}
  315. case ColorGreen:
  316. return color.NRGBA{R: 0x8b, G: 0xc3, B: 0x4a, A: 0x3f}
  317. case ColorPurple:
  318. return color.NRGBA{R: 0x9c, G: 0x27, B: 0xb0, A: 0x3f}
  319. case ColorBrown:
  320. return color.NRGBA{R: 0x79, G: 0x55, B: 0x48, A: 0x3f}
  321. case ColorGray:
  322. return color.NRGBA{R: 0x9e, G: 0x9e, B: 0x9e, A: 0x3f}
  323. }
  324. // We return the value for ColorBlue for every other value.
  325. // There is no need to have it in the switch above.
  326. return color.NRGBA{R: 0x00, G: 0x6C, B: 0xff, A: 0x40}
  327. }
  328. func setupDefaultTheme() fyne.Theme {
  329. theme := &builtinTheme{variant: variantNameUserPreference}
  330. theme.initFonts()
  331. return theme
  332. }