Files
Just-DataInspector/Utilities/Inc/PopupMenu.hpp
T
DeepSeek V4.1 Flash 542aa51e40 feat: 重构绘图区网格布局与信号拖拽,精简界面信息并新增 Y 轴精确控制
网格与标尺:
- 消除网格之间无意义的间隔,改用较重的横向分界线明确区分相邻网格
- 纵轴标尺从绘图区左侧外侧移入网格内侧,并改为带半透明衬底的覆盖层绘制,
  使其在波形之上依然清晰可读;绘图区左侧空间因此完全还给波形
- 继续保证底部时间轴预留高度,并把时间列名贴左显示、避开刻度标签

常规模式拖拽:
- 常规模式下水平拖动平移时间轴,纵向拖动偏移当前网格的幅值显示位置
- 绘图区右键菜单新增「恢复 Y 轴自适应」「设置 Y 轴最大值…」「设置 Y 轴最小值…」
  「全部自适应」,上下限通过内联输入框直接键入(与线宽输入一致的交互)

信号拖拽:
- 鼠标贴近信号曲线时给出光晕高亮与提示气泡(与时间光标的提示方式一致)
- 仅在出现提示时按左键才会进入拖拽;拖拽过程中在目标网格内绘制曲线虚影、
  高亮目标网格边框并提示「移动到网格 N」,松开左键确认归属变更
- 命中检测基于折线到光标的精确点到线段距离,散点模式按采样点距离判定

界面精简:
- 移除底部状态栏(左下角统计与右下角提示均不再占用空间),错误改由对话框提示
- 标题栏只显示应用名称
- 移除工具栏右上角的视图时间范围文本;按钮除「导入文件」外整体靠右排列

其他:
- 新增 PlotView::PaintEditField 统一绘制时间光标与网格 Y 轴上下限的输入框
- 移除已无引用的 UiDrawDashedLine 与绘图区标尺占位宽度
2026-09-29 20:49:21 +08:00

224 lines
6.5 KiB
C++

