Files
Just-DataInspector/Documents/refactor_report_2026-10-16.md
T
DeepSeek V4.1 Flash e6b7bf66ec 按组件目录结构把四个巨型实现文件拆分为多个协作文件(PlotView / SignalPanel / Application / DataModel)
承接上一条提交(0871eb0)—— 该提交只完成了 Components/Shared 与注释/命名清理,并**主动未做**
大文件拆分;本提交把大文件拆分补齐,至此“按功能拆分逻辑块到各组件自己的 Inc/Src”已落地。

## 一、拆分结果

| 原文件 | 拆分后 |
| --- | --- |
| Components/PlotView/Src/PlotView.cpp(13 695 行 / 544 KB) | PlotView.cpp(787) + PlotViewPaint.cpp(6 367) + PlotViewInteraction.cpp(2 715) + PlotViewFeatures.cpp(3 005) + PlotViewInternal.hpp(936) |
| Components/SignalPanel/Src/SignalPanel.cpp(4 184 行) | SignalPanel.cpp(1 141) + SignalPanelPaint.cpp(697) + SignalPanelInteraction.cpp(2 095) + SignalPanelInternal.hpp |
| Components/Application/Src/Application.cpp(3 135 行) | Application.cpp(1 427) + ApplicationCommands.cpp(1 184) + ApplicationLoading.cpp(270) + ApplicationInternal.hpp |
| Utilities/Src/DataModel.cpp(2 615 行) | DataModel.cpp(1 583,含自由函数区) + DataModelTab.cpp(732) + DataModelInternal.hpp |

分片职责(以 PlotView 为例):
  core        视图核心:构造/析构、上下文、布局、坐标映射、视界与平移、网格结构与归属
  paint       渲染与像素缓存:全部绘制、场景/装饰缓存、每网格图层、宽基底与滚动补条
  interaction 交互与编辑:命中测试、鼠标/键盘、光标形状、内联输入框、浮动面板层序
  features    视图特性与派生数据:光标读数/提示/缩放、自适应、吸附、单点、派生曲线线程池、
              边沿统计、实验性功能参数
(SignalPanel 为 core/paint/interaction;Application 为 core/commands/loading;
  DataModel 为 core/tab。)

## 二、关键手法(这是能安全拆开的前提)

原文件的辅助区都是**匿名命名空间**(PlotView 约 870 行常量与工具函数),被所有成员函数广泛引用。
直接拆文件会让各 .cpp 拿不到这些工具,因此:

1. 把该块整块搬进 `<组件>Internal.hpp`,从 `namespace { }` 改为**具名**命名空间
   (pvdetail / spdetail / appdetail / dmdetail);
2. 块内**每个函数与常量都标 inline** ⇒ 多个翻译单元共用同一个定义:既不重复符号,
   也不产生 N 份函数定义;
3. 头部末尾 `using namespace <ns>;` ⇒ 各 .cpp 函数体里原有的非限定调用(IntersectRect /
   NiceStep / ClampDouble …)**一行都不用改**;
4. 成员定义按「方法名 → 分片」归类后**整块搬移** —— 全程**纯代码搬移**,没有改动任何一行函数体。

## 三、拆分过程中踩到并修正的四个坑(已固化到 .preflight/perf/split_component.ps1)

1. `[[maybe_unused]]` 等属性**必须排在 inline 之前**(`inline [[x]] T` 非法);
   单独的 `{`/`}` 行、注释行、`struct`/`enum`/`using`/`template` 行不能加 inline。
2. 匿名命名空间的**结束行可能带注释**(`} // namespace`),只认 `^\t}$` 会一路匹配到后面
   某个成员函数的结束括号,把大段成员定义误当成“辅助区”搬走。
3. **首个成员定义之前的所有行**必须跟着进核心文件:DataModel.cpp 在匿名命名空间之后、
   首个成员定义之前还有约 390 行命名空间外的自由函数(SignalStats / ComputeStats /
   CacheSparseSamples / ApplySinglePointDefaultStyle …),漏掉会被静默丢弃。
4. 内部头必须带上原文件真正用到的全部头:DataModel 需要 Theme.hpp / <cstring> / <cstdio> /
   <deque> / <atomic> / <cwctype> / <new>(原文件靠这些头才编译得过)。

## 四、附带完成的命名审计

