fix(plotview): 边沿计数表格归位、统计面板悬停时持续更新、加载不再被统计阻塞、统计提速
1) 表格改造归位(上一轮误做在中心轴上) - 「边沿计数」改为表格:表头(信号名/上升沿计数/下降沿计数)、每信号一行、 行底色取信号配色、三列对齐、内容可滚动(Lane::edgeCountScroll)。 - 「中心轴边沿计数」恢复为可滚动分组列表(条目栏 + 复选框 + 展开后统计行 + 两行滑块), Lane::EnsureEdgeCenterItem 恢复默认展开。 - EdgePanelWidth / EdgePanelLayoutFor / PaintEdgeCursor 统一 listStyle / countTable 判定; EdgeCenterTableHeader 更名为 EdgeCountTableHeader。 2) 鼠标持续移动时统计面板不更新 - 根因:交互态(拖拽或 140ms 内移动)把统计数字整段从面板缓存键中跳过, 只要鼠标不停移动就永远命中旧缓存。 - 修法:扫描推进/完成时自增 m_edgeScanRevision,Paint 开头转为 m_panelStatsVersion (非交互帧立即同步,交互帧按 kEdgeScanStatsRefreshFrames=4 节流), PanelSlotKey 对 COUNT/CENTER 仅混入该版本号(O(1),不再逐信号查询)。 3) 统计进行中无法加载文件 - 新增 PlotView::SetLoadingBusy:加载期间统计时间预算归零、跳过浮动面板绘制、 不再自请求扫描帧;Application::LoadFileAsync 进出各设置一次。实测加载 22~31ms。 4) 统计提速 - 扫描预算由固定采样对改为 QPC 时间片(非交互 6ms / 交互 1.5ms,每块检查超时), 采样对配额改为按未完成状态数公平分摊并把上限提到 8M(交互 2M)。 - 真实 MF4-Record.MF4(283 信号)全部统计状态在 1 帧内完成;悬停+移动帧中位 2~3ms。 验证:离屏探针 p20/p20c/p20d/p21/p22/p23/p25 全绿(新增 p25 12 项); make release / make debug 零 warning;Release 808,960 B。共享规则已同步到 Documents/agent_mem.md。
This commit is contained in:
1 parent
8de8a8fb85
commit
ea704b03ea
9 files changed
+424
-553
No files matched your search
@@ -28,12 +28,13 @@ namespace jdi
|
||||
const bool centerAxis = (kind == EdgePanelKind::CENTER);
|
||||
const bool meanFilter = (kind == EdgePanelKind::MEAN);
|
||||
const bool weakPeak = (kind == EdgePanelKind::PEAK);
|
||||
// 「均值滤波」「弱峰调整」= 可滚动的分组列表(信号色叠底条目栏 + 右侧复选框 +
|
||||
// 展开后的两行滑块);**「中心轴边沿计数」= 表格**(信号名 / 上升沿计数 / 下降沿计数)。
|
||||
// ⚠️ 旧式「逐条目」排版(名称 + 复选框 + 参数行,无滚动)已彻底删除:
|
||||
// 上百个信号时它会算出上万像素高的面板,超出绘图区后**面板像素缓存一次都存不进去**
|
||||
// ⇒ 每一帧整块重画 ⇒ 所有交互卡顿(用户 2026-11-02 报告)。
|
||||
const bool listStyle = (meanFilter || weakPeak);
|
||||
// 「均值滤波」「中心轴边沿计数」「弱峰调整」= 可滚动的分组列表
|
||||
//(信号色叠底条目栏 + 右侧复选框 + 展开后的参数行);
|
||||
// **「边沿计数」= 表格**(表头:信号名 / 上升沿计数 / 下降沿计数)。
|
||||
// ⚠️ 旧式「逐条目」排版(无滚动的名称+参数行)已彻底删除:上百个信号时它会算出
|
||||
// 上万像素高的面板,超出绘图区后**面板像素缓存一次都存不进去** ⇒ 每帧整块重画。
|
||||
const bool listStyle = (centerAxis || meanFilter || weakPeak);
|
||||
const bool countTable = (kind == EdgePanelKind::COUNT);
|
||||
|
||||
// 标题行 = “GX” 徽标 + 标题(“边沿计数” / “中心轴边沿计数” / “均值滤波” / “弱峰调整”
|
||||
// + 光标区间尾缀)。
|
||||
@@ -78,15 +79,30 @@ namespace jdi
|
||||
(kFreeMarkerScrollWidth + 4.0f) * m_scale;
|
||||
width = std::max(width, groupWidth);
|
||||
}
|
||||
if (!countTable && !centerAxis)
|
||||
{
|
||||
continue; // 只有「边沿计数」表格 / 「中心轴」统计行需要量计数列宽
|
||||
}
|
||||
std::size_t rising = 0;
|
||||
std::size_t falling = 0;
|
||||
bool complete = true;
|
||||
if (meanFilter || weakPeak)
|
||||
if (countTable)
|
||||
{
|
||||
continue;
|
||||
EdgeCountsFor(*resolved.axis, *resolved.data, *resolved.style, lane.edgeValue,
|
||||
EdgeRangeActive(), EdgeRangeMin(), EdgeRangeMax(), rising, falling, complete);
|
||||
// 宽度按实际要显示的文本测量(含未算完时的“…”),否则推进过程中数字变长会被截断。
|
||||
const float pendingWidth = UiMeasureTextWidth(m_fonts->monoSmall, L"统计中…");
|
||||
statRisingWidth = std::max(
|
||||
statRisingWidth,
|
||||
std::max(UiMeasureTextWidth(m_fonts->monoSmall, EdgeCountCellText(rising, complete)),
|
||||
pendingWidth));
|
||||
statFallingWidth =
|
||||
std::max(statFallingWidth,
|
||||
UiMeasureTextWidth(m_fonts->monoSmall, EdgeCountCellText(falling, complete)));
|
||||
}
|
||||
if (centerAxis)
|
||||
else
|
||||
{
|
||||
// 「中心轴边沿计数」的统计行 = 「上升沿 N / 下降沿 M」**两列**(只量已勾选的条目)。
|
||||
const Lane::EdgeCenterItem* item = lane.FindEdgeCenterItem(ref);
|
||||
if (item == nullptr)
|
||||
{
|
||||
@@ -95,49 +111,37 @@ namespace jdi
|
||||
EdgeCenterCountsFor(*resolved.axis, *resolved.data, *resolved.style, item->points,
|
||||
item->iterations, EdgeRangeActive(), EdgeRangeMin(), EdgeRangeMax(),
|
||||
rising, falling, complete);
|
||||
const float pendingWidth = UiMeasureTextWidth(m_fonts->monoSmall, L"统计中…");
|
||||
statRisingWidth = std::max(
|
||||
statRisingWidth,
|
||||
std::max(UiMeasureTextWidth(m_fonts->monoSmall, EdgeRisingText(rising, complete)),
|
||||
pendingWidth));
|
||||
statFallingWidth = std::max(
|
||||
statFallingWidth,
|
||||
UiMeasureTextWidth(m_fonts->monoSmall, EdgeFallingText(falling, complete)));
|
||||
}
|
||||
else
|
||||
{
|
||||
EdgeCountsFor(*resolved.axis, *resolved.data, *resolved.style, lane.edgeValue,
|
||||
EdgeRangeActive(), EdgeRangeMin(), EdgeRangeMax(), rising, falling, complete);
|
||||
}
|
||||
// 宽度按实际要显示的文本测量(含未算完时的“…”),否则推进过程中数字变长会被截断。
|
||||
// 完全未算出时整行显示“统计中…”,因此两列都要预留它的宽度。
|
||||
const float pendingWidth = UiMeasureTextWidth(m_fonts->monoSmall, L"统计中…");
|
||||
statRisingWidth = std::max(
|
||||
statRisingWidth,
|
||||
std::max(UiMeasureTextWidth(m_fonts->monoSmall, EdgeCountCellText(rising, complete)),
|
||||
pendingWidth));
|
||||
statFallingWidth =
|
||||
std::max(statFallingWidth,
|
||||
UiMeasureTextWidth(m_fonts->monoSmall, EdgeCountCellText(falling, complete)));
|
||||
}
|
||||
if (centerAxis)
|
||||
if (countTable)
|
||||
{
|
||||
// 「中心轴边沿计数」= **表格**(信号名 / 上升沿计数 / 下降沿计数):
|
||||
// 宽度 = 复选框 + 名称列 + 两列计数(列宽取「表头文字」「实际数字」的较大者)+ 滚动条。
|
||||
// 「边沿计数」= **表格**(信号名 / 上升沿计数 / 下降沿计数):
|
||||
// 宽度 = 名称列 + 两列计数(列宽取「表头文字」「实际数字」的较大者)+ 滚动条留位。
|
||||
const float headerRising =
|
||||
UiMeasureTextWidth(m_fonts->uiSmall, EdgeCenterTableHeader(1));
|
||||
UiMeasureTextWidth(m_fonts->uiSmall, EdgeCountTableHeader(1));
|
||||
const float headerFalling =
|
||||
UiMeasureTextWidth(m_fonts->uiSmall, EdgeCenterTableHeader(2));
|
||||
UiMeasureTextWidth(m_fonts->uiSmall, EdgeCountTableHeader(2));
|
||||
const float risingColumn = std::max(headerRising, statRisingWidth) + 10.0f * m_scale;
|
||||
const float fallingColumn = std::max(headerFalling, statFallingWidth) + 10.0f * m_scale;
|
||||
const float gap = kEdgePanelStatColumnGap * m_scale;
|
||||
width = std::max(width, kEdgePanelPad * 2.0f * m_scale +
|
||||
kEdgePanelDisclosureWidth * m_scale + 6.0f * m_scale +
|
||||
kEdgePanelMeanCheckSize * m_scale + 6.0f * m_scale +
|
||||
std::min(nameColumnWidth, kEdgePanelNameMaxWidth * m_scale) +
|
||||
gap + risingColumn + gap + fallingColumn +
|
||||
nameColumnWidth + gap + risingColumn + gap + fallingColumn +
|
||||
(kFreeMarkerScrollWidth + 4.0f) * m_scale);
|
||||
}
|
||||
else if (statRisingWidth > 0.0f || statFallingWidth > 0.0f)
|
||||
else if (centerAxis && (statRisingWidth > 0.0f || statFallingWidth > 0.0f))
|
||||
{
|
||||
// 两列**等宽**(取两列最大值中的较大者)→ 绘制时两列起点固定,
|
||||
// 多条信号的“上升沿 / 下降沿”文字严格垂直对齐(用户明确要求)。
|
||||
// 「中心轴」的统计行 = 两列**等宽**(列起点固定 ⇒ 多条信号严格垂直对齐)。
|
||||
const float maxColumn = std::max(statRisingWidth, statFallingWidth);
|
||||
const float countsWidth = maxColumn * 2.0f + kEdgePanelStatColumnGap * m_scale;
|
||||
width = std::max(width, kEdgePanelPad * 2.0f * m_scale +
|
||||
std::max(nameColumnWidth, countsWidth));
|
||||
width = std::max(width, kEdgePanelPad * 2.0f * m_scale + countsWidth);
|
||||
}
|
||||
|
||||
if (listStyle)
|
||||
@@ -170,18 +174,18 @@ namespace jdi
|
||||
width = std::max(width, kEdgePanelPad * 2.0f * m_scale + rowWidth);
|
||||
}
|
||||
|
||||
if (listStyle)
|
||||
if (meanFilter || weakPeak)
|
||||
{
|
||||
// 底部按钮行 = 「构建为新信号」,按按钮文字实测宽度确定下限。
|
||||
// 底部按钮行 = 「构建为新信号」(仅均值滤波 / 弱峰调整有),按按钮文字实测宽度确定下限。
|
||||
const float buttonWidth =
|
||||
UiMeasureTextWidth(m_fonts->uiSmall, kBuildSignalLabel) + 28.0f * m_scale;
|
||||
width = std::max(width, kEdgePanelPad * 2.0f * m_scale + buttonWidth);
|
||||
}
|
||||
|
||||
if (kind == EdgePanelKind::COUNT)
|
||||
if (countTable)
|
||||
{
|
||||
// 「边沿计数」:右侧要给内容滚动条留位(上百个信号时内容必然超高)。
|
||||
width = std::max(width, kEdgePanelPad * 3.0f * m_scale +
|
||||
// 「边沿计数」表格:右侧要给内容滚动条留位(上百个信号时内容必然超高)。
|
||||
width = std::max(width, kEdgePanelPad * 2.0f * m_scale +
|
||||
(kFreeMarkerScrollWidth + 4.0f) * m_scale);
|
||||
}
|
||||
|
||||
@@ -272,10 +276,10 @@ namespace jdi
|
||||
const bool meanFilter = (kind == EdgePanelKind::MEAN);
|
||||
const bool weakPeak = (kind == EdgePanelKind::PEAK);
|
||||
const bool freeMarker = (kind == EdgePanelKind::MARK);
|
||||
// 「均值滤波」与「弱峰调整」共用同一套**可滚动分组列表**排布(信号色叠底条目栏 +
|
||||
// 展开后的两行滑块);「中心轴边沿计数」自 2026-11-02 起单独走**表格**分支(`centerTable`)。
|
||||
const bool listStyle = (meanFilter || weakPeak);
|
||||
const bool centerTable = centerAxis;
|
||||
// 「均值滤波」「中心轴边沿计数」「弱峰调整」共用同一套**可滚动分组列表**排布
|
||||
//(信号色叠底条目栏 + 展开后的参数行);「边沿计数」自 2026-11-02 起走**表格**分支。
|
||||
const bool listStyle = (meanFilter || centerAxis || weakPeak);
|
||||
const bool countTable = (kind == EdgePanelKind::COUNT);
|
||||
const bool hasButton = (meanFilter || weakPeak);
|
||||
const bool enabled = meanFilter ? lane.meanFilter
|
||||
: (weakPeak ? lane.weakPeak
|
||||
@@ -317,16 +321,11 @@ namespace jdi
|
||||
|
||||
const float width = EdgePanelWidth(laneIndex, kind);
|
||||
const float titleHeight = kEdgePanelTitleHeight * m_scale;
|
||||
const float rowHeight = kEdgePanelRowHeight * m_scale;
|
||||
const float pad = kEdgePanelPad * m_scale;
|
||||
// 「中心轴边沿计数」「均值滤波」「弱峰调整」共用同一套**可滚动列表**排布,
|
||||
// 因此不再有独立的底部编辑行;只有「边沿计数」保留两行文本式排版(同样可滚动)。
|
||||
// 因此不再有独立的底部编辑行;「边沿计数」走独立的**表格**分支。
|
||||
const float buttonHeight = hasButton ? (kEdgePanelButtonHeight * m_scale) : 0.0f;
|
||||
const float rowsPerSignal = 2.0f; // 「边沿计数」每信号两行(名称 / 上升沿·下降沿)
|
||||
// 面板底部(内容区之下)占的纵向空间:有按钮时 = pad + 按钮 + pad,
|
||||
// 无按钮时只有 pad。⚠️ 旧实现无条件按「按钮行 + 三个 pad」预留 ——
|
||||
// 无按钮的面板(中心轴边沿计数)因此在内容区下方留下**一大块空白**
|
||||
//(= 按钮高度 30 逻辑像素 + 多出的 pad,用户 2026-11-02 报告)。
|
||||
// 面板底部(内容区之下)占的纵向空间:有按钮时 = pad + 按钮 + pad,无按钮时只有 pad。
|
||||
const float footerHeight = hasButton ? (buttonHeight + pad) : 0.0f;
|
||||
const float bottomPad = pad;
|
||||
|
||||
@@ -334,31 +333,12 @@ namespace jdi
|
||||
float meanContentHeight = 0.0f;
|
||||
float height = 0.0f;
|
||||
float tableHeaderHeight = 0.0f;
|
||||
if (centerTable)
|
||||
if (countTable)
|
||||
{
|
||||
// 「中心轴边沿计数」= **表格**:表头(信号名 / 上升沿计数 / 下降沿计数)固定不滚动,
|
||||
// 下面每条**已勾选**的信号占一行(行底色 = 信号配色),单击行展开两行参数滑块。
|
||||
// 默认只显示表格行(`EnsureEdgeCenterItem` 新建条目时 `collapsed = true`)。
|
||||
// 「边沿计数」= **表格**:表头(信号名 / 上升沿计数 / 下降沿计数)固定不滚动,
|
||||
// 下面每条可见信号占一行(行底色 = 信号配色),内容超高时滚动。
|
||||
const float rowH = kEdgePanelMeanRowHeight * m_scale;
|
||||
float content = 0.0f;
|
||||
for (const SignalRef& ref : lane.signals)
|
||||
{
|
||||
const ResolvedSignal resolved = ResolveSignal(ref);
|
||||
if (resolved.data == nullptr || !resolved.visible)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const Lane::EdgeCenterItem* item = lane.FindEdgeCenterItem(ref);
|
||||
if (item == nullptr && !resolved.visible)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
content += rowH;
|
||||
if (item != nullptr && !item->collapsed)
|
||||
{
|
||||
content += weakPeakSliderRow * 2.0f;
|
||||
}
|
||||
}
|
||||
const float content = static_cast<float>(rowCount) * rowH;
|
||||
meanContentHeight = content;
|
||||
tableHeaderHeight = rowH;
|
||||
const float budget =
|
||||
@@ -371,8 +351,10 @@ namespace jdi
|
||||
}
|
||||
else if (listStyle)
|
||||
{
|
||||
// 「均值滤波 / 弱峰调整」:内容区 = 逐信号的**信号色叠底条目栏**
|
||||
//(内容总是显示,用于勾选/取消)+(勾选且未收起时)两行参数滑块。
|
||||
// 「均值滤波 / 中心轴边沿计数 / 弱峰调整」:内容区 = 逐信号的**信号色叠底条目栏**
|
||||
//(内容总是显示,用于勾选/取消)+(勾选且未收起时)参数行。
|
||||
// 「中心轴」的参数行 = 一行「上升沿 / 下降沿」统计 + 两行滑块;
|
||||
// 「均值滤波」与「弱峰调整」= 两行滑块。
|
||||
// 内容超高时出现滚动条;底部「构建为新信号」按钮固定不滚动。
|
||||
const float groupRow = kEdgePanelMeanRowHeight * m_scale;
|
||||
const float sliderRow = weakPeakSliderRow;
|
||||
@@ -391,6 +373,11 @@ namespace jdi
|
||||
const Lane::WeakPeakItem* item = lane.FindWeakPeakItem(ref);
|
||||
expanded = (item != nullptr && !item->collapsed);
|
||||
}
|
||||
else if (centerAxis)
|
||||
{
|
||||
const Lane::EdgeCenterItem* item = lane.FindEdgeCenterItem(ref);
|
||||
expanded = (item != nullptr && !item->collapsed);
|
||||
}
|
||||
else
|
||||
{
|
||||
const Lane::MeanFilterItem* item = lane.FindMeanFilterItem(ref);
|
||||
@@ -399,6 +386,10 @@ namespace jdi
|
||||
if (expanded)
|
||||
{
|
||||
content += sliderRow * 2.0f;
|
||||
if (centerAxis)
|
||||
{
|
||||
content += groupRow; // 统计行(上升沿 / 下降沿)
|
||||
}
|
||||
}
|
||||
}
|
||||
meanContentHeight = content;
|
||||
@@ -417,14 +408,8 @@ namespace jdi
|
||||
}
|
||||
else
|
||||
{
|
||||
// 「边沿计数」:每信号两行(名称 / 上升沿·下降沿),内容超高时滚动。
|
||||
const float content = static_cast<float>(rowCount) * rowHeight * rowsPerSignal;
|
||||
meanContentHeight = content;
|
||||
const float budget = m_plotRect.Height - (titleHeight + pad + bottomPad);
|
||||
const float maxContent =
|
||||
std::max(2.0f * rowHeight, std::min(kFreeMarkerContentMaxHeight * m_scale, budget));
|
||||
const float view = std::min(content, maxContent);
|
||||
height = titleHeight + pad + view + bottomPad;
|
||||
// 不应到达:所有面板种类都已覆盖。保留一个保守的高度以免布局全零。
|
||||
height = titleHeight + pad + bottomPad;
|
||||
}
|
||||
|
||||
// 「自由标记」面板:按信号分组(**分组栏** + 下属固定点条目),
|
||||
@@ -598,18 +583,17 @@ namespace jdi
|
||||
{
|
||||
return layout;
|
||||
}
|
||||
if (centerTable)
|
||||
if (countTable)
|
||||
{
|
||||
// 「中心轴边沿计数」= **表格**:表头固定、内容区可滚动。
|
||||
// 列:① 信号名(含复选框与展开三角)② 上升沿计数 ③ 下降沿计数。
|
||||
// 「边沿计数」= **表格**:表头固定、内容区可滚动。
|
||||
// 列:① 信号名 ② 上升沿计数 ③ 下降沿计数(行底色 = 信号配色)。
|
||||
const float rowH = kEdgePanelMeanRowHeight * m_scale;
|
||||
const float sliderRow = kWeakPeakSliderRowHeight * m_scale;
|
||||
const float scrollW = kFreeMarkerScrollWidth * m_scale;
|
||||
const float viewHeight =
|
||||
std::max(0.0f, height - titleHeight - pad - tableHeaderHeight - pad);
|
||||
const bool scrollable = meanContentHeight > viewHeight + 0.5f;
|
||||
const float maxScroll = std::max(0.0f, meanContentHeight - viewHeight);
|
||||
const float scroll = std::max(0.0f, std::min(lane.edgeCenterScroll, maxScroll));
|
||||
const float scroll = std::max(0.0f, std::min(lane.edgeCountScroll, maxScroll));
|
||||
const float innerWidth =
|
||||
std::max(0.0f, width - pad * 2.0f - (scrollable ? (scrollW + 4.0f * m_scale) : 0.0f));
|
||||
layout.tableHeader =
|
||||
@@ -631,32 +615,32 @@ namespace jdi
|
||||
(viewHeight - thumbHeight) * ((maxScroll > 0.0f) ? (scroll / maxScroll) : 0.0f);
|
||||
layout.scrollThumb = gd::RectF(layout.scrollTrack.X, thumbTop, scrollW, thumbHeight);
|
||||
}
|
||||
|
||||
// 三列:名称列吃掉剩余宽度,两列计数按内容宽度固定右对齐 —— 表头与数据行严格对齐。
|
||||
const float closeSize = kEdgePanelMeanCheckSize * m_scale;
|
||||
// 三列:名称列吃掉剩余宽度,两列计数按内容宽度固定 —— 表头与数据行严格对齐。
|
||||
const float gap = kEdgePanelStatColumnGap * m_scale;
|
||||
const float contentLeft = layout.content.X;
|
||||
// 计数列宽 = 「表头文字」「当前数字」的较大者(与 `EdgePanelWidth` 同源)。
|
||||
float risingCol = UiMeasureTextWidth(m_fonts->uiSmall, EdgeCenterTableHeader(1)) + 10.0f * m_scale;
|
||||
float fallingCol = UiMeasureTextWidth(m_fonts->uiSmall, EdgeCenterTableHeader(2)) + 10.0f * m_scale;
|
||||
float risingCol =
|
||||
UiMeasureTextWidth(m_fonts->uiSmall, EdgeCountTableHeader(1)) + 10.0f * m_scale;
|
||||
float fallingCol =
|
||||
UiMeasureTextWidth(m_fonts->uiSmall, EdgeCountTableHeader(2)) + 10.0f * m_scale;
|
||||
for (const SignalRef& ref : lane.signals)
|
||||
{
|
||||
const ResolvedSignal resolved = ResolveSignal(ref);
|
||||
const Lane::EdgeCenterItem* item = lane.FindEdgeCenterItem(ref);
|
||||
if (resolved.data == nullptr || resolved.axis == nullptr || resolved.style == nullptr ||
|
||||
item == nullptr)
|
||||
if (resolved.data == nullptr || resolved.axis == nullptr ||
|
||||
resolved.style == nullptr || !resolved.visible)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
std::size_t rising = 0;
|
||||
std::size_t falling = 0;
|
||||
bool complete = true;
|
||||
EdgeCenterCountsFor(*resolved.axis, *resolved.data, *resolved.style, item->points,
|
||||
item->iterations, EdgeRangeActive(), EdgeRangeMin(), EdgeRangeMax(),
|
||||
rising, falling, complete);
|
||||
risingCol = std::max(risingCol,
|
||||
UiMeasureTextWidth(m_fonts->monoSmall, EdgeCountCellText(rising, complete)) +
|
||||
10.0f * m_scale);
|
||||
EdgeCountsFor(*resolved.axis, *resolved.data, *resolved.style, lane.edgeValue,
|
||||
EdgeRangeActive(), EdgeRangeMin(), EdgeRangeMax(), rising, falling,
|
||||
complete);
|
||||
risingCol = std::max(
|
||||
risingCol,
|
||||
UiMeasureTextWidth(m_fonts->monoSmall, EdgeCountCellText(rising, complete)) +
|
||||
10.0f * m_scale);
|
||||
fallingCol = std::max(
|
||||
fallingCol,
|
||||
UiMeasureTextWidth(m_fonts->monoSmall, EdgeCountCellText(falling, complete)) +
|
||||
@@ -670,21 +654,6 @@ namespace jdi
|
||||
layout.tableColumns[1].width = risingCol;
|
||||
layout.tableColumns[2].left = layout.tableColumns[1].left + risingCol + gap;
|
||||
layout.tableColumns[2].width = fallingCol;
|
||||
|
||||
// 参数行(两行滑块)的列宽:与均值滤波同一套算式。
|
||||
const float labelColumn =
|
||||
std::max(UiMeasureTextWidth(m_fonts->uiSmall, kMeanFilterPointsSliderLabel),
|
||||
UiMeasureTextWidth(m_fonts->uiSmall, kMeanFilterIterationsSliderLabel));
|
||||
const float valueColumn = UiMeasureTextWidth(m_fonts->monoSmall, L"100000");
|
||||
const float resetSize = kWeakPeakResetButtonSize * m_scale;
|
||||
const float resetGap = kWeakPeakResetGap * m_scale;
|
||||
const float trackHeight = kWeakPeakTrackHeight * m_scale;
|
||||
const float trackLeft = contentLeft + labelColumn + 6.0f * m_scale;
|
||||
const float resetLeft = contentLeft + innerWidth - resetSize;
|
||||
const float trackWidth =
|
||||
std::max(10.0f * m_scale,
|
||||
(resetLeft - resetGap - valueColumn - 6.0f * m_scale) - trackLeft);
|
||||
|
||||
float rowY = layout.content.Y - scroll;
|
||||
for (const SignalRef& ref : lane.signals)
|
||||
{
|
||||
@@ -693,63 +662,23 @@ namespace jdi
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const Lane::EdgeCenterItem* item = lane.FindEdgeCenterItem(ref);
|
||||
if (item == nullptr && !resolved.visible)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// 未勾选(`item == nullptr`)的行也列出来:它是“启用统计”的唯一入口
|
||||
//(表格模式下没有独立的开关;点行内复选框即启用 / 取消)。
|
||||
const bool checked = (item != nullptr);
|
||||
const bool expanded = (item != nullptr && !item->collapsed);
|
||||
layout.rowRefs.push_back(ref);
|
||||
layout.rowHeaders.push_back(gd::RectF(contentLeft, rowY, innerWidth, rowH));
|
||||
layout.rowNameRects.push_back(gd::RectF(
|
||||
layout.tableColumns[0].left + kEdgePanelDisclosureWidth * m_scale + 4.0f * m_scale,
|
||||
rowY, std::max(10.0f, nameColumn - closeSize - kEdgePanelDisclosureWidth * m_scale),
|
||||
rowH));
|
||||
layout.rowTriangles.push_back(
|
||||
checked ? gd::RectF(contentLeft + 2.0f * m_scale,
|
||||
rowY + (rowH - kEdgePanelRowTriangleSize * m_scale) * 0.5f,
|
||||
kEdgePanelRowTriangleSize * m_scale,
|
||||
kEdgePanelRowTriangleSize * m_scale)
|
||||
: gd::RectF());
|
||||
layout.rowChecks.push_back(gd::RectF(
|
||||
contentLeft + nameColumn - closeSize - 2.0f * m_scale,
|
||||
rowY + (rowH - closeSize) * 0.5f, closeSize, closeSize));
|
||||
layout.rowNameRects.push_back(
|
||||
gd::RectF(contentLeft + 4.0f * m_scale, rowY,
|
||||
std::max(10.0f, nameColumn - 8.0f * m_scale), rowH));
|
||||
layout.rowTriangles.push_back(gd::RectF());
|
||||
layout.rowChecks.push_back(gd::RectF());
|
||||
layout.rowPoints.push_back(gd::RectF());
|
||||
layout.rowIterations.push_back(gd::RectF());
|
||||
layout.rowStats.push_back(gd::RectF());
|
||||
layout.rowLevelTracks.push_back(gd::RectF());
|
||||
layout.rowLevelValues.push_back(gd::RectF());
|
||||
layout.rowLevelResets.push_back(gd::RectF());
|
||||
layout.rowIterTracks.push_back(gd::RectF());
|
||||
layout.rowIterValues.push_back(gd::RectF());
|
||||
layout.rowIterResets.push_back(gd::RectF());
|
||||
rowY += rowH;
|
||||
|
||||
if (!expanded)
|
||||
{
|
||||
layout.rowLevelTracks.push_back(gd::RectF());
|
||||
layout.rowLevelValues.push_back(gd::RectF());
|
||||
layout.rowLevelResets.push_back(gd::RectF());
|
||||
layout.rowIterTracks.push_back(gd::RectF());
|
||||
layout.rowIterValues.push_back(gd::RectF());
|
||||
layout.rowIterResets.push_back(gd::RectF());
|
||||
continue;
|
||||
}
|
||||
const gd::RectF pointsTrack(trackLeft, rowY + (sliderRow - trackHeight) * 0.5f,
|
||||
trackWidth, trackHeight);
|
||||
layout.rowLevelTracks.push_back(pointsTrack);
|
||||
layout.rowLevelValues.push_back(
|
||||
gd::RectF(pointsTrack.X + pointsTrack.Width + 6.0f * m_scale, rowY, valueColumn,
|
||||
sliderRow));
|
||||
layout.rowLevelResets.push_back(
|
||||
gd::RectF(resetLeft, rowY + (sliderRow - resetSize) * 0.5f, resetSize, resetSize));
|
||||
rowY += sliderRow;
|
||||
const gd::RectF iterTrack(trackLeft, rowY + (sliderRow - trackHeight) * 0.5f,
|
||||
trackWidth, trackHeight);
|
||||
layout.rowIterTracks.push_back(iterTrack);
|
||||
layout.rowIterValues.push_back(
|
||||
gd::RectF(iterTrack.X + iterTrack.Width + 6.0f * m_scale, rowY, valueColumn,
|
||||
sliderRow));
|
||||
layout.rowIterResets.push_back(
|
||||
gd::RectF(resetLeft, rowY + (sliderRow - resetSize) * 0.5f, resetSize, resetSize));
|
||||
rowY += sliderRow;
|
||||
}
|
||||
}
|
||||
else if (listStyle)
|
||||
@@ -771,7 +700,8 @@ namespace jdi
|
||||
std::max(0.0f, height - titleHeight - pad - pad - footerHeight);
|
||||
const bool scrollable = meanContentHeight > viewHeight + 0.5f;
|
||||
const float maxScroll = std::max(0.0f, meanContentHeight - viewHeight);
|
||||
const float storedScroll = meanFilter ? lane.meanFilterScroll : lane.weakPeakScroll;
|
||||
const float storedScroll = meanFilter ? lane.meanFilterScroll
|
||||
: (weakPeak ? lane.weakPeakScroll : lane.edgeCenterScroll);
|
||||
const float scroll = std::max(0.0f, std::min(storedScroll, maxScroll));
|
||||
const float innerWidth =
|
||||
std::max(0.0f, width - pad * 2.0f - (scrollable ? (scrollW + 4.0f * m_scale) : 0.0f));
|
||||
@@ -827,6 +757,12 @@ namespace jdi
|
||||
checked = (item != nullptr);
|
||||
itemCollapsed = (item != nullptr && item->collapsed);
|
||||
}
|
||||
else if (centerAxis)
|
||||
{
|
||||
const Lane::EdgeCenterItem* item = lane.FindEdgeCenterItem(ref);
|
||||
checked = (item != nullptr);
|
||||
itemCollapsed = (item != nullptr && item->collapsed);
|
||||
}
|
||||
else
|
||||
{
|
||||
const Lane::MeanFilterItem* item = lane.FindMeanFilterItem(ref);
|
||||
@@ -863,7 +799,12 @@ namespace jdi
|
||||
layout.rowIterResets.push_back(gd::RectF());
|
||||
continue;
|
||||
}
|
||||
// 「中心轴边沿计数」已改为独立表格分支,这里不再需要额外的统计行。
|
||||
// 「中心轴边沿计数」:参数行最上面是**同一行**的「上升沿 / 下降沿」统计。
|
||||
if (centerAxis && !layout.rowStats.empty())
|
||||
{
|
||||
layout.rowStats.back() = gd::RectF(contentLeft, rowY, innerWidth, groupRow);
|
||||
rowY += groupRow;
|
||||
}
|
||||
const gd::RectF pointsTrack(trackLeft, rowY + (sliderRow - trackHeight) * 0.5f,
|
||||
trackWidth, trackHeight);
|
||||
layout.rowLevelTracks.push_back(pointsTrack);
|
||||
@@ -885,32 +826,6 @@ namespace jdi
|
||||
rowY += sliderRow;
|
||||
}
|
||||
}
|
||||
else if (!freeMarker)
|
||||
{
|
||||
// 「边沿计数」:内容区 = 逐信号两行(名称 / 上升沿·下降沿),可滚动。
|
||||
const float scrollW = kFreeMarkerScrollWidth * m_scale;
|
||||
const float viewHeight = std::max(0.0f, height - titleHeight - pad - pad);
|
||||
const bool scrollable = meanContentHeight > viewHeight + 0.5f;
|
||||
const float maxScroll = std::max(0.0f, meanContentHeight - viewHeight);
|
||||
const float scroll = std::max(0.0f, std::min(lane.edgeCountScroll, maxScroll));
|
||||
const float innerWidth =
|
||||
std::max(0.0f, width - pad * 2.0f - (scrollable ? (scrollW + 4.0f * m_scale) : 0.0f));
|
||||
layout.content = gd::RectF(left + pad, top + titleHeight + pad, innerWidth, viewHeight);
|
||||
layout.markerContentHeight = meanContentHeight;
|
||||
layout.scrollOffset = scroll;
|
||||
if (scrollable)
|
||||
{
|
||||
layout.scrollTrack =
|
||||
gd::RectF(left + pad + innerWidth + 4.0f * m_scale, layout.content.Y, scrollW,
|
||||
viewHeight);
|
||||
const float thumbHeight =
|
||||
std::max(18.0f * m_scale, viewHeight * (viewHeight / meanContentHeight));
|
||||
const float thumbTop =
|
||||
layout.content.Y +
|
||||
(viewHeight - thumbHeight) * ((maxScroll > 0.0f) ? (scroll / maxScroll) : 0.0f);
|
||||
layout.scrollThumb = gd::RectF(layout.scrollTrack.X, thumbTop, scrollW, thumbHeight);
|
||||
}
|
||||
}
|
||||
|
||||
if (freeMarker)
|
||||
{
|
||||
@@ -1704,4 +1619,4 @@ namespace jdi
|
||||
return probe;
|
||||
}
|
||||
|
||||
} // namespace jdi
|
||||
} // namespace jdi
|
||||
Reference in new issue
Block a user