# Just DataInspector UI 重构 —— 计划 > 本文档只描述**要做什么、为什么这么做、怎么验收**。 > 已完成情况见 `UI_Refactor_Progress.md`,结论与遗留见 `UI_Refactor_Report.md`。 > 最后更新:2026-10-29 --- ## 1. 目标与非目标 ### 1.1 目标 | # | 目标 | 判据 | | --- | --- | --- | | G1 | 收敛"宿主点名每个组件"的散乱结构 | 宿主只遍历一张组件表,不再 `if (component == plot)` 式分支 | | G2 | 几何只有**一处**来源 | "画出来的形状"与"能点到的区域"由同一批函数给出,不再各算一遍 | | G3 | 源码按**职责**分片,单片可读 | 单个 `.cpp` 不超过约 1200 行;文件名与内容一致 | | G4 | 减小产物体积,且**仍然只有一个 `.exe`** | 体积比基线小,无新增依赖 DLL | | G5 | 关闭 C++ 异常后**不牺牲健壮性** | 所有大块分配前有预检验;OOM 有可见提示 | | G6 | 重构**不得改变任何绘制像素** | 8 个场景的逐像素哈希与基线逐一相同 | ### 1.2 非目标(明确不做) * **不把编译产物拆成 DLL**:本项目的定位是"单文件、拷过去就能跑的轻量工具"。 任何以拆 DLL 换体积的方案一律否决。 * **不改优化等级**(`-Oz` 保持不变)。 * **不把"波形"与"网格线"拆成两层位图**:像素级平移不变量会被破坏。 * **不为体积牺牲可维护性**(例如放弃分片、放弃几何同源)。 --- ## 2. 硬约束 1. **单一可执行文件**:不得引入额外的 `.dll` / 运行时依赖。 2. **体积预警阈值 = 1MB(1,048,576 B)**。这不是硬上限:超过仍可发布, 但必须主动提醒用户,并说明"超在哪一段(`.text` / `.rdata` / `.xdata`+`.pdata` / `.rsrc`)、 由哪个改动贡献"。旧的 900KB / 800KB 红线**已作废**。 3. **异常处理用预检验,不用 try/catch**:`-fno-exceptions` 下 `try/catch` 不会被编译, 因此所有"按数据规模预留大块内存"的地方必须先问 `MemReserveIfRoom()`。 4. **像素级零回归**:验收以 `Tools/rendercheck` 的 8 个场景哈希为准。 5. **中文注释按 UTF-8 无 BOM 保存**;构建参数用 `-finput-charset=UTF-8 -fexec-charset=GBK`。 --- ## 3. 分层架构(L0–L3) ``` L3 体积与异常策略 -fno-exceptions / MemProbe / LTO / gc-sections ▲ L2 重绘范围控制 UiDamage(脏矩形) ← 本轮评估后**缓做**,见 §5 ▲ L1 几何产物层 UiBox / UiBoxTable / UiComponent::BuildBoxes ▲ L0 组件协议 UiComponent + 组件表 + UiWidget 控件树 ``` ### L0 组件协议(前几轮已完成) * `UiComponent`:`SetScale / Layout / Area / Paint / HitTest / OnMouseLeave / IsEditing / QueryCursor`。 * 宿主持有一张**有序组件表**,逐个遍历;新增组件不必改宿主的 5 处消息分支。 * `UiWidget` 与 `UiComponent` 是**兄弟**而非父子:前者管"一块区域内部的布局/命中/绘制", 后者管"自己这一整块地盘:区域、缓存、局部重绘策略"。 `Toolbar` / `TabBar` 同时是两者。 ### L1 几何产物层(本轮完成) * 新文件 `Components/Shared/Inc/UiBox.hpp` + `Src/UiBox.cpp`。 * `struct UiBox { rect, clip, depth, layer }`:**纯值类型**,不继承任何东西。 * `class UiBoxTable`:几何收集器,只维护"矩形 / 层级 / 绘制层 / 裁剪 / 遮挡"这些**几何事实**, **不含任何绘制代码**。 * `UiComponent::BuildBoxes(UiBoxTable&)`:组件上报自己的几何产物(默认只登记 `Area()`)。 * `UiDebugBounds` 退化为**消费者**:`UiDebugPaintBoxList(g, table)` 把几何画成彩色包角线。 ⇒ 组件不再需要知道"调试"这个概念存在。 ### L2 重绘范围控制(缓做 —— 见 §5) * 设计意图:`UiDamage`(最多 12 个矩形的脏区集合,超限退化为包围盒)替代 "全窗重绘 / 绘图区重绘"的二值分支;`PlotView::Paint(g, UiDamage&)` 逐路径累加脏区。 * 收益:去掉 `SetForceFullRepaint`(布局边界开关打开时的整幅重绘)。 ### L3 体积与异常策略(本轮完成) 见 `UI_Refactor_Progress.md` 的实测表。 --- ## 4. 阶段与验收标准 | 阶段 | 内容 | 验收 | | --- | --- | --- | | P1–P6 | 文件按职责拆分、`UiComponent` 协议、组件表、布局边界功能 | 编译 + `uicheck` | | P7 | 布局边界"几何同源"(`RowRect` 前缀和、文本墨迹矩形、浮层层级/遮挡/裁剪) | `lbcovr` / `lbzorder` / `lbresid` / `lbpanel` | | P8 | 体积:`-fno-exceptions` + `MemProbe` + `-ffunction-sections` + `--gc-sections` | 体积下降且 8 哈希不变 | | P9 | 体积:LTO(含 `-flto-partition=one` 稳定性约束) | 同上,且可重复构建 | | P10 | L1 几何产物层(`UiBox` / `UiBoxTable` / `BuildBoxes`) | 同上,且源码零残留旧名 | | P11 | 代码审计(过时注释 / 名实不符 / 死代码) | 零 warning + 全量回归 | | P12 | 文档重建 + 本地提交 | `git status` 干净 | **统一验收命令** ```powershell make clean make release TARGET_FILE_NAME=JustDataInspector -j16 # 期望零 error / 零 warning make debug TARGET_FILE_NAME=JustDataInspectorD -j16 # 期望零 warning objdump -h Build\Release\JustDataInspector.exe # 段构成,用于体积溯源 Tools\uicheck\uicheck.exe # 69/69 .preflight\lbcheck\lbcheck.exe # 21/21 .preflight\lbresid\lbresid.exe # 5/5 .preflight\lbzorder\lbzorder.exe # 4/4 .preflight\lbcovr\lbcovr.exe # 7/7 .preflight\lbpanel\lbpanel.exe # 10/10 Tools\rendercheck\rendercheck.exe # 8 哈希不变 + FUNC 7/7 ``` --- ## 5. 明确缓做的部分及理由 ### L2 `UiDamage` + 独立叠加层(S4) **为什么缓做** 1. 受益方只有"**调试开关打开时**的帧率"。`SetForceFullRepaint` 只在 「显示布局边界」开启时被置位(`Application::Render` 里 `m_plot.SetForceFullRepaint(m_showLayoutBounds)`), 普通用户路径上**本来就是快路径**。 2. 正确实现无法只改 `PlotView` 内部:宿主用的是**持久后备缓冲区**, 上一帧叠加层画上去的边框像素必须在下一帧被"底层内容重画"覆盖掉。 若改成"独立叠加位图 + 只混合脏区",就必须把叠加层的旧矩形**注入**到底层组件的重绘范围里 —— 这要穿透 `PlotView` 的全部四条快路径(静态场景贴图 / 宽基底贴图 / 样式补丁 / 网格层重建) 与两层缓存。 3. 风险与收益不成比例:用户已在 2026-10-28 反复报告过"拖拽错位 / 贴图残留"一类事故, 而这类事故正是从"某条快路径漏了某块像素"开始的。 **保留了什么**:`SetForceFullRepaint` 及其四处守卫、`UiBoxTable` 的几何产物。 将来若真要做,只需把 `UiBoxTable` 的 `rect` 直接喂给 `UiDamage`,不需要重新收集几何。 --- ## 6. 交付物清单 ``` Components/Shared/Inc/UiBox.hpp 新增(几何产物层) Components/Shared/Src/UiBox.cpp 新增 Components/Shared/Inc/UiDebugBounds.hpp 精简为"只画"(消费者) Components/Shared/Src/UiDebugBounds.cpp 精简为"只画" Components/Shared/Inc/UiComponent.hpp CollectDebugBoxes → BuildBoxes Utilities/Inc/MemProbe.hpp 新增(分配预检验 + terminate 兜底) Utilities/Src/MemProbe.cpp 新增 Makefile LAYOUT_BOUNDS / NO_EXCEPTIONS / LTO 三个开关 Documents/UI_Refactor_{Plan,Progress,Report}.md 重建 ```