#ifndef __POPUP_MENU_HPP__
#define __POPUP_MENU_HPP__
#include <windows.h>
#include <objidl.h>
#include <gdiplus.h>
#include <cstdint>
#include <string>
#include <vector>
#include "PaintUtil.hpp"
#include "Theme.hpp"
namespace jdi
{
/**
* @brief 弹出菜单的打开来源,用于把选择结果派发回正确的组件。
*/
enum class MenuScope : int
{
NONE = 0, /**< 无来源。 */
SIGNAL_PANEL = 1, /**< 信号面板。 */
TAB_BAR = 2, /**< 标签栏。 */
PLOT_VIEW = 3 /**< 波形绘图区。 */
};
/**
* @brief 弹出菜单项的显示类型。
*/
enum class MenuItemKind : int
{
NORMAL = 0, /**< 普通可点击项。 */
SEPARATOR = 1, /**< 分隔线。 */
HEADER = 2, /**< 分组标题(不可点击)。 */
SWATCHES = 3 /**< 颜色色块行。 */
};
/**
* @brief 单个弹出菜单项。
*/
struct MenuItem
{
MenuItemKind kind = MenuItemKind::NORMAL; /**< 项目类型。 */
std::wstring label; /**< 显示文本。 */
int id = 0; /**< 命令标识。 */
bool enabled = true; /**< 是否可用。 */
bool checked = false; /**< 是否处于选中(单选)状态。 */
int swatchCount = 0; /**< 色块数量(仅色块行有效)。 */
std::uint32_t swatches[16] = {}; /**< 色块颜色。 */
int checkedSwatch = -1; /**< 被选中的色块下标。 */
};
/**
* @brief 菜单选择结果。
*/
struct MenuResult
{
bool valid = false; /**< 是否产生了有效选择。 */
MenuScope scope = MenuScope::NONE; /**< 来源组件。 */
int id = 0; /**< 被选中项的命令标识。 */
int swatchIndex = -1; /**< 被选中的色块下标(非色块行为 -1)。 */
};
/**
* @brief 与界面主题一致的自绘弹出菜单。
*
* 菜单不创建独立窗口,而是作为宿主窗口绘制流程的最上层内容直接绘制到后备缓冲区,
* 因此外观与主界面完全统一,不会出现系统默认菜单样式。
*/
class PopupMenu
{
public:
PopupMenu();
/**
* @brief 绑定字体与主题资源。
* @param [IN] fonts 字体集合,需在菜单生命周期内保持有效。
* @param [IN] theme 主题配色,需在菜单生命周期内保持有效。
*/
void Setup(const UiFonts* fonts, const Theme* theme);
/**
* @brief 设置 DPI 缩放比例。
* @param [IN] scale 缩放比例(1.0 对应 96 DPI)。
*/
void SetScale(float scale);
/**
* @brief 在指定位置打开菜单。
* @param [IN] scope 来源组件标识。
* @param [IN] bounds 宿主客户区矩形,用于把菜单限制在窗口内。
* @param [IN] x 锚点横坐标(客户区坐标)。
* @param [IN] y 锚点纵坐标(客户区坐标)。
* @param [IN] items 菜单项集合。
*/
void Open(MenuScope scope, const gd::RectF& bounds, int x, int y, const std::vector<MenuItem>& items);
/**
* @brief 关闭菜单。
*/
void Close();
/**
* @brief 判断菜单是否处于打开状态。
* @return 打开返回 true。
*/
bool IsOpen() const { return m_open; }
/**
* @brief 获取当前菜单的来源组件标识。
* @return 来源标识。
*/
MenuScope Scope() const { return m_scope; }
/**
* @brief 绘制菜单(应在宿主绘制的最后一步调用)。
* @param [IN,OUT] g 绘图上下文。
*/
void Paint(gd::Graphics& g);
/**
* @brief 处理鼠标移动。
* @param [IN] x 客户区横坐标。
* @param [IN] y 客户区纵坐标。
* @return 需要重绘返回 true。
*/
bool OnMouseMove(int x, int y);
/**
* @brief 处理鼠标左键按下。
* @param [IN] x 客户区横坐标。
* @param [IN] y 客户区纵坐标。
* @param [OUT] result 在菜单外按下时返回被取消的选择结果。
* @return 事件已被菜单消费返回 true。
*/
bool OnLButtonDown(int x, int y, MenuResult& result);
/**
* @brief 处理鼠标左键抬起。
* @param [IN] x 客户区横坐标。
* @param [IN] y 客户区纵坐标。
* @param [OUT] result 选择结果。
* @return 事件已被菜单消费返回 true。
*/
bool OnLButtonUp(int x, int y, MenuResult& result);
/**
* @brief 处理键盘按键。
* @param [IN] key 虚拟键码。
* @param [OUT] result 选择结果。
* @return 事件已被菜单消费返回 true。
*/
bool OnKeyDown(WPARAM key, MenuResult& result);
/**
* @brief 判断坐标是否落在菜单内部。
* @param [IN] x 客户区横坐标。
* @param [IN] y 客户区纵坐标。
* @return 落在菜单内返回 true。
*/
bool ContainsPoint(int x, int y) const;
private:
/**
* @brief 计算单个菜单项的纵向高度。
* @param [IN] item 菜单项。
* @return 高度(像素)。
*/
float ItemHeight(const MenuItem& item) const;
/**
* @brief 依据当前项目集合计算整体矩形与各项矩形。
* @param [IN] bounds 宿主客户区矩形。
* @param [IN] x 锚点横坐标。
* @param [IN] y 锚点纵坐标。
*/
void BuildLayout(const gd::RectF& bounds, int x, int y);
/**
* @brief 查询坐标命中的菜单项下标。
* @param [IN] x 客户区横坐标。
* @param [IN] y 客户区纵坐标。
* @param [OUT] swatchIndex 命中的色块下标(非色块行为 -1)。
* @return 项目下标,未命中返回 -1。
*/
int ItemAtPoint(int x, int y, int& swatchIndex) const;
/**
* @brief 将悬停项移动到下一个可用项。
* @param [IN] step 步进(+1 向下,-1 向上)。
*/
void MoveHover(int step);
/**
* @brief 确认当前悬停项并生成结果。
* @param [OUT] result 选择结果。
* @return 生成了有效选择返回 true。
*/
bool CommitHover(MenuResult& result);
const UiFonts* m_fonts = nullptr; /**< 字体资源。 */
const Theme* m_theme = nullptr; /**< 主题资源。 */
float m_scale = 1.0f; /**< DPI 缩放比例。 */
bool m_open = false; /**< 是否处于打开状态。 */
MenuScope m_scope = MenuScope::NONE; /**< 来源组件标识。 */
std::vector<MenuItem> m_items; /**< 菜单项集合。 */
gd::RectF m_rect; /**< 菜单整体矩形。 */
std::vector<gd::RectF> m_itemRects; /**< 各项矩形。 */
std::vector<std::vector<gd::RectF>> m_swatchRects; /**< 各项内的色块矩形。 */
int m_hoverIndex = -1; /**< 当前悬停项。 */
int m_hoverSwatch = -1; /**< 当前悬停色块。 */
int m_pressedIndex = -1; /**< 按下时的项目下标。 */
int m_pressedSwatch = -1; /**< 按下时的色块下标。 */
};
} // namespace jdi
#endif // __POPUP_MENU_HPP__