feat(ui): 修复光标放大后丢失、选区放大合并与右键放弃、双光标与数据点取值标注

1) 打开「选区 Y 轴自适应」时立即自适应一次
- 之前打开开关(单个网格的菜单项或全局项/工具栏按钮)不会真的改变量程,
  只有当用户再次做时间轴选区放大时才生效,观感上就是"点了没反应"
- ToggleLaneAutoFitToSelection / SetAllLanesAutoFitToSelection(enable, applyNow=true)
  在打开时按当前视图立刻执行一次 AutoRangeLaneToView
- 新增 ApplyAutoFitToSelectionNow / ApplyAllAutoFitToSelectionNow

2) 「按网格时间轴展开」同时对该网格做一次 Y 轴自适应
- FitTimeToLane 在把时间轴展开到本网格信号范围后调用 AutoRangeLaneToView,
  避免出现"时间对了但幅值区间不合适"的波形

3) 修复时间光标在大幅放大后丢失、无法拖动的问题
- 根因:缩放/平移改变 ViewMin/ViewMax 后,光标时间可能落到新视野之外,
  PaintCursor 直接 return → 光标消失,用户报告为"放大到 10ms 一格后拖动光标会直接丢失"
- 新增 ClampCursorToView(),在 ApplyTimeSelection / FitAll / FitTimeToLane /
  平移(PAN) 后把两条光标夹回可见区间;CURSOR_MOVE 也把目标时间夹在 [ViewMin, ViewMax]
- IsOnCursorLine 改为输出命中的光标序号,为双光标拖动做准备

4) 工具栏「时间轴选区」+「Y 轴选区」合并为「选区放大」,并支持右键放弃选区
- 单一 toggle(ToolbarAction::ZOOM_SELECT / PlotZoomMode::SELECT);
  按下后按首次明显位移方向决定轴向:横向=时间轴选区,纵向=该网格 Y 轴选区,
  轴向固定不变,轴向未定时不画选区(避免闪烁)
- 框选过程中按右键 = 放弃当前选区并显示提示浮层;与 Esc 不同,**不退出**选区放大模式
- 为此 WM_RBUTTONUP 在左键仍按下时不 ReleaseCapture / 不清 m_activeTarget,
  否则松左键收不到 OnLButtonUp,m_dragMode 会永远停在 RUBBER_SELECT
- 新增 PlotView 内的提示浮层(m_hintText + PaintHint):切换模式/放弃选区时显示一行说明,
  在网格内再次移动鼠标自动清除,无需定时器
- Esc 语义保持原样:放弃拖拽 + 退出选区放大模式

5) 光标功能改为三态:常态 → 单光标 → 双光标 → 常态
- TabConfig 用 PlotCursorMode{NONE,SINGLE,DOUBLE} + CursorTime/CursorTimeAlt 两个时间;
  CursorEnabled() 变成只读派生;ToggleCursor 删除,改为 CycleCursorMode()
- 单 → 双时第二条光标落在第一条附近(+12% 视图跨度,越界反向)并保证在可见范围内
- 两条光标颜色区分且同族:光标 1 = Solarized 橙 #CB4B16(theme.cursor),
  光标 2 = 青 #2AA198(新增 theme.cursorAlt),光标 2 的顶部把手画成朝上的三角
- 时间读数从绘图区顶部移到**底部**,单/双光标都始终显示(不必悬停才可见)

6) 取消原有的光标数值读数框,改为直接在数据点位置标注取值
- 删除 PaintLaneReadout/PaintLaneReadouts(网格右上角的读数框)
- PaintCursor 在光标与每条可见信号曲线的交点处绘制信号色圆角胶囊(底色=信号色,
  文字按 IsDarkColor 自动取深/浅色),一条信号一个标注,颜色与信号一一对应;
  右侧空间不足时自动标到左侧
- 双光标时以时间较小者为左;**右侧光标**显示"当前值 , ±增量"(增量 = 右 − 左),
  增量为负时用负号前缀,避免出现 "±-" 双符号

