feat(UI): 布局边界改为四角粗/四边细包角 + 全元素覆盖 + 几何同源(RowRect O(1))

## 1. 边界观感:四角粗、四边细

用户反馈"边框就是实心矩形、粗细不一,效果不佳"。旧实现是四条等宽实心矩形
(层级 0 用 2px、其余 1px),同一元素的角与边粗细不一致,父子矩形共边时还会叠成更粗的一条。

新规则:
- 四条边固定 1 设备像素(四条独立实心矩形;不用 DrawRectangle,笔宽奇偶会偏半像素);
- 四个角额外加粗:层级 0 = 4px、其余 3px,沿水平/垂直各延伸 cornerLen(≤14px 且 ≤ 短边一半);
- 层级 > 0 整体内缩 1px,父子矩形共边不再叠粗;
- 绝不填充(几十层嵌套填充会把内容糊住);
- 整像素对齐 + PixelOffsetModeNone + 关抗锯齿。

四角粗不只是观感:多条边重合时靠角的形状分辨层次(父=青蓝大包角、子=同位置品红小包角)。

## 2. 协议:组件"收集 box",宿主统一绘制

UiComponent::CollectDebugBoxes(UiDebugBoxList&)  默认只登记 Area()
UiWidget::CollectDebugBoxes(boxes, depth)        递归整棵控件树
Application::PaintLayoutBounds                   收集后按 depth 升序一次性描边

刻意保持 box 为**值类型**而不是让它继承控件基类:PlotView 的 4 层位图缓存、
SignalPanel 的 192 槽行位图缓存都要求"绘制由组件自己负责",几何进继承体系会与
缓存键/失效体系打架。box 不参与绘制,因此能让绘制/命中/调试共用同一份几何。

## 3. 覆盖补齐(此前大量元素被裹在外层单位里)

- Toolbar:区域 / 每个按钮 / **按钮内容(文字或齿轮图标,新增 UiButtonWidget::ContentRect())**
- TabBar:区域 / 标签条 / 新建按钮 / 每个 ✕
- SignalPanel:4 分区 + 筛选输入框/清空✕/全部显示 + 滚动轨道与滑块
  + 样式区 5 行及其 12 色块/自定义/3 模式/3 调整/线宽与透明度轨道和数值
  + 目标行/名称/GX 徽标 + **每个可见行**的折起三角/复选框/色块/名称/GX 徽标/✕
  (组行另有组名/计数)+ 内联编辑框
- PlotView:区域 / 每个网格 / GX 徽标 / 装饰层矩形 / 图例托底+色块+文本
  / **5 类浮动面板全部 20+ 字段**(标题/✕/折起三角/徽标/编辑行/按钮/内容区/滚动轨道与滑块
  /透明度轨道与数值/两个开关/各行复选框与数值与滑轨与复位与名称与✕/分组栏/表头/固定点行)
- PopupMenu:浮层 / 每个条目 / 可滚动可视区 / 轨道 / 滑块 / 二级子菜单与条目
- ColorPickerPopup:面板 / 标题 / 预览 / 色号 / 色域 / 色相条 / 3 条滑轨与数值 / 输入框

标签增加**尺寸门槛**(< 56×16 逻辑像素不画):复选框/色块/✕ 紧邻,逐个小框贴字会糊成一片。

## 4. 设置按钮配色 + 齿轮图标

- ToolbarAction::SETTINGS 改为 primary = true,与「导入文件」同为强调色主按钮。
- 齿轮几何重做:齿占最外一圈(0.66R→1.0R)、外环中心线 0.54R/笔宽 0.24R、轴孔 0.17R。
  旧版让齿从环中心线伸出,齿被环的描边吃掉 ⇒ 看起来只剩一个"甜甜圈"。

## 5. 几何同源与性能

- RowRect(row) 原本每次调用都从头累加行高(O(row)),而画一行要调它 **5 次**
  (✕/徽标/名称/组计数/折起三角各一次)⇒ 200 行只见 30 行时每帧上万次加法,
  命中测试(每次鼠标移动)同样走这条路。
  新增 m_rowTop 前缀和(由 UpdateAnimGeometry() 统一重建)⇒ **O(1)**;
  长度与行集合不一致时自动退回逐行累加(正确性优先)。
