#include "PaintUtil.hpp" #include namespace jdi { namespace { /** * @brief 构造圆角矩形路径。 * @param [IN,OUT] path 目标路径对象。 * @param [IN] rect 目标矩形。 * @param [IN] radius 圆角半径。 */ void BuildRoundRectPath(gd::GraphicsPath& path, const gd::RectF& rect, float radius) { if (rect.Width <= 0.0f || rect.Height <= 0.0f) { return; } float diameter = radius * 2.0f; if (diameter > rect.Width) { diameter = rect.Width; } if (diameter > rect.Height) { diameter = rect.Height; } if (diameter < 1.0f) { path.AddRectangle(rect); return; } const float right = rect.X + rect.Width; const float bottom = rect.Y + rect.Height; path.AddArc(rect.X, rect.Y, diameter, diameter, 180.0f, 90.0f); path.AddArc(right - diameter, rect.Y, diameter, diameter, 270.0f, 90.0f); path.AddArc(right - diameter, bottom - diameter, diameter, diameter, 0.0f, 90.0f); path.AddArc(rect.X, bottom - diameter, diameter, diameter, 90.0f, 90.0f); path.CloseFigure(); } /** * @brief 创建指定字号的界面字体。 * @param [IN] face 字体名称。 * @param [IN] pixelHeight 字符高度(像素)。 * @param [IN] bold 是否加粗。 * @return 字体句柄,失败返回 nullptr。 */ HFONT CreateUiFont(const wchar_t* face, int pixelHeight, bool bold) { LOGFONTW logFont = {}; logFont.lfHeight = -pixelHeight; logFont.lfWeight = bold ? FW_SEMIBOLD : FW_NORMAL; logFont.lfCharSet = DEFAULT_CHARSET; logFont.lfOutPrecision = OUT_TT_PRECIS; logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS; logFont.lfQuality = CLEARTYPE_QUALITY; logFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; wcsncpy(logFont.lfFaceName, face, LF_FACESIZE - 1); logFont.lfFaceName[LF_FACESIZE - 1] = L'\0'; HFONT font = CreateFontIndirectW(&logFont); if (font == nullptr) { font = static_cast(GetStockObject(DEFAULT_GUI_FONT)); } return font; } } bool UiFonts::Create(UINT targetDpi) { Destroy(); dpi = (targetDpi == 0) ? 96u : targetDpi; const double scale = static_cast(dpi) / 96.0; const int uiHeight = static_cast(std::lround(14.0 * scale)); const int smallHeight = static_cast(std::lround(12.0 * scale)); const int titleHeight = static_cast(std::lround(15.0 * scale)); const int monoHeight = static_cast(std::lround(13.0 * scale)); ui = CreateUiFont(L"Segoe UI", uiHeight, false); uiBold = CreateUiFont(L"Segoe UI Semibold", uiHeight, true); uiSmall = CreateUiFont(L"Segoe UI", smallHeight, false); uiSmallBold = CreateUiFont(L"Segoe UI Semibold", smallHeight, true); title = CreateUiFont(L"Segoe UI Semibold", titleHeight, true); mono = CreateUiFont(L"Consolas", monoHeight, false); monoSmall = CreateUiFont(L"Consolas", smallHeight, false); return ui != nullptr && uiBold != nullptr && uiSmall != nullptr && title != nullptr && mono != nullptr; } void UiFonts::Destroy() { const HFONT resources[] = {ui, uiBold, uiSmall, uiSmallBold, title, mono, monoSmall}; for (const HFONT font : resources) { if (font != nullptr) { DeleteObject(font); } } ui = nullptr; uiBold = nullptr; uiSmall = nullptr; uiSmallBold = nullptr; title = nullptr; mono = nullptr; monoSmall = nullptr; } void UiFillRect(gd::Graphics& g, const gd::RectF& rect, std::uint32_t argb) { if (rect.Width <= 0.0f || rect.Height <= 0.0f) { return; } gd::SolidBrush brush(ToGdiColor(argb)); g.FillRectangle(&brush, rect); } void UiFillRoundRect(gd::Graphics& g, const gd::RectF& rect, float radius, std::uint32_t argb) { if (rect.Width <= 0.0f || rect.Height <= 0.0f) { return; } gd::GraphicsPath path; BuildRoundRectPath(path, rect, radius); gd::SolidBrush brush(ToGdiColor(argb)); g.FillPath(&brush, &path); } void UiDrawRoundRect(gd::Graphics& g, const gd::RectF& rect, float radius, std::uint32_t argb, float width) { if (rect.Width <= 0.0f || rect.Height <= 0.0f) { return; } gd::GraphicsPath path; BuildRoundRectPath(path, rect, radius); gd::Pen pen(ToGdiColor(argb), width); g.DrawPath(&pen, &path); } void UiDrawLine(gd::Graphics& g, float x1, float y1, float x2, float y2, std::uint32_t argb, float width) { gd::Pen pen(ToGdiColor(argb), width); pen.SetStartCap(gd::LineCapFlat); pen.SetEndCap(gd::LineCapFlat); g.DrawLine(&pen, x1, y1, x2, y2); } void UiDrawDashedLine(gd::Graphics& g, float x1, float y1, float x2, float y2, std::uint32_t argb, float width, gd::DashStyle dashPattern) { gd::Pen pen(ToGdiColor(argb), width); pen.SetDashStyle(dashPattern); pen.SetStartCap(gd::LineCapFlat); pen.SetEndCap(gd::LineCapFlat); g.DrawLine(&pen, x1, y1, x2, y2); } void UiText(gd::Graphics& g, const std::wstring& text, HFONT font, const gd::RectF& rect, std::uint32_t argb, TextAlign align) { if (text.empty() || font == nullptr || rect.Width <= 0.0f || rect.Height <= 0.0f) { return; } HDC hdc = g.GetHDC(); if (hdc == nullptr) { return; } HGDIOBJ oldFont = SelectObject(hdc, font); const int oldMode = SetBkMode(hdc, TRANSPARENT); const COLORREF oldColor = SetTextColor(hdc, ToColorRef(argb)); RECT rc; rc.left = static_cast(std::floor(rect.X)); rc.top = static_cast(std::floor(rect.Y)); rc.right = static_cast(std::ceil(rect.X + rect.Width)); rc.bottom = static_cast(std::ceil(rect.Y + rect.Height)); UINT flags = DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX | DT_END_ELLIPSIS; switch (align) { case TextAlign::CENTER: flags |= DT_CENTER; break; case TextAlign::RIGHT: flags |= DT_RIGHT; break; case TextAlign::LEFT: default: flags |= DT_LEFT; break; } DrawTextW(hdc, text.c_str(), static_cast(text.size()), &rc, flags); SetTextColor(hdc, oldColor); SetBkMode(hdc, oldMode); SelectObject(hdc, oldFont); g.ReleaseHDC(hdc); } float UiTextWidth(gd::Graphics& g, const std::wstring& text, HFONT font) { if (text.empty() || font == nullptr) { return 0.0f; } HDC hdc = g.GetHDC(); if (hdc == nullptr) { return 0.0f; } HGDIOBJ oldFont = SelectObject(hdc, font); SIZE size = {}; GetTextExtentPoint32W(hdc, text.c_str(), static_cast(text.size()), &size); SelectObject(hdc, oldFont); g.ReleaseHDC(hdc); return static_cast(size.cx); } float UiMeasureTextWidth(HFONT font, const std::wstring& text) { if (text.empty() || font == nullptr) { return 0.0f; } HDC hdc = GetDC(nullptr); if (hdc == nullptr) { return 0.0f; } HGDIOBJ oldFont = SelectObject(hdc, font); SIZE size = {}; GetTextExtentPoint32W(hdc, text.c_str(), static_cast(text.size()), &size); SelectObject(hdc, oldFont); ReleaseDC(nullptr, hdc); return static_cast(size.cx); } std::wstring UiEllipsize(gd::Graphics& g, const std::wstring& text, HFONT font, float maxWidth) { if (text.empty() || font == nullptr || maxWidth <= 0.0f) { return std::wstring(); } if (UiTextWidth(g, text, font) <= maxWidth) { return text; } const std::wstring ellipsis = L"…"; const float ellipsisWidth = UiTextWidth(g, ellipsis, font); if (ellipsisWidth > maxWidth) { return std::wstring(); } std::wstring result = text; while (!result.empty()) { result.pop_back(); if (UiTextWidth(g, result, font) + ellipsisWidth <= maxWidth) { return result + ellipsis; } } return ellipsis; } } // namespace jdi