Files
Just-DataInspector/Components/Shared/Inc/Theme.hpp
T
DeepSeek-V4.1-Flash bdd703b88b 主题自适应配色 + 预览曲线连续配色 + 样式区复位按钮 + 均值滤波面板重做 + 面板缓存环带残留 + 调试层序 + 自由标记选取与性能
① 主题自适应(`Theme.cpp`):新增 `SrgbLuminance` / `ContrastRatio` / `EnsureContrast` /
   `MixToLuminance` / `ThemeLaneBackground`;四套主题的结构色(gridWeak 1.30 / gridStrong 1.65 /
   axisLine 2.10 / borderStrong 1.70)按底色自动校正,Solarised Light 逐位不变;网格叠底改用
   `ThemeLaneBackground()`(4 处),深色主题下网格层与绘图区底色可辨。
② 派生预览曲线(`HighContrastDerived`)重写为**连续**方案:方向沿信号所在侧远离底色,
   目标亮度 = max(与底色对比度 3.2, 信号亮度 ± 0.18),再按亮度平滑混色;调用点传入真实网格底色
   (旧实现默认写死 Solarised Light 底色 ⇒ 深色主题永远选到更暗的那一侧、几乎看不见)。
③ 信号样式区「线宽 / 透明度」两行右侧新增**复位按钮**(`UiPaintResetIcon` 成为唯一实现,
   菜单/弱峰面板/样式区共用同一套笔画);默认线宽统一到 `DefaultSignalLineWidth(finiteCount)`
   (单点信号取 10.0)。按钮右边界与内容列对齐,内容列过窄时整列不显示。
④ 「均值滤波」面板重做:信号色叠底标题栏 + 复选框 + 展开三角 + 「均值点数 / 迭代次数」两行滑块
   (点击数值可改滑块上限、右侧复位)+ 内容区滚动;新增 `MeanFilterItem::{maxPoints,maxIterations,
   collapsed}` 与 `Lane::meanFilterScroll`,缓存键同步补全。
⑤ 浮动面板命中像素缓存时只贴**面板圆角形状之内**的像素(`CreateRoundRectRgn` 裁剪)并按本帧重画投影
   ⇒ 中键移视界后面板周围不再出现一圈旧波形。
⑥ 显示布局边界:每个浮动面板**独占一个绘制层**(按 `m_panelOrder` 递增),绘制侧
   `UiSetTextLayer` 在同一循环内同步推进 ⇒ 下层面板的边框/文字不再画到上层面板上。
⑦ 自由标记:采样候选距离改为**二维欧氏距离**(单点信号不再在整幅宽度内吸住光标),
   纵向光标画在鼠标位置、圆点落在真实采样点(新增 `FreeMarkerProbe::pointX`);
   `MinorCellSparse` 只对胜出信号算一次;折线缓存容量改为 `max(64, 信号数×2)` 且查找改为
   单键哈希 ⇒ MF4 的 285 信号合并网格中 `ProbeFreeMarker` 平均 0.11ms。

验证:`.preflight` 探针重建(`panelcheck` 71/71、`markercheck` 13/13、`ringcheck` 6/6,合计 90 条,
LTO 目标文件上复跑同样全绿);`make release` / `make debug` 零警告;产物 815,104 B。
文档:`agent_mem.md` 第十六轮(含补丁脚本与编码/行尾踩坑)。
2026-10-30 03:10:51 +08:00

