Skip to content

Commit 64f7d38

Browse files
committed
feat(toolbar): L2 几何进 schema,工具栏几何主题可覆盖
L1 解耦后,把硬编码的工具栏几何提升为主题可覆盖字段: - ToolbarViews 加几何字段 height/grip_width/button_width/button_padding/button_radius (均 *Dimension,nil=内置默认零回归);button_width 的 *Dimension 形态预留 nil=未来内容驱动 - resolveToolbarGeom 按 scale 换算几何、缺省回退默认;buildToolbarTree 改吃 toolbarGeom - 总宽 116→114:root 去 FixedW、measure 汇总=grip+4×button 槽位,去掉旧尾部 2px 死区 (按钮左缘位置不变) - 间距用按钮 margin 保命中带无缝(引擎通用 View.Gap 已具备、候选列表在用) - 窗口尺寸收口:创建/DPI 变化改用 renderer.GetToolbarSize()(与渲染同源); computeGeometry 用 zeroMeasurer 脱离文本后端,窗口创建早期可安全调 - 删旧常量 toolbarBaseWidth/Height/gripWidth/buttonWidth/buttonPadding,换 tbDefault* - 新增 TestResolveToolbarGeom_SchemaOverride/_NilDefaults,更新既有几何测试 真机验证工具栏外观/按钮点击/菜单/tooltip 正常。设计见 docs/design/theme-toolbar-geometry.md。
1 parent e6694dd commit 64f7d38

8 files changed

Lines changed: 159 additions & 58 deletions

File tree

docs/design/theme-toolbar-geometry.md

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,29 @@ computeGeometry() → 用零 state/零色 buildToolbarTree + Layout,提取:
5454

5555
**不变**:渲染(`Render`)与矢量符号后处理已用 `tt.X.Rect()`,本就单源,无需改。
5656

