fix: 修复网格滚动溢出并重构绘图区缩放与时间光标交互

网格滚动:
- 修复纵向滚动时波形与文字溢出到工具栏/时间轴的问题:网格内容裁剪改为与绘图区求交,
  并对网格内的图例、幅值标签与读数框增加显式可见性判定
  (GDI 文本不遵循 GDI+ 裁剪区,仅裁剪矢量绘制不足以阻止溢出)。
- 网格之间改为完整矩形描边并加大间距,边界清晰;时间轴固定预留高度,不再被网格挤压。
- 时间轴刻度标签夹在绘图区横向范围内,避免与左侧时间列名重叠。

缩放交互:
- 取消滚轮缩放:滚轮改为纵向滚动网格,Shift+滚轮水平平移。
- 新增「时间轴缩放」「Y 轴缩放」工具栏切换按钮:选中后在绘图区拖拽选区放大对应轴向,
  拖动过程显示选区与跨度提示(时间用强调色、Y 轴用警示色);再次点击退出该模式。
- 「适应视图」回到初始状态:恢复自适应范围并退出缩放模式。
- 移除右键框选缩放,避免与模式化缩放重复。

时间光标:
- 改为工具栏「光标」按钮切换;启用时置于当前视图中心,再次点击关闭(Esc 亦可关闭)。
- 光标由鼠标靠近后拖拽移动,不再跟随鼠标;纵向贯穿全部网格并延伸到时间轴底部,
  顶部带三角把手提示可拖拽。
- 具体时间读数移至绘图区顶部,仅在鼠标覆盖光标时显示,不再遮挡时间轴刻度尺。
- 双击光标进入精确时间输入,交互与线宽输入一致:整段高亮、首次键入即整体覆盖、
  回车提交、Esc 取消、点击框外提交。
- 光标形态按模式区分:默认与悬停线外为箭头(不再出现十字指针),
  时间轴缩放/光标为水平双向箭头,Y 轴缩放为垂直双向箭头。
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-09-29 19:32:37 +08:00
1 parent cba62c8218
commit 17eb03a1b3
6 files changed
+852 -359

No files matched your search

