refactor(panels): 浮动面板抽出唯一"面板特征层",消除 3 处分类/滚动几何/滚动设置的重复
背景(用户观察):7 个实验性功能都有自己的浮动面板,观感与骨架高度一致
(标题栏 = 折起三角 + GX 徽标 + 标题 + ✕,内容区可滚动 + 滚动条,底部按钮行),
但每新增一种面板或改动布局,总会出"意料之外"的缺陷。
审计结论:骨架绘制本来只有一份,真正的问题是**同一套"按种类分支"的代码被抄了多份**:
| 重复项 | 位置 | 份数 |
| --- | --- | --- |
| 分类布尔量(listStyle / countTable / hasButton…) | EdgePanelWidth / EdgePanelLayoutFor / PaintEdgeCursor | 3 |
| 滚动条几何 + 滚动量夹取 | 布局里表格 / 列表 / 自由标记三个分支 | 3 |
| 滚动条绘制(6 行) | 绘制里同样三个分支 | 3 |
| SetLane*Scroll 实现 | PlotViewFeatures.cpp | 7 |
| 按种类转发滚动写入 | 滚轮 / 拖滚动条 | 2 |
| 标题文本 | EdgePanelWidth 的三元式链 / 绘制里的三元式链 | 2(内容还不一致!) |
一、新增"面板特征层"(PlotViewInternal.hpp,pvdetail)
* `PanelStyle PanelStyleOf(EdgePanelKind)`:唯一分类来源,`listRows` / `countTable` /
`markerGroups` + `buildButton` / `twoButtons` / `markerFooter`,`switch` 穷举不写 default
(新增面板漏登记 ⇒ `-Wswitch` 编译报警)。
其中专门区分 `footerButtons()`(底部有按钮行)与 `buildButton`(是"构建为新信号")——
混为一谈会让「选区截取」底部少预留一行高度(本轮实测 124 → 87 px,被像素基线当场抓住)。
* `PanelScrollMetricsFor(...)`:滚动条轨道/滑块几何 + 滚动量夹取 + 内容宽度扣减的唯一实现。
* `PlotView::PanelTitle(EdgePanelKind)`:标题唯一来源。
⚠️ 修掉一处**潜伏**错误:`EdgePanelWidth` 的标题链漏了 MARK/CLIP(两者按"边沿计数"量宽),
目前两种标题恰好等宽所以看不出来,但任何文案改动都会让宽度与标题不匹配。
二、收敛调用点
* `EdgePanelWidth` / `EdgePanelLayoutFor`(表格 / 列表 / 自由标记三分支)/ `PaintEdgeCursor`
全部改用 `PanelStyleOf` 与 `PanelTitle`;滚动几何改用 `PanelScrollMetricsFor`。
* `EdgePanelLayoutFor` 的 5 层滚动量三元式 → `LanePanelScrollOf(lane, kind)`(该派发点第 33 轮
就已存在,但布局这里一直没用它)。
* `PaintEdgeCursor` 的滚动条绘制抽成 `paintPanelScrollBar(layout)`,三处调用。
* 7 个 `SetLane*Scroll` 改为一行转发到新的 `SetLanePanelScroll(laneIndex, kind, scroll)`;
滚轮与"拖滚动条"两条 `switch`/`if` 链改为直接调它(读与写从此同一个入口)。
三、新增离屏检查 `panelscroll`(护栏)
* 对 7 种面板逐一:可滚动时滚到底 / 滚回顶,断言 `scrollOffset` 夹取到 `maxScroll`、
滑块到达/返回轨道端点、内容区几何不随滚动改变;不可滚动时断言不出现滚动条。
* 用 `MF4-Record.MF4`(566 信号合并到同一网格)实测 6/7 面板可滚(自由标记无固定点,正确)。
没有这条检查,本次收敛就等于"改完只能靠肉眼"。
验证(本机 GCC 15.2.0 UCRT / 125% DPI)
* `panels -mode compare`:7 个面板的**像素哈希与几何逐项不变**(零差异)。
这条检查当场抓到了上面 `footerButtons` 的回归,证明基线有效。
* `OffscreenChecks -all`:6 个检查全 PASS,`checks=87 failed=0`。
* `UnitTests`:`checks=219 failed=0`。
* release 全量零 error / 零 warning;产物 788,480 → **787,968 B**(−512)。
This commit is contained in:
1 parent
5c48bf922e
commit
abcf3e6c44
7 files changed
+416
-250
No files matched your search
@@ -1778,7 +1778,16 @@ namespace jdi
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlotView::SetLaneFreeMarkerScroll(int laneIndex, float scroll)
|
||||
/**
|
||||
* @brief 设置某种面板的**内容滚动偏移**(唯一实现,公开包装都在下面转发到这里)。
|
||||
*
|
||||
* ⚠️ 过去这里是七个几乎逐字重复的函数(自由标记 / 均值滤波 / 重采样 / 选区截取 /
|
||||
* 中心轴 / 弱峰 / 边沿计数),唯一差别是要写的字段名;调用侧还有两条 `if`/`switch`
|
||||
* 链再按种类转发一次。新增面板时必须在**三个**地方登记,漏一处就出现
|
||||
* "滚轮能滚、拖滚动条滚不动"或反之。
|
||||
* 现在字段一律经 `LanePanelScrollOf(lane, kind)` 取(唯一派发点),本函数是唯一写入点。
|
||||
*/
|
||||
bool PlotView::SetLanePanelScroll(int laneIndex, EdgePanelKind kind, float scroll)
|
||||
{
|
||||
if (m_tab == nullptr || laneIndex < 0 || laneIndex >= static_cast<int>(m_tab->Lanes().size()))
|
||||
{
|
||||
@@ -1786,31 +1795,26 @@ namespace jdi
|
||||
}
|
||||
Lane& lane = m_tab->Lanes()[static_cast<std::size_t>(laneIndex)];
|
||||
const float clamped = std::max(0.0f, scroll);
|
||||
if (std::fabs(lane.freeMarkerScroll - clamped) < 0.5f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
lane.freeMarkerScroll = clamped;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlotView::SetLaneMeanFilterScroll(int laneIndex, float scroll)
|
||||
{
|
||||
if (m_tab == nullptr || laneIndex < 0 || laneIndex >= static_cast<int>(m_tab->Lanes().size()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Lane& lane = m_tab->Lanes()[static_cast<std::size_t>(laneIndex)];
|
||||
const float clamped = std::max(0.0f, scroll);
|
||||
if (std::fabs(lane.meanFilterScroll - clamped) < 0.5f)
|
||||
float& stored = LanePanelScrollOf(lane, kind);
|
||||
if (std::fabs(stored - clamped) < 0.5f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// 滚动只影响面板内部像素,缓冲区/场景都不变(面板像素缓存按键失效即可)。
|
||||
lane.meanFilterScroll = clamped;
|
||||
stored = clamped;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlotView::SetLaneFreeMarkerScroll(int laneIndex, float scroll)
|
||||
{
|
||||
return SetLanePanelScroll(laneIndex, EdgePanelKind::MARK, scroll);
|
||||
}
|
||||
|
||||
bool PlotView::SetLaneMeanFilterScroll(int laneIndex, float scroll)
|
||||
{
|
||||
return SetLanePanelScroll(laneIndex, EdgePanelKind::MEAN, scroll);
|
||||
}
|
||||
|
||||
// ============================ 「重采样」 ============================
|
||||
|
||||
bool PlotView::SignalDataInterval(const SignalRef& ref, double& maxInterval,
|
||||
@@ -1963,18 +1967,7 @@ namespace jdi
|
||||
|
||||
bool PlotView::SetLaneResampleScroll(int laneIndex, float scroll)
|
||||
{
|
||||
if (m_tab == nullptr || laneIndex < 0 || laneIndex >= static_cast<int>(m_tab->Lanes().size()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Lane& lane = m_tab->Lanes()[static_cast<std::size_t>(laneIndex)];
|
||||
const float clamped = std::max(0.0f, scroll);
|
||||
if (std::fabs(lane.resampleScroll - clamped) < 0.5f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
lane.resampleScroll = clamped;
|
||||
return true;
|
||||
return SetLanePanelScroll(laneIndex, EdgePanelKind::RESAMPLE, scroll);
|
||||
}
|
||||
|
||||
bool PlotView::TakePendingResampleBuild(int& laneIndex, std::vector<ResampleBuild>& items)
|
||||
@@ -2072,18 +2065,7 @@ namespace jdi
|
||||
|
||||
bool PlotView::SetLaneClipScroll(int laneIndex, float scroll)
|
||||
{
|
||||
if (m_tab == nullptr || laneIndex < 0 || laneIndex >= static_cast<int>(m_tab->Lanes().size()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Lane& lane = m_tab->Lanes()[static_cast<std::size_t>(laneIndex)];
|
||||
const float clamped = std::max(0.0f, scroll);
|
||||
if (std::fabs(lane.clipScroll - clamped) < 0.5f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
lane.clipScroll = clamped;
|
||||
return true;
|
||||
return SetLanePanelScroll(laneIndex, EdgePanelKind::CLIP, scroll);
|
||||
}
|
||||
|
||||
bool PlotView::TakePendingClipBuild(int& laneIndex, std::vector<ClipBuild>& items)
|
||||
@@ -2100,54 +2082,19 @@ namespace jdi
|
||||
}
|
||||
|
||||
|
||||
bool PlotView::SetLaneEdgeCenterScroll(int laneIndex, float scroll) {
|
||||
if (m_tab == nullptr || laneIndex < 0 || laneIndex >= static_cast<int>(m_tab->Lanes().size()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Lane& lane = m_tab->Lanes()[static_cast<std::size_t>(laneIndex)];
|
||||
const float clamped = std::max(0.0f, scroll);
|
||||
if (std::fabs(lane.edgeCenterScroll - clamped) < 0.5f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// 滚动只影响面板内部像素,缓冲区/场景都不变(面板像素缓存按键失效即可)。
|
||||
lane.edgeCenterScroll = clamped;
|
||||
return true;
|
||||
bool PlotView::SetLaneEdgeCenterScroll(int laneIndex, float scroll)
|
||||
{
|
||||
return SetLanePanelScroll(laneIndex, EdgePanelKind::CENTER, scroll);
|
||||
}
|
||||
|
||||
bool PlotView::SetLaneWeakPeakScroll(int laneIndex, float scroll)
|
||||
{
|
||||
if (m_tab == nullptr || laneIndex < 0 || laneIndex >= static_cast<int>(m_tab->Lanes().size()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Lane& lane = m_tab->Lanes()[static_cast<std::size_t>(laneIndex)];
|
||||
const float clamped = std::max(0.0f, scroll);
|
||||
if (std::fabs(lane.weakPeakScroll - clamped) < 0.5f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// 滚动只影响面板内部像素,缓冲区/场景都不变(面板像素缓存按键失效即可)。
|
||||
lane.weakPeakScroll = clamped;
|
||||
return true;
|
||||
return SetLanePanelScroll(laneIndex, EdgePanelKind::PEAK, scroll);
|
||||
}
|
||||
|
||||
bool PlotView::SetLaneEdgeCountScroll(int laneIndex, float scroll)
|
||||
{
|
||||
if (m_tab == nullptr || laneIndex < 0 || laneIndex >= static_cast<int>(m_tab->Lanes().size()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Lane& lane = m_tab->Lanes()[static_cast<std::size_t>(laneIndex)];
|
||||
const float clamped = std::max(0.0f, scroll);
|
||||
if (std::fabs(lane.edgeCountScroll - clamped) < 0.5f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// 滚动只影响面板内部像素,缓冲区/场景都不变(面板像素缓存按键失效即可)。
|
||||
lane.edgeCountScroll = clamped;
|
||||
return true;
|
||||
return SetLanePanelScroll(laneIndex, EdgePanelKind::COUNT, scroll);
|
||||
}
|
||||
|
||||
bool PlotView::ToggleLaneFreeMarkerGroup(int laneIndex, const SignalRef& ref)
|
||||
|
||||
Reference in new issue
Block a user