网格右键菜单「实验性功能」展开二级菜单后,鼠标从父项移向子菜单途中经过内边距窄带会被立即收起。改为子菜单展开期间只要指针仍在主菜单∪子菜单范围内且未落到其它可点击条目即保持展开并高亮父项。
10 KiB
10 KiB
Just DataInspector — Agent 记忆与项目须知
本文件是本仓库的权威上下文备忘录,供任何接手的 AI Agent 在动手前阅读。 每次完成任务后必须同步更新本文件(补充新需求、修复记录、约定变更等)。 保持内容精确、明细、简练;多台设备协作时,可将各自的记忆合并到本文件。
1. 项目目标
- 使用 C++17 + Win32 API(GDI/GDI+) 从零构建的桌面数据查看器,项目名 Just DataInspector。
- 定位:替代 MATLAB/Simulink 的
Simulink.sdi.view仿真数据检查器,用于更方便地查看带时间轴的 CSV 仿真数据。 - 目标数据特征(参考 MathWorks 导入 CSV 的说明):CSV 中存在一个或多个时间轴列,其余列表示各时间点上的信号值。
- 产物为 GUI 程序(非 CUI)。
2. 构建与工具链
- 工具链:MinGW-w64(
x86_64-win32-seh-rev0, MinGW-Builds 16.1.0),可直接用make。 - 构建入口:根目录
Makefile(C / C++ / windres 混合构建)。 - 常用命令:
make release→Build/Release/Target.exe(-Os,strip:-s)。make debug→Build/Debug/Target.exe(-O0 -g)。make→ 同时构建 Release 与 Debug。make clean→ 删除Build/。
- 关键编译设置:
-std=c++17;-finput-charset=UTF-8 -fexec-charset=GBK -fwide-exec-charset=UTF-16LE;-static;-mwindows(GUI);链接gdi32 gdiplus comdlg32 d3d11 dwmapi d3dcompiler。 - 新增组件/工具目录后,必须在
Makefile的CXX_INCLUDES_PATHS与CXX_SOURCES_PATHS中登记(源文件按目录通配自动收集)。 - 构建产物统一在
Build/,已由.gitignore忽略;同时忽略*.o/*.d/*.lst/*.map/*.exe与TestData/。
3. 目录结构与约定
Components/ 功能组件块(每块 Inc/ 放 .hpp,Src/ 放 .cpp)
Application/ 应用主框架:窗口、布局、消息分发、标签页、绘制合成
Main/ 程序入口(WinMain → Application::Run)
TabBar/ 顶部多标签栏
Toolbar/ 顶部工具栏(导入文件 / 适应视图 / 时间轴自适应 / Y 轴自适应 / 选区放大 / 光标 / 合并·拆分·清空网格)
SignalPanel/ 左侧信号面板:按文件分组的信号树、可见性勾选、样式编辑
PlotView/ 波形绘图区:网格、时间轴、光标、缩放、拖拽、实验性功能面板
Utilities/ 工具与辅助模块(Inc/ + Src/)
Application/ ... (见上)
CsvTable/ CSV 流式解析(列优先)
DataModel/ 数据层:DataSet / DataFile / SignalData / 显示配置
PaintUtil/ GDI+ 绘制与文本/控件辅助(含 UiPointInRect、UiMeasureTextWidth 等)
PopupMenu/ 主题化自绘弹出菜单(含二级子菜单、内联编辑)
ColorPickerPopup/ 浮动拾色器
TextEdit/ 内联文本编辑模型
Theme/ 主题色板(Solarised Light)与波形默认配色
Utf8Util/ 编码转换(UTF-8 ↔ UTF-16 等)
Resources/ 图标、清单(.manifest)、版本信息(.rc)、ResourceIds.h
TestData/ 本地测试 CSV(**不纳入版本控制**)
Documents/ 项目文档(含本文件)
- 命名硬性约束:源文件/头文件不得含空格,只能字母与下划线组合。
- 头文件后缀:C 用
.h,C++ 用.hpp;C 源.c,C++ 源.cpp。 - 防重复包含:每个头文件必须
#ifndef __XXX_XX_HPP__ / #define __XXX_XX_HPP__ / ... / #endif;宏名 = 文件名的双边双下划线 + 全大写。 - 宏定义与枚举常量必须全大写 + 下划线。
- 所有对外函数/方法声明用 Doxygen 风格注释:
@brief、@param [IN/OUT]、@return(中文说明)。 - 代码风格统一,强调解耦与清晰的分层。
4. 架构要点
- 数据层共享、视图配置分标签页:全局
DataSet(已加载文件、信号、时间轴)被所有标签页共享;每个标签页仅持有各自的TabConfig(信号可见性、配色、分组、网格等),互不影响。 Application是总编排者:创建窗口、Relayout布局、HandleMessage分发消息、合成绘制、把菜单/拾色器结果派发回组件。- 自定义绘制的浮动控件(
PopupMenu、ColorPickerPopup)不创建独立窗口,作为宿主绘制的最上层内容直接画进后备缓冲区,保证外观与主界面统一(不出现系统默认菜单样式)。 - 消息优先级:菜单打开 > 拾色器打开 > 常规组件交互(见
Application::HandleMessage)。 - DPI 感知:
ApplyDpi重建字体与布局;浮动控件通过SetScale缩放。
数据模型(Utilities/Inc/DataModel.hpp)
SignalData:单条信号元数据 + 采样值。DataFile:一个已加载文件及其自有时间轴(timeColumnName/time/minTime/maxTime/timeMonotonic)。DataSet:文件集合与全局时间范围查询(MinTime/MaxTime、最近采样点查找等)。DrawMode:LINE(插值折线) /SCATTER(散点) /LINE_SCATTER(两者)。LaneMode:PER_SIGNAL/MERGED/CUSTOM(网格划分策略)。PlotCursorMode:NONE/SINGLE/DOUBLE(时间光标三态)。- 各网格自身状态(Y 轴范围、自适应开关、实验性功能开关等)随信号继承。
CSV 解析(Utilities/Src/DataModel.cpp)
- 列优先存储(每条信号一整块连续内存):避免百万行级按行存储的堆分配放大,并支持整列移动进
SignalData。 - 流式分块读取,可携带真实字节进度(
CsvProgressInfo),支持取消。 - 时间列判定
LooksLikeTimeHeader:小写匹配t / time / timestamp / simulation time,或包含time/时间;未命中时回退第 0 列。 - 缺失值以
NaN表示;要求时间列有效数值 ≥ 2,否则报错。 - 分隔符自动探测(默认
,)。
5. 功能需求清单(来自需求交互,持续补充)
5.1 基础功能(首轮构建目标,已实现)
- 波形网格(多网格);时间轴。
- 光标(单/双光标,含差值读数)。
- 缩放与自适应(适应视图、Y 轴自适应、选区放大)。
- 导入并加载符合要求(带时间轴列)的 CSV,解析出信号名。
- 信号样式:修改颜色、粗细、散点/插值线段显示方式(
DrawMode)。 - 信号可绘制到同一网格或不同网格。
5.2 后续扩展(已在代码中实现,供接手参考)
- 多标签页(新建/关闭/重命名/拖拽排序)。
- 工具栏:合并网格 / 拆分网格 / 清空网格;时间轴自适应。
- 信号面板:按文件分组、勾选可见性、仅显示/全部显示/隐藏全部、移除文件。
- 绘图区拖拽:平移、选区放大(按首个明显位移方向决定横/纵轴)、网格滚动、光标拖拽、把信号曲线拖到其它网格、网格图例 GX 徽标上下重排。
- 图例:悬停高亮、内联重命名、信号删除按钮。
5.3 「实验性功能」二级菜单(网格右键菜单内,横线上方)
包含且仅包含以下可勾选项(每网格独立状态):
- 边沿计数
- 边沿计数(中心轴)
- 均值滤波
- 弱波增强
这些功能开关会联动绘图区的浮动参数面板;部分支持「构建为新信号」。
注意:新增/改名实验性功能时,需同步
PlotView.hpp的PLOT_CMD_LANE_*、Application.cpp的菜单构建与派发、PlotView/DataModel的状态位与面板绘制。
6. UI / 交互设计语言
- 参考 VS Code 与 WinUI 的设计语言;配色为 VS Code 的 Solarised Light 方案(
Theme::SolarizedLight(),强调色accent)。 - 全部界面与交互用 Win32 API 自绘,不用系统默认控件样式。
- 文本对齐、菜单内边距、圆角、阴影等在各浮动控件内保持统一风格。
7. 交付与 Git 规范
- 工作区由本地 git 管辖。
- 仅在功能完善(可验证)后才提交;提交信息用简洁中文描述修改内容。
- 提交时仅对本工作区生效(
git config --local),不得影响全局设置:user.name= 当前模型全称。user.email=<>(留空尖括号)。
TestData/、Build/等不提交(见.gitignore)。
8. Agent 工作流要求
- 接手前先探索工作区/提交历史/代码建立认知(本文件应作为第一参考)。
- 完成需求后编译验证(
make release至少成功),必要时用TestData/数据功能验证。 - 验证与探索时尽量不占用用户的鼠标/屏幕/键盘(后台构建为主)。
- 本地仓库提交完成后,不要在未经指示的情况下继续探索或优化,等待用户下一步指令。
- 每次完成任务后更新本文件(需求、修复、约定变更)。
9. 已知问题与修复记录
9.1 网格右键菜单「实验性功能」二级菜单瞬间关闭(已修复)
- 症状:在网格右键菜单里把鼠标从「实验性功能」移向右侧二级菜单的途中,只要指针短暂离开父项交互区域,二级菜单立即关闭,用户来不及完成选择。旧设备 CPU 较慢、鼠标移动事件稀疏,指针可能"跳"进二级菜单而未暴露该问题;CPU 更快、事件更密集时必然复现。
- 根因(
Utilities/Src/PopupMenu.cpp的PopupMenu::OnMouseMove):- 子菜单展开后,只要指针不在子菜单矩形内且未命中任何可点击条目,就无条件
CloseSubmenu()。 - 主菜单条目矩形的右缘为
menu.right - kPadH,而子菜单左缘为menu.right - kSubOverlap,两者之间存在约 3px 的内边距"死区"(位于主菜单矩形内但落在任何条目之外)。鼠标事件密集时指针必然落入该窄带,从而触发误关闭。
- 子菜单展开后,只要指针不在子菜单矩形内且未命中任何可点击条目,就无条件
- 修复:子菜单展开期间,若指针仍位于「主菜单 ∪ 子菜单」矩形范围内、且尚未落到其它可点击条目上(例如掠过内边距窄带、分隔线、禁用项),则保持子菜单展开并维持父项高亮;只有明确移动到其它条目或完全离开上述范围时才收起。
10. 变更日志(本文件维护记录)
| 日期 | 设备/模型 | 摘要 |
|---|---|---|
| 2026-10-11 | DeepSeek-V4.1-Flash | 新建本文件;记录项目要点与规范;修复「实验性功能」二级菜单瞬间关闭问题。 |