Files
Just-DataInspector/Documents/UI_Refactor_Progress.md
T
DeepSeek V4.1 Flash 3529c7b44f UI 重构 P0+P1:新增布局/控件基础层与离屏回归网(对产品像素零影响)
新增 Components/Shared 下的 UI 基础层:
- UiLayout:矩形原语 / 对齐 / 设备像素内侧对齐 / Stack / Flow / 布局缓存(deque 保证引用稳定、
  定向失效、按最久未命中淘汰)。
- UiWidget:控件树基类。两段式布局(Measure 自下而上带缓存、Arrange 自上而下)、
  脏区下探绘制(dirty 为空=全画;与子树矩形无交集=整棵跳过;有效裁剪=祖先裁剪切∩自身∩脏区)、
  只沿命中链派发的冒泡模型(绝不分发给同层兄弟)、capture/modal/hover/focus 仲裁、
  光标认领链、统一动画 tick、内容指纹入口。

新增 Tools/uicheck 离屏回归网(62 项断言,全程不创建窗口、不注入输入):
- 布局数学:对齐/STRETCH/超尺寸夹取、Stack 固定与自动均分且绝不产生负尺寸、Flow 换行与 overflow。
- 缓存:引用稳定、定向失效、容量淘汰。
- 绘制:全画 / 子树跳过 / 裁剪收窄 / 不可见跳过 / 失效冒泡。
- 派发:重叠子节点只投最上层、hit-transparent 穿透、消费不上抛、capture 越过边界仍投递、
  仅持有者可释放、modal 屏蔽其余子树、祖先兜底、光标由最上层认领、键盘沿焦点链。
- 动画:时间戳统一下发、活跃状态汇总。

过程中修正自身两处缺陷:补齐 AdvanceAnimationSelf/AnimationActiveSelf 声明;
把"遍历同层兄弟"改为"只沿命中链派发 + 祖先兜底"(否则一次点击可能被视觉上位于下方的控件也处理)。

验证:make release TARGET_FILE_NAME=TargetCheck 零警告(873,984 B,低于 900KB 阈值);
Tools/uicheck 62 passed / 0 failed。

本提交不改动任何现有组件,Toolbar/TabBar/SignalPanel/PlotView 仍走旧路径,产品像素零变化。
文档:Documents/UI_Refactor_Plan.md(蓝图)、Documents/UI_Refactor_Progress.md(台账)。
2026-10-28 12:15:41 +08:00

4.9 KiB
Raw Blame History

UI 重构进度(UI_Refactor 分支)

本文件是跨会话/跨 Agent 的交接台账。每次提交前更新;每一行都要能对应到一个可复跑的验证结果。 计划见 Documents/UI_Refactor_Plan.md;硬约束见 Documents/agent_mem.md §11.2。

分支状态

分支 基线 用途
main df715a0 可发布,不受本次重构影响
dev 4e51b41(删除过时报告 refactor_report_2026-10-16.md) 日常开发基线
UI_Refactor 从 dev 切出 本次 UI 重构(全部提交只留在本地)

阶段看板

阶段 内容 状态
P0 离屏回归网 Tools/uicheck(入库) ✅ 完成(62 项断言全绿)
P1 UiLayout + UiWidget 基础层 ✅ 完成(编译零警告,uicheck 覆盖)
P2 PlotViewPaint.cpp 零行为分片 ⏳ 待做
P3 迁移 chrome(Toolbar / TabBar / 菜单 / 拾色器 / 列表行) ⏳ 待做
P4 迁移 5 个浮动面板 ⏳ 待做
P5 波形管线包成叶子 surface + 共享 DIB ⏳ 待做

提交记录

1. P0 + P1:UI 基础层与离屏回归网

新增文件

