自由标记:面板表头/吸顶/行选中与悬停联动 + 原位显示 + 探测容差与阶跃吸附

- 面板:每个展开分组下方新增一行列表头(标记值/枚举文本/增量/时间点/时间间隔),
  与数据行共用列边界;列宽同时容纳列标题;表头行计入内容总高与面板高度;
  全部无枚举文本时仍整列缺省
- 吸顶:分组栏与表头各自按 max(自然, min(吸顶, 组下边界-自身高)) 钉住,
  组末尾按"表头先走 -> 条目 -> 分组栏最后离场";绘制顺序改为条目->表头->吸顶分组栏,
  被盖住的条目不绘制也不可命中
- 行交互:Lane::freeMarkerSelected(单击选中/再点取消)+ 独立的整行悬停状态
  (整行悬停不会把该行 ✕ 变成 danger 色);悬停时网格内对应固定点整体高亮
  (数据点圆环 + 胶囊强制不透明 + accent 描边,仅视界内)
- 固定点恢复原位显示(删除纵向避让,允许遮挡,回到初版方案)
- 探测判据重写:鼠标到画出来的折线的垂直距离 <= kSignalHitTolerance(与曲线高亮同源),
  命中后吸附到最近采样时刻 => 修复阶跃竖直跳变线无法探测/固定的问题;
  删除已无用的 kFreeMarkerHoverTolerance
- 修复滚动时条目文字串到面板标题栏(GDI 文本不认 GDI+ 裁剪,改用 UiTextClipped)
- 顺带清理过期注释(四列->五列、折起指示器已无横线、探测判据说明),
  RemoveFreeMarker 改为复用 RemoveFreeMarkerAt(选中下标校正只有一处)
- 验证:新增离屏 .preflight/mark4check 38/38(三组 A/B 均如实失败);
  回归 markcheck 61/61、fixcheck 37/37、round3check 31/31、round4check 34/34、
  round5check 5/5、editcheck 20/20、convcheck 18/18;make release/debug 零警告
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-10-22 21:04:03 +08:00
1 parent 8f735d51be
commit 79eebc281e
10 files changed
+618 -116

No files matched your search

@@ -1584,6 +1584,8 @@ namespace jdi
SignalRef hoverSignal;
int triLane = -1;
SignalRef triSignal;
int rowLane = -1;
std::size_t rowItem = static_cast<std::size_t>(-1);
int markLane = -1;
int markKind = 0;
if (m_tab != nullptr &&
@@ -1606,6 +1608,20 @@ namespace jdi
break;
}
}
// 整行悬停(用于把网格内对应的固定点整体高亮)。
if (hoverLane < 0 && insideContent)
{
for (const EdgePanelLayout::MarkerRow& row : layout.markerRows)
{
if (row.rect.Width > 0.0f &&
UiPointInRect(row.rect, static_cast<float>(x), static_cast<float>(y)))
{
rowLane = markLane;
rowItem = row.itemIndex;
break;
}
}
}
if (hoverLane < 0)
{
for (const EdgePanelLayout::MarkerGroup& group : layout.markerGroups)
@@ -1635,6 +1651,12 @@ namespace jdi
m_freeMarkerPanelHoverSignal = hoverSignal;
changed = true;
}
if (rowLane != m_freeMarkerRowHoverLane || rowItem != m_freeMarkerRowHoverItem)
{
m_freeMarkerRowHoverLane = rowLane;
m_freeMarkerRowHoverItem = rowItem;
changed = true;
}
if (triLane != m_freeMarkerTriangleHoverLane ||
!(triSignal == m_freeMarkerTriangleHoverSignal))
{
@@ -2337,6 +2359,17 @@ namespace jdi
m_freeMarkerPanelHoverSignal = SignalRef();
return true;
}
// 行的**数据区域**(✕ 之外):单击选中该条目(再次单击取消选中)。
// 选中行在面板里高亮;与网格内的固定点一一对应。
if (insideContent && row.rect.Width > 0.0f && UiPointInRect(row.rect, fx, fy))
{
SelectLaneFreeMarker(
index, (tabLane.freeMarkerSelected == static_cast<int>(row.itemIndex))
? -1
: static_cast<int>(row.itemIndex));
m_dragMode = PlotDragMode::NONE;
return true;
}
}
}
@@ -2978,6 +3011,13 @@ namespace jdi
m_freeMarkerPanelHoverSignal = SignalRef();
changed = true;
}
if (m_freeMarkerRowHoverLane != -1 ||
m_freeMarkerRowHoverItem != static_cast<std::size_t>(-1))
{
m_freeMarkerRowHoverLane = -1;
m_freeMarkerRowHoverItem = static_cast<std::size_t>(-1);
changed = true;
}
if (m_cursorHover)
{
m_cursorHover = false;