# Fix-BUG-20260409-0001 > 适用场景:记录某个 BUG 的修复方案、影响评估、验证结果与落地信息。 ## 关联信息 - Fix ID: Fix-BUG-20260409-0001 - 关联 BUG ID: BUG-20260409-0001 - 修复目标: 收紧对话框快照、遮挡、事件分发和统一提交重绘链 - 状态:已完成 / 待持续回归 - 负责人:Codex 协作修改 - 分支 / 版本:`master` ## 根因分析 - 根因: - 旧实现中“作废快照”和“回贴旧背景”混在一起,resize 时容易把旧画面贴回屏幕。 - 非模态对话框与底层控件之间缺少明确的事件阻断和 hover 清理机制。 - 重绘长期依赖控件即时局部绘制,导致底层先画、对话框后补,容易闪烁或层级错乱。 - 标题曾作为独立 `Label` 存在,导致额外一层背景快照,时序敏感。 - 触发条件: - 非模态遮挡下的 hover / click - 模态或非模态 resize - 对话框打开 / 关闭 - 为什么之前没发现: - 问题主要集中在“高频交互 + 对话框遮挡 + resize”组合路径 ## 修复方案 - 修复思路: - 拆分快照语义 - 收紧对话框事件消费 - 让 `Dialog` 在窗口变化时只重新居中 - 将标题绘制并入 `Dialog` - 把重绘推进到窗口统一收口 - 关键改动: - `discardBackground()` 与 `invalidateBackgroundSnapshot()` 分离 - `Dialog` 标题改为直接绘制,不再使用独立 `Label` - `Dialog::handleEvent()` 对自身区域内鼠标事件统一吞掉 - 对话框关闭及遮挡场景补发合成 `WM_MOUSEMOVE`,清理底层 hover - `Window` 引入托管重绘,先重绘受影响 root,再补画相交对话框 - 涉及文件 / 类 / 函数: - `Window.h / Window.cpp` - `Dialog.h / Dialog.cpp` - `Control.h / Control.cpp` - `Canvas.cpp` - `TabControl.cpp` - `Button.cpp` - `TextBox.cpp` - 影响的 API / 行为: - `Dialog` 在窗口变化时的语义固定为“只重新居中,不拉伸” - 非模态对话框覆盖区域内不再允许鼠标事件穿透到底层 - 关键约束 / 不变量: - 对话框始终绘制在普通控件之上 - 托管分发期间控件不直接提交绘制 - resize 和对话框开关仍走整场景重绘兜底 ## 影响评估 - 影响范围: - 所有包含 `Dialog`、按钮 hover、遮挡重绘的场景 - 兼容性影响:有(行为更严格,底层控件不再穿透响应) - 行为变化:有(对话框 resize 只居中;遮挡区域 hover 不再透传) - 性能影响:有正向变化(减少无意义整场景补画);同时在某些兜底场景仍保留整场景重绘 - 回归风险: - 托管重绘 root 选择不当可能导致嵌套容器局部重绘异常 - 需持续回归 `Canvas / TabControl / Table / Dialog` 组合场景 ## 验证结果 - 验证步骤: 1. 在 `KEY == 2 / 4` 中打开非模态对话框,快速在遮挡区域附近 hover 和点击。 2. 在 `KEY == 3 / 4` 中打开模态对话框并拖动窗口大小。 3. 关闭对话框后观察底层按钮 hover 恢复。 - 验证结果: - 穿透、残影、关闭后 hover 不恢复等主问题已压住。 - 回归检查: - 保留对“快速划过多个按钮时偶发一帧双高亮”的已知限制记录。 - 验证证据: - 静态推演 + 多轮编译验证 + 用户回归反馈 ## 落地信息 - Commit: - `4a6e153` - `7f8431a` - `b07a4ec` - 当前工作区未提交阶段 - PR:[可选] - 发布版本:[可选] - 备注:该修复跨多个阶段逐步收敛,不是一次性完成