fix(UI): 浮层不再被下层边界穿透 + 菜单条目裁剪 + 修正性能误判
## 0. 仓库同步 远端 gitea 已把 UI_Refactor 合入 main(PR #1,合并提交 322b658,分支保留)。 main 与 UI_Refactor 均同步到该提交;dev 分支远端从未存在(意外产物),本地已删除。 ## 1. 修正上一轮的性能误判(重要) 上一轮报告称「PlotView 基线每帧在 WarmLaneLayersStep 上多花约 2.4ms」。本轮用两级计时查证, 该结论是错的: 排空前 idle 2.60 ms ← 含"图层预热队列排空"的瞬时成本 排空后 idle 0.53 ms ← 真正的稳态 强制整帧 idle 0.46 ms 那 2.4ms 是载入 283 个网格后给 16 个网格补建宽基底图层的**瞬时**开销(每帧补 1 个,约 16 帧结束), 属于设计内的预热,不存在每帧白烧的固定成本。 仍修掉顺带发现的一处顺序问题:RollWideBuffer 原来对全部 283 个网格先算 LaneLayerKey() (逐网格内容哈希 ≈0.7ms/帧)再判断可视性,且离屏网格的"结构不可用"分支会逐帧把它们塞进 待重画队列再被清掉。现在把 LaneVisuallyRelevant 判断提到循环最前面(见函数内注释)。 ## 2. 浮动面板被网格边界穿透(层级 + 挖洞 + 裁剪) 根因:PlotView::CollectDebugBoxes 把网格盒子与面板盒子放在同一绘制层,只靠 depth 排序; 而调试线画在所有内容之后,"后画"只决定谁压谁,不能阻止下层组件的线仍然可见。 修复三步: - 收集面板前 boxes.PushLayer() ⇒ 面板层高于网格层; - 每个面板 boxes.AddOccluder(layout.panel) ⇒ 绘制更低层盒子时用 Region::Exclude 挖洞; - boxes.SetClip(layout.panel) ⇒ 面板内部元素(如滚出内容区的行)不再登记到面板外。 ## 3. 滚动时面板/菜单边界闪烁或溢出 滚动容器里的条目矩形始终存在,只是绘制时被裁剪掉;调试层读到这些矩形就会给"看不见的内容"画框, 滚动时条目不断进出可视区,框也就不断出现/消失 ⇒ 闪烁 / "边界超出菜单"。 - 面板内容裁到 layout.panel; - 菜单条目裁到 m_scrollEnabled ? m_scrollViewport : m_rect;二级子菜单条目裁到 m_subRect。 ## 4. 顺带小优化 EdgePanelLayoutFor 单次约 2~3µs,283 网格 × 5 类 = 1415 次/帧。现在先看该网格的 5 个面板 open 标志,全关就跳过内层循环。 ## 5. 验证 - make release / make debug 零 error 零 warning;TargetCheck.exe 909,824 B(888.5 KiB < 900 KiB)。 - 新增探针 .preflight/lbpanel 10/10: panel-open / panel-contents-clipped-to-panel / panel-registered-as-occluder / no-lane-bounds-inside-panel(面板内网格层颜色像素=0)/ scrolling-actually-happened (真滚动 0.0→1295.1)/ panel-boxes-stable-while-scrolling(11/11 帧签名一致)/ menu-open-scrollable / no-box-escapes-menu / scrolled-out-items-not-collected。 - 回归:lbcovr 7/7、lbzorder 4/4、lbresid 5/5、lbcheck 21/21、uicheck 69/69。 - rendercheck:8 个场景哈希与上一轮**逐一相同** + FUNC 7/7(本轮不应改变任何正常绘制像素)。 ## 6. 文档 Documents/UI_Refactor_Plan.md / UI_Refactor_Progress.md / UI_Refactor_Report.md 追加第十阶段; Documents/agent_mem.md 同步变更日志与「2026-10-29 第十二轮」的结论修正、浮层三件事、踩坑。
This commit is contained in:
1 parent
322b6582d7
commit
739a523af5
7 files changed
+321
-15
No files matched your search
@@ -602,10 +602,34 @@ namespace jdi
|
||||
// 层级 3:内联输入框(正在编辑时才有意义)。
|
||||
boxes.Add(EditFieldRect(), 3);
|
||||
|
||||
// ⚠️⚠️ **浮动面板必须另起一个绘制层,并把面板矩形登记为遮挡矩形**:
|
||||
// 面板是不透明浮层、且常常**跨多个网格上下排列**,与网格区域重叠。
|
||||
// 若与网格同层,网格的矩形边线会横穿面板 —— 用户 2026-10-29 报告的
|
||||
// "网格的布局边界穿透显示在浮动面板上"。`layer` 决定谁压谁,
|
||||
// `AddOccluder` 才真正把下层 box 从面板区域里**挖掉**。
|
||||
bool panelLayerPushed = false;
|
||||
|
||||
// 层级 2/3/4:5 类实验性功能浮动面板的内部控件。
|
||||
// 这些矩形由 `EdgePanelLayoutFor` 统一算出(绘制与命中共用),一个循环即可全部上报。
|
||||
for (std::size_t index = 0; index < m_lanes.size(); ++index)
|
||||
//
|
||||
// ⚠️ **先用最便宜的判据跳过"没有面板"的网格**:`EdgePanelLayoutFor` 内部要取
|
||||
// 标签页配置、逐项判断种类与开关,单次约 2~3µs;283 个网格 × 5 种 = 1415 次调用
|
||||
// 实测约 **4ms/帧**(这是"开调试后卡"的主要来源之一)。
|
||||
// 只有该网格**至少有一个面板处于展开状态**时才值得去算布局。
|
||||
const std::vector<Lane>& laneConfigs =
|
||||
(m_tab != nullptr) ? m_tab->Lanes() : m_localTab.Lanes();
|
||||
for (std::size_t index = 0; index < m_lanes.size() && index < laneConfigs.size(); ++index)
|
||||
{
|
||||
const Lane& laneConfig = laneConfigs[index];
|
||||
const bool anyPanelOpen = laneConfig.edgePanel.open ||
|
||||
laneConfig.edgeCenterPanel.open ||
|
||||
laneConfig.meanFilterPanel.open ||
|
||||
laneConfig.weakPeakPanel.open ||
|
||||
laneConfig.freeMarkerPanel.open;
|
||||
if (!anyPanelOpen)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const int laneIndex = static_cast<int>(index);
|
||||
for (int kindValue = static_cast<int>(EdgePanelKind::COUNT);
|
||||
kindValue <= static_cast<int>(EdgePanelKind::MARK); ++kindValue)
|
||||
@@ -616,6 +640,15 @@ namespace jdi
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!panelLayerPushed)
|
||||
{
|
||||
boxes.PushLayer();
|
||||
panelLayerPushed = true;
|
||||
}
|
||||
boxes.AddOccluder(layout.panel);
|
||||
// 面板内部元素的调试框同样裁到面板本身:面板滚动的内容区里,
|
||||
// 滚出可视区的行其矩形仍在(与弹出菜单的条目同一类问题)。
|
||||
boxes.SetClip(layout.panel);
|
||||
boxes.Add(layout.panel, 2);
|
||||
boxes.Add(layout.title, 3);
|
||||
boxes.Add(layout.close, 3);
|
||||
@@ -658,6 +691,8 @@ namespace jdi
|
||||
}
|
||||
}
|
||||
}
|
||||
// 收尾:解除面板裁剪(后续若还有调用者依赖"不裁剪"的默认行为)。
|
||||
boxes.SetClip(gd::RectF());
|
||||
}
|
||||
|
||||
void PlotView::Paint(gd::Graphics& g)
|
||||
|
||||
Reference in new issue
Block a user