修复浮动面板双击穿透、网格高亮触发来源、新增样式区「目标」行与联动平滑滚动

用户 2026-10-15 第六轮报告的四个问题与要求(详见 Documents/agent_mem.md §9.34):

1. 在网格内的实验性功能浮动面板上双击任意位置会穿透到下层网格,导致网格触发自适应。
   根因:PlotView::OnDoubleClick 在“差值标签 / 光标标签 / 光标线 / 图例”都不命中时
        无条件调用 FitAll()。
   修法:在该函数最前面(高于一切下层命中)用 TopPanelAtPoint() 判定,命中浮动面板
        则直接消费(面板自身双击无额外语义)。确立“上层元素的交互不得影响下层”的规则。

2. 未开启任何工具栏功能时鼠标移到网格上会让整个网格变色高亮(旧需求已不适用)。
   修法:取消“鼠标在网格里移动就高亮”的行为;改为在信号选择区域悬停某条信号时,
        若该信号已被勾选绘制到某个网格,则该网格变色高亮,并且该信号曲线本身也高亮;
        鼠标移开即恢复。悬停未勾选的信号不产生任何高亮。
   实现:PlotView 新增 m_externalHoverSignal + SetExternalHoverSignal()(与 m_hoverSignal
        分开保存,因为绘图区 OnMouseLeave 会清掉后者,而列表悬停时鼠标不在绘图区内)、
        EffectiveHoverLane();SignalPanel 新增 HoveredSignal(),并在 InvalidateRows()
        里一并作废;Application::Render() 每帧同步(覆盖 WM_MOUSELEAVE / 数据变化 /
        切换标签页等全部路径)。GX 徽标的悬停高亮仍按鼠标位置独立判定。

3. 在信号样式自定义区域最上方(预设颜色色块之上)新增「目标」行。
   内容:当前正在被编辑的目标信号名称;若它已绘制到网格,则在名称后方显示所在 GX 标签。
   交互:与信号选择区域的信号行一致 —— 名称/GX 标签悬停有高亮提示,且悬停时同样触发
        该信号所在网格高亮 + 曲线高亮;单击不进入编辑(只触发联动滚动),双击才进入改名
        (复用 StyleEditField::SIGNAL_NAME,m_editRow < 0 表示目标是选中信号)。
   实现:SignalPanel 新增 m_targetRowRect / PaintTargetRow / UpdateTargetRowGeometry
        (名称宽度与“是否有 GX 标签”都可能在不重排时变化,因此按需重算并带缓存键)、
        StyleHit::TARGET_NAME / TARGET_BADGE;DesiredStyleHeight() 与 LayoutStyleControls()
        相应多算一行。

4. 联动机制更新:从文件导入大量信号(如 MF4 的 124 条)时,被选中的信号可能被滚动到
   可见范围之外而找不到。
   修法:新增 SignalPanel::ScrollToSignalAnimated()(+ AdvanceScrollAnimation),
        触发联动时把信号列表平滑滚动到目标信号完整可见:
        * 目标在视界上方 ⇒ 向上滚动,且顶部让出一个组行高度,避免被悬浮组名遮挡;
        * 目标在视界下方 ⇒ 向下滚动到完整露出;
        * 已经完整可见则不动;
        * ease-out cubic(越远越快、靠近变慢),时长 = clamp(距离×0.35ms, 130, 300),
          总时长有上限;用户滚轮 / 拖滚动条立即取消动画。
   触发点:绘图区内把某条信号选为当前目标(点击图例、点击信号曲线、双击图例改名)
        ⇒ PlotView::SelectSignal() 发布请求(TakePanelScrollRequest()),宿主在鼠标消息
        处理后调用 ApplyPendingPanelScroll();样式自定义区的任何交互(预设色块、自定义
        颜色入口、样式按钮、线宽/透明度滑块与数值框、「系数/偏移」「镜像」二级菜单、
        绝对值)以及「目标」行的单击也都会触发同一次滚动。
   宿主:Application 新增 16ms 定时器(kScrollTimerId)驱动动画,并由 Render 自管开关
        (与提示浮层淡出同一套做法)。

验证(全部离屏,不占用鼠标 / 键盘 / 屏幕):
- .preflight/check(SW_HIDE + 只注入合成消息 + 进程/线程 BELOW_NORMAL)**73/73 通过**,
  连跑 3 次稳定:[A] 浮动面板双击不移视界,且对照组(空白处双击)必须触发自适应;
  [B] 鼠标在网格里移动不高亮;[C] 列表悬停 → 网格高亮 + 移开恢复 + 未勾选信号不高亮;
  [D] 目标行的布局/占位/GX 标签位置与显隐/悬停联动/单击不编辑 + 双击改名(初始文本正确);
  [E] 长列表 122 行:动画分多帧、单调、时长上限 300ms、目标完整可见且不被悬浮组名遮挡、
      已可见时不动、滚轮取消动画;[F] 样式区交互与绘图区联动都会滚动到目标并完整显示;
  [G] 布局回归(目标行在列表下方、样式区仍完整、默认无悬停与高亮)。
