回退 5b52eef 并修复「屏幕不刷新」根因:拖动滚动条卡顿 / 悬停高亮卡住 / 信号拖拽无效果
根因(代码级确认)
- Render() 把界面画进后备缓冲区后用 GetDC + BitBlt 回送窗口,5b52eef 把回送写成
`if (fullFrame) { BitBlt(整窗) } else if (panelOnly) { BitBlt(本帧重绘区域并集) }`,没有兜底 else。
- 「鼠标只在绘图区内移动」的帧(WM_MOUSEMOVE 的 plotOnlyMove 路径只 InvalidatePlotRect)恰好
fullFrame == false && panelOnly == false ⇒ 后备缓冲区被正确重绘,窗口 DC 一个像素都不回送。
- 这一个根因同时造成用户报告的三个症状:悬停光晕卡住(移开不消失)、拖动滚动条不跟手/非常卡顿、
拖拽信号换网格看不到任何结果。
修法
- git reset --hard HEAD~1 撤销 5b52eef(保留 backup-bad-commit 标签供 A/B)。
- 只挑回该提交里与更新区域无关的功能改动(用户明确要求过、本身正确):
* SignalPanel::ExpandGroupForSignal():联动滚动前立刻展开折起的分组(不播展开动画);
* FitAll() / FitSinglePointCluster() 不再复位 Tab().LaneScroll()(适应视图不影响滚动条);
* 右下角「回到最上方」浮动按钮 + 网格滚动到可视区的平滑动画。
- Application.cpp 只保留 3 处最小改动:Render 里把 panelAnimating 计入整帧脏、WM_TIMER 推进
AdvanceBackToTopAnimation() 且保持整窗失效、定时器存活判定带 BackToTopAnimationActive()。
Render 的绘制与 BitBlt 路径逐字节等同于 82b5ebe。
- 彻底删除 m_paintPlot / m_panelDirty / panelOnly / InvalidatePanelRect / 并集回送。
验证(全部离屏执行,不移动真实光标、不抢焦点、不截屏)
- .preflight/diag(新增离屏探针,19/19):窗口移到屏幕外 + WS_EX_TOOLWINDOW + SW_SHOWNOACTIVATE,
保证有真实更新区域与 WM_PAINT 但不影响用户。断言滚动条拖动(滑块与绘图区都在后备缓冲区被重绘)、
悬停光晕出现与消退(像素完全回到基线)、在曲线上按下进入 MOVE_SIGNAL 且拖到另一网格后归属正确。
- .preflight/check 137/137。
- .preflight/diag/verify_blit.ps1(新增源码级护栏,10/10):断言 Render() 的回送块必须有无条件 else、
不得出现 else if、m_paintPlot/m_panelDirty/panelOnly 必须不存在、失效只允许整窗或仅绘图区。
A/B:换回 backup-bad-commit ⇒ 4 项如实失败;换回修复版 ⇒ 10/10。
- make release 零错误零警告。
文档:Documents/agent_mem.md 新增 §9.41、§11.2 第 108–116 条(含 §9.40 与旧 108–114 条的作废标注)
与变更日志一行。
教训(写进记忆):① 离屏探针无法验证「窗口回送 / 合成」(屏幕外 DWM 不合成、窗口 DC 恒为空白)⇒
回送类约束必须有源码级护栏;② 离屏窗口 + TrackMouseEvent 会立刻收到 WM_MOUSELEAVE,探针须丢弃该消息;
③ 网格回收会让下标左移,断言要用锚信号。
This commit is contained in:
1 parent
82b5ebe4f9
commit
f845616938
10 files changed
+460
-8
No files matched your search
@@ -399,8 +399,17 @@ namespace jdi
|
||||
// 工具栏/标签栏/面板各自带悬停态,内容只在鼠标位于其中或发生交互时改变。
|
||||
// 绘图区内的拖拽(如双光标左右移动)期间这些区域完全静止,跳过其绘制,
|
||||
// 每帧省下约 6-8ms;弹出菜单 / 拾色器 / 加载遮罩会覆盖到装饰区域,必须整帧重绘。
|
||||
//
|
||||
// ⚠️⚠️ **面板动画(滚动到目标信号 / 分组折起、展开)必须由动画状态本身驱动重绘**:
|
||||
// 这两个动画每帧都在改变面板像素,而它们只由 `WM_TIMER` 驱动 —— 而 `WM_TIMER`
|
||||
// **不会**置 `m_chromeDirty`(它刻意在排除列表里),于是旧实现完全依赖
|
||||
// `WM_PAINT` 里“更新区域不在绘图区内 ⇒ 置脏”这个启发式:一旦某帧的更新区域恰好
|
||||
// 不含面板(例如与鼠标消息或 `kMessagePlotDerivedReady` 的失效区域交错),
|
||||
// 那一帧就**完全不重画面板** ⇒ 用户报告“过渡动画有时直接没了”。
|
||||
// ✓ 把动画状态直接纳入重绘条件(**只增加重绘条件,不改变任何绘制/回送路径**)。
|
||||
const bool panelAnimating = m_panel.ScrollAnimationActive() || m_panel.GroupAnimationActive();
|
||||
const bool overlaysVisible = m_menu.IsOpen() || m_colorPicker.IsOpen();
|
||||
const bool chromeDirty = m_chromeDirty || m_loading || overlaysVisible;
|
||||
const bool chromeDirty = m_chromeDirty || m_loading || overlaysVisible || panelAnimating;
|
||||
m_chromeDirty = false;
|
||||
// 提示浮层淡出由定时器驱动,其绘制范围局限在绘图区,但为稳妥起见整帧回送。
|
||||
const bool fullFrame = chromeDirty || m_plot.HintNeedsAnimation();
|
||||
@@ -472,10 +481,10 @@ namespace jdi
|
||||
m_hintTimerActive = false;
|
||||
}
|
||||
|
||||
// 信号列表与绘图区的动画(目标信号滚动 / 分组折起展开 / 网格滚动到可视区):
|
||||
// 仅在动画进行中保持定时重绘。
|
||||
// 信号列表与绘图区的动画(目标信号滚动 / 分组折起展开 / 网格滚动到可视区 /
|
||||
//「回到最上方」按钮的淡入淡出):仅在动画进行中保持定时重绘。
|
||||
if (m_panel.ScrollAnimationActive() || m_panel.GroupAnimationActive() ||
|
||||
m_plot.LaneScrollAnimationActive())
|
||||
m_plot.LaneScrollAnimationActive() || m_plot.BackToTopAnimationActive())
|
||||
{
|
||||
if (!m_scrollTimerActive)
|
||||
{
|
||||
@@ -1403,13 +1412,18 @@ namespace jdi
|
||||
InvalidateRect(hwnd, nullptr, FALSE);
|
||||
return 0;
|
||||
}
|
||||
// 信号列表与绘图区动画(「滚动到目标信号」/「分组折起、展开」/「网格滚动到可视区」):
|
||||
// 推进一步并重绘(Render 会在动画结束后停表)。
|
||||
// 信号列表与绘图区动画(「滚动到目标信号」/「分组折起、展开」/「网格滚动到可视区」/
|
||||
//「回到最上方」按钮的淡入淡出):推进一步并整窗重绘(Render 会在动画结束后停表)。
|
||||
// ⚠️ 这里**保持整窗失效**(不要改成“只失效动画所在的区域”):面板与绘图区的
|
||||
// 回送范围由 `Render` 按 `fullFrame` 决定,任何“精确失效”都可能让屏幕上留下
|
||||
// 未刷新的区域(历史上就因此让滚动条/时间轴不跟手);而整窗失效下动画帧
|
||||
// 也只有 ~6-8ms(124 网格),完全在 16ms 预算内。
|
||||
if (wParam == kScrollTimerId)
|
||||
{
|
||||
m_panel.AdvanceScrollAnimation();
|
||||
m_panel.AdvanceGroupAnimations();
|
||||
m_plot.AdvanceLaneScrollAnimation();
|
||||
m_plot.AdvanceBackToTopAnimation();
|
||||
InvalidateRect(hwnd, nullptr, FALSE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user