均值面板越界根因 + 网格线进入主题配色 + 条目栏悬停/整行点击 + 均值点数下限 1

1. 「元素超出浮动面板」真根因(用户 2026-10-30 报告的截图)
   - 绘制侧 PaintEdgeCursor 的 itemized 仍是 (centerAxis || meanFilter || weakPeak),
     而布局侧 EdgePanelLayoutFor 早已是 (centerAxis || weakPeak)。
     于是均值滤波面板被旧的「逐条目」分支**再画一遍**(每行名称 + 复选框,且没有任何裁切),
     滚动后那些行的坐标是负值/越界 ⇒ 直接画到标题栏上。
   - 修法:绘制侧与布局侧统一;旧分支的兜底循环跳过条件补 meanFilter。
     底部「构建为新信号」由共享的 if (hasButton) 绘制,不受影响。
   - 顺带删除「只画完全落在内容区里的行」的 fullyInside 门控:真因修掉后
     g.SetClip(layout.content) 已足够,半截行正常显示,滚动观感连续。

2. 条目栏交互(与「自由标记」分组栏一致)
   - 整行可点:复选框区 = 切换是否预览;已启用 = 展开/收起参数行;未启用 = 直接开始预览。
     命中只要求点落在 layout.content 内(不再要求整行在内容区内),因此半截行也能操作。
   - 悬停行 → 对应预览曲线高亮:新增 m_meanRowHoverLane / m_meanRowHoverRef,
     由 OnMouseMove 维护、OnMouseLeave 与 InvalidateDerivedCaches 清除;
     PaintDerivedCurve/PaintMeanFilterCurve/PaintWeakPeakCurve 新增 bool highlight,
     先画 +5px 半透明同色光晕再把线加粗,行头叠一层 accent;悬停态变化会 InvalidateSceneCache。

3. 均值点数下限 1(用户要求:不可以为 0)
   - kMeanFilterPointsMin 0 → 1;输入提交用 centerTarget ? 0 : kMeanFilterPointsMin
     (中心轴边沿计数沿用旧语义 0 = 不平滑);预览与构建请求都再夹一次下限,
     即使条目被外部写成 0,渲染/构建结果与 1 点逐位一致。

4. 网格线颜色成为主题配色的一部分
   - struct Theme 新增 laneBg(网格叠底色);NormalizePlotContrast → FinalizeTheme,
     先解 laneBg = EnsureContrast(plotBg, cardBg, 1.03),再让 gridWeak/gridStrong/
     axisLine/borderStrong 统一对 laneBg 归一(1.30 / 1.70 / 2.10 / 1.70)。
   - 四个主题各自给出显式取值:Dark 的网格线对叠底色原本只有 1.071(几乎不可见)⇒ 现 ≥1.30;
     其它三个主题同步微调,网格可见性一致。ThemeLaneBackground 返回 laneBg。

5. 行三角尺寸统一
   - 新增 kEdgePanelRowTriangleSize = 12 逻辑像素,均值滤波行与自由标记分组栏共用;
     标题栏的 kEdgePanelDisclosureWidth = 20 保持不变。

6. 均值面板滚动条与文本
   - 滚动条必须在 g.Restore(meanSave) 之后绘制(它在内容裁剪区之外,裁剪内画会被整条裁掉)。
   - 滑块标签/数值改用 UiTextClipped(..., layout.content)(GDI 文本不受 GDI+ 裁剪约束)。

验证(全部离屏)
   uicheck 69/69、lbcheck 21/21、lbresid 5/5、lbzorder 4/4、lbcovr 7/7、lbpanel 10/10、
   derivecheck 16/16、round15check 28/28、themecheck 17/17、.preflight/meancheck 17/17;
   .preflight/meanpix:滚到顶/中/底时标题栏与面板外均为 0 像素差异,内容区正常滚动;
   rendercheck:A–H 八个场景哈希与基线完全一致;
   make release 零 warning(805,376 B);make debug 零 warning。

同步文档:Documents/agent_mem.md 追加「2026-10-30 第十七轮」,
并把第十六轮里「均值点数(0…上限)」「命中要求行完全落在内容区里」两条改成现行做法。
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-10-30 10:51:39 +08:00
1 parent bdd703b88b
commit 2d83b494a3
9 files changed
+396 -100

No files matched your search