- ContentOffsetBeforeRow 同样改用前缀和。
- PanelBounds() 收敛 6 处重复的 (m_clientBounds.Width > 1.0f) ? m_clientBounds : m_area。

## 6. 验证

- make release / make debug 零 error 零 warning;TargetCheck.exe 910,336 B(889 KiB < 900 KiB)。
- .preflight/lbcheck:21 / 21 PASS(含 bounds-off-restores-pixels:关闭后逐像素回到开启前)。
- Tools/rendercheck:「工具栏带以下」区域哈希 8/8 与上一轮**逐一相同**
  (证明本轮改动只落在工具栏带内);FUNC 断言 7/7 PASS。
- Tools/uicheck:69 passed / 0 failed。

## 7. 文档

Documents/UI_Refactor_Plan.md / UI_Refactor_Progress.md / UI_Refactor_Report.md 追加第七阶段;
Documents/agent_mem.md 同步变更日志与「2026-10-28 第九轮」硬规则与踩坑记录。
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-10-28 16:28:57 +08:00
1 parent ab19a6e72e
commit 24706c906a
26 files changed
+1003 -163

No files matched your search

+40
View File
@@ -442,3 +442,43 @@ protected:
3. `SignalPanel` 的复选框 / 徽标 / ✕ 等行内子块(现在只到"行"这一层)。
4. 开关状态目前**不持久化**(重启恢复为关闭);若要持久化需要引入配置存储。
5. 菜单只有一项;后续设置项按 `HEADER` 分组追加即可。
---
# 第七阶段计划(2026-10-28 第九轮):边界观感重做 + 覆盖补全 + 几何同源
> 用户反馈:① 设置齿轮按钮的颜色应与「导入文件」一致;
> ② 边界绘制"就是实心矩形、粗细不一",希望是**四角粗、其他边细**;
> ③ 按上一轮总结的覆盖率问题着手改进(很多元素没有边界)。
## 任务清单
| # | 任务 | 验证手段 |
| --- | --- | --- |
| P7-1 | 齿轮按钮改为主按钮(`primary = true`),与「导入文件」同色 | 探针断言两按钮底色一致 |
| P7-2 | 边界的描边改为**四角粗(3–4px)+ 四边细(1px)**的包角样式;只描边不填充 | 逐像素探针 + 人工核对 |
| P7-3 | 层级 > 0 的 box 内缩 1px,避免与父矩形共边时叠成粗线 | 同上 |
| P7-4 | 协议从「组件自己画」改为「组件**收集 box**、宿主统一画」(`CollectDebugBoxes`) | 编译 + 探针 |
| P7-5 | `SignalPanel`:行内元素全部上报(复选框/色块/名称/GX 徽标/✕/折起三角/组名/计数)+ 筛选框/清空✕/滚动条/滑块/样式区 5 行与行内控件/目标行/编辑框 | 探针断言 box 数量 |
| P7-6 | `PlotView`:GX 徽标、装饰层矩形、图例条目(托底/色块/文本)、5 类浮动面板全部字段 | 同上 |
| P7-7 | `Toolbar`:按钮内部**文本/图标**矩形(新增 `UiButtonWidget::ContentRect()`) | 同上 |
| P7-8 | `PopupMenu` / `ColorPickerPopup`:条目、滚动区/轨道/滑块、子菜单;拾色器各控件 | 同上 |
| P7-9 | `RowRect` 从 **O(row) → O(1)**(行顶前缀和 `m_rowTop`) | 行为不变(像素哈希) |
| P7-10 | `PanelBounds()` 收敛 6 处重复三元表达式 | 编译 |
| P7-11 | 三份文档 + `agent_mem.md` 同步;`TargetCheck.exe` < 900 KiB | 构建 |
## 风险与对策
| 风险 | 对策 |
| --- | --- |
| 前缀和与行集合短暂不一致 | `RowRect` / `ContentOffsetBeforeRow` 检测长度后**退回逐行累加**(正确性优先) |
| box 数量暴涨导致调试帧变慢 | 只在开关打开时执行;标签设**尺寸门槛**(< 56×16 逻辑像素不画标签) |
| 调试层污染位图缓存 | 仍只在 `Render` 最后一步叠加,探针 `bounds-off-restores-pixels` 钉住 |
| 浮层 box 需要读取私有几何 | 给两个浮层加 `CollectDebugBoxes`,几何仍由它们自己给出(不外泄成员) |
## 完成判据
* `make release` / `make debug` 零 error 零 warning;
* 「工具栏带以下」区域哈希与上一轮**逐一相同**(证明改动只落在工具栏带内);
* `lbcheck` 21/21、`rendercheck` FUNC 7/7、`uicheck` 69/69;
* 打开开关后:每行的复选框/色块/名称/GX 徽标/✕、每个网格的 GX 徽标与图例、每个浮层控件都有独立包角框。
+98
View File
@@ -401,3 +401,101 @@ uicheck 69 passed, 0 failed
`UiDebugBounds.cpp` / `UiComponent.cpp` 仍会被 Makefile 的 `Src/*.cpp` 通配扫到而编译失败。
* 「工具栏带以下」的起点必须用**运行时** `m_toolbarRect.Y + Height`;写死 70 会切进工具栏,
齿轮正好落在被统计的区间里,导致本应相同的哈希全都不同。
---
## 第九轮(2026-10-28):边界观感重做 + 覆盖补全 + 几何同源
### 进度总览
| # | 任务 | 状态 | 验证 |
| --- | --- | --- | --- |
| 1 | 齿轮按钮 `primary = true`(与「导入文件」同色) | ✅ | 截图核对(两按钮同为强调色) |
| 2 | 边界改为「四角粗 + 四边细」包角 | ✅ | 截图核对 |
| 3 | 层级 > 0 内缩 1px | ✅ | 同上 |
| 4 | 协议改为 `CollectDebugBoxes`(收集 → 宿主统一绘制) | ✅ | 编译 + 探针 |
| 5 | `SignalPanel` 行内元素全覆盖 | ✅ | box 计数 |
| 6 | `PlotView` GX 徽标 / 图例 / 5 类浮动面板 | ✅ | box 计数 |
| 7 | `Toolbar` 按钮内容矩形 | ✅ | 截图(文字/图标各有框) |
| 8 | `PopupMenu` / `ColorPickerPopup` 内部结构 | ✅ | 截图(菜单项有框) |
| 9 | `RowRect` O(row) → O(1) | ✅ | 8 场景区域哈希不变 |
| 10 | `PanelBounds()` 收敛 6 处重复 | ✅ | 编译 |
| 11 | 文档 + 共享记忆 | ✅ | — |
### 1. 描边样式:`⌜ ⌝ ⌞ ⌟` 包角
之前是四条**等宽实心矩形**(层级 0 用 2px、其余 1px),所以同一元素的"角"和"边"粗细不一致,
而且父子矩形共边时会叠成更粗的一条线。
现在:
| 部位 | 规则 |
| --- | --- |
| 四边 | 固定 **1 设备像素** |
| 四角 | 额外加粗(层级 0 = 4px,其余 3px),沿水平/垂直各延伸 `cornerLen`(≤14px,且不超过短边一半) |
| 内缩 | 层级 > 0 整体内缩 **1px**,与父矩形共边时不再叠粗 |
| 填充 | **完全不填充**(几十层嵌套填充会把内容糊住) |
实现要点:整像素对齐 + `PixelOffsetModeNone` + 关抗锯齿,四条边与八个角段全部用
`FillPixels` 实心矩形画(`DrawRectangle` 会因笔宽奇偶偏移半像素)。
### 2. 覆盖率:从"一根大框"到"逐元素"
协议改成**两段式**:组件只负责"收集 box",宿主统一描边。
```
UiComponent::CollectDebugBoxes(UiDebugBoxList&) 默认只登记 Area()
UiWidget::CollectDebugBoxes(boxes, depth) 递归整棵控件树
宿主 PaintLayoutBounds 收集完一次性画(按 depth 升序)
```
新增覆盖:
| 组件 | 本轮新增的 box |
| --- | --- |
| `Toolbar` | 每个按钮 + **按钮内容**(文本/齿轮图标,居中矩形) |
| `TabBar` | 标签条 / 新建按钮 / 每个 ✕ |
| `SignalPanel` | 表头 / 筛选框 / 列表 / 样式区;筛选输入框 + 清空 ✕ + 全部显示;滚动条轨道 + 滑块;样式 5 行 + 12 个色块 + 自定义 + 3 个模式按钮 + 3 个调整按钮 + 线宽/透明度轨道与数值;目标行 + 名称 + GX 徽标;**每行**(仅可见)+ 折起三角 + 复选框 + 色块 + 名称 + GX 徽标 + ✕ / 组名 + 计数;内联编辑框 |
| `PlotView` | 每个网格 + GX 徽标 + 装饰层矩形;图例条目(托底/色块/文本);5 类浮动面板的面板/标题/✕/折起三角/徽标/编辑行/按钮/内容区/滚动轨道/滑块/透明度轨道与数值/两个显示开关 + 各行复选框/数值/滑轨/复位/名称/✕ + 分组栏/表头/三角/✕ + 每个固定点行与 ✕ |
| `PopupMenu` | 菜单浮层 + 每个条目 + 可滚动可视区 + 轨道 + 滑块 + 二级子菜单与其条目 |
| `ColorPickerPopup` | 面板 + 标题/预览/色号/色域/色相条 + 3 条分量滑轨与数值 + 输入框 |
**标签做尺寸门槛**:小于 56×16 逻辑像素的 box 不画文字标签(复选框/色块/✕ 紧邻,
逐个小框贴字会糊成一片;它们的归属靠包角形状与颜色层级就能分辨)。
### 3. 几何同源与性能
* `RowRect(row)`:原来每次都从头累加行高(**O(row)**),而画一行要调它 **5 次**
(✕ / 徽标 / 名称 / 组计数 / 折起三角)。新增 `m_rowTop` 前缀和,由
`UpdateAnimGeometry()` 统一重建 ⇒ **O(1)**;长度不一致时自动退回逐行累加(正确性优先)。
* `ContentOffsetBeforeRow()` 同样改用前缀和。
* `PanelBounds()` 收敛 `(m_clientBounds.Width > 1.0f) ? m_clientBounds : m_area`
在 6 处的重复。
### 4. 验证数据
```
make release / make debug 零 error 零 warning
TargetCheck.exe 910,336 B(889 KiB,< 900 KiB)
.preflight/lbcheck 21 passed, 0 failed
INFO area-pixels=16080 node-pixels=20629
uicheck 69 passed, 0 failed
rendercheck 「工具栏带以下」区域哈希(与上一轮逐一相同):
AB AABF558F499583EA BB 1B8B2D389D41B988 CB CAC17AA78C515079 DB 154617738CD9C422
EB DA484B02EEEC778A FB B3CE740F60CF3688 GB 4DD14C0C2EC4CE91 HB D84400B723089F43
rendercheck FUNC 7/7 PASS
```
> 整帧哈希必然变化(齿轮变主色 + 图标重画),因此继续用「工具栏带以下」的区域哈希
> 证明"改动被完全限制在工具栏带内"。
### 5. 本轮踩到的坑
* `UiDebugPaintBounds` 的旧签名带 `scale` 参数,但线宽刻意不随 DPI 变 —— 重构时应当**删掉这个
参数**而不是留个 `(void)scale`(已删)。
* 用 PowerShell 按行替换大段代码时,`while ($lines[$end].TrimEnd() -ne "}")` 找结尾会失败:
文件里的闭括号带**前导缩进**,必须用 `Trim()` 而不是 `TrimEnd()`;否则会越界并报
`InvokeMethodOnNull`,而且**已经把开头删掉了**,必须在替换后逐个核对括号配对。
* 齿轮图标在 22px 时,"齿从环中心线伸出"会让齿被环的描边吃掉,看起来只剩一个甜甜圈。
必须让**齿占最外一圈、外环缩到 0.54R**,齿才会明显。
* 中文不能写进 `.ps1`(PS 5.1 按 GBK 读),文本块要落到 UTF-8 数据文件再由 ASCII 脚本拼接。
+89
View File
@@ -350,3 +350,92 @@ lbcheck 21/21 · rendercheck FUNC 7/7 · 区域哈希 8/8 相同 · uicheck 69/6
两者的几何都已经集中,属于"再插一个循环"的量级。
3. 开关状态不持久化 —— 重启回到关闭。
4. 设置菜单目前只有一项,但分组结构(`HEADER`)已经就位,后续按组追加即可。
---
# 第七阶段报告:把"布局边界"从一根框变成一张可以逐元素对照的几何图
## 1. 上一版为什么不理想
两个独立的问题被混在一起:
1. **观感**:四条边等宽(层级 0 用 2px、其余 1px),同一个元素的"角"比"边"粗;
父子矩形共边时两条同色线叠在一起 ⇒ 更粗。于是看起来"就是包了个边、粗细不一"。
2. **覆盖**:`PlotView` / `SignalPanel` 的内部元素全是"按需计算的矩形函数",
没有任何代码把它们交给调试层 ⇒ 整块区域只有一个大框,"复选框、信号名、GX、✕"
全被裹在外层单位里。
第 2 点并不是调试功能的缺陷,而是**几何没有统一出口**的症状。本轮把它一次解决。
## 2. 描边:四角粗、四边细
```
边:固定 1 设备像素(4 条独立实心矩形,非 DrawRectangle —— 后者会偏移半像素)
角:层级 0 = 4px、其余 3px,沿水平/垂直各延伸 cornerLen(≤14px 且 ≤ 短边一半)
内缩:层级 > 0 整体内缩 1px,父子共边不再叠成粗线
填充:无
```
包角的意义不只是好看:**多条边重合时,靠"角的形状"就能分辨层次**(父是青蓝大包角、
子是同位置的品红小包角),这是纯直线做不到的。
## 3. 覆盖率:从"组件自己画"改成"组件收集、宿主统一画"
```cpp
class UiDebugBoxList; // 值类型容器,装 {rect, depth, tag}
virtual void CollectDebugBoxes(UiDebugBoxList&); // UiComponent 默认只登记 Area()
void UiWidget::CollectDebugBoxes(boxes, depth); // 递归整棵控件树
Application::PaintLayoutBounds(...) // 收集完一次性按 depth 升序描边
```
**为什么不是"所有元素都继承同一个基类"**:`PlotView` 的 4 层位图缓存、
`SignalPanel` 的 192 槽行位图缓存,都要求"绘制由组件自己负责"。
把几何塞进继承体系会与缓存体系打架(谁脏了谁重画、缓存键里要不要混调试状态……)。
而 box 是**纯值类型**,只描述"哪里有一个矩形",不参与绘制 —— 于是
**几何可以被绘制、命中与调试共用**,还不需要任何生命周期管理。
这正是项目里已有三处雏形(`m_decorRects` / `m_legendHits` / `m_freeMarkerHits`,
即"绘制时登记、命中时读取")的正式版本。
## 4. 覆盖率数据
| 组件 | 覆盖内容 |
| --- | --- |
| `Toolbar` | 区域 / 每个按钮 / **按钮内容(文字或齿轮图标)** |
| `TabBar` | 区域 / 每个标签条 / 新建按钮 / 每个 ✕ |
| `SignalPanel` | 4 个分区 + 筛选框与清空✕ + 全部显示 + 滚动轨道/滑块 + 样式区 5 行及其 12 色块/自定义/3 模式/3 调整/线宽与透明度轨道和数值 + 目标行/名称/GX + **每个可见行**的折起三角/复选框/色块/名称/GX/✕(组行另加组名/计数)+ 内联编辑框 |
| `PlotView` | 区域 / 每个网格 / GX 徽标 / 装饰层矩形 / 图例托底 + 色块 + 文本 / 5 类浮动面板的 20+ 个字段(面板、标题、✕、折起三角、徽标、编辑行、按钮、内容区、滚动轨道与滑块、透明度轨道与数值、两个开关、各行复选框/数值/滑轨/复位/名称/✕、分组栏/表头/三角/✕、每个固定点行与 ✕) |
| `PopupMenu` | 浮层 / 每个条目 / 可滚动可视区 / 轨道 / 滑块 / 二级子菜单与其条目 |
| `ColorPickerPopup` | 面板 / 标题 / 预览 / 色号 / 色域 / 色相条 / 3 条分量滑轨与数值 / 输入框 |
标签增加了**尺寸门槛**(< 56×16 逻辑像素不画):复选框、色块、✕ 紧邻,逐个小框贴字会糊成一片。
## 5. 顺带的几何同源与性能
`RowRect(row)` 是 **O(row)** 的逐行累加,而画一行要调它 **5 次**(✕ / 徽标 / 名称 / 组计数 /
折起三角各一次)——200 行列表只见 30 行时每帧上万次加法,命中测试每次鼠标移动同样走这条路。
本轮加了 `m_rowTop` 前缀和(由 `UpdateAnimGeometry()` 统一重建),查询变 **O(1)**,
长度不一致时自动退回逐行累加以保证正确性。
另外把 `(m_clientBounds.Width > 1.0f) ? m_clientBounds : m_area` 在 6 处的重复收敛成
`PanelBounds()`;齿轮按钮改为主按钮配色,与「导入文件」构成一组文件/设置入口;
齿轮图标重画(齿占最外一圈、外环缩到 0.54R),否则齿会被环的描边吃掉只剩"甜甜圈"。
## 6. 验证
```
make release / make debug 零 error 零 warning
TargetCheck.exe 910,336 B(889 KiB)
lbcheck 21/21 PASS(含"关闭后逐像素还原")
rendercheck 「工具栏带以下」区域哈希 8/8 与上一轮逐一相同 + FUNC 7/7
uicheck 69/69
```
## 7. 遗留
1. 仍有两类几何没有上报:`PlotView` 的坐标轴刻度带、`SignalPanel` 的样式区滑块**滑块**(只有轨道)。
2. `m_decorRects` 按"是否落在该网格纵向范围内"归属,跨网格时可能归到相邻网格;
调试用途可接受,若要精确应改成绘制时就带上 laneIndex。
3. 开关状态仍未持久化。
4. `TargetCheck.exe` 已接近 900 KiB(889 KiB),`CollectDebugBoxes` 的文字常量与
`std::to_wstring` 是主要增量;若继续增长需要把标签文本改回静态字面量并去掉动态编号。
+73
View File
@@ -3029,6 +3029,8 @@ else if (panelOnly) { /* 只回送“本帧重绘过的区域并集” */ }
| 2026-10-28 | DeepSeek-V4.1-Flash(**UI_Refactor 分支**) | **「显示布局边界」调试开关(Android 风格)+ 工具栏设置菜单(齿轮)+ 三处可见收益优化(详见 §12 末尾「2026-10-28 第八轮」)**。① **齿轮按钮**:`ToolbarAction::SETTINGS` 位于「导入文件」左侧(工具栏布局改为「左侧固定簇 `kLeftClusterCount=2` + 右侧组」),矢量化绘制(8 齿 + 外环 + 轴孔,按 `half` 比例推导;不用字体字符 `⚙`,因为不同回退字体大小/基线不一致)。② **设置菜单**:`MenuScope::APP_SETTINGS` + `HEADER "调试"` + 勾选切换项「显示布局边界」(`keepOpen`,点击后菜单保持打开、勾选态回写);锚点取 `Toolbar::ButtonRect(SETTINGS)` 的**控件实测矩形**。③ **布局边界**:`UiComponent::PaintDebugBounds(g,scale)` 虚函数(默认只画 `Area()`),`Toolbar`/`TabBar` 跳过根控件上报控件树子节点、`SignalPanel` 上报表头/筛选框/列表/样式区 + 可见行、`PlotView` 上报每个网格区域,宿主补客户区外框/分隔条/浮层;`UiWidget::PaintDebugBounds(g,scale,depth)` 递归整棵树。**只描边不填充**、线宽按设备像素固定、临时 `PixelOffsetModeNone` + 关抗锯齿 + 整像素对齐;层级配色 青蓝/品红/琥珀/绿。⚠️ 调试层**绝不进任何位图缓存**(只在 `Render` 最后一步叠加),且开启时 `fullFrame` 必须并入开关(否则只回送绘图区会让装饰区边界留在旧帧)。④ **可见收益优化**:`Toolbar::Layout` 控件树改按需重建(`m_treeDirty`,拖分隔条每帧不再重建);`SetActionLabel` 只写覆盖表(消除双写不同步);新增 `UiWidget::DispatchRedraw` 收敛 `Dispatch()+TakeDirty()` 重复写法。验证:新增离屏探针 `.preflight/lbcheck` **21/21**(含"关闭后逐像素还原""菜单锚定/勾选翻转");`rendercheck` **工具栏带以下区域哈希 8/8 与改造前逐一相同**(整帧哈希因新增按钮整体变化,属预期)+ FUNC 7/7;`uicheck` 69/69;`make release/debug` 零警告;`TargetCheck.exe` **897,024 字节(< 900KB)**。 |
| 2026-10-28 | DeepSeek-V4.1-Flash(**UI_Refactor 分支**) | **布局边界观感重做(四角粗/四边细)+ 全覆盖 + 几何同源(详见 §12 末尾「2026-10-28 第九轮」)**。① **描边规则改了**:四条边固定 **1 设备像素**,四个角加粗(层级 0 = 4px、其余 3px,沿边延伸 ≤14px 且 ≤ 短边一半),层级 > 0 整体**内缩 1px**(父子共边不再叠粗),**绝不填充**;整像素对齐 + `PixelOffsetModeNone` + 关抗锯齿。② **协议改为「收集 → 统一绘制」**:`UiComponent::CollectDebugBoxes(UiDebugBoxList&)`(默认只登记 `Area()`)+ `UiWidget::CollectDebugBoxes(boxes, depth)` 递归控件树;**不要**改成"所有元素继承同一基类"(会与 `PlotView` 4 层缓存 / `SignalPanel` 行位图缓存的失效体系打架)。③ **覆盖补齐**:`Toolbar` 按钮内容(新增 `UiButtonWidget::ContentRect()`)、`TabBar` ✕、`SignalPanel` 每个可见行的折起三角/复选框/色块/名称/GX 徽标/✕(组行另有组名/计数)+ 筛选框/清空✕/滚动轨道与滑块/样式 5 行与 12 色块/3 模式/3 调整/线宽与透明度轨道和数值/目标行/编辑框、`PlotView` GX 徽标/装饰层矩形/图例托底+色块+文本/**5 类浮动面板全部 20+ 字段**、`PopupMenu` 条目/可视区/轨道/滑块/二级子菜单、`ColorPickerPopup` 各控件。标签设**尺寸门槛**(< 56×16 逻辑像素不画)避免糊字。④ **性能**:`RowRect` 曾是 O(row) 逐行累加且画一行调 5 次 ⇒ 改用 `m_rowTop` 前缀和(`UpdateAnimGeometry()` 重建,长度不一致时退回累加)⇒ O(1);`ContentOffsetBeforeRow` 同步;`PanelBounds()` 收敛 6 处重复三元式。⑤ **齿轮按钮改主按钮配色**(与「导入文件」一致),图标重画(齿占最外一圈 0.66R→1.0R、外环 0.54R,否则齿被环描边吃掉只剩甜甜圈)。验证:`lbcheck` 21/21(含关闭后逐像素还原)、`rendercheck` 「工具栏带以下」区域哈希 **8/8 与上一轮逐一相同** + FUNC 7/7、`uicheck` 69/69、零警告、`TargetCheck.exe` **910,336 字节(889 KiB)**。 |
## 11. 跨设备协作须知(起源设备视角)
> **本文件是唯一权威记忆**。所有设备 / Agent 共用它,**不要再新建 `agent_mem_origin.md` 等旁支记忆文件**。
@@ -8056,3 +8058,74 @@ rendercheck FUNC 7/7 PASS; uicheck 69 passed
* 区域哈希起点必须用**运行时** `m_toolbarRect.Y + Height`;写死 `70` 会切进工具栏,
齿轮正好落在统计区间内,导致本应相同的哈希全都不同。
* 图标不要用字体字符 `⚙`(不同回退字体大小/基线不一致);用 GDI+ 矢量按 `half` 比例画。
---
## 本轮(2026-10-28 第九轮,本机):布局边界观感重做(四角粗/四边细)+ 全覆盖 + 几何同源
### 1. 边界描边的硬规则(**再改动必须守住**)
| 部位 | 规则 |
| --- | --- |
| 四边 | 固定 **1 设备像素**(四条独立实心矩形;**不要用 `DrawRectangle`**,笔宽奇偶会偏半像素) |
| 四角 | 额外加粗:层级 0 = **4px**、其余 **3px**,沿水平/垂直各延伸 `cornerLen`(≤14px 且 ≤ 短边一半) |
| 内缩 | 层级 > 0 整体内缩 **1px** ⇒ 父子矩形共边时不会叠成粗线 |
| 填充 | **绝不填充**(几十层嵌套填充会把内容糊住) |
| 对齐 | 整像素对齐 + `PixelOffsetModeNone` + 关抗锯齿 ⇒ 1px 就是 1px |
「四角粗」不只是观感:**多条边重合时靠角的形状分辨层次**(父=青蓝大包角、子=同位置品红小包角)。
### 2. 协议:组件"收集 box",宿主统一绘制
```
class UiDebugBoxList; // 值类型:{rect, depth, tag}
virtual void UiComponent::CollectDebugBoxes(UiDebugBoxList&); // 默认只登记 Area()
void UiWidget::CollectDebugBoxes(boxes, depth); // 递归整棵控件树
Application::PaintLayoutBounds(...) // 收集完按 depth 升序一次性描边
```
⚠️ **不要**改成"所有元素继承同一基类":`PlotView` 4 层位图缓存 / `SignalPanel` 192 槽行位图缓存
要求绘制归组件自己,几何进继承体系会与缓存键/失效体系打架。box 是值类型、不参与绘制,
所以能让**绘制/命中/调试共用同一份几何**且零生命周期成本。
### 3. 覆盖清单(本轮补齐)
- `Toolbar`:区域 / 按钮 / **按钮内容(文字或齿轮)**
- `TabBar`:区域 / 标签条 / 新建按钮 / 每个 ✕
- `SignalPanel`:4 分区 + 筛选框/清空✕/全部显示 + 滚动轨道/滑块 + 样式 5 行与 12 色块/自定义/3 模式/3 调整/线宽与透明度轨道和数值 + 目标行/名称/GX + **每个可见行**的折起三角/复选框/色块/名称/GX/✕(组行另有组名/计数)+ 内联编辑框
- `PlotView`:区域 / 每网格 / GX 徽标 / 装饰层矩形 / 图例托底+色块+文本 / **5 类浮动面板全部 20+ 字段**
- `PopupMenu`:浮层 / 条目 / 可视区 / 轨道 / 滑块 / 二级子菜单与条目
- `ColorPickerPopup`:面板 / 标题 / 预览 / 色号 / 色域 / 色相条 / 3 条滑轨与数值 / 输入框
标签有**尺寸门槛**(< 56×16 逻辑像素不画):小方框紧邻时逐个小框贴字会糊成一片。
### 4. 性能与一致性
- `RowRect(row)` 曾是 **O(row)** 逐行累加,而画一行要调 **5 次**(✕/徽标/名称/组计数/折起三角)
⇒ 200 行只看 30 行时每帧上万次加法,命中测试同样走这条路。
现在用 `m_rowTop` 前缀和(由 `UpdateAnimGeometry()` 统一重建)⇒ **O(1)**;
长度不一致时**退回逐行累加**(正确性优先)。
- `ContentOffsetBeforeRow()` 同样用前缀和。
- `PanelBounds()` 收敛 6 处 `(m_clientBounds.Width > 1.0f) ? m_clientBounds : m_area`。
### 5. 图标
* 齿轮按钮 `primary = true`(与「导入文件」同为强调色主按钮)。
* 齿轮矢量几何:**齿占最外一圈(0.66R→1.0R)、外环中心线 0.54R、笔宽 0.24R**。
⚠️ 齿若从环中心线伸出会被环的描边吃掉,只剩"甜甜圈"。
### 6. 验证
```
make release / make debug 零 error 零 warning;TargetCheck.exe 910,336 B(889 KiB)
.preflight/lbcheck 21/21(含 bounds-off-restores-pixels)
uicheck 69/69
rendercheck 「工具栏带以下」区域哈希 8/8 与上一轮逐一相同 + FUNC 7/7
```
### 7. 踩坑
* 用 PS 按行替换大段代码时,找函数结尾必须 `Trim() -eq "}"` 而不是 `TrimEnd()`:
文件里的闭括号带前导缩进,用 `TrimEnd()` 会越界(`InvokeMethodOnNull`)且**开头已被删掉**,
要在替换后逐个核对括号配对。
* 中文不能写进 `.ps1`(PS 5.1 按 GBK 读)⇒ 文本块落 UTF-8 数据文件、脚本保持纯 ASCII。