验证([1].CSV 11 信号 / VERY_BIG_DATA 1.77GB,窗口 1725x996,125% DPI):
- 三态循环:点 1 次出现单光标、2 次双光标、3 次回到无光标
- 双光标读数:左"426.316 / 427.749",右"538.583 , ±112.267 / 513.726 , ±85.9769"
- 时间读数在底部显示(橙 0.63 / 青 0.7812);第二条光标出现在第一条右侧同一视野内
- 超大文件:放大两次后开启光标仍然可见,拖动从 68.2343 变为 68.1911(原先直接消失)
- 「Y 轴自适应」在放大后的视图中点击立即把量程收到可见区间(300~520)
- 「选区 Y 轴自适应」菜单项点击后立即生效并被勾选,全局项同步勾选
- 「按网格时间轴展开」把时间轴展开回全范围并重新自适应 Y 轴
- 选区放大:横向拖拽只放大时间轴、纵向拖拽只放大 Y 轴;右键放弃后视图不变、提示出现、
  模式仍保持选中;Esc 则同时放弃并退出模式
- 回归:空闲 CPU 0.00%;make clean; make all 无警告无错误
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-10-08 16:58:55 +08:00
1 parent 0fffb3ed09
commit 388d3b6441
8 files changed
+656 -362

No files matched your search