+29 -3
View File
@@ -120,7 +120,23 @@ namespace jdi
std::uint32_t MixToLuminance(std::uint32_t color, double targetLuminance);
/**
* @brief 界面主题色板(Solarised Light 派生)。
* @brief 界面主题色板。
*
* ## 结构色的对比度约定(重要,改配色前必读)
*
* `gridWeak` / `gridStrong` / `axisLine` / `borderStrong` 这些“结构色”是**主题配色的一部分**
* (每个主题各自给值),它们画在**网格叠底**(`laneBg`)之上,而不是绘图区底色 `plotBg`。
* 因此它们的最小对比度一律针对 `laneBg` 校验:
*
* | 字段 | 最小对比度(相对 `laneBg`) |
* | --- | ---: |
* | `gridWeak` | 1.30 |
* | `gridStrong` | 1.70 |
* | `axisLine` | 2.10 |
* | `borderStrong` | 1.70 |
*
* 各主题给出的值本就满足上述下限,因此最终化时**逐位返回原色**、不做任何改色;
* 这些阈值只是“不允许配置出看不见的网格线”的兜底。
*/
struct Theme
{
@@ -143,9 +159,19 @@ namespace jdi
std::uint32_t pressed = 0; /**< 按下底色。 */
std::uint32_t selection = 0; /**< 选中底色。 */
std::uint32_t gridWeak = 0; /**< 次级网格线颜色。 */
std::uint32_t gridStrong = 0; /**< 主要网格线颜色。 */
std::uint32_t gridWeak = 0; /**< 次级网格线颜色(主要刻度 + 五等分线)。 */
std::uint32_t gridStrong = 0; /**< 主要网格线颜色(零线 / 加重线)。 */
std::uint32_t axisLine = 0; /**< 坐标轴线颜色。 */
/**
* @brief 每条网格内部的**叠底**颜色(绘图区底色之外的“卡片层”)。
*
* ⚠️ 网格线是画在**它**上面(不是 `plotBg` 上),因此结构色的对比度必须针对
* 本字段校验(见 `Theme.hpp` 顶部的 `EnsureContrast` 约定);落在深色主题上时
* 两者相差很大,只盯着 `plotBg` 算会让网格线“几乎看不见”(用户 2026-10-30 报告)。
*
* 0 表示“未设置”:最终化时回退到 `cardBg` 并保证与 `plotBg` 有最小对比度。
*/
std::uint32_t laneBg = 0;
std::uint32_t cursor = 0; /**< 时间光标颜色(单/双光标共用同一样式)。 */
std::uint32_t readoutBg = 0; /**< 数值读数底色。 */
std::uint32_t danger = 0; /**< 警示色。 */
+60 -39
View File
@@ -36,31 +36,40 @@ namespace jdi
};
/**
* @brief 把绘图区内部的"结构性"颜色按**底色**自动校正到足够对比(见 `EnsureContrast`)。
* @brief 主题**最终化**:确定网格叠底色,并保证结构色在**它**之上可辨。
*
* 阈值(对比度)取"Solarised Light 现有配色刚好达标"的水位:
* 该主题的历史配色是人工调过的,达标时 `EnsureContrast` 逐位返回原色 ⇒ 默认主题
* **一个像素都不会变**;其它主题(尤其深色)则自动把网格线/时间轴/分界线提亮。
* ⚠️⚠️ **参照底色必须是 `laneBg`(网格叠底),不是 `plotBg`**:
* 网格线画在每条网格内部的叠底上。深色主题里 `plotBg`(#1E1E1E)与 `laneBg`
* (#2D2D30)亮度差得远,只按 `plotBg` 校正会让网格线落进叠底色的亮度范围
* ⇒ 实测对比度只有 **1.07**(几乎看不见,用户 2026-10-30 报告)。
*
* | 字段 | 阈值 | Solarised Light 现状 |
* 阈值取"Solarised Light 现有配色刚好达标"的水位(针对 `laneBg` 实测:
* 1.335 / 1.771 / 2.395 / 2.395),因此默认主题**逐位不变**;其它主题若不足才被校正。
*
* | 字段 | 阈值(相对 `laneBg`) | Solarised Light 现状 |
* | --- | ---: | ---: |
* | `gridWeak`(网格主刻线 + 五等分线基色) | 1.30 | 约 1.38 |
* | `gridStrong`(零线/加重线) | 1.65 | 约 1.80 |
* | `axisLine`(时间轴、网格分界线、绘图区外框) | 2.10 | 约 2.43 |
* | `borderStrong`(网格间分界线) | 1.70 | 约 2.43 |
* | `gridWeak`(网格主刻线 + 五等分线基色) | 1.30 | 1.335 |
* | `gridStrong`(零线/加重线) | 1.70 | 1.771 |
* | `axisLine`(时间轴、网格分界线、绘图区外框) | 2.10 | 2.395 |
* | `borderStrong`(网格间分界线) | 1.70 | 2.395 |
*/
Theme NormalizePlotContrast(Theme theme)
Theme FinalizeTheme(Theme theme)
{
const std::uint32_t plot = theme.plotBg | 0xFF000000u;
theme.gridWeak = EnsureContrast(plot, theme.gridWeak | 0xFF000000u, 1.30);
theme.gridStrong = EnsureContrast(plot, theme.gridStrong | 0xFF000000u, 1.65);
theme.axisLine = EnsureContrast(plot, theme.axisLine | 0xFF000000u, 2.10);
theme.borderStrong = EnsureContrast(plot, theme.borderStrong | 0xFF000000u, 1.70);
// 网格叠底:未显式配置时回退到 `cardBg`,并保证与绘图区底色可辨(1.03)。
const std::uint32_t laneSource =
(theme.laneBg != 0u) ? (theme.laneBg | 0xFF000000u) : (theme.cardBg | 0xFF000000u);
theme.laneBg = EnsureContrast(plot, laneSource, 1.03);
theme.gridWeak = EnsureContrast(theme.laneBg, theme.gridWeak | 0xFF000000u, 1.30);
theme.gridStrong = EnsureContrast(theme.laneBg, theme.gridStrong | 0xFF000000u, 1.70);
theme.axisLine = EnsureContrast(theme.laneBg, theme.axisLine | 0xFF000000u, 2.10);
theme.borderStrong = EnsureContrast(theme.laneBg, theme.borderStrong | 0xFF000000u, 1.70);
return theme;
}
/** @brief 全局唯一主题实例(Solarised Light;历史默认主题)。 */
const Theme kSolarizedLight = NormalizePlotContrast([]
const Theme kSolarizedLight = FinalizeTheme([]
{
Theme theme;
theme.windowBg = MakeArgb(kBase3);
@@ -82,8 +91,10 @@ namespace jdi
theme.pressed = MakeArgb(0xD8CFB6u);
theme.selection = MakeArgb(0xDCEAF6u);
// 网格线需要在绘图区底色(kBase3)上清晰可辨,同时又要与网格边界
// (axisLine/borderStrong 的蓝灰)明显区分——因此采用偏暖的加深米色。
// 网格线需要在**网格叠底**(kBase3 推暗 1.03 ≈ #F9F2E0)上清晰可辨,同时又要与
// 网格边界(axisLine/borderStrong 的蓝灰)明显区分——因此采用偏暖的加深米色。
// 这三个值相对叠底色的对比度分别是 1.335 / 1.771 / 2.395,已达标 ⇒ 最终化不改动。
theme.laneBg = MakeArgb(0xF9F2E0u);
theme.gridWeak = MakeArgb(0xDDD3B8u);
theme.gridStrong = MakeArgb(0xC3B896u);
theme.axisLine = MakeArgb(kBase1);
@@ -100,7 +111,7 @@ namespace jdi
* 与 Solarised Light 的区别:底色是**中性白/浅灰**而不是暖米色,
* 强调色是 VS Code 的系统蓝 `#0078D4`,描边是中性灰。
*/
const Theme kLight = NormalizePlotContrast([]
const Theme kLight = FinalizeTheme([]
{
Theme theme;
theme.windowBg = MakeArgb(0xFFFFFFu); // editor.background
@@ -109,7 +120,7 @@ namespace jdi
theme.cardBg = MakeArgb(0xFFFFFFu);
theme.plotBg = MakeArgb(0xFFFFFFu);
theme.border = MakeArgb(0xE5E5E5u); // panel.border
theme.borderStrong = MakeArgb(0xCECECEu);
theme.borderStrong = MakeArgb(0xC3C3C3u);
theme.text = MakeArgb(0x3B3B3Bu); // foreground
theme.textStrong = MakeArgb(0x1F1F1Fu);
@@ -122,11 +133,12 @@ namespace jdi
theme.pressed = MakeArgb(0xE8E8E8u);
theme.selection = MakeArgb(0xE8E8E8u);
// 白底上网格线必须够淡(否则抢波形),但主刻线要能与次级线区分。
// ⚠️ 这三个颜色会被 `NormalizePlotContrast` 按白底自动校正到可见水位。
theme.gridWeak = MakeArgb(0xEDEDEDu);
theme.gridStrong = MakeArgb(0xD6D6D6u);
theme.axisLine = MakeArgb(0xCECECEu);
// 网格叠底比绘图区白底略暗一档,便于分辨相邻网格;
// 结构色按**叠底色**选中(对比度:1.30 / 1.70 / 2.10),不要再用原白底的值。
theme.laneBg = MakeArgb(0xFBFBFBu);
theme.gridWeak = MakeArgb(0xDEDEDEu);
theme.gridStrong = MakeArgb(0xC3C3C3u);
theme.axisLine = MakeArgb(0xAFAFAFu);
theme.cursor = MakeArgb(0xD18616u); // VS Code charts.orange
theme.readoutBg = MakeArgb(0xF3F3F3u);
theme.danger = MakeArgb(0xE51400u); // errorForeground
@@ -137,7 +149,7 @@ namespace jdi
/**
* @brief 深色主题(参考 VS Code Dark+)。
*/
const Theme kDark = NormalizePlotContrast([]
const Theme kDark = FinalizeTheme([]
{
Theme theme;
theme.windowBg = MakeArgb(0x1E1E1Eu); // editor.background
@@ -146,7 +158,7 @@ namespace jdi
theme.cardBg = MakeArgb(0x2D2D30u);
theme.plotBg = MakeArgb(0x1E1E1Eu);
theme.border = MakeArgb(0x3C3C3Cu);
theme.borderStrong = MakeArgb(0x4A4A4Au);
theme.borderStrong = MakeArgb(0x505050u);
theme.text = MakeArgb(0xCCCCCCu);
theme.textStrong = MakeArgb(0xFFFFFFu);
@@ -159,11 +171,13 @@ namespace jdi
theme.pressed = MakeArgb(0x37373Du);
theme.selection = MakeArgb(0x04395Eu);
// ⚠️ 深色底上的原始取值几乎与底色同亮度(差 2~3 个色阶)⇒ 交给
// `NormalizePlotContrast` 按 `plotBg` 自动提亮到可见水位。
theme.gridWeak = MakeArgb(0x2B2B2Bu);
theme.gridStrong = MakeArgb(0x3A3A3Au);
theme.axisLine = MakeArgb(0x4A4A4Au);
// ⚠️ 深色主题的结构色**必须比旧值亮得多**:它们画在网格叠底(#2D2D30)上,
// 旧值(#2B2B2B / #3A3A3A)与叠底几乎同亮度(实测对比度 1.07)⇒ 网格线看不见。
// 这里的值按叠底色选:1.30 / 1.70 / 2.10。
theme.laneBg = MakeArgb(0x2D2D30u);
theme.gridWeak = MakeArgb(0x3F3F3Fu);
theme.gridStrong = MakeArgb(0x505050u);
theme.axisLine = MakeArgb(0x5E5E5Eu);
theme.cursor = MakeArgb(0xD18616u);
theme.readoutBg = MakeArgb(0x2D2D30u);
theme.danger = MakeArgb(0xF44747u);
@@ -177,7 +191,7 @@ namespace jdi
* 底色用 base03、面板用 base02,强调色仍是 Solarised 那一套 —— 因此波形配色表
* (`SignalPalette`)与 Solarised Light 完全一致。
*/
const Theme kSolarizedDark = NormalizePlotContrast([]
const Theme kSolarizedDark = FinalizeTheme([]
{
Theme theme;
theme.windowBg = MakeArgb(kBase03);
@@ -186,7 +200,7 @@ namespace jdi
theme.cardBg = MakeArgb(kBase03);
theme.plotBg = MakeArgb(kBase03);
theme.border = MakeArgb(0x18505Fu);
theme.borderStrong = MakeArgb(kBase01);
theme.borderStrong = MakeArgb(0x205363u);
theme.text = MakeArgb(kBase0);
theme.textStrong = MakeArgb(kBase1);
@@ -199,8 +213,11 @@ namespace jdi
theme.pressed = MakeArgb(0x0A3F4Cu);
theme.selection = MakeArgb(0x12414Fu);
theme.gridWeak = MakeArgb(0x0B3A46u);
theme.gridStrong = MakeArgb(0x14495Au);
// 网格叠底与绘图区底色同为 base03 时看不出网格层次 ⇒ 显式给出略亮一档的叠底,
// 结构色按**它**选(对比度 1.30 / 1.70 / 2.10)。
theme.laneBg = MakeArgb(0x032E38u);
theme.gridWeak = MakeArgb(0x13414Cu);
theme.gridStrong = MakeArgb(0x205363u);
theme.axisLine = MakeArgb(kBase01);
theme.cursor = MakeArgb(kOrange);
theme.readoutBg = MakeArgb(kBase02);
@@ -316,10 +333,14 @@ namespace jdi
std::uint32_t ThemeLaneBackground(const Theme& theme)
{
// 网格底色必须与绘图区底色**可辨**(否则几十条网格看起来是一整片)。
// 用统一的对比度阈值 1.03:本就有差异的主题(Solarised Light / Light / Dark)
// 逐位返回 `cardBg`;两侧完全同色的主题(Solarised Dark 的 base03)会被轻轻推开。
return EnsureContrast(theme.plotBg | 0xFF000000u, theme.cardBg | 0xFF000000u, 1.03);
// 网格叠底色是**主题配色的一部分**(`Theme::laneBg`),由 `FinalizeTheme` 在构建主题时
// 保证与绘图区底色可辨;这里只做"未配置"的兜底。
if (theme.laneBg != 0u)
{
return theme.laneBg | 0xFF000000u;
}
const std::uint32_t plot = theme.plotBg | 0xFF000000u;
return EnsureContrast(plot, theme.cardBg | 0xFF000000u, 1.03);
}
const Theme& ThemeFor(ThemeKind kind)