fix: 修复网格滚动溢出并重构绘图区缩放与时间光标交互
网格滚动: - 修复纵向滚动时波形与文字溢出到工具栏/时间轴的问题:网格内容裁剪改为与绘图区求交, 并对网格内的图例、幅值标签与读数框增加显式可见性判定 (GDI 文本不遵循 GDI+ 裁剪区,仅裁剪矢量绘制不足以阻止溢出)。 - 网格之间改为完整矩形描边并加大间距,边界清晰;时间轴固定预留高度,不再被网格挤压。 - 时间轴刻度标签夹在绘图区横向范围内,避免与左侧时间列名重叠。 缩放交互: - 取消滚轮缩放:滚轮改为纵向滚动网格,Shift+滚轮水平平移。 - 新增「时间轴缩放」「Y 轴缩放」工具栏切换按钮:选中后在绘图区拖拽选区放大对应轴向, 拖动过程显示选区与跨度提示(时间用强调色、Y 轴用警示色);再次点击退出该模式。 - 「适应视图」回到初始状态:恢复自适应范围并退出缩放模式。 - 移除右键框选缩放,避免与模式化缩放重复。 时间光标: - 改为工具栏「光标」按钮切换;启用时置于当前视图中心,再次点击关闭(Esc 亦可关闭)。 - 光标由鼠标靠近后拖拽移动,不再跟随鼠标;纵向贯穿全部网格并延伸到时间轴底部, 顶部带三角把手提示可拖拽。 - 具体时间读数移至绘图区顶部,仅在鼠标覆盖光标时显示,不再遮挡时间轴刻度尺。 - 双击光标进入精确时间输入,交互与线宽输入一致:整段高亮、首次键入即整体覆盖、 回车提交、Esc 取消、点击框外提交。 - 光标形态按模式区分:默认与悬停线外为箭头(不再出现十字指针), 时间轴缩放/光标为水平双向箭头,Y 轴缩放为垂直双向箭头。
This commit is contained in:
1 parent
cba62c8218
commit
17eb03a1b3
6 files changed
+852
-359
No files matched your search
@@ -32,7 +32,8 @@ namespace jdi
|
||||
const int kMinClientWidth = 860; /**< 窗口最小客户区宽度。 */
|
||||
const int kMinClientHeight = 540; /**< 窗口最小客户区高度。 */
|
||||
const UINT kMessageRemoveFile = WM_APP + 1; /**< 面板请求移除文件的自定义消息。 */
|
||||
const wchar_t* kStatusHint = L"滚轮缩放 · Shift+滚轮平移 · Ctrl+滚轮调幅值 · 右键框选 · 双击自适应";
|
||||
const wchar_t* kStatusHint =
|
||||
L"滚轮纵向滚动网格 · Shift+滚轮平移 · 双击绘图区恢复自适应 · Esc 关闭光标";
|
||||
|
||||
/** @brief 标签页右键菜单命令 ID。 */
|
||||
const int kMenuTabNew = 1;
|
||||
@@ -576,6 +577,13 @@ namespace jdi
|
||||
m_toolbar.SetStatusText(std::wstring());
|
||||
}
|
||||
|
||||
// 工具栏可切换按钮的选中状态始终与当前标签页配置保持一致。
|
||||
m_toolbar.SetToggleState(ToolbarAction::ZOOM_TIME,
|
||||
m_plot.CurrentZoomMode() == PlotZoomMode::TIME);
|
||||
m_toolbar.SetToggleState(ToolbarAction::ZOOM_VALUE,
|
||||
m_plot.CurrentZoomMode() == PlotZoomMode::VALUE);
|
||||
m_toolbar.SetToggleState(ToolbarAction::CURSOR, m_plot.CursorEnabled());
|
||||
|
||||
{
|
||||
gd::Graphics g(m_backDc);
|
||||
g.SetSmoothingMode(gd::SmoothingModeAntiAlias);
|
||||
@@ -641,7 +649,7 @@ namespace jdi
|
||||
rightText = m_statusMessage;
|
||||
rightColor = m_statusIsError ? m_theme.danger : m_theme.textStrong;
|
||||
}
|
||||
else if (m_plot.HasCursor() && !m_dataSet.Empty())
|
||||
else if (m_plot.CursorEnabled() && !m_dataSet.Empty())
|
||||
{
|
||||
rightText = L"光标 · " + FormatValue(m_plot.CursorTime());
|
||||
rightColor = m_theme.text;
|
||||
@@ -813,13 +821,36 @@ namespace jdi
|
||||
OpenFileDialog();
|
||||
break;
|
||||
case ToolbarAction::FIT_ALL:
|
||||
// 自适应同时回到初始状态:退出缩放模式并显示全部信号范围。
|
||||
m_plot.FitAll();
|
||||
SetStatusMessage(L"已恢复自适应视图。", false);
|
||||
break;
|
||||
case ToolbarAction::ZOOM_IN:
|
||||
m_plot.ZoomTimeStep(1.4);
|
||||
case ToolbarAction::ZOOM_TIME:
|
||||
if (m_plot.ToggleZoomMode(PlotZoomMode::TIME))
|
||||
{
|
||||
SetStatusMessage((m_plot.CurrentZoomMode() == PlotZoomMode::TIME)
|
||||
? L"时间轴缩放:在绘图区横向拖拽选择区间即可放大。"
|
||||
: L"已退出时间轴缩放模式。",
|
||||
false);
|
||||
}
|
||||
break;
|
||||
case ToolbarAction::ZOOM_OUT:
|
||||
m_plot.ZoomTimeStep(1.0 / 1.4);
|
||||
case ToolbarAction::ZOOM_VALUE:
|
||||
if (m_plot.ToggleZoomMode(PlotZoomMode::VALUE))
|
||||
{
|
||||
SetStatusMessage((m_plot.CurrentZoomMode() == PlotZoomMode::VALUE)
|
||||
? L"Y 轴缩放:在某个网格内纵向拖拽选择区间即可放大。"
|
||||
: L"已退出 Y 轴缩放模式。",
|
||||
false);
|
||||
}
|
||||
break;
|
||||
case ToolbarAction::CURSOR:
|
||||
if (m_plot.ToggleCursor())
|
||||
{
|
||||
SetStatusMessage(m_plot.CursorEnabled()
|
||||
? L"时间光标已启用:拖动竖线移动,双击竖线可输入精确时间。"
|
||||
: L"时间光标已关闭。",
|
||||
false);
|
||||
}
|
||||
break;
|
||||
case ToolbarAction::MERGE_LANES:
|
||||
if (!m_dataSet.Empty())
|
||||
@@ -895,7 +926,7 @@ namespace jdi
|
||||
switch (key)
|
||||
{
|
||||
case VK_ESCAPE:
|
||||
m_plot.ClearCursor();
|
||||
m_plot.DisableCursor();
|
||||
return true;
|
||||
case VK_ADD:
|
||||
case VK_OEM_PLUS:
|
||||
@@ -1336,6 +1367,12 @@ namespace jdi
|
||||
const int x = GET_X_LPARAM(lParam);
|
||||
const int y = GET_Y_LPARAM(lParam);
|
||||
|
||||
// 时间光标处于精确输入态时,点击绘图区之外视为提交(与线宽输入一致)。
|
||||
if (m_plot.IsEditingCursor() && !m_plot.HitTest(x, y))
|
||||
{
|
||||
m_plot.OnEditKey(VK_RETURN);
|
||||
}
|
||||
|
||||
// 点击面板之外的位置时收起面板内展开的自定义颜色面板。
|
||||
if (!m_panel.HitTest(x, y))
|
||||
{
|
||||
@@ -1496,33 +1533,12 @@ namespace jdi
|
||||
m_activeTarget = Target::NONE;
|
||||
return 0;
|
||||
}
|
||||
if (m_plot.HitTest(x, y))
|
||||
{
|
||||
SetCapture(hwnd);
|
||||
m_activeTarget = Target::PLOT;
|
||||
if (m_plot.OnRButtonDown(x, y))
|
||||
{
|
||||
InvalidateRect(hwnd, nullptr, FALSE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
case WM_RBUTTONUP:
|
||||
{
|
||||
const int x = GET_X_LPARAM(lParam);
|
||||
const int y = GET_Y_LPARAM(lParam);
|
||||
bool changed = false;
|
||||
if (m_activeTarget == Target::PLOT)
|
||||
{
|
||||
changed = m_plot.OnRButtonUp(x, y);
|
||||
}
|
||||
ReleaseCapture();
|
||||
m_activeTarget = Target::NONE;
|
||||
if (changed)
|
||||
{
|
||||
InvalidateRect(hwnd, nullptr, FALSE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
case WM_MOUSEWHEEL:
|
||||
@@ -1531,9 +1547,7 @@ namespace jdi
|
||||
ScreenToClient(hwnd, &point);
|
||||
|
||||
const int delta = GET_WHEEL_DELTA_WPARAM(wParam);
|
||||
const bool control = (GET_KEYSTATE_WPARAM(wParam) & MK_CONTROL) != 0;
|
||||
const bool shift = (GET_KEYSTATE_WPARAM(wParam) & MK_SHIFT) != 0;
|
||||
const bool alt = (GetKeyState(VK_MENU) & 0x8000) != 0;
|
||||
|
||||
bool changed = false;
|
||||
if (m_panel.HitTest(point.x, point.y))
|
||||
@@ -1542,7 +1556,7 @@ namespace jdi
|
||||
}
|
||||
else if (m_plot.HitTest(point.x, point.y))
|
||||
{
|
||||
changed = m_plot.OnMouseWheel(point.x, point.y, delta, control, shift, alt);
|
||||
changed = m_plot.OnMouseWheel(point.x, point.y, delta, shift);
|
||||
}
|
||||
if (changed)
|
||||
{
|
||||
@@ -1577,7 +1591,7 @@ namespace jdi
|
||||
break;
|
||||
}
|
||||
case WM_KEYDOWN:
|
||||
// 线宽数值输入状态优先接管按键(回车提交、Esc 取消、退格删除)。
|
||||
// 数值输入状态优先接管按键(回车提交、Esc 取消、退格删除)。
|
||||
if (m_panel.IsEditingWidth())
|
||||
{
|
||||
if (m_panel.OnEditKey(wParam))
|
||||
@@ -1586,6 +1600,14 @@ namespace jdi
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (m_plot.IsEditingCursor())
|
||||
{
|
||||
if (m_plot.OnEditKey(wParam))
|
||||
{
|
||||
InvalidateRect(hwnd, nullptr, FALSE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (HandleKeyDown(wParam))
|
||||
{
|
||||
InvalidateRect(hwnd, nullptr, TRUE);
|
||||
@@ -1594,7 +1616,7 @@ namespace jdi
|
||||
break;
|
||||
case WM_CHAR:
|
||||
{
|
||||
// 线宽数值仅接受数字与小数点,其余字符一律吞掉。
|
||||
// 数值输入仅接受数字与小数点(或科学计数法符号),其余字符一律吞掉。
|
||||
if (m_panel.IsEditingWidth())
|
||||
{
|
||||
if (m_panel.OnEditChar(static_cast<wchar_t>(wParam)))
|
||||
@@ -1603,6 +1625,14 @@ namespace jdi
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (m_plot.IsEditingCursor())
|
||||
{
|
||||
if (m_plot.OnEditChar(static_cast<wchar_t>(wParam)))
|
||||
{
|
||||
InvalidateRect(hwnd, nullptr, FALSE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_KILLFOCUS:
|
||||
|
||||
@@ -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; /**< 当前网格绘制信息集合。 */
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -21,9 +21,10 @@ namespace jdi
|
||||
{
|
||||
NONE = 0, /**< 未触发任何动作。 */
|
||||
OPEN_FILE, /**< 打开 CSV 文件。 */
|
||||
FIT_ALL, /**< 视图自适应到可见信号范围。 */
|
||||
ZOOM_IN, /**< 放大时间轴。 */
|
||||
ZOOM_OUT, /**< 缩小时间轴。 */
|
||||
FIT_ALL, /**< 视图自适应到可见信号范围(同时退出缩放模式)。 */
|
||||
ZOOM_TIME, /**< 切换「在时间轴缩放」模式。 */
|
||||
ZOOM_VALUE, /**< 切换「在 Y 轴缩放」模式。 */
|
||||
CURSOR, /**< 切换时间光标。 */
|
||||
MERGE_LANES, /**< 合并为单一网格。 */
|
||||
SPLIT_LANES, /**< 按信号拆分为多网格。 */
|
||||
SELECT_ALL, /**< 勾选当前标签页全部信号。 */
|
||||
@@ -63,6 +64,13 @@ namespace jdi
|
||||
*/
|
||||
void SetStatusText(const std::wstring& text);
|
||||
|
||||
/**
|
||||
* @brief 设置可切换按钮的选中状态。
|
||||
* @param [IN] action 目标按钮动作。
|
||||
* @param [IN] checked 是否处于选中(生效)状态。
|
||||
*/
|
||||
void SetToggleState(ToolbarAction action, bool checked);
|
||||
|
||||
/**
|
||||
* @brief 计算工具栏在指定缩放下的期望高度。
|
||||
* @return 期望高度(像素)。
|
||||
@@ -132,8 +140,8 @@ namespace jdi
|
||||
gd::RectF rect; /**< 按钮矩形。 */
|
||||
float width = 0.0f; /**< 按钮宽度。 */
|
||||
bool primary = false; /**< 是否为主按钮(强调色填充)。 */
|
||||
bool needsDocument = true; /**< 是否依赖已加载数据。 */
|
||||
bool hover = false; /**< 悬停状态。 */
|
||||
bool needsDocument = true; /**< 是否依赖已加载数据。 */ bool toggle = false; /**< 是否为可切换按钮。 */
|
||||
bool checked = false; /**< 可切换按钮的选中状态。 */ bool hover = false; /**< 悬停状态。 */
|
||||
bool pressed = false; /**< 按下状态。 */
|
||||
};
|
||||
|
||||
|
||||
@@ -67,17 +67,19 @@ namespace jdi
|
||||
const wchar_t* label;
|
||||
bool primary;
|
||||
bool needsDocument;
|
||||
bool toggle;
|
||||
};
|
||||
|
||||
const Spec specs[] = {
|
||||
{ToolbarAction::OPEN_FILE, L"打开 CSV", true, false},
|
||||
{ToolbarAction::FIT_ALL, L"适应视图", false, true},
|
||||
{ToolbarAction::ZOOM_IN, L"放大", false, true},
|
||||
{ToolbarAction::ZOOM_OUT, L"缩小", false, true},
|
||||
{ToolbarAction::MERGE_LANES, L"合并网格", false, true},
|
||||
{ToolbarAction::SPLIT_LANES, L"每信号一格", false, true},
|
||||
{ToolbarAction::SELECT_ALL, L"全选信号", false, true},
|
||||
{ToolbarAction::CLEAR_ALL, L"清空勾选", false, true},
|
||||
{ToolbarAction::OPEN_FILE, L"打开 CSV", true, false, false},
|
||||
{ToolbarAction::FIT_ALL, L"适应视图", false, true, false},
|
||||
{ToolbarAction::ZOOM_TIME, L"时间轴缩放", false, true, true},
|
||||
{ToolbarAction::ZOOM_VALUE, L"Y 轴缩放", false, true, true},
|
||||
{ToolbarAction::CURSOR, L"光标", false, true, true},
|
||||
{ToolbarAction::MERGE_LANES, L"合并网格", false, true, false},
|
||||
{ToolbarAction::SPLIT_LANES, L"每信号一格", false, true, false},
|
||||
{ToolbarAction::SELECT_ALL, L"全选信号", false, true, false},
|
||||
{ToolbarAction::CLEAR_ALL, L"清空勾选", false, true, false},
|
||||
};
|
||||
|
||||
// 按钮宽度按实际文本像素宽 + 固定内边距计算,避免中文标签被裁切。
|
||||
@@ -93,11 +95,12 @@ namespace jdi
|
||||
button.label = spec.label;
|
||||
button.primary = spec.primary;
|
||||
button.needsDocument = spec.needsDocument;
|
||||
button.toggle = spec.toggle;
|
||||
button.width = horizontalPadding + UiMeasureTextWidth(m_fonts->ui, button.label);
|
||||
button.rect = gd::RectF(cursorX, top, button.width, height);
|
||||
cursorX += button.width + kButtonGap * m_scale;
|
||||
|
||||
if (spec.action == ToolbarAction::OPEN_FILE || spec.action == ToolbarAction::ZOOM_OUT ||
|
||||
if (spec.action == ToolbarAction::OPEN_FILE || spec.action == ToolbarAction::CURSOR ||
|
||||
spec.action == ToolbarAction::SPLIT_LANES)
|
||||
{
|
||||
cursorX += kGroupGap * m_scale;
|
||||
@@ -107,6 +110,18 @@ namespace jdi
|
||||
}
|
||||
}
|
||||
|
||||
void Toolbar::SetToggleState(ToolbarAction action, bool checked)
|
||||
{
|
||||
for (Button& button : m_buttons)
|
||||
{
|
||||
if (button.action == action && button.toggle)
|
||||
{
|
||||
button.checked = checked;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Toolbar::Paint(gd::Graphics& g)
|
||||
{
|
||||
if (m_theme == nullptr || m_fonts == nullptr)
|
||||
@@ -139,17 +154,33 @@ namespace jdi
|
||||
}
|
||||
else
|
||||
{
|
||||
background = m_theme->toolbarBg;
|
||||
// 可切换按钮在选中时用强调色浅底 + 强调描边,与普通按钮区分。
|
||||
background = (button.toggle && button.checked) ? m_theme->accentSoft : m_theme->toolbarBg;
|
||||
if (button.pressed)
|
||||
{
|
||||
background = m_theme->pressed;
|
||||
}
|
||||
else if (enabled && button.hover)
|
||||
{
|
||||
background = m_theme->hover;
|
||||
background = (button.toggle && button.checked) ? MixColor(m_theme->accentSoft, m_theme->accent,
|
||||
0.16)
|
||||
: m_theme->hover;
|
||||
}
|
||||
border = MixColor(m_theme->border, m_theme->toolbarBg, enabled ? 0.0 : 0.5);
|
||||
textColor = enabled ? m_theme->text : m_theme->textMuted;
|
||||
if (!enabled)
|
||||
{
|
||||
background = m_theme->toolbarBg;
|
||||
}
|
||||
|
||||
if (button.toggle && button.checked && enabled)
|
||||
{
|
||||
border = m_theme->accent;
|
||||
}
|
||||
else
|
||||
{
|
||||
border = MixColor(m_theme->border, m_theme->toolbarBg, enabled ? 0.0 : 0.5);
|
||||
}
|
||||
textColor = enabled ? ((button.toggle && button.checked) ? m_theme->textStrong : m_theme->text)
|
||||
: m_theme->textMuted;
|
||||
}
|
||||
|
||||
if (background != m_theme->toolbarBg)
|
||||
|
||||
+20
-10
@@ -21,6 +21,16 @@ namespace jdi
|
||||
LINE_SCATTER = 2 /**< 同时绘制插值直线与采样点散点。 */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 绘图区拖拽缩放模式(由工具栏按钮切换)。
|
||||
*/
|
||||
enum class PlotZoomMode : int
|
||||
{
|
||||
NONE = 0, /**< 常规模式:拖拽平移时间轴,光标为箭头。 */
|
||||
TIME = 1, /**< 时间轴缩放:在绘图区拖拽竖向选区,放大时间范围。 */
|
||||
VALUE = 2 /**< Y 轴缩放:在某个网格内拖拽横向选区,放大该网格幅值范围。 */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 将绘制方式转换为中文显示名称。
|
||||
* @param [IN] mode 绘制方式。
|
||||
@@ -446,18 +456,18 @@ namespace jdi
|
||||
bool& ViewInitialized() { return m_viewInitialized; }
|
||||
/// @brief 网格纵向滚动偏移量(可读写)。
|
||||
float& LaneScroll() { return m_laneScroll; }
|
||||
/// @brief 时间光标是否可见(可读写)。
|
||||
bool& CursorVisible() { return m_cursorVisible; }
|
||||
/// @brief 时间光标是否可见(只读)。
|
||||
bool CursorVisible() const { return m_cursorVisible; }
|
||||
/// @brief 时间光标是否已固定(可读写)。
|
||||
bool& CursorPinned() { return m_cursorPinned; }
|
||||
/// @brief 时间光标是否已固定(只读)。
|
||||
bool CursorPinned() const { return m_cursorPinned; }
|
||||
/// @brief 时间光标是否启用(可读写)。
|
||||
bool& CursorEnabled() { return m_cursorEnabled; }
|
||||
/// @brief 时间光标是否启用(只读)。
|
||||
bool CursorEnabled() const { return m_cursorEnabled; }
|
||||
/// @brief 时间光标位置(可读写)。
|
||||
double& CursorTime() { return m_cursorTime; }
|
||||
/// @brief 时间光标位置(只读)。
|
||||
double CursorTime() const { return m_cursorTime; }
|
||||
/// @brief 绘图区拖拽缩放模式(可读写)。
|
||||
PlotZoomMode& ZoomMode() { return m_zoomMode; }
|
||||
/// @brief 绘图区拖拽缩放模式(只读)。
|
||||
PlotZoomMode ZoomMode() const { return m_zoomMode; }
|
||||
|
||||
private:
|
||||
/**
|
||||
@@ -478,9 +488,9 @@ namespace jdi
|
||||
double m_viewMax = 1.0; /**< 视图右边界时间。 */
|
||||
bool m_viewInitialized = false; /**< 视图范围是否已初始化。 */
|
||||
float m_laneScroll = 0.0f; /**< 网格纵向滚动偏移量。 */
|
||||
bool m_cursorVisible = false; /**< 时间光标是否可见。 */
|
||||
bool m_cursorPinned = false; /**< 时间光标是否已固定。 */
|
||||
bool m_cursorEnabled = false; /**< 时间光标是否启用(由工具栏按钮切换)。 */
|
||||
double m_cursorTime = 0.0; /**< 时间光标位置。 */
|
||||
PlotZoomMode m_zoomMode = PlotZoomMode::NONE; /**< 绘图区拖拽缩放模式。 */
|
||||
};
|
||||
|
||||
} // namespace jdi
|
||||
|
||||
Reference in new issue
Block a user