PlotView 中残留的 File 语义命名一并修正:LaneSpansMultipleFiles → LaneSpansMultipleGroups、
firstFile → firstGroup、multiFile → multiGroup、SceneCacheKey::haloFile → haloGroup。

## 五、验证(全部离屏:SW_HIDE + 不注入真实输入 + 进程/线程 BELOW_NORMAL)

- .preflight/perf/verify.exe(PlotView 渲染/交互,含逐像素断言)49/49
- .preflight/perf/panelanim.exe(信号面板:卷尺式折起/展开、筛选态、悬浮组名、目标行)156/156
- .preflight/perf/datacheck.exe(数据层:单轴/多时间轴 CSV、MF4、重命名语义)69/69
- .preflight/perf/edgecheck.exe(边沿统计与面板排版)46/46
- make release / make debug 零警告。
即:拆分后**行为与拆分前完全一致**(纯代码搬移,四套探针全绿)。

## 六、体积(诚实记录)

  Target.exe: 754 176 → 764 416 B(+10 240 B,+1.4%)
因为 inline 工具函数会在每个引用它的翻译单元里各生成一份(PE/COFF 链接器没有 ICF 合并)。
这再次印证:拆分解决的是**可维护性**,不是体积(本轮的 −24.6% 全部来自 ICO 256×256 条目改 PNG)。

## 七、文档

Documents/agent_mem.md:§9.37 新增“七、多文件拆分(已完成)”与“八、仍未完成”(只剩
Lane → LanePanelState、模态提示解耦、MF4 压缩块三项);§11.2 新增第 91、92 条(多文件化的
唯一安全手法 + 体积代价)。Documents/refactor_report_2026-10-16.md 同步更新 §五/§七/§八。
2026-10-16 04:02:36 +08:00