- A/B 判别:回退六处实现后 **15 项如实失败**(含"面板双击仍触发自适应"、"鼠标移到网格即高亮"、
  "目标行不联动"、"联动瞬移而非动画")。
- make release:全部源文件重新编译 **零警告**;链接用临时 `make release TARGET_FILE_NAME=Target_check` 通过。

同步记忆:Documents/agent_mem.md 新增 §9.34、§11.2 第 72–75 条硬约束、变更日志新行。
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-10-15 20:32:16 +08:00
1 parent 73389f424c
commit cb9d3e4b8f
7 files changed
+848 -32

No files matched your search

+55
View File
@@ -880,6 +880,39 @@ namespace jdi
*/
bool TakePendingRename(SignalRef& ref, std::wstring& name);
/**
* @brief 设置「外部悬停信号」(信号列表 / 样式区「目标」行悬停到某条信号时调用)。
*
* 用户 2026-10-15 要求:
* * 鼠标**在网格里移动不再触发**网格变色高亮(旧行为已取消);
* * 改为**在信号列表里悬停某条信号**时,若该信号已勾选绘制到某个网格,
* 则该网格变色高亮,并且该信号曲线本身也高亮;鼠标移开即恢复。
*
* ⚠️ 与 `m_hoverSignal`(鼠标在绘图区内直接命中的信号)**分开保存**:绘图区的
* `OnMouseLeave` 会清掉后者,而列表悬停时鼠标恰恰不在绘图区内。
*
* @param [IN] ref 悬停的信号;无效引用表示取消外部悬停。
*/
void SetExternalHoverSignal(const SignalRef& ref);
/**
* @brief 当前外部悬停的信号(供验证 / 宿主查询)。
* @return 悬停的信号引用,无则无效。
*/
const SignalRef& ExternalHoverSignal() const { return m_externalHoverSignal; }
/**
* @brief 取出并清除「请把信号列表滚动到该信号」的联动请求。
*
* 用户 2026-10-15 要求:在绘图区触发联动(点击图例 / 点击信号曲线等把某条信号选为
* 当前目标)时,信号选择区域要**平滑滚动**到该信号并完整显示它 ——
* 信号很多时(如 MF4 的 124 条)否则找不见选中的那一条。
*
* @param [OUT] ref 需要滚动到的信号。
* @return 存在待处理的请求返回 true。
*/
bool TakePanelScrollRequest(SignalRef& ref);
/**
* @brief 处理编辑态下的字符输入。
* @param [IN] character 输入字符。
@@ -1974,6 +2007,20 @@ namespace jdi
gd::RectF m_rubberRect; /**< 拖拽选区矩形。 */
SignalRef m_hoverSignal; /**< 鼠标悬停命中的信号(曲线高亮)。 */
/**
* @brief 「外部悬停信号」:信号列表 / 样式区「目标」行悬停到某条信号时的联动高亮源。
*
* 与 `m_hoverSignal` 分开(绘图区的 `OnMouseLeave` 会清掉后者,而列表悬停时
* 鼠标并不在绘图区内);二者的高亮效果相同:网格变色 + 曲线光晕。
*/
SignalRef m_externalHoverSignal;
/**
* @brief 待宿主处理的「把信号列表滚动到该信号」请求(见 `TakePanelScrollRequest`)。
*
* 绘图区自身无法访问信号面板,因此把请求记在这里,由宿主在鼠标消息处理后取走。
*/
SignalRef m_pendingPanelScroll;
bool m_pendingPanelScrollFlag = false;
SignalRef m_hoverLegend; /**< 鼠标悬停命中的图例条目(半高亮提示,不触发曲线拖拽提示)。 */
SignalRef m_dragSignal; /**< 正在被拖拽的信号。 */
int m_dragTargetLane = -1; /**< 拖拽信号时的目标网格下标。 */
@@ -2035,6 +2082,14 @@ namespace jdi
EdgePanelKind m_pendingPanelCloseKind = EdgePanelKind::COUNT; /**< 待关闭面板的功能类型。 */
int m_hoverLane = -1; /**< 当前悬停网格下标。 */
/**
* @brief 取本次绘制应该高亮的网格下标(-1 表示无)。
*
* 来源两处:① 鼠标悬停在实验性功能浮动面板上(该面板所属网格);
* ② 外部悬停信号(信号列表 / 样式区)且它已绘制到某个网格时,高亮该网格。
* ⚠️ 鼠标在网格里移动**不再**触发高亮(用户 2026-10-15 要求取消旧行为)。
*/
int EffectiveHoverLane() const;
/**
* @brief 上一帧鼠标是否悬停在某个网格的“GX”编号徽标上(是则记录网格下标,否则 -1)。
*