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(台账)。
This commit is contained in:
1 parent
4e51b41a42
commit
3529c7b44f
6 files changed
+1906
No files matched your search
@@ -0,0 +1,86 @@
|
||||
# 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. 每个阶段单独提交,提交信息写清"改了什么 / 为什么 / 怎么验证"。
|
||||
Reference in new issue
Block a user