theme.go
1 package desktop 2 3 import ( 4 "image/color" 5 6 "fyne.io/fyne/v2" 7 "fyne.io/fyne/v2/theme" 8 ) 9 10 // ThemeMode 主题模式 11 type ThemeMode int 12 13 const ( 14 ThemeModeLight ThemeMode = iota 15 ThemeModeDark 16 ThemeModeAuto 17 ) 18 19 // customTheme 自定义主题 20 type customTheme struct { 21 baseTheme fyne.Theme 22 mode ThemeMode 23 forceDark bool 24 // 添加主题变化回调 25 onThemeChange []func(ThemeMode) 26 } 27 28 func NewCustomTheme(mode ThemeMode) fyne.Theme { 29 forceDark := mode == ThemeModeDark 30 return &customTheme{ 31 baseTheme: theme.DefaultTheme(), 32 mode: mode, 33 forceDark: forceDark, 34 onThemeChange: make([]func(ThemeMode), 0), 35 } 36 } 37 38 func (t *customTheme) Color(name fyne.ThemeColorName, variant fyne.ThemeVariant) color.Color { 39 if t.forceDark || (t.mode == ThemeModeAuto && variant == theme.VariantDark) || t.mode == ThemeModeDark { 40 return t.darkColors(name) 41 } 42 return t.lightColors(name) 43 } 44 45 // lightColors 明亮主题配色方案 46 func (t *customTheme) lightColors(name fyne.ThemeColorName) color.Color { 47 switch name { 48 // 主色系 - 更现代的蓝色 49 case theme.ColorNamePrimary: 50 return color.NRGBA{R: 59, G: 130, B: 246, A: 255} // 更鲜艳的蓝色 51 52 // 背景与前景 53 case theme.ColorNameBackground: 54 return color.NRGBA{R: 248, G: 250, B: 252, A: 255} // 极浅灰背景 55 case theme.ColorNameForeground: 56 return color.NRGBA{R: 17, G: 24, B: 39, A: 255} // 深灰文字 57 case theme.ColorNameDisabled: 58 return color.NRGBA{R: 156, G: 163, B: 175, A: 150} // 柔和禁用色 59 60 // 按钮状态 61 case theme.ColorNameButton: 62 return color.NRGBA{R: 59, G: 130, B: 246, A: 255} 63 case theme.ColorNameHover: 64 return color.NRGBA{R: 37, G: 99, B: 235, A: 255} // 深蓝悬停 65 case theme.ColorNamePressed: 66 return color.NRGBA{R: 29, G: 78, B: 216, A: 255} // 更深蓝按下 67 68 // 输入组件 69 case theme.ColorNameInputBackground: 70 return color.NRGBA{R: 255, G: 255, B: 255, A: 255} // 纯白输入框 71 case theme.ColorNameInputBorder: 72 return color.NRGBA{R: 209, G: 213, B: 219, A: 255} // 浅灰边框 73 case theme.ColorNamePlaceHolder: 74 return color.NRGBA{R: 156, G: 163, B: 175, A: 200} // 灰占位符 75 76 // 其他 77 case theme.ColorNameSelection: 78 return color.NRGBA{R: 219, G: 234, B: 254, A: 180} // 淡蓝选中 79 case theme.ColorNameScrollBar: 80 return color.NRGBA{R: 209, G: 213, B: 219, A: 200} 81 case theme.ColorNameShadow: 82 return color.NRGBA{R: 0, G: 0, B: 0, A: 25} // 柔和阴影 83 84 // 状态色 85 case theme.ColorNameError: 86 return color.NRGBA{R: 239, G: 68, B: 68, A: 255} // 红色错误 87 case theme.ColorNameWarning: 88 return color.NRGBA{R: 245, G: 158, B: 11, A: 255} // 橙色警告 89 case theme.ColorNameSuccess: 90 return color.NRGBA{R: 34, G: 197, B: 94, A: 255} // 绿色成功 91 case theme.ColorNameFocus: 92 return color.NRGBA{R: 59, G: 130, B: 246, A: 100} // 半透明焦点 93 94 default: 95 return t.baseTheme.Color(name, theme.VariantLight) 96 } 97 } 98 99 // darkColors 夜晚主题配色方案 100 func (t *customTheme) darkColors(name fyne.ThemeColorName) color.Color { 101 switch name { 102 // 主色系 103 case theme.ColorNamePrimary: 104 return color.NRGBA{R: 96, G: 165, B: 250, A: 255} // 亮蓝色 105 106 // 背景与前景 107 case theme.ColorNameBackground: 108 return color.NRGBA{R: 15, G: 23, B: 42, A: 255} // 深蓝灰背景 109 case theme.ColorNameForeground: 110 return color.NRGBA{R: 248, G: 250, B: 252, A: 255} // 浅灰文字 111 case theme.ColorNameDisabled: 112 return color.NRGBA{R: 100, G: 116, B: 139, A: 150} // 深色禁用 113 114 // 按钮状态 115 case theme.ColorNameButton: 116 return color.NRGBA{R: 30, G: 41, B: 59, A: 255} // 深按钮背景 117 case theme.ColorNameHover: 118 return color.NRGBA{R: 51, G: 65, B: 85, A: 255} // 浅灰悬停 119 case theme.ColorNamePressed: 120 return color.NRGBA{R: 15, G: 23, B: 42, A: 255} // 更深按下 121 122 // 输入组件 123 case theme.ColorNameInputBackground: 124 return color.NRGBA{R: 30, G: 41, B: 59, A: 255} // 深输入框背景 125 case theme.ColorNameInputBorder: 126 return color.NRGBA{R: 51, G: 65, B: 85, A: 255} // 深边框 127 case theme.ColorNamePlaceHolder: 128 return color.NRGBA{R: 148, G: 163, B: 184, A: 200} // 灰占位符 129 130 // 其他 131 case theme.ColorNameSelection: 132 return color.NRGBA{R: 59, G: 130, B: 246, A: 180} // 蓝色选中 133 case theme.ColorNameScrollBar: 134 return color.NRGBA{R: 51, G: 65, B: 85, A: 200} // 深滚动条 135 case theme.ColorNameShadow: 136 return color.NRGBA{R: 0, G: 0, B: 0, A: 50} // 深色阴影 137 138 // 状态色(更鲜艳) 139 case theme.ColorNameError: 140 return color.NRGBA{R: 248, G: 113, B: 113, A: 255} 141 case theme.ColorNameWarning: 142 return color.NRGBA{R: 251, G: 191, B: 36, A: 255} 143 case theme.ColorNameSuccess: 144 return color.NRGBA{R: 74, G: 222, B: 128, A: 255} 145 case theme.ColorNameFocus: 146 return color.NRGBA{R: 96, G: 165, B: 250, A: 100} 147 148 default: 149 return t.baseTheme.Color(name, theme.VariantDark) 150 } 151 } 152 153 func (t *customTheme) Icon(name fyne.ThemeIconName) fyne.Resource { 154 return t.baseTheme.Icon(name) 155 } 156 157 func (t *customTheme) Font(style fyne.TextStyle) fyne.Resource { 158 return t.baseTheme.Font(style) 159 } 160 161 func (t *customTheme) Size(name fyne.ThemeSizeName) float32 { 162 switch name { 163 case theme.SizeNamePadding: 164 return 12 165 case theme.SizeNameInlineIcon: 166 return 20 167 case theme.SizeNameScrollBar: 168 return 8 169 case theme.SizeNameScrollBarSmall: 170 return 4 171 case theme.SizeNameSeparatorThickness: 172 return 1 173 case theme.SizeNameText: 174 return 14 175 case theme.SizeNameInputBorder: 176 return 1.5 177 case theme.SizeNameInputRadius: 178 return 6 179 default: 180 return t.baseTheme.Size(name) 181 } 182 } 183 184 // GetThemeMode 获取当前主题模式 185 func (t *customTheme) GetThemeMode() ThemeMode { 186 return t.mode 187 } 188 189 // SetThemeMode 设置主题模式 190 func (t *customTheme) SetThemeMode(mode ThemeMode) { 191 if t.mode != mode { 192 t.mode = mode 193 t.forceDark = mode == ThemeModeDark 194 // 通知所有回调函数 195 for _, callback := range t.onThemeChange { 196 callback(mode) 197 } 198 } 199 } 200 201 // AddThemeChangeCallback 添加主题变化回调 202 func (t *customTheme) AddThemeChangeCallback(callback func(ThemeMode)) { 203 t.onThemeChange = append(t.onThemeChange, callback) 204 }