299 lines
9.5 KiB
C++
299 lines
9.5 KiB
C++
#include "candidate_window_d2d.h"
|
|
#include <vector>
|
|
#include "utils/common_utils.h"
|
|
#include <debugapi.h>
|
|
#include <fmt/xchar.h>
|
|
|
|
struct CandWndUiInfo
|
|
{
|
|
float x, y, w, h;
|
|
float fontSize;
|
|
float fontSizeOfNum;
|
|
float marginLeft, marginRight, marginTop, marginBottom;
|
|
float paddingLeft, paddingRight, paddingTop, paddingBottom;
|
|
} candWndUiInfo;
|
|
|
|
struct CandItemUiInfo
|
|
{
|
|
float x, y, w, h;
|
|
float fontSize;
|
|
float marginLeft, marginRight, marginTop, marginBottom;
|
|
float paddingLeft, paddingRight, paddingTop, paddingBottom;
|
|
} candItemUiInfo[8];
|
|
|
|
struct GeneralCandItemUiInfo
|
|
{
|
|
float lineHeight;
|
|
float marginLeft, marginRight, marginTop, marginBottom;
|
|
float paddingLeft, paddingRight, paddingTop, paddingBottom;
|
|
} generalCandItemUiInfo;
|
|
|
|
int initUiInfo()
|
|
{
|
|
candWndUiInfo.x = 0.0f;
|
|
candWndUiInfo.y = 0.0f;
|
|
candWndUiInfo.fontSize = 17.0f;
|
|
candWndUiInfo.fontSizeOfNum = 14.0f;
|
|
candWndUiInfo.marginLeft = 0.0f;
|
|
candWndUiInfo.marginRight = 0.0f;
|
|
candWndUiInfo.marginTop = 0.0f;
|
|
candWndUiInfo.marginBottom = 0.0f;
|
|
candWndUiInfo.paddingLeft = 0.0f;
|
|
candWndUiInfo.paddingRight = 0.0f;
|
|
candWndUiInfo.paddingTop = 0.0f;
|
|
candWndUiInfo.paddingBottom = 0.0f;
|
|
|
|
generalCandItemUiInfo.lineHeight = 26.0f;
|
|
generalCandItemUiInfo.paddingLeft = 2.0f;
|
|
generalCandItemUiInfo.lineHeight = 26.0f;
|
|
generalCandItemUiInfo.lineHeight = 26.0f;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int computeUiInfo(std::vector<std::wstring> lines)
|
|
{
|
|
// MeasureTextWidth(pDWriteFactory, pTextFormat, lines[0]);
|
|
candItemUiInfo[0].x = 8.0f;
|
|
candItemUiInfo[0].y = 0.0f;
|
|
candItemUiInfo[0].w = 56.0f;
|
|
candItemUiInfo[0].h = 26.0f;
|
|
candItemUiInfo[0].fontSize = 17.0f;
|
|
candItemUiInfo[0].marginLeft = 0.0f;
|
|
candItemUiInfo[0].marginRight = 0.0f;
|
|
candItemUiInfo[0].marginTop = 0.0f;
|
|
candItemUiInfo[0].marginBottom = 0.0f;
|
|
candItemUiInfo[0].paddingLeft = 0.0f;
|
|
candItemUiInfo[0].paddingRight = 0.0f;
|
|
candItemUiInfo[0].paddingTop = 0.0f;
|
|
candItemUiInfo[0].paddingBottom = 0.0f;
|
|
return 0;
|
|
}
|
|
|
|
bool InitD2DAndDWrite()
|
|
{
|
|
initUiInfo();
|
|
// Direct2D
|
|
HRESULT hr = D2D1CreateFactory( //
|
|
D2D1_FACTORY_TYPE_SINGLE_THREADED, //
|
|
IID_PPV_ARGS(pD2DFactory.GetAddressOf()) //
|
|
);
|
|
if (FAILED(hr))
|
|
return false;
|
|
|
|
// DirectWrite
|
|
hr = DWriteCreateFactory( //
|
|
DWRITE_FACTORY_TYPE_SHARED, //
|
|
__uuidof(IDWriteFactory), //
|
|
reinterpret_cast<IUnknown **>(pDWriteFactory.GetAddressOf()) //
|
|
);
|
|
if (FAILED(hr))
|
|
return false;
|
|
|
|
// TextFormat
|
|
hr = pDWriteFactory->CreateTextFormat( //
|
|
L"Noto Sans SC", //
|
|
nullptr, //
|
|
DWRITE_FONT_WEIGHT_NORMAL, //
|
|
DWRITE_FONT_STYLE_NORMAL, //
|
|
DWRITE_FONT_STRETCH_NORMAL, //
|
|
candWndUiInfo.fontSize, //
|
|
L"zh-cn", //
|
|
pTextFormat.GetAddressOf() //
|
|
);
|
|
|
|
// TextFormatOfNum
|
|
hr = pDWriteFactory->CreateTextFormat( //
|
|
L"Noto Sans SC", //
|
|
nullptr, //
|
|
DWRITE_FONT_WEIGHT_NORMAL, //
|
|
DWRITE_FONT_STYLE_NORMAL, //
|
|
DWRITE_FONT_STRETCH_NORMAL, //
|
|
candWndUiInfo.fontSizeOfNum, //
|
|
L"zh-cn", //
|
|
pTextFormatOfNum.GetAddressOf() //
|
|
);
|
|
|
|
if (FAILED(hr))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool InitD2DRenderTarget(HWND hwnd)
|
|
{
|
|
if (!pD2DFactory)
|
|
return false;
|
|
|
|
RECT rc;
|
|
GetClientRect(hwnd, &rc);
|
|
|
|
HRESULT hr = pD2DFactory->CreateHwndRenderTarget( //
|
|
D2D1::RenderTargetProperties( //
|
|
D2D1_RENDER_TARGET_TYPE_DEFAULT, //
|
|
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED) //
|
|
), //
|
|
D2D1::HwndRenderTargetProperties( //
|
|
hwnd, //
|
|
D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top), //
|
|
D2D1_PRESENT_OPTIONS_IMMEDIATELY //
|
|
), //
|
|
pRenderTarget.GetAddressOf() //
|
|
);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), pBrush.GetAddressOf());
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pRenderTarget.As(&pDeviceContext);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CreateBlurEffect();
|
|
}
|
|
|
|
return SUCCEEDED(hr);
|
|
}
|
|
|
|
void CreateBlurEffect()
|
|
{
|
|
if (!pDeviceContext)
|
|
return;
|
|
|
|
HRESULT hr = pDeviceContext->CreateEffect(CLSID_D2D1GaussianBlur, &pGaussianBlurEffect);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pGaussianBlurEffect->SetValue(D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION, 3.0f);
|
|
pGaussianBlurEffect->SetValue(D2D1_GAUSSIANBLUR_PROP_BORDER_MODE, D2D1_BORDER_MODE_HARD);
|
|
}
|
|
}
|
|
|
|
float MeasureTextWidth( //
|
|
ComPtr<IDWriteFactory> &pDWriteFactory, //
|
|
ComPtr<IDWriteTextFormat> &pTextFormat, //
|
|
const std::wstring &text //
|
|
)
|
|
{
|
|
ComPtr<IDWriteTextLayout> pTextLayout;
|
|
|
|
HRESULT hr = pDWriteFactory->CreateTextLayout( //
|
|
text.c_str(), //
|
|
static_cast<UINT32>(text.length()), //
|
|
pTextFormat.Get(), //
|
|
1000.0f, // Max width enough to avoid wrapping
|
|
1000.0f, // Max height
|
|
pTextLayout.GetAddressOf() //
|
|
);
|
|
|
|
if (FAILED(hr) || !pTextLayout)
|
|
return 0.0f;
|
|
|
|
DWRITE_TEXT_METRICS metrics;
|
|
hr = pTextLayout->GetMetrics(&metrics);
|
|
if (FAILED(hr))
|
|
return 0.0f;
|
|
|
|
return metrics.width; // Accurate width in pixels
|
|
}
|
|
|
|
void PaintCandidates(HWND hwnd, std::wstring &text)
|
|
{
|
|
if (!pRenderTarget || !pDeviceContext)
|
|
return;
|
|
|
|
pRenderTarget->BeginDraw();
|
|
|
|
/* Clear to transparent */
|
|
pRenderTarget->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f)); // Fully transparent
|
|
|
|
/* Draw shadow/blur effect only once */
|
|
ComPtr<ID2D1BitmapRenderTarget> pCompatibleRenderTarget;
|
|
HRESULT hr = pRenderTarget->CreateCompatibleRenderTarget(&pCompatibleRenderTarget);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pCompatibleRenderTarget->BeginDraw();
|
|
pCompatibleRenderTarget->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f)); // Clear shadow render target
|
|
|
|
/* Draw background to compatible render target */
|
|
pBrush->SetColor(D2D1::ColorF(10.0 / 255.0f, 10.0 / 255.0f, 10.0 / 255.0f, 0.5f));
|
|
D2D1_ROUNDED_RECT roundedRect = {
|
|
D2D1::RectF( //
|
|
6.0f, //
|
|
6.0f, //
|
|
126.0f / 1.25f, //
|
|
302.0f / 1.25f), //
|
|
8.0f, //
|
|
8.0f //
|
|
};
|
|
pCompatibleRenderTarget->FillRoundedRectangle(roundedRect, pBrush.Get());
|
|
pCompatibleRenderTarget->EndDraw();
|
|
|
|
/* Get bitmap and apply blur */
|
|
ComPtr<ID2D1Bitmap> pBitmap;
|
|
hr = pCompatibleRenderTarget->GetBitmap(&pBitmap);
|
|
if (SUCCEEDED(hr) && pGaussianBlurEffect)
|
|
{
|
|
pGaussianBlurEffect->SetInput(0, pBitmap.Get());
|
|
pDeviceContext->DrawImage(pGaussianBlurEffect.Get());
|
|
}
|
|
}
|
|
|
|
/* Draw main content */
|
|
pBrush->SetColor(D2D1::ColorF(22.0f / 255.0f, 22.0f / 255.0f, 22.0f / 255.0f, 1.0f));
|
|
D2D1_ROUNDED_RECT roundedRect = {
|
|
D2D1::RectF( //
|
|
0.0f, //
|
|
0.0f, //
|
|
120.0f / 1.25f, //
|
|
296.0f / 1.25f), //
|
|
8.0f, //
|
|
8.0f //
|
|
};
|
|
pRenderTarget->FillRoundedRectangle(roundedRect, pBrush.Get());
|
|
|
|
/* Draw text content */
|
|
std::vector<std::wstring> lines = CommonUtils::cvt_str_to_vector(text);
|
|
float lineHeight = 26.0f;
|
|
float x = 8.0f;
|
|
float y = 0.0f;
|
|
|
|
for (int i = 0; i < lines.size(); ++i)
|
|
{
|
|
if (i == 1)
|
|
{
|
|
float radius = 6.0f;
|
|
float width = MeasureTextWidth(pDWriteFactory, pTextFormat, lines[i]);
|
|
if (width < 56)
|
|
width = 56;
|
|
|
|
D2D1_ROUNDED_RECT roundedRect = {D2D1::RectF(x - 3.0f, y, x + width + 5.0f, y + lineHeight - 1.0f), radius,
|
|
radius};
|
|
pBrush->SetColor(D2D1::ColorF(D2D1::ColorF::LightBlue, 0.3f));
|
|
pRenderTarget->FillRoundedRectangle(roundedRect, pBrush.Get());
|
|
pBrush->SetColor(D2D1::ColorF(D2D1::ColorF::Pink, 1.0f));
|
|
}
|
|
else
|
|
{
|
|
pBrush->SetColor(D2D1::ColorF(D2D1::ColorF::White, 1.0f));
|
|
}
|
|
|
|
pRenderTarget->DrawText(lines[i].c_str(), static_cast<UINT32>(lines[i].length()), pTextFormat.Get(),
|
|
D2D1::RectF(x, y, 590.0f, y + lineHeight), pBrush.Get());
|
|
y += lineHeight;
|
|
}
|
|
|
|
hr = pRenderTarget->EndDraw();
|
|
if (hr == D2DERR_RECREATE_TARGET)
|
|
{
|
|
pRenderTarget.Reset();
|
|
pDeviceContext.Reset();
|
|
pGaussianBlurEffect.Reset();
|
|
InitD2DRenderTarget(hwnd);
|
|
}
|
|
|
|
ValidateRect(hwnd, nullptr);
|
|
} |