滚动过渡动画根治 + 快速连点跟手 + 分组名单击展开 + 光标吸附同源 + 按钮淡出对齐 + 滚动位置复位
一、滚动过渡动画卡顿(用户报告:甚至不如手动拖滚动条)—— 三处独立根因
1) WM_TIMER(kScrollTimerId) 无条件整窗失效 ⇒ WM_PAINT 看到更新区域不在绘图区内 ⇒ 把
m_chromeDirty 置真 ⇒ 每一帧都重绘标签栏/工具栏/面板。
修法:用「推进之前」的面板动画状态决定失效范围 —— 面板动画(滚动到目标信号 / 分组折起展开)
确实要改面板像素 ⇒ 整窗失效;只有绘图区动画(网格滚动到可视区 / 回到最上方)⇒ 只失效绘图区。
实测:绘图区动画帧 13.6ms → 3.5ms;手动拖滚动条参照 4.2ms。
⚠️ 未改变 §9.41 的回送结构(仍是 if (fullFrame) {整窗} else {绘图区})。
2) 图层预热不足:上百个离屏网格长期不建图层,长距离滚动动画扫进去时每一帧都有十几个网格
没有图层像素,只能靠「网格底衬兜底」重画(约 0.5ms/网格 ⇒ +8ms/帧)。
修法:LaneVisuallyRelevant(laneIndex, prefetchScreens) 增加 ±1 屏预取带(AnyVisibleLanePending 用),
并新增 PrefetchStepAllowed()(kPrefetchMinIntervalMs=32 ⇒ 约 31 次/秒)限速地继续请求重绘,
让整批图层在静置期间逐步建好。
实测:lane-only + VERY_BIG_DATA 帧 13.9ms → 5.3ms;signal-click + VERY_BIG_DATA 18.3ms → 10.3ms。
3) 网格滚动量原本参与「宽基底可用性」判据(上一轮已修:SceneContentHash(..., includeLaneScroll=false))。
二、光标吸附改用「放大自动点线」的同一判定(用户 2026-10-17 修订)
AutoPointLineNeeded 改名 MinorCellSparse(中性名,同一判定两处用途):任一小格内有效数据点 < 5 即为稀疏。
- 「放大自动切点线」与「光标吸附」共用它;判定完全不看绘制方式 ⇒ 即使用户手动改过显示样式也照样吸附。
- 鼠标下方网格内既有符合又有不符合的信号时,只有符合的那条的数据点参与吸附;没有符合的则自由移动。
- 删除旧的整屏判据 SignalSnapEnabled 与 kSnapVisibleCellsPerPoint(死代码)。
三、「回到最上方」按钮淡出与「浮动提示」一致
kBackToTopFadeOutMs = kHintFadeMs(500ms)+ 线性 alpha,到达顶部立即开始淡出(不停留)。
淡入保留 170ms ease-out。实测 50% 时刻 alpha = 0.500(线性),旧 ease-out 会是 0.875。
四、快速连点跟手(根因:CS_DBLCLKS 合成 WM_LBUTTONDBLCLK 导致按下丢失)
组件各自实现 DoubleClickIsPlainPress(x, y)(该落点有没有双击语义),宿主 WM_LBUTTONDBLCLK 据此
选择走 OnLButtonDown(当作按下)还是 OnDoubleClick:
- PlotView:浮动面板(含复选框/滑块/复位/✕/标题栏)、「回到最上方」按钮、绘图区滚动条、GX 徽标 ⇒ 按下;
时间光标线/读数标签/图例条目名/绘图区空白 ⇒ 保留双击。
- SignalPanel:样式区数值框、「目标」行名称与 GX 标签、行名称区域 ⇒ 保留双击;其余全部 ⇒ 按下。
- 弹出菜单 / 拾色器打开时,WM_LBUTTONDBLCLK 也按各自 OnLButtonDown 走。
五、分组行:名称恢复单击展开收起 + 只有快速双击进入改名
- OnLButtonDown 分组分支不再排除 RowPart::BODY:三角 /「组X」/ 名称本体统一折叠展开。
- OnDoubleClick 名称区域:只有快速双击(kGroupRenameDoubleClickMs=300,比系统 500ms 紧)才进入改名;
超阈值的「慢双击」只当又一次单击;快速双击前先撤销按下那一次的折叠切换,保证进编辑时状态与点击前一致。
六、信号重现时网格滚动回到最上方
“有过需滚动条的网格 → 没有任何信号显示 → 又出现需滚动条数量的网格”这一类情形必须从最上方开始看。
修法:Application::SynchronizeView 的「首次出现可见信号」分支里,在 FitAll() 之后 tab.LaneScroll() = 0。
验证(全部离屏,不占用鼠标/键盘/屏幕)
- grabdanim(.preflight/perf/gridanim_main.cpp)93/93:真实消息循环帧距 + 逐帧渲染耗时(与手动拖滚动条
对比)、光标吸附新判定、按钮淡出线性、快速连点路由、分组名单击/双击/慢双击、滚动位置复位。
- 既有回归:verify 49/49、panelanim 158/158(按新分组名规则更新 8 条断言)、datacheck 69/69、
edgecheck 46/46、errcheck 19/19。
- make release / make debug 零错误零警告(用 TARGET_FILE_NAME=TargetCheck/TargetCheckD 避免与正在运行的实例冲突)。
文档:Documents/agent_mem.md 新增 §9.43、§11.2 第 125–133 条与变更日志一行。
This commit is contained in:
1 parent
34375481de
commit
436dcbfd52
12 files changed
+618
-182
No files matched your search
@@ -856,13 +856,16 @@ namespace jdi
|
||||
// 真并行(3.98x),但**图层光栅化(GDI+)本身会被串行化**,并行重建 5 个网格 ≈ 逐个重建
|
||||
//(1.03x)→ 一次性补齐只会把一帧从 ~10ms 抬到 ~31ms(更容易掉帧)。
|
||||
// 仍按预算逐个补:不用一次把全部过期网格塞进同一帧(并行重建≈逐个重建)。
|
||||
// ⚠️ **“是否再来一帧”只看还有没有【可视】网格待补**:上百个离屏网格若每帧都
|
||||
// 请求重绘,会形成持续一秒以上的高频重绘,把动画定时器饿死(见 `AnyVisibleLanePending`)。
|
||||
// ⚠️ **“是否再来一帧”的判据**(见 `AnyVisibleLanePending` / `PrefetchStepAllowed`):
|
||||
// * 还有可视 / 预取带内的网格待补 ⇒ 立刻继续;
|
||||
// * 只剩离屏网格 ⇒ 按 `kPrefetchMinIntervalMs` 限速继续(不会饿死动画定时器)。
|
||||
if (viewStopped)
|
||||
{
|
||||
RenderPendingLaneRebuilds(1);
|
||||
}
|
||||
const bool morePending = AnyVisibleLanePending();
|
||||
const bool morePending =
|
||||
AnyVisibleLanePending() ||
|
||||
(!m_layerRebuildPending.empty() && PrefetchStepAllowed());
|
||||
// 绘图区底色(覆盖时间轴与侧边留白区域)。
|
||||
UiFillRect(g, m_area, m_theme->plotBg);
|
||||
// 时间轴(宽缓冲不含它,前台按当前视图绘制)—— 必须在贴图**之前**算好:
|
||||
@@ -2441,6 +2444,11 @@ namespace jdi
|
||||
}
|
||||
|
||||
bool PlotView::LaneVisuallyRelevant(int laneIndex) const
|
||||
{
|
||||
return LaneVisuallyRelevant(laneIndex, 0);
|
||||
}
|
||||
|
||||
bool PlotView::LaneVisuallyRelevant(int laneIndex, int prefetchScreens) const
|
||||
{
|
||||
if (laneIndex < 0 || laneIndex >= static_cast<int>(m_lanes.size()))
|
||||
{
|
||||
@@ -2451,8 +2459,10 @@ namespace jdi
|
||||
return false;
|
||||
}
|
||||
const gd::RectF& rect = m_lanes[static_cast<std::size_t>(laneIndex)].rect;
|
||||
// 用一个网格高的余量:即将滚入视区的网格也算相关,避免滚动时出现一帧空档。
|
||||
const float margin = std::max(rect.Height, 0.0f);
|
||||
// 可视判定本身留一个网格高的余量(即将滚入视区的网格也算相关);
|
||||
// 预取带在其上再按屏高放大(见头文件说明)。
|
||||
const float margin = std::max(rect.Height, 0.0f) +
|
||||
static_cast<float>(std::max(0, prefetchScreens)) * m_plotRect.Height;
|
||||
return (rect.Y + rect.Height + margin > m_plotRect.Y) &&
|
||||
(rect.Y - margin < m_plotRect.Y + m_plotRect.Height);
|
||||
}
|
||||
@@ -2481,9 +2491,12 @@ namespace jdi
|
||||
|
||||
bool PlotView::AnyVisibleLanePending() const
|
||||
{
|
||||
// ⚠️ 用 **±1 屏预取带**(而不是“严格可见”):网格滚动动画会在几百毫秒内扫过一整个屏高,
|
||||
// 只预建严格可见的网格时,动画扫进去的每一帧都要靠底衬兜底重画十几份网格
|
||||
// (实测 ~8ms/帧 ⇒ 动画明显卡顿)。预取带让静置期间就把附近网格的图层建好。
|
||||
for (const int laneIndex : m_layerRebuildPending)
|
||||
{
|
||||
if (LaneVisuallyRelevant(laneIndex))
|
||||
if (LaneVisuallyRelevant(laneIndex, 1))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -2959,6 +2972,18 @@ namespace jdi
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool PlotView::PrefetchStepAllowed()
|
||||
{
|
||||
const std::uint64_t now = GetTickCount64();
|
||||
if (now >= m_lastPrefetchRequestMs &&
|
||||
(now - m_lastPrefetchRequestMs) < kPrefetchMinIntervalMs)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_lastPrefetchRequestMs = now;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlotView::WarmLaneLayersStep()
|
||||
{
|
||||
if (m_dragMode != PlotDragMode::NONE || m_lanes.empty() || m_fonts == nullptr)
|
||||
@@ -2989,9 +3014,13 @@ namespace jdi
|
||||
// 也不会走"位移超出画布 → 立刻整块重画"那条分支。
|
||||
RollWideBuffer();
|
||||
RenderPendingLaneRebuilds(1);
|
||||
// ⚠️ 只在还有**可视**网格待补时才请求再送一帧:离屏网格的数量可能上百,
|
||||
// 若按“队列非空”每帧请求重绘,会持续一秒以上地高频重绘并饿死动画(见 `AnyVisibleLanePending`)。
|
||||
const bool morePending = AnyVisibleLanePending();
|
||||
// ⚠️ 是否继续请求下一帧:
|
||||
// * 还有**可视 / 预取带内**的网格待补 ⇒ 立刻继续(收敛快,见 `AnyVisibleLanePending`);
|
||||
// * 只剩**离屏**网格 ⇒ 按 `kPrefetchMinIntervalMs` **限速**继续(慢速后台预热,
|
||||
// 既不饿死动画定时器,又能把整批图层建好 —— 见 `PrefetchStepAllowed`)。
|
||||
const bool morePending =
|
||||
AnyVisibleLanePending() ||
|
||||
(!m_layerRebuildPending.empty() && PrefetchStepAllowed());
|
||||
if (morePending)
|
||||
{
|
||||
const HWND host = m_host;
|
||||
|
||||
Reference in new issue
Block a user