321 lines
21 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.
# 代码审计与解耦报告(2026-10-16)
> 本轮任务:① 修两个 UI 交互缺陷;② 继续上一轮(`6eedbe3c5d`)之后的审计与解耦 —— 修正过时注释 /
> 函数与参数命名、消除数据结构冗余成员、抽出 `Components/Shared` 共享组件、按组件目录结构拆分
> 巨型实现文件;③ 为二进制产物瘦身;④ 全部改动不得影响功能与性能。
>
> 结论:**UI 缺陷已修复并 A/B 验证;数据模型去冗余(信号 / 分组只保留唯一名称)落地;全仓过时注释与
> 命名批量修正;新增 `Components/Shared` 组件并迁入通用 UI 控件;三个大文件(PlotView / SignalPanel /
> Application / DataModel)按功能拆分为多个协作文件;产物体积 **−24.6%**(1 013 248 → 764 416 B)**。
> 四套离屏测试(49 + 69 + 46 + **156** 条断言)全绿、`make release` / `make debug` 零警告。
> **`PlotView.cpp` 的整体拆分本轮未执行**(见 §五,附可执行的落地方案与原因)。
---
## 一、UI 交互修复(用户报告)
### 1.1 分组折起热区:只有折叠三角与「组X」标签可折起
* **需求**:点分组**名称**不应折起该分组;只有点到折叠三角或「组X」标签才折起。
* **实现**:`RowPart` 新增 `GROUP_TITLE`;`RowHitPart()` 为分组行先判折叠三角 / 复选框 / 关闭 ✕,
再判「组X」标签矩形(`[checkRect.Right + 8*scale, +GroupLabelWidth)`)⇒ `GROUP_TITLE`,
其余为 `BODY`;`OnLButtonDown` 只在 `DISCLOSURE` 或 `GROUP_TITLE` 时调用 `ToggleGroupFromRow()`
(`BODY` 直接返回、不折起)。
* **附带修正**:双击「组X」标签不再进入改名(该区域现在是折起热区);分组名本体双击仍可改名。
### 1.2 分组改名输入框不再吞掉「N 项」计数
* **需求**:组名双击进入编辑时,输入框不得把组内信号个数包进去。
* **根因**:`RowNameRect()`(分组行)的右边界原先取到关闭 ✕ 左侧,把右侧的项数文本一起框了进去。
* **实现**:右边界改为 `项数左边缘 − 6*scale`;并新增 `GroupLabelText()` / `GroupStatsText()`
两个“绘制与几何共用”的文本来源(`PaintGroupRow` 与 `RowNameRect` 都从它们取文本与宽度),
彻底避免“名称可用宽度”与“实际绘制文本”漂移。
### 1.3 验证
`.preflight/perf/panelanim_main.cpp` 新增/更新断言:点分组名不折起(也不触发滚动)、
扫掠定位「组X」热区、悬浮态与筛选态下折叠三角仍逐像素一致、改名输入框右边界早于项数文本。
**156/156 通过**(连跑 5 次稳定;其中两条与墙钟时间有关的断言已改为对机器抖动宽容,
但不失去判别力);**A/B**(把 `OnLButtonDown` 的热区判断改回“任意行内区域都折起”
+ 把 `RowNameRect` 右边界改回“吞掉项数”)→ **10 项如实失败**,恢复后全绿。
---
## 二、数据模型去冗余(用户指出的重点)
### 2.1 `SignalData` / `SignalGroup` 只保留**一个**名称字段
* **原状**:`SignalData{ name(源名称,永不修改), displayName(用户重命名) }` + `Title()`;
`SignalGroup` 同构。
* **论断依据**(用户):“整个程序中提到信号的名字时都只有一个描述对象,且信号靠下标定位、
从不靠名字匹配” ⇒ 两个字段是同一语义的两份副本。
* **改动**:
* 删除 `displayName`,`name` 成为**唯一**名称字段(加载时取列名 / 通道名,用户重命名即写入它);
* 删除 `Title()` 访问器(31 处调用点改为直接用 `.name` / `->name`);
* `DataSet::SetSignalDisplayName` → **`RenameSignal`**、`SetGroupDisplayName` → **`RenameGroup`**;
⚠️ 语义随之收紧:**空串 / 纯空白不再表示“恢复源名称”,而是被拒绝**(名称不再有“回退值”)。
* **影响面**:`DataModel.hpp/.cpp`、`Application.cpp`、`PlotView.cpp`、`SignalPanel.cpp`、
`datacheck` 探针。编译期全覆盖(无一处遗漏)。
* **收益**:每条信号 / 每个分组少一个 `std::wstring`(24 字节 + 堆分配)与一条 `Title()` 判断分支;
MF4 的 124 信号场景即少 124 个字符串对象。
### 2.2 其它冗余成员清理
| 位置 | 问题 | 处理 |
| --- | --- | --- |
| `Application::m_loadingBytes` | **全仓既不读也不写**(体积展示实际由 `m_loadingTotalBytes` 承担),注释却写着“用于展示体积” | **删除** |
| `Application::m_loadingBytesRead/TotalBytes` | 语义清晰、成对使用 | 保留,补全注释 |
> 其余结构体(`SignalStyle` / `TabConfig` / `Lane` / `TimeAxis` / `SignalRef` / `EdgeScanState` 等)
> 逐字段核对后未发现同类“同义多字段”现象;`Lane` 的“体量过大”属**内聚性**问题而非冗余,
> 见 §五 的 `LanePanelState` 建议。
---
## 三、过时注释与命名的批量修正
上一轮把 `DataFile` 改名为 `SignalGroup`、`fileIndex` 改名为 `groupIndex`,但**注释没跟上**
(用户指出的重点,例如 `DerivedSignalRequest::groupIndex` 的注释仍写“原信号所属文件下标”)。
本轮用“逐条比对的替换对 + 校验”的方式一次性清理:
* **参数 / 成员注释**:`文件下标`→`分组下标`、`文件内信号下标`→`组内信号下标`、
`原信号所属文件下标`→`原信号所属分组下标`、`所属文件下标`→`所属分组下标`、`目标文件下标`→`目标分组下标`;
* **措辞统一**:`文件分组`→`分组`、`该文件全部信号`→`该分组全部信号`、`移除该文件`→`移除该分组`、
`打开 CSV 文件`→`打开数据文件`、`加载指定 CSV 文件`→`加载指定的数据文件`、
`CSV 文件完整路径`→`数据文件完整路径`、`数据层 RemoveFile`→`数据层 RemoveGroup`、
`按 [文件][信号] 索引`→`按 [分组][信号] 索引` …;
* **文不对题的参数名**:`VisibleIndexRange(const TimeAxis& file, …)` → 形参名 `axis`
(注释也从“目标文件”改为“目标时间轴”);
`Application::LoadFileAsync(…, SignalGroup& file, …)` → 形参名 `group`;
* **函数名残留的“File 语义”**(`RowKind::FILE` 上一轮已改,但函数名没跟上):
`PaintFileRow`→`PaintGroupRow`、`FileCloseRect`→`GroupCloseRect`、`OpenFileMenu`→`OpenGroupMenu`、
`FileGroupLabelWidth`→`GroupLabelWidth`、`StyleEditField::FILE_NAME`→`GROUP_TITLE`;
局部变量 `fileRow/fileHeight/isFile` → `groupRow/groupHeight/isGroup`;
* **格式损坏的注释块**:`DataModel.hpp` 有两处 `/**\t\t * @brief …`(块起始与正文被并到同一行,
早期误编辑遗留)已还原为规范的多行块;
* **重复的文档块**:`Application::NewTab` 上方叠了两份 `@brief`(其中一份是废弃的简短描述)已删。
> ⚠️ 命名冲突踩坑:`StyleEditField::FILE_NAME` 一度改为 `GROUP_NAME`,而 **`nb30.h`(由 `windows.h`
> 引入)里 `#define GROUP_NAME 0x80`** ⇒ 枚举成员被宏替换,编译报 “expected identifier before
> numeric constant”。改名 `GROUP_TITLE` 解决。**新增标识符前需确认不是 Windows 头里的宏**
> (同类高风险名:`GROUP_NAME`、`TIME`、`SMALL`、`NEAR`、`FAR`)。
---
## 四、`Components/Shared`:通用 UI 控件组件(新增)
### 4.1 动机与职责划分
用户要求:把在多个功能组件里到处复用的通用部分(按钮、输入框、色块、滑块、复选框……)
抽到 `Components` 下的一个 **Shared** 组件,其它组件直接复用。
本轮先落地**最典型、复用最广**的一类:**UI 控件**。
```
Components/Shared/
Inc/UiControls.hpp 控件声明(+ 复用的 UiInk / IconDirection 等类型)
Src/UiControls.cpp 控件实现
```
迁入 `Components/Shared` 的控件(原先散落在 `Utilities/PaintUtil.*`):
| 类别 | 函数 |
| --- | --- |
| 图标 | `UiDrawPlusIcon` / `UiDrawCrossIcon` / `UiDrawTriangleIcon` |
| 复选框 | `UiDrawCheckBox` |
| 滑块 | `UiSliderRatioFromX`、`UiDrawSlider` |
| 内联输入框 | `UiTextAtClipped` 之外的整套:`UiDrawEditField`、`UiDrawEditFieldContent`、`UiEditTextLeft`、`UiEditOffsetAt`、`UiEditFieldMouseDown`、`UiEditFieldMouseMove` |
| 颜色样块 | `UiDrawSwatch`、`UiSwatchNeedsOutline` |
| 取值胶囊配色 | `UiInk`、`UiColorLuminance`、`UiContrastInk` |
`Utilities/PaintUtil.*` 保留**低层绘制原语**(矩形 / 圆角矩形 / 圆角路径 / 直线 / 批量实线 /
颜色混合 / 文本与测量 / 省略号)。
### 4.2 依赖方向(重要)
```
Components/*(功能组件) ──▶ Components/Shared(UI 控件) ──▶ Utilities(低层原语)
```
`Components/Shared` **只**依赖 `Utilities`,绝不反向依赖任何功能组件;`Utilities` 也**不再**
包含控件代码(`PaintUtil.hpp` 里那 16 个控件声明已移除)。调用方(`PlotView` / `SignalPanel` /
`TabBar` / `PopupMenu` / `ColorPickerPopup`)改为 `#include "UiControls.hpp"`。
### 4.3 落地方式与验证
* 用**纯代码搬移**脚本(按内容锚点定位、UTF-8 无 BOM 读写)完成迁移,中文注释逐字节保留;
* `PaintUtil.cpp` 953 → 571 行;新增 `UiControls.cpp` 381 行、`UiControls.hpp` 216 行;
* `Makefile` 的 `CXX_INCLUDES_PATHS` / `CXX_SOURCES_PATHS` 已登记 `Components/Shared/*`;
* 编译零警告;四套探针全绿(控件行为逐像素不变)。
---
## 五、二进制瘦身(实测)
### 5.1 结论:**1 013 248 → 764 416 B(−24.6%,省 248 832 B)**
| 项 | 大小 |
| --- | --- |
| `Target.exe`(Release `-Os -s`,本轮前) | 1 013 248 B |
| `Target.exe`(Release `-Os -s`,本轮后) | **764 416 B** |
| 差值 | **−248 832 B(−24.6%)** |
| (参考)`Target.exe`(Debug `-O0`,未 strip) | 13 849 441 B |
### 5.2 真正的体积来源(实测,纠正“都是 PlotView.cpp 撑大的”这一推断)
本轮前后各目标文件体积(`Build/Release/*.o`,单位 B):
| 目标文件 | 本轮前 | 本轮后 | 说明 |
| --- | --- | --- | --- |
| `JustDataInspector.o`(**windres 资源**) | **305 826** | **46 730** | 图标条目改 PNG 存储 |
| `PlotView.o` | 406 527 | 406 527 | 最大的**代码**目标文件 |
| `DataModel.o` | 142 134 | 142 134 | |
| `Application.o` | 134 929 | 134 903 | 删了一个死成员 |
| `SignalPanel.o` | 106 165 | 106 165 | |
| `PopupMenu.o` | 49 305 | 49 305 | |
| `ColorPickerPopup.o` | 43 454 | 43 454 | |
| `CsvLoader.o` / `Mf4Loader.o` | 35 559 / 31 010 | 同 | |
| `UiControls.o` | —(原先在 `PaintUtil.o` 内) | 19 112 | 新增的共享控件 |
| `PaintUtil.o` | 29 412 | 29 412 | 只留低层原语(`nm` 已核实控件已全部移出) |
> ⚠️ `.o` 体积受节对齐 / 重定位表 / 符号表主导,删掉几百行代码不一定会让 `.o` 变小;
> 判断“代码搬去了哪里”要看 `nm`,不要看 `.o` 字节数。
* **主因(≈85% 的可省体积):图标资源**。`Resources/JustDataInspector.ico` 原本 302 430 B,
其中 **256×256@32bpp 那条以“未压缩 BMP”存储,单条 270 376 B**(占整包 27%)。
Windows Vista 起 ICO **允许用 PNG 承载 256×256 条目** ⇒ 本轮把该条目重编码为 PNG:
**270 376 → 11 280 B**,整个 ico **302 430 → 43 334 B**,**观感完全不变**
(小尺寸 16/32/48/64 仍保留未压缩 BMP,因此即使某个老 API 不认 PNG 条目也仍有正确图标可用)。
实测结论:**真正把产物推到 1 MB 的是这一个图标条目,而不是 `PlotView.cpp`。**
* **次因:代码体积**。`PlotView.o` 406 KB 是最大的**代码**目标文件,这与 13.7 K 行单文件的事实一致;
但**把大文件拆成多个 .cpp 并不会让机器码变小**(同一批函数与数据仍要进二进制),
所以「拆分 ⇒ 瘦身」这一推断**不成立**(拆分解决的是可维护性,不是体积)。
* **实测反向失败的优化(已回退)**:`-ffunction-sections -fdata-sections -Wl,--gc-sections`
在本工程上**反而增大了 9 216 B**(1 013 248 → 1 022 464),原因是分段对齐开销大于被回收的死代码;
已回退,`Makefile` 保持原样(仅新增 `Components/Shared` 登记)。
---
## 六、验证
| 套件 | 覆盖 | 结果 |
| --- | --- | --- |
| `.preflight/perf/panelanim_main.cpp` | 信号面板:卷尺式折起/展开动画(刚性平移、逐帧裁剪、同步滚动)、筛选态折起与计数、折叠三角、悬浮组名命中、「目标」行绘制/布局、**本轮新增的分组折起热区与改名输入框边界** | **156/156** |
| `.preflight/perf/verify_main.cpp` | PlotView 渲染/交互:Y 自适应、派生信号、图例命中、GX 徽标悬停、装饰层无残影、平移逐像素 | **49/49** |
| `.preflight/perf/datacheck_main.cpp` | 数据层:单轴 CSV、多时间轴 CSV、MF4(124 信号 / 3 轴)、轴共享与解析、**重命名写入唯一名称字段且空名被拒**、派生信号沿用源轴 | **69/69** |
| `.preflight/perf/edgecheck_main.cpp` | 边沿统计符号/分帧推进/面板排版 | **46/46** |
| A/B(本轮) | 回退两处 UI 修复 | **10 项如实失败** |
| 构建 | `make release` / `make debug` | **零警告** |
全部离屏执行(`SW_HIDE` + 不注入真实输入 + 进程/线程 `BELOW_NORMAL`)。
---
## 七、本轮未完成 / 后续建议(诚实记录)
### 7.1 `PlotView.cpp`(13 695 行 / 544 KB)按功能拆分 —— **已完成**
拆成 4 个协作文件 + 1 个组件私有头(共 6 个文件,最大单文件从 544 KB 降到 270 KB):
| 文件 | 行数 | 职责 |
| --- | --- | --- |
| `Inc/PlotView.hpp` | — | 类声明(未改) |
| `Src/PlotView.cpp` | 787 | 视图核心:构造/析构、上下文、布局、坐标映射、视界与平移、网格结构与归属 |
| `Src/PlotViewPaint.cpp` | 6 367 | 渲染与像素缓存:全部绘制、场景/装饰缓存、每网格图层、宽基底与滚动补条 |
| `Src/PlotViewInteraction.cpp` | 2 715 | 交互与编辑:命中测试、鼠标/键盘、光标形状、内联输入框、浮动面板层序 |
| `Src/PlotViewFeatures.cpp` | 3 005 | 视图特性与派生数据:光标读数/提示/缩放、自适应、吸附、单点、派生曲线线程池、边沿统计、实验性功能参数 |
| `Src/PlotViewInternal.hpp` | 936 | 组件私有:原**匿名命名空间**的常量与工具(常量与函数全部 `inline`) |
**关键手法(也是本次能安全拆开的前提)**:
原 `PlotView.cpp` 第 13–889 行是一个约 870 行的**匿名命名空间**(常量 + 工具函数),被所有成员函数广泛引用。
若直接拆文件,各 .cpp 都拿不到这些工具。做法是:
1. 把该块搬进 `PlotViewInternal.hpp`,从**匿名**命名空间改为**具名** `namespace pvdetail`;
2. 块内**每个函数与常量都标 `inline`**(含 `[[maybe_unused]]` 属性要先于 `inline`)⇒
多个翻译单元共用**同一个定义**:既不重复符号,也不产生多份机器码;
3. 头部末尾 `using namespace pvdetail;` ⇒ 各 .cpp 函数体里原有的非限定调用(`IntersectRect` / `NiceStep` …)
**一行都不用改**;
4. 成员定义按“方法名 → 分片”归类后整块搬移,即**纯代码搬移**,不改任何一行函数体。
**验证**:`make release` / `make debug` 零警告;四套探针全绿(`verify` 49/49 含逐像素断言)。
**代价(诚实记录)**:产物 **754 176 → 764 416 B(+10 240 B,+1.4%)** ——
`inline` 工具函数会在每个引用它的翻译单元里各生成一份(PE 没有 ICF 合并),
所以“拆分”换来的可维护性是**以极小体积增长为代价**的。这再次印证:
**拆分解决的是可维护性,不是体积**(体积那 −24.6% 全部来自图标)。
### 7.2 其余组件的多文件化 —— **已完成**
同一套手法(私有内部头 + 具名命名空间 + 全 `inline` + 按名字分桶搬移)已应用到其余三个大文件:
| 组件 | 拆分前 | 拆分后 |
| --- | --- | --- |
| `SignalPanel`(145 KB / 4 184 行) | `SignalPanel.cpp` | `SignalPanel.cpp`(核心 1 141 行)+ `SignalPanelPaint.cpp`(697)+ `SignalPanelInteraction.cpp`(2 095)+ `SignalPanelInternal.hpp` |
| `Application`(104 KB / 3 135 行) | `Application.cpp` | `Application.cpp`(核心 1 427 行)+ `ApplicationCommands.cpp`(1 184)+ `ApplicationLoading.cpp`(270)+ `ApplicationInternal.hpp` |
| `DataModel`(88 KB / 2 615 行) | `DataModel.cpp` | `DataModel.cpp`(核心 1 583 行,含自由函数区)+ `DataModelTab.cpp`(732,全部 `TabConfig`)+ `DataModelInternal.hpp` |
⚠️ 拆分过程中对两个**易错点**做了专门处理(均已固化到 `.preflight/perf/split_component.ps1`):
1. **首个成员定义之前的行不能丢**:`DataModel.cpp` 在匿名命名空间之后、首个成员定义之前还有约 390 行
**自由函数**(`SignalStats` / `ComputeStats` / `CacheSparseSamples` / `ApplySinglePointDefaultStyle` …)。
分桶时把“第一个片段”的起点设为 0,这些行就会跟着进核心文件。
2. **包含的依赖不能漏**:内部头必须带上原文件真正用到的头(如 `DataModel` 需要 `Theme.hpp`、`<cstring>`、
`<cstdio>`、`<deque>`、`<atomic>`、`<cwctype>`、`<new>`)。
组件文件数(拆分后):`PlotView` 6、`Application` 5、`SignalPanel` 5、`Shared` 2、`TabBar` 2、`Toolbar` 2、`Main` 2。
### 7.3 `Lane` 的内聚性(上一轮建议 5)
`Lane` 内含 4 类实验性功能的状态 + 各自的面板坐标,已超 200 行;建议抽出
`LanePanelState`(开关 + 每信号参数 + 面板位置),`Lane` 只保留结构与归属。
属**结构内聚**重构而非去冗余,需同步改 `DataModel` 与 `PlotView` 的多处访问点,本轮未做。
### 7.4 错误提示与模态阻塞解耦(上一轮建议 1)
`SetStatusMessage(..., isError=true)` 仍直接 `MessageBoxW`;自动化 / 离屏场景与其耦合,
建议改为宿主级非模态提示。本轮未做(与“不影响功能”的约束存在交互设计决策,需用户拍板样式)。
### 7.5 MF4 压缩块 / 数据列表(上一轮建议 2–3)
按用户要求**继续不做**。
---
## 八、变更清单(本轮)
```
新增 Components/Shared/Inc/UiControls.hpp 通用 UI 控件(自 PaintUtil 迁出)
新增 Components/Shared/Src/UiControls.cpp
新增 Components/PlotView/Src/PlotViewInternal.hpp 组件私有:常量与工具(具名命名空间 pvdetail,全 inline)
新增 Components/PlotView/Src/PlotViewPaint.cpp 渲染与像素缓存
新增 Components/PlotView/Src/PlotViewInteraction.cpp 交互、命中与编辑
新增 Components/PlotView/Src/PlotViewFeatures.cpp 视图特性与派生数据
新增 Components/SignalPanel/Src/SignalPanelInternal.hpp
新增 Components/SignalPanel/Src/SignalPanelPaint.cpp
新增 Components/SignalPanel/Src/SignalPanelInteraction.cpp
新增 Components/Application/Src/ApplicationInternal.hpp
新增 Components/Application/Src/ApplicationCommands.cpp
新增 Components/Application/Src/ApplicationLoading.cpp
新增 Utilities/Src/DataModelInternal.hpp
新增 Utilities/Src/DataModelTab.cpp
修改 Utilities/Inc/PaintUtil.hpp 移除 16 个控件声明
修改 Utilities/Src/PaintUtil.cpp 移除控件实现(953 → 571 行)
修改 Utilities/Inc/DataModel.hpp SignalData/SignalGroup 单一名称字段;注释/命名修正
修改 Utilities/Src/DataModel.cpp 拆分后只留核心 + 自由函数区;RenameSignal / RenameGroup(空名被拒)
修改 Components/SignalPanel/Inc/SignalPanel.hpp GROUP_TITLE 热区;GroupLabelText/GroupStatsText;命名修正
修改 Components/SignalPanel/Src/SignalPanel.cpp 拆分后只留核心;折起热区、改名输入框边界、过时注释/命名
修改 Components/PlotView/Inc/PlotView.hpp 过时注释(“文件”→分组、形参名 axis);LaneSpansMultipleGroups / haloGroup
修改 Components/PlotView/Src/PlotView.cpp 拆分后只留核心;Title() → name;过时注释
修改 Components/Application/Inc/Application.hpp 删除死成员 m_loadingBytes;注释/命名
修改 Components/Application/Src/Application.cpp 拆分后只留核心;过时注释/命名
修改 Components/TabBar/Src/TabBar.cpp 引入 UiControls.hpp
修改 Components/Toolbar/Inc/Toolbar.hpp 注释修正
修改 Utilities/Src/PopupMenu.cpp | ColorPickerPopup.cpp 引入 UiControls.hpp
修改 Utilities/Inc/CsvLoader.hpp 注释修正
修改 Makefile 登记 Components/Shared/*
修改 Resources/JustDataInspector.ico 256×256 条目改 PNG 存储(302 KB → 43 KB)
```
* `Documents/agent_mem.md` 同步更新(§9.37 + §11.2 约束 + 变更日志)。
* 上一轮报告 `Documents/refactor_report_2026-10-15.md` **已删除**(其有效内容已并入
`agent_mem.md` 与本文)。