fix(ui): 选区截取面板空白真正根因、拖拽指示线层序、新建分组修复、避让动画更顺

一、选区截取面板「条目区与按钮之间的空白」(连续三轮未修好的那个)
根因不是内容区几何,而是**底部固定区高度算了两行**:footerHeight 用
`buttonHeight * 2 + 6 + pad` 预留,而布局把「截取为新信号 / 原始信号截取」
两个按钮并排画在同一行 ⇒ 内容区下边界与按钮上边界之间空出一整行。
改为按一行算(两处:高度算式分支 + 布局几何分支同步修改)。
数值核对(scale=1.25、1 条可见信号):GAP 53.8px → 8.8px(= 一个 pad 的预期留白)。

二、拖拽插入指示线不再盖住被拖条目
PaintSignalDragGhost 的绘制顺序改为「插入指示线 → 阴影 → 幽灵行 → 描边」,
指示线始终位于跟随鼠标的那部分之下。

三、「移动到…」二级菜单
* 条目文案改为「组号 | 组名」(不再是括号形式)。
* 修复「新建分组」点了没反应:DataSet::AddGroup 拒收「无信号且无时间轴」的空分组,
  旧实现「先建空组再搬信号」必然在建组那一步失败。新增
  DataSet::MoveSignalToNewGroup 把建组 + 搬运时间轴 + 搬运信号做成原子操作(失败回滚)。
* TabConfig::RemapAfterSignalMove 在目标分组是新建时自行扩展 m_visible
  (调用方不再先 SyncToDataSet —— 那次同步会按旧下标清理引用)。
* Application::MoveSignalCore 增加 newGroupName 参数并返回 bool,避免失败仍报"已移动"。

四、信号拖拽避让动画更流畅
* 插值系数按真实帧间隔计算:1 - exp(-dt/22ms),dt 夹在 1~60ms,帧间隔抖动时不再忽快忽慢。
* 鼠标移动事件里也推进一次插值,快速拖动时让位效果跟得上指针。

验证:删 .o 全量重编译零 error / 零 warning,产物 875,008 B(< 1MB 预警阈值)。
本轮未占用鼠标/键盘/屏幕,未运行任何 GUI 探针;面板空白改为纯数值算式核对。
规则已同步到 Documents/agent_mem.md(含"改面板几何必须数值验证"的教训条目)。
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-11-03 20:09:01 +08:00
1 parent 97b361dedd
commit 328a3b3b59
10 files changed
+259 -75

No files matched your search

+52
View File
@@ -10112,3 +10112,55 @@ MEAN_SCROLL / 滚轮 / 悬停 / `SetLanePanelCollapsed` / `LanePanelCollapsed` /
* 交互注意:`BeginSignalDrag` 只在**行主体**(else 分支)登记;`OnLButtonUp` 最前面先判拖拽;
`OnDoubleClick` 一开始就 `CancelSignalDrag()`(否则改名后轻微移动会把信号拖走);
`InvalidateRows()` 必须清空全部拖拽状态(行下标与逐行位移都会失效)。
---
## 2026-11-03 · 第 30 轮(选区截取空白真正的根因 / 拖拽排序打磨 / 新建分组)
> ⚠️ **教训(必须遵守)**:第 29 轮我向用户报告「已修复空白」,但只做了「高度算式与内容几何同源」
> 这一步,**没有核对底部固定区的高度与实际按钮几何是否一致**,于是连续三轮没修好。
> 今后凡是改**面板几何/布局**:必须把算式在纯数值层面跑一遍(python 或纯控制台程序,
> 不启动 GUI),打印出「内容区下边界 vs 按钮上边界」这类**具体像素值**再下结论。
### 1. ⭐⭐⭐ 选区截取面板「条目区与按钮之间的空白」——真正的根因
**根因**:底部固定区高度 `footerHeight` 按**两行按钮**预留
(`buttonHeight * 2 + 6 + pad`),但布局阶段把「截取为新信号 / 原始信号截取」两个按钮
**并排放在同一行**(`buttonTop = top + height - pad - buttonRowHeight`)。
⇒ 内容区下边界与按钮上边界之间恰好空出 `buttonHeight + 6` 一整行。
**现行规则**:
* 两个按钮**并排一行**,所以 `footerHeight = kEdgePanelButtonHeight * m_scale + pad`(**一行**)。
* `footerHeight` 在**两处**(高度算式分支 + 布局/几何分支)必须同为一行,改一处必改另一处。
* 数值验证(`scale = 1.25`、1 条可见信号):
修前 `GAP = 53.8px`(截图里的空白),修后 `GAP = 8.8px` = 一个 `pad`(预期留白)。
### 2. 拖拽插入指示线不得盖住被拖条目
用户要求:指示线**始终在被跟随鼠标绘制的那部分之下**。
⇒ `PaintSignalDragGhost` 内的绘制顺序固定为:**插入指示线 → 阴影 → 幽灵行 → 强调色描边**。
(旧实现把指示线放在最后,条目被拖到落点附近时蓝线会横穿条目本体。)
### 3. 「移动到…」二级菜单两个问题
* 条目文案从 `分组 N(组名)` 改为 **`N | 组名`**(组号 + 竖线 + 组名)。
* **「新建分组」点了没反应**:`DataSet::AddGroup` 的守卫是
`if (group.signals.empty() || group.axes.empty()) return false;`
⇒ 「先 AddGroup 建空组、再搬信号」**必然在建组那一步就失败返回**。
(用户的推测——空分组被当作不需要而拒收——完全正确。)
**修法**:新增 `DataSet::MoveSignalToNewGroup(from, signal, name, &groupIndex, &signalIndex)`,
把「建组 + 搬运时间轴 + 搬运信号」做成一次原子操作(失败则回滚空组)。
⚠️ 不要再去改 `AddGroup` 的守卫:它服务加载器/派生信号,放宽会让空分组到处出现。
* 目标分组是**新建**的(`m_visible` 还没有它那一行):
`TabConfig::RemapAfterSignalMove` 必须**自行扩展** `m_visible`(`resize(toSlot + 1)`)。
⚠️ 调用方**不能**先 `SyncToDataSet` 来补列 —— 那次同步会按**旧下标**清理/修正引用,
随后下标一变更就指向错误的信号。
* `Application::MoveSignalCore` 增加可选参数 `newGroupName`(`toGroup < 0 && 非空` = 新建分组),
并返回 `bool`,调用方据其结果决定是否报告「已移动」。
### 4. 拖拽避让动画更流畅
* 插值系数改为按**真实帧间隔**求:`factor = 1 - exp(-dt / 22ms)`(`dt` 夹在 1–60ms)。
固定系数在「鼠标移动帧(3–5ms)+ 定时器帧(约 10ms)」混杂时会忽快忽慢。
* `UpdateSignalDrag` 里在更新落点后**再推进一次插值**:快速拖动时不必等宿主定时器那 10ms。
* 快照阈值 0.4px(避免残差抖动)。