- 新增 pvdetail::PanelKindTraits + PanelTraitsOf(唯一 switch):标题 / 排版类别 / 滚动字段 / 面板状态 / 功能开关同表登记;新增面板只改「枚举 + 一行」, -Wswitch 仍在唯一登记点生效。PanelTitle 改为「前缀 + rangeSuffix 标志」, 不再依赖 switch(标题降级为数据,不再需要函数指针)。 - PaintPanelMarkerGroups(约 340 行)拆成 6 个具名函数,主函数只保留绘制顺序 (普通分组栏 -> 条目 -> 表头 -> 吸顶栏 -> 滚动条 -> 透明度行);纯搬移、0 字节变化。 - 顺手修正该函数体遗留的多余缩进。 验证:release/debug/tools 零 warning;UnitTests 260/19;OffscreenChecks -all 7/133; panels -mode compare 7 面板像素哈希逐项不变;rowgeom compare 965+7 / 1268+7 键 0 差异。 产物 781,824 -> 782,336 B(+512,全部来自登记表)。
29 KiB
Code Review 计划(分支 Code_Review)
目标:在不引入性能回归、不改变用户可见行为的前提下,对全项目做一次结构 + 性能 + 体积 的查漏补缺,并把「改一个功能要动 40 处」的屎山点收敛成表驱动/单一实现。
基线(本计划开始时):
- 分支
Code_Review,起点764e5be(dev 已删除过时的UI_Refactor_*三份文档)。make release产物 890,880 B(.text753,136 B、.rdata36,936 B、.rsrc20,656 B、.pdata30,420 B、.xdata29,496 B、.idata12,856 B)。- 源码约 5.6 万行;最大单文件
PlotViewPaint.cpp6,561 行、PlotView.hpp4,654 行。约束:
- 每完成一个节点编译验证(release + debug 零警告)并提交,同时更新
Documents/Code_Review_Progress.md。- 性能相关节点必须给出同一次运行内的相对量(历史教训:绝对毫秒阈值不可靠)。
- 不占用用户的鼠标/键盘/屏幕(只用纯控制台探针或源码级核对)。
一、已确认的问题(本计划的事实基础)
A. 体积
| 项 | 实测 | 说明 |
|---|---|---|
dynamic_cast(Toolbar.cpp:370) |
+75,264 B | 最小复现程序:39,936 B → 115,200 B。它会链入 RTTI 元数据 + cp-demangle.o(map 里 56,320 B)。而该处 Button::widget 本来就是 UiButtonWidget* ⇒ 转换完全多余。 |
-fno-rtti |
未启用 | 移除上一条后即可开启,进一步去掉 type_info 表。 |
.pdata + .xdata |
60 KB | -fno-exceptions 已开;SEH 展开表仍存在。可尝试 -fno-asynchronous-unwind-tables(历史上链接失败,需复核)。 |
libmingwex 的 strtodg/gdtoa/mingw_wpformat |
27.8 KB | 来自 wcstod / wprintf 类调用;可评估用自写解析替代 wcstod 的收益(公式/输入框解析)。 |
.rsrc |
20.7 KB | 图标(PNG 帧)+ manifest + 版本信息,已优化过。 |
| 重复常量/重复实现 | 多处 | kFormulaEnvelopeBuckets 已清理;仍有 ClampDouble 两份实现等。 |
B. 性能(结构性热点)
- 面板开关/参数处理是 6 个
EdgePanelKind× ~20–26 处 switch 分支(实测每个 kind 出现 21–26 次,全部由手写if/switch维护)。这是新增功能必然漏改 2–3 处的根本原因 (历史已发生 4 次:MARK漏拖动、RESAMPLE漏拖动+关闭、CLIP漏滚轮+缓存键)。 SceneContentHash里 6 段几乎相同的逐面板混入循环(PlotViewCacheKey.cpp)。EdgePanelLayoutFor是巨型分支,EdgePanelLayout结构体带 20+ 个按面板类型区分的 矩形数组;高度算式与几何算式分开写(历史上多减一个 pad 造成空白空白 BUG)。PlotView有 30+ 个缓存,每个都手写「键 + DC + 位图 + 贴图 + 失效」五件套; 失效路径散落在 8 个文件里,m_sceneValid=false出现 6 处。ApplicationCommands.cpp2,165 行混杂:标签页、数据派生、菜单、工具栏、键盘、鼠标。PlotView.hpp4,654 行(声明 + 内联实现),单文件认知负担极大。
C. 过时/错误注释与命名(抽样已确认)
agent_mem.md§3「目录结构」仍写着已不存在的Utilities/CsvTable/、Utilities/DataModel/等目录;§2 写-Os而实际是-Oz。- 源码中留有历史轮次的「本轮」「上一轮」叙述(对当前读者无意义),以及被推翻方案的注释。
PlotViewInternal.hpp里kFormulaEnvelope*注释已随实现迁移,需同步。
二、节点计划
优先级:S(安全、收益明确、风险低)→ A(结构调整,中风险,需逐步验证)→ B(深度重构,必须有可验证的前后一致性)。
阶段 0:建立基线(节点 P0)
- P0.1 记录基线:release/debug 体积、
.text/.rdata等节表、关键路径的可复跑测量程序 (纯控制台,链接Build/Release/*.o,不入库)。已完成基线的部分见下表。 - P0.2 建立
Documents/Code_Review_Progress.md,每节点完成后追加一行结论 + 数字。
阶段 1:体积(节点 P1–P5,安全优先)
- P1 移除
dynamic_cast+ 开启-fno-rtti- 把
Toolbar::BuildBoxes的dynamic_cast<const UiButtonWidget*>改为直接用Button::widget(已是该类型)或给UiWidget加虚函数ContentBounds()。 - Makefile
CXXFLAGS += -fno-rtti;全仓确认无typeid/dynamic_cast/type_info。 - 预期 −70~80 KB。验收:产物下降 ≥60 KB;功能逐项核对(工具栏按钮、布局边界调试)。
- 把
- P2 体积审计第二轮
- 复核
-fno-asynchronous-unwind-tables(历史上链接失败,试-fno-unwind-tables组合)。 - 评估
wcstod替身(自写定点/浮点解析,仅在公式与输入框使用)去掉strtodg/gdtoa/gethex/hexnan(约 20 KB)。 - 评估
--gc-sections在 LTO 下是否已生效(读 map 确认无死节)。 - 验收:逐项给出「测得的字节数 + 是否采纳」的结论,不接受「感觉变小」。
- 复核
- P3 重复实现归并
ClampDouble两份(PlotViewInternal.hpp/UiLayout.cpp)→ 统一到Shared。- 扫描全部
static/匿名命名空间重复工具函数,合并为单一实现。 - 重复字符串/常量字面量、重复的三元式开关表。
- P4 死代码与未使用成员清理
- 用非 LTO 对象 +
nm反查「定义但从未被引用」的函数;删除确认无引用的实现。 - 清理
PlotView.hpp中已无人读取的成员/字段。
- 用非 LTO 对象 +
- P5 资源与启动数据
.rsrc复核(图标帧数、manifest、版本字符串);.idata导入表复核。
阶段 2:性能(节点 P6–P10)
- P6 建立可复跑的渲染性能探针(纯控制台)
- 目标:能在不弹窗口的情况下测量
BuildLaneVisuals/SceneContentHash/EdgePanelLayoutFor/ 面板绘制 / 信号面板绘制的耗时。 - 方式:链接
Build/Release/*.o+ 手工构造DataSet/TabConfig,只调用纯函数与PlotView的非窗口方法(避免Application::Initialize触发模态框,见历史教训)。 - 所有断言用同一进程内的相对量。
- 目标:能在不弹窗口的情况下测量
- P7 面板元数据表驱动(核心)
- 新增
EdgePanelSpec(每 kind 一行:命令 id、默认尺寸、是否列表型、footer 类型、 滚动字段偏移、面板状态字段偏移、绘制函数指针、命中函数指针)。 - 把 6×~20 处
switch (kind)收敛为「查表 + 3 个通用实现(列表型 / 表格型 / 固定行型)」。 - 验收:逐像素对比改造前后面板(离屏渲染哈希);新增一个假 kind 只需加一行表项即可 被绘制/命中/拖动/关闭/滚动全覆盖(用编译期断言或探针验证)。
- 新增
- P8
SceneContentHash混入收敛- 把 6 段重复循环改为「按面板表遍历 + 每面板一个
MixContent()」。 - 验收:改造前后哈希值逐位相同(同一状态)。
- 把 6 段重复循环改为「按面板表遍历 + 每面板一个
- P9
EdgePanelLayoutFor表驱动 + 高度/几何同源- 高度算式与内容几何共用同一份
PanelMetrics计算(消除重复减 pad 类 BUG)。 - 验收:探针打印「内容区下边界 vs 按钮上边界」的像素差,与改造前一致。
- 高度算式与内容几何共用同一份
- P10 缓存五件套抽象(低风险版)
- 抽出
OffscreenCache(DC/位图/键/贴图/失效),只替换三处结构完全相同的 (derived / decor / panel),保留 scene 与 lane layer 不动(热路径最复杂)。 - 验收:逐像素一致 + 帧时间不回退。
- 抽出
阶段 3:架构(节点 P11–P14)
- P11
ApplicationCommands.cpp拆分- →
ApplicationTabs.cpp(标签页)/ApplicationDerive.cpp(派生/重采样/截取/克隆)/ApplicationCommands.cpp(命令分发)/ApplicationMenus.cpp(上下文菜单与设置菜单)。 - 纯搬移,零行为变更;Makefile 无需改动(按目录通配收集)。
- →
- P12
PlotView.hpp拆分- 把
EdgePanelLayout、派生曲线缓存条目、图层结构体等纯数据结构搬到PlotViewTypes.hpp;把PlotView的内联小函数按关注点分块放到PlotViewInline.hpp。 - 目标:
PlotView.hpp≤ 2,500 行。
- 把
- P13 UI 基础层职责核对
- 核对
UiWidget/UiComponent/UiLayout/UiBox/UiSurface/UiButtonWidget/UiControls的边界是否重叠;合并确认冗余的(只保留一套矩形/命中/布局原语)。 - 明确依赖方向:
Components/*→Components/Shared→Utilities(校验无反向)。
- 核对
- P14 绘制/功能组件边界
PlotViewPaint/Raster/Panels/Features/Interaction的职责再核对; 把「面板几何」与「面板绘制」的交叉依赖理顺(几何 → 共享PanelMetrics)。- 把散落在
PlotView里的「颜色/线宽/胶囊样式」常量收敛到Theme/PaintUtil。
阶段 4:注释与命名清理(节点 P15)
- P15 注释/命名审计
- 删除「本轮/上一轮」叙述、被推翻方案的描述、与实现不符的注释。
- 统一命名:
*Dc/*DC、Lane*vsGrid*、Edge*vsPanel*等不一致处。 - 确保所有注释与当前实现一致(重点:
PlotViewInternal.hpp、PlotViewCacheKey.cpp)。 - 同步更新
agent_mem.md的 §2/§3(已经不准确的目录结构与-Os)。
阶段 5:验证与报告(节点 P16)
- P16 最终验证与报告
- release + debug 零警告;产物体积对比;关键路径探针全部通过。
- 生成
Documents/Code_Review_Report.md:逐节点写「改了什么 / 为什么 / 效果 / 未做的事与原因」,并给出体积与性能的前后对照表。
三、验收标准(硬性)
make release与make debug零 error、零 warning。- 产物体积必须下降(目标:从 890,880 B 降到 ≤ 780 KB;若某项无法达成,在报告里 说明实测字节数与原因)。
- 不改变任何用户可见行为(面板几何、绘制像素、交互语义一致)。
- 每条性能结论都必须有同进程相对量或逐像素/逐位一致性证据。
- 每个节点单独提交,提交信息用中文、说明改动与验证方式。
四、风险与对策
| 风险 | 对策 |
|---|---|
| 面板表驱动改造引入像素差异 | 改造前后用离屏渲染哈希对比;先改 CLIP/MARK 两个最简单面板做样板,再推广 |
-fno-rtti 破坏第三方/库代码 |
全仓 grep 确认;先单独提交,失败可独立回退 |
| 缓存抽象拖慢热路径 | 只抽象非热路径(derived/decor/panel),scene/lane 不动;每次改完测帧时间 |
| 大文件拆分时误改逻辑 | 纯搬移(不重写函数体),逐文件 diff 核对;先拆 ApplicationCommands |
| 探针触发模态「内存不足」框 | 绝不在探针里调 Application::Initialize()(历史教训);只构造数据层/纯函数 |
五、进度索引
进度与每节点的实测数字见 Documents/Code_Review_Progress.md。
最终结论见 Documents/Code_Review_Report.md。
六、第 34 轮追加(2026-11-04):验证基础设施 + 后续建议落地
触发:§三/§10 之后剩下的三项建议(
EdgeScanFor哈希化、EdgePanelLayoutFor表驱动化、wcstod自写替身)都卡在**"没有可自证的验证手段"**上。 用户要求:先把验证工具做成仓库内的正式资产(Tools/入库),再动被建议的代码。
6.1 节点与验收标准
| 节点 | 内容 | 验收标准 |
|---|---|---|
| T1 | Tools/ 解除忽略;新增 Tools/Makefile;两个工具目录 + README.md |
make -C Tools 构建全部、make UnitTests 构建单个、make clean 只清工具产物;改头文件/增删源文件自动重编重链 |
| T2 | Tools/UnitTests:数据层语义单元测试 |
result=PASS,断言数 ≥ 200 |
| T3 | Tools/OffscreenChecks:隐藏窗口渲染 + 帧序列 + 面板像素基线 + 边沿统计参考实现 + 性能 |
零失败;全程不显示窗口、不动真实鼠标/键盘 |
| T4 | 建议 #2:EdgeScanFor 状态查找哈希化 |
先量后改;性能收益必须大于体积代价,否则回退 |
| T5 | 建议 #3:EdgePanelLayoutFor 表驱动化 |
面板像素基线 A/B 逐像素一致才允许合入 |
| T6 | 建议 #4:wcstod 自写替身(省 gdtoa 链) |
体积收益可测且数值语义逐位一致 |
6.2 本轮新增的硬约束
- 工具不得把产物写进
Tools/:截图 / 基线 / 报告一律写Temp/tools/(已被忽略);Tools/*/Build/同样不入库(其它协作者本地make -C Tools重建)。 - 工具与本体 ABI 必须一致:
-fno-exceptions、-fno-rtti、链接时-flto -flto-partition=one; 工具源码自身不编成 LTO 字节码(GCC 15.2.0 会在choose_baseaddr处 ICE)。 - "先量后改":任何以性能 / 体积为名的改动,必须在同一台机器、同一次会话内给出前后数字;
收益低于噪声、或换来体积上涨的,一律回退,并把数字写进
Code_Review_Report.md。 - 像素基线只做本地 A/B:哈希与 DPI/字体/数据文件绑定,不入库、不当黄金图。
七、第 35 轮追加(2026-11-04):浮动面板架构评估与收敛 + 整数格式化替换
触发:用户指出「7 个实验性功能面板观感/骨架高度一致,但每新增一种或改一次布局 总会出意料之外的错误」,要求先评估(能否抽公共基类 / 公共组件,浮动面板、 二级菜单、右键菜单是否算公共组件),再动手;同时把 §11.5 的 「替换
std::to_wstring/std::to_string」做掉。
7.1 节点与验收标准
| 节点 | 内容 | 验收标准 |
|---|---|---|
| P20 | 面板「特征层」:唯一分类 / 标题 / 滚动几何来源 | 面板像素基线与几何逐项不变 |
| P21 | 滚动设置收敛为唯一实现(7 个包装 + 2 条调用链) | panelscroll 新检查全绿 |
| P22 | 全仓替换整数格式化(76 + 2 处) | 面板像素不变 + 单元测试逐字符对照 |
| P23 | 其余审查清理(垃圾形参 / 重复判定) | 零 warning、像素不变 |
7.2 本轮新增硬约束
- 新增一种实验性功能面板,只允许改三个地方:
LanePanelScrollOf/PanelStyleOf/PanelTitle(都在PlotViewInternal.hpp/PlotViewPanels.cpp)。三者都是穷举switch且不写default:, 漏登记会被-Wswitch编译报警。 禁止再在宽度 / 布局 / 绘制里各写一份kind == XXX组合判断。 - 底部"有按钮行"与"是构建按钮"是两个概念(
PanelStyle::footerButtons()vsbuildButton)。混用会让面板高度与内容区高度不一致。 - 整数转文本一律用
Utf8Util::FormatInteger*,不要用std::to_wstring/std::to_string(它们把 MinGW printf 引擎拉进产物,且 libstdc++ 会强制__USE_MINGW_ANSI_STDIO=1,命令行关不掉 —— 见 Makefile 注释)。 - 改面板几何/绘制前后必须跑
OffscreenChecks -run panels -mode record/compare; 本轮它当场抓到了"截取面板少了 37px 高度"的回归。
八、第 36 轮追加(2026-11-04):§12.7 两项建议的落地
触发:用户要求「继续按照上轮报告的后续建议推进」。§12.7 列了两项: ①
EdgePanelLayout平行数组合并;②PaintEdgeCursor拆分。
| 节点 | 内容 | 结论 |
|---|---|---|
| P26 | 拆分 PaintEdgeCursor 三段内容绘制 |
✅ 完成(新建 PlotViewPanelPaint.cpp) |
| P27 | 平行数组错位隐患 | ✅ 用 AppendRow() 唯一入口消除(未做完整结构合并,理由见下) |
| P28 | 收紧不变式 | ✅ 18 个数组全部必须等于行数(不再允许"重采样专属列可为空") |
8.1 本轮新增硬约束
- 新增一种面板行,只能走
EdgePanelLayout::AppendRow(...)。 18 个平行数组由它一次补齐;参数行 / 重采样行一律rowXxx.back() = …赋值。 禁止在任何布局分支里手写rowXxx.push_back(...)。 - 面板内容绘制按排版类别分文件:骨架(边框/标题栏/✕/按钮)在
PlotViewPaint.cpp, 内容在PlotViewPanelPaint.cpp。新增一种面板 = 加一个PanelStyleOf分支 + 在对应内容函数里加一支。 OffscreenChecks -run panels的长度不变式现在是"全等"判定: 任何一列短于行数即失败(这条检查是"绕过AppendRow"的唯一探测器)。
8.2 为什么没有做「完整合并成 std::vector<PanelRow> + 列视图」
技术上可行(PanelColumn<T> 持有 std::vector<PanelRow>* + 成员指针,
读点只需在 layout.rowXxx 后加一对括号),但 EdgePanelLayout 需要自定义拷贝构造/赋值
来重新绑定视图;而它是 EdgePanelLayoutFor() 的返回值、又被存进 std::vector,
拷贝是热路径 —— 拷贝语义一旦写错,会产生比原缺陷更隐蔽的悬垂视图(原缺陷至少会被
长度不变式抓到,悬垂指针不会)。
本轮先用 AppendRow() 把真实隐患("某分支漏推一列")消除,
完整结构合并留待"逐行几何基线"检查就绪后再做。
九、第 37 轮追加(2026-11-04):逐行几何基线(§13.6 第 1 项)
触发:用户要求「按照你的建议继续推进」。§13.6 第 1 项 = 逐行几何基线, 它是 §13.3「完整结构合并」与「重写布局算式」的前置护栏。
| 节点 | 内容 | 验收标准 |
|---|---|---|
| P29 | 新增 OffscreenChecks -run rowgeom:把 7 个面板每个矩形的每个字段导出为文本基线,逐项比对 |
-mode record/-mode compare 无差异;篡改基线必须报错(负向自检) |
| P30 | 修正长度不变式的判据:从"18 列全等"改为按排版类别判定 | panels / panelscroll 仍全绿,且对 MARK 面板不再误判 |
9.1 基线的两层覆盖
| 场景 | 数据 | 记录内容 | 用途 |
|---|---|---|---|
| A:细节 | 前 6 条信号合并成 1 个网格,7 个面板全开、参数行展开、3 个固定点、折起/展开各一份 | 每行每列的精确矩形(Temp/tools/rows-baseline/rowgeom-baseline.txt,983 条键) |
定位级:一眼看出"哪一行哪一列偏了几个像素" |
| B:规模 | 全部信号合并成 1 个网格(MF4 = 566 行) | 每个面板一个 FNV-1a 摘要哈希 + 行数/固定点行数 | 覆盖级:任何一行变动都会改哈希(不产生几十万行基线) |
9.2 本轮新增硬约束
- 改面板几何(宽高算式 / 行距 / 内边距 / 折起规则)前后必须跑
OffscreenChecks -run rowgeom -mode record|compare。 基线是检测器不是黄金图:变化是预期时要重新 record,并在提交信息里说明原因。 rowRefs是重载的:「列表式面板」用rowRefs表示"每个信号一行", 而「自由标记面板」用它表示"每个信号一个分组",只填rowNameRects/rowNameCloses, 其余 14 列必须为空(真正的条目行在markerRows)。因此任何"平行数组长度"不变式 必须按PanelStyle分类判定,不能写"全部 18 列全等"。ExecuteToolbarAction(MERGE_LANES / SPLIT_LANES)之后、读LaneCount()之前, 必须先SynchronizeView(true):SetMode()只重建TabConfig的网格配置,PlotView::m_lanes要等下一次布局/绘制才刷新。- 检查里不要依赖"上一个场景的网格级状态还在":
Session::ShowSignals()先SetAllVisible(false),而"全部不可见 ⇒ 丢弃网格"是既有设计 (RebuildLanes的 MERGED 分支),固定点/面板开关会随之清空 —— 该场景要重新播。
十、第 38 轮追加(2026-11-04):§14.5 第 1 项(删死列)+ 第 3 项前半(拆重采样行)
触发:用户要求「按照上一轮报告中的 §14.5 下一步(按价值排序)继续推进」。
| 节点 | 内容 | 验收标准 |
|---|---|---|
| P31 | 删除 EdgePanelLayout::rowPoints / rowIterations(写入方只剩 AppendRow,读取方只剩调试框与缓存键,几何恒为空) |
rowgeom -mode compare 的差异只来自这两列(64 条键,0 条其它差异);panels -mode compare 像素不变 |
| P32 | 从 PaintPanelListRows 提取 PaintPanelResampleRows("四行只读统计 + 联动输入框"这一段) |
面板像素哈希(尤其 resample)与逐行几何逐项不变 |
10.1 为什么"删掉一列"也可以证明
rowgeom 的两层基线正好给出可证伪的等价性:
- 旧基线 983 行、新基线 919 行;集合差 = 只在旧里的 64 行、只在新里的 0 行;
- 那 64 行全部属于
rowPoints/rowIterations(脚本按正则断言,非人工核对); - ⇒ 其余 919 条键逐字节相同,也就是说"删掉的两列从没被赋予过非空矩形"这一判断被基线证实。
10.2 本轮新增硬约束
EdgePanelLayout行相关的平行数组现在是 16 个(原 18)。任何"列清单" (rowgeom导出、长度不变式、调试框、缓存键)都要同步;AppendRow里少推一列 仍会被长度不变式当场抓到。- 只读统计/输入框这类"专属排版"要独立成函数:
PaintPanelResampleRows的四行 与其它面板的滑块参数行没有共同点,混在同一个行循环里只会让三种排版互相纠缠。 - §14.5 第 2 项(完整结构合并)仍处于"闸门关闭"状态,未做(理由见 Report §15.3)。
10.3 §14.5 第 3 项的完成度
| 段落 | 状态 |
|---|---|
| 重采样行(四行只读+输入) | ✅ 已提取为 PaintPanelResampleRows |
| 通用参数行(中心轴统计 + 两行滑块) | ⏸ 未提取:依赖 3 个 kind 标志 + 2 套拖拽/编辑状态 + 2 个滑块 lambda,提取需传 9 个参数,可读性收益低于参数表本身 |
| 条目栏(三角 + 名称 + 复选框 + 悬停) | 保留在 PaintPanelListRows 里(它是三种排版的公共前缀) |
十一、第 39 轮追加(2026-11-04):平行数组合并为 std::vector<PanelRow>(§15.6 第 1 项)
触发:用户要求「继续按照上轮的建议继续推进」。上一轮结论是"若要做就做方案 B:
std::vector<PanelRow>+ 具名成员访问(无视图),并且必须单独一轮"。
| 节点 | 内容 | 验收标准 |
|---|---|---|
| P33 | EdgePanelLayout 的 16 个平行 std::vector 合并为 std::vector<PanelRow> rows |
rowgeom -mode compare:0 条键消失、0 条已有键的值变化;新增键全部是空矩形 |
| P34 | 唯一入口收敛为 AppendRow / AppendMarkerGroupRow;int rows 改名 signalCount |
编译零 warning;7 个面板像素哈希逐项不变 |
| P35 | 删掉因合并而变成恒真的 50+ 处 rowIndex < layout.rows.size() 判空 |
全量检查 133 断言全绿 |
11.1 本轮新增硬约束
- 行的几何只能通过
rows[i].<字段>访问;禁止再加回平行数组,也禁止引入PanelColumn<T>列视图(拷贝EdgePanelLayout后忘重绑 = 静默悬垂,没有检查可以抓到)。 - 新增一个行字段 =
PanelRow加一个成员 +AppendRow里赋默认值(现在连这一步都不用, 成员自带默认构造)+ 需要绘制的地方读rows[i].字段。不需要再同步任何"列清单"。 signalCount≠rows.size():前者是"网格里的可见信号数"(EdgePanelLayoutFor开头统计), 后者是"本面板实际产生的行数"。两者在「自由标记」面板里不相等(只有有固定点的信号才有行)。rowgeom的键名保持<panel>.<旧列名>[NNN]:这样重构前后的基线可以逐字节比对, 等价性不靠人工核对。
11.2 等价性是怎么证明的(可复用的流程)
old keys = 919, new keys = 965
仅存在于旧 = 0 ← 没有任何键消失
已有键的值变化 = 20 ← 全部是 ".count" 键:0 -> N(结构变化,不是几何变化)
仅存在于新 = 46 ← 全部是 <column>[NNN],且值全为 0.00,0.00,0.00,0.00
含义:旧结构里"某列根本没被 push"的那些行,现在每行都拥有该字段(值为空矩形)。 逐行逐字段的真实几何一位都没变,7 个面板的像素哈希也逐项未变。
十二、第 40 轮追加(2026-11-04):几何算式重写 + 参数行提取(§16.7 第 1 / 第 2 项)
触发:用户要求「继续上一轮报告中 §16.7 的 1 和 2 建议」,并明确第 3 点(体积)暂不投入, 等以后功能更新产生新的冗余时再优化。
| 节点 | 内容 | 验收标准 |
|---|---|---|
| P36 | 宽度算式与列几何共用一份 PanelColumnMetrics(MeasurePanelColumns + EdgePanelWidthFor) |
panels 7 个面板像素哈希 + 矩形逐项不变 |
| P37 | 行高累加与行几何共用一份 PanelRowPlan(PlanListPanelRows):两趟遍历合一 |
rowgeom 965+7 键 0 差异;panelscroll 全绿 |
| P38 | PaintPanelListRows 的通用参数行提取为 PaintPanelParamRows(5 参数) |
双基线归零;PaintPanelListRows 由约 300 行降到约 120 行 |
12.1 本轮新增硬约束
- 宽度算式与列几何只能有一份来源:任何"这个面板的标签列 / 数值列有多宽"都从
PanelColumnMetrics取。禁止在EdgePanelLayoutFor里再写UiMeasureTextWidth链 (旧代码注释写着"与EdgePanelWidth同一组算式 ⇒ 不会漂移"——那是靠人守的约定, 改一处漏一处就是"面板变宽而列不动",表现为文字压到轨道上)。 - 行高与行几何只能有一份来源:都读
PlanListPanelRows的返回值(PanelRowPlan)。 禁止再出现"第一趟只算高度、第二趟才算几何"的双遍历 —— 两趟的判定条件必须逐字一致, 任何一处写错就表现为底部留白 / 最后一行被裁 / 没内容却能滚动。 PanelRowPlan::sliderRows用整数表达"展开几个参数行"(0/2/4),hasStats表达「中心轴」多一行统计 —— 这两个量是"高度"与"几何"共享的唯一判定。- 参数行绘制走
PaintPanelParamRows(g, layout, row, kind, PanelParamState{…}); 逐行状态打包进PanelParamState(解析结果 / 两种条目指针 / 网格下标), 目的是把参数表压在 5 个 —— 一个个透传会让签名比函数体更难读。
十三、第 41 轮(收尾):面板登记表 + 标记面板绘制分段
触发:用户要求「按上轮报告 §17.6 继续推进」,并在本轮结束时判断是否已达到可以结束 本次代码审查的程度;体积一项按用户指示继续暂停(§17.6 第 3 项)。
| 节点 | 内容 | 验收标准 |
|---|---|---|
| P39 | PanelStyleOf / PanelTitle / LanePanelScrollOf / LanePanelStateOf / LanePanelEnabled 五处穷举 switch 合成一张 constexpr 登记表 PanelTraitsOf(kind) |
编译零 warning;panels -mode compare 7 面板像素哈希逐项不变 |
| P40 | PaintPanelMarkerGroups(约 340 行)拆成 6 个具名函数(分组栏 / 列宽 / 分隔线 / 条目 / 表头 / 透明度行),主函数只留"顺序" |
panels -mode compare + rowgeom -mode compare(默认 + MF4)0 差异 |
13.1 本轮新增硬约束
- 新增一种实验性功能面板只改两处:
EdgePanelKind加一个枚举值 +pvdetail::PanelTraitsOf加一行(标题 / 排版类别 / 滚动字段 / 面板状态 / 功能开关全在那一行)。PlotView::PanelTitle不再需要改 —— 它只读表里的"标题前缀 +rangeSuffix标志"。 PanelTraitsOf的switch一律不写default::漏登记EdgePanelKind会被-Wswitch当场拦下(这是"编译期保护"的唯一执行点,其余四个消费者都只是取值)。PanelStyle用三个具名构造函数(ListPanelStyle/CountTablePanelStyle/MarkerPanelStyle)构造,禁止在表里写 6 个裸 bool(聚合顺序错一位就变成"表格面板带按钮")。- 标记面板的绘制顺序是硬约束:普通分组栏 → 条目 → 表头 → 吸顶分组栏 → 滚动条 → 透明度行。
它现在只出现在
PaintPanelMarkerGroups一个地方(过去藏在一个 340 行函数的循环中间)。 新增任何"吸顶/悬浮"元素时,必须加在"条目之后"。