226 lines
8.4 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#ifndef __THEME_HPP__
#define __THEME_HPP__
#include <windows.h>
#include <objidl.h>
#include <gdiplus.h>
#include <cstdint>
/// @brief GDI+ 命名空间短别名,便于在界面代码中引用绘图类型。
namespace gd = Gdiplus;
namespace jdi
{
/**
* @brief 将 0xAARRGGBB 颜色值转换为 GDI+ 颜色对象。
* @param [IN] argb 颜色值。
* @return GDI+ 颜色对象。
*/
inline gd::Color ToGdiColor(std::uint32_t argb)
{
return gd::Color(static_cast<BYTE>((argb >> 24) & 0xFFu), static_cast<BYTE>((argb >> 16) & 0xFFu),
static_cast<BYTE>((argb >> 8) & 0xFFu), static_cast<BYTE>(argb & 0xFFu));
}
/**
* @brief 由 0xRRGGBB 构造不透明的 0xAARRGGBB 颜色值。
* @param [IN] rgb RGB 颜色值。
* @return 不透明颜色值。
*/
inline std::uint32_t MakeArgb(std::uint32_t rgb)
{
return 0xFF000000u | (rgb & 0x00FFFFFFu);
}
/**
* @brief 替换颜色值的 Alpha 通道。
* @param [IN] argb 原颜色值。
* @param [IN] alpha 新的 Alpha 值(0-255)。
* @return 新的颜色值。
*/
inline std::uint32_t WithAlpha(std::uint32_t argb, unsigned int alpha)
{
return (argb & 0x00FFFFFFu) | ((static_cast<std::uint32_t>(alpha) & 0xFFu) << 24);
}
/**
* @brief 在两个颜色之间做线性插值。
* @param [IN] fromColor 起始颜色。
* @param [IN] toColor 结束颜色。
* @param [IN] ratio 插值比例,0 返回起始色,1 返回结束色。
* @return 插值后的颜色值。
*/
inline std::uint32_t MixColor(std::uint32_t fromColor, std::uint32_t toColor, double ratio)
{
if (ratio < 0.0)
{
ratio = 0.0;
}
if (ratio > 1.0)
{
ratio = 1.0;
}
std::uint32_t result = 0;
for (int shift = 0; shift <= 24; shift += 8)
{
const double a = static_cast<double>((fromColor >> shift) & 0xFFu);
const double b = static_cast<double>((toColor >> shift) & 0xFFu);
const std::uint32_t mixed = static_cast<std::uint32_t>(a + (b - a) * ratio + 0.5);
result |= ((mixed & 0xFFu) << shift);
}
return result;
}
/**
* @brief 颜色的**相对亮度**(WCAG 2.x 定义:sRGB 线性化后按 0.2126/0.7152/0.0722 加权)。
* @param [IN] argb 颜色值(只看 RGB 通道)。
* @return 相对亮度,取值 0(黑)… 1(白)。
*/
double SrgbLuminance(std::uint32_t argb);
/**
* @brief 两色之间的**对比度**(WCAG 2.x 定义:`(L1 + 0.05) / (L2 + 0.05)`)。
* @param [IN] firstColor 第一色。
* @param [IN] secondColor 第二色。
* @return 对比度,取值 1(完全相同)… 21(黑白)。
*/
double ContrastRatio(std::uint32_t firstColor, std::uint32_t secondColor);
/**
* @brief 保证前景色相对底色有**足够对比度**:不足时沿"远离底色"的方向混向黑/白。
*
* 用途:绘图区内部的网格线、时间轴等分线、分界线等原本按某一个主题手工调色的元素,
* 必须跟着主题背景色自动适配(浅底上加深、深底上提亮),否则换到深色主题后
* 网格线只剩几个色阶、几乎看不见。
*
* ⚠️ **已达标时逐位返回原色**:Solarised Light 的历史配色是人工调过的,
* 只要它满足阈值就必须保持逐像素不变(避免"换主题修复"顺带改变默认主题观感)。
*
* @param [IN] background 底色(不透明)。
* @param [IN] foreground 前景色(不透明)。
* @param [IN] minRatio 期望的最小对比度(≥ 1,1 表示不限制)。
* @return 满足阈值的颜色(RGB 不变、alpha = 0xFF)。
*/
std::uint32_t EnsureContrast(std::uint32_t background, std::uint32_t foreground, double minRatio);
/**
* @brief 把颜色沿"亮度"插值到目标相对亮度(保持色相,二分求解混色比例)。
*
* 比"固定比例混向黑/白"更可控:调用方指定**目标亮度**,本函数负责求出混色量。
* 目标亮度高于当前亮度时混向白、低于时混向黑;求解连续 ⇒ 输入微调时输出也连续
*(不会在两个极端色之间跳变)。
*
* @param [IN] color 原色(不透明)。
* @param [IN] targetLuminance 目标相对亮度(0…1)。
* @return 亮度接近目标值的颜色(无法达到时取该方向上的极限)。
*/
std::uint32_t MixToLuminance(std::uint32_t color, double targetLuminance);
/**
* @brief 界面主题色板(Solarised Light 派生)。
*/
struct Theme
{
std::uint32_t windowBg = 0; /**< 窗口底色。 */
std::uint32_t toolbarBg = 0; /**< 顶部工具栏底色。 */
std::uint32_t panelBg = 0; /**< 侧边面板底色。 */
std::uint32_t cardBg = 0; /**< 面板内卡片底色。 */
std::uint32_t plotBg = 0; /**< 绘图区底色。 */
std::uint32_t border = 0; /**< 常规描边色。 */
std::uint32_t borderStrong = 0; /**< 强调描边色。 */
std::uint32_t text = 0; /**< 正文文字色。 */
std::uint32_t textStrong = 0; /**< 强调文字色。 */
std::uint32_t textMuted = 0; /**< 次要文字色。 */
std::uint32_t textOnAccent = 0; /**< 强调色之上的文字色。 */
std::uint32_t accent = 0; /**< 主题强调色。 */
std::uint32_t accentSoft = 0; /**< 强调色的浅色版本。 */
std::uint32_t hover = 0; /**< 悬停底色。 */
std::uint32_t pressed = 0; /**< 按下底色。 */
std::uint32_t selection = 0; /**< 选中底色。 */
std::uint32_t gridWeak = 0; /**< 次级网格线颜色。 */
std::uint32_t gridStrong = 0; /**< 主要网格线颜色。 */
std::uint32_t axisLine = 0; /**< 坐标轴线颜色。 */
std::uint32_t cursor = 0; /**< 时间光标颜色(单/双光标共用同一样式)。 */
std::uint32_t readoutBg = 0; /**< 数值读数底色。 */
std::uint32_t danger = 0; /**< 警示色。 */
std::uint32_t warning = 0; /**< 提示色。 */
};
/**
* @brief 内置主题配色种类。
*
* 顺序即「主题配色」二级菜单里的显示顺序(用户 2026-10-29 指定)。
*/
enum class ThemeKind : int
{
LIGHT = 0, /**< 浅色(参考 VS Code 2026 默认浅色主题)。 */
SOLARIZED_LIGHT = 1, /**< Solarised Light(历史默认主题)。 */
DARK = 2, /**< 深色(参考 VS Code Dark+)。 */
SOLARIZED_DARK = 3 /**< Solarised Dark(参考 VS Code 的 Solarized Dark)。 */
};
/**
* @brief 取某个内置主题的配色(静态实例,地址稳定)。
* @param [IN] kind 主题种类;越界时退回 Solarised Light。
* @return 主题常引用。
*/
const Theme& ThemeFor(ThemeKind kind);
/**
* @brief 取主题在「主题配色」菜单里的显示名称。
* @param [IN] kind 主题种类。
* @return 名称(静态字符串,无需释放)。
*/
const wchar_t* ThemeName(ThemeKind kind);
/**
* @brief 全局「当前主题」指针。
*
* ⚠️ 为什么需要全局:`UiControls` 里少数几个原语(色块描边、非主题化的默认底色)
* 没有 `Theme` 形参,却必须跟随用户选择的主题。宿主在启动与切换主题时调用
* `SetCurrentTheme(&m_theme)`,把**宿主自己那个** `Theme` 实例的地址交给它 ——
* 这样切换主题只需修改 `m_theme` 的内容,不需要通知每一个组件刷新指针。
*
* @return 当前主题;未设置时返回 Solarised Light。
*/
const Theme& CurrentTheme();
/**
* @brief 设置全局「当前主题」。
* @param [IN] theme 主题实例(必须比使用期更长;传 nullptr 表示恢复默认)。
*/
void SetCurrentTheme(const Theme* theme);
/**
* @brief 取「绘图区网格底色」——**网格叠底**(每条网格内部的底色)。
*
* 它与 `Theme::plotBg`(绘图区整体底色)分开:网格之外的时间轴/留白区用 `plotBg`,
* 网格内部用本函数。当两者过于接近时,本函数会把网格底色**轻轻地**往远离绘图区
* 底色的方向挪一点(仅供深色主题的 Solarised Dark 这类"两者完全同色"的情形),
* 保证相邻网格仍能被分界线以外的底色层次区分开。
*
* ⚠️ 两者本来就有可辨识的亮度差时**逐位返回 `cardBg`**(Solarised Light / Light / Dark
* 的默认观感不变)。
*
* @param [IN] theme 主题。
* @return 网格底色(不透明)。
*/
std::uint32_t ThemeLaneBackground(const Theme& theme);
/**
* @brief 获取波形默认配色表。
* @param [OUT] count 配色数量输出。
* @return 颜色数组首地址。
*/
const std::uint32_t* SignalPalette(int& count);
} // namespace jdi
#endif // __THEME_HPP__