05 · 样式与主题¶
Style 是什么¶
ui.Style 是不可变的外观与盒模型声明。用 free function 起头,再链式方法叠加:
style := ui.Background(ui.TokenAccent).
TextColor(ui.TokenAccentForeground).
Radius(8).
PaddingX(12).PaddingY(6).
Cursor(ui.CursorPointer)
挂到控件:
没有单独的 Builder / Build 步骤;每次方法返回新的 Style 值。
级联顺序(固定)¶
组件 defaults
→ 继承文本(父级 Text 声明)
→ variant
→ size
→ StyleScope 祖先
→ 实例 Style
→ ExpandTokens → Cascade(When) → ResolveColors → Transitions
后写的同名属性在同一层内覆盖先写的;When 在运行时按状态叠加。
When:状态样式¶
primary := ui.Background(ui.TokenAccent).
TextColor(ui.TokenAccentForeground).
Radius(8).
Cursor(ui.CursorPointer).
When(ui.Hovered, ui.Background(ui.TokenAccentHover)).
When(ui.Pressed, ui.Background(ui.TokenAccentPressed).Scale(0.96, 0.96)).
When(ui.Disabled, ui.Opacity(0.5))
常见条件:Hovered、Pressed、FocusVisible、Disabled、Checked、Selected、Invalid、Loading 等。
把 Model 布尔值 接进同一条路径:
StyleScope:子树默认¶
ui.StyleScope(
ui.FontSize(14).TextColor(ui.TokenForeground),
ui.Column(
ui.Text("继承字号与颜色"),
ui.Button("save", ui.Text("保存")).Style(primary),
).Gap(8),
)
Scope 向下传递;实例 Style 仍优先。Scope 不会改全局 Theme。
Part:复合控件零件¶
根 Style 作用在外层盒子;内部用 Part:
barStyle := ui.Background(ui.RGBA(0x111827cc)).
Part(ui.PartTrack, ui.Height(6).Background(ui.TokenSurfaceRaised)).
Part(ui.PartFill, ui.Background(ui.TokenAccent)).
Part(ui.PartLabel, ui.TextColor(ui.TokenMutedForeground))
ui.ProgressBar("upload", 42).Label("上传").Style(barStyle)
常见 Part:PartContent、PartLabel、PartIcon、PartTrack、PartFill、PartThumb、PartPanel、PartItem、PartBackdrop、PartPlaceholder、PartPrefix、PartSuffix 等。
字段类控件(Select、Input 组)常用 PartContent 表示字段表面。
颜色与 Token¶
优先用主题 Token,便于明暗主题切换:
字面色:
渐变与自定义绘制¶
自定义组件需要绘制渐变时,可以先用主题解析画刷,再交给公共绘制函数:
brush, ok := ui.ResolveBrush(ctx, ui.LinearGradient(
ui.ColorStop(0, ui.TokenAccent),
ui.ColorStop(1, ui.TokenDanger),
))
if ok {
ui.DrawBrush(gtx, image.Rect(0, 0, 160, 32), 6, brush)
}
ResolveColor 用于取得单个主题解析色;DrawBrushRRect 用于已有
clip.RRect 的形状。组件不应在每个绘制路径中重新解析 Token,也不要把
渐变绘制扩展成浮层系统。
主题¶
启动时¶
ui.Run(ui.NewProgram(Model{}, Update, View),
ui.WithTheme(ui.DarkTheme()), // 或 DefaultTheme()
// 或局部改:
ui.CustomizeTheme(func(theme *ui.Theme) {
theme.Palette.Accent = color.NRGBA{R: 0x00, G: 0x78, B: 0xd4, A: 0xff}
theme.Components.Button.Radius = 8
}),
ui.Locale(ui.LanguageChinese),
)
运行时(多窗口 Application)¶
必须在目标窗口事件循环路径上生效(见 examples/multi_windows)。
字体¶
默认主题使用 sans-serif 和 monospace 字体族,并允许 Gio 使用系统字体
回退。这样不需要额外资源就能显示不同语言;但不同系统的字形可能略有差异。
系统字体回退会建立完整的系统字体索引,启动和内存开销都可能明显增加;只需要
固定字体时,建议关闭它并提供完整的字体回退集合。
需要固定字体时,可以把字体文件随程序打包。ParseFontCollection 支持 TTF、OTF
和 TTC:
package main
import (
_ "embed"
"github.com/qianniancn/flowui/ui"
)
//go:embed fonts/Inter-Regular.ttf
var interRegular []byte
func theme() ui.Theme {
faces, err := ui.ParseFontCollection(interRegular)
if err != nil {
panic(err)
}
theme := ui.DefaultTheme()
theme.Typography.Typeface = "Inter, sans-serif"
theme.Fonts.Collection = faces
theme.Fonts.SystemFonts = false
return theme
}
Theme.Fonts.Collection 应在应用启动前准备好,多个窗口可以共享解析后的字体
数据;每个窗口仍会创建自己的 Shaper。关闭系统字体后,请确保集合包含应用需要
的字符范围,必要时将多个字体集合并到同一个 Collection。
完整示例:
如果需要检查绘制和内存开销,可以运行 profiling 示例直接生成性能文件, 不需要启动 HTTP 服务:
go run ./examples/profiling -memprofile="profiling.heap.pprof" -cpuprofile="profiling.cpu.pprof" -duration=30
go tool pprof -top profiling.heap.pprof
go tool pprof -top profiling.cpu.pprof
示例会在指定时长后写出文件,窗口可以在这段时间内正常调整和操作。
如果要专门分析字体示例,请在 examples/fonts 中使用系统级进程分析器,
或将相同的字体配置复制到 profiling 示例中。
几何常用项¶
ui.Width(200).Height(40)
ui.MinWidth(100).MaxWidth(400)
ui.Padding(12) // 或 PaddingX / PaddingY
ui.Margin(8)
ui.Radius(8) // 或四角分别设置(API 以 go doc 为准)
ui.BorderWidth(1).BorderColor(ui.TokenBorder)
ui.BorderBottomWidth(1).BorderBottomColor(ui.TokenBorder)
ui.BoxShadow(0, 6, 18, 0, ui.RGBA(0x00000030))
ui.Opacity(0.9)
ui.Cursor(ui.CursorPointer)
ui.Overflow(ui.StyleOverflowHidden) // 裁剪溢出
Transition(声明式属性动画)¶
ui.Background(ui.TokenSurface).
Transition(ui.PropBackgroundColor, 120*time.Millisecond).
When(ui.Hovered, ui.Background(ui.TokenSurfaceRaised))
需要稳定 Key;详见 12-动画。
原则小结¶
- 外观与盒模型 → Style
- 排列策略 → 布局容器
- 状态外观 →
When,不要为 hover 另建一棵 UI 树 - 主题色 → Token,少写死 RGB