feat(clip): 新增「选区截取」实验性功能 + 筛选态组复选框只作用于筛选结果 + 重采样统计不被省略
一、重采样面板统计信息不再被省略为 `…`
- 根因:EdgePanelWidth 里数值列按"典型样例"(1h 1min 2s(1.23456 GHz))估宽,
真实文本(16ms 182.614us(61.795 Hz))更长 => 绘制矩形不够宽 => 尾部被裁。
- 新增 FormatIntervalWithFrequency() 作为「间隔(频率)」文本的唯一实现,
宽度计算与绘制共用它;EdgePanelWidth 改为逐信号实测真实文本宽度取最大值。
二、筛选状态下组复选框只作用于「筛选出来的信号」
- TabConfig 新增 SetGroupVisibleSubset / GroupAllVisibleSubset / GroupHasVisibleSubset,
只读写掩码内元素,未通过筛选的信号可见性保持不变。
- SignalPanel::BuildFilterMask 提供逐信号掩码(未筛选时返回 false,走整组语义)。
- 组复选框的点击、绘制、行位图缓存键三处统一改用子集判定
(只改绘制不改缓存键会导致勾选后复选框不刷新)。
三、新增实验性功能「选区截取」(EdgePanelKind::CLIP = 6,PLOT_CMD_LANE_CLIP = 14)
- 菜单位置:实验性功能二级菜单,「重采样」之下、「自由标记」上方,带勾选。
- 面板:逐信号一行(信号色叠底条目栏 + 右侧复选框,不可展开),内容可滚动;
底部两个不滚动的按钮「截取为新信号」与「原始信号截取」;
未启用双光标时两个按钮为空矩形并给出提示。
- 截取为新信号:区间内原始数据(不烘焙)+ 继承源信号整套调整,
名称为 <源信号名> (AAAA - BBBB)(显示时刻、保留三位小数、带 s),插入源信号下方,
使用 AppendGroupAxis 新建的独立时间轴。
- 原始信号截取:DataSet::ClipSignalRange 就地裁剪时间轴与采样,
用新建+swap+shrink_to_fit 真正归还内存,并重算统计与轴的 minTime/maxTime/monotonic。
调用前先 PauseDerivedAsync() 避免后台派生读到已释放采样。
- 时间轴偏移参与区间比较:双光标是显示时刻(time+offsetTime),
BuildClippedSignal 比较时加偏移,ClipSignalInPlace 换算回采样时刻(low-offsetTime)。
- 同步点已全部补齐:LaneCarry(三处)/SplitLane/PruneLaneSignalItems/RemapAfterSignalInsert/
purgeRefs/PanelSlotKey/CollectDebugBoxes/SceneContentHash/kEdgePanelKinds/kOrder/
EdgePanelLayoutFor/EDGE_PANEL_MOVE/m_pendingPanelClose/MEAN_SCROLL/滚轮/悬停/
SetLanePanelCollapsed/LanePanelCollapsed/QueryCursor。
make release 零 warning,产物 860,160 B。
This commit is contained in:
1 parent
5d305cb10f
commit
62220477c0
18 files changed
+1263
-67
No files matched your search
@@ -868,6 +868,8 @@ namespace jdi
|
||||
boxes.Add(layout.opacityValue, 3);
|
||||
boxes.Add(layout.showDelta, 3);
|
||||
boxes.Add(layout.showEnum, 3);
|
||||
boxes.Add(layout.clipBuildButton, 3);
|
||||
boxes.Add(layout.clipDestructiveButton, 3);
|
||||
|
||||
boxes.AddAll(layout.rowChecks, 4);
|
||||
boxes.AddAll(layout.rowPoints, 4);
|
||||
@@ -4067,6 +4069,10 @@ namespace jdi
|
||||
mixRect(layout.button);
|
||||
mixRect(layout.showDelta);
|
||||
mixRect(layout.showEnum);
|
||||
// 「选区截取」:底部两个按钮的矩形(双光标关闭时为空 ⇒ 自动区分两种状态)。
|
||||
mixRect(layout.clipBuildButton);
|
||||
mixRect(layout.clipDestructiveButton);
|
||||
mixRect(layout.clipHint);
|
||||
mixRect(layout.content);
|
||||
mixRect(layout.tableHeader);
|
||||
for (const EdgePanelLayout::TableColumn& column : layout.tableColumns)
|
||||
@@ -4195,6 +4201,15 @@ namespace jdi
|
||||
mix(intervalBits);
|
||||
mix(resampleItem->collapsed ? 1ull : 0ull);
|
||||
}
|
||||
// 「选区截取」:只有勾选状态影响该行像素;
|
||||
// 但底部按钮是否显示取决于「双光标」模式,双光标开关变化同样要让面板重绘。
|
||||
mix(lane.ClipEnabled(ref) ? 1ull : 0ull);
|
||||
}
|
||||
// 「选区截取」的双光标状态决定底部按钮/提示的像素。
|
||||
if (kind == EdgePanelKind::CLIP)
|
||||
{
|
||||
mix(Tab().CursorMode() == PlotCursorMode::DOUBLE ? 1ull : 0ull);
|
||||
mix(lane.clipScroll > 0.0f ? 1ull : 0ull);
|
||||
}
|
||||
|
||||
// ⚠️⚠️ **边沿统计的数字本身就是面板内容,数字变化必须能让面板重绘**:
|
||||
@@ -4883,13 +4898,14 @@ namespace jdi
|
||||
const bool weakPeak = (kind == EdgePanelKind::PEAK);
|
||||
const bool freeMarker = (kind == EdgePanelKind::MARK);
|
||||
const bool resamplePanel = (kind == EdgePanelKind::RESAMPLE);
|
||||
// ⚠️ 「均值滤波」「中心轴边沿计数」「弱峰调整」「重采样」= 可滚动的分组列表
|
||||
const bool clipPanel = (kind == EdgePanelKind::CLIP);
|
||||
// ⚠️ 「均值滤波」「中心轴边沿计数」「弱峰调整」「重采样」「选区截取」= 可滚动的分组列表
|
||||
//(条目栏 + 展开后的参数行);
|
||||
// **「边沿计数」= 表格**(表头:信号名 / 上升沿计数 / 下降沿计数),单独一支。
|
||||
// 它们都**不能**再走旧的逐条目分支:那个分支没有任何裁切,滚动时会画到标题栏上。
|
||||
const bool listStyle = (meanFilter || centerAxis || weakPeak || resamplePanel);
|
||||
const bool listStyle = (meanFilter || centerAxis || weakPeak || resamplePanel || clipPanel);
|
||||
const bool countTable = (kind == EdgePanelKind::COUNT);
|
||||
const bool hasButton = (meanFilter || weakPeak || resamplePanel);
|
||||
const bool hasButton = (meanFilter || weakPeak || resamplePanel || clipPanel);
|
||||
{
|
||||
if (index >= count)
|
||||
{
|
||||
@@ -5088,8 +5104,11 @@ namespace jdi
|
||||
: (freeMarker ? std::wstring(L"自由标记")
|
||||
: (resamplePanel
|
||||
? std::wstring(kResampleTitle)
|
||||
: ((centerAxis ? L"中心轴边沿计数" : L"边沿计数") +
|
||||
EdgeRangeSuffix()))));
|
||||
: (clipPanel
|
||||
? std::wstring(kClipTitle)
|
||||
: ((centerAxis ? L"中心轴边沿计数"
|
||||
: L"边沿计数") +
|
||||
EdgeRangeSuffix())))));
|
||||
UiText(g, title, m_fonts->uiSmall,
|
||||
gd::RectF(badgeRect.X + badgeRect.Width + 8.0f * m_scale, panel.Y,
|
||||
panel.Width - badgeRect.Width - 8.0f * m_scale - titleHeight,
|
||||
@@ -5347,13 +5366,15 @@ namespace jdi
|
||||
: tabLane.FindMeanFilterItem(ref));
|
||||
const Lane::ResampleItem* resampleItem =
|
||||
resamplePanel ? tabLane.FindResampleItem(ref) : nullptr;
|
||||
const bool checked = resamplePanel ? (resampleItem != nullptr)
|
||||
: (weakPeak ? (peakItem != nullptr)
|
||||
: (item != nullptr));
|
||||
const bool checked = clipPanel ? tabLane.ClipEnabled(ref)
|
||||
: (resamplePanel ? (resampleItem != nullptr)
|
||||
: (weakPeak ? (peakItem != nullptr)
|
||||
: (item != nullptr)));
|
||||
const bool rowCollapsed =
|
||||
resamplePanel ? (resampleItem != nullptr && resampleItem->collapsed)
|
||||
: (weakPeak ? (peakItem != nullptr && peakItem->collapsed)
|
||||
: (item != nullptr && item->collapsed));
|
||||
clipPanel ? true
|
||||
: (resamplePanel ? (resampleItem != nullptr && resampleItem->collapsed)
|
||||
: (weakPeak ? (peakItem != nullptr && peakItem->collapsed)
|
||||
: (item != nullptr && item->collapsed)));
|
||||
|
||||
// 内容区已由 `g.SetClip(layout.content)` 裁切(行可以“半截”显示,滚动更顺滑);
|
||||
// 已经完整滚出可视范围的行在上面就 `continue` 了,这里不再额外门控。
|
||||
@@ -5414,6 +5435,11 @@ namespace jdi
|
||||
gd::RectF(row.X, row.Y, std::max(10.0f, labelWidth),
|
||||
row.Height),
|
||||
m_theme->textMuted, TextAlign::LEFT, layout.content);
|
||||
// ⚠️ 统计数字**绝不省略**(用户 2026-11-03 要求):
|
||||
// 面板宽度已按真实文本量宽(`EdgePanelWidth` 逐信号实测),
|
||||
// 因此这里用 `TextAlign::RIGHT` + 完整矩形即可;
|
||||
// 万一仍被窗口边界夹住,也宁可超出面板右缘(由内容区裁剪兜底),
|
||||
// **不要**退化成 `…` 让用户看不到真实数值。
|
||||
UiTextClipped(g, text, m_fonts->monoSmall,
|
||||
gd::RectF(row.X + labelWidth + 8.0f * m_scale, row.Y,
|
||||
std::max(10.0f, row.Width - labelWidth -
|
||||
@@ -6114,7 +6140,25 @@ namespace jdi
|
||||
UiText(g, text, m_fonts->uiSmall, inner,
|
||||
checked ? m_theme->textStrong : m_theme->text, TextAlign::CENTER);
|
||||
};
|
||||
if (hasButton)
|
||||
if (clipPanel)
|
||||
{
|
||||
// 「选区截取」:底部两个并排按钮;未启用双光标时都给提示、不画按钮。
|
||||
const bool doubleCursor = (Tab().CursorMode() == PlotCursorMode::DOUBLE);
|
||||
if (doubleCursor)
|
||||
{
|
||||
paintToggleButton(layout.clipBuildButton, kClipBuildLabel, false);
|
||||
paintToggleButton(layout.clipDestructiveButton, kClipDestructiveLabel, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 提示文字不参与滚动,画在内容区正中。
|
||||
const std::wstring hintText(kClipNeedDoubleCursorHint);
|
||||
UiTextClipped(g, UiEllipsize(hintText, m_fonts->uiSmall, layout.content.Width),
|
||||
m_fonts->uiSmall, layout.content, m_theme->textMuted,
|
||||
TextAlign::CENTER, layout.content);
|
||||
}
|
||||
}
|
||||
if (hasButton && !clipPanel)
|
||||
{
|
||||
paintToggleButton(layout.button, kBuildSignalLabel, false);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user