Skip to content

Commit 194ecd7

Browse files
committed
fix(theme): mergeViewNode 修复 PrevChar/NextChar 两处 bug
Bug 1:PrevChar/NextChar 从未出现在 mergeViewNode 中,子主题 配置的翻页字符在 deepMerge 后静默丢失,无论怎么配置都不生效。 Bug 2:_base 的 prev_image/next_image 通过继承到达子主题后, 图片优先级(img != nil → useChar=false)会永远压制子主题配置 的 prev_char/next_char,字符配置形同虚设。 修复:在 mergeViewNode 中将 prev_image 与 prev_char(next 同理) 设计为互斥字段——子主题显式设置字符时清除继承来的图片,显式设置 图片时清除继承来的字符,两者均未配置时保留 base 的设置。
1 parent f25ba3e commit 194ecd7

2 files changed

Lines changed: 64 additions & 4 deletions

File tree

wind_input/pkg/theme/footer_image_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,56 @@ func TestFooterImagesInheritedFromBase(t *testing.T) {
5959
}
6060
}
6161

62+
// TestMergeViewNode_CharClearsInheritedImage 守护回归:子主题显式设置 PrevChar/NextChar 时
63+
// 必须清除从 base 继承来的 PrevImage/NextImage,否则图片优先级会永远压制字符配置。
64+
func TestMergeViewNode_CharClearsInheritedImage(t *testing.T) {
65+
base := ViewNode{
66+
PrevImage: &ViewImage{Ref: "chevron_prev.svg"},
67+
NextImage: &ViewImage{Ref: "chevron_next.svg"},
68+
}
69+
customPrev := "◀"
70+
customNext := "▶"
71+
ov := ViewNode{
72+
PrevChar: &customPrev,
73+
NextChar: &customNext,
74+
}
75+
out := mergeViewNode(base, ov)
76+
if out.PrevImage != nil {
77+
t.Errorf("子主题配了 PrevChar,应清除继承的 PrevImage,got %+v", out.PrevImage)
78+
}
79+
if out.NextImage != nil {
80+
t.Errorf("子主题配了 NextChar,应清除继承的 NextImage,got %+v", out.NextImage)
81+
}
82+
if out.PrevChar == nil || *out.PrevChar != customPrev {
83+
t.Errorf("PrevChar 应为 %q,got %v", customPrev, out.PrevChar)
84+
}
85+
if out.NextChar == nil || *out.NextChar != customNext {
86+
t.Errorf("NextChar 应为 %q,got %v", customNext, out.NextChar)
87+
}
88+
}
89+
90+
// TestMergeViewNode_ImageClearsInheritedChar 守护回归:子主题显式设置 PrevImage/NextImage 时
91+
// 应清除 base 可能存在的字符(保持互斥语义)。
92+
func TestMergeViewNode_ImageClearsInheritedChar(t *testing.T) {
93+
prev := "◀"
94+
next := "▶"
95+
base := ViewNode{PrevChar: &prev, NextChar: &next}
96+
ov := ViewNode{
97+
PrevImage: &ViewImage{Ref: "arrow_left.svg"},
98+
NextImage: &ViewImage{Ref: "arrow_right.svg"},
99+
}
100+
out := mergeViewNode(base, ov)
101+
if out.PrevChar != nil {
102+
t.Errorf("子主题配了 PrevImage,应清除继承的 PrevChar,got %v", out.PrevChar)
103+
}
104+
if out.NextChar != nil {
105+
t.Errorf("子主题配了 NextImage,应清除继承的 NextChar,got %v", out.NextChar)
106+
}
107+
if out.PrevImage == nil || out.PrevImage.Ref != "arrow_left.svg" {
108+
t.Errorf("PrevImage 应保留,got %v", out.PrevImage)
109+
}
110+
}
111+
62112
// TestMergeViewNode_FooterImages 守护回归:footer_bar 的 prev_image/next_image 必须能通过
63113
// base 单链继承的 mergeViewNode 保留(曾漏合并这两个字段,导致薄主题配的翻页箭头图被静默丢弃)。
64114
func TestMergeViewNode_FooterImages(t *testing.T) {

wind_input/pkg/theme/views.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,9 @@ type ViewNode struct {
130130
ColGap *Dimension `yaml:"col_gap,omitempty"` // 多列列间距(仅 tooltip,兜底 16)
131131
TitleGap *Dimension `yaml:"title_gap,omitempty"` // 标题与正文间距(仅 toast,兜底 6)
132132

133-
// 状态态 patch(递归 ViewNode)。渲染只消费状态态的颜色/边框/字重覆盖;
134-
// padding/margin/font_size 在状态态**不渲染**(见 capability `state_geometry`=unsupported,
135-
// 以及 resolveState 的"有无覆盖"判定 + effectiveNode 的合并范围)。需要状态态改几何时,
136-
// 须先补齐这两处消费再把 state_geometry 转 supported。
133+
// 状态态 patch(递归 ViewNode)。渲染消费状态态的颜色/背景图/渐变/边框/字体/层覆盖;
134+
// **唯几何(padding/margin/font_size)不渲染**——状态改几何会牵动行高/列宽致候选框跳动
135+
// (capability `state_geometry`=unsupported;见 resolveState 判定 + effectiveNode 合并范围)。
137136
Selected *ViewNode `yaml:"selected,omitempty"`
138137
Hover *ViewNode `yaml:"hover,omitempty"`
139138
Disabled *ViewNode `yaml:"disabled,omitempty"` // P7-D:禁用态 patch(候选项暂无运行时触发器,schema 预留)
@@ -471,11 +470,22 @@ func mergeViewNode(base, ov ViewNode) ViewNode {
471470
if ov.HeightRatio != nil {
472471
out.HeightRatio = ov.HeightRatio
473472
}
473+
// prev_image / next_image 与 prev_char / next_char 互斥:
474+
// 子主题显式配了字符 → 表示"用字符替代图片",清除继承来的图片(反之亦然)。
475+
// 两者均未配 → 保留 base 的设置。
474476
if ov.PrevImage != nil {
475477
out.PrevImage = ov.PrevImage
478+
out.PrevChar = nil // 图片优先,清除 base 可能继承来的字符
479+
} else if ov.PrevChar != nil {
480+
out.PrevChar = ov.PrevChar
481+
out.PrevImage = nil // 字符优先,清除 base 继承来的图片
476482
}
477483
if ov.NextImage != nil {
478484
out.NextImage = ov.NextImage
485+
out.NextChar = nil
486+
} else if ov.NextChar != nil {
487+
out.NextChar = ov.NextChar
488+
out.NextImage = nil
479489
}
480490
if ov.LineSpacing != nil {
481491
out.LineSpacing = ov.LineSpacing

0 commit comments

Comments
 (0)