57-
## L2:盒模型化(规划,L1 绿后再开)
58-
59-
L1 解耦后,几何收口到 `buildToolbarTree`,可安全地让 measure 真正生效、几何进主题 schema:
60-
61-
1. **schema 补几何字段**`ToolbarViews` 增按钮 padding / gap / grip 宽 / 圆角等;默认值=当前常量(零回归)。
62-
2. **`FixedW`**:按钮尺寸由内容 measure + padding 决定,root 尺寸由 measure 汇总;`GetToolbarSize` 自然反映。
63-
3. **`ResolveToolbarViews`**`other_views.go` 当前唯一缺失槽位;走统一 `resolveViewNode`
64-
消费目前被忽略的 `ToolbarButtonNode.Border` 等字段。
65-
4. 协调 `GetDPIScale` vs `ScaleIntForDPI`,统一窗口尺寸到 `GetToolbarSize`#4 收口)。
57+
## L2:几何进 schema(已实现)
58+
59+
L1 解耦后几何收口到 `buildToolbarTree`,L2 把硬编码几何提升为主题可覆盖字段:
60+
61+
1. **schema 几何字段**`ToolbarViews``*Dimension`,nil=内置默认零回归):
62+
`height`(30) / `grip_width`(10) / `button_width`(26 槽位含 padding) / `button_padding`(2) / `button_radius`(4)。
63+
`resolveToolbarGeom(rv, scale)`(internal/ui)按 scale 换算为设备像素、缺省回退默认;
64+
`buildToolbarTree` 改吃 `toolbarGeom`
65+
2. **保持固定统一按钮 + 预留内容驱动**:按钮仍 `FixedW`(统一固定,工具栏的正确模型);
66+
`button_width``*Dimension` 形态即为内容驱动预留——未来 nil 可改走"内容 measure + padding",
67+
届时只改 `resolveToolbarGeom`/`buildToolbarTree` 一处,命中/尺寸经 L1 自动跟随。
68+
3. **总宽 116→114**`root` 去掉 `FixedW`,由 measure 汇总 = `grip + 4×button 槽位` = 114,
69+
消除旧 116 的尾部 2px 死区。
70+
4. **间距用按钮 margin(非引擎 Gap)**:margin 盒首尾相接 → 命中带无缝(`viewOuterRect` 平铺),
71+
避免 `Gap` 在按钮间留 2px 死区。引擎通用 `View.Gap` 已具备(候选列表在用),
72+
工具栏出于命中无缝考量用 margin;`button_padding` 即间距控制。
73+
5. **窗口尺寸收口(#4**`toolbar_window.go` 创建/DPI 变化处改用 `renderer.GetToolbarSize()`
74+
(与渲染同源;`GetDPIScale``ScaleIntForDPI` 本就同一 provider,仅四舍五入 vs 截断之差)。
75+
实际显示尺寸由 `UpdateLayeredWindow` 取渲染图像 bounds,`GetToolbarSize` 使三者一致。
76+
`computeGeometry``zeroMeasurer`(几何全 FixedW,免依赖文本后端,窗口创建早期可安全调)。
77+
78+
**未做(可延后)**:补 `other_views.go``ResolveToolbarViews` 统一走 `resolveViewNode`(颜色解析现仍在
79+
`viewbox_toolbar.go` 自实现,工作正常);矢量符号尺寸仍按 scale(不随 button_width 变),属 L3 内容动态化范畴。
6680

6781
## L3 愿景(远期,不在本次)
6882

wind_input/internal/ui/AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
| `toolbar_window.go` | 工具栏 Win32 窗口创建和消息循环 |
6767
| `toolbar_window_event.go` | 工具栏鼠标事件(拖拽、按钮点击) |
6868
| `toolbar_renderer.go` | 工具栏渲染:`Render` 走盒模型 View 引擎(见 viewbox_toolbar.go),整条背景/边框/按钮框/mode 文字走 View,grip/全半角/标点/齿轮矢量符号后处理(`paintGrip`/`paintWidthSymbol`/`paintPunctSymbols`/`paintGear`,定位用 View rect)。**L1 几何单一真相源**`HitTest`/`GetButtonBounds`/`GetToolbarSize` 不再各算线性公式,统一查 `computeGeometry()`(一次 `buildToolbarTree`+`Layout` 派生)——`GetButtonBounds`=按钮 View 的 `Rect()`(content),`HitTest`=`viewOuterRect`(margin 盒,平铺整条满高),`GetToolbarSize`=root 尺寸;设计见 `docs/design/theme-toolbar-geometry.md`。含 `RenderTooltip`(按钮悬停提示,仍 gg 直绘) |
69-
| `viewbox_toolbar.go` | **工具栏 View 化(P4-C)**`buildToolbarTree`(整条 LayoutRow:grip 占位 + 4 按钮框,按钮 Stretch 撑高 + margin,mode 是带背景文本叶子,width/punct/settings 是无 Text 框)返回 `toolbarTree`(各按钮 View 引用);`(*ToolbarRenderer).resolveToolbarViews`(button base 默认 FullWidthOff* + mode 中/英 token 覆盖,映射 `Palette.Toolbar`)。几何 hardcode×scale 与现状逐像素一致 |
69+
| `viewbox_toolbar.go` | **工具栏 View 化(P4-C)+ L2 几何进 schema**`buildToolbarTree(state, rtv, toolbarGeom)`(整条 LayoutRow:grip 占位 + 4 按钮框,按钮 Stretch 撑高 + margin,mode 是带背景文本叶子,width/punct/settings 是无 Text 框)返回 `toolbarTree`(各按钮 View 引用);`(*ToolbarRenderer).resolveToolbarViews`(button base 默认 FullWidthOff* + mode 中/英 token 覆盖,映射 `Palette.Toolbar`)。**L2**:几何取自 `toolbarGeom``resolveToolbarGeom``views.toolbar``height/grip_width/button_width/button_padding/button_radius` *Dimension×scale,缺省=内置默认);root 去 FixedW、measure 汇总宽=grip+4×button 槽位=114(去旧 116 尾部死区);间距用按钮 margin 保命中带无缝 |
7070
| `toolbar_shellhook.go` | 工具栏 Shell Hook 集成:`RegisterShellHookWindow` + 动态注册 `SHELLHOOK` 消息;拦截 `HSHELL_WINDOWENTERFULLSCREEN=53`/`HSHELL_WINDOWEXITFULLSCREEN=54` 通过 `ToolbarCallback.OnForegroundFullscreenChange` 派发 |
7171
| `popup_menu.go` | `PopupMenu`:自定义弹出菜单窗口,支持子菜单、勾选状态、主题;`Show`/`Hide`/`Destroy`;键盘导航通过全局低级键盘钩子(`WH_KEYBOARD_LL`)实现;子菜单共享父菜单渲染资源(`newPopupMenuShared`|
7272
| `popup_menu_event.go` | 弹出菜单事件处理:鼠标移动/点击/离开事件、键盘导航(`handleKeyDown`:↑↓/←→/Enter/Esc/字母快捷键)、`checkMouseState` 跨进程点击检测;`menuKbNavActive` 抑制键盘导航时的幻象鼠标事件 |

wind_input/internal/ui/toolbar_renderer.go

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,61 @@ import (
1111
"github.com/huanfeng/wind_input/pkg/theme"
1212
)
1313

14-
// Toolbar layout constants (will be scaled for DPI)
14+
// Toolbar 默认几何(逻辑像素 dp;随 DPI 缩放,主题可经 views.toolbar 逐项覆盖)。
15+
// 整条宽度不是常量,由 grip + 4×button 槽位 measure 汇总 = 10 + 4×26 = 114。
1516
const (
16-
toolbarBaseWidth = 116 // gripWidth + 4 * buttonWidth + 2 = 10 + 104 + 2 = 116
17-
toolbarBaseHeight = 30
18-
gripWidth = 10
19-
buttonWidth = 26
20-
buttonPadding = 2
17+
tbDefaultHeight = 30
18+
tbDefaultGripWidth = 10
19+
tbDefaultButtonWidth = 26 // 按钮槽位宽(含 padding),可见框 = 槽位 - 2×padding
20+
tbDefaultButtonPad = 2 // 按钮四周间距/竖向内缩(margin)
21+
tbDefaultButtonRadius = 4
22+
tbDefaultBarRadius = 6
23+
tbDefaultBarBorderW = 1 // 整条边框宽(px 发丝线,不缩放)
2124
)
2225

26+
// toolbarGeom 工具栏解析后的几何(设备像素):views.toolbar 的 *Dimension×scale,缺省=内置默认。
27+
type toolbarGeom struct {
28+
height, gripWidth, buttonWidth, buttonPadding, buttonRadius int
29+
barRadius, barBorderW int
30+
fontSize float64
31+
}
32+
33+
// resolveToolbarGeom 把主题 views.toolbar 的几何 *Dimension 按 scale 换算为设备像素,缺省回退内置默认。
34+
func resolveToolbarGeom(rv *theme.ResolvedV3, scale float64) toolbarGeom {
35+
g := toolbarGeom{
36+
height: theme.Dp(tbDefaultHeight).Scaled(scale),
37+
gripWidth: theme.Dp(tbDefaultGripWidth).Scaled(scale),
38+
buttonWidth: theme.Dp(tbDefaultButtonWidth).Scaled(scale),
39+
buttonPadding: theme.Dp(tbDefaultButtonPad).Scaled(scale),
40+
buttonRadius: theme.Dp(tbDefaultButtonRadius).Scaled(scale),
41+
barRadius: theme.Dp(tbDefaultBarRadius).Scaled(scale),
42+
barBorderW: tbDefaultBarBorderW,
43+
fontSize: 14.0 * scale,
44+
}
45+
if rv == nil || rv.Views == nil || rv.Views.Toolbar == nil {
46+
return g
47+
}
48+
t := rv.Views.Toolbar
49+
dim := func(d *theme.Dimension, def int) int {
50+
if d != nil {
51+
return d.Scaled(scale)
52+
}
53+
return def
54+
}
55+
g.height = dim(t.Height, g.height)
56+
g.gripWidth = dim(t.GripWidth, g.gripWidth)
57+
g.buttonWidth = dim(t.ButtonWidth, g.buttonWidth)
58+
g.buttonPadding = dim(t.ButtonPadding, g.buttonPadding)
59+
g.buttonRadius = dim(t.ButtonRadius, g.buttonRadius)
60+
return g
61+
}
62+
63+
// zeroMeasurer 是几何专用文本度量桩:工具栏尺寸全由 FixedW/FixedH 决定,文本度量不影响布局,
64+
// 故几何查询(computeGeometry)用它即可,免依赖文本后端——HitTest/GetToolbarSize 可在窗口创建早期安全调用。
65+
type zeroMeasurer struct{}
66+
67+
func (zeroMeasurer) MeasureString(string, float64) float64 { return 0 }
68+
2369
// ToolbarRenderer renders the toolbar UI
2470
type ToolbarRenderer struct {
2571
resolvedV3 *theme.ResolvedV3
@@ -54,9 +100,10 @@ func (r *ToolbarRenderer) getTooltipColors() (bgColor, textColor, borderColor co
54100
func (r *ToolbarRenderer) Render(state ToolbarState) *image.RGBA {
55101
scale := GetDPIScale()
56102
rtv := r.resolveToolbarViews()
103+
geom := resolveToolbarGeom(r.resolvedV3, scale)
57104
td := r.TextDrawer()
58105

59-
tt := buildToolbarTree(state, rtv, scale)
106+
tt := buildToolbarTree(state, rtv, geom)
60107
Layout(tt.root, 0, 0, td)
61108
dc, img := newSharedDrawContext(tt.root.Rect().Dx(), tt.root.Rect().Dy())
62109
PaintTree(tt.root, dc, img, td)
@@ -189,9 +236,9 @@ func viewOuterRect(v *View) image.Rectangle {
189236
// computeGeometry 用零 state/零色构建工具栏 View 树并 Layout,派生几何——命中/边界/尺寸的唯一来源。
190237
// 几何与 state/颜色无关(按钮 FixedW 固定、mode 文字不影响布局),故按需计算、无需缓存。
191238
func (r *ToolbarRenderer) computeGeometry() toolbarGeometry {
192-
scale := GetDPIScale()
193-
tt := buildToolbarTree(ToolbarState{}, theme.ResolvedToolbarViews{}, scale)
194-
Layout(tt.root, 0, 0, r.TextDrawer())
239+
geom := resolveToolbarGeom(r.resolvedV3, GetDPIScale())
240+
tt := buildToolbarTree(ToolbarState{}, theme.ResolvedToolbarViews{}, geom)
241+
Layout(tt.root, 0, 0, zeroMeasurer{})
195242
return toolbarGeometry{
196243
size: tt.root.Rect().Size(),
197244
bounds: map[ToolbarHitResult]image.Rectangle{

wind_input/internal/ui/toolbar_window.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,8 @@ func (w *ToolbarWindow) Create() error {
215215
exStyle := uint32(WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE)
216216
style := uint32(WS_POPUP)
217217

218-
// Initial size - match toolbarBaseWidth/Height in toolbar_renderer.go
219-
w.width = ScaleIntForDPI(toolbarBaseWidth)
220-
w.height = ScaleIntForDPI(toolbarBaseHeight)
218+
// Initial size from renderer geometry (single source: computeGeometry)
219+
w.width, w.height = w.renderer.GetToolbarSize()
221220

222221
hwnd, _, err := procCreateWindowExW.Call(
223222
uintptr(exStyle),
@@ -544,8 +543,7 @@ func (w *ToolbarWindow) Destroy() {
544543
func (w *ToolbarWindow) handleDPIChanged() {
545544
// Recalculate toolbar size with new DPI
546545
w.mu.Lock()
547-
w.width = ScaleIntForDPI(toolbarBaseWidth)
548-
w.height = ScaleIntForDPI(toolbarBaseHeight)
546+
w.width, w.height = w.renderer.GetToolbarSize()
549547
w.mu.Unlock()
550548

551549
// Re-render with the new DPI scale

wind_input/internal/ui/viewbox_toolbar.go

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -121,24 +121,23 @@ func modeButtonText(state ToolbarState) string {
121121
// buildToolbarTree 构建工具栏 View 树(整条 LayoutRow:grip + 4 按钮)。
122122
// 按钮框走 View(Background+Border,Stretch 撑满整条高 - margin);mode 是带背景的文本叶子;
123123
// width/punct/settings 是无 Text 的框(符号后处理)。grip 是占位框。
124-
// 几何 hardcode × scale,与现状 Render 的按钮布局逐像素一致。
125-
func buildToolbarTree(state ToolbarState, rtv theme.ResolvedToolbarViews, scale float64) *toolbarTree {
126-
gripW := int(float64(gripWidth) * scale)
127-
btnW := int(float64(buttonWidth) * scale)
128-
pad := int(float64(buttonPadding) * scale)
129-
btnRadius := int(4.0 * scale)
130-
fontSize := 14.0 * scale
124+
// 几何全部取自 toolbarGeom(来自 views.toolbar 的 *Dimension×scale,缺省=内置默认)。
125+
// root 不设 FixedW:整条宽 = grip + 4×button 槽位(measure 汇总,去除旧 116 的尾部死区→114)。
126+
// 间距用按钮 margin(margin 盒首尾相接、命中带无缝,避免 Gap 在按钮间留死区;引擎通用 Gap 候选列表在用)。
127+
func buildToolbarTree(state ToolbarState, rtv theme.ResolvedToolbarViews, g toolbarGeom) *toolbarTree {
128+
pad := g.buttonPadding
129+
inner := g.buttonWidth - pad*2 // 按钮可见框宽(槽位 - 两侧 padding)
131130

132131
// grip 占位框(Stretch 撑高,后处理画点阵)
133-
grip := &View{FixedW: gripW, Stretch: true}
132+
grip := &View{FixedW: g.gripWidth, Stretch: true}
134133

135-
// 按钮框(无 Text,符号后处理):FixedW = btnW-pad*2,margin pad,Stretch 撑高
134+
// 按钮框(无 Text,符号后处理):FixedW = inner,margin pad,Stretch 撑高
136135
mkFrame := func(bg color.Color) *View {
137136
return &View{
138-
FixedW: btnW - pad*2,
137+
FixedW: inner,
139138
Margin: Edges{Top: pad, Right: pad, Bottom: pad, Left: pad},
140139
Background: Fill{Color: bg},
141-
Border: Border{Radius: btnRadius},
140+
Border: Border{Radius: g.buttonRadius},
142141
Stretch: true,
143142
}
144143
}
@@ -150,11 +149,11 @@ func buildToolbarTree(state ToolbarState, rtv theme.ResolvedToolbarViews, scale
150149
// mode 是带背景的文本叶子(文字居中由 paintText AlignCenter + 基线居中)
151150
mode := &View{
152151
Text: modeButtonText(state),
153-
TextStyle: TextStyle{FontSize: fontSize, Color: rtv.ModeText, Align: AlignCenter},
154-
FixedW: btnW - pad*2,
152+
TextStyle: TextStyle{FontSize: g.fontSize, Color: rtv.ModeText, Align: AlignCenter},
153+
FixedW: inner,
155154
Margin: Edges{Top: pad, Right: pad, Bottom: pad, Left: pad},
156155
Background: Fill{Color: modeBg},
157-
Border: Border{Radius: btnRadius},
156+
Border: Border{Radius: g.buttonRadius},
158157
Stretch: true,
159158
}
160159

@@ -163,11 +162,10 @@ func buildToolbarTree(state ToolbarState, rtv theme.ResolvedToolbarViews, scale
163162
settings := mkFrame(rtv.SettingsBg)
164163

165164
root := &View{
166-
FixedW: int(float64(toolbarBaseWidth) * scale),
167-
FixedH: int(float64(toolbarBaseHeight) * scale),
165+
FixedH: g.height,
168166
Layout: LayoutRow,
169167
Background: Fill{Color: rtv.BarBg},
170-
Border: Border{Radius: int(6.0 * scale), Color: rtv.BarBorder, Width: 1},
168+
Border: Border{Radius: g.barRadius, Color: rtv.BarBorder, Width: g.barBorderW},
171169
Children: []*View{grip, mode, width, punct, settings},
172170
}
173171
return &toolbarTree{root: root, grip: grip, mode: mode, width: width, punct: punct, settings: settings}

wind_input/internal/ui/viewbox_toolbar_test.go

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,12 @@ func TestBuildToolbarTree_Geometry(t *testing.T) {
5050
SettingsBg: color.RGBA{230, 234, 239, 255}, SettingsIcon: color.RGBA{1, 1, 1, 255}, SettingsHole: color.RGBA{2, 2, 2, 255},
5151
}
5252
m := fixedMeasurer{charW: 10}
53-
tt := buildToolbarTree(ToolbarState{ChineseMode: true, ModeLabel: "拼"}, rtv, 1.0)
53+
geom := resolveToolbarGeom(nil, 1.0)
54+
tt := buildToolbarTree(ToolbarState{ChineseMode: true, ModeLabel: "拼"}, rtv, geom)
5455
Layout(tt.root, 0, 0, m)
55-
// 整条宽 = 116, 高 = 30(scale=1)
56-
if tt.root.Rect().Dx() != 116 || tt.root.Rect().Dy() != 30 {
57-
t.Errorf("整条尺寸应 116x30, got %dx%d", tt.root.Rect().Dx(), tt.root.Rect().Dy())
56+
// 整条宽 = 114(grip 10 + 4×26 槽位;L2 去除旧 116 尾部死区), 高 = 30(scale=1)
57+
if tt.root.Rect().Dx() != 114 || tt.root.Rect().Dy() != 30 {
58+
t.Errorf("整条尺寸应 114x30, got %dx%d", tt.root.Rect().Dx(), tt.root.Rect().Dy())
5859
}
5960
// 按钮框 Stretch 撑高 = 30 - pad*2 = 26
6061
if tt.mode.Rect().Dy() != 26 {
@@ -65,7 +66,7 @@ func TestBuildToolbarTree_Geometry(t *testing.T) {
6566
t.Errorf("中文模式 mode 背景应=ModeChineseBg, got %v", tt.mode.Background.Color)
6667
}
6768
// 英文模式
68-
tt2 := buildToolbarTree(ToolbarState{ChineseMode: false}, rtv, 1.0)
69+
tt2 := buildToolbarTree(ToolbarState{ChineseMode: false}, rtv, geom)
6970
if tt2.mode.Background.Color != (color.RGBA{115, 127, 148, 255}) {
7071
t.Errorf("英文模式 mode 背景应=ModeEnglishBg, got %v", tt2.mode.Background.Color)
7172
}
@@ -80,7 +81,7 @@ func TestBuildToolbarTree_Geometry(t *testing.T) {
8081
// 且与旧线性公式逐项等价(scale=1:grip=10, button=26, pad=2, H=30)。
8182
func TestToolbarGeometry_SingleSource(t *testing.T) {
8283
m := fixedMeasurer{charW: 10}
83-
tt := buildToolbarTree(ToolbarState{ChineseMode: true}, theme.ResolvedToolbarViews{}, 1.0)
84+
tt := buildToolbarTree(ToolbarState{ChineseMode: true}, theme.ResolvedToolbarViews{}, resolveToolbarGeom(nil, 1.0))
8485
Layout(tt.root, 0, 0, m)
8586

8687
// content 矩形(GetButtonBounds 语义)= 旧 GetButtonBounds 公式
@@ -130,8 +131,41 @@ func TestToolbarGeometry_SingleSource(t *testing.T) {
130131
prevMax = hb.Max.X
131132
}
132133

133-
// 整条尺寸(GetToolbarSize 语义)
134-
if sz := tt.root.Rect().Size(); sz.X != 116 || sz.Y != 30 {
135-
t.Errorf("整条尺寸应 116x30, got %dx%d", sz.X, sz.Y)
134+
// 整条尺寸(GetToolbarSize 语义)= grip 10 + 4×26 槽位 = 114(去尾部死区)
135+
if sz := tt.root.Rect().Size(); sz.X != 114 || sz.Y != 30 {
136+
t.Errorf("整条尺寸应 114x30, got %dx%d", sz.X, sz.Y)
137+
}
138+
}
139+
140+
// tbDim 构造 *theme.Dimension(dp)供测试覆盖几何。
141+
func tbDim(v int) *theme.Dimension { d := theme.Dp(v); return &d }
142+
143+
// TestResolveToolbarGeom_SchemaOverride 守护 L2:views.toolbar 几何字段覆盖默认、总宽随之变化;
144+
// 未覆盖项保持默认(零回归)。
145+
func TestResolveToolbarGeom_SchemaOverride(t *testing.T) {
146+
rv := &theme.ResolvedV3{Views: &theme.Views{Toolbar: &theme.ToolbarViews{
147+
GripWidth: tbDim(12),
148+
ButtonWidth: tbDim(30),
149+
}}}
150+
g := resolveToolbarGeom(rv, 1.0)
151+
if g.gripWidth != 12 || g.buttonWidth != 30 {
152+
t.Errorf("schema 覆盖未生效: gripW=%d btnW=%d", g.gripWidth, g.buttonWidth)
153+
}
154+
if g.height != 30 || g.buttonPadding != 2 || g.buttonRadius != 4 {
155+
t.Errorf("未覆盖项应保持默认: h=%d pad=%d r=%d", g.height, g.buttonPadding, g.buttonRadius)
156+
}
157+
// 总宽随之变化 = grip + 4×button 槽位
158+
tt := buildToolbarTree(ToolbarState{}, theme.ResolvedToolbarViews{}, g)
159+
Layout(tt.root, 0, 0, zeroMeasurer{})
160+
if w, want := tt.root.Rect().Dx(), 12+4*30; w != want {
161+
t.Errorf("覆盖后总宽应=%d, got %d", want, w)
162+
}
163+
}
164+
165+
// TestResolveToolbarGeom_NilDefaults 守护:rv 为 nil 时回退内置默认(grip 10 + 4×26 = 114)。
166+
func TestResolveToolbarGeom_NilDefaults(t *testing.T) {
167+
g := resolveToolbarGeom(nil, 1.0)
168+
if g.gripWidth != 10 || g.buttonWidth != 26 || g.height != 30 {
169+
t.Errorf("默认几何错误: gripW=%d btnW=%d h=%d", g.gripWidth, g.buttonWidth, g.height)
136170
}
137171
}

0 commit comments

Comments
 (0)