Files
Just-DataInspector/Utilities/Inc/PaintUtil.hpp
T
DeepSeek V4.1 Flash d3c83237f1 信号 X 按钮移至行尾并统一抬起响应,光标读数补秒单位,图例悬停高亮信号,去除筛选框内圈
1. 信号条目的「移除」X 按钮移到最右端
   - 与分组行关闭 X 同尺寸(18)、同右边距(同一列)、同行内垂直居中,描边宽度也统一。
   - 网格编号 GX 徽标让位到 X 左侧,二者恒在列表最右侧;名称可用宽度改按实际徽标是否显示计算,
     不再白留一段空间。

2. 所有 X 关闭按钮由"按下即响应"改为"左键抬起才响应"
   - 与常见的可取消按钮一致:按下只记录目标,按住后移开鼠标再松开视为放弃。
   - 覆盖文件分组 X、信号 X、筛选框清空 X、标签页 X 与实验性功能面板 X。
   - 筛选框的清空 X 在编辑态下走的是宿主的 EDIT_FIELD 分支(OnEditMouseDown/OnEditMouseUp),
     非编辑态才走面板的按下/抬起分支,两条路径都已改造。
   - 标签页 X 原先只要求"抬起时仍在同一标签内",现收紧为"仍在 X 上"。
   - PlotView::OnLButtonUp 增加横坐标参数以便判定抬起位置。

3. 时间光标读数补上 s 后缀
   - 新增 FormatCursorTime,标签宽度测量与文字绘制同时使用,避免文字被裁掉;
     内联编辑仍使用不带单位的秒值。

4. 鼠标悬停网格图例时高亮对应信号
   - 除图例条目的半高亮外,对命中信号绘制与"鼠标贴近曲线"相同的光晕,
     便于确认该条目对应哪条曲线。

5. 筛选信号输入框编辑态不再出现内圈
   - 把输入框绘制拆分为"底板+边框"与"仅内容"两部分;筛选框外框已自绘,
     编辑态改用仅内容版本,空内容时不再套一层多余的圆角内框;
     有输入时的清空 X 不受影响。
2026-10-10 11:30:07 +08:00

