feat(ui): 工具栏"选区"改名、菜单对勾右置与边距修正、选区自适应勾选、悬停/选区拖拽性能

1) 工具栏文案:"时间轴缩放"/"Y 轴缩放" -> "时间轴选区"/"Y 轴选区"
- Toolbar.cpp 按钮标签、Toolbar.hpp 枚举注释、Application 状态提示语同步更新

2) 网格右键菜单:对勾从左侧移到右侧,并修正菜单内各元素边距
- 文本列统一为 kPadH + kItemPad(5 + 8),三类条目(HEADER/NORMAL/EDIT)共用;
  原先左侧预留 22px 标记列,导致"网格 N"、"Y 轴最大值"、"选区 Y 轴自适应"等
  所有文本的左边距被整体推右(用户报告的"文本左边距过大")
- 对勾改画在条目右侧的 kMarkerColumn(18) 列内,文本宽度相应预留,不会与文字重叠
- EDIT 行输入框改为右对齐到条目内容区右边界,宽度由 NaturalEditFieldWidth() 统一计算,
  空间不足时为左侧说明文字保留自然宽度(避免被菜单宽度上限挤成省略号)
- 分隔线、色块行同样改用 kItemPad 内缩;kRightPad 已删除

3) "选区 Y 轴自适应"增加与"Y 轴自适应"一致的勾选标记
- Lane 新增 viewAutoRange / viewAutoMin / viewAutoMax,记录应用该操作时的视图时间区间
- 新增 PlotView::LaneRangeIsAutoToView():只有未被任何手动幅值改动且时间选区未变化时才为 true
- 所有手动改动路径(ApplyValueSelection/ScaleLaneRange/ShiftLaneRange/ApplyLaneValueText/
  EndEdit/ResetLaneRange/MoveSignalToLane)都会清除该状态;
  时间选区变化后 viewAutoMin/Max 与当前视图不符,勾选自动消失
- RefreshPlotMenuValues() 同步刷新该项状态

4) 修复大文件(VERY_BIG_DATA 1.77GB / 5 信号)下悬停与选区缩放的严重卡顿
- 根因:任何悬停态变化(左侧信号列表、网格内命中曲线提示)或选区拖拽都会触发整幅重绘,
  其中波形绘制约 80ms/帧,因此交互帧率只有 ~10fps
- 静态场景缓存从"仅拖拽信号/光标"推广为"只要场景键一致就复用",缓存区域由整个客户区
  收敛为仅绘图区:工具栏/标签栏/面板各自带悬停态必须每帧绘制(约 10.5ms),
  而真正昂贵的波形改为直接贴图(约 1ms)
- 场景键新增 contentHash(FNV-1a 折叠网格几何/幅值、每条信号的引用/可见性/
  颜色/透明度/线宽/样式/显示名与所属文件名):改样式、重命名、增删信号都会自动失效
- 随鼠标变化的内容全部移出缓存:新增 PaintHoverHighlight() 在贴图之后绘制
  悬停网格底色(半透明叠加)与悬停信号光晕(PaintSignalHalo());
  PaintLane 内只绘制拖拽中的信号光晕(key 中记录 haloFile/haloSignal)
- EnsureSceneCache 改为要求位图尺寸与当前区域严格相等(原 >= 复用会在窗口缩小后
  把旧像素画到绘图区之外)
- Application::Render 移除 reuseScene 分支,统一每帧绘制界面框架

实测(VERY_BIG_DATA 5 信号,Release):
- 整幅重绘 ~80ms -> 命中缓存贴图 ~1ms
- 悬停左侧信号列表:~95ms -> ~14ms/帧
- 悬停网格内信号(含光晕高亮):~95ms -> ~28~32ms/帧
- 时间轴 / Y 轴选区拖拽:~95~105ms -> ~19~22ms/帧
- 非拖拽静止帧 ~118ms 维持不变;空闲 CPU 0.00%

构建: make clean; make all 无警告无错误
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-10-07 11:26:43 +08:00
1 parent a3e638fa58
commit dd48cdd2e1
9 files changed
+414 -194

No files matched your search

