发布前托管重绘与布局封版收口

收口 Dialog/overlay 后鼠标状态同步、Tooltip 临时 coverage 与持久 coverage 拆分、跨 root 脏区补提交、TextBox/Button 绘制副作用修复,并补充 KEY6 回归用例和 BUG/Fix/Feature 开发记录。
This commit is contained in:
Codex
2026-05-17 00:26:08 +08:00
parent 2388f22c99
commit 9155a86a8a
26 changed files with 1355 additions and 175 deletions
@@ -0,0 +1,55 @@
# Fix-BUG-20260511-0009
> 适用场景:记录某个 BUG 的修复方案、影响评估、验证结果与落地信息。
## 关联信息
- Fix ID: Fix-BUG-20260511-0009
- 关联 BUG ID: BUG-20260511-0009
- 修复目标: 保证 `TabControl::indexOf()` 未命中时返回 `-1`
- 状态:已完成
- 负责人: Codex
- 分支 / 版本: 当前工作区
## 根因分析
- 根因: `indexOf()` 循环结束后直接返回 `idx`,没有显式处理未命中分支。
- 触发条件: 查询不存在的页签文本。
- 为什么之前没发现:[可选] 当前测试更多关注页签切换行为,未覆盖未命中返回值。
- 关键证据:[可选] `TabControl.cpp` 旧实现未命中路径返回最后索引。
## 修复方案
- 修复思路: 未找到匹配项时显式返回 `-1`
- 关键改动: 将 `TabControl::indexOf()` 末尾返回值从 `idx` 改为 `-1`
- 涉及文件 / 类 / 函数: `TabControl.cpp::TabControl::indexOf`
- 影响的 API / 行为:[可选] 行为修正;符合函数语义。
- 关键约束 / 不变量:[可选] 找到时仍返回真实索引。
- 回滚点 / 开关:[可选] 无
## 影响评估
- 影响范围: 仅影响 `indexOf()` 未命中返回值。
- 兼容性影响:无
- 行为变化:有(未命中从最后索引修正为 `-1`
- 性能影响:无
- 回归风险: 低
## 验证结果
- 验证步骤:
1. 编译 `TabControl.cpp`
2. 编译 `z-testDome.cpp``KEY1 ~ KEY6`
3. 静态确认未命中路径返回 `-1`
- 验证结果: 编译通过
- 回归检查:[可选] KEY1 ~ KEY6 编译级回归通过
- 验证证据:[可选]
## 落地信息
- Commit: 未提交
- PR[可选]
- 发布版本:[可选]
- 备注:[可选]
@@ -0,0 +1,57 @@
# Fix-BUG-20260511-0010
> 适用场景:记录某个 BUG 的修复方案、影响评估、验证结果与落地信息。
## 关联信息
- Fix ID: Fix-BUG-20260511-0010
- 关联 BUG ID: BUG-20260511-0010
- 修复目标: 避免局部 root 内部 coverage 扩张后漏补 Dialog / 上层 overlay
- 状态:已完成
- 负责人: Codex
- 分支 / 版本: 当前工作区
## 根因分析
- 根因: `commitManagedRepaint()` 只执行绘制,不返回本轮实际写屏 coverage;Window 仍按登记时的初始 coverage 进行上层补画判断。
- 触发条件: Canvas / TabControl 内部局部提交中补画了后序兄弟控件,实际写屏范围大于初始 source coverage。
- 为什么之前没发现:[可选] 需要 Dialog、Tooltip、后序兄弟补画三者同时满足。
- 关键证据:[可选] 临时日志中 `Canvas partial end coverage` 已与 Dialog 相交,但 `dialog overlay check` 使用旧 working coverage 结果为不相交。
## 修复方案
- 修复思路: 在 Window 层进行保守兜底。root 局部提交完成后,如果 root 仍支持局部提交,则将 root 当前 managed coverage 并入上层补画判断 coverage。
- 关键改动:
- `Window::flushManagedRepaint()``root->commitManagedRepaint()` 后合并 `root->getManagedRepaintCoverageRect()`
- 只扩大上层补画判断,不改变 root 自身局部提交策略。
- 涉及文件 / 类 / 函数: `Window.cpp::flushManagedRepaint`
- 影响的 API / 行为:[可选] 无公开 API 变化。
- 关键约束 / 不变量:[可选] Dialog 仍保持顶层 overlay 语义。
- 回滚点 / 开关:[可选] 回退该 coverage 合并逻辑。
## 影响评估
- 影响范围: 托管重绘后的上层普通控件 / Dialog 补画判断。
- 兼容性影响:无
- 行为变化:有(Dialog / 上层控件补画更保守)
- 性能影响:有(可能多补画少量 overlay)
- 回归风险: 中低;主要风险是局部 root 大范围覆盖时上层补画次数增加。
## 验证结果
- 验证步骤:
1. 编译 `Window.cpp / Canvas.cpp`
2. 编译 `z-testDome.cpp /DKEY=6`
3. 编译 `z-testDome.cpp``KEY1 ~ KEY6`
- 验证结果: 编译通过;临时日志已删除
- 回归检查:[可选] KEY6 非模态 Dialog + Tooltip 覆盖链待用户手测
- 验证证据:[可选]
## 落地信息
- Commit: 未提交
- PR[可选]
- 发布版本:[可选]
- 备注: 下版本建议让 `commitManagedRepaint()` 返回 actual coverage,替代 root 全覆盖兜底。
@@ -0,0 +1,60 @@
# Fix-BUG-20260511-0011
> 适用场景:记录某个 BUG 的修复方案、影响评估、验证结果与落地信息。
## 关联信息
- Fix ID: Fix-BUG-20260511-0011
- 关联 BUG ID: BUG-20260511-0011
- 修复目标: 事件尾补收集跨 root dirty 子树,保证同轮托管提交
- 状态:已完成
- 负责人: Codex
- 分支 / 版本: 当前工作区
## 根因分析
- 根因: `Window::requestManagedRepaint()` 只登记当前事件链触发的 source/root;回调里直接修改其他 root 的控件时,该 root 只变 dirty,没有进入 `managedRepaintItems`
- 触发条件: 点击一个 root 中的控件,回调修改另一个 root 下的 Label / 控件状态。
- 为什么之前没发现:[可选] 需要跨 root 回调才能稳定暴露。
- 关键证据:[可选] 下一次鼠标消息后 Label 才刷新,说明目标 root dirty 状态滞留。
## 修复方案
- 修复思路: 在普通输入事件收口阶段、`flushManagedRepaint()` 前扫描所有顶层普通控件和可见 Dialog,发现未登记但存在 dirty 子树的 root,则补登记为托管重绘项。
- 关键改动:
- 新增 `Window::collectDirtyRootsForManagedRepaint()`
- `runEventLoop()``flushManagedRepaint()` 前调用该函数。
- 涉及文件 / 类 / 函数:
- `Window.h`
- `Window.cpp::collectDirtyRootsForManagedRepaint`
- `Window.cpp::runEventLoop`
- 影响的 API / 行为:[可选] 无公开 API 变化。
- 关键约束 / 不变量:[可选] 只处理可见 root;已登记 root 不重复登记。
- 回滚点 / 开关:[可选] 移除事件尾补收集调用。
## 影响评估
- 影响范围: 普通输入事件尾的托管重绘收口。
- 兼容性影响:无
- 行为变化:有(跨 root dirty 会更及时刷新)
- 性能影响:有(事件尾多一次顶层 dirty 子树扫描)
- 回归风险: 中;如果存在历史残留 dirty,可能更早暴露重绘问题。
## 验证结果
- 验证步骤:
1. 编译 `Window.cpp`
2. 编译 `z-testDome.cpp /DKEY=6`
3. 编译 `z-testDome.cpp``KEY1 ~ KEY6`
- 验证结果: 编译通过
- 回归检查:[可选] KEY6 Right 页内按钮更新 A 区状态 Label 待用户手测
- 验证证据:[可选]
## 落地信息
- Commit: 未提交
- PR[可选]
- 发布版本:[可选]
- 备注:[可选]
@@ -0,0 +1,68 @@
# Fix-BUG-20260511-0012
> 适用场景:记录某个 BUG 的修复方案、影响评估、验证结果与落地信息。
## 关联信息
- Fix ID: Fix-BUG-20260511-0012
- 关联 BUG ID: BUG-20260511-0012
- 修复目标: 区分 Tooltip 临时浮层 coverage 与会污染背景快照的持久 coverage
- 状态:已完成
- 负责人: Codex
- 分支 / 版本: 当前工作区
## 根因分析
- 根因: 托管重绘只有一类 coverage,既用于完整绘制范围判断,也用于兄弟控件背景快照刷新判断。Tooltip 属于临时浮层,不应进入后者。
- 触发条件: Tooltip coverage 覆盖到后序兄弟控件,兄弟控件被局部 overlay 补画。
- 为什么之前没发现:[可选] 需要 Tooltip 与透明 Label 或带快照兄弟控件重叠。
- 关键证据:[可选] 透明 Label 已设置 `setTextdisap(true)`,但 `draw()` 仍会先回贴背景快照。
## 修复方案
- 修复思路: 拆分完整 coverage 与持久 coverage。Tooltip 进入完整 coverage,用于 Window/Dialog overlay 判断;但不进入持久 coverage,避免兄弟控件刷新快照时捕获 Tooltip。
- 关键改动:
- `Control` 新增内部虚函数 `getManagedRepaintPersistentCoverageRect()`
- `Button` 的完整 coverage 包含 Tooltip,持久 coverage 仅包含按钮本体。
- `Canvas / TabControl / Table` 递归合并子控件持久 coverage。
- `Canvas / TabControl` 局部提交中,只有命中持久 coverage 的兄弟控件才作废快照并补画。
- 只命中 Tooltip 等临时 coverage 的兄弟控件不再补画,避免透明控件回贴旧快照擦掉 Tooltip。
- 涉及文件 / 类 / 函数:
- `Control.h / Control.cpp`
- `Button.h / Button.cpp`
- `Canvas.h / Canvas.cpp`
- `TabControl.h / TabControl.cpp`
- `Table.h / Table.cpp`
- 影响的 API / 行为:[可选] 新增内部 virtual 接口,无公开用户 API 变化。
- 关键约束 / 不变量:[可选] Tooltip 智能选位仍不在本轮范围内。
- 回滚点 / 开关:[可选] 回退持久 coverage 拆分逻辑。
## 影响评估
- 影响范围: Tooltip、Canvas / TabControl 局部 overlay 补画、背景快照捕获。
- 兼容性影响:无
- 行为变化:有(Tooltip 作为临时浮层时,不再触发普通兄弟控件补画)
- 性能影响:轻微(多维护一组矩形并集和相交判断)
- 回归风险:
- 如果某控件持久 coverage 定义过小,可能漏补兄弟快照。
- Tooltip 会短暂显示在同容器后序普通控件上方,这是本轮有意选择。
## 验证结果
- 验证步骤:
1. 编译 `Control.cpp / Button.cpp / Canvas.cpp / TabControl.cpp / Table.cpp / Window.cpp`
2. 编译 `z-testDome.cpp``KEY1 ~ KEY6`
3. 手测 KEY6 A 区禁用按钮 Tooltip 与状态 Label 交叠。
4. 手测 TabControl 页签 / 页面内 Tooltip。
- 验证结果: 编译通过;GUI 行为待用户手测
- 回归检查:[可选] KEY6 A 区、KEY5 Tooltip/overlay、KEY4 Dialog overlay
- 验证证据:[可选]
## 落地信息
- Commit: 未提交
- PR[可选]
- 发布版本:[可选]
- 备注: 下版本可进一步引入 actual coverage 返回值和正式 coverage 诊断日志。