自由标记体验重做 + 全部实验性面板可折起

- 网格内固定点:取值胶囊按已放置矩形做纵向避让(不再互相遮挡);行尾 ✕ 改为与
  胶囊同高的正方形;移除固定点的纵向半透明竖线
- 新增「标记透明度」(0–100%),作用于本网格全部固定点的圆点/胶囊/描边/文字/✕;
  半透明时文字改走 GDI+(GDI DrawTextW 忽略 alpha),100% 时像素零变化;
  ≤2% 时不绘制且不登记 ✕ 命中矩形(看不见的按钮不可点)
- 面板按信号分组:信号色托底分组栏(展开三角 + 信号名 + N 标记点 + ✕,文字/三角/✕
  一律用自适应对比色),点击分组栏收起/展开;条目五列(值/枚举/增量/时刻/间隔),
  首项增列与 Δt 显示 N/A,整个面板没有枚举文本时枚举列整列缺省;行尾仍有 ✕
- 面板内容超高时右侧出现滚动条(分组栏与条目一起滚动),滚轮在内容区翻页;
  底部按钮与「标记透明度」滑块固定在底部、不参与滚动
- 底部按钮改为「光标选取模式」切换类按钮(外观与工具栏切换按钮一致,激活高亮),
  激活时其上方出现「标记透明度」滑动条(与信号样式区的透明度滑块同款)
- 所有实验性功能浮动面板支持展开/折起:标题栏 GX 徽标左侧新增「三角 + 横线」指示器,
  单击切换、双击标题栏切换;折起的面板只有标题栏
- 数据层:LanePanelState.collapsed、Lane.freeMarkerOpacity/freeMarkerScroll/
  freeMarkerCollapsedGroups,LaneCarry 与 SplitLane 一并继承
- 验证:新增离屏 .preflight/perf/mark2check 69/69(含两组 A/B);回归 datacheck 69/69、
  verify 49/49、panelanim 158/158、errcheck 19/19;make release/debug 零警告
This commit is contained in:
DeepSeek-V4.1-Flash committed 2026-10-21 02:59:05 +08:00
1 parent 067128f46c
commit 3400ac7c29
9 files changed
+1225 -202

No files matched your search

@@ -1693,6 +1693,115 @@ namespace jdi
return enabled;
}
bool PlotView::SetLaneFreeMarkerOpacity(int laneIndex, float opacity)
{
if (m_tab == nullptr || laneIndex < 0 || laneIndex >= static_cast<int>(m_tab->Lanes().size()))
{
return false;
}
Lane& lane = m_tab->Lanes()[static_cast<std::size_t>(laneIndex)];
const float clamped = std::max(0.0f, std::min(1.0f, opacity));
if (std::fabs(lane.freeMarkerOpacity - clamped) < 0.0005f)
{
return false;
}
lane.freeMarkerOpacity = clamped;
return true;
}
bool PlotView::SetLaneFreeMarkerScroll(int laneIndex, float scroll)
{
if (m_tab == nullptr || laneIndex < 0 || laneIndex >= static_cast<int>(m_tab->Lanes().size()))
{
return false;
}
Lane& lane = m_tab->Lanes()[static_cast<std::size_t>(laneIndex)];
const float clamped = std::max(0.0f, scroll);
if (std::fabs(lane.freeMarkerScroll - clamped) < 0.5f)
{
return false;
}
lane.freeMarkerScroll = clamped;
return true;
}
bool PlotView::ToggleLaneFreeMarkerGroup(int laneIndex, const SignalRef& ref)
{
if (m_tab == nullptr || laneIndex < 0 || laneIndex >= static_cast<int>(m_tab->Lanes().size()))
{
return false;
}
Lane& lane = m_tab->Lanes()[static_cast<std::size_t>(laneIndex)];
return lane.ToggleFreeMarkerGroupCollapsed(ref);
}
bool PlotView::SetLanePanelCollapsed(int laneIndex, EdgePanelKind kind, bool collapsed)
{
if (m_tab == nullptr || laneIndex < 0 || laneIndex >= static_cast<int>(m_tab->Lanes().size()))
{
return false;
}
Lane& lane = m_tab->Lanes()[static_cast<std::size_t>(laneIndex)];
LanePanelState* state = nullptr;
switch (kind)
{
case EdgePanelKind::COUNT:
state = &lane.edgePanel;
break;
case EdgePanelKind::CENTER:
state = &lane.edgeCenterPanel;
break;
case EdgePanelKind::MEAN:
state = &lane.meanFilterPanel;
break;
case EdgePanelKind::PEAK:
state = &lane.weakPeakPanel;
break;
case EdgePanelKind::MARK:
state = &lane.freeMarkerPanel;
break;
default:
return false;
}
if (state->collapsed == collapsed)
{
return false;
}
state->collapsed = collapsed;
return true;
}
bool PlotView::LanePanelCollapsed(int laneIndex, EdgePanelKind kind) const
{
if (m_tab == nullptr || laneIndex < 0 || laneIndex >= static_cast<int>(m_tab->Lanes().size()))
{
return false;
}
const Lane& lane = m_tab->Lanes()[static_cast<std::size_t>(laneIndex)];
switch (kind)
{
case EdgePanelKind::COUNT:
return lane.edgePanel.collapsed;
case EdgePanelKind::CENTER:
return lane.edgeCenterPanel.collapsed;
case EdgePanelKind::MEAN:
return lane.meanFilterPanel.collapsed;
case EdgePanelKind::PEAK:
return lane.weakPeakPanel.collapsed;
case EdgePanelKind::MARK:
return lane.freeMarkerPanel.collapsed;
default:
return false;
}
}
bool PlotView::ToggleLanePanelCollapsed(int laneIndex, EdgePanelKind kind)
{
const bool collapsed = !LanePanelCollapsed(laneIndex, kind);
SetLanePanelCollapsed(laneIndex, kind, collapsed);
return collapsed;
}
bool PlotView::MeanFilterValueAt(const TimeAxis& axis, const SignalData& signal, int points, int iterations,
double time, double& value) const
{