一、两个「边沿计数」按「画出来的曲线」判定(原先按原始采样值 → 应用过「调整」的信号恒为 0)
- 用户复述的权威定义:设定一个横向(Y 轴)光标,信号绘制出来的曲线从下往上穿过该幅值 = 上升沿,
从上往下穿过 = 下降沿。
- 症状:VERY_BIG_DATA 的「编码器B相」设系数 50 + 时间轴偏移 -20s,把光标拖到画面中间,
很规整的方波却统计出 0/0。
- 根因:EdgeCountsFor / EdgeCenterCountsFor 只比较 signal.values 的原始采样值,而横向光标读的是
屏幕上的值(已乘系数、已加偏移)→ 原始值域与阈值完全不相交。
- 修法:判定值一律取 ApplyDrawTransform(style, values[SourceIndexFor(style, count, index)]);
* 中心轴两侧必须套用同一套变换(drawn - drawnCenter);
* 水平镜像时曲线数组按源下标存储、而统计区间是显示下标 → 请求曲线窗口前必须把
[needFirst, needEnd) 映射为 [count-needEnd, count-needFirst);
* 缓存必须带样式指纹:EdgeCountEntry / EdgeCenterCountEntry 新增 styleKey
(EdgeStyleKey():gain/offsetY 按 1e6 定点 + absolute/mirrorVertical/mirrorHorizontal 位),
否则改完「调整」仍会命中变换前的旧计数。
二、时间区间之外的采样不得计入(区间边界点)
- 症状:A 在阈值以下、B 超过阈值;双光标下一个光标正好在 B 点,旧实现把 B 算成一个上升沿。
- 根因:EdgeIndexRange 在左端多取了一个采样点(if (begin > 0) --begin;)。
- 修法:删掉该外扩,收紧为「只有两个端点都落在 [begin, end) 内的采样对才计入」
(begin = lower_bound(rangeMin)、end = upper_bound(rangeMax),调用方从 begin+1 枚举到 end-1)。
- 结果:把区间起点从 times[crossing-1] 移到 times[crossing] 恰好少一个上升沿。
三、派生信号把 offsetTime 也烘进采样值(推翻 §9.19 的「唯一例外」)
- 用户要求:新信号默认不启用任何「调整」时就必须与网格里的预览逐点重合。
- 修法(Application::BuildDerivedSignal,在「值变换 + 水平镜像反序」之后追加一步):
新信号第 i 个采样 = 已变换的曲线在 time[i] - offsetTime 处的取值(取最近的采样点);
单调时间轴用只前进的游标(整段 O(n));平移后越界的采样置 NaN(与预览一致);
新信号样式全部保持默认(gain=1 / absolute=false / 两个镜像=false / offsetY=0 / offsetTime=0)。
- 退化路径:时间轴非单调或时间列缺失时无法按下标推进取样,退回把 offsetTime 带在样式上
(两条路径的显示效果相同)。
四、图例托底统一为 GX 编号徽标同款 + 新增更小字体 uiTiny
- 托底高度 = kLaneBadgeHeight、上下边界与 LaneBadgeRect() 完全一致、圆角 = 高度的一半(胶囊形)、
配色与描边与徽标相同(选中态 accentSoft/accent)、水平内边距 = 胶囊半径。
- 文本改用新增字体 UiFonts::uiTiny(10 逻辑 px):原 uiSmall(12px) 几乎顶满 17px 高的胶囊。
- LegendItem 拆出 plateWidth(托底宽 = 内容宽 + 2×内边距),条目步进 = plateWidth + kLegendItemGap;
kLegendPlateRadius 已删除(半径一律由徽标高度推出)。
五、「系数/偏移」「镜像」二级面板改为向上展开
- PopupMenu::Open(..., bool expandUp = false):true 时 (x, y) 是菜单的左下角、菜单向上展开;
BuildLayout 增加同参数并记录 m_buildExpandUp(RebuildLayout 必须复用,否则数值回写触发的重排
会让面板跳回向下展开)。
- SignalPanel::OpenGainMenu / OpenMirrorMenu:锚点 = (button.X, button.Y - kAdjustMenuGap),
即「面板左下角对准按钮左上角、再略微上去一点」(kAdjustMenuGap = 4 逻辑像素)。
六、验证(全部离屏,不占用用户的鼠标 / 键盘 / 屏幕)
- .preflight/uitest 新增 [AR]:
* 边沿计数 plain / gain=50 / offsetY / mirrorVertical / mirrorHorizontal 逐个与「独立参考实现」
逐位一致;offsetY 用例给出 A/B:按原始采样值算会得到 7/6 个边沿(正是用户报告的缺陷);
* 区间边界:起点从 crossing-1 移到 crossing 恰好少一个上升沿;
* 中心轴:absolute + offsetY 的样式下与参考实现逐位一致(32234 个边沿量级);
* 派生信号:无偏移时全部 128 万采样逐点等于 transform(滤波);带 +10 个平均采样周期的偏移时,
抽样点等于 transform(滤波) 在 time[i] - offsetTime(最近采样)处的取值;样式全默认;
* 图例托底:height == 徽标高度、Y == 徽标 Y、radius == height/2、色块与文本都在托底之内;
* 调整面板:菜单左边界 == 按钮左边界、菜单底 == 按钮顶 - 4*scale、且不覆盖按钮(两个按钮各测一次);
* GX 拖拽让位:±0.6H 各让位 1 格(目标 0 / 3),±0.4H 与恰好 ±0.5H 都不让位(严格「超过」)。
- 同步修正 harness 里两处「旧规则」断言(否则必然 FAIL 并误导后来者):
[AP] 「派生信号继承调整变换」→ 「样式默认 + 采样值按 time[i]-offsetTime 烘焙」;
[AN] 「图例带无托底(网格线可透出)」→ 「装饰矩形逐块、无一块跨越整条带、未被托底覆盖的列
仍能看到底层内容」。
- 合计 722/722 通过;make release 零警告(本机 GCC 15.2.0 UCRT;Target.exe 被占用时链接会报
Permission denied,已用临时 -o 名字验证链接正确性)。
规则同步:Documents/agent_mem.md 新增 §9.21,§9.19 的「offsetTime 仍需继承」标注为已作废,
新增 §11.2 第 21–25 条硬约束,并补变更日志一行。
442 lines
16 KiB
C++
442 lines
16 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 "TextEdit.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, /**< 颜色色块行。 */
|
||
EDIT = 4, /**< 带内联输入框的项:在菜单内部直接编辑数值(菜单不关闭)。 */
|
||
SUBMENU = 5, /**< 带二级子菜单的项:鼠标悬停时向右展开。 */
|
||
HEADER_ACTION = 6 /**< 分组标题 + **右侧图标按钮**(如「还原」),图标可点击。 */
|
||
};
|
||
|
||
/**
|
||
* @brief 单个弹出菜单项。
|
||
*/
|
||
struct MenuItem
|
||
{
|
||
MenuItemKind kind = MenuItemKind::NORMAL; /**< 项目类型。 */
|
||
std::wstring label; /**< 显示文本;EDIT 项为输入框左侧的说明文字。 */
|
||
int id = 0; /**< 命令标识。 */
|
||
bool enabled = true; /**< 是否可用。 */
|
||
bool checked = false; /**< 是否处于选中(单选)状态。 */
|
||
int swatchCount = 0; /**< 色块数量(仅色块行有效)。 */
|
||
std::uint32_t swatches[16] = {}; /**< 色块颜色。 */
|
||
int checkedSwatch = -1; /**< 被选中的色块下标。 */
|
||
std::wstring value; /**< EDIT 项的当前数值文本(非编辑态显示)。 */
|
||
/**
|
||
* EDIT 项**进入编辑时的初始文本**;为空则使用 `value`。
|
||
*
|
||
* 用于「非编辑态的显示文本」与「编辑态的数值」不一致的场合,例如时间轴偏移:
|
||
* 平时显示 `Δt:5s 60ms`,进入编辑后应显示以秒为单位的数值 `5.06`。
|
||
*/
|
||
std::wstring editValue;
|
||
/**
|
||
* EDIT 项**编辑态**在输入框最右侧提示的单位文本(如 `s`);为空则不提示。
|
||
*
|
||
* 非编辑态不显示(那时显示的是 `value` 的完整文案)。
|
||
*/
|
||
std::wstring editSuffix;
|
||
EditFilter filter = EditFilter::ANY; /**< EDIT 项的输入过滤规则。 */
|
||
std::vector<MenuItem> submenu; /**< 二级子菜单项(kind == SUBMENU 时有效)。 */
|
||
/**
|
||
* 普通项被点击后是否**保持菜单打开**。
|
||
*
|
||
* 用于「带复选框的切换项」(如镜像的水平/垂直):可以连续勾选多项,
|
||
* 勾选状态通过 `UpdateItemLabel` 回写。默认 false(点击即关):与既有行为一致。
|
||
*/
|
||
bool keepOpen = false;
|
||
};
|
||
|
||
/**
|
||
* @brief 菜单选择结果。
|
||
*/
|
||
struct MenuResult
|
||
{
|
||
bool valid = false; /**< 是否产生了有效选择。 */
|
||
MenuScope scope = MenuScope::NONE; /**< 来源组件。 */
|
||
int id = 0; /**< 被选中项的命令标识。 */
|
||
int swatchIndex = -1; /**< 被选中的色块下标(非色块行为 -1)。 */
|
||
gd::RectF itemRect; /**< 被点击条目的矩形(客户区坐标),供就近弹出输入框。 */
|
||
bool editCommit = false; /**< 该结果来自菜单内联输入框的提交(菜单保持打开)。 */
|
||
std::wstring editText; /**< 提交的输入文本(editCommit 为 true 时有效)。 */
|
||
};
|
||
|
||
/**
|
||
* @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 菜单项集合。
|
||
* @param [IN] expandUp 为 true 时菜单**向上展开**,且 `(x, y)` 是菜单的**左下角**
|
||
* (用于“二级面板贴在按钮左上方”这类锚定);为 false(默认)时向右下展开,
|
||
* `(x, y)` 是菜单的左上角。
|
||
*/
|
||
void Open(MenuScope scope, const gd::RectF& bounds, int x, int y,
|
||
const std::vector<MenuItem>& items, bool expandUp = false);
|
||
|
||
/**
|
||
* @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;
|
||
|
||
/**
|
||
* @brief 设置剪贴板所有者窗口句柄(供内联输入框复制/粘贴)。
|
||
* @param [IN] host 宿主窗口句柄,允许为空。
|
||
*/
|
||
void SetHostWindow(HWND host) { m_host = host; }
|
||
|
||
/**
|
||
* @brief 判断是否有菜单项正处于内联编辑状态。
|
||
* @return 处于编辑状态返回 true。
|
||
*/
|
||
bool IsEditing() const { return m_editIndex >= 0; }
|
||
|
||
/**
|
||
* @brief 判断坐标是否落在当前正在编辑的输入框内。
|
||
* @param [IN] x 客户区横坐标。
|
||
* @param [IN] y 客户区纵坐标。
|
||
* @return 落在输入框内返回 true。
|
||
*/
|
||
bool IsOnEditField(int x, int y) const;
|
||
|
||
/**
|
||
* @brief 编辑态下按下左键:定位插入符并开始拖拽框选。
|
||
* @param [IN] x 客户区横坐标。
|
||
* @param [IN] y 客户区纵坐标。
|
||
* @return 命中输入框返回 true。
|
||
*/
|
||
bool OnEditMouseDown(int x, int y);
|
||
|
||
/**
|
||
* @brief 编辑态下拖拽鼠标:扩展选区。
|
||
* @param [IN] x 客户区横坐标。
|
||
* @return 选区发生变化返回 true。
|
||
*/
|
||
bool OnEditMouseMove(int x);
|
||
|
||
/**
|
||
* @brief 编辑态下松开左键:结束拖拽框选。
|
||
* @return 之前处于拖拽中返回 true。
|
||
*/
|
||
bool OnEditMouseUp();
|
||
|
||
/**
|
||
* @brief 编辑态下处理字符输入。
|
||
* @param [IN] character 字符。
|
||
* @param [OUT] result 提交时返回 editCommit 结果。
|
||
* @return 需要重绘返回 true。
|
||
*/
|
||
bool OnChar(wchar_t character, MenuResult& result);
|
||
|
||
/**
|
||
* @brief 更新指定命令项显示的值文本(菜单保持打开,立即重新排布)。
|
||
* @param [IN] id 命令标识。
|
||
* @param [IN] value 新的值文本(非编辑态显示)。
|
||
* @param [IN] editValue 新的**编辑初值**(为空时保持原值)。
|
||
* ⚠️ 非编辑态文本与编辑态文本不一致的行(如时间轴偏移:`Δt:1h 1min 2s` ↔ `3662`)
|
||
* 必须一并更新,否则下次进入编辑会拿到陈旧的初值。
|
||
* @return 找到对应项并更新返回 true。
|
||
*/
|
||
bool UpdateItemValue(int id, const std::wstring& value,
|
||
const std::wstring& editValue = std::wstring());
|
||
|
||
/**
|
||
* @brief 更新指定普通项的文字与可用/选中状态(用于实时反映状态)。
|
||
* @param [IN] id 命令标识。
|
||
* @param [IN] label 新的文字。
|
||
* @param [IN] checked 新的选中状态。
|
||
* @param [IN] enabled 新的可用状态。
|
||
* @return 找到对应项并更新返回 true。
|
||
*/
|
||
bool UpdateItemLabel(int id, const std::wstring& label, bool checked = false, bool enabled = true);
|
||
|
||
private:
|
||
/**
|
||
* @brief 计算单个菜单项的纵向高度。
|
||
* @param [IN] item 菜单项。
|
||
* @return 高度(像素)。
|
||
*/
|
||
float ItemHeight(const MenuItem& item) const;
|
||
|
||
/**
|
||
* @brief 依据当前项目集合计算整体矩形与各项矩形。
|
||
* @param [IN] bounds 宿主客户区矩形。
|
||
* @param [IN] x 锚点横坐标。
|
||
* @param [IN] y 锚点纵坐标。
|
||
* @param [IN] expandUp 为 true 时以 `(x, y)` 为**左下角**向上展开。
|
||
*/
|
||
void BuildLayout(const gd::RectF& bounds, int x, int y, bool expandUp);
|
||
|
||
/**
|
||
* @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);
|
||
|
||
/**
|
||
* @brief 计算指定下标项的内联输入框矩形(EDIT 项)。
|
||
* @param [IN] index 项目下标。
|
||
* @return 输入框矩形,非法下标返回空矩形。
|
||
*/
|
||
gd::RectF EditFieldRect(int index) const;
|
||
|
||
/**
|
||
* @brief 计算 EDIT 项**编辑态单位后缀**所占的宽度(0 表示无后缀)。
|
||
*
|
||
* 排版、命中测试与绘制共用:输入框正文区会向右让出这么多宽度,
|
||
* 后缀文本绘制在它右侧(靠右对齐)。
|
||
*
|
||
* @param [IN] item 菜单项。
|
||
* @return 后缀宽度(像素)。
|
||
*/
|
||
float EditSuffixWidth(const MenuItem& item) const;
|
||
|
||
/**
|
||
* @brief 计算 `HEADER_ACTION` 项右侧**图标按钮**(如「还原」)的矩形。
|
||
*
|
||
* 图标紧贴该行右端(右边距与其它行一致、行内垂直居中),大小按缩放比例推导。
|
||
* 鼠标悬停时该图标会高亮,点击即下发该项的命令。
|
||
*
|
||
* @param [IN] index 项目下标。
|
||
* @return 图标矩形;非 `HEADER_ACTION` 项或非法下标返回空矩形。
|
||
*/
|
||
gd::RectF HeaderIconRect(int index) const;
|
||
|
||
/**
|
||
* @brief 计算 EDIT 项输入框的“自然宽度”(排版与绘制共用同一公式)。
|
||
* @param [IN] item 菜单项。
|
||
* @return 宽度(像素)。
|
||
*/
|
||
float NaturalEditFieldWidth(const MenuItem& item) const;
|
||
|
||
/**
|
||
* @brief 进入指定项的内联编辑状态。
|
||
* @param [IN] index 项目下标。
|
||
* @return 成功进入返回 true。
|
||
*/
|
||
bool BeginItemEdit(int index);
|
||
|
||
/**
|
||
* @brief 结束内联编辑。
|
||
* @param [IN] commit 是否提交(false 为放弃修改)。
|
||
* @param [OUT] result 提交时输出 editCommit 结果。
|
||
* @return 之前处于编辑状态返回 true。
|
||
*/
|
||
bool EndItemEdit(bool commit, MenuResult& result);
|
||
|
||
/**
|
||
* @brief 依据当前项集合重新排布(用于值文本变化后)。
|
||
*/
|
||
void RebuildLayout();
|
||
|
||
/**
|
||
* @brief 退出内联编辑状态(不产生任何结果)。
|
||
*/
|
||
void CloseEditState();
|
||
|
||
/**
|
||
* @brief 展开指定项的二级子菜单(项不存在或非 SUBMENU 时等价于关闭)。
|
||
* @param [IN] parentIndex 父项下标。
|
||
*/
|
||
void BuildSubLayout(int parentIndex);
|
||
|
||
/**
|
||
* @brief 关闭二级子菜单并清空其状态。
|
||
*/
|
||
void CloseSubmenu();
|
||
|
||
/**
|
||
* @brief 查询坐标命中的子菜单项下标。
|
||
* @param [IN] x 客户区横坐标。
|
||
* @param [IN] y 客户区纵坐标。
|
||
* @return 子菜单项下标,未命中返回 -1。
|
||
*/
|
||
int SubItemAtPoint(int x, int y) const;
|
||
|
||
/**
|
||
* @brief 绘制二级子菜单(含投影与全部条目)。
|
||
* @param [IN,OUT] g 绘图上下文。
|
||
*/
|
||
void PaintSubmenu(gd::Graphics& g);
|
||
|
||
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; /**< 各项矩形。 */
|
||
/**
|
||
* 同一次排布中,**带说明文字的 EDIT 项**共用的输入框宽度(0 表示没有这类项)。
|
||
*
|
||
* 宽度取这些项里最宽的自然宽度,并在排布时按“不能挤掉说明文字 / 不超过内容区”夹好。
|
||
* 同一面板内的输入框因此一样宽、右边界对齐(用户要求「Y轴」与「时间轴」等宽对齐,
|
||
* 而不是各自按自己的文本长度自适应)。
|
||
*/
|
||
float m_editFieldWidth = 0.0f;
|
||
std::vector<std::vector<gd::RectF>> m_swatchRects; /**< 各项内的色块矩形。 */
|
||
|
||
int m_hoverIndex = -1; /**< 当前悬停项。 */
|
||
int m_hoverIconIndex = -1; /**< 当前悬停的 `HEADER_ACTION` 图标所在项下标。 */
|
||
int m_hoverSwatch = -1; /**< 当前悬停色块。 */
|
||
int m_pressedIndex = -1; /**< 按下时的项目下标。 */
|
||
int m_pressedSwatch = -1; /**< 按下时的色块下标。 */
|
||
|
||
HWND m_host = nullptr; /**< 剪贴板所有者窗口句柄。 */
|
||
TextEdit m_edit; /**< 内联输入框编辑模型。 */
|
||
int m_editIndex = -1; /**< 正在编辑的项目下标,-1 表示未编辑。 */
|
||
|
||
gd::RectF m_buildBounds; /**< 最近一次排布使用的宿主矩形。 */
|
||
int m_buildX = 0; /**< 最近一次排布使用的锚点横坐标。 */
|
||
int m_buildY = 0; /**< 最近一次排布使用的锚点纵坐标。 */
|
||
bool m_buildExpandUp = false; /**< 最近一次排布是否为“向上展开(锚点为左下角)”。 */
|
||
|
||
bool m_subOpen = false; /**< 二级子菜单是否展开。 */
|
||
int m_subParentIndex = -1; /**< 展开子菜单的父项下标。 */
|
||
gd::RectF m_subRect; /**< 子菜单整体矩形。 */
|
||
std::vector<MenuItem> m_subItems; /**< 子菜单项集合。 */
|
||
std::vector<gd::RectF> m_subItemRects; /**< 子菜单各项矩形。 */
|
||
int m_subHoverIndex = -1; /**< 子菜单当前悬停项。 */
|
||
int m_subPressedIndex = -1; /**< 子菜单按下时的项下标。 */
|
||
};
|
||
|
||
} // namespace jdi
|
||
|
||
#endif // __POPUP_MENU_HPP__
|