+135 -40
View File
@@ -21,10 +21,11 @@ namespace jdi
enum class PlotDragMode : int
{
NONE = 0, /**< 未拖拽。 */
PAN_TIME, /**< 水平平移时间轴。 */
ZOOM_Y, /**< 纵向缩放某网格的幅值轴。 */
BOX_ZOOM, /**< 右键框选缩放。 */
SCROLL_LANES /**< 拖动网格滚动条。 */
PAN_TIME, /**< 水平平移时间轴(常规模式下的左键拖拽)。 */
RUBBER_TIME, /**< 时间轴缩放选区(竖向贯穿全高)。 */
RUBBER_VALUE, /**< Y 轴缩放选区(限定在某个网格内)。 */
SCROLL_LANES, /**< 拖动网格滚动条。 */
CURSOR_MOVE /**< 拖拽时间光标。 */
};
/**
@@ -95,35 +96,17 @@ namespace jdi
bool OnLButtonUp(int x, int y);
/**
* @brief 处理鼠标右键按下(开始框选缩放)。
* @param [IN] x 客户区横坐标。
* @param [IN] y 客户区纵坐标。
* @return 需要重绘返回 true。
*/
bool OnRButtonDown(int x, int y);
/**
* @brief 处理鼠标右键抬起(结束框选缩放或清除光标)。
* @param [IN] x 客户区横坐标。
* @param [IN] y 客户区纵坐标。
* @return 需要重绘返回 true。
*/
bool OnRButtonUp(int x, int y);
/**
* @brief 处理鼠标滚轮。
* @brief 处理鼠标滚轮(仅用于平移与网格滚动,不再承担缩放)。
* @param [IN] x 客户区横坐标。
* @param [IN] y 客户区纵坐标。
* @param [IN] delta 滚轮增量。
* @param [IN] ctrl 是否按下 Ctrl(缩放当前网格幅值)。
* @param [IN] shift 是否按下 Shift(平移时间轴)。
* @param [IN] alt 是否按下 Alt(纵向滚动网格)。
* @param [IN] shift 是否按下 Shift(水平平移时间轴)。
* @return 需要重绘返回 true。
*/
bool OnMouseWheel(int x, int y, int delta, bool ctrl, bool shift, bool alt);
bool OnMouseWheel(int x, int y, int delta, bool shift);
/**
* @brief 处理鼠标双击(恢复自适应视图)。
* @brief 处理鼠标双击(命中时间光标时进入精确时间输入)。
* @param [IN] x 客户区横坐标。
* @param [IN] y 客户区纵坐标。
* @return 需要重绘返回 true。
@@ -182,22 +165,61 @@ namespace jdi
float LaneContentHeight() const { return m_laneContentHeight; }
/**
* @brief 清除已固定的时间光标。
* @brief 清除已启用的时间光标。
*/
void ClearCursor();
void DisableCursor();
/**
* @brief 判断当前是否存在激活的时间光标。
* @return 存在返回 true。
* @brief 切换时间光标:启用时置于当前视图中心,再调用则关闭。
* @return 状态发生变化返回 true。
*/
bool HasCursor() const;
bool ToggleCursor();
/**
* @brief 获取当前激活光标对应的时间。
* @brief 判断时间光标是否已启用。
* @return 启用返回 true。
*/
bool CursorEnabled() const;
/**
* @brief 获取当前光标对应的时间。
* @return 光标时间。
*/
double CursorTime() const;
/**
* @brief 切换拖拽缩放模式(再次调用同一模式则取消)。
* @param [IN] mode 目标模式。
* @return 状态发生变化返回 true。
*/
bool ToggleZoomMode(PlotZoomMode mode);
/**
* @brief 获取当前拖拽缩放模式。
* @return 模式枚举。
*/
PlotZoomMode CurrentZoomMode() const;
/**
* @brief 判断是否已进入精确时间输入状态。
* @return 处于编辑态返回 true。
*/
bool IsEditingCursor() const { return m_cursorEditing; }
/**
* @brief 处理编辑态下的字符输入。
* @param [IN] character 输入字符。
* @return 界面状态发生变化返回 true。
*/
bool OnEditChar(wchar_t character);
/**
* @brief 处理编辑态下的按键。
* @param [IN] key 虚拟键码。
* @return 界面状态发生变化返回 true。
*/
bool OnEditKey(WPARAM key);
/**
* @brief 获取当前视图左边界时间。
* @return 视图左边界时间。
@@ -334,6 +356,55 @@ namespace jdi
*/
void ZoomTimeAt(double factor, double anchorTime);
/**
* @brief 将时间轴缩放到指定的时间区间(拖拽选区释放时调用)。
* @param [IN] x0 选区起点横坐标。
* @param [IN] x1 选区终点横坐标。
* @return 产生了有效缩放返回 true。
*/
bool ApplyTimeSelection(float x0, float x1);
/**
* @brief 将某个网格的幅值轴缩放到指定区间(拖拽选区释放时调用)。
* @param [IN] laneIndex 目标网格下标。
* @param [IN] y0 选区起点纵坐标。
* @param [IN] y1 选区终点纵坐标。
* @return 产生了有效缩放返回 true。
*/
bool ApplyValueSelection(int laneIndex, float y0, float y1);
/**
* @brief 计算时间光标当前的横坐标。
* @return 横坐标。
*/
float CursorX() const;
/**
* @brief 计算时间读数标签(或编辑框)的矩形。
* @return 标签矩形,位于绘图区顶部以避免遮挡时间轴刻度。
*/
gd::RectF CursorLabelRect() const;
/**
* @brief 判断给定横坐标是否落在时间光标附近(允许拖拽的容差范围)。
* @param [IN] x 横坐标。
* @return 命中返回 true。
*/
bool IsOnCursorLine(int x) const;
/**
* @brief 进入时间光标的精确时间输入状态。
* @return 进入成功返回 true。
*/
bool BeginCursorEdit();
/**
* @brief 结束时间光标的精确时间输入。
* @param [IN] commit 是否提交当前输入。
* @return 状态发生变化返回 true。
*/
bool EndCursorEdit(bool commit);
/**
* @brief 构建单条信号在当前视图下的折线点序列。
* @param [IN] resolved 已解析的信号视图。
@@ -364,12 +435,18 @@ namespace jdi
void PaintTimeAxis(gd::Graphics& g, const std::vector<double>& timeTicks, double timeStep);
/**
* @brief 绘制时间光标及其读数。
* @brief 绘制时间光标、选区及其读数。
* @param [IN,OUT] g 绘图上下文。
* @param [IN] lanes 网格绘制信息集合。
*/
void PaintCursor(gd::Graphics& g, const std::vector<LaneVisual>& lanes);
/**
* @brief 绘制拖动中的缩放选区。
* @param [IN,OUT] g 绘图上下文。
*/
void PaintRubberBand(gd::Graphics& g);
/**
* @brief 绘制网格纵向滚动条。
* @param [IN,OUT] g 绘图上下文。
@@ -381,16 +458,32 @@ namespace jdi
* @param [IN,OUT] g 绘图上下文。
* @param [IN] laneIndex 网格下标。
* @param [IN] lane 网格绘制信息。
* @param [IN] visibleTop 网格在绘图区内的可见上边界。
* @param [IN] visibleBottom 网格在绘图区内的可见下边界。
*/
void PaintLaneLegend(gd::Graphics& g, int laneIndex, const LaneVisual& lane);
void PaintLaneLegend(gd::Graphics& g, int laneIndex, const LaneVisual& lane, float visibleTop,
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);
void PaintLaneReadout(gd::Graphics& g, int laneIndex, const LaneVisual& lane, float visibleTop,
float visibleBottom);
/**
* @brief 判断给定矩形是否完整落在可见纵向区间内。
* @param [IN] rect 待测矩形。
* @param [IN] visibleTop 可见区上边界。
* @param [IN] visibleBottom 可见区下边界。
* @return 完整可见返回 true。
* @note GDI 文本绘制不会遵循 GDI+ 的裁剪区,必须显式判定,否则滚动时文字会溢出到工具栏。
*/
static bool IsFullyVisible(const gd::RectF& rect, float visibleTop, float visibleBottom);
/**
* @brief 绘制空状态提示文本。
@@ -424,11 +517,13 @@ namespace jdi
int m_dragStartY = 0; /**< 拖拽起点纵坐标。 */
int m_dragLastX = 0; /**< 上一次拖拽横坐标。 */
int m_dragLastY = 0; /**< 上一次拖拽纵坐标。 */
double m_dragAnchorValue = 0.0; /**< 纵轴缩放锚点幅值。 */
double m_dragAnchorRatio = 0.5; /**< 纵轴缩放锚点相对位置。 */
double m_dragStartYMin = 0.0; /**< 拖拽起始幅值下限。 */
double m_dragStartYMax = 1.0; /**< 拖拽起始幅值上限。 */
gd::RectF m_boxRect; /**< 框选缩放矩形。 */
gd::RectF m_rubberRect; /**< 拖拽选区矩形。 */
bool m_cursorHover = false; /**< 鼠标是否悬停在时间光标上。 */
bool m_cursorEditing = false; /**< 是否处于精确时间输入状态。 */
bool m_cursorEditFresh = false; /**< 编辑框是否处于“首次输入整体替换”状态。 */
std::wstring m_cursorEditText; /**< 编辑框当前文本。 */
gd::RectF m_cursorEditRect; /**< 编辑框矩形(用于命中测试与绘制)。 */
int m_hoverLane = -1; /**< 当前悬停网格下标。 */
std::vector<LaneVisual> m_lanes; /**< 当前网格绘制信息集合。 */