预览曲线按面板高亮 + 统计数字不再卡在“统计中…” + 中心轴面板改表格 + 默认主题 Light + 标签页 DPI 修复 + 布局边界幽灵元素

一、预览曲线悬停高亮(只开弱峰+中心轴时无反应;三个全开时总亮均值曲线)
   根因:PaintLaneDerivedOverlays 里只有 MEAN 分支传 highlight,且悬停状态没有记录面板种类。
   修法:新增 m_meanRowHoverKind,高亮条件 = 悬停行 == 本行 && 面板种类 == 本曲线来源;
         条目栏强调色同样按种类门控;悬停种类进入面板缓存键。

二、统计数字松手后停在“统计中…”
   根因:交互期间面板缓存键被冻结(为拖动跟手),冻结期满后没有重绘请求(鼠标已停、
         统计也已完成),最后一次绘制仍是冻结帧。
   修法:记录 m_panelKeysFrozen 并公开 PanelRefreshPending();Render 末尾把它并入
         滚动动画定时器的武装条件 ⇒ 空闲后自动补一帧真实数字,随后停表。

三、「中心轴边沿计数」面板改为表格
   表头固定:信号名 / 上升沿计数 / 下降沿计数;每行底色 = 该信号配色(未勾选的行普通底色),
   三列与表头严格对齐,内容可滚动,单击行展开两行参数滑块。
   - EdgePanelLayout 新增 tableHeader / tableColumns[3];EdgePanelLayoutFor 新增 centerTable 分支;
   - 新增 EdgeCenterTableHeader() / EdgeCountCellText();EnsureEdgeCenterItem 默认 collapsed;
   - PanelSlotKey 的 CENTER 分支改为按 item 判断并混合计数(表格已没有 rowStats)。

四、默认主题改为 Light(Application、ThemeFor/ThemeName 默认分支、CurrentTheme 兜底)。

五、标签页文本在 DPI 切换后被省略
   根因:标签宽度恰好等于“内边距+文本+间距+关闭按钮”,无任何余量;TabItemWidget 缓存了
         Configure 时的 scale 并用它反推可用文本宽度,而 TabBar::SetScale 不重建控件树
         ⇒ 125%→100% 后可用宽度少约 4.5px ⇒ 吃掉末字。
   修法:SetScale 变化时重建控件树;并把文本可用矩形改由 ArrangeWidgets 统一下发
         (SetState 增加 textRect 参数),绘制侧不再自行反推。

六、布局边界里的幽灵元素
   - 新增 SignalPanel::HasStyleTarget();BuildBoxes 与 QueryCursor 在无样式目标时
     不再上报/响应色块、样式、调整、线宽、透明度等控件;
   - Paint 的“无网格”分支显式清空 m_legendHits / m_cursorLabelRects / m_cursorSpanRect /
     m_freeMarkerHits / m_panelSlots / m_panelOrder(关闭整个分组后不再残留边界框);
   - 浮动面板之上的滚动条 / 回到最上方 / 内联输入框改为按可见性上报几何。

验证(离屏探针,SW_HIDE + 合成消息,不占用鼠标键盘屏幕)
  .preflight/p21.exe 9/9   .preflight/p22.exe 14/14   .preflight/p23.exe 7/7
  .preflight/p20 系列(9/11/5)全部回归通过
  make release / make debug 零 warning;Release 813,568 B
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-11-02 15:22:26 +08:00
1 parent 7a6fcd68e2
commit 8de8a8fb85
14 files changed
+934 -141

No files matched your search