328 lines
12 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 __PAINT_UTIL_HPP__
#define __PAINT_UTIL_HPP__
#include <windows.h>
#include <objidl.h>
#include <gdiplus.h>
#include <cstdint>
#include <string>
#include "TextEdit.hpp"
#include "Theme.hpp"
namespace jdi
{
/**
* @brief 文本水平对齐方式(垂直方向始终居中)。
*/
enum class TextAlign : int
{
LEFT = 0, /**< 左对齐。 */
CENTER = 1, /**< 水平居中。 */
RIGHT = 2 /**< 右对齐。 */
};
/**
* @brief 图标朝向。
*/
enum class IconDirection : int
{
LEFT = 0, /**< 指向左侧。 */
RIGHT = 1, /**< 指向右侧。 */
UP = 2, /**< 指向上方。 */
DOWN = 3 /**< 指向下方。 */
};
/**
* @brief 按 DPI 创建并缓存整套界面字体(GDI 字体句柄)。
*/
struct UiFonts
{
HFONT ui = nullptr; /**< 常规界面字体。 */
HFONT uiBold = nullptr; /**< 加粗界面字体。 */
HFONT uiSmall = nullptr; /**< 小号界面字体。 */
HFONT uiSmallBold = nullptr;/**< 小号加粗界面字体。 */
HFONT title = nullptr; /**< 标题字体。 */
HFONT mono = nullptr; /**< 等宽数值字体。 */
HFONT monoSmall = nullptr; /**< 小号等宽字体。 */
UINT dpi = 96; /**< 创建时所使用的 DPI。 */
/**
* @brief 依据 DPI 创建全部字体。
* @param [IN] dpi 目标 DPI(96 对应 100% 缩放)。
* @return 创建成功返回 true。
*/
bool Create(UINT dpi);
/**
* @brief 释放全部字体句柄。
*/
void Destroy();
~UiFonts() { Destroy(); }
};
/**
* @brief 将 0xAARRGGBB 颜色值转换为 GDI COLORREF。
* @param [IN] argb 颜色值。
* @return GDI 颜色值。
*/
inline COLORREF ToColorRef(std::uint32_t argb)
{
return RGB((argb >> 16) & 0xFFu, (argb >> 8) & 0xFFu, argb & 0xFFu);
}
/**
* @brief 判断点是否落在矩形内部。
* @param [IN] rect 目标矩形。
* @param [IN] x 横坐标。
* @param [IN] y 纵坐标。
* @return 落在内部返回 true。
*/
inline bool UiPointInRect(const gd::RectF& rect, float x, float y)
{
return x >= rect.X && x <= (rect.X + rect.Width) && y >= rect.Y && y <= (rect.Y + rect.Height);
}
/**
* @brief 填充矩形区域。
* @param [IN,OUT] g 绘图上下文。
* @param [IN] rect 目标矩形。
* @param [IN] argb 填充颜色。
*/
void UiFillRect(gd::Graphics& g, const gd::RectF& rect, std::uint32_t argb);
/**
* @brief 填充圆角矩形区域。
* @param [IN,OUT] g 绘图上下文。
* @param [IN] rect 目标矩形。
* @param [IN] radius 圆角半径。
* @param [IN] argb 填充颜色。
*/
void UiFillRoundRect(gd::Graphics& g, const gd::RectF& rect, float radius, std::uint32_t argb);
/**
* @brief 描边圆角矩形。
* @param [IN,OUT] g 绘图上下文。
* @param [IN] rect 目标矩形。
* @param [IN] radius 圆角半径。
* @param [IN] argb 线条颜色。
* @param [IN] width 线条宽度。
*/
void UiDrawRoundRect(gd::Graphics& g, const gd::RectF& rect, float radius, std::uint32_t argb, float width);
/**
* @brief 绘制一条直线。
* @param [IN,OUT] g 绘图上下文。
* @param [IN] x1 起点横坐标。
* @param [IN] y1 起点纵坐标。
* @param [IN] x2 终点横坐标。
* @param [IN] y2 终点纵坐标。
* @param [IN] argb 线条颜色。
* @param [IN] width 线条宽度。
*/
void UiDrawLine(gd::Graphics& g, float x1, float y1, float x2, float y2, std::uint32_t argb, float width);
/**
* @brief 在指定矩形内绘制单行文本(垂直居中,超长自动省略)。
* @param [IN,OUT] g 绘图上下文。
* @param [IN] text 文本内容。
* @param [IN] font 字体句柄。
* @param [IN] rect 文本布局矩形。
* @param [IN] argb 文本颜色。
* @param [IN] align 水平对齐方式。
*/
void UiText(gd::Graphics& g, const std::wstring& text, HFONT font, const gd::RectF& rect, std::uint32_t argb,
TextAlign align = TextAlign::LEFT);
/**
* @brief 测量单行文本的像素宽度(无需 GDI+ 上下文,可在布局阶段调用)。
* @param [IN] font 字体句柄。
* @param [IN] text 文本内容。
* @return 文本像素宽度。
*/
float UiMeasureTextWidth(HFONT font, const std::wstring& text);
/**
* @brief 在指定裁剪矩形内以显式起点绘制一段文本(使用 GDI ExtTextOut 裁剪)。
*
* `UiText` 走 `DrawTextW`,**不遵循 GDI+ 裁剪区**;输入框在长内容、水平滚动的情况下
* 会把字符画到框体之外。本函数用 `ETO_CLIPPING` 让 GDI 自己裁剪,彻底避免越界绘制。
*
* @param [IN,OUT] g 绘图上下文。
* @param [IN] text 文本内容。
* @param [IN] font 字体句柄。
* @param [IN] x 文本起始横坐标。
* @param [IN] rect 垂直居中参考矩形。
* @param [IN] argb 文本颜色。
* @param [IN] clipRect 裁剪矩形(超出部分不绘制)。
*/
void UiTextAtClipped(gd::Graphics& g, const std::wstring& text, HFONT font, float x, const gd::RectF& rect,
std::uint32_t argb, const gd::RectF& clipRect);
/**
* @brief 计算输入框文本的绘制起点(已考虑超出框宽时的水平滚动)。
* @param [IN] rect 输入框矩形。
* @param [IN] edit 编辑模型。
* @param [IN] font 字体句柄。
* @param [IN] scale 缩放比例。
* @param [OUT] innerWidth 框内可用宽度输出。
* @return 文本绘制起点横坐标。
*/
float UiEditTextLeft(const gd::RectF& rect, const TextEdit& edit, HFONT font, float scale,
float& innerWidth);
/**
* @brief 把输入框内的横坐标换算为字符偏移(就近取整,用于鼠标定位插入符)。
* @param [IN] font 字体句柄。
* @param [IN] text 当前文本。
* @param [IN] localX 相对文本起点的横坐标(可超出 [0, 文本宽度])。
* @return 字符偏移。
*/
std::size_t UiEditOffsetAt(HFONT font, const std::wstring& text, float localX);
/**
* @brief 在超出可用宽度时对文本做尾部省略截断(不绘制任何内容)。
* @param [IN] text 原始文本。
* @param [IN] font 字体句柄。
* @param [IN] maxWidth 允许的最大像素宽度。
* @return 截断后的文本。
*/
std::wstring UiEllipsize(const std::wstring& text, HFONT font, float maxWidth);
/**
* @brief 处理输入框内的鼠标按下:命中则把插入符定位到最近的字符边界并开始拖拽框选。
* @param [IN] font 字体句柄。
* @param [IN] rect 输入框矩形。
* @param [IN,OUT] edit 编辑模型。
* @param [IN] scale 缩放比例。
* @param [IN] x 客户区横坐标。
* @param [IN] y 客户区纵坐标。
* @return 命中输入框并开始拖拽返回 true。
*/
bool UiEditFieldMouseDown(HFONT font, const gd::RectF& rect, TextEdit& edit, float scale, int x, int y);
/**
* @brief 处理输入框内的鼠标拖拽移动(扩展选区)。
* @param [IN] font 字体句柄。
* @param [IN] rect 输入框矩形。
* @param [IN,OUT] edit 编辑模型。
* @param [IN] scale 缩放比例。
* @param [IN] x 客户区横坐标。
* @return 选区发生变化返回 true。
*/
bool UiEditFieldMouseMove(HFONT font, const gd::RectF& rect, TextEdit& edit, float scale, int x);
/**
* @brief 在矩形中心绘制一个加号图标。
* @param [IN,OUT] g 绘图上下文。
* @param [IN] rect 图标布局矩形。
* @param [IN] argb 线条颜色。
* @param [IN] strokeWidth 线条宽度。
*/
void UiDrawPlusIcon(gd::Graphics& g, const gd::RectF& rect, std::uint32_t argb, float strokeWidth);
/**
* @brief 在矩形中心绘制一个叉号(关闭)图标。
* @param [IN,OUT] g 绘图上下文。
* @param [IN] rect 图标布局矩形。
* @param [IN] argb 线条颜色。
* @param [IN] strokeWidth 线条宽度。
*/
void UiDrawCrossIcon(gd::Graphics& g, const gd::RectF& rect, std::uint32_t argb, float strokeWidth);
/**
* @brief 在矩形中心绘制一个实心三角形指示器。
* @param [IN,OUT] g 绘图上下文。
* @param [IN] rect 图标布局矩形。
* @param [IN] argb 填充颜色。
* @param [IN] direction 三角形朝向。
*/
void UiDrawTriangleIcon(gd::Graphics& g, const gd::RectF& rect, std::uint32_t argb, IconDirection direction);
/**
* @brief 将滑块轨道上的横坐标换算为取值比例。
* @param [IN] trackRect 轨道矩形。
* @param [IN] x 横坐标。
* @return 取值比例,范围 0-1。
*/
double UiSliderRatioFromX(const gd::RectF& trackRect, float x);
/**
* @brief 绘制水平滑块(轨道 + 已填充部分 + 圆钮)。
* @param [IN,OUT] g 绘图上下文。
* @param [IN] track 轨道矩形。
* @param [IN] ratio 当前值比例(0-1)。
* @param [IN] fillColor 已填充部分颜色。
* @param [IN] active 是否处于悬停或拖拽状态(影响圆钮半径)。
* @param [IN] scale DPI 缩放比例。
*/
void UiDrawSlider(gd::Graphics& g, const gd::RectF& track, double ratio, std::uint32_t fillColor,
bool active, float scale);
/**
* @brief 绘制单行内联输入框(含选区高亮、插入符与水平滚动)。
*
* 初始状态为“整体选中”时以强调色底衬显示全部内容,首字符键入即整体替换,
* 交互观感与常见登录界面的账号/密码输入框一致;文本超出框宽时自动横向滚动,
* 保证插入符始终可见。
*
* @param [IN,OUT] g 绘图上下文。
* @param [IN] rect 输入框矩形。
* @param [IN] edit 输入框编辑模型。
* @param [IN] font 文本字体。
* @param [IN] scale DPI 缩放比例。
*/
void UiDrawEditField(gd::Graphics& g, const gd::RectF& rect, const TextEdit& edit, HFONT font,
float scale);
/**
* @brief 只绘制输入框的**内容**(选区高亮 / 文本 / 插入符),不绘制底板与边框。
*
* 用于"宿主已经画好了外框"的场景(例如信号面板的「筛选信号」输入框):若直接调用
* UiDrawEditField,会在外框内部再套一个带边框的圆角框,形成一圈多余的内圈。
*
* @param [IN,OUT] g 绘图上下文。
* @param [IN] rect 输入框矩形(内容区,几何与 UiDrawEditField 完全一致)。
* @param [IN] edit 输入框编辑模型。
* @param [IN] font 文本字体。
* @param [IN] scale DPI 缩放比例。
*/
void UiDrawEditFieldContent(gd::Graphics& g, const gd::RectF& rect, const TextEdit& edit, HFONT font,
float scale);
/**
* @brief 绘制一个带边框的复选框。
* @param [IN,OUT] g 绘图上下文。
* @param [IN] rect 复选框矩形。
* @param [IN] state 选中状态:0 未选,1 全选,2 部分选。
* @param [IN] radius 圆角半径。
*/
void UiDrawCheckBox(gd::Graphics& g, const gd::RectF& rect, int state, float radius);
/**
* @brief 绘制颜色样块:叠加不透明度填充,透明度过低时补充描边以保证可辨识。
* @param [IN,OUT] g 绘图上下文。
* @param [IN] rect 样块矩形。
* @param [IN] color 基础颜色(0xAARRGGBB,其 Alpha 通道会被不透明度覆盖)。
* @param [IN] opacity 不透明度(0-1)。
* @param [IN] radius 圆角半径。
* @param [IN] scale DPI 缩放比例,用于推导描边粗细。
* @return 实际填充颜色的 0xAARRGGBB 值(供需要同步其他元素的调用方使用)。
*/
std::uint32_t UiDrawSwatch(gd::Graphics& g, const gd::RectF& rect, std::uint32_t color, float opacity,
float radius, float scale);
/**
* @brief 判断给定不透明度是否低到需要为样块补充描边。
* @param [IN] opacity 不透明度(0-1)。
* @return 需要描边返回 true。
*/
bool UiSwatchNeedsOutline(float opacity);
} // namespace jdi
#endif // __PAINT_UTIL_HPP__