review(panels+size): 面板结构收敛收尾、审查清理、新增平行数组护栏

一、审查清理
* `PlotView::FreeMarkerValue` 的 `const Lane&` 形参从未使用(函数体里只有一句
  `(void)lane;`)⇒ 删除形参与该语句,更新 2 个调用点。
* `EdgePanelLayoutFor` 里「选区截取是否就绪(双光标)」算了 3 遍 ⇒ 收敛为函数顶部
  一次 `clipReady`。它同时决定面板高度 footer、条目行可见性、按钮几何,
  三处各算一遍时只要有一处条件写错,就是"底部多一段空白"或"没双光标却能滚动"。

二、新增护栏(离屏检查)
* `panels`:新增「平行数组长度一致」不变式 —— `EdgePanelLayout` 的 13 个按行平行
  vector 必须与行数一致;4 个「重采样」专属数组允许为空、非空则必须对齐。
  这是"某列读到别人矩形"这类错位缺陷的唯一低成本护栏(O(1))。
* `panelscroll`:同样加一份(大文件、566 行时才会暴露的问题)。
* `panels` 输出增加 rows 数,便于定位。

三、文档
* `Documents/Code_Review_Plan.md` 新增 §七(第 35 轮节点与硬约束)。
* `Documents/Code_Review_Progress.md` 新增第 35 轮(面板架构评估 + 三次重构记录)。
* `Documents/Code_Review_Report.md` 新增 §12(评估结论 + gdtoa 最终结论 + 护栏价值)。
* `Documents/agent_mem.md` 新增第 35 轮六条规则。
* `Tools/README.md`:补 `panelscroll`、`integer_replacement_matches_std`,
  断言数更新为 260 / 108,最小集表补充面板相关条目。

验证:
* `make release`(删 .o 全量)零 error / 零 warning,产物 **786,944 B**。
* `make -C Tools` 零 error / 零 warning。
* `UnitTests` → checks=260 failed=0 cases=19。
* `OffscreenChecks -all` → 6/6 检查 PASS,checks=108 failed=0。
* `panels -mode compare` → 7 个面板像素哈希与几何逐项不变。
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-11-04 14:37:59 +08:00
1 parent 34365b3c1c
commit b58b427f62
8 files changed
+408 -15

No files matched your search

+1 -1
View File
@@ -2377,7 +2377,7 @@ namespace jdi
* @param [OUT] enumValue 输出采样原值(用于查枚举表)。
* @return 取到有效值返回 true。
*/
bool FreeMarkerValue(const Lane& lane, const Lane::FreeMarkerItem& item, double& value,
bool FreeMarkerValue(const Lane::FreeMarkerItem& item, double& value,
double& enumValue) const;
/**
+9 -7
View File
@@ -395,6 +395,9 @@ namespace jdi
// 预留,而实际只画出并排的一行 ⇒ 内容区下方多出一整行“无意义的空白”
// (用户 2026-11-03 连续三轮报告的那片空白,根因就在这一行)。
// ⚠️ **未启用双光标**时两个按钮都不显示 ⇒ footer 归零(否则面板会留一大段空白)。
// 「选区截取」的两个按钮 / 信号条目都只在**双光标**模式下存在;
// 这个判定同时决定面板高度(footer)、条目行与按钮几何,过去在三个分支里各算一遍,
// 任何一处写错就是「底部多一段空白」或「没双光标却能滚动」(用户 2026-11-03 报告)。
const bool clipReady = clipPanel && (Tab().CursorMode() == PlotCursorMode::DOUBLE);
const float footerHeight =
clipPanel ? (clipReady ? (buttonHeight + pad) : 0.0f)
@@ -432,7 +435,6 @@ namespace jdi
// ⚠️ 「选区截取」在**未启用双光标**时没有任何可操作内容(只有一行提示文字),
// 因此不按信号数累加行高、也不套用「至少 3 行」的下限
// (用户 2026-11-03 报告:未启用双光标时仍显示信号条目、且能触发滚动)。
const bool clipReady = clipPanel && (Tab().CursorMode() == PlotCursorMode::DOUBLE);
float content = 0.0f;
if (clipPanel && !clipReady)
{
@@ -781,8 +783,9 @@ namespace jdi
const float groupRow = kEdgePanelMeanRowHeight * m_scale;
const float sliderRow = kWeakPeakSliderRowHeight * m_scale;
const float scrollW = kFreeMarkerScrollWidth * m_scale;
const bool clipReadyHere =
clipPanel && (Tab().CursorMode() == PlotCursorMode::DOUBLE);
// ⚠️ 与高度算式共用**同一个** `clipReady`(见函数顶部):两处各算一遍时,
// 只要有一处条件写错,面板高度与内容区高度就会不一致(底部多出空白)。
const bool clipReadyHere = clipReady;
// ⚠️ 两处(高度算式与这里)必须同为**一行按钮**高:两个按钮并排,不是上下两行。
const float footerHeight =
clipPanel ? (clipReadyHere ? (buttonRowHeight + pad) : 0.0f)
@@ -1203,7 +1206,7 @@ namespace jdi
return layout;
}
bool PlotView::FreeMarkerValue(const Lane& lane, const Lane::FreeMarkerItem& item, double& value,
bool PlotView::FreeMarkerValue(const Lane::FreeMarkerItem& item, double& value,
double& enumValue) const
{
if (m_dataSet == nullptr)
@@ -1226,7 +1229,6 @@ namespace jdi
{
return false;
}
(void)lane;
enumValue = sampled; // 枚举按**原值**查
value = ApplyDrawTransform(*resolved.style, sampled); // 读数与画出来的波形一致
return std::isfinite(value);
@@ -1262,7 +1264,7 @@ namespace jdi
const Lane::FreeMarkerItem& previous = lane.freeMarkerItems[order[position - 1]];
double previousValue = 0.0;
double previousEnum = 0.0;
if (!FreeMarkerValue(lane, previous, previousValue, previousEnum))
if (!FreeMarkerValue(previous, previousValue, previousEnum))
{
return true; // 前一个点没有有效取值:不显示增量(避免编造一个数)
}
@@ -1354,7 +1356,7 @@ namespace jdi
entry.itemIndex = itemIndex;
double value = 0.0;
double enumValue = 0.0;
const bool valueOk = FreeMarkerValue(lane, item, value, enumValue);
const bool valueOk = FreeMarkerValue(item, value, enumValue);
bool hasPrev = false;
double delta = 0.0;
double timeDelta = 0.0;