+124
View File
@@ -9186,3 +9186,127 @@ make release / make debug 零 warning
取像素做 A/B 时要**避开圆角 8px**,否则会误报 7 个像素。
- ⚠️ `git diff --numstat` 出现整文件级别的增删时要先怀疑行尾/BOM 被 PowerShell 改掉;
本轮所有写回都用 `[System.IO.File]::WriteAllText(... UTF8Encoding($false))`。
---
## 2026-11-02 第二十一轮(本机):预览曲线按面板高亮 + 统计数字"卡在统计中" + 中心轴面板改表格 + 默认主题 Light + 标签页 DPI + 布局边界幽灵元素
> 六件事,用户逐条报告。全部用离屏探针验证(`.preflight/p21`、`p22`、`p23`,`SW_HIDE` + 合成消息,
> 不占用鼠标/键盘/屏幕);`p20`(第二十轮的探针)回归全绿。
### 1. ⭐⭐ 预览曲线悬停高亮:只认「鼠标所在的那个面板」
现象:只开「弱峰调整 + 中心轴边沿计数」时,悬停信号条目栏**毫无反应**;三个实验性功能全开时,
悬停**任何一个**面板都只看到「均值滤波」那条曲线被高亮。
根因(`PaintLaneDerivedOverlays`):三条曲线的绘制分支里**只有 MEAN 分支传了 `highlight`**,
CENTER 分支压根没传(默认 false);而悬停状态只记录了 `(lane, ref)`,**没有记录面板种类** ——
同一条信号在三个面板里各有一行,无法区分"该高亮哪一条"。
修法:
* 新增 `m_meanRowHoverKind`(`EdgePanelKind`),`OnMouseMove` 与 `(lane, ref)` 一起维护;
* 每条曲线的高亮条件 = `悬停行 == 本行 && 悬停面板种类 == 本曲线的面板种类`;
* 条目栏的强调色叠加同样加上 `m_meanRowHoverKind == kind`(否则悬停一个面板会把其它面板的
同名行一起点亮);
* 面板缓存键(`PanelGlobalKey`)混入悬停种类(它直接改变面板像素)。
### 2. ⭐⭐ 统计数字"松手后永远停在统计中…"
现象:按住横向光标快速拖动、并在**鼠标继续快速移动时**松开左键 ⇒ 面板一直显示"统计中…",
直到鼠标移到面板上或做别的操作才更新(中心轴面板拖参数滑块后松手同理)。
根因:交互期间(拖拽 / 鼠标 140ms 内移动过)面板缓存键被**冻结**(`InputIsInteractive()`),
面板直接贴上一帧像素 —— 这是第十九轮为"拖动跟手"刻意做的。但**冻结结束后没有任何重绘请求**:
鼠标停了就没有 `WM_MOUSEMOVE`,统计也早已完成(不会再 `RequestEdgeScanFrame`)⇒
最后一次绘制仍是冻结帧,界面上永远停在旧文字。
修法(补一帧):
* `PanelGlobalKey()` 里记录 `m_panelKeysFrozen = interactivePanelKey`(`mutable`);
* 新增公开查询 `PlotView::PanelRefreshPending()`;
* `Application::Render` 末尾:`PanelRefreshPending()` 为真时也保持 `kScrollTimerId` 定时器,
`WM_TIMER` 走既有的"只失效绘图区"分支 ⇒ 冻结窗口结束后的下一帧按最新数字重画面板,
之后 `m_panelKeysFrozen` 变假、定时器自动停。
### 3. 「中心轴边沿计数」面板改为**表格**
表头固定不滚动:**信号名 | 上升沿计数 | 下降沿计数**;下面每条可见信号一行,行底色 = 该信号的
配色(未勾选的行用普通底色 + 空复选框,表示"尚未参与统计"),三列与表头严格对齐;内容超高时
右侧出现滚动条;单击行(非复选框)展开该信号自己的两行参数滑块(均值点数 / 迭代次数)。
实现要点:
* `EdgePanelLayout` 新增 `tableHeader` + `tableColumns[3]`(`TableColumn{left,width}`);
* `EdgePanelLayoutFor` 新增 `centerTable` 分支(CENTER 不再走 `listStyle`);
* 新增 `EdgeCenterTableHeader(col)` / `EdgeCountCellText(value, complete)`(单元格只写数字,
未算完时追加 `…`);
* `Lane::EnsureEdgeCenterItem` 新建条目时 **`collapsed = true`**(表格行默认收起参数滑块);
* `PanelSlotKey` 的 CENTER 分支**不能再按 `rowStats` 门控**(表格没有 rowStats 了)——
改为"凡 `FindEdgeCenterItem != nullptr` 的行都把 (rising, falling, complete) 混进键",
否则数字变化不会让键变化、面板永远停在"统计中…";
* 表格复用原有的 `rowHeaders/rowChecks/rowTriangles/rowNameRects/rowLevel*/rowIter*`,
因此**交互代码不用改**(勾选切换、整行展开收起、滑块拖动、滚动条 / 滚轮全部沿用)。
### 4. 默认主题改为 **Light**
`Application::m_themeKind = ThemeKind::LIGHT`;`ThemeFor()` 的 `default`、`ThemeName()` 的
`default`、`CurrentTheme()` / `SetCurrentTheme(nullptr)` 的兜底也一并改为 `kLight`
(避免"宿主未设置时回落到 Solarised Light"的不一致)。
### 5. 标签页文本在 DPI 切换后被省略
现象:主屏 125% 打开,拖到 100% 副屏后「视图 1」变成「视图…」,双击改名再取消就恢复正常。
根因:标签宽度恰好等于 `2*pad + 文本 + gap + close`(**没有任何余量**),而
`TabItemWidget` 在 `Configure({fonts, theme, scale})` 时**缓存了当时的 scale**,绘制时用它
从 `closeRect` 反推"可用文本宽度"。`TabBar::SetScale` 只改自己的 `m_scale`、**不重建控件树**
⇒ DPI 切换后控件仍用旧缩放扣边距(`14*1.25` vs `14*1.0`、`6*1.25` vs `6*1.0`)⇒
可用宽度比实测文本窄约 4.5px ⇒ `UiEllipsize` 吃掉末字。双击改名会走到重建路径,于是"又好了"。
修法(两层):
* `TabBar::SetScale` 在缩放**真的变化**时 `RebuildWidgets()`(与 `Toolbar::SetScale` 的
`m_treeDirty = true` 同一思路);
* 更根本的一层:**文本可用矩形改由 `TabBar::ArrangeWidgets` 统一下发**
(`TabItemWidget::SetState(..., textRect)`),绘制侧不再自己反推 —— 几何单一来源,
以后不会再被"缩放/取整"这类差异咬到。
### 6. 布局边界里的"幽灵元素"
* ⭐ **样式区控件在未选中信号时也上报几何、也接受光标**:`LayoutStyleControls()` 无条件算出
色块/样式/调整/线宽/透明度等矩形,而绘制在"没有目标"时只画占位文字。
`OnLButtonDown` 与 `OnMouseMove` 早已按 `StyleOf(SelectedRef())` 门控,
但 **`BuildBoxes` 与 `QueryCursor` 没有** ⇒ 打开「显示布局边界」能看到整排控件框,
鼠标掠过「系数/偏移 | 镜像 | 绝对值」还会变手型。
修法:新增 `SignalPanel::HasStyleTarget()`(与绘制/命中同一判据),
`BuildBoxes` 只在有目标时登记这些控件(「目标」行本体总是登记),
`QueryCursor` 同样只在有目标时给手型。
* ⭐ **关闭整个分组后残留大量边界框**:`Paint` 在 `m_lanes.empty()` 时直接 return,
`PaintLaneLegend / PaintCursor / PaintFreeMarker` 都不跑 ⇒
`m_legendHits / m_cursorLabelRects / m_cursorSpanRect / m_freeMarkerHits / m_panelSlots`
里的**旧矩形**一直留着(布局边界继续画、命中测试也会命中已不存在的元素)。
修法:空分支里显式清空这些几何并清 `m_panelOrder`。
* 浮动面板之上的叠加层(滚动条 / 回到最上方 / 内联输入框)以前**无条件上报**矩形
(即使没画)⇒ 现在按 `m_scrollBarVisible` / `BackToTopVisible()` / `m_editTarget != NONE`
门控。
### 7. 验证
```
p21.exe 9/9 无幽灵样式区框、空样式区无手型;选中信号后框与手型恢复;
关闭分组后 lanes=0、legendHits=0、绘图区内只剩 4 个真实元素框
p22.exe 14/14 默认主题 = Light;表格表头存在/列不重叠/内容在表头之下;
行底色 = 信号配色;交互帧冻结 ⇒ 空闲窗口后自动解冻(宿主补帧);
悬停 CENTER 行高亮中心轴曲线且种类记为 CENTER;悬停 PEAK 面板记为 PEAK;
**DPI 往返(96→120→96)后标签文本墨迹宽度 38 vs 实测 40(A/B 去掉重建 ⇒ 25)**
p23.exe 7/7 60 信号:表格限高/可滚动/像素缓存命中;勾选框切换统计;整行展开收起;
展开后出现滑块轨道;内容滚动生效
p20 / p20c / p20d 全绿(第二十轮的面板限高 / 弱峰列表 / 统计推进 / 组名折起)
make release / make debug 零 warning;Release 813,568 B(< 1MB 红线)
```
### 8. 踩坑
* ⚠️ 用 `#define private public` 的离屏探针里,"读像素来判断文字是否被省略"必须**与同一 DPI
下实测的文本宽度比较**:跨 DPI 比较字号本来就不同(本机主屏 125%,进程启动即 120 DPI)。
* ⚠️ 探针里两个面板默认都叠在右上角 ⇒ `TopPanelAtPoint` 只返回最上层那个,
要验证"某个面板的悬停"必须先把另一块的 `panel.x/y` 挪开。
* ⚠️ 改完 `TabBar` 的 `SetState` 签名要同步 `ArrangeWidgets` 唯一调用点;
探针 A/B 时**先确认 `make` 真的重编了**(过滤输出只看 error 会漏掉"没重编")。