Files
Just-DataInspector/Documents/UI_Refactor_Progress.md
T
DeepSeek V4.1 Flash 5cb84e4f97 UI 重构 P2:PlotViewPaint.cpp 按关注点分片(机械搬移,逐像素 0 差异)
PlotViewPaint.cpp 9,080 行里混住四类内容(真绘制 / 光栅化缓存 / 内容指纹 / 布局与命中几何),
按"函数名前缀"分片是上一轮膨胀的根因。本提交按关注点机械搬移函数定义,不改一行逻辑:

- 新增 PlotViewRaster.cpp(1,876 行):光晕离屏层、静态场景缓存、装饰层(AlphaBlend)、
  每网格宽基底图层/并行重建/滚动补条/预热预算。
- 新增 PlotViewCacheKey.cpp(474 行):SceneCacheKey、SceneContentHash/Geometry/Decor、
  样式与派生快照、只改外观的网格定位、样式补丁帧。
- PlotViewPaint.cpp 只留绘制:9,080 -> 6,252 行(-31%)。
- 顶层注释从「四个分片」改为「六个分片(按关注点)」,并保留
  「PlotView::Paint/PaintLane 只允许定义在 PlotViewPaint.cpp」。

pvdetail 全部 inline,故跨分片安全;无需改任何 include,Makefile 按 basename 自动收集。

验证:
- make release TARGET_FILE_NAME=TargetCheck 零警告;Target.exe 873,984 -> 875,008 B(+1KB 对齐差异)。
- 逐像素 A/B(仓库外一次性渲染哈希探针,三场景:全览 / 深放大+自由标记+均值滤波+弱峰调整+时间光标 /
  再平移一帧):分片前后哈希完全相同
  A 2166933039626F66、B C444ECFEB18FD73D、C 7984446D8E7A4418。
- Tools/uicheck 62 passed / 0 failed。

坑:用旧文件回退做 A/B 时必须先删除 Build/<Config>/PlotViewRaster.o 与 PlotViewCacheKey.o,
否则链接会因重复符号失败。
2026-10-28 12:18:42 +08:00

116 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` 零行为分片 | ✅ 完成(**逐像素 0 差异**,见 §2) |
| **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 之前引入行为差异。
---
### 2. `P2`:`PlotViewPaint.cpp` 按关注点分片(零行为变更)
**为什么**:该文件 9 080 行里混住四类内容 —— 真绘制 ≈3.5k / 光栅化缓存 ≈2.5k / 缓存键 ≈1.1k /
布局与命中几何 ≈1.3k。按"函数名前缀"分片是上一轮膨胀的直接原因,本轮按**关注点**再分一次。
**做法**:**机械搬移成员函数定义**(不改一行逻辑)。`PlotViewInternal.hpp` 里的 `pvdetail` 全部 `inline`
⇒ 跨分片安全,无需改任何 include、无需改 `Makefile`(按 basename 自动收集 `.cpp`)。
| 文件 | 搬入内容 | 行数变化 |
| --- | --- | --- |
| `PlotViewRaster.cpp`(新) | 光晕离屏层(`PaintSignalHalo*` / `UploadHaloLayer` / `ReleaseHaloLayer`)<br>场景缓存(`Ensure/Capture/Blit/Invalidate/ReleaseSceneCache`)<br>装饰层(`Ensure/Release/Render/BlitDecorCache` / `ResolveAlphaBlend`)<br>宽基底图层与预算(`EnsureLaneLayer` … `BlitWideBuffer`、并行重建、滚动补条、预热) | 1 876 |
| `PlotViewCacheKey.cpp`(新) | `MakeSceneCacheKey` / `SceneCacheMatches` / `SceneHaloRef`<br>`SceneContentHash` / `SceneGeometryHash` / `SceneDecorHash`<br>`CollectSceneStyles` / `FindStyleOnlyLanes` / `CollectSceneDerived` / `RepaintSceneCachePatch` | 474 |
| `PlotViewPaint.cpp` | 只剩绘制(`Paint*` / `Render*` / 输入框绘制) | 9 080 → **6 252**(−31%) |
**验证(关键)**
1. `make release TARGET_FILE_NAME=TargetCheck` **零警告**;`Target.exe` 873 984 → **875 008 B**(+1 KB,
属分片后各 TU 的对齐差异;仍远低于 900 KB 阈值)。
2. **逐像素 A/B**:临时写了一个渲染哈希探针(仓库外),对三个场景渲染后备缓冲区并取 FNV 哈希:
* A 默认全览;B 深放大 + 自由标记(含固定点)+ 均值滤波 + 弱峰调整 + 时间光标;C 再平移一帧(触发宽基底滚动/补条)。
* **分片前**:`A 2166933039626F66 / B C444ECFEB18FD73D / C 7984446D8E7A4418`
* **分片后**:**完全相同**(三个场景逐一一致)。
* 该探针未入库(一次性验证用),结论记录在此以便复现。
3. `Tools/uicheck` 62/62 全绿。
4. 顶层注释从"四个分片"更新为"六个分片(按关注点)",并写明"`Paint/PaintLane` 仍只允许定义在 `PlotViewPaint.cpp`"。
**⚠️ 复现 A/B 的坑**:机械搬移后用旧的 `.bak` 回退时,`Build/<Config>/` 里会残留
`PlotViewRaster.o` / `PlotViewCacheKey.o`,链接探针(`Build/Release/*.o`)会因重复符号失败 ——
**必须先删掉这两个陈旧 `.o`** 再链接。
---
## 下一步(接手者请从这里开始)
1. **P2 续**:把"布局与命中几何"也搬出去(`EdgePanelLayoutFor` 651 行 + `ProbeFreeMarker` 286 行 +
`FreeMarkerLabelRects` 118 行 → 新 `PlotViewPanels.cpp`),判据同上(逐像素 0 差异)。
2. **P3:Toolbar 优先**(最小、最容易验证的组件),迁移后跑 `uicheck` + 新旧像素对比。
3. 每个阶段单独提交,提交信息写清"改了什么 / 为什么 / 怎么验证"。