文件 行数 职责
Components/Shared/Inc/UiLayout.hpp ~250 布局原语 + UiLayoutCache
Components/Shared/Src/UiLayout.cpp ~300 矩形原语 / 对齐 / Stack / Flow / 缓存
Components/Shared/Inc/UiWidget.hpp ~330 控件基类接口与设计说明
Components/Shared/Src/UiWidget.cpp ~560 树 / Measure·Arrange / Paint(脏区+裁剪)/ 派发 / 仲裁 / 动画
Tools/uicheck/main.cpp ~570 离屏回归网(62 项断言)
Tools/uicheck/build.ps1 ~70 链接 Build/<Config>/*.o(排除 Main.o)

已实现并验证的行为

  • 布局:UiAlignInRect(含 STRETCH、超尺寸夹取、偏移后置)、UiRectSnapInward(设备像素内侧对齐)、 UiStackRects(固定 / kUiSizeAuto 均分 / 绝不产生负尺寸)、UiFlowRects(换行 + overflow 下标)。
  • 缓存:UiLayoutCache 用 std::deque 保证引用稳定;容量淘汰按"最久未命中";Invalidate 为定向失效。
  • 布局 pass:Measure 命中缓存不重复调用 OnMeasure;InvalidateMeasure 只沿祖先链失效(兄弟不重算)。
  • 绘制:dirty 为空 = 全画;与子树矩形无交集 ⇒ 整棵子树跳过;有效裁剪 = 祖先裁剪切 ∩ 自身矩形 ∩ 脏区; 不可见节点跳过。
  • 派发:只沿命中链(最深的命中节点 → 祖先冒泡),绝不分发给同层兄弟;capture 期间事件直投捕获者; modal 只向模态子树派发;hit-transparent 让事件穿透;QUERY_CURSOR 由最上层认领者返回; 键盘/字符沿焦点链派发(无焦点交给根,保证宿主级快捷键可用)。
  • 动画:AdvanceAnimation 逐节点下发同一时间戳并汇总"是否仍有动画"。
  • 悬停:进入 / 离开各只触发一次,跨子节点迁移时正确配对。

过程中修掉的两个自身缺陷

  1. UiWidget 缺 AdvanceAnimationSelf / AnimationActiveSelf 两个 protected 虚函数(.cpp 已引用、头文件漏声明)。
  2. 最初的派发实现是"遍历同层兄弟、未消费就继续" ⇒ 一个点击可能同时被视觉上位于下方的控件处理; 已改为"只沿命中链派发 + 祖先兜底"。回归网里对应的断言从"绝对次数"改为增量断言 (绝对次数会被前面用例计数污染,本轮踩过一次)。

验证

make release TARGET_FILE_NAME=TargetCheck      # 零警告
powershell -File Tools/uicheck/build.ps1       # 链接 Build/Release/*.o(排除 Main.o)
Tools/uicheck/uicheck.exe                      # 62 passed, 0 failed

未做(有意)

  • 未改动任何现有组件(Toolbar / TabBar / SignalPanel / PlotView 仍走旧路径),因此本提交对产品像素零影响。
  • 未把 UiWidget 接入 Application,避免在 P2 之前引入行为差异。

下一步(接手者请从这里开始)

  1. P2:PlotViewPaint.cpp 零行为分片(9 080 行)
    • 目标分片:Paint(只画)/ Raster(缓存与预算)/ CacheKey(指纹)/ Geometry(面板与命中几何)。
    • 做法:机械搬移函数定义,不改一行逻辑;PlotViewInternal.hpp 里的 pvdetail 全是 inline,跨分片安全。
    • 判据:make release 零警告 + Target.exe 体积变化 < 1KB + 场景哈希/像素快照与迁移前一致。
    • ⚠️ 搬移后务必确认没有残留的 static 文件级变量(只在 PlotViewPaint.cpp 内可见的辅助函数要一并搬走)。
  2. P3:Toolbar 优先(最小、最容易验证的组件),迁移后跑 uicheck + 新旧像素对比。
  3. 每个阶段单独提交,提交信息写清"改了什么 / 为什么 / 怎么验证"。