交互修正:悬停提示跟随鼠标、信号拖拽手掌/抓握光标、增量只显示在右侧光标
- 信号悬停提示改为真正跟随鼠标(PaintSignalInteraction 用 m_mouseX/m_mouseY 定位, 删除 m_hoverAnchorX/m_hoverAnchorY)。 根因:OnMouseMove 只在状态变化时返回 changed,仅鼠标位置变化时返回 false, 宿主不重绘 → 提示停在上一帧位置。现补上 `if (m_hoverSignal.IsValid() && mouseMoved) changed = true;`(mouseMoved 由前后坐标比较得出), 鼠标在可拖拽区域内持续移动时提示条会跟随。 - 可拖拽信号的光标响应参考 Photoshop 平移元素: 悬停时用手掌光标(IDC_HAND),按住左键拖拽途中用抓握光标(IDC_SIZEALL)。 - 双光标增量差值:改为**只**由"右侧光标"(时间较大的那条)显示, 按差值显式给出正负号(+ / -,不再固定用 ±);把右光标拖到左侧后, 增量会自动改由新的右侧光标显示。(判定改用时间比较,不再用 m_activeCursor。) - 光标时间读数标签与 Δt 差值标签:悬停(未按下)时不再改变鼠标光标, 真正拖拽时统一使用左右箭头(IDC_SIZEWE)。 两个标签都叠在光标线正上方,因此在 QueryCursor 中必须先于光标线判定返回 nullptr, 否则会命中光标线给出 SIZEWE。 - Δt 差值文案由 "Δt " 改为 "Δt:"(全角冒号)。 - 一并移除遗留的 TEMP-PROBE 调试输出(HitTestSignal / PaintSignalInteraction / OnMouseMove 探针)与已不再使用的 m_dragOnLabel。 验证(TestData/VERY_BIG_DATA.CSV,编码器A相密集方波,125%、1725x996): - 提示跟随:悬停 (700,250)→(700,400)→(760,620)→(900,700) 后, 提示条出现在最后一次鼠标位置旁(截图核对),不再停留在首次出现处。 - 光标形态探测:信号悬停 = HAND;拖拽信号 = SIZEALL; 时间读数标签/Δt 标签悬停 = ARROW(不改变);Δt 标签拖拽 = SIZEWE; 时间读数标签拖拽 = SIZEWE;光标线上方悬停 = SIZEWE。 - 增量归属:默认双光标左 4.9417(无增量)/ 右 4.9425 , +0.0008; 把右光标拖到左侧后变为左 0.288688(无增量)/ 右 4.9417 , +4.65301; 把右光标拖到低电平处得到 4.80724 , -0.134458(负号正确)。 - Δt 文案:截图确认显示 "Δt: 15s 457.214ms"。 - 回归([1].CSV 双光标):左 426.316(无增量)/ 右 538.583 , +112.267; Δt:151ms 200us。 - make clean && make all 无警告无错误。
This commit is contained in:
1 parent
ed13aa7d49
commit
a566d98bab
2 files changed
+34
-80
No files matched your search
@@ -185,13 +185,13 @@ namespace jdi
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 双光标差值的完整显示文本("Δt " + 复合单位间隔)。
|
||||
* @brief 双光标差值的完整显示文本("Δt:" + 复合单位间隔)。
|
||||
* @param [IN] seconds 时间间隔(秒)。
|
||||
* @return 形如 "Δt 1h 1min 2s" 的文本。
|
||||
* @return 形如 "Δt:1h 1min 2s" 的文本。
|
||||
*/
|
||||
std::wstring FormatSpanText(double seconds)
|
||||
{
|
||||
return L"Δt " + FormatDurationCompound(seconds);
|
||||
return L"Δt:" + FormatDurationCompound(seconds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1677,17 +1677,6 @@ namespace jdi
|
||||
SignalRef best;
|
||||
float bestDistance = tolerance;
|
||||
|
||||
// TEMP-PROBE
|
||||
{
|
||||
FILE* fp = fopen(".preflight\\hit.log", "a");
|
||||
if (fp != nullptr)
|
||||
{
|
||||
fprintf(fp, "query=(%d,%d) plot=(%.0f,%.0f,%.0f,%.0f) lane=%d\n", x, y, m_plotRect.X,
|
||||
m_plotRect.Y, m_plotRect.Width, m_plotRect.Height, LaneAtY(y));
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
// 只为当前网格内的可见信号做精确的点到线段距离检测,避免跨网格误命中。
|
||||
for (const SignalRef& ref : m_tab->Lanes()[static_cast<std::size_t>(laneIndex)].signals)
|
||||
{
|
||||
@@ -1729,34 +1718,8 @@ namespace jdi
|
||||
}
|
||||
if (windowFirst >= windowLast)
|
||||
{
|
||||
// TEMP-PROBE
|
||||
FILE* fp = fopen(".preflight\\hit.log", "a");
|
||||
if (fp != nullptr)
|
||||
{
|
||||
fprintf(fp, " window empty first=%zu last=%zu pts=%zu fx=%.1f tol=%.1f\n", windowFirst,
|
||||
windowLast, points.size(), fx, tolerance);
|
||||
fclose(fp);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// TEMP-PROBE
|
||||
{
|
||||
FILE* fp = fopen(".preflight\\hit.log", "a");
|
||||
if (fp != nullptr)
|
||||
{
|
||||
float minY = 1.0e9f;
|
||||
float maxY = -1.0e9f;
|
||||
for (std::size_t i = windowFirst; i < windowLast; ++i)
|
||||
{
|
||||
minY = std::min(minY, points[i].Y);
|
||||
maxY = std::max(maxY, points[i].Y);
|
||||
}
|
||||
fprintf(fp, " window first=%zu last=%zu pts=%zu yRange=%.1f..%.1f fy=%.1f\n",
|
||||
windowFirst, windowLast, points.size(), minY, maxY, fy);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
// 散点模式下把采样点本身也视为可命中目标。
|
||||
if (resolved.style->mode == DrawMode::SCATTER)
|
||||
{
|
||||
@@ -2208,13 +2171,13 @@ namespace jdi
|
||||
case PlotDragMode::SCROLL_LANES:
|
||||
return LoadCursorW(nullptr, IDC_SIZENS);
|
||||
case PlotDragMode::CURSOR_MOVE:
|
||||
// 从时间读数标签上抓起的拖拽统一用抓握(移动)图标;
|
||||
// 直接从光标线上抓起的拖拽保持左右箭头。
|
||||
return LoadCursorW(nullptr, m_dragOnLabel ? IDC_SIZEALL : IDC_SIZEWE);
|
||||
// 光标拖拽(无论是抓住光标线还是抓住时间读数标签)都沿时间轴移动 → 左右箭头。
|
||||
return LoadCursorW(nullptr, IDC_SIZEWE);
|
||||
case PlotDragMode::CURSOR_SPAN_MOVE:
|
||||
// 差值标签拖拽:统一使用抓握图标(不是左右箭头)。
|
||||
return LoadCursorW(nullptr, IDC_SIZEALL);
|
||||
// 差值标签拖拽:两条光标整体沿时间轴平移 → 左右箭头。
|
||||
return LoadCursorW(nullptr, IDC_SIZEWE);
|
||||
case PlotDragMode::MOVE_SIGNAL:
|
||||
// 拖动信号(参考 Photoshop 平移元素的抓握光标)。
|
||||
return LoadCursorW(nullptr, IDC_SIZEALL);
|
||||
default:
|
||||
break;
|
||||
@@ -2226,18 +2189,18 @@ namespace jdi
|
||||
return LoadCursorW(nullptr, IDC_SIZENS);
|
||||
}
|
||||
|
||||
// 双光标差值标签:悬停时用"手形"表示可交互(与拖拽中的抓握图标区分)。
|
||||
// 光标时间读数标签与双光标差值标签:**悬停(未按下)时不改变鼠标形状**。
|
||||
// 两个标签都叠在光标线的正上方,因此必须先于"光标线"判定返回 nullptr,
|
||||
// 由调用方回退成箭头;只有真正拖拽时才切成左右箭头(见 CURSOR_MOVE / CURSOR_SPAN_MOVE)。
|
||||
if (IsOnCursorSpanLabel(x, y))
|
||||
{
|
||||
return LoadCursorW(nullptr, IDC_HAND);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 光标时间读数标签同样可拖拽:悬停用手形。
|
||||
{
|
||||
int labelWhich = 0;
|
||||
if (IsOnCursorTimeLabel(x, y, labelWhich))
|
||||
{
|
||||
return LoadCursorW(nullptr, IDC_HAND);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2251,10 +2214,10 @@ namespace jdi
|
||||
|
||||
if (UiPointInRect(m_plotRect, static_cast<float>(x), static_cast<float>(y)))
|
||||
{
|
||||
// 命中信号曲线时给出可拖拽提示光标(仅在常规模式下)。
|
||||
// 悬停命中可拖拽信号时给出"手掌"提示光标;按下拖拽过程中改用抓握光标。
|
||||
if (Tab().ZoomMode() == PlotZoomMode::NONE && HitTestSignal(x, y).IsValid())
|
||||
{
|
||||
return LoadCursorW(nullptr, IDC_SIZEALL);
|
||||
return LoadCursorW(nullptr, IDC_HAND);
|
||||
}
|
||||
|
||||
// 选区放大模式下**未开始框选时保持默认箭头**(不改变鼠标形状);
|
||||
@@ -2266,6 +2229,7 @@ namespace jdi
|
||||
|
||||
bool PlotView::OnMouseMove(int x, int y)
|
||||
{
|
||||
const bool mouseMoved = (x != m_mouseX) || (y != m_mouseY);
|
||||
m_mouseX = x;
|
||||
m_mouseY = y;
|
||||
|
||||
@@ -2456,13 +2420,6 @@ namespace jdi
|
||||
}
|
||||
if (hoverSignal != m_hoverSignal)
|
||||
{
|
||||
if (hoverSignal.IsValid())
|
||||
{
|
||||
// 悬停提示固定在"刚刚进入可拖拽区域"的位置:之后鼠标即使继续在该区域内
|
||||
// 移动,提示也只停留在原地(不再跟随鼠标)。
|
||||
m_hoverAnchorX = x;
|
||||
m_hoverAnchorY = y;
|
||||
}
|
||||
m_hoverSignal = hoverSignal;
|
||||
changed = true;
|
||||
}
|
||||
@@ -2474,6 +2431,13 @@ namespace jdi
|
||||
m_spanHover = spanHover;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// 悬停提示跟随鼠标:只要提示可见,鼠标移动本身就要触发重绘;
|
||||
// 否则鼠标在可拖拽区域内继续移动时,提示会停在上一帧的位置。
|
||||
if (m_hoverSignal.IsValid() && mouseMoved)
|
||||
{
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
@@ -2494,7 +2458,6 @@ namespace jdi
|
||||
m_dragMoved = false;
|
||||
m_rubberRect = gd::RectF();
|
||||
m_rubberAxis = PlotRubberAxis::UNDECIDED;
|
||||
m_dragOnLabel = false;
|
||||
m_cursorGrabOffset = 0;
|
||||
// 新的交互开始:清掉上一次留下的提示(拖动过程中会重新设置粘滞提示)。
|
||||
SetHint(std::wstring());
|
||||
@@ -2520,7 +2483,6 @@ namespace jdi
|
||||
if (IsOnCursorSpanLabel(x, y))
|
||||
{
|
||||
m_dragMode = PlotDragMode::CURSOR_SPAN_MOVE;
|
||||
m_dragOnLabel = true;
|
||||
m_spanDragStartX = x;
|
||||
m_spanDragTime1 = Tab().CursorTime();
|
||||
m_spanDragTime2 = Tab().CursorTimeAlt();
|
||||
@@ -2533,7 +2495,6 @@ namespace jdi
|
||||
if (IsOnCursorTimeLabel(x, y, labelWhich))
|
||||
{
|
||||
m_dragMode = PlotDragMode::CURSOR_MOVE;
|
||||
m_dragOnLabel = true;
|
||||
m_hoverCursor = labelWhich;
|
||||
m_activeCursor = labelWhich;
|
||||
m_cursorHover = true;
|
||||
@@ -2658,7 +2619,6 @@ namespace jdi
|
||||
m_dragLane = -1;
|
||||
m_dragMoved = false;
|
||||
m_rubberRect = gd::RectF();
|
||||
m_dragOnLabel = false;
|
||||
m_cursorGrabOffset = 0;
|
||||
// 松开左键:粘滞提示结束计时,开始"停留后淡出"。
|
||||
ReleaseHint();
|
||||
@@ -4220,6 +4180,10 @@ namespace jdi
|
||||
// 重叠时把"最近操作过的那条光标"的内容画在上层,因此先画另一条。
|
||||
const int order[2] = { (m_activeCursor == 2) ? 1 : 2, (m_activeCursor == 2) ? 2 : 1 };
|
||||
|
||||
// 增量差值**只**显示在"右侧光标"上。右侧 = 时间较大的那条,随拖拽实时变化:
|
||||
// 把右光标拖到左边后,增量会自动改由新的右侧光标显示。
|
||||
const int rightCursor = (Tab().CursorTime() <= Tab().CursorTimeAlt()) ? 2 : 1;
|
||||
|
||||
// ---- 光标竖线 + 顶部把手 ----
|
||||
for (int pass = 0; pass < cursorCount; ++pass)
|
||||
{
|
||||
@@ -4254,7 +4218,8 @@ namespace jdi
|
||||
const int which = doubleMode ? order[pass] : 1;
|
||||
const double time = (which == 2) ? Tab().CursorTimeAlt() : Tab().CursorTime();
|
||||
const double otherTime = (which == 2) ? Tab().CursorTime() : Tab().CursorTimeAlt();
|
||||
const bool showDelta = doubleMode && (which == order[1]);
|
||||
// 只有右侧光标显示增量差值(左光标只显示自身取值)。
|
||||
const bool showDelta = doubleMode && (which == rightCursor);
|
||||
|
||||
// 光标在可视区外时,把标注"钉"在对应的网格边上,保证取值仍然可见。
|
||||
const float pinX = CursorDisplayX(which);
|
||||
@@ -4331,10 +4296,9 @@ namespace jdi
|
||||
if (SampleFileAt(*resolved.file, *resolved.data, otherTime, interpolate, otherValue))
|
||||
{
|
||||
const double delta = value - otherValue;
|
||||
// 增量为右侧光标减去左侧光标;正值用 ±,负值直接用负号,
|
||||
// 避免出现 “±-0.001” 这种双符号写法。
|
||||
// 增量 = 右侧光标取值 − 左侧光标取值,按差值显式给出正负号。
|
||||
text += L" , ";
|
||||
text += (delta < 0.0) ? L"-" : L"±";
|
||||
text += (delta < 0.0) ? L"-" : L"+";
|
||||
text += FormatValue(std::fabs(delta));
|
||||
}
|
||||
}
|
||||
@@ -4619,13 +4583,6 @@ namespace jdi
|
||||
// 悬停提示:光标贴近曲线时给出可拖拽说明(与时间光标的提示方式一致)。
|
||||
if (!m_hoverSignal.IsValid())
|
||||
{
|
||||
// TEMP-PROBE
|
||||
FILE* fp0 = fopen(".preflight\\paint.log", "a");
|
||||
if (fp0 != nullptr)
|
||||
{
|
||||
fprintf(fp0, "hover-invalid dragMode=%d\n", static_cast<int>(m_dragMode));
|
||||
fclose(fp0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const int laneIndex = m_tab->LaneOfSignal(m_hoverSignal);
|
||||
@@ -4642,17 +4599,17 @@ namespace jdi
|
||||
const LaneVisual& lane = m_lanes[static_cast<std::size_t>(laneIndex)];
|
||||
const std::wstring text = resolved.data->Title() + L" · 按住拖拽可移入其它网格";
|
||||
|
||||
// 提示锚定在"首次进入可拖拽区域"的位置,鼠标在该区域内继续移动时提示不会跟随。
|
||||
// 提示跟随鼠标:在可拖拽区域内持续移动时,提示条也要跟着走。
|
||||
const float textWidth = UiTextWidth(g, text, m_fonts->uiSmall) + 16.0f * m_scale;
|
||||
const float height = 20.0f * m_scale;
|
||||
float left = static_cast<float>(m_hoverAnchorX) + 12.0f * m_scale;
|
||||
float left = static_cast<float>(m_mouseX) + 12.0f * m_scale;
|
||||
if (left + textWidth > m_plotRect.X + m_plotRect.Width)
|
||||
{
|
||||
left = m_plotRect.X + m_plotRect.Width - textWidth;
|
||||
}
|
||||
left = std::max(m_plotRect.X, left);
|
||||
|
||||
float top = static_cast<float>(m_hoverAnchorY) + 14.0f * m_scale;
|
||||
float top = static_cast<float>(m_mouseY) + 14.0f * m_scale;
|
||||
top = std::min(top, m_plotRect.Y + m_plotRect.Height - height - 2.0f * m_scale);
|
||||
top = std::max(top, lane.rect.Y + 2.0f * m_scale);
|
||||
|
||||
|
||||
Reference in new issue
Block a user