修复五项交互缺陷:折起后滚动条 / 单点信号样式 / 线宽实时生效 / 网格菜单限高滚动 / 点选信号滚动绘图区
1) 分组折起后再展开,信号列表的滚动条消失(筛选态与常规态均可复现)
- 根因:AdvanceGroupAnimations 只在“折起完成”时重建行集合;而“展开完成”那一帧
m_contentHeight 从收起态跳回展开态,却没有任何路径重算滚动条可见性 ⇒ 滚动条停在隐藏。
- 修法:任一方向动画结束都 m_rowsDirty = true + BuildRows(),并每帧 RefreshScrollBar(false)。
⚠️ 动画期间不夹取滚动偏移,避免打断与滚动同步的折起动画(scrollSynced)。
2) 单点信号(finiteCount == 1)默认绘制方式由「点线」改为「散点」
- ApplySinglePointDefaultStyle → DrawMode::SCATTER(仍取最大线宽)。
- 新增 SignalPanel::ModeOptionEnabled:单点信号的「线段」「点线」置灰、不可点、
且不参与悬停高亮(避免“看着能点其实点不动”);样式区的绘制与命中都用它。
- PlotView::UpdateAutoScatterStyles 跳过单点信号,否则“放大自动点线”会把它又改回「点线」。
3) 拖动线宽滑块完全不生效(拖动时线宽不变、松手瞬间变了、再移动鼠标又变回去)
- 根因一:AcquirePolyline 的折线缓存键没有 lineWidth,而 BuildPolyline 把线宽烘进
包络带几何(每个像素列的上下包络各向外扩 max(1,线宽)*scale*0.5,「梳状」判定也依赖它)
⇒ 稠密信号(采样数 > 绘图区宽度)永远复用旧线宽的顶点与包络带。
- 根因二:样式补丁只更新“静态场景缓存 + 后备缓冲区”,而每网格图层(宽基底)仍是旧样式像素,
下一帧命中“宽基底快路径”时把新样式覆盖回旧像素。
- 修法:PolylineCacheEntry 增加 lineWidth 并进比较;
样式补丁之后 QueueLaneRebuild(受影响网格) + RenderPendingLaneRebuilds(1)(沿用每帧至多 1 个的预算)。
4) 「目标」行的 GX 标签复用「所属网格」菜单,并先给 PopupMenu 增加「限高 + 条目区滚动 + 尾部固定项」
- MenuLayoutConstraint::maxHeight(OpenLaneMenu 传客户区高度的 62%);
- 固定区 = 最后一个分隔线及其之后的条目(即「新建网格」)始终钉在菜单底部,其余条目可滚动;
- 滚轮滚动条目区(Application 在菜单内滚动时不再关闭菜单,其余情形保持既有行为);
- 键盘移动悬停项自动滚入可视区(EnsureItemVisible);ItemAtPoint 要求条目同时命中滚动视口;
- 滚动区文本用 UiTextClipped(GDI 文本不遵循 GDI+ SetClip),并跳过完全不可见的条目(性能);
- BuildLayout 不再无条件清零 m_scrollOffset(否则 RebuildLayout 会把滚轮位置吃掉);
- 「目标」行 GX 标签点击**不参与**联动滚动(悬停联动保留)。
5) 点选信号名 ⇒ 绘图区自动把目标网格滚动到可视区内(上百个独立网格时旧有高亮无法定位)
- SignalPanel::RequestPlotLaneScroll / TakePlotLaneScrollRequest;
Application::ApplyPendingPlotLaneScroll(WM_LBUTTONDOWN 的面板分支);
PlotView::ScrollLaneIntoView(在可视区上方就上滚、在下方就下滚、已完整可见则不动)。
- 只有“点击信号名”触发;悬停绝不改变视图;勾选框 / GX 徽标 / 移除 ✕ 也不触发。
验证(全部离屏执行,窗口 SW_HIDE + 只注入合成消息 + 进程与线程 BELOW_NORMAL,
不移动真实光标、不抢焦点、不截屏):
- 新增 .preflight/check(不入库)68/68 通过;
- A/B:五处修复全部回退 ⇒ 17 项如实失败(B 4 / C 4 / D 7 / E 2);“折起后滚动条”需要
逐帧重绘驱动动画才能复现,单独回退时 ⇒ 5 项如实失败;恢复后 68/68;
- make release / make debug 零警告。
其它:
- 按要求删除工作区 Tools/(structcheck 脚本)并移除 Makefile 的 structcheck 目标;
- Documents/agent_mem.md 同步:新增 §9.38、§11.2 第 97–103 条与变更日志一行。
This commit is contained in:
1 parent
9cf6b2fedc
commit
2904699eae
19 files changed
+743
-350
No files matched your search
@@ -2288,6 +2288,108 @@ Components/Shared/Src/UiControls.cpp
|
||||
|
||||
---
|
||||
|
||||
### 9.38 五项交互修复:折起后滚动条 / 单点信号样式 / 线宽实时生效 / 网格菜单限高滚动 / 点选信号滚动绘图区(本轮,起源设备)
|
||||
|
||||
> 用户 2026-10-16 第四轮(起源设备)。
|
||||
> 离屏验证:**`.preflight/check`(新建)68/68 通过**(`SW_HIDE` + 只注入合成消息 + 进程/线程 `BELOW_NORMAL`);
|
||||
> **A/B**:五处修复全部回退 ⇒ **17 项如实失败**(B 4 / C 4 / D 7 / E 2),
|
||||
> 其中「折起后滚动条」需要**逐帧重绘驱动动画**才能复现 ⇒ 单独回退时**5 项如实失败**;
|
||||
> 恢复后 **68/68**;`make release` / `make debug` **零警告**。
|
||||
> 另按要求**删除 `Tools/`(`structcheck` 脚本)并移除 `make structcheck` 目标**(见本节七)。
|
||||
|
||||
#### 一、分组折起后再展开,信号列表的滚动条消失(用户报告)
|
||||
|
||||
- **现象**:加载 MF4 → 折起分组 → 展开分组 ⇒ 列表右侧滚动条**消失且不再回来**(筛选态、普通态均可复现)。
|
||||
- **根因**(两处叠加):
|
||||
* `AdvanceGroupAnimations` 只在**折起**完成时 `m_rowsDirty = true`(认为“展开后行集合本来就是展开态”);
|
||||
* `RefreshScrollBar()` 只在 `EnsureRows()`(行集脏)/`Layout()` 里被调用 ——
|
||||
展开结束那一帧 `m_contentHeight` 从“收起态”跳回“展开态”,但**没有任何路径去重算滚动条可见性**。
|
||||
* 于是 `m_scrollBarVisible` 停在动画开始时的 `false`、`m_listRect.Width` 停在“无滚动条”的整宽,
|
||||
直到用户做别的动作(筛选 / 缩放窗口 / 勾选信号)才恢复。
|
||||
- **修法**:`AdvanceGroupAnimations` 里 **任一方向的动画结束都 `m_rowsDirty = true` 并 `BuildRows()`**
|
||||
(内部重算内容高度与逐行位移),随后**每帧都调 `RefreshScrollBar(false)`** ——
|
||||
让滚动条可见性/列表可用宽度跟随动画中的内容高度实时更新。
|
||||
- ⚠️ `RefreshScrollBar` 新增 `clampScroll` 形参(默认 true);**动画期间必须传 false**:
|
||||
与滚动同步的折起动画(`scrollSynced`)正按自己的进度推进 `m_scrollOffset`,夹取会打断它。
|
||||
- ⚠️⚠️ **探针教训**:必须**逐帧重绘**(每帧 `Render()`)来驱动这个动画,
|
||||
否则 `EnsureRows()` 会在动画结束后顺手修好“完成帧的不一致”,把缺陷**掩盖**掉
|
||||
(第一版探针就是这样假绿的)。
|
||||
|
||||
#### 二、单点信号:默认绘制方式改「散点」+ 「线段」/「点线」置灰(用户要求)
|
||||
|
||||
- `ApplySinglePointDefaultStyle`:`DrawMode::LINE_SCATTER` → **`DrawMode::SCATTER`**(仍取最大线宽)。
|
||||
- 新增 `SignalPanel::ModeOptionEnabled(optionIndex)`:**只有一个有效采样点**的信号只允许「散点」,
|
||||
「线段」「点线」**置灰**(弱色文字)**且不可点**、**不参与悬停高亮**(避免“看着能点其实点不动”)。
|
||||
- `PlotView::UpdateAutoScatterStyles` **跳过单点信号**(`finiteCount == 1`):
|
||||
否则“放大自动点线”会把它又改回「点线」,与置灰状态自相矛盾。
|
||||
|
||||
#### 三、拖动线宽滑块完全不生效(用户报告)—— **根因是折线缓存键漏了线宽**
|
||||
|
||||
- **现象**:拖动线宽滑块时信号线宽**纹丝不动**;松开左键的瞬间变了;再移动鼠标又**变回初始线宽**。
|
||||
- **根因(两个,都要修)**:
|
||||
1. ⚠️⚠️ **`PlotView::AcquirePolyline` 的缓存键没有 `lineWidth`**,而 `BuildPolyline` 会把线宽
|
||||
**烘进包络带几何**(`strokeHalf = max(1, 线宽) * scale * 0.5`,逐列上下包络各向外扩这么多),
|
||||
「梳状」判定也依赖扩展后的列跨度 ⇒ **稠密信号(采样数 > 绘图区宽度)改变线宽时命中旧条目**、
|
||||
永远按旧线宽绘制(顶点、包络带全部复用)。实测常量信号:`0.5px → 5.3px → 10px` 时
|
||||
绘图区列厚度始终是 **1px**。
|
||||
2. **样式补丁没有同步宽基底图层**:补丁只更新了“静态场景缓存 + 后备缓冲区”,
|
||||
而每网格图层仍是旧样式像素;下一帧命中“宽基底快路径”时会把新样式**覆盖回旧像素**
|
||||
⇒ 这正是用户看到的“松手后变了、移动鼠标又变回去”。
|
||||
- **修法**:
|
||||
* `PolylineCacheEntry` 新增 `lineWidth` 并进比较(凡是**被烘进几何**的样式都必须进键);
|
||||
* 样式补丁分支在 `RepaintSceneCachePatch` 之后 **`QueueLaneRebuild(受影响网格) + RenderPendingLaneRebuilds(1)`**
|
||||
—— 按既有预算机制(每帧至多 1 个网格)把宽基底同步到新样式,不在交互帧里做整批重建。
|
||||
- **实测**(`.preflight/check`,常量信号 + 逐帧测量信号色像素):
|
||||
`0.5px` → 列厚 **1px**;`5.3px` → **7px**;`10px` → **12px**;松手后 **12px 保持**;再移动鼠标 **仍 12px**。
|
||||
|
||||
#### 四、「目标」行 GX 标签复用「所属网格」菜单 + 限高滚动 + 底部固定项(用户要求)
|
||||
|
||||
- **入口**:「目标」行(样式区)的 GX 标签单击 ⇒ 展开与信号列表行末尾 GX 徽标**完全同一份**菜单
|
||||
(`SignalPanel::OpenLaneMenu`;按下只登记 `m_pendingTargetLaneMenu`,**抬起仍在同一徽标上**才展开)。
|
||||
- ⚠️ GX 标签点击**不做**联动滚动(只有信号名点击才滚动);**悬停联动仍然保留**(`StyleHit::TARGET_BADGE`)。
|
||||
- **`PopupMenu` 新增「限高 + 条目区滚动 + 尾部固定」能力**(`MenuLayoutConstraint::maxHeight`):
|
||||
* 菜单最高 ≈ 客户区高度的 62%(`OpenLaneMenu` 传入);
|
||||
* **固定区 = 最后一个分隔线及其之后的条目**(即「新建网格」+ 它上面的分隔线),
|
||||
始终钉在菜单底部(`m_pinnedIndex` / `m_pinnedTargetTop`);其余条目在滚动区内;
|
||||
* 滚轮滚动(`PopupMenu::OnMouseWheel`;`Application` 在菜单内滚动时**不关闭菜单**),
|
||||
键盘方向键移动悬停项时自动把它滚入可视区(`EnsureItemVisible`);
|
||||
* ⚠️ 滚动区文本必须用 **`UiTextClipped`**(GDI 文本不遵循 GDI+ 的 `SetClip`),
|
||||
并**跳过完全不可见的条目**(否则上百条逐条绘制照样卡顿);
|
||||
* ⚠️ `ItemAtPoint` 要求滚动区条目**同时命中滚动视口**(夹在边界上的部分可见条目只有可视部分可点);
|
||||
* ⚠️⚠️ **`BuildLayout` 不得无条件清零 `m_scrollOffset`**:`RebuildLayout()`(滚轮 / 数值回写触发)
|
||||
会重走一遍本函数,清零就等于把滚动位置吃掉(滚轮“滚不动”,实测踩到)。
|
||||
|
||||
#### 五、点选信号 ⇒ 绘图区自动把目标网格滚到可视区内(用户要求)
|
||||
|
||||
- **背景**:把 MF4 的上百条信号各自放进独立网格时,绝大多数网格在可视区之外,
|
||||
仅靠网格高亮无法定位到画面之外的网格。
|
||||
- **链路**:`SignalPanel::RequestPlotLaneScroll(ref)`(→ `m_pendingPlotLane`)
|
||||
→ `Application::ApplyPendingPlotLaneScroll()`(`WM_LBUTTONDOWN` 的面板分支里调用)
|
||||
→ `PlotView::ScrollLaneIntoView(laneIndex)`。
|
||||
- **触发条件**:**点击信号名**(列表行主体 / 样式区「目标」行的信号名)。
|
||||
⚠️ **悬停不滚动**(只保留既有网格高亮联动);勾选框 / GX 徽标 / 移除 ✕ 也不滚动。
|
||||
- **规则**:目标网格在可视区上方 ⇒ 向上滚到它的上边界贴绘图区上沿;在下方 ⇒ 向下滚到它的下边界贴下沿;
|
||||
已完整可见 ⇒ **不动视图**(避免无意义改动)。
|
||||
|
||||
#### 六、验证(全部离屏,不占用鼠标 / 键盘 / 屏幕)
|
||||
|
||||
- 新增 `.preflight/check/{main.cpp,build.ps1}`(不入库):真实 `Application`(`SW_HIDE`)+ 合成消息注入,
|
||||
六个断言组:**[A]** 折起/展开与滚动条、**[B]** 单点信号样式与置灰(含“悬停置灰按钮像素零变化”)、
|
||||
**[C]** 线宽拖动逐帧像素度量、**[D]** 「目标」行 GX 菜单(限高 / 滚动 / 固定项 / 命中 / 复用换网格)、
|
||||
**[E]** 点选信号滚动绘图区(含“悬停不滚动”与“已可见不动”)、**[F]** 回归(列表行徽标菜单、向上滚动、筛选后滚动条)。
|
||||
- 结果:**68/68 通过**;`make release` / `make debug` **零警告**。
|
||||
- **A/B 反证**:五处修复全部回退 ⇒ **17 项如实失败**(B 4 / C 4 / D 7 / E 2);
|
||||
A 组(滚动条)因探针需要**逐帧重绘**才能复现,单独回退时 ⇒ **5 项如实失败**。
|
||||
|
||||
#### 七、按要求移除远端协助设备的测试工具(用户明确要求)
|
||||
|
||||
- 删除工作区 `Tools/`(`structcheck.ps1` / `structcheck_phrases.txt`)并移除 `Makefile` 的 `structcheck` 目标
|
||||
(该工具只是另一台设备上次为“多文件拆分”验证一次性上传的)。
|
||||
- ⚠️ §9.37 / §11.2 第 96 条里凡是“`make structcheck` 可复跑”的描述**自此失效**,
|
||||
仅作为当时那一轮拆分的**历史证据**保留;后续若要再加结构检查,需要重新上传脚本。
|
||||
|
||||
---
|
||||
|
||||
## 10. 变更日志(本文件维护记录)
|
||||
|
||||
| 日期 | 设备/模型 | 摘要 |
|
||||
@@ -2339,6 +2441,7 @@ Components/Shared/Src/UiControls.cpp
|
||||
| 2026-10-16 | DeepSeek-V4.1-Flash(**另一台设备**) | **动画改「抽拉卷尺」式刚性平移 + 筛选态折叠三角修正 + 样式区「目标」行绘制/布局修正(详见 §9.36)**。① **动画重做**(推翻上一轮的“按行缩放”):整组信号作为**刚性整体**平移,**行高永不变**、只维护一个逐行平移量 `m_rowShift` ⇒ 动画期间**无需重建行集合**(流畅关键);收起时逐条被组名栏吞掉、展开时逐条冒出;中途反向点击**从当前位移续接**不跳变;时长 `clamp(115+6×行数, 150, 320)` ms。② **藏进组名栏的裁剪**:信号行越过组名的**绘制**下边界即裁掉,且除 GDI 裁剪外**必须用 `g.SetClip(..., CombineModeIntersect)` 同时收紧 GDI+ 裁剪**(否则行内色块/复选框/徽标画到组名栏上)。③ **悬浮组名折起改为“一边滚动一边收起”**(推翻“先滚动到位再折起”):`scrollSynced` 让滚动与平移按同一进度推进 ⇒ 组名屏幕位置恒定;结束时滚动偏移恰好落在“组名正好在最上方”,并且**无跳变**;旧 `m_pendingGroupToggle` 机制删除。④ **筛选态折叠三角**:`PaintFileRow` 改读 `IsGroupCollapsed()`(筛选模式用的是独立的 `m_filterCollapsed`),修复“三角维持筛选前状态”。⑤ **「目标」行**:`PaintTargetRow` 误用列表裁剪版 `PaintRowText` ⇒ 名称与 GX 文本被完全裁掉(改用 `UiText`);GX 标签改为**右对齐到内容列右边缘**、名称无标签时占满内容列;`EditFieldRect()` 对目标行只返回名称矩形(否则改名输入框把 GX 标签包进去)。验证:离屏探针 `.preflight/perf/panelanim_main.cpp` **147/147 通过**(连跑 3 次稳定;含逐帧断言组名栏像素零变化、`m_rowsDirty` 恒 false、滚动单调且恰好落位、筛选/普通态折叠三角逐像素 0 差异、目标行文本像素存在与两端对齐);**A/B** 五组(回退筛选三角+非刚性平移 / 回退目标行绘制与布局 / 关闭同步滚动 / 去掉 GDI+ 收紧裁剪 / 不续接位移)分别 **4 / 6 / 6 / 2 / 1 项如实失败**;`verify` **49/49**、`datacheck` **69/69**、`edgecheck` **46/46**;`make clean && make all` 零警告。 |
|
||||
| 2026-10-16 | DeepSeek-V4.1-Flash(**另一台设备**) | **分组折起热区与改名输入框修正 + 数据模型去冗余(单一名称)+ 新增 `Components/Shared` 组件 + 过时注释/命名大清理 + 二进制瘦身 24.6% 并完成多文件拆分(详见 §9.37 与本轮报告 `Documents/refactor_report_2026-10-16.md`)**。① **折起热区**(用户要求):点分组**名称**不再折起,只有**折叠三角**或**「组X」标签**(新增 `RowPart::GROUP_TITLE`)才折起。② **分组改名输入框不再吞掉「N 项」**:`RowNameRect` 右边界改为项数左缘;新增 `GroupLabelText`/`GroupStatsText` 作为文本与宽度的唯一来源。③ **`SignalData`/`SignalGroup` 只留一个名称字段**:删除 `displayName` 与 `Title()`(31 处改用 `name`),`SetSignalDisplayName`/`SetGroupDisplayName` → **`RenameSignal`/`RenameGroup`**(空名一律拒绝);删除死成员 `Application::m_loadingBytes`。④ **新增 `Components/Shared`**(用户要求):从 `PaintUtil` 迁出全部**控件**(图标/复选框/滑块/内联输入框/颜色样块/取值胶囊配色)到 `UiControls.hpp/.cpp`,`PaintUtil` 只留低层原语;依赖方向固定 `Components/* → Components/Shared → Utilities`。⑤ **过时注释/命名**:`文件下标`→`分组下标`、`文件分组`→`分组` 等一批;形参名 `file`→`axis`/`group`;`PaintFileRow`→`PaintGroupRow`、`FileCloseRect`→`GroupCloseRect`、`OpenFileMenu`→`OpenGroupMenu`、`FileGroupLabelWidth`→`GroupLabelWidth`、`StyleEditField::FILE_NAME`→`GROUP_TITLE`;修复 `DataModel.hpp` 两处被压扁的注释块与 `NewTab` 重复文档块。⑥ **多文件拆分(本轮完成)**:`PlotView.cpp`(13 695 行)+ `SignalPanel.cpp` + `Application.cpp` + `DataModel.cpp` 四个大文件全部按功能拆成多个协作文件(私有内部头 + 具名命名空间 + 全 `inline` + 按名字分桶),最大单文件 544 KB → 276 KB。⑦ **二进制瘦身**:真因是 **ICO 里 256×256 未压缩 BMP 条目(270 KB,占整包 27%)**,改为 **PNG 存储**(观感零变化)⇒ `Target.exe` **1 013 248 → 764 416 B(−24.6%)**;实测“拆分不会减小机器码,反而 +1.4%”、`--gc-sections` 反而 +9 KB(已回退)。验证:`panelanim` **156/156**、`verify` **49/49**、`datacheck` **69/69**、`edgecheck` **46/46**;**A/B** 回退两处 UI 修复 **10 项如实失败**;`make release`/`make debug` 零警告。⚠️ 该提交当时的待办(`PlotView.cpp` 等大文件多文件化拆分、`LanePanelState` 抽取、模态提示解耦)**已全部在紧随其后的两个提交里完成**:拆分 → 提交 **`e6b7bf6`**(详见 §9.37 第七节),`LanePanelState` + 模态解耦 → 提交 **`d2e6fdd`**(详见 §9.37 第八节)。**本行不再表示任何未完成项。** |
|
||||
| 2026-10-16 | DeepSeek-V4.1-Flash(**另一台设备**) | **收尾:`LanePanelState` 抽取 + 错误提示非模态化(把上一行的「未完成」清零,详见 §9.37 第八节与报告 `Documents/refactor_report_2026-10-16.md` §九)**。① **`Lane` 的 12 个平铺面板字段**(`edgePanelOpen/X/Y` × 4 类实验性功能)→ **4 个 `LanePanelState`**(`struct LanePanelState { bool open=true; double x=-1; double y=-1; }`),访问点全部改为 `lane.edgePanel.open` 形式;`RebuildLanes` 的 `LaneCarry` 同步收敛为 4 个 `LanePanelState`(逐字段赋值 → 整体赋值)。⚠️ 教训:**批量改名脚本不能对「声明」与「使用」跑同一组替换对**(会生成 `bool edgePanel.open = true;` 非法声明),声明处须单独重写。② **错误提示与模态阻塞解耦**:新增 `Application::SetModalErrorsEnabled(bool)`/`ModalErrorsEnabled()`(成员默认 `true`),`Initialize` **最先**执行 `SetModalErrorsEnabled(showCommand != SW_HIDE)`(隐藏窗口弹模态框 = 进程被钉死);`SetStatusMessage(err,true)` 允许模态时原样 `MessageBoxW`,否则走 `m_plot.ShowPersistentHint(text)`(绘图区底部非模态浮层);`PlotView` 新增公开 `ShowPersistentHint`。普通提示/空文本仍被忽略 ⇒ **交互行为不变**。③ **顺带修掉真实缺陷**:`PlotView::Paint` 在 `m_lanes.empty()` 时提前 `return`,提示浮层在其后绘制 ⇒ **未勾选任何信号时错误永远不可见**(正是"加载失败"场景),该分支补一次 `PaintHint(g)`。④ **新增探针 `.preflight/perf/errcheck` 19/19**:默认值/隐藏降级/宿主开关、**工作线程 + 5s 超时**判"不阻塞"(旧行为会永久阻塞 ⇒ 判失败并 `ExitProcess`,探针不挂)、`EnumWindows` 数本进程 `#32770` 顶层窗口数不变、提示文本/粘滞/需重绘/起始时刻、**像素级**"有/无提示"差异 3781 且全帧差异同为 3781。验证:`make release`/`make debug` 零警告,`Target.exe` **764 416 B**;`verify` 49/49、`datacheck` 69/69、`edgecheck` 46/46、`panelanim` 156/156。⑤ **新增结构检查** `Tools/structcheck.ps1` + `make structcheck`(**93/93**):[1] 四个巨型文件已按职责拆分(`PlotView.cpp` 13 695 行 → 705 行,`nm`:绘制符号只在 `PlotViewPaint.o`);[2] `Components/Shared` 16 个控件全部由 `UiControls.o` 定义、69 处调用跨 ≥3 个组件 + 分层方向;[3] 本轮三项用户要求的源码级断言(折起热区、改名框不吞项数、`SignalData` 单一 `name` 字段 + 注释核验)。**判别力 A/B**:回退两处 UI 修复 ⇒ `panelanim` 147/156(9 项如实失败,文案点名“group rename box stops before the item counter”“clicking the group name does not collapse the group”)、`structcheck` 90/93;恢复后 156/156 与 93/93。 |
|
||||
| 2026-10-16 | DeepSeek-V4.1-Flash(**起源设备**) | **五项交互修复:折起后滚动条 / 单点信号样式 / 线宽实时生效 / 网格菜单限高滚动 / 点选信号滚动绘图区(详见 §9.38)**。① **分组折起后再展开滚动条消失**:`AdvanceGroupAnimations` 只在折起结束时重建行集合,而展开结束那一帧 `m_contentHeight` 跳回展开态却无人重算滚动条可见性 ⇒ 滚动条停在“隐藏”。修法:任一方向结束都 `m_rowsDirty = true` + `BuildRows()`,并**每帧** `RefreshScrollBar(false)`(动画期间不夹取滚动偏移,避免打断 `scrollSynced`)。② **单点信号默认绘制方式改「散点」**(原「点线」),且信号面板「样式」行的「线段」「点线」对它**置灰且不可点**(新增 `ModeOptionEnabled`,也不参与悬停高亮);`UpdateAutoScatterStyles` 跳过单点信号,避免“自动点线”与置灰状态自相矛盾。③ **拖动线宽滑块完全不生效**(用户报告:拖动时不变、松手瞬间变了、再移动鼠标又变回去)——根因两个:**`AcquirePolyline` 的缓存键漏了 `lineWidth`**(而 `BuildPolyline` 把线宽烘进包络带几何:逐列上下包络各外扩半个线宽,「梳状」判定也依赖它),以及**样式补丁没有同步宽基底图层**(下一帧宽基底快路径把新样式覆盖回旧像素)。修法:`PolylineCacheEntry` 新增 `lineWidth` 并进比较;样式补丁后 `QueueLaneRebuild(patchLanes) + RenderPendingLaneRebuilds(1)`。实测常量信号列厚 `0.5px→1px / 5.3px→7px / 10px→12px` 且松手与移动鼠标后**保持 12px**。④ **「目标」行的 GX 标签复用「所属网格」菜单**(与列表行徽标同一份),并先给 `PopupMenu` 加**限高 + 条目区滚动 + 尾部固定项**(`MenuLayoutConstraint::maxHeight`;固定区 = 最后一个分隔线及其后的「新建网格」;滚轮滚动、键盘移动自动滚入可视区;滚动区文本用 `UiTextClipped` 并跳过不可见条目;`ItemAtPoint` 增加滚动视口判定)——顺手解决“上百个网格时菜单超长且交互卡顿”;GX 标签点击**不参与**联动滚动,悬停联动保留。⑤ **点选信号名 ⇒ 绘图区自动把目标网格滚到可视区内**(`RequestPlotLaneScroll` / `ApplyPendingPlotLaneScroll` / `PlotView::ScrollLaneIntoView`):在上方就上滚、在下方就下滚、已可见则不动;**悬停不滚动**。验证:新增离屏 `.preflight/check`(`SW_HIDE` + 合成消息 + `BELOW_NORMAL`)**68/68**;**A/B** 五处修复全回退 ⇒ **17 项如实失败**(A 组需逐帧重绘驱动,单独回退 ⇒ 5 项);`make release` / `make debug` 零警告。⑥ 按要求**删除 `Tools/`(`structcheck`)并移除 `make structcheck` 目标**(§9.37/§11.2 第 96 条的“可复跑”描述自此失效,仅作历史证据)。 |
|
||||
|
||||
---
|
||||
|
||||
@@ -2808,6 +2911,37 @@ Components/Shared/Src/UiControls.cpp
|
||||
因为分组在列表里连续排列)。用户滚轮 / 拖滚动条必须**立即取消**动画。
|
||||
⚠️ 宿主定时器同时驱动「折起/展开动画」(见第 77 条):`Render` 的判定必须是
|
||||
`ScrollAnimationActive() || GroupAnimationActive()`。
|
||||
97. ⚠️ **分组折起/展开**动画结束的那一帧**必须重建行集合并按新内容高度刷新滚动条**:
|
||||
`AdvanceGroupAnimations` 里任一方向结束都会 `m_rowsDirty = true` + `BuildRows()`(重算
|
||||
`m_contentHeight` 与逐行位移),随后**每帧** `RefreshScrollBar(false)`。
|
||||
旧实现只在折起结束时重建 ⇒ **展开后滚动条消失且不再回来**(用户 2026-10-16 报告)。
|
||||
⚠️ `RefreshScrollBar(clampScroll = false)`:动画期间不能夹取滚动偏移(会打断 `scrollSynced`)。
|
||||
⚠️ 探针必须以**逐帧重绘**驱动动画,否则 `EnsureRows()` 会把缺陷掩盖掉。
|
||||
98. ⚠️ **单点信号(`finiteCount == 1`)固定使用「散点」**:`ApplySinglePointDefaultStyle` 写入
|
||||
`DrawMode::SCATTER` + 最大线宽;信号面板「样式」行的「线段」「点线」对它**置灰且不可点**
|
||||
(`SignalPanel::ModeOptionEnabled`,同时不参与悬停高亮);
|
||||
`PlotView::UpdateAutoScatterStyles` **必须跳过单点信号**(否则会又把它改回「点线」)。
|
||||
99. ⚠️⚠️ **折线顶点缓存(`PolylineCacheEntry`)的键必须包含所有“被烘进几何”的样式** ——
|
||||
目前是 **`lineWidth`**(`BuildPolyline` 把每个像素列的上下包络向外扩 `max(1, 线宽)*scale*0.5`,
|
||||
「梳状」判定也依赖它)。漏掉线宽会让**稠密信号**(采样数 > 绘图区宽)的线宽改动完全不生效
|
||||
(用户 2026-10-16 报告:拖动线宽滑块无变化、松手后变了、再移动鼠标又变回去)。
|
||||
新增任何影响顶点/包络几何的样式时,必须同步加入缓存键。
|
||||
100. ⚠️ **“样式补丁”(`RepaintSceneCachePatch`)之后必须同步重建受影响的宽基底图层**
|
||||
(`QueueLaneRebuild(patchLanes)` + `RenderPendingLaneRebuilds(1)`):
|
||||
补丁只更新“场景缓存 + 后备缓冲区”,而每网格图层(宽基底)里仍是旧样式像素,
|
||||
下一帧命中“宽基底快路径”时会把新样式**覆盖回旧像素**(同上一条的第二个根因)。
|
||||
101. ⚠️ **`PopupMenu` 的“限高 + 可滚动 + 尾部固定项”**(`MenuLayoutConstraint::maxHeight`):
|
||||
固定区 = **最后一个分隔线及其之后的条目**(如「新建网格」)钉在菜单底部,其余条目可滚动;
|
||||
⚠️ 滚动区文本必须用 `UiTextClipped`(GDI 不遵循 `SetClip`)、**跳过完全不可见的条目**(性能);
|
||||
`ItemAtPoint` 要求滚动区条目同时命中**滚动视口**;
|
||||
⚠️⚠️ `BuildLayout` **不得无条件清零 `m_scrollOffset`**(`RebuildLayout` 会重走它 ⇒ 滚轮“滚不动”)。
|
||||
`Application` 在菜单内滚动滚轮时**不关闭菜单**(其余情形仍是“滚轮关菜单”)。
|
||||
102. ⚠️ **“点选信号 ⇒ 绘图区滚到目标网格”只在点击时生效**(`RequestPlotLaneScroll` /
|
||||
`ApplyPendingPlotLaneScroll` / `PlotView::ScrollLaneIntoView`):**悬停绝不改变视图**;
|
||||
目标网格已完整可见时**不动**。“目标”行 GX 标签点击 = 打开网格菜单(不滚动),悬停仍联动高亮。
|
||||
103. ⚠️ **交互类缺陷的离屏探针必须“每帧重绘”驱动**(动画 / 分帧推进 / 自持帧):
|
||||
面板 `Paint` → `EnsureRows()`、绘图区 `Paint` → 预算推进,都会在重绘时**顺手修复**一部分
|
||||
中间态;只在动作结束后才重绘会把“完成帧”的缺陷掩盖掉(本轮滚动条缺陷就因此假绿过一次)。
|
||||
|
||||
### 11.3 离屏验证方法学(本机偏好,强烈建议沿用)
|
||||
|
||||
|
||||
Reference in new issue
Block a user