refactor(panels): 几何算式收敛为单一来源 + 参数行提取(§17.6 第 1/2 项)
第 1 项:宽度算式与行高累加各自合并成一份来源
- 新增 `PanelColumnMetrics` + `MeasurePanelColumns(lane, kind)`:全仓唯一的一次"逐信号量宽"。
* `EdgePanelWidth(laneIndex, kind)` 退化为"量一次 + 算一次"(`EdgePanelWidthFor`);
* `EdgePanelLayoutFor` 量一次后,宽度与列几何(表格列 / 滑块轨道)都读同一份度量。
* 改前两者各写一份同名同形的 UiMeasureTextWidth 链,注释只写着"同一组算式 ⇒ 不会漂移",
那是靠人守的约定,改一处漏一处就是"面板变宽而列不动"(文字压到轨道上)。
- 新增 `PanelRowPlan` + `PlanListPanelRows(lane, kind)`:行高累加与行几何的唯一来源。
* 改前 `EdgePanelLayoutFor` 对同一批信号遍历两趟(先算总高、再出行几何),
两处判定(可见/勾选/收起/统计行/参数行数)必须逐字一致 —— 这类"两趟必须同步修改"
正是"改一次布局就出错"的另一半机制。
* 现在一个 {ref, checked, collapsed, hasStats, sliderRows} 数组同时喂高度算式与几何生成。
- 合并前先做等价性判别:`valueColumn` 两处算法不同但结果相同(100000 最宽)⇒ 可合并;
`sliderLabel` 与 `resampleLabel` 不可合并(重采样标签更长,并进会让均值/弱峰变宽)。
第 2 项:`PaintPanelListRows` 的通用参数行提取
- 新增 `PaintPanelParamRows(g, layout, row, kind, PanelParamState{…})`:中心轴统计行 +
两条滑块分支(均值/弱峰),`paintCounts`/`paintMeanSlider`/`paintPeakSlider` 三个 lambda
与 `valueColumn` 随之迁入。
- 逐行状态打包进 `PanelParamState`(解析结果 / 两种条目指针 / 网格下标)⇒ 参数表 5 个,
而不是 9 个散参数(签名比函数体更难读)。
- `PaintPanelListRows` 由约 300 行降到约 120 行,只负责"条目栏 + 按 kind 分派"。
- 顺手把机械搬移遗留的"整个函数体多缩进 2 个 tab"修正。
过程记录(两次锚点误伤,已写入文档避免重犯)
- 锚点 `// 两行滑块` 在 `EdgePanelWidthFor` 内也匹配 ⇒ 误删宽度函数尾部;
- 锚点 `float rowY = ...` 在表格分支先出现 ⇒ 误删「边沿计数」的行发射。
两次都是编译器立刻报错,从 Temp 备份回滚后重做。规则:整段替换的锚点必须唯一,
同名语句多次出现时先定位所属区块再取偏移。
第 3 项(体积)按用户指示暂停投入。
验证(双基线均为"零差异")
- make release / make debug / make -C Tools:零 warning;产物 782,336 → 781,824 B(−512)
- UnitTests:checks=260 failed=0 cases=19
- OffscreenChecks -all:7 检查 / 133 断言全 PASS
- panels -mode compare:7 面板像素哈希逐项不变(含 resample=2BFC74B3D9B3239C)
- rowgeom -mode compare:965+7 键 0 差异;-data MF4-Record.MF4 时 1268+7 键 0 差异
文档:Plan §十二、Progress 第 40 轮、Report §17、agent_mem 第 40 轮
This commit is contained in:
1 parent
1463f9d3f8
commit
a9f1563896
7 files changed
+1023
-599
No files matched your search
@@ -1972,6 +1972,108 @@ namespace jdi
|
||||
*/
|
||||
bool IsOnEdgeCursorLine(int x, int y, int& laneIndex) const;
|
||||
|
||||
/**
|
||||
* @brief 面板的「逐信号量宽」结果(宽度算式与列几何的**唯一来源**)。
|
||||
*
|
||||
* ⚠️⚠️ 历史上 `EdgePanelWidth`(决定面板宽)与 `EdgePanelLayoutFor`(决定列边界)
|
||||
* 各自写了一份**同名同形**的 `UiMeasureTextWidth` 链(标签列 / 数值列 / 重采样列),
|
||||
* 注释里写着"与 `EdgePanelWidth` 同一组算式 ⇒ 不会漂移" —— 但那是**靠人守**的约定:
|
||||
* 改一处漏一处,面板变宽而列不动(或反之),表现为文字压到轨道上 / 右边留一大片空白。
|
||||
* 现在两者都读同一个 `PanelColumnMetrics`,**没有第二份算式**。
|
||||
*/
|
||||
struct PanelColumnMetrics
|
||||
{
|
||||
/** 可见信号数(= 面板的数据行数上限;「自由标记」只算有固定点的信号)。 */
|
||||
std::size_t signalCount = 0;
|
||||
/** 名称列最大宽度(已按 `kEdgePanelNameMaxWidth` 夹取)。 */
|
||||
float nameColumn = 0.0f;
|
||||
/** 「边沿计数」表格 / 「中心轴」统计列:上升沿文本宽度(含“统计中…”)。 */
|
||||
float statRising = 0.0f;
|
||||
/** 下降沿文本宽度。 */
|
||||
float statFalling = 0.0f;
|
||||
/**
|
||||
* @brief 参数行的**标签列**宽度(「均值滤波」/「弱峰调整」两种标签取最大)。
|
||||
*
|
||||
* 两种面板共用同一列宽,这样同一个网格里切换面板时滑块不会横向跳动。
|
||||
* ⚠️ **不含**「重采样」的标签(它更长,见 `resampleLabel`):把它并进来会让
|
||||
* 均值/弱峰面板整体变宽 —— 历史行为就是分开算的,合并会改变像素。
|
||||
*/
|
||||
float sliderLabel = 0.0f;
|
||||
/** @brief 「重采样」四行的标签列宽度(四者取最大,比均值/弱峰的标签更长)。 */
|
||||
float resampleLabel = 0.0f;
|
||||
/**
|
||||
* @brief 参数行的**数值列**宽度(`100000` 这类最宽数值文本)。
|
||||
*
|
||||
* 它同时是"数值文本的裁剪宽度"(绘制侧)与"宽度预算的一项"(宽度算式),
|
||||
* 两处必须同值,否则数值会被截断或右侧留白。
|
||||
*/
|
||||
float valueColumn = 0.0f;
|
||||
/**
|
||||
* @brief 「重采样」两行只读统计的数值列宽度(逐信号实测真实文本,含(频率)尾缀)。
|
||||
*
|
||||
* 用户要求统计文本**不得省略**,所以这一列必须按真实内容量宽。
|
||||
*/
|
||||
float resampleValueColumn = 0.0f;
|
||||
};
|
||||
/**
|
||||
* @brief 量取某网格某面板的逐信号列宽(只读、无副作用)。
|
||||
*
|
||||
* ⚠️ 本函数会被鼠标移动的高频路径间接调用(`TopPanelAtPoint` / `SyncPanelOrder`),
|
||||
* 因此**不得**逐条查询采样值:边沿统计只能走 `EdgeCountsFor` / `EdgeCenterCountsFor`
|
||||
* 的缓存入口,枚举列只允许查 `SignalEnumText::Empty()`。
|
||||
*
|
||||
* @param [IN] lane 目标网格。
|
||||
* @param [IN] kind 面板种类。
|
||||
* @return 列宽度量(`signalCount` 与各列宽度)。
|
||||
*/
|
||||
PanelColumnMetrics MeasurePanelColumns(const Lane& lane, EdgePanelKind kind) const;
|
||||
|
||||
/**
|
||||
* @brief 依据已量好的 `PanelColumnMetrics` 计算面板宽度(宽度算式的**唯一实现**)。
|
||||
*
|
||||
* `EdgePanelLayoutFor` 与 `EdgePanelWidth` 都走它,且前者会把同一份 metrics 继续用于
|
||||
* 列几何 ⇒ 宽度与列边界**不可能不一致**(旧结构里两份算式各量一遍,只要有一处改了
|
||||
* 就会出现"面板变宽而列不动")。
|
||||
*
|
||||
* @param [IN] lane 目标网格。
|
||||
* @param [IN] kind 面板种类。
|
||||
* @param [IN] metrics 列宽度量(必须由 `MeasurePanelColumns(lane, kind)` 得到)。
|
||||
* @return 面板宽度(已夹到绘图区宽度之内)。
|
||||
*/
|
||||
float EdgePanelWidthFor(const Lane& lane, EdgePanelKind kind,
|
||||
const PanelColumnMetrics& metrics) const;
|
||||
|
||||
/**
|
||||
* @brief 「列表式面板」的一行**计划**(行高累加与行几何的**唯一来源**)。
|
||||
*
|
||||
* ⚠️⚠️ 旧实现有**两趟独立遍历**:第一趟只累加内容高度(`meanContentHeight`),
|
||||
* 第二趟才真正生成 `rows` 并累加 `rowY`。两趟的判定条件(可见性 / 勾选 / 收起 /
|
||||
* 是否需要统计行 / 参数行数)必须**逐字一致**,任何一处写错就表现为
|
||||
* "内容区高度与实际画出来的行不一致"(底部留白、或最后一行被裁掉、或明明没内容却能滚动)。
|
||||
* 现在只遍历一次得到本结构,高度与几何都从它派生 ⇒ 结构上不可能不一致。
|
||||
*/
|
||||
struct PanelRowPlan
|
||||
{
|
||||
SignalRef ref; /**< 该行的信号。 */
|
||||
bool checked = false; /**< 是否已勾选(决定展开三角与参数行是否存在)。 */
|
||||
bool collapsed = false; /**< 条目是否收起(收起时不生成参数行)。 */
|
||||
bool hasStats = false; /**< 是否有一行「上升 / 下降沿」统计(仅「中心轴」)。 */
|
||||
/** 参数行行数:0 = 不展开,2 = 两行滑块,4 = 「重采样」四行。 */
|
||||
int sliderRows = 0;
|
||||
};
|
||||
/**
|
||||
* @brief 生成某网格某「列表式」面板的逐信号行计划。
|
||||
*
|
||||
* 「边沿计数」是表格、「自由标记」是分组结构,两者都**不**使用本函数。
|
||||
* 「选区截取」的行不可展开 ⇒ `sliderRows` 恒为 0(是否生成行由调用方按
|
||||
* 「双光标是否就绪」决定:未就绪时整个计划作废,面板只显示一行提示)。
|
||||
*
|
||||
* @param [IN] lane 目标网格。
|
||||
* @param [IN] kind 面板种类。
|
||||
* @return 行计划(顺序 = 从上到下)。
|
||||
*/
|
||||
std::vector<PanelRowPlan> PlanListPanelRows(const Lane& lane, EdgePanelKind kind) const;
|
||||
|
||||
/**
|
||||
* @brief 实验性功能浮动面板的几何布局(三类面板共用同一套样式)。
|
||||
*/
|
||||
@@ -2315,6 +2417,43 @@ namespace jdi
|
||||
std::size_t laneIndex, const SignalRef& ref,
|
||||
const Lane::ResampleItem& item);
|
||||
|
||||
/**
|
||||
* @brief 「列表式面板」**单个信号参数行**绘制所需的逐行状态(一次收集、一次传入)。
|
||||
*
|
||||
* 打包成结构的目的是把 `PaintPanelParamRows` 的参数表压到 5 个
|
||||
* (`g` / `layout` / `row` / `kind` / `state`):参数行的绘制需要"解析结果 + 两种条目
|
||||
* 指针 + 网格下标"四样东西,逐个透传会让签名比函数体更难读。
|
||||
*/
|
||||
struct PanelParamState
|
||||
{
|
||||
/** 该信号的解析结果(「中心轴」统计行要用 `axis` / `data` / `style`)。 */
|
||||
const ResolvedSignal* resolved = nullptr;
|
||||
/** 「均值滤波」/「中心轴边沿计数」的逐信号参数(未勾选时为空)。 */
|
||||
const Lane::MeanFilterItem* meanItem = nullptr;
|
||||
/** 「弱峰调整」的逐信号参数(未勾选时为空)。 */
|
||||
const Lane::WeakPeakItem* peakItem = nullptr;
|
||||
/** 网格下标(与 `m_meanDragLane` / `m_editLane` 等状态比对)。 */
|
||||
std::size_t laneIndex = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 「列表式面板」**单个信号的参数行**(中心轴统计行 + 两行滑块)。
|
||||
*
|
||||
* 从 `PaintPanelListRows` 里提取(第 40 轮):三行/两行的滑块排版(标签 / 轨道 / 数值 /
|
||||
* 复位按钮,含拖拽态、悬停态、编辑态)与"条目栏"没有共同点,留在同一个函数里只会让
|
||||
* 行循环膨胀到三百行。**零逻辑改动**,判据 = `panels -mode compare` + `rowgeom -mode compare`。
|
||||
* 定义在 `PlotViewPanelPaint.cpp`(与另外两个内容函数同一分片)。
|
||||
*
|
||||
* @param [IN,OUT] g 绘图上下文(调用方已按内容区裁剪)。
|
||||
* @param [IN] layout 面板布局(内容区裁剪矩形、滑块/统计行矩形都来自它)。
|
||||
* @param [IN] row 该行的几何(`stats` / `levelTrack` / `iterTrack` 等字段)。
|
||||
* @param [IN] kind 面板种类(CENTER / MEAN / PEAK)。
|
||||
* @param [IN] state 逐行状态(解析结果 / 条目指针 / 网格下标)。
|
||||
*/
|
||||
void PaintPanelParamRows(gd::Graphics& g, const EdgePanelLayout& layout,
|
||||
const EdgePanelLayout::PanelRow& row, EdgePanelKind kind,
|
||||
const PanelParamState& state);
|
||||
|
||||
/**
|
||||
* @brief 「自由标记」面板的**内容区**(分组栏 + 表头 + 条目 + 吸顶)。
|
||||
*
|
||||
|
||||
Reference in new issue
Block a user