+75 -23
View File
@@ -253,17 +253,7 @@ namespace jdi
bool CancelDrag();
/**
* @brief 判断本帧是否可以整体复用拖拽静态场景缓存。
*
* 拖动信号曲线 / 时间光标时视图与数据都不变,缓存内容仍然有效。
* 宿王可据此跳过工具栏/标签栏/面板的重复绘制(它们也在缓存内)。
*
* @return 可复用返回 true。
*/
bool CanReuseSceneCache() const;
/**
* @brief 退出全部缩放模式(时间轴缩放 / Y 轴缩放),使工具栏缩放按钮回到未选中态。
* @brief 退出全部缩放模式(时间轴选区 / Y 轴选区),使工具栏缩放按钮回到未选中态。
* @return 之前处于某个缩放模式返回 true。
*/
bool ExitZoomModes();
@@ -320,6 +310,17 @@ namespace jdi
*/
bool LaneRangeIsAuto(int laneIndex) const;
/**
* @brief 判断某网格当前范围是否正是「选区 Y 轴自适应」的结果(用于菜单勾选标记)。
*
* 只有在该操作之后未发生任何手动幅值改动、且时间选区没有变化时才返回 true,
* 因为一旦时间选区改变,之前的选区自适应结果就不再对应当前视野。
*
* @param [IN] laneIndex 网格下标。
* @return 当前生效返回 true。
*/
bool LaneRangeIsAutoToView(int laneIndex) const;
/**
* @brief 让某网格的 Y 轴恢复自适应。
* @param [IN] laneIndex 网格下标。
@@ -844,23 +845,27 @@ namespace jdi
mutable std::deque<PolylineCacheEntry> m_polylineCache; /**< 折线顶点缓存(按几何键复用)。 */
/**
* @brief 静态场景缓存的几何键。
* @brief 静态场景缓存的几何/内容键。
*
* 只要这组参数完全一致,"背景 + 全部网格 + 时间轴"的渲染结果就逐像素相同,
* 可以直接复用离屏位图,无需重算百万级采样的包络与绘制。
* 这使鼠标悬停、拖拽选区等"只改一小块叠加层"的交互不再重绘波形:
* 大文件下整幅重绘约 80ms,命中缓存时只需 1~2ms 贴图。
*/
struct SceneCacheKey
{
int dragMode = -1; /**< 拖拽模式(拖拽中才允许复用)。 */
double viewMin = 0.0; /**< 时间轴下界。 */
double viewMax = 0.0; /**< 时间轴上界。 */
float laneScroll = 0.0f; /**< 网格滚动偏移。 */
int laneCount = 0; /**< 网格数量。 */
float areaX = 0.0f; /**< 绘图区左上角。 */
double viewMin = 0.0; /**< 时间轴下界。 */
double viewMax = 0.0; /**< 时间轴上界。 */
float laneScroll = 0.0f; /**< 网格滚动偏移。 */
int laneCount = 0; /**< 网格数量。 */
float areaX = 0.0f; /**< 绘图区左上角。 */
float areaY = 0.0f;
float areaW = 0.0f; /**< 绘图区尺寸。 */
float areaW = 0.0f; /**< 绘图区尺寸。 */
float areaH = 0.0f;
float scale = 1.0f; /**< DPI 缩放。 */
float scale = 1.0f; /**< DPI 缩放。 */
int haloFile = -1; /**< 已绘入缓存的拖拽光晕信号(无则 -1)。 */
int haloSignal = -1;
std::uint64_t contentHash = 0; /**< 信号集合/显示样式/网格幅值等内容指纹。 */
};
/**
@@ -869,6 +874,49 @@ namespace jdi
*/
SceneCacheKey MakeSceneCacheKey() const;
/**
* @brief 判断当前缓存键是否与已捕获的场景一致。
* @return 可直接贴图返回 true。
*/
bool SceneCacheMatches() const;
/**
* @brief 计算场景内容指纹(信号集合、显示样式、网格幅值、图例文案)。
*
* 视图几何以外的任何内容变化(改颜色/线宽/透明度/样式、重命名、增删信号)
* 都会改变该指纹,从而让静态场景缓存自动失效,无需在各处手工调用失效接口。
*
* @return 指纹值。
*/
std::uint64_t SceneContentHash() const;
/**
* @brief 当前应当绘入静态场景缓存的信号光晕(仅拖拽中的信号)。
*
* 拖拽信号的光晕在整个拖拽过程中不变,可以进入缓存;
* 悬停光晕随鼠标移动,必须画在缓存之外。
*
* @return 信号引用,无则返回无效引用。
*/
SignalRef SceneHaloRef() const;
/**
* @brief 绘制静态场景缓存之外的"悬停高亮"(悬停网格底色 + 悬停信号光晕)。
* @param [IN,OUT] g 绘图上下文。
*/
void PaintHoverHighlight(gd::Graphics& g);
/**
* @brief 绘制某条信号的悬停/拖拽光晕。
* @param [IN,OUT] g 绘图上下文。
* @param [IN] ref 信号引用。
* @param [IN] resolved 已解析的信号。
* @param [IN] points 折线顶点。
* @param [IN] breaks 每段折线的起始下标。
*/
void PaintSignalHalo(gd::Graphics& g, const SignalRef& ref, const ResolvedSignal& resolved,
const std::vector<gd::PointF>& points, const std::vector<std::size_t>& breaks);
/**
* @brief 确保离屏缓存位图存在且尺寸匹配。
* @param [IN] width 位图宽度(像素)。
@@ -878,7 +926,11 @@ namespace jdi
bool EnsureSceneCache(int width, int height);
/**
* @brief 把当前画布内容(背景 + 界面外框 + 全部网格 + 时间轴)拷贝进离屏缓存。
* @brief 把当前画布内容(绘图区底色 + 全部网格 + 时间轴)拷贝进离屏缓存。
*
* 缓存区域严格限定为绘图区(m_area)。悬停与拖拽产生的内容一律不进入缓存,
* 因此在任何交互过程中都可以安全复用,只贴图而不再重绘波形。
*
* @param [IN,OUT] g 当前绘图上下文(必须未设置裁剪区)。
* @return 拷贝成功返回 true。
*/
@@ -906,8 +958,8 @@ namespace jdi
HGDIOBJ m_sceneOldBitmap = nullptr; /**< 离屏 DC 原选位图。 */
int m_sceneWidth = 0; /**< 离屏位图宽度。 */
int m_sceneHeight = 0; /**< 离屏位图高度。 */
int m_sceneX = 0; /**< 离屏缓存对应的客户区横坐标。 */
int m_sceneY = 0; /**< 离屏缓存对应的客户区纵坐标。 */
int m_sceneX = 0; /**< 缓存对应的客户区横坐标(绘图区左边界)。 */
int m_sceneY = 0; /**< 缓存对应的客户区纵坐标(绘图区上边界)。 */
bool m_sceneValid = false; /**< 缓存内容是否有效。 */
SceneCacheKey m_sceneKey; /**< 缓存对应的场景参数。 */