按组件目录结构把四个巨型实现文件拆分为多个协作文件(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 同步更新 §五/§七/§八。
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-10-16 04:02:36 +08:00
1 parent 0871eb0cba
commit e6b7bf66ec
19 files changed
+19130 -18771

No files matched your search

+68 -31
View File
@@ -5,7 +5,8 @@
> 巨型实现文件;③ 为二进制产物瘦身;④ 全部改动不得影响功能与性能。
>
> 结论:**UI 缺陷已修复并 A/B 验证;数据模型去冗余(信号 / 分组只保留唯一名称)落地;全仓过时注释与
> 命名批量修正;新增 `Components/Shared` 组件并迁入通用 UI 控件;产物体积 −25.6%(1013 KB → 737 KB)**。
> 命名批量修正;新增 `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` 的整体拆分本轮未执行**(见 §五,附可执行的落地方案与原因)。
@@ -151,13 +152,13 @@ Components/*(功能组件) ──▶ Components/Shared(UI 控件) ─
## 五、二进制瘦身(实测)
### 5.1 结论:**1013 KB → 737 KB(−25.6%,省 ≈276 KB)**
### 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`,本轮后) | **754 176 B** |
| 差值 | **−259 072 B(−25.6%)** |
| `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 撑大的”这一推断)
@@ -212,31 +213,55 @@ Components/*(功能组件) ──▶ Components/Shared(UI 控件) ─
## 七、本轮未完成 / 后续建议(诚实记录)
### 7.1 `PlotView.cpp`(13 695 行 / 544 KB)按功能拆分 —— **本轮未执行**
### 7.1 `PlotView.cpp`(13 695 行 / 544 KB)按功能拆分 —— **已完成**
* **为什么不硬做**:拆分本身是**纯代码搬移**(不改变行为),真正的成本在**前置条件**:
该文件的辅助区(第 13–888 行的匿名命名空间,含 ~870 行常量与工具函数)被**全部**成员函数
广泛引用。要让多个 .cpp 共享它们,必须先把匿名命名空间改成具名命名空间并**逐个函数标注
`inline`**(否则多 TU 重复定义或产生 N 份机器码),再逐个把成员定义搬走。
这是一次需要“搬一类 → 编译 → 跑四套探针”反复迭代的工程(预计 5 个 .cpp:基底/图层缓存、
绘制、命中与交互、派生曲线调度、浮动面板),**在没有足够时间逐步验证时贸然动手会破坏
性能敏感的渲染路径**,因此本轮**主动不做**,而不是草率地做一半。
* **推荐的落地方案(可直接照着做)**:
1. 新建 `Components/PlotView/Src/PlotViewInternal.hpp`:包含现有 `#include` 段 +
`namespace jdi { namespace pvdetail { <常量与工具函数,逐个加 inline> } using namespace pvdetail; }`;
2. `PlotView.cpp` 只留核心(构造/析构、`Layout`/`RefreshLanes`/`BuildLaneVisuals`、缓存失效);
3. 按**粘连度**由低到高搬移:`PlotViewDerived.cpp`(曲线线程池与 `CurveSpanFor` / `ScheduleCurveJob`)、
`PlotViewPanels.cpp`(4 类实验性浮动面板的几何/命中/绘制)、`PlotViewInteraction.cpp`
(`On*` 与命中测试)、`PlotViewPaint.cpp`(`Paint*` 与图层/装饰缓存);
4. 每搬一个文件:`make release` 零警告 + `verify`/`panelanim` 全绿,再继续。
5. ⚠️ 千万别动 `Paint()` 的**分支顺序**与图层/装饰缓存的既有不变量(见 `agent_mem.md` §11.2)。
拆成 4 个协作文件 + 1 个组件私有头(共 6 个文件,最大单文件从 544 KB 降到 270 KB):
### 7.2 其余组件的多文件化(同样未执行)
| 文件 | 行数 | 职责 |
| --- | --- | --- |
| `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`) |
`SignalPanel.cpp`(145 KB / 3.7 K 行)、`Application.cpp`(104 KB)、`DataModel.cpp`(88 KB)
同样适合按“列表/绘制/交互/编辑”“窗口与消息/命令与动作/加载”“数据集合/统计与采样/标签页配置”
拆成 3–4 个文件。`SignalPanel.cpp` 的辅助区仅约 60 行(十几个常量 + 少量格式化函数),
**改造成本远低于 `PlotView.cpp`**,是下一步性价比最高的拆分入口。
**关键手法(也是本次能安全拆开的前提)**:
原 `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)
@@ -260,16 +285,28 @@ Components/*(功能组件) ──▶ Components/Shared(UI 控件) ─
```
新增 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(空名被拒)
修改 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)
修改 Components/PlotView/Src/PlotView.cpp Title() → name;过时注释
修改 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/Application/Src/Application.cpp 拆分后只留核心;过时注释/命名
修改 Components/TabBar/Src/TabBar.cpp 引入 UiControls.hpp
修改 Components/Toolbar/Inc/Toolbar.hpp 注释修正
修改 Utilities/Src/PopupMenu.cpp | ColorPickerPopup.cpp 引入 UiControls.hpp