修复浮动面板双击穿透、网格高亮触发来源、新增样式区「目标」行与联动平滑滚动
用户 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:
1 parent
73389f424c
commit
cb9d3e4b8f
7 files changed
+848
-32
No files matched your search
@@ -49,6 +49,15 @@ namespace jdi
|
||||
const UINT kHintTimerPeriodMs = 33; /**< 提示浮层淡出的重绘间隔(约 30fps)。 */
|
||||
const UINT_PTR kRamTimerId = 2; /**< 标题栏内存占用刷新的定时器 ID。 */
|
||||
const UINT kRamTimerPeriodMs = 1000; /**< 标题栏内存占用的刷新间隔(1 秒)。 */
|
||||
/**
|
||||
* @brief 信号列表「滚动到目标信号」动画的定时器 ID。
|
||||
*
|
||||
* 用户 2026-10-15 要求:联动时列表要**平滑滚动**(不是立刻翻到目标位置),
|
||||
* 且"整个动画过程不要太久"。动画本身由 `SignalPanel::AdvanceScrollAnimation()`
|
||||
* 按经过时间推进,这里只负责按帧驱动它(约 60fps)。
|
||||
*/
|
||||
const UINT_PTR kScrollTimerId = 3;
|
||||
const UINT kScrollTimerPeriodMs = 16;
|
||||
|
||||
/**
|
||||
* @brief 查询本进程当前占用的内存(**私有工作集**,与任务管理器"内存"列同口径)。
|
||||
@@ -889,6 +898,12 @@ namespace jdi
|
||||
g.SetPixelOffsetMode(gd::PixelOffsetModeHalf);
|
||||
g.SetTextRenderingHint(gd::TextRenderingHintClearTypeGridFit);
|
||||
|
||||
// 信号列表 / 样式区「目标」行的悬停 → 绘图区联动高亮(网格变色 + 该信号曲线高亮)。
|
||||
// 用户 2026-10-15 要求;鼠标在网格里移动**不再**触发网格变色高亮。
|
||||
// ⚠️ 每帧同步(而不是只在 WM_MOUSEMOVE 里同步)才能覆盖全部改变悬停的路径:
|
||||
// 鼠标离开窗口(WM_MOUSELEAVE)、数据/行结构变化、切换标签页等。
|
||||
m_plot.SetExternalHoverSignal(m_panel.HoveredSignal());
|
||||
|
||||
if (chromeDirty)
|
||||
{
|
||||
UiFillRect(g, gd::RectF(0.0f, 0.0f, static_cast<float>(width), static_cast<float>(height)),
|
||||
@@ -943,6 +958,21 @@ namespace jdi
|
||||
KillTimer(m_hwnd, kHintTimerId);
|
||||
m_hintTimerActive = false;
|
||||
}
|
||||
|
||||
// 信号列表的目标信号滚动动画:仅在动画进行中保持定时重绘。
|
||||
if (m_panel.ScrollAnimationActive())
|
||||
{
|
||||
if (!m_scrollTimerActive)
|
||||
{
|
||||
SetTimer(m_hwnd, kScrollTimerId, kScrollTimerPeriodMs, nullptr);
|
||||
m_scrollTimerActive = true;
|
||||
}
|
||||
}
|
||||
else if (m_scrollTimerActive)
|
||||
{
|
||||
KillTimer(m_hwnd, kScrollTimerId);
|
||||
m_scrollTimerActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Application::SetStatusMessage(const std::wstring& text, bool isError)
|
||||
@@ -1777,6 +1807,18 @@ namespace jdi
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Application::ApplyPendingPanelScroll()
|
||||
{
|
||||
SignalRef ref;
|
||||
if (!m_plot.TakePanelScrollRequest(ref) || !ref.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// 平滑滚动(带动画):用户要求"是滚动的而不是立刻翻到目标信号位置"。
|
||||
m_panel.ScrollToSignalAnimated(ref);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Application::ApplyPendingDerivedBuild()
|
||||
{
|
||||
// 两类「构建为新信号」请求(均值滤波 / 弱峰调整)机制完全一致,
|
||||
@@ -2640,6 +2682,8 @@ namespace jdi
|
||||
ApplyPendingDerivedBuild();
|
||||
InvalidateRect(hwnd, nullptr, FALSE);
|
||||
}
|
||||
// 联动:在绘图区选中了某条信号 ⇒ 让信号列表平滑滚动到它并完整显示。
|
||||
ApplyPendingPanelScroll();
|
||||
// 拖动期间 Windows 不再派发 WM_SETCURSOR,需在按下后立即刷新一次,
|
||||
// 使"按下但尚未拖动"时能立刻看到十字/方向箭头提示。
|
||||
UpdateDragCursor(x, y);
|
||||
@@ -2791,6 +2835,8 @@ namespace jdi
|
||||
{
|
||||
InvalidateRect(hwnd, nullptr, TRUE);
|
||||
}
|
||||
// 联动:双击图例改名也会选中该信号 ⇒ 同步把列表滚动到它。
|
||||
ApplyPendingPanelScroll();
|
||||
return 0;
|
||||
}
|
||||
case WM_RBUTTONDOWN:
|
||||
@@ -3034,6 +3080,13 @@ namespace jdi
|
||||
InvalidateRect(hwnd, nullptr, FALSE);
|
||||
return 0;
|
||||
}
|
||||
// 信号列表「滚动到目标信号」动画:推进一步并重绘(Render 会在结束后停表)。
|
||||
if (wParam == kScrollTimerId)
|
||||
{
|
||||
m_panel.AdvanceScrollAnimation();
|
||||
InvalidateRect(hwnd, nullptr, FALSE);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_DROPFILES:
|
||||
|
||||
Reference in new issue
Block a user