+26 -34
View File
@@ -678,10 +678,8 @@ namespace jdi
// 工具栏可切换按钮的选中状态始终与当前标签页配置保持一致。 // 工具栏可切换按钮的选中状态始终与当前标签页配置保持一致。
m_toolbar.SetToggleState(ToolbarAction::AUTO_FIT_Y, m_plot.AllLanesAutoFitToSelection()); m_toolbar.SetToggleState(ToolbarAction::AUTO_FIT_Y, m_plot.AllLanesAutoFitToSelection());
m_toolbar.SetToggleState(ToolbarAction::ZOOM_TIME, m_toolbar.SetToggleState(ToolbarAction::ZOOM_SELECT,
m_plot.CurrentZoomMode() == PlotZoomMode::TIME); m_plot.CurrentZoomMode() == PlotZoomMode::SELECT);
m_toolbar.SetToggleState(ToolbarAction::ZOOM_VALUE,
m_plot.CurrentZoomMode() == PlotZoomMode::VALUE);
m_toolbar.SetToggleState(ToolbarAction::CURSOR, m_plot.CursorEnabled()); m_toolbar.SetToggleState(ToolbarAction::CURSOR, m_plot.CursorEnabled());
{ {
@@ -1028,40 +1026,19 @@ namespace jdi
case ToolbarAction::AUTO_FIT_Y: case ToolbarAction::AUTO_FIT_Y:
{ {
// 与网格右键菜单中的「选区 Y 轴自适应(全局)」同一开关: // 与网格右键菜单中的「选区 Y 轴自适应(全局)」同一开关:
// 打开后每次时间轴选区放大都会自动把各网格 Y 轴匹配到新的可见区间。 // 打开后立即按当前视图自适应一次,之后每次时间轴选区放大都会自动匹配。
const bool enable = !m_plot.AllLanesAutoFitToSelection(); const bool enable = !m_plot.AllLanesAutoFitToSelection();
m_plot.SetAllLanesAutoFitToSelection(enable); m_plot.SetAllLanesAutoFitToSelection(enable, true);
SetStatusMessage(enable ? L"已开启选区 Y 轴自适应:时间轴选区放大后自动匹配各网格 Y 轴。" SetStatusMessage(enable ? L"已开启选区 Y 轴自适应:时间轴选区放大后自动匹配各网格 Y 轴。"
: L"已关闭选区 Y 轴自适应。", : L"已关闭选区 Y 轴自适应。",
false); false);
break; break;
} }
case ToolbarAction::ZOOM_TIME: case ToolbarAction::ZOOM_SELECT:
if (m_plot.ToggleZoomMode(PlotZoomMode::TIME)) m_plot.ToggleZoomMode(PlotZoomMode::SELECT);
{
SetStatusMessage((m_plot.CurrentZoomMode() == PlotZoomMode::TIME)
? L"时间轴选区:在绘图区横向拖拽选择区间即可放大。"
: L"已退出时间轴选区模式。",
false);
}
break;
case ToolbarAction::ZOOM_VALUE:
if (m_plot.ToggleZoomMode(PlotZoomMode::VALUE))
{
SetStatusMessage((m_plot.CurrentZoomMode() == PlotZoomMode::VALUE)
? L"Y 轴选区:在某个网格内纵向拖拽选择区间即可放大。"
: L"已退出 Y 轴选区模式。",
false);
}
break; break;
case ToolbarAction::CURSOR: case ToolbarAction::CURSOR:
if (m_plot.ToggleCursor()) m_plot.CycleCursorMode();
{
SetStatusMessage(m_plot.CursorEnabled()
? L"时间光标已启用:拖动竖线移动,双击竖线可输入精确时间。"
: L"时间光标已关闭。",
false);
}
break; break;
case ToolbarAction::MERGE_LANES: case ToolbarAction::MERGE_LANES:
if (!m_dataSet.Empty()) if (!m_dataSet.Empty())
@@ -1513,12 +1490,13 @@ namespace jdi
break; break;
case PLOT_CMD_AUTO_FIT_ALL: case PLOT_CMD_AUTO_FIT_ALL:
{ {
// 全局切换:一次打开/关闭全部网格各自的开关。 // 全局切换:一次打开/关闭全部网格各自的开关;打开时立即生效一次。
const bool enable = !m_plot.AllLanesAutoFitToSelection(); const bool enable = !m_plot.AllLanesAutoFitToSelection();
m_plot.SetAllLanesAutoFitToSelection(enable); m_plot.SetAllLanesAutoFitToSelection(enable, true);
break; break;
} }
case PLOT_CMD_LANE_FIT_TIME: case PLOT_CMD_LANE_FIT_TIME:
// 「按网格时间轴展开」:展开时间轴后同时把该网格的 Y 轴自适应一次。
m_plot.FitTimeToLane(laneIndex); m_plot.FitTimeToLane(laneIndex);
break; break;
case PLOT_CMD_FIT_ALL: case PLOT_CMD_FIT_ALL:
@@ -2039,6 +2017,16 @@ namespace jdi
const int x = GET_X_LPARAM(lParam); const int x = GET_X_LPARAM(lParam);
const int y = GET_Y_LPARAM(lParam); const int y = GET_Y_LPARAM(lParam);
// 「选区放大」正在框选时右键 = 放弃当前选区(显示提示),
// 与 Esc 不同:这里**不退出**选区放大模式,也不弹出网格菜单。
// 左键仍按着,因此保留鼠标捕获与激活目标,松手时不会命中被放弃的选区。
if (m_plot.IsRubberSelecting())
{
m_plot.CancelDrag(true);
InvalidateRect(hwnd, nullptr, FALSE);
return 0;
}
// 编辑态下右键 = 取消选区并把插入符移到文本末尾(常规输入框行为), // 编辑态下右键 = 取消选区并把插入符移到文本末尾(常规输入框行为),
// 其它操作(弹菜单、拖拽)一并被吞掉。 // 其它操作(弹菜单、拖拽)一并被吞掉。
if (m_tabBar.IsEditing() || m_panel.IsEditing() || m_plot.IsEditing() || m_colorPicker.IsEditing()) if (m_tabBar.IsEditing() || m_panel.IsEditing() || m_plot.IsEditing() || m_colorPicker.IsEditing())
@@ -2097,8 +2085,12 @@ namespace jdi
} }
case WM_RBUTTONUP: case WM_RBUTTONUP:
{ {
ReleaseCapture(); // 左键仍按着时不释放捕获与激活目标(例如框选过程中右键放弃选区后再松开左键)。
m_activeTarget = Target::NONE; if ((wParam & MK_LBUTTON) == 0)
{
ReleaseCapture();
m_activeTarget = Target::NONE;
}
return 0; return 0;
} }
case WM_MOUSEWHEEL: case WM_MOUSEWHEEL:
+87 -39
View File
@@ -23,13 +23,22 @@ namespace jdi
{ {
NONE = 0, /**< 未拖拽。 */ NONE = 0, /**< 未拖拽。 */
PAN, /**< 常规模式:水平平移时间轴 + 纵向偏移当前网格幅值。 */ PAN, /**< 常规模式:水平平移时间轴 + 纵向偏移当前网格幅值。 */
RUBBER_TIME, /**< 时间轴缩放选区(竖向贯穿全高)。 */ RUBBER_SELECT, /**< 选区放大:按拖拽方向决定横向(时间轴)或纵向(幅值轴)选区。 */
RUBBER_VALUE, /**< Y 轴缩放选区(限定在某个网格内)。 */
SCROLL_LANES, /**< 拖动网格滚动条。 */ SCROLL_LANES, /**< 拖动网格滚动条。 */
CURSOR_MOVE, /**< 拖拽时间光标。 */ CURSOR_MOVE, /**< 拖拽时间光标。 */
MOVE_SIGNAL /**< 拖拽信号曲线到其它网格。 */ MOVE_SIGNAL /**< 拖拽信号曲线到其它网格。 */
}; };
/**
* @brief 选区放大拖拽的轴向(由首次明显位移的方向决定)。
*/
enum class PlotRubberAxis : int
{
UNDECIDED = 0, /**< 尚未确定(拖拽刚刚开始)。 */
TIME = 1, /**< 横向拖拽:放大时间轴。 */
VALUE = 2 /**< 纵向拖拽:放大当前网格的幅值轴。 */
};
/** /**
* @brief 绘图区内的内联数值输入目标。 * @brief 绘图区内的内联数值输入目标。
*/ */
@@ -255,13 +264,20 @@ namespace jdi
/** /**
* @brief 放弃当前正在进行的拖拽(缩放选区/平移/信号拖动),不改变视图范围。 * @brief 放弃当前正在进行的拖拽(缩放选区/平移/信号拖动),不改变视图范围。
* @param [IN] showHint 是否同时显示“已放弃选区”提示。
* @return 确实取消了某个拖拽返回 true。 * @return 确实取消了某个拖拽返回 true。
*/ */
bool CancelDrag(); bool CancelDrag(bool showHint = false);
/** /**
* @brief 退出全部缩放模式(时间轴选区 / Y 轴选区),使工具栏缩放按钮回到未选中态。 * @brief 判断当前是否处于「选区放大」的框选过程中(供宿主处理右键放弃)。
* @return 之前处于某个缩放模式返回 true。 * @return 正在框选返回 true。
*/
bool IsRubberSelecting() const { return m_dragMode == PlotDragMode::RUBBER_SELECT; }
/**
* @brief 退出全部缩放模式(选区放大),使工具栏缩放按钮回到未选中态。
* @return 之前处于缩放模式返回 true。
*/ */
bool ExitZoomModes(); bool ExitZoomModes();
@@ -272,20 +288,26 @@ namespace jdi
* 必须清空缓存,避免旧几何键与新的信号意外重合而取到过期顶点。 * 必须清空缓存,避免旧几何键与新的信号意外重合而取到过期顶点。
*/ */
void InvalidatePolylineCache(); /** void InvalidatePolylineCache(); /**
* @brief 切换时间光标:启用时置于当前视图中心,再调用则关闭。 * @brief 三态循环切换时间光标:无 → 单光标 → 双光标 → 无。
* @return 状态发生变化返回 true。 * @return 状态发生变化返回 true。
*/ */
bool ToggleCursor(); bool CycleCursorMode();
/** /**
* @brief 判断时间光标是否已启用。 * @brief 判断时间光标是否已启用(单/双光标均为启用)。
* @return 启用返回 true。 * @return 启用返回 true。
*/ */
bool CursorEnabled() const; bool CursorEnabled() const;
/** /**
* @brief 获取当前光标对应的时间。 * @brief 获取当前时间光标模式。
* @return 光标时间。 * @return 光标模式。
*/
PlotCursorMode CursorMode() const;
/**
* @brief 获取光标 1 对应的时间。
* @return 光标 1 时间。
*/ */
double CursorTime() const; double CursorTime() const;
@@ -330,12 +352,24 @@ namespace jdi
*/ */
bool ToggleLaneAutoFitToSelection(int laneIndex); bool ToggleLaneAutoFitToSelection(int laneIndex);
/**
* @brief 让某网格的「选区 Y 轴自适应」开关立即生效一次(打开时调用)。
* @param [IN] laneIndex 网格下标。
*/
void ApplyAutoFitToSelectionNow(int laneIndex);
/**
* @brief 对全部网格的「选区 Y 轴自适应」开关立即生效一次(全局打开时调用)。
*/
void ApplyAllAutoFitToSelectionNow();
/** /**
* @brief 一次性设置全部网格的「选区 Y 轴自适应」开关(全局菜单项 / 工具栏按钮)。 * @brief 一次性设置全部网格的「选区 Y 轴自适应」开关(全局菜单项 / 工具栏按钮)。
* @param [IN] enabled 目标状态。 * @param [IN] enabled 目标状态。
* @param [IN] applyNow 打开时是否立即按当前视图执行一次 Y 轴自适应。
* @return 任一网格状态发生变化返回 true。 * @return 任一网格状态发生变化返回 true。
*/ */
bool SetAllLanesAutoFitToSelection(bool enabled); bool SetAllLanesAutoFitToSelection(bool enabled, bool applyNow = true);
/** /**
* @brief 进入某网格 Y 轴上限/下限的内联输入状态。 * @brief 进入某网格 Y 轴上限/下限的内联输入状态。
@@ -601,23 +635,47 @@ namespace jdi
void ShiftLaneRange(int laneIndex, double deltaValue); void ShiftLaneRange(int laneIndex, double deltaValue);
/** /**
* @brief 计算时间光标当前的横坐标。 * @brief 计算指定时间光标当前的横坐标。
* @param [IN] which 光标序号(1 或 2)。
* @return 横坐标。 * @return 横坐标。
*/ */
float CursorX() const; float CursorX(int which) const;
/** /**
* @brief 计算时间读数标签(或编辑框)的矩形。 * @brief 计算指定光标时间读数标签的矩形。
* @return 标签矩形,位于绘图区顶部以避免遮挡时间轴刻度。 * @param [IN] which 光标序号(1 或 2)。
* @return 标签矩形,位于绘图区底部(不遮挡网格内数据点标注)。
*/ */
gd::RectF CursorLabelRect() const; gd::RectF CursorLabelRect(int which) const;
/** /**
* @brief 判断给定横坐标是否落在时间光标附近(允许拖拽的容差范围)。 * @brief 判断给定横坐标是否落在某条时间光标附近(允许拖拽的容差范围)。
* @param [IN] x 横坐标。 * @param [IN] x 横坐标。
* @param [OUT] which 命中的光标序号(1 或 2),未命中时为 0。
* @return 命中返回 true。 * @return 命中返回 true。
*/ */
bool IsOnCursorLine(int x) const; bool IsOnCursorLine(int x, int& which) const;
/**
* @brief 将两条光标的时间夹取到当前可见时间范围内。
*
* 缩放/平移/自适应会改变可见区间,若不夹取,光标会落到视图之外并“消失”,
* 表现为“放大后无法再拖动光标”。
*/
void ClampCursorToView();
/**
* @brief 获取指定光标对应的显示颜色。
* @param [IN] which 光标序号(1 或 2)。
* @return 颜色。
*/
std::uint32_t CursorColor(int which) const;
/**
* @brief 设置并显示一条临时提示(在绘图区底部以浮层方式绘制)。
* @param [IN] text 提示文本;传空串则清除提示。
*/
void SetHint(const std::wstring& text);
/** /**
* @brief 精确检测鼠标位置是否命中某条可见信号曲线。 * @brief 精确检测鼠标位置是否命中某条可见信号曲线。
@@ -703,6 +761,12 @@ namespace jdi
*/ */
void PaintSignalInteraction(gd::Graphics& g); void PaintSignalInteraction(gd::Graphics& g);
/**
* @brief 在绘图区底部绘制临时提示条(如“已放弃当前选区”)。
* @param [IN,OUT] g 绘图上下文。
*/
void PaintHint(gd::Graphics& g);
/** /**
* @brief 绘制网格纵向滚动条。 * @brief 绘制网格纵向滚动条。
* @param [IN,OUT] g 绘图上下文。 * @param [IN,OUT] g 绘图上下文。
@@ -720,17 +784,6 @@ namespace jdi
void PaintLaneLegend(gd::Graphics& g, int laneIndex, const LaneVisual& lane, float visibleTop, void PaintLaneLegend(gd::Graphics& g, int laneIndex, const LaneVisual& lane, float visibleTop,
float visibleBottom); float visibleBottom);
/**
* @brief 在网格内绘制各信号的幅值读数。
* @param [IN,OUT] g 绘图上下文。
* @param [IN] laneIndex 网格下标。
* @param [IN] lane 网格绘制信息。
* @param [IN] visibleTop 网格在绘图区内的可见上边界。
* @param [IN] visibleBottom 网格在绘图区内的可见下边界。
*/
void PaintLaneReadout(gd::Graphics& g, int laneIndex, const LaneVisual& lane, float visibleTop,
float visibleBottom);
/** /**
* @brief 判断给定矩形是否完整落在可见纵向区间内。 * @brief 判断给定矩形是否完整落在可见纵向区间内。
* @param [IN] rect 待测矩形。 * @param [IN] rect 待测矩形。
@@ -747,15 +800,6 @@ namespace jdi
*/ */
void PaintEmptyState(gd::Graphics& g); void PaintEmptyState(gd::Graphics& g);
/**
* @brief 绘制全部网格的时间光标读数框。
*
* 读数随时间光标实时变化,因此不能进入拖拽静态场景缓存,必须每帧单独绘制。
*
* @param [IN,OUT] g 绘图上下文。
*/
void PaintLaneReadouts(gd::Graphics& g);
const UiFonts* m_fonts = nullptr; /**< 字体资源。 */ const UiFonts* m_fonts = nullptr; /**< 字体资源。 */
const Theme* m_theme = nullptr; /**< 主题资源。 */ const Theme* m_theme = nullptr; /**< 主题资源。 */
const DataSet* m_dataSet = nullptr; /**< 数据集合(只读访问)。 */ const DataSet* m_dataSet = nullptr; /**< 数据集合(只读访问)。 */
@@ -777,6 +821,7 @@ namespace jdi
bool m_scrollBarVisible = false; /**< 是否需要显示滚动条。 */ bool m_scrollBarVisible = false; /**< 是否需要显示滚动条。 */
PlotDragMode m_dragMode = PlotDragMode::NONE; /**< 当前拖拽模式。 */ PlotDragMode m_dragMode = PlotDragMode::NONE; /**< 当前拖拽模式。 */
PlotRubberAxis m_rubberAxis = PlotRubberAxis::UNDECIDED; /**< 选区放大拖拽的轴向。 */
int m_dragLane = -1; /**< 拖拽所作用的网格下标。 */ int m_dragLane = -1; /**< 拖拽所作用的网格下标。 */
bool m_dragMoved = false; /**< 拖拽过程中是否发生了位移。 */ bool m_dragMoved = false; /**< 拖拽过程中是否发生了位移。 */
int m_dragStartX = 0; /**< 拖拽起点横坐标。 */ int m_dragStartX = 0; /**< 拖拽起点横坐标。 */
@@ -790,7 +835,9 @@ namespace jdi
int m_dragTargetLane = -1; /**< 拖拽信号时的目标网格下标。 */ int m_dragTargetLane = -1; /**< 拖拽信号时的目标网格下标。 */
int m_dragMouseX = 0; /**< 拖拽过程中鼠标的当前横坐标(虚影跟随)。 */ int m_dragMouseX = 0; /**< 拖拽过程中鼠标的当前横坐标(虚影跟随)。 */
int m_dragMouseY = 0; /**< 拖拽过程中鼠标的当前纵坐标(虚影跟随)。 */ int m_dragMouseY = 0; /**< 拖拽过程中鼠标的当前纵坐标(虚影跟随)。 */
bool m_cursorHover = false; /**< 鼠标是否悬停在时间光标上。 */ bool m_cursorHover = false; /**< 鼠标是否悬停在某条时间光标上。 */
int m_hoverCursor = 0; /**< 悬停/拖拽中的光标序号(1 或 2,0 表示无)。 */
int m_editCursor = 1; /**< 内联输入所作用的 Time 光标序号。 */
PlotEditTarget m_editTarget = PlotEditTarget::NONE; /**< 当前内联输入目标。 */ PlotEditTarget m_editTarget = PlotEditTarget::NONE; /**< 当前内联输入目标。 */
TextEdit m_edit; /**< 输入框编辑模型(光标/选区/剪贴板)。 */ TextEdit m_edit; /**< 输入框编辑模型(光标/选区/剪贴板)。 */
int m_editLane = -1; /**< 内联输入所作用的网格下标。 */ int m_editLane = -1; /**< 内联输入所作用的网格下标。 */
@@ -803,6 +850,7 @@ namespace jdi
int m_hoverLane = -1; /**< 当前悬停网格下标。 */ int m_hoverLane = -1; /**< 当前悬停网格下标。 */
int m_mouseX = 0; /**< 最近一次鼠标横坐标(用于悬停提示定位)。 */ int m_mouseX = 0; /**< 最近一次鼠标横坐标(用于悬停提示定位)。 */
int m_mouseY = 0; /**< 最近一次鼠标纵坐标(用于悬停提示定位)。 */ int m_mouseY = 0; /**< 最近一次鼠标纵坐标(用于悬停提示定位)。 */
std::wstring m_hintText; /**< 临时提示文本(空表示不显示)。 */
std::vector<LaneVisual> m_lanes; /**< 当前网格绘制信息集合。 */ std::vector<LaneVisual> m_lanes; /**< 当前网格绘制信息集合。 */
/** /**
File diff suppressed because it is too large. Load diff
+2 -3
View File
@@ -23,9 +23,8 @@ namespace jdi
OPEN_FILE, /**< 打开 CSV 文件。 */ OPEN_FILE, /**< 打开 CSV 文件。 */
FIT_ALL, /**< 视图自适应到可见信号范围(同时退出缩放模式)。 */ FIT_ALL, /**< 视图自适应到可见信号范围(同时退出缩放模式)。 */
AUTO_FIT_Y, /**< 切换「选区 Y 轴自适应」全局开关:时间轴选区放大后自动匹配各网格 Y 轴。 */ AUTO_FIT_Y, /**< 切换「选区 Y 轴自适应」全局开关:时间轴选区放大后自动匹配各网格 Y 轴。 */
ZOOM_TIME, /**< 切换「时间轴选区」模式(框选时间区间后放大)。 */ ZOOM_SELECT, /**< 切换「选区放大」模式(横向拖拽放大时间轴,纵向拖拽放大该网格幅值)。 */
ZOOM_VALUE, /**< 切换「Y 轴选区」模式(框选幅值区间后放大)。 */ CURSOR, /**< 三态循环切换时间光标:无 → 单光标 → 双光标。 */
CURSOR, /**< 切换时间光标。 */
MERGE_LANES, /**< 合并为单一网格。 */ MERGE_LANES, /**< 合并为单一网格。 */
SPLIT_LANES, /**< 按信号拆分为多网格。 */ SPLIT_LANES, /**< 按信号拆分为多网格。 */
CLEAR_ALL /**< 清空当前标签页的全部网格与信号勾选。 */ CLEAR_ALL /**< 清空当前标签页的全部网格与信号勾选。 */
+1 -2
View File
@@ -69,8 +69,7 @@ namespace jdi
{ToolbarAction::OPEN_FILE, L"导入文件", true, false, false}, {ToolbarAction::OPEN_FILE, L"导入文件", true, false, false},
{ToolbarAction::FIT_ALL, L"适应视图", false, true, false}, {ToolbarAction::FIT_ALL, L"适应视图", false, true, false},
{ToolbarAction::AUTO_FIT_Y, L"Y 轴自适应", false, true, true}, {ToolbarAction::AUTO_FIT_Y, L"Y 轴自适应", false, true, true},
{ToolbarAction::ZOOM_TIME, L"时间轴选区", false, true, true}, {ToolbarAction::ZOOM_SELECT, L"选区放大", false, true, true},
{ToolbarAction::ZOOM_VALUE, L"Y 轴选区", false, true, true},
{ToolbarAction::CURSOR, L"光标", false, true, true}, {ToolbarAction::CURSOR, L"光标", false, true, true},
{ToolbarAction::MERGE_LANES, L"合并网格", false, true, false}, {ToolbarAction::MERGE_LANES, L"合并网格", false, true, false},
{ToolbarAction::SPLIT_LANES, L"拆分网格", false, true, false}, {ToolbarAction::SPLIT_LANES, L"拆分网格", false, true, false},
+26 -10
View File
@@ -27,8 +27,17 @@ namespace jdi
enum class PlotZoomMode : int enum class PlotZoomMode : int
{ {
NONE = 0, /**< 常规模式:拖拽平移时间轴,光标为箭头。 */ NONE = 0, /**< 常规模式:拖拽平移时间轴,光标为箭头。 */
TIME = 1, /**< 时间轴缩放:在绘图区拖拽竖向选区,放大时间范围。 */ SELECT = 1 /**< 选区放大:横向拖拽放大时间轴,纵向拖拽放大当前网格的幅值范围。 */
VALUE = 2 /**< Y 轴缩放:在某个网格内拖拽横向选区,放大该网格幅值范围。 */ };
/**
* @brief 时间光标模式(由工具栏「光标」按钮三态循环切换)。
*/
enum class PlotCursorMode : int
{
NONE = 0, /**< 不显示光标。 */
SINGLE = 1, /**< 单光标。 */
DOUBLE = 2 /**< 双光标(第二条用于与第一条对比取值)。 */
}; };
/** /**
@@ -559,14 +568,20 @@ namespace jdi
float& LaneScroll() { return m_laneScroll; } float& LaneScroll() { return m_laneScroll; }
/// @brief 网格纵向滚动偏移量(只读)。 /// @brief 网格纵向滚动偏移量(只读)。
float LaneScroll() const { return m_laneScroll; } float LaneScroll() const { return m_laneScroll; }
/// @brief 时间光标是否启用(可读写)。 /// @brief 时间光标模式(可读写)。
bool& CursorEnabled() { return m_cursorEnabled; } PlotCursorMode& CursorMode() { return m_cursorMode; }
/// @brief 时间光标是否启用(只读)。 /// @brief 时间光标模式(只读)。
bool CursorEnabled() const { return m_cursorEnabled; } PlotCursorMode CursorMode() const { return m_cursorMode; }
/// @brief 时间光标位置(可读写)。 /// @brief 是否启用了时间光标(单/双光标均为启用,只读)。
bool CursorEnabled() const { return m_cursorMode != PlotCursorMode::NONE; }
/// @brief 光标 1(左侧)的时间位置(可读写)。
double& CursorTime() { return m_cursorTime; } double& CursorTime() { return m_cursorTime; }
/// @brief 时间光标位置(只读)。 /// @brief 光标 1(左侧)的时间位置(只读)。
double CursorTime() const { return m_cursorTime; } double CursorTime() const { return m_cursorTime; }
/// @brief 光标 2 的时间位置(可读写,仅双光标模式使用)。
double& CursorTimeAlt() { return m_cursorTimeAlt; }
/// @brief 光标 2 的时间位置(只读)。
double CursorTimeAlt() const { return m_cursorTimeAlt; }
/// @brief 绘图区拖拽缩放模式(可读写)。 /// @brief 绘图区拖拽缩放模式(可读写)。
PlotZoomMode& ZoomMode() { return m_zoomMode; } PlotZoomMode& ZoomMode() { return m_zoomMode; }
/// @brief 绘图区拖拽缩放模式(只读)。 /// @brief 绘图区拖拽缩放模式(只读)。
@@ -582,8 +597,9 @@ namespace jdi
double m_viewMax = 1.0; /**< 视图右边界时间。 */ double m_viewMax = 1.0; /**< 视图右边界时间。 */
bool m_viewInitialized = false; /**< 视图范围是否已初始化。 */ bool m_viewInitialized = false; /**< 视图范围是否已初始化。 */
float m_laneScroll = 0.0f; /**< 网格纵向滚动偏移量。 */ float m_laneScroll = 0.0f; /**< 网格纵向滚动偏移量。 */
bool m_cursorEnabled = false; /**< 时间光标是否启用(由工具栏按钮切换)。 */ PlotCursorMode m_cursorMode = PlotCursorMode::NONE; /**< 时间光标模式(无/单/双)。 */
double m_cursorTime = 0.0; /**< 时间光标位置。 */ double m_cursorTime = 0.0; /**< 光标 1(左侧)的时间位置。 */
double m_cursorTimeAlt = 0.0; /**< 光标 2 的时间位置(仅双光标模式有效)。 */
PlotZoomMode m_zoomMode = PlotZoomMode::NONE; /**< 绘图区拖拽缩放模式。 */ PlotZoomMode m_zoomMode = PlotZoomMode::NONE; /**< 绘图区拖拽缩放模式。 */
}; };
+2 -1
View File
@@ -102,7 +102,8 @@ namespace jdi
std::uint32_t gridWeak = 0; /**< 次级网格线颜色。 */ std::uint32_t gridWeak = 0; /**< 次级网格线颜色。 */
std::uint32_t gridStrong = 0; /**< 主要网格线颜色。 */ std::uint32_t gridStrong = 0; /**< 主要网格线颜色。 */
std::uint32_t axisLine = 0; /**< 坐标轴线颜色。 */ std::uint32_t axisLine = 0; /**< 坐标轴线颜色。 */
std::uint32_t cursor = 0; /**< 时间光标颜色。 */ std::uint32_t cursor = 0; /**< 时间光标 1 的颜色。 */
std::uint32_t cursorAlt = 0; /**< 时间光标 2 的颜色(与光标 1 区分且同一色系)。 */
std::uint32_t readoutBg = 0; /**< 数值读数底色。 */ std::uint32_t readoutBg = 0; /**< 数值读数底色。 */
std::uint32_t danger = 0; /**< 警示色。 */ std::uint32_t danger = 0; /**< 警示色。 */
std::uint32_t warning = 0; /**< 提示色。 */ std::uint32_t warning = 0; /**< 提示色。 */
+2
View File
@@ -62,6 +62,8 @@ namespace jdi
theme.gridStrong = MakeArgb(0xC3B896u); theme.gridStrong = MakeArgb(0xC3B896u);
theme.axisLine = MakeArgb(kBase1); theme.axisLine = MakeArgb(kBase1);
theme.cursor = MakeArgb(kOrange); theme.cursor = MakeArgb(kOrange);
// 第二光标用 Solarized 青色:与橙色同族饱和度、对比清晰又不刺眼。
theme.cursorAlt = MakeArgb(kCyan);
theme.readoutBg = MakeArgb(0xF6EFDCu); theme.readoutBg = MakeArgb(0xF6EFDCu);
theme.danger = MakeArgb(kRed); theme.danger = MakeArgb(kRed);
theme.warning = MakeArgb(kYellow); theme.warning = MakeArgb(kYellow);