1*8af74909SZhong Yang // Scintilla source code edit control
2*8af74909SZhong Yang /** @file PlatWin.cxx
3*8af74909SZhong Yang ** Implementation of platform facilities on Windows.
4*8af74909SZhong Yang **/
5*8af74909SZhong Yang // Copyright 1998-2003 by Neil Hodgson <[email protected]>
6*8af74909SZhong Yang // The License.txt file describes the conditions under which this software may be distributed.
7*8af74909SZhong Yang
8*8af74909SZhong Yang #include <cstddef>
9*8af74909SZhong Yang #include <cstdlib>
10*8af74909SZhong Yang #include <cstring>
11*8af74909SZhong Yang #include <cstdio>
12*8af74909SZhong Yang #include <cstdarg>
13*8af74909SZhong Yang #include <ctime>
14*8af74909SZhong Yang #include <cmath>
15*8af74909SZhong Yang #include <climits>
16*8af74909SZhong Yang
17*8af74909SZhong Yang #include <string_view>
18*8af74909SZhong Yang #include <vector>
19*8af74909SZhong Yang #include <map>
20*8af74909SZhong Yang #include <algorithm>
21*8af74909SZhong Yang #include <iterator>
22*8af74909SZhong Yang #include <memory>
23*8af74909SZhong Yang #include <mutex>
24*8af74909SZhong Yang
25*8af74909SZhong Yang // Want to use std::min and std::max so don't want Windows.h version of min and max
26*8af74909SZhong Yang #if !defined(NOMINMAX)
27*8af74909SZhong Yang #define NOMINMAX
28*8af74909SZhong Yang #endif
29*8af74909SZhong Yang #undef _WIN32_WINNT
30*8af74909SZhong Yang #define _WIN32_WINNT 0x0500
31*8af74909SZhong Yang #undef WINVER
32*8af74909SZhong Yang #define WINVER 0x0500
33*8af74909SZhong Yang #include <windows.h>
34*8af74909SZhong Yang #include <commctrl.h>
35*8af74909SZhong Yang #include <richedit.h>
36*8af74909SZhong Yang #include <windowsx.h>
37*8af74909SZhong Yang
38*8af74909SZhong Yang #if !defined(DISABLE_D2D)
39*8af74909SZhong Yang #define USE_D2D 1
40*8af74909SZhong Yang #endif
41*8af74909SZhong Yang
42*8af74909SZhong Yang #if defined(USE_D2D)
43*8af74909SZhong Yang #include <d2d1.h>
44*8af74909SZhong Yang #include <dwrite.h>
45*8af74909SZhong Yang #endif
46*8af74909SZhong Yang
47*8af74909SZhong Yang #include "Platform.h"
48*8af74909SZhong Yang #include "XPM.h"
49*8af74909SZhong Yang #include "UniConversion.h"
50*8af74909SZhong Yang #include "DBCS.h"
51*8af74909SZhong Yang #include "FontQuality.h"
52*8af74909SZhong Yang
53*8af74909SZhong Yang #include "PlatWin.h"
54*8af74909SZhong Yang
55*8af74909SZhong Yang #ifndef SPI_GETFONTSMOOTHINGCONTRAST
56*8af74909SZhong Yang #define SPI_GETFONTSMOOTHINGCONTRAST 0x200C
57*8af74909SZhong Yang #endif
58*8af74909SZhong Yang
59*8af74909SZhong Yang #ifndef LOAD_LIBRARY_SEARCH_SYSTEM32
60*8af74909SZhong Yang #define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800
61*8af74909SZhong Yang #endif
62*8af74909SZhong Yang
63*8af74909SZhong Yang // __uuidof is a Microsoft extension but makes COM code neater, so disable warning
64*8af74909SZhong Yang #if defined(__clang__)
65*8af74909SZhong Yang #pragma clang diagnostic ignored "-Wlanguage-extension-token"
66*8af74909SZhong Yang #endif
67*8af74909SZhong Yang
68*8af74909SZhong Yang namespace Scintilla {
69*8af74909SZhong Yang
70*8af74909SZhong Yang UINT CodePageFromCharSet(DWORD characterSet, UINT documentCodePage) noexcept;
71*8af74909SZhong Yang
72*8af74909SZhong Yang #if defined(USE_D2D)
73*8af74909SZhong Yang IDWriteFactory *pIDWriteFactory = nullptr;
74*8af74909SZhong Yang ID2D1Factory *pD2DFactory = nullptr;
75*8af74909SZhong Yang IDWriteRenderingParams *defaultRenderingParams = nullptr;
76*8af74909SZhong Yang IDWriteRenderingParams *customClearTypeRenderingParams = nullptr;
77*8af74909SZhong Yang D2D1_DRAW_TEXT_OPTIONS d2dDrawTextOptions = D2D1_DRAW_TEXT_OPTIONS_NONE;
78*8af74909SZhong Yang
79*8af74909SZhong Yang static HMODULE hDLLD2D {};
80*8af74909SZhong Yang static HMODULE hDLLDWrite {};
81*8af74909SZhong Yang
LoadD2DOnce()82*8af74909SZhong Yang void LoadD2DOnce() noexcept {
83*8af74909SZhong Yang DWORD loadLibraryFlags = 0;
84*8af74909SZhong Yang HMODULE kernel32 = ::GetModuleHandleW(L"kernel32.dll");
85*8af74909SZhong Yang if (kernel32) {
86*8af74909SZhong Yang if (::GetProcAddress(kernel32, "SetDefaultDllDirectories")) {
87*8af74909SZhong Yang // Availability of SetDefaultDllDirectories implies Windows 8+ or
88*8af74909SZhong Yang // that KB2533623 has been installed so LoadLibraryEx can be called
89*8af74909SZhong Yang // with LOAD_LIBRARY_SEARCH_SYSTEM32.
90*8af74909SZhong Yang loadLibraryFlags = LOAD_LIBRARY_SEARCH_SYSTEM32;
91*8af74909SZhong Yang }
92*8af74909SZhong Yang }
93*8af74909SZhong Yang
94*8af74909SZhong Yang typedef HRESULT (WINAPI *D2D1CFSig)(D2D1_FACTORY_TYPE factoryType, REFIID riid,
95*8af74909SZhong Yang CONST D2D1_FACTORY_OPTIONS *pFactoryOptions, IUnknown **factory);
96*8af74909SZhong Yang typedef HRESULT (WINAPI *DWriteCFSig)(DWRITE_FACTORY_TYPE factoryType, REFIID iid,
97*8af74909SZhong Yang IUnknown **factory);
98*8af74909SZhong Yang
99*8af74909SZhong Yang hDLLD2D = ::LoadLibraryEx(TEXT("D2D1.DLL"), 0, loadLibraryFlags);
100*8af74909SZhong Yang D2D1CFSig fnD2DCF = DLLFunction<D2D1CFSig>(hDLLD2D, "D2D1CreateFactory");
101*8af74909SZhong Yang if (fnD2DCF) {
102*8af74909SZhong Yang // A single threaded factory as Scintilla always draw on the GUI thread
103*8af74909SZhong Yang fnD2DCF(D2D1_FACTORY_TYPE_SINGLE_THREADED,
104*8af74909SZhong Yang __uuidof(ID2D1Factory),
105*8af74909SZhong Yang nullptr,
106*8af74909SZhong Yang reinterpret_cast<IUnknown**>(&pD2DFactory));
107*8af74909SZhong Yang }
108*8af74909SZhong Yang hDLLDWrite = ::LoadLibraryEx(TEXT("DWRITE.DLL"), 0, loadLibraryFlags);
109*8af74909SZhong Yang DWriteCFSig fnDWCF = DLLFunction<DWriteCFSig>(hDLLDWrite, "DWriteCreateFactory");
110*8af74909SZhong Yang if (fnDWCF) {
111*8af74909SZhong Yang const GUID IID_IDWriteFactory2 = // 0439fc60-ca44-4994-8dee-3a9af7b732ec
112*8af74909SZhong Yang { 0x0439fc60, 0xca44, 0x4994, { 0x8d, 0xee, 0x3a, 0x9a, 0xf7, 0xb7, 0x32, 0xec } };
113*8af74909SZhong Yang
114*8af74909SZhong Yang const HRESULT hr = fnDWCF(DWRITE_FACTORY_TYPE_SHARED,
115*8af74909SZhong Yang IID_IDWriteFactory2,
116*8af74909SZhong Yang reinterpret_cast<IUnknown**>(&pIDWriteFactory));
117*8af74909SZhong Yang if (SUCCEEDED(hr)) {
118*8af74909SZhong Yang // D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT
119*8af74909SZhong Yang d2dDrawTextOptions = static_cast<D2D1_DRAW_TEXT_OPTIONS>(0x00000004);
120*8af74909SZhong Yang } else {
121*8af74909SZhong Yang fnDWCF(DWRITE_FACTORY_TYPE_SHARED,
122*8af74909SZhong Yang __uuidof(IDWriteFactory),
123*8af74909SZhong Yang reinterpret_cast<IUnknown**>(&pIDWriteFactory));
124*8af74909SZhong Yang }
125*8af74909SZhong Yang }
126*8af74909SZhong Yang
127*8af74909SZhong Yang if (pIDWriteFactory) {
128*8af74909SZhong Yang const HRESULT hr = pIDWriteFactory->CreateRenderingParams(&defaultRenderingParams);
129*8af74909SZhong Yang if (SUCCEEDED(hr)) {
130*8af74909SZhong Yang unsigned int clearTypeContrast;
131*8af74909SZhong Yang if (::SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &clearTypeContrast, 0)) {
132*8af74909SZhong Yang
133*8af74909SZhong Yang FLOAT gamma;
134*8af74909SZhong Yang if (clearTypeContrast >= 1000 && clearTypeContrast <= 2200)
135*8af74909SZhong Yang gamma = static_cast<FLOAT>(clearTypeContrast) / 1000.0f;
136*8af74909SZhong Yang else
137*8af74909SZhong Yang gamma = defaultRenderingParams->GetGamma();
138*8af74909SZhong Yang
139*8af74909SZhong Yang pIDWriteFactory->CreateCustomRenderingParams(gamma, defaultRenderingParams->GetEnhancedContrast(), defaultRenderingParams->GetClearTypeLevel(),
140*8af74909SZhong Yang defaultRenderingParams->GetPixelGeometry(), defaultRenderingParams->GetRenderingMode(), &customClearTypeRenderingParams);
141*8af74909SZhong Yang }
142*8af74909SZhong Yang }
143*8af74909SZhong Yang }
144*8af74909SZhong Yang }
145*8af74909SZhong Yang
LoadD2D()146*8af74909SZhong Yang bool LoadD2D() {
147*8af74909SZhong Yang static std::once_flag once;
148*8af74909SZhong Yang std::call_once(once, LoadD2DOnce);
149*8af74909SZhong Yang return pIDWriteFactory && pD2DFactory;
150*8af74909SZhong Yang }
151*8af74909SZhong Yang
152*8af74909SZhong Yang #endif
153*8af74909SZhong Yang
154*8af74909SZhong Yang struct FormatAndMetrics {
155*8af74909SZhong Yang int technology;
156*8af74909SZhong Yang HFONT hfont;
157*8af74909SZhong Yang #if defined(USE_D2D)
158*8af74909SZhong Yang IDWriteTextFormat *pTextFormat;
159*8af74909SZhong Yang #endif
160*8af74909SZhong Yang int extraFontFlag;
161*8af74909SZhong Yang int characterSet;
162*8af74909SZhong Yang FLOAT yAscent;
163*8af74909SZhong Yang FLOAT yDescent;
164*8af74909SZhong Yang FLOAT yInternalLeading;
FormatAndMetricsScintilla::FormatAndMetrics165*8af74909SZhong Yang FormatAndMetrics(HFONT hfont_, int extraFontFlag_, int characterSet_) noexcept :
166*8af74909SZhong Yang technology(SCWIN_TECH_GDI), hfont(hfont_),
167*8af74909SZhong Yang #if defined(USE_D2D)
168*8af74909SZhong Yang pTextFormat(nullptr),
169*8af74909SZhong Yang #endif
170*8af74909SZhong Yang extraFontFlag(extraFontFlag_), characterSet(characterSet_), yAscent(2), yDescent(1), yInternalLeading(0) {
171*8af74909SZhong Yang }
172*8af74909SZhong Yang #if defined(USE_D2D)
FormatAndMetricsScintilla::FormatAndMetrics173*8af74909SZhong Yang FormatAndMetrics(IDWriteTextFormat *pTextFormat_,
174*8af74909SZhong Yang int extraFontFlag_,
175*8af74909SZhong Yang int characterSet_,
176*8af74909SZhong Yang FLOAT yAscent_,
177*8af74909SZhong Yang FLOAT yDescent_,
178*8af74909SZhong Yang FLOAT yInternalLeading_) noexcept :
179*8af74909SZhong Yang technology(SCWIN_TECH_DIRECTWRITE),
180*8af74909SZhong Yang hfont{},
181*8af74909SZhong Yang pTextFormat(pTextFormat_),
182*8af74909SZhong Yang extraFontFlag(extraFontFlag_),
183*8af74909SZhong Yang characterSet(characterSet_),
184*8af74909SZhong Yang yAscent(yAscent_),
185*8af74909SZhong Yang yDescent(yDescent_),
186*8af74909SZhong Yang yInternalLeading(yInternalLeading_) {
187*8af74909SZhong Yang }
188*8af74909SZhong Yang #endif
189*8af74909SZhong Yang FormatAndMetrics(const FormatAndMetrics &) = delete;
190*8af74909SZhong Yang FormatAndMetrics(FormatAndMetrics &&) = delete;
191*8af74909SZhong Yang FormatAndMetrics &operator=(const FormatAndMetrics &) = delete;
192*8af74909SZhong Yang FormatAndMetrics &operator=(FormatAndMetrics &&) = delete;
193*8af74909SZhong Yang
~FormatAndMetricsScintilla::FormatAndMetrics194*8af74909SZhong Yang ~FormatAndMetrics() {
195*8af74909SZhong Yang if (hfont)
196*8af74909SZhong Yang ::DeleteObject(hfont);
197*8af74909SZhong Yang #if defined(USE_D2D)
198*8af74909SZhong Yang ReleaseUnknown(pTextFormat);
199*8af74909SZhong Yang #endif
200*8af74909SZhong Yang extraFontFlag = 0;
201*8af74909SZhong Yang characterSet = 0;
202*8af74909SZhong Yang yAscent = 2;
203*8af74909SZhong Yang yDescent = 1;
204*8af74909SZhong Yang yInternalLeading = 0;
205*8af74909SZhong Yang }
206*8af74909SZhong Yang HFONT HFont() noexcept;
207*8af74909SZhong Yang };
208*8af74909SZhong Yang
HFont()209*8af74909SZhong Yang HFONT FormatAndMetrics::HFont() noexcept {
210*8af74909SZhong Yang LOGFONTW lf = {};
211*8af74909SZhong Yang #if defined(USE_D2D)
212*8af74909SZhong Yang if (technology == SCWIN_TECH_GDI) {
213*8af74909SZhong Yang if (0 == ::GetObjectW(hfont, sizeof(lf), &lf)) {
214*8af74909SZhong Yang return {};
215*8af74909SZhong Yang }
216*8af74909SZhong Yang } else {
217*8af74909SZhong Yang const HRESULT hr = pTextFormat->GetFontFamilyName(lf.lfFaceName, LF_FACESIZE);
218*8af74909SZhong Yang if (!SUCCEEDED(hr)) {
219*8af74909SZhong Yang return {};
220*8af74909SZhong Yang }
221*8af74909SZhong Yang lf.lfWeight = pTextFormat->GetFontWeight();
222*8af74909SZhong Yang lf.lfItalic = pTextFormat->GetFontStyle() == DWRITE_FONT_STYLE_ITALIC;
223*8af74909SZhong Yang lf.lfHeight = -static_cast<int>(pTextFormat->GetFontSize());
224*8af74909SZhong Yang }
225*8af74909SZhong Yang #else
226*8af74909SZhong Yang if (0 == ::GetObjectW(hfont, sizeof(lf), &lf)) {
227*8af74909SZhong Yang return {};
228*8af74909SZhong Yang }
229*8af74909SZhong Yang #endif
230*8af74909SZhong Yang return ::CreateFontIndirectW(&lf);
231*8af74909SZhong Yang }
232*8af74909SZhong Yang
233*8af74909SZhong Yang #ifndef CLEARTYPE_QUALITY
234*8af74909SZhong Yang #define CLEARTYPE_QUALITY 5
235*8af74909SZhong Yang #endif
236*8af74909SZhong Yang
PointerFromWindow(HWND hWnd)237*8af74909SZhong Yang void *PointerFromWindow(HWND hWnd) noexcept {
238*8af74909SZhong Yang return reinterpret_cast<void *>(::GetWindowLongPtr(hWnd, 0));
239*8af74909SZhong Yang }
240*8af74909SZhong Yang
SetWindowPointer(HWND hWnd,void * ptr)241*8af74909SZhong Yang void SetWindowPointer(HWND hWnd, void *ptr) noexcept {
242*8af74909SZhong Yang ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(ptr));
243*8af74909SZhong Yang }
244*8af74909SZhong Yang
245*8af74909SZhong Yang namespace {
246*8af74909SZhong Yang
247*8af74909SZhong Yang // system DPI, same for all monitor.
248*8af74909SZhong Yang UINT uSystemDPI = USER_DEFAULT_SCREEN_DPI;
249*8af74909SZhong Yang
250*8af74909SZhong Yang using GetDpiForWindowSig = UINT(WINAPI *)(HWND hwnd);
251*8af74909SZhong Yang GetDpiForWindowSig fnGetDpiForWindow = nullptr;
252*8af74909SZhong Yang
253*8af74909SZhong Yang HMODULE hDLLShcore {};
254*8af74909SZhong Yang using GetDpiForMonitorSig = HRESULT (WINAPI *)(HMONITOR hmonitor, /*MONITOR_DPI_TYPE*/int dpiType, UINT *dpiX, UINT *dpiY);
255*8af74909SZhong Yang GetDpiForMonitorSig fnGetDpiForMonitor = nullptr;
256*8af74909SZhong Yang
257*8af74909SZhong Yang using GetSystemMetricsForDpiSig = int(WINAPI *)(int nIndex, UINT dpi);
258*8af74909SZhong Yang GetSystemMetricsForDpiSig fnGetSystemMetricsForDpi = nullptr;
259*8af74909SZhong Yang
260*8af74909SZhong Yang using AdjustWindowRectExForDpiSig = BOOL(WINAPI *)(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi);
261*8af74909SZhong Yang AdjustWindowRectExForDpiSig fnAdjustWindowRectExForDpi = nullptr;
262*8af74909SZhong Yang
LoadDpiForWindow()263*8af74909SZhong Yang void LoadDpiForWindow() noexcept {
264*8af74909SZhong Yang HMODULE user32 = ::GetModuleHandleW(L"user32.dll");
265*8af74909SZhong Yang fnGetDpiForWindow = DLLFunction<GetDpiForWindowSig>(user32, "GetDpiForWindow");
266*8af74909SZhong Yang fnGetSystemMetricsForDpi = DLLFunction<GetSystemMetricsForDpiSig>(user32, "GetSystemMetricsForDpi");
267*8af74909SZhong Yang fnAdjustWindowRectExForDpi = DLLFunction<AdjustWindowRectExForDpiSig>(user32, "AdjustWindowRectExForDpi");
268*8af74909SZhong Yang
269*8af74909SZhong Yang using GetDpiForSystemSig = UINT(WINAPI *)(void);
270*8af74909SZhong Yang GetDpiForSystemSig fnGetDpiForSystem = DLLFunction<GetDpiForSystemSig>(user32, "GetDpiForSystem");
271*8af74909SZhong Yang if (fnGetDpiForSystem) {
272*8af74909SZhong Yang uSystemDPI = fnGetDpiForSystem();
273*8af74909SZhong Yang } else {
274*8af74909SZhong Yang HDC hdcMeasure = ::CreateCompatibleDC({});
275*8af74909SZhong Yang uSystemDPI = ::GetDeviceCaps(hdcMeasure, LOGPIXELSY);
276*8af74909SZhong Yang ::DeleteDC(hdcMeasure);
277*8af74909SZhong Yang }
278*8af74909SZhong Yang
279*8af74909SZhong Yang if (!fnGetDpiForWindow) {
280*8af74909SZhong Yang hDLLShcore = ::LoadLibraryExW(L"shcore.dll", {}, LOAD_LIBRARY_SEARCH_SYSTEM32);
281*8af74909SZhong Yang if (hDLLShcore) {
282*8af74909SZhong Yang fnGetDpiForMonitor = DLLFunction<GetDpiForMonitorSig>(hDLLShcore, "GetDpiForMonitor");
283*8af74909SZhong Yang }
284*8af74909SZhong Yang }
285*8af74909SZhong Yang }
286*8af74909SZhong Yang
287*8af74909SZhong Yang HINSTANCE hinstPlatformRes {};
288*8af74909SZhong Yang
FamFromFontID(void * fid)289*8af74909SZhong Yang FormatAndMetrics *FamFromFontID(void *fid) noexcept {
290*8af74909SZhong Yang return static_cast<FormatAndMetrics *>(fid);
291*8af74909SZhong Yang }
292*8af74909SZhong Yang
Win32MapFontQuality(int extraFontFlag)293*8af74909SZhong Yang constexpr BYTE Win32MapFontQuality(int extraFontFlag) noexcept {
294*8af74909SZhong Yang switch (extraFontFlag & SC_EFF_QUALITY_MASK) {
295*8af74909SZhong Yang
296*8af74909SZhong Yang case SC_EFF_QUALITY_NON_ANTIALIASED:
297*8af74909SZhong Yang return NONANTIALIASED_QUALITY;
298*8af74909SZhong Yang
299*8af74909SZhong Yang case SC_EFF_QUALITY_ANTIALIASED:
300*8af74909SZhong Yang return ANTIALIASED_QUALITY;
301*8af74909SZhong Yang
302*8af74909SZhong Yang case SC_EFF_QUALITY_LCD_OPTIMIZED:
303*8af74909SZhong Yang return CLEARTYPE_QUALITY;
304*8af74909SZhong Yang
305*8af74909SZhong Yang default:
306*8af74909SZhong Yang return SC_EFF_QUALITY_DEFAULT;
307*8af74909SZhong Yang }
308*8af74909SZhong Yang }
309*8af74909SZhong Yang
310*8af74909SZhong Yang #if defined(USE_D2D)
DWriteMapFontQuality(int extraFontFlag)311*8af74909SZhong Yang constexpr D2D1_TEXT_ANTIALIAS_MODE DWriteMapFontQuality(int extraFontFlag) noexcept {
312*8af74909SZhong Yang switch (extraFontFlag & SC_EFF_QUALITY_MASK) {
313*8af74909SZhong Yang
314*8af74909SZhong Yang case SC_EFF_QUALITY_NON_ANTIALIASED:
315*8af74909SZhong Yang return D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
316*8af74909SZhong Yang
317*8af74909SZhong Yang case SC_EFF_QUALITY_ANTIALIASED:
318*8af74909SZhong Yang return D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
319*8af74909SZhong Yang
320*8af74909SZhong Yang case SC_EFF_QUALITY_LCD_OPTIMIZED:
321*8af74909SZhong Yang return D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
322*8af74909SZhong Yang
323*8af74909SZhong Yang default:
324*8af74909SZhong Yang return D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
325*8af74909SZhong Yang }
326*8af74909SZhong Yang }
327*8af74909SZhong Yang #endif
328*8af74909SZhong Yang
SetLogFont(LOGFONTW & lf,const char * faceName,int characterSet,float size,int weight,bool italic,int extraFontFlag)329*8af74909SZhong Yang void SetLogFont(LOGFONTW &lf, const char *faceName, int characterSet, float size, int weight, bool italic, int extraFontFlag) {
330*8af74909SZhong Yang lf = LOGFONTW();
331*8af74909SZhong Yang // The negative is to allow for leading
332*8af74909SZhong Yang lf.lfHeight = -(std::abs(std::lround(size)));
333*8af74909SZhong Yang lf.lfWeight = weight;
334*8af74909SZhong Yang lf.lfItalic = italic ? 1 : 0;
335*8af74909SZhong Yang lf.lfCharSet = static_cast<BYTE>(characterSet);
336*8af74909SZhong Yang lf.lfQuality = Win32MapFontQuality(extraFontFlag);
337*8af74909SZhong Yang UTF16FromUTF8(faceName, lf.lfFaceName, LF_FACESIZE);
338*8af74909SZhong Yang }
339*8af74909SZhong Yang
CreateFontFromParameters(const FontParameters & fp)340*8af74909SZhong Yang FontID CreateFontFromParameters(const FontParameters &fp) {
341*8af74909SZhong Yang LOGFONTW lf;
342*8af74909SZhong Yang SetLogFont(lf, fp.faceName, fp.characterSet, fp.size, fp.weight, fp.italic, fp.extraFontFlag);
343*8af74909SZhong Yang FontID fid = nullptr;
344*8af74909SZhong Yang if (fp.technology == SCWIN_TECH_GDI) {
345*8af74909SZhong Yang HFONT hfont = ::CreateFontIndirectW(&lf);
346*8af74909SZhong Yang fid = new FormatAndMetrics(hfont, fp.extraFontFlag, fp.characterSet);
347*8af74909SZhong Yang } else {
348*8af74909SZhong Yang #if defined(USE_D2D)
349*8af74909SZhong Yang IDWriteTextFormat *pTextFormat = nullptr;
350*8af74909SZhong Yang const std::wstring wsFace = WStringFromUTF8(fp.faceName);
351*8af74909SZhong Yang const FLOAT fHeight = fp.size;
352*8af74909SZhong Yang const DWRITE_FONT_STYLE style = fp.italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL;
353*8af74909SZhong Yang HRESULT hr = pIDWriteFactory->CreateTextFormat(wsFace.c_str(), nullptr,
354*8af74909SZhong Yang static_cast<DWRITE_FONT_WEIGHT>(fp.weight),
355*8af74909SZhong Yang style,
356*8af74909SZhong Yang DWRITE_FONT_STRETCH_NORMAL, fHeight, L"en-us", &pTextFormat);
357*8af74909SZhong Yang if (SUCCEEDED(hr)) {
358*8af74909SZhong Yang pTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
359*8af74909SZhong Yang
360*8af74909SZhong Yang FLOAT yAscent = 1.0f;
361*8af74909SZhong Yang FLOAT yDescent = 1.0f;
362*8af74909SZhong Yang FLOAT yInternalLeading = 0.0f;
363*8af74909SZhong Yang IDWriteTextLayout *pTextLayout = nullptr;
364*8af74909SZhong Yang hr = pIDWriteFactory->CreateTextLayout(L"X", 1, pTextFormat,
365*8af74909SZhong Yang 100.0f, 100.0f, &pTextLayout);
366*8af74909SZhong Yang if (SUCCEEDED(hr) && pTextLayout) {
367*8af74909SZhong Yang constexpr int maxLines = 2;
368*8af74909SZhong Yang DWRITE_LINE_METRICS lineMetrics[maxLines]{};
369*8af74909SZhong Yang UINT32 lineCount = 0;
370*8af74909SZhong Yang hr = pTextLayout->GetLineMetrics(lineMetrics, maxLines, &lineCount);
371*8af74909SZhong Yang if (SUCCEEDED(hr)) {
372*8af74909SZhong Yang yAscent = lineMetrics[0].baseline;
373*8af74909SZhong Yang yDescent = lineMetrics[0].height - lineMetrics[0].baseline;
374*8af74909SZhong Yang
375*8af74909SZhong Yang FLOAT emHeight;
376*8af74909SZhong Yang hr = pTextLayout->GetFontSize(0, &emHeight);
377*8af74909SZhong Yang if (SUCCEEDED(hr)) {
378*8af74909SZhong Yang yInternalLeading = lineMetrics[0].height - emHeight;
379*8af74909SZhong Yang }
380*8af74909SZhong Yang }
381*8af74909SZhong Yang ReleaseUnknown(pTextLayout);
382*8af74909SZhong Yang pTextFormat->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_UNIFORM, lineMetrics[0].height, lineMetrics[0].baseline);
383*8af74909SZhong Yang }
384*8af74909SZhong Yang fid = new FormatAndMetrics(pTextFormat, fp.extraFontFlag, fp.characterSet, yAscent, yDescent, yInternalLeading);
385*8af74909SZhong Yang }
386*8af74909SZhong Yang #endif
387*8af74909SZhong Yang }
388*8af74909SZhong Yang return fid;
389*8af74909SZhong Yang }
390*8af74909SZhong Yang
391*8af74909SZhong Yang }
392*8af74909SZhong Yang
Font()393*8af74909SZhong Yang Font::Font() noexcept : fid{} {
394*8af74909SZhong Yang }
395*8af74909SZhong Yang
~Font()396*8af74909SZhong Yang Font::~Font() {
397*8af74909SZhong Yang }
398*8af74909SZhong Yang
Create(const FontParameters & fp)399*8af74909SZhong Yang void Font::Create(const FontParameters &fp) {
400*8af74909SZhong Yang Release();
401*8af74909SZhong Yang if (fp.faceName)
402*8af74909SZhong Yang fid = CreateFontFromParameters(fp);
403*8af74909SZhong Yang }
404*8af74909SZhong Yang
Release()405*8af74909SZhong Yang void Font::Release() {
406*8af74909SZhong Yang if (fid)
407*8af74909SZhong Yang delete FamFromFontID(fid);
408*8af74909SZhong Yang fid = nullptr;
409*8af74909SZhong Yang }
410*8af74909SZhong Yang
411*8af74909SZhong Yang // Buffer to hold strings and string position arrays without always allocating on heap.
412*8af74909SZhong Yang // May sometimes have string too long to allocate on stack. So use a fixed stack-allocated buffer
413*8af74909SZhong Yang // when less than safe size otherwise allocate on heap and free automatically.
414*8af74909SZhong Yang template<typename T, int lengthStandard>
415*8af74909SZhong Yang class VarBuffer {
416*8af74909SZhong Yang T bufferStandard[lengthStandard];
417*8af74909SZhong Yang public:
418*8af74909SZhong Yang T *buffer;
VarBuffer(size_t length)419*8af74909SZhong Yang explicit VarBuffer(size_t length) : buffer(nullptr) {
420*8af74909SZhong Yang if (length > lengthStandard) {
421*8af74909SZhong Yang buffer = new T[length];
422*8af74909SZhong Yang } else {
423*8af74909SZhong Yang buffer = bufferStandard;
424*8af74909SZhong Yang }
425*8af74909SZhong Yang }
426*8af74909SZhong Yang // Deleted so VarBuffer objects can not be copied.
427*8af74909SZhong Yang VarBuffer(const VarBuffer &) = delete;
428*8af74909SZhong Yang VarBuffer(VarBuffer &&) = delete;
429*8af74909SZhong Yang VarBuffer &operator=(const VarBuffer &) = delete;
430*8af74909SZhong Yang VarBuffer &operator=(VarBuffer &&) = delete;
431*8af74909SZhong Yang
~VarBuffer()432*8af74909SZhong Yang ~VarBuffer() {
433*8af74909SZhong Yang if (buffer != bufferStandard) {
434*8af74909SZhong Yang delete []buffer;
435*8af74909SZhong Yang buffer = nullptr;
436*8af74909SZhong Yang }
437*8af74909SZhong Yang }
438*8af74909SZhong Yang };
439*8af74909SZhong Yang
440*8af74909SZhong Yang constexpr int stackBufferLength = 1000;
441*8af74909SZhong Yang class TextWide : public VarBuffer<wchar_t, stackBufferLength> {
442*8af74909SZhong Yang public:
443*8af74909SZhong Yang int tlen; // Using int instead of size_t as most Win32 APIs take int.
TextWide(std::string_view text,bool unicodeMode,int codePage=0)444*8af74909SZhong Yang TextWide(std::string_view text, bool unicodeMode, int codePage=0) :
445*8af74909SZhong Yang VarBuffer<wchar_t, stackBufferLength>(text.length()) {
446*8af74909SZhong Yang if (unicodeMode) {
447*8af74909SZhong Yang tlen = static_cast<int>(UTF16FromUTF8(text, buffer, text.length()));
448*8af74909SZhong Yang } else {
449*8af74909SZhong Yang // Support Asian string display in 9x English
450*8af74909SZhong Yang tlen = ::MultiByteToWideChar(codePage, 0, text.data(), static_cast<int>(text.length()),
451*8af74909SZhong Yang buffer, static_cast<int>(text.length()));
452*8af74909SZhong Yang }
453*8af74909SZhong Yang }
454*8af74909SZhong Yang };
455*8af74909SZhong Yang typedef VarBuffer<XYPOSITION, stackBufferLength> TextPositions;
456*8af74909SZhong Yang
DpiForWindow(WindowID wid)457*8af74909SZhong Yang UINT DpiForWindow(WindowID wid) noexcept {
458*8af74909SZhong Yang if (fnGetDpiForWindow) {
459*8af74909SZhong Yang return fnGetDpiForWindow(HwndFromWindowID(wid));
460*8af74909SZhong Yang }
461*8af74909SZhong Yang if (fnGetDpiForMonitor) {
462*8af74909SZhong Yang HMONITOR hMonitor = ::MonitorFromWindow(HwndFromWindowID(wid), MONITOR_DEFAULTTONEAREST);
463*8af74909SZhong Yang UINT dpiX = 0;
464*8af74909SZhong Yang UINT dpiY = 0;
465*8af74909SZhong Yang if (fnGetDpiForMonitor(hMonitor, 0 /*MDT_EFFECTIVE_DPI*/, &dpiX, &dpiY) == S_OK) {
466*8af74909SZhong Yang return dpiY;
467*8af74909SZhong Yang }
468*8af74909SZhong Yang }
469*8af74909SZhong Yang return uSystemDPI;
470*8af74909SZhong Yang }
471*8af74909SZhong Yang
SystemMetricsForDpi(int nIndex,UINT dpi)472*8af74909SZhong Yang int SystemMetricsForDpi(int nIndex, UINT dpi) noexcept {
473*8af74909SZhong Yang if (fnGetSystemMetricsForDpi) {
474*8af74909SZhong Yang return fnGetSystemMetricsForDpi(nIndex, dpi);
475*8af74909SZhong Yang }
476*8af74909SZhong Yang
477*8af74909SZhong Yang int value = ::GetSystemMetrics(nIndex);
478*8af74909SZhong Yang value = (dpi == uSystemDPI) ? value : ::MulDiv(value, dpi, uSystemDPI);
479*8af74909SZhong Yang return value;
480*8af74909SZhong Yang }
481*8af74909SZhong Yang
482*8af74909SZhong Yang class SurfaceGDI : public Surface {
483*8af74909SZhong Yang bool unicodeMode=false;
484*8af74909SZhong Yang HDC hdc{};
485*8af74909SZhong Yang bool hdcOwned=false;
486*8af74909SZhong Yang HPEN pen{};
487*8af74909SZhong Yang HPEN penOld{};
488*8af74909SZhong Yang HBRUSH brush{};
489*8af74909SZhong Yang HBRUSH brushOld{};
490*8af74909SZhong Yang HFONT fontOld{};
491*8af74909SZhong Yang HBITMAP bitmap{};
492*8af74909SZhong Yang HBITMAP bitmapOld{};
493*8af74909SZhong Yang
494*8af74909SZhong Yang int logPixelsY = USER_DEFAULT_SCREEN_DPI;
495*8af74909SZhong Yang
496*8af74909SZhong Yang int maxWidthMeasure = INT_MAX;
497*8af74909SZhong Yang // There appears to be a 16 bit string length limit in GDI on NT.
498*8af74909SZhong Yang int maxLenText = 65535;
499*8af74909SZhong Yang
500*8af74909SZhong Yang int codePage = 0;
501*8af74909SZhong Yang
502*8af74909SZhong Yang void BrushColour(ColourDesired back) noexcept;
503*8af74909SZhong Yang void SetFont(const Font &font_) noexcept;
504*8af74909SZhong Yang void Clear() noexcept;
505*8af74909SZhong Yang
506*8af74909SZhong Yang public:
507*8af74909SZhong Yang SurfaceGDI() noexcept;
508*8af74909SZhong Yang // Deleted so SurfaceGDI objects can not be copied.
509*8af74909SZhong Yang SurfaceGDI(const SurfaceGDI &) = delete;
510*8af74909SZhong Yang SurfaceGDI(SurfaceGDI &&) = delete;
511*8af74909SZhong Yang SurfaceGDI &operator=(const SurfaceGDI &) = delete;
512*8af74909SZhong Yang SurfaceGDI &operator=(SurfaceGDI &&) = delete;
513*8af74909SZhong Yang
514*8af74909SZhong Yang ~SurfaceGDI() noexcept override;
515*8af74909SZhong Yang
516*8af74909SZhong Yang void Init(WindowID wid) override;
517*8af74909SZhong Yang void Init(SurfaceID sid, WindowID wid) override;
518*8af74909SZhong Yang void InitPixMap(int width, int height, Surface *surface_, WindowID wid) override;
519*8af74909SZhong Yang
520*8af74909SZhong Yang void Release() override;
521*8af74909SZhong Yang bool Initialised() override;
522*8af74909SZhong Yang void PenColour(ColourDesired fore) override;
523*8af74909SZhong Yang int LogPixelsY() override;
524*8af74909SZhong Yang int DeviceHeightFont(int points) override;
525*8af74909SZhong Yang void MoveTo(int x_, int y_) override;
526*8af74909SZhong Yang void LineTo(int x_, int y_) override;
527*8af74909SZhong Yang void Polygon(Point *pts, size_t npts, ColourDesired fore, ColourDesired back) override;
528*8af74909SZhong Yang void RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) override;
529*8af74909SZhong Yang void FillRectangle(PRectangle rc, ColourDesired back) override;
530*8af74909SZhong Yang void FillRectangle(PRectangle rc, Surface &surfacePattern) override;
531*8af74909SZhong Yang void RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back) override;
532*8af74909SZhong Yang void AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill,
533*8af74909SZhong Yang ColourDesired outline, int alphaOutline, int flags) override;
534*8af74909SZhong Yang void GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) override;
535*8af74909SZhong Yang void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) override;
536*8af74909SZhong Yang void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) override;
537*8af74909SZhong Yang void Copy(PRectangle rc, Point from, Surface &surfaceSource) override;
538*8af74909SZhong Yang
539*8af74909SZhong Yang std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override;
540*8af74909SZhong Yang
541*8af74909SZhong Yang void DrawTextCommon(PRectangle rc, const Font &font_, XYPOSITION ybase, std::string_view text, UINT fuOptions);
542*8af74909SZhong Yang void DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override;
543*8af74909SZhong Yang void DrawTextClipped(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override;
544*8af74909SZhong Yang void DrawTextTransparent(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore) override;
545*8af74909SZhong Yang void MeasureWidths(Font &font_, std::string_view text, XYPOSITION *positions) override;
546*8af74909SZhong Yang XYPOSITION WidthText(Font &font_, std::string_view text) override;
547*8af74909SZhong Yang XYPOSITION Ascent(Font &font_) override;
548*8af74909SZhong Yang XYPOSITION Descent(Font &font_) override;
549*8af74909SZhong Yang XYPOSITION InternalLeading(Font &font_) override;
550*8af74909SZhong Yang XYPOSITION Height(Font &font_) override;
551*8af74909SZhong Yang XYPOSITION AverageCharWidth(Font &font_) override;
552*8af74909SZhong Yang
553*8af74909SZhong Yang void SetClip(PRectangle rc) override;
554*8af74909SZhong Yang void FlushCachedState() override;
555*8af74909SZhong Yang
556*8af74909SZhong Yang void SetUnicodeMode(bool unicodeMode_) override;
557*8af74909SZhong Yang void SetDBCSMode(int codePage_) override;
558*8af74909SZhong Yang void SetBidiR2L(bool bidiR2L_) override;
559*8af74909SZhong Yang };
560*8af74909SZhong Yang
SurfaceGDI()561*8af74909SZhong Yang SurfaceGDI::SurfaceGDI() noexcept {
562*8af74909SZhong Yang }
563*8af74909SZhong Yang
~SurfaceGDI()564*8af74909SZhong Yang SurfaceGDI::~SurfaceGDI() noexcept {
565*8af74909SZhong Yang Clear();
566*8af74909SZhong Yang }
567*8af74909SZhong Yang
Clear()568*8af74909SZhong Yang void SurfaceGDI::Clear() noexcept {
569*8af74909SZhong Yang if (penOld) {
570*8af74909SZhong Yang ::SelectObject(hdc, penOld);
571*8af74909SZhong Yang ::DeleteObject(pen);
572*8af74909SZhong Yang penOld = {};
573*8af74909SZhong Yang }
574*8af74909SZhong Yang pen = {};
575*8af74909SZhong Yang if (brushOld) {
576*8af74909SZhong Yang ::SelectObject(hdc, brushOld);
577*8af74909SZhong Yang ::DeleteObject(brush);
578*8af74909SZhong Yang brushOld = {};
579*8af74909SZhong Yang }
580*8af74909SZhong Yang brush = {};
581*8af74909SZhong Yang if (fontOld) {
582*8af74909SZhong Yang // Fonts are not deleted as they are owned by a Font object
583*8af74909SZhong Yang ::SelectObject(hdc, fontOld);
584*8af74909SZhong Yang fontOld = {};
585*8af74909SZhong Yang }
586*8af74909SZhong Yang if (bitmapOld) {
587*8af74909SZhong Yang ::SelectObject(hdc, bitmapOld);
588*8af74909SZhong Yang ::DeleteObject(bitmap);
589*8af74909SZhong Yang bitmapOld = {};
590*8af74909SZhong Yang }
591*8af74909SZhong Yang bitmap = {};
592*8af74909SZhong Yang if (hdcOwned) {
593*8af74909SZhong Yang ::DeleteDC(hdc);
594*8af74909SZhong Yang hdc = {};
595*8af74909SZhong Yang hdcOwned = false;
596*8af74909SZhong Yang }
597*8af74909SZhong Yang }
598*8af74909SZhong Yang
Release()599*8af74909SZhong Yang void SurfaceGDI::Release() {
600*8af74909SZhong Yang Clear();
601*8af74909SZhong Yang }
602*8af74909SZhong Yang
Initialised()603*8af74909SZhong Yang bool SurfaceGDI::Initialised() {
604*8af74909SZhong Yang return hdc != 0;
605*8af74909SZhong Yang }
606*8af74909SZhong Yang
Init(WindowID wid)607*8af74909SZhong Yang void SurfaceGDI::Init(WindowID wid) {
608*8af74909SZhong Yang Release();
609*8af74909SZhong Yang hdc = ::CreateCompatibleDC({});
610*8af74909SZhong Yang hdcOwned = true;
611*8af74909SZhong Yang ::SetTextAlign(hdc, TA_BASELINE);
612*8af74909SZhong Yang logPixelsY = DpiForWindow(wid);
613*8af74909SZhong Yang }
614*8af74909SZhong Yang
Init(SurfaceID sid,WindowID wid)615*8af74909SZhong Yang void SurfaceGDI::Init(SurfaceID sid, WindowID wid) {
616*8af74909SZhong Yang Release();
617*8af74909SZhong Yang hdc = static_cast<HDC>(sid);
618*8af74909SZhong Yang ::SetTextAlign(hdc, TA_BASELINE);
619*8af74909SZhong Yang // Windows on screen are scaled but printers are not.
620*8af74909SZhong Yang const bool printing = ::GetDeviceCaps(hdc, TECHNOLOGY) != DT_RASDISPLAY;
621*8af74909SZhong Yang logPixelsY = printing ? ::GetDeviceCaps(hdc, LOGPIXELSY) : DpiForWindow(wid);
622*8af74909SZhong Yang }
623*8af74909SZhong Yang
InitPixMap(int width,int height,Surface * surface_,WindowID wid)624*8af74909SZhong Yang void SurfaceGDI::InitPixMap(int width, int height, Surface *surface_, WindowID wid) {
625*8af74909SZhong Yang Release();
626*8af74909SZhong Yang SurfaceGDI *psurfOther = dynamic_cast<SurfaceGDI *>(surface_);
627*8af74909SZhong Yang // Should only ever be called with a SurfaceGDI, not a SurfaceD2D
628*8af74909SZhong Yang PLATFORM_ASSERT(psurfOther);
629*8af74909SZhong Yang hdc = ::CreateCompatibleDC(psurfOther->hdc);
630*8af74909SZhong Yang hdcOwned = true;
631*8af74909SZhong Yang bitmap = ::CreateCompatibleBitmap(psurfOther->hdc, width, height);
632*8af74909SZhong Yang bitmapOld = SelectBitmap(hdc, bitmap);
633*8af74909SZhong Yang ::SetTextAlign(hdc, TA_BASELINE);
634*8af74909SZhong Yang SetUnicodeMode(psurfOther->unicodeMode);
635*8af74909SZhong Yang SetDBCSMode(psurfOther->codePage);
636*8af74909SZhong Yang logPixelsY = DpiForWindow(wid);
637*8af74909SZhong Yang }
638*8af74909SZhong Yang
PenColour(ColourDesired fore)639*8af74909SZhong Yang void SurfaceGDI::PenColour(ColourDesired fore) {
640*8af74909SZhong Yang if (pen) {
641*8af74909SZhong Yang ::SelectObject(hdc, penOld);
642*8af74909SZhong Yang ::DeleteObject(pen);
643*8af74909SZhong Yang pen = {};
644*8af74909SZhong Yang penOld = {};
645*8af74909SZhong Yang }
646*8af74909SZhong Yang pen = ::CreatePen(0,1,fore.AsInteger());
647*8af74909SZhong Yang penOld = SelectPen(hdc, pen);
648*8af74909SZhong Yang }
649*8af74909SZhong Yang
BrushColour(ColourDesired back)650*8af74909SZhong Yang void SurfaceGDI::BrushColour(ColourDesired back) noexcept {
651*8af74909SZhong Yang if (brush) {
652*8af74909SZhong Yang ::SelectObject(hdc, brushOld);
653*8af74909SZhong Yang ::DeleteObject(brush);
654*8af74909SZhong Yang brush = {};
655*8af74909SZhong Yang brushOld = {};
656*8af74909SZhong Yang }
657*8af74909SZhong Yang brush = ::CreateSolidBrush(back.AsInteger());
658*8af74909SZhong Yang brushOld = SelectBrush(hdc, brush);
659*8af74909SZhong Yang }
660*8af74909SZhong Yang
SetFont(const Font & font_)661*8af74909SZhong Yang void SurfaceGDI::SetFont(const Font &font_) noexcept {
662*8af74909SZhong Yang const FormatAndMetrics *pfm = FamFromFontID(font_.GetID());
663*8af74909SZhong Yang PLATFORM_ASSERT(pfm->technology == SCWIN_TECH_GDI);
664*8af74909SZhong Yang if (fontOld) {
665*8af74909SZhong Yang SelectFont(hdc, pfm->hfont);
666*8af74909SZhong Yang } else {
667*8af74909SZhong Yang fontOld = SelectFont(hdc, pfm->hfont);
668*8af74909SZhong Yang }
669*8af74909SZhong Yang }
670*8af74909SZhong Yang
LogPixelsY()671*8af74909SZhong Yang int SurfaceGDI::LogPixelsY() {
672*8af74909SZhong Yang return logPixelsY;
673*8af74909SZhong Yang }
674*8af74909SZhong Yang
DeviceHeightFont(int points)675*8af74909SZhong Yang int SurfaceGDI::DeviceHeightFont(int points) {
676*8af74909SZhong Yang return ::MulDiv(points, LogPixelsY(), 72);
677*8af74909SZhong Yang }
678*8af74909SZhong Yang
MoveTo(int x_,int y_)679*8af74909SZhong Yang void SurfaceGDI::MoveTo(int x_, int y_) {
680*8af74909SZhong Yang ::MoveToEx(hdc, x_, y_, nullptr);
681*8af74909SZhong Yang }
682*8af74909SZhong Yang
LineTo(int x_,int y_)683*8af74909SZhong Yang void SurfaceGDI::LineTo(int x_, int y_) {
684*8af74909SZhong Yang ::LineTo(hdc, x_, y_);
685*8af74909SZhong Yang }
686*8af74909SZhong Yang
Polygon(Point * pts,size_t npts,ColourDesired fore,ColourDesired back)687*8af74909SZhong Yang void SurfaceGDI::Polygon(Point *pts, size_t npts, ColourDesired fore, ColourDesired back) {
688*8af74909SZhong Yang PenColour(fore);
689*8af74909SZhong Yang BrushColour(back);
690*8af74909SZhong Yang std::vector<POINT> outline;
691*8af74909SZhong Yang std::transform(pts, pts + npts, std::back_inserter(outline), POINTFromPoint);
692*8af74909SZhong Yang ::Polygon(hdc, outline.data(), static_cast<int>(npts));
693*8af74909SZhong Yang }
694*8af74909SZhong Yang
RectangleDraw(PRectangle rc,ColourDesired fore,ColourDesired back)695*8af74909SZhong Yang void SurfaceGDI::RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) {
696*8af74909SZhong Yang PenColour(back);
697*8af74909SZhong Yang BrushColour(fore);
698*8af74909SZhong Yang const RECT rcw = RectFromPRectangle(rc);
699*8af74909SZhong Yang ::Rectangle(hdc, rcw.left, rcw.top, rcw.right, rcw.bottom);
700*8af74909SZhong Yang }
701*8af74909SZhong Yang
FillRectangle(PRectangle rc,ColourDesired back)702*8af74909SZhong Yang void SurfaceGDI::FillRectangle(PRectangle rc, ColourDesired back) {
703*8af74909SZhong Yang // Using ExtTextOut rather than a FillRect ensures that no dithering occurs.
704*8af74909SZhong Yang // There is no need to allocate a brush either.
705*8af74909SZhong Yang const RECT rcw = RectFromPRectangle(rc);
706*8af74909SZhong Yang ::SetBkColor(hdc, back.AsInteger());
707*8af74909SZhong Yang ::ExtTextOut(hdc, rcw.left, rcw.top, ETO_OPAQUE, &rcw, TEXT(""), 0, nullptr);
708*8af74909SZhong Yang }
709*8af74909SZhong Yang
FillRectangle(PRectangle rc,Surface & surfacePattern)710*8af74909SZhong Yang void SurfaceGDI::FillRectangle(PRectangle rc, Surface &surfacePattern) {
711*8af74909SZhong Yang HBRUSH br;
712*8af74909SZhong Yang if (SurfaceGDI *psgdi = dynamic_cast<SurfaceGDI *>(&surfacePattern); psgdi && psgdi->bitmap) {
713*8af74909SZhong Yang br = ::CreatePatternBrush(psgdi->bitmap);
714*8af74909SZhong Yang } else { // Something is wrong so display in red
715*8af74909SZhong Yang br = ::CreateSolidBrush(RGB(0xff, 0, 0));
716*8af74909SZhong Yang }
717*8af74909SZhong Yang const RECT rcw = RectFromPRectangle(rc);
718*8af74909SZhong Yang ::FillRect(hdc, &rcw, br);
719*8af74909SZhong Yang ::DeleteObject(br);
720*8af74909SZhong Yang }
721*8af74909SZhong Yang
RoundedRectangle(PRectangle rc,ColourDesired fore,ColourDesired back)722*8af74909SZhong Yang void SurfaceGDI::RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back) {
723*8af74909SZhong Yang PenColour(fore);
724*8af74909SZhong Yang BrushColour(back);
725*8af74909SZhong Yang const RECT rcw = RectFromPRectangle(rc);
726*8af74909SZhong Yang ::RoundRect(hdc,
727*8af74909SZhong Yang rcw.left + 1, rcw.top,
728*8af74909SZhong Yang rcw.right - 1, rcw.bottom,
729*8af74909SZhong Yang 8, 8);
730*8af74909SZhong Yang }
731*8af74909SZhong Yang
732*8af74909SZhong Yang namespace {
733*8af74909SZhong Yang
dwordFromBGRA(byte b,byte g,byte r,byte a)734*8af74909SZhong Yang constexpr DWORD dwordFromBGRA(byte b, byte g, byte r, byte a) noexcept {
735*8af74909SZhong Yang return (a << 24) | (r << 16) | (g << 8) | b;
736*8af74909SZhong Yang }
737*8af74909SZhong Yang
AlphaScaled(unsigned char component,unsigned int alpha)738*8af74909SZhong Yang constexpr byte AlphaScaled(unsigned char component, unsigned int alpha) noexcept {
739*8af74909SZhong Yang return static_cast<byte>(component * alpha / 255);
740*8af74909SZhong Yang }
741*8af74909SZhong Yang
dwordMultiplied(ColourDesired colour,unsigned int alpha)742*8af74909SZhong Yang constexpr DWORD dwordMultiplied(ColourDesired colour, unsigned int alpha) noexcept {
743*8af74909SZhong Yang return dwordFromBGRA(
744*8af74909SZhong Yang AlphaScaled(colour.GetBlue(), alpha),
745*8af74909SZhong Yang AlphaScaled(colour.GetGreen(), alpha),
746*8af74909SZhong Yang AlphaScaled(colour.GetRed(), alpha),
747*8af74909SZhong Yang static_cast<byte>(alpha));
748*8af74909SZhong Yang }
749*8af74909SZhong Yang
750*8af74909SZhong Yang class DIBSection {
751*8af74909SZhong Yang HDC hMemDC {};
752*8af74909SZhong Yang HBITMAP hbmMem {};
753*8af74909SZhong Yang HBITMAP hbmOld {};
754*8af74909SZhong Yang SIZE size {};
755*8af74909SZhong Yang DWORD *pixels = nullptr;
756*8af74909SZhong Yang public:
757*8af74909SZhong Yang DIBSection(HDC hdc, SIZE size_) noexcept;
758*8af74909SZhong Yang // Deleted so DIBSection objects can not be copied.
759*8af74909SZhong Yang DIBSection(const DIBSection&) = delete;
760*8af74909SZhong Yang DIBSection(DIBSection&&) = delete;
761*8af74909SZhong Yang DIBSection &operator=(const DIBSection&) = delete;
762*8af74909SZhong Yang DIBSection &operator=(DIBSection&&) = delete;
763*8af74909SZhong Yang ~DIBSection() noexcept;
operator bool() const764*8af74909SZhong Yang operator bool() const noexcept {
765*8af74909SZhong Yang return hMemDC && hbmMem && pixels;
766*8af74909SZhong Yang }
Pixels() const767*8af74909SZhong Yang DWORD *Pixels() const noexcept {
768*8af74909SZhong Yang return pixels;
769*8af74909SZhong Yang }
Bytes() const770*8af74909SZhong Yang unsigned char *Bytes() const noexcept {
771*8af74909SZhong Yang return reinterpret_cast<unsigned char *>(pixels);
772*8af74909SZhong Yang }
DC() const773*8af74909SZhong Yang HDC DC() const noexcept {
774*8af74909SZhong Yang return hMemDC;
775*8af74909SZhong Yang }
SetPixel(LONG x,LONG y,DWORD value)776*8af74909SZhong Yang void SetPixel(LONG x, LONG y, DWORD value) noexcept {
777*8af74909SZhong Yang PLATFORM_ASSERT(x >= 0);
778*8af74909SZhong Yang PLATFORM_ASSERT(y >= 0);
779*8af74909SZhong Yang PLATFORM_ASSERT(x < size.cx);
780*8af74909SZhong Yang PLATFORM_ASSERT(y < size.cy);
781*8af74909SZhong Yang pixels[y * size.cx + x] = value;
782*8af74909SZhong Yang }
783*8af74909SZhong Yang void SetSymmetric(LONG x, LONG y, DWORD value) noexcept;
784*8af74909SZhong Yang };
785*8af74909SZhong Yang
DIBSection(HDC hdc,SIZE size_)786*8af74909SZhong Yang DIBSection::DIBSection(HDC hdc, SIZE size_) noexcept {
787*8af74909SZhong Yang hMemDC = ::CreateCompatibleDC(hdc);
788*8af74909SZhong Yang if (!hMemDC) {
789*8af74909SZhong Yang return;
790*8af74909SZhong Yang }
791*8af74909SZhong Yang
792*8af74909SZhong Yang size = size_;
793*8af74909SZhong Yang
794*8af74909SZhong Yang // -size.y makes bitmap start from top
795*8af74909SZhong Yang const BITMAPINFO bpih = { {sizeof(BITMAPINFOHEADER), size.cx, -size.cy, 1, 32, BI_RGB, 0, 0, 0, 0, 0},
796*8af74909SZhong Yang {{0, 0, 0, 0}} };
797*8af74909SZhong Yang void *image = nullptr;
798*8af74909SZhong Yang hbmMem = CreateDIBSection(hMemDC, &bpih, DIB_RGB_COLORS, &image, {}, 0);
799*8af74909SZhong Yang if (!hbmMem || !image) {
800*8af74909SZhong Yang return;
801*8af74909SZhong Yang }
802*8af74909SZhong Yang pixels = static_cast<DWORD *>(image);
803*8af74909SZhong Yang hbmOld = SelectBitmap(hMemDC, hbmMem);
804*8af74909SZhong Yang }
805*8af74909SZhong Yang
~DIBSection()806*8af74909SZhong Yang DIBSection::~DIBSection() noexcept {
807*8af74909SZhong Yang if (hbmOld) {
808*8af74909SZhong Yang SelectBitmap(hMemDC, hbmOld);
809*8af74909SZhong Yang hbmOld = {};
810*8af74909SZhong Yang }
811*8af74909SZhong Yang if (hbmMem) {
812*8af74909SZhong Yang ::DeleteObject(hbmMem);
813*8af74909SZhong Yang hbmMem = {};
814*8af74909SZhong Yang }
815*8af74909SZhong Yang if (hMemDC) {
816*8af74909SZhong Yang ::DeleteDC(hMemDC);
817*8af74909SZhong Yang hMemDC = {};
818*8af74909SZhong Yang }
819*8af74909SZhong Yang }
820*8af74909SZhong Yang
SetSymmetric(LONG x,LONG y,DWORD value)821*8af74909SZhong Yang void DIBSection::SetSymmetric(LONG x, LONG y, DWORD value) noexcept {
822*8af74909SZhong Yang // Plot a point symmetrically to all 4 quadrants
823*8af74909SZhong Yang const LONG xSymmetric = size.cx - 1 - x;
824*8af74909SZhong Yang const LONG ySymmetric = size.cy - 1 - y;
825*8af74909SZhong Yang SetPixel(x, y, value);
826*8af74909SZhong Yang SetPixel(xSymmetric, y, value);
827*8af74909SZhong Yang SetPixel(x, ySymmetric, value);
828*8af74909SZhong Yang SetPixel(xSymmetric, ySymmetric, value);
829*8af74909SZhong Yang }
830*8af74909SZhong Yang
Proportional(unsigned char a,unsigned char b,float t)831*8af74909SZhong Yang constexpr unsigned int Proportional(unsigned char a, unsigned char b, float t) noexcept {
832*8af74909SZhong Yang return static_cast<unsigned int>(a + t * (b - a));
833*8af74909SZhong Yang }
834*8af74909SZhong Yang
Proportional(ColourAlpha a,ColourAlpha b,float t)835*8af74909SZhong Yang ColourAlpha Proportional(ColourAlpha a, ColourAlpha b, float t) noexcept {
836*8af74909SZhong Yang return ColourAlpha(
837*8af74909SZhong Yang Proportional(a.GetRed(), b.GetRed(), t),
838*8af74909SZhong Yang Proportional(a.GetGreen(), b.GetGreen(), t),
839*8af74909SZhong Yang Proportional(a.GetBlue(), b.GetBlue(), t),
840*8af74909SZhong Yang Proportional(a.GetAlpha(), b.GetAlpha(), t));
841*8af74909SZhong Yang }
842*8af74909SZhong Yang
GradientValue(const std::vector<ColourStop> & stops,float proportion)843*8af74909SZhong Yang ColourAlpha GradientValue(const std::vector<ColourStop> &stops, float proportion) noexcept {
844*8af74909SZhong Yang for (size_t stop = 0; stop < stops.size() - 1; stop++) {
845*8af74909SZhong Yang // Loop through each pair of stops
846*8af74909SZhong Yang const float positionStart = stops[stop].position;
847*8af74909SZhong Yang const float positionEnd = stops[stop + 1].position;
848*8af74909SZhong Yang if ((proportion >= positionStart) && (proportion <= positionEnd)) {
849*8af74909SZhong Yang const float proportionInPair = (proportion - positionStart) /
850*8af74909SZhong Yang (positionEnd - positionStart);
851*8af74909SZhong Yang return Proportional(stops[stop].colour, stops[stop + 1].colour, proportionInPair);
852*8af74909SZhong Yang }
853*8af74909SZhong Yang }
854*8af74909SZhong Yang // Loop should always find a value
855*8af74909SZhong Yang return ColourAlpha();
856*8af74909SZhong Yang }
857*8af74909SZhong Yang
SizeOfRect(RECT rc)858*8af74909SZhong Yang constexpr SIZE SizeOfRect(RECT rc) noexcept {
859*8af74909SZhong Yang return { rc.right - rc.left, rc.bottom - rc.top };
860*8af74909SZhong Yang }
861*8af74909SZhong Yang
862*8af74909SZhong Yang constexpr BLENDFUNCTION mergeAlpha = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
863*8af74909SZhong Yang
864*8af74909SZhong Yang }
865*8af74909SZhong Yang
AlphaRectangle(PRectangle rc,int cornerSize,ColourDesired fill,int alphaFill,ColourDesired outline,int alphaOutline,int)866*8af74909SZhong Yang void SurfaceGDI::AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill,
867*8af74909SZhong Yang ColourDesired outline, int alphaOutline, int /* flags*/ ) {
868*8af74909SZhong Yang const RECT rcw = RectFromPRectangle(rc);
869*8af74909SZhong Yang const SIZE size = SizeOfRect(rcw);
870*8af74909SZhong Yang
871*8af74909SZhong Yang if (size.cx > 0) {
872*8af74909SZhong Yang
873*8af74909SZhong Yang DIBSection section(hdc, size);
874*8af74909SZhong Yang
875*8af74909SZhong Yang if (section) {
876*8af74909SZhong Yang
877*8af74909SZhong Yang // Ensure not distorted too much by corners when small
878*8af74909SZhong Yang const LONG corner = std::min<LONG>(cornerSize, (std::min(size.cx, size.cy) / 2) - 2);
879*8af74909SZhong Yang
880*8af74909SZhong Yang constexpr DWORD valEmpty = dwordFromBGRA(0,0,0,0);
881*8af74909SZhong Yang const DWORD valFill = dwordMultiplied(fill, alphaFill);
882*8af74909SZhong Yang const DWORD valOutline = dwordMultiplied(outline, alphaOutline);
883*8af74909SZhong Yang
884*8af74909SZhong Yang // Draw a framed rectangle
885*8af74909SZhong Yang for (int y=0; y<size.cy; y++) {
886*8af74909SZhong Yang for (int x=0; x<size.cx; x++) {
887*8af74909SZhong Yang if ((x==0) || (x==size.cx-1) || (y == 0) || (y == size.cy -1)) {
888*8af74909SZhong Yang section.SetPixel(x, y, valOutline);
889*8af74909SZhong Yang } else {
890*8af74909SZhong Yang section.SetPixel(x, y, valFill);
891*8af74909SZhong Yang }
892*8af74909SZhong Yang }
893*8af74909SZhong Yang }
894*8af74909SZhong Yang
895*8af74909SZhong Yang // Make the corners transparent
896*8af74909SZhong Yang for (LONG c=0; c<corner; c++) {
897*8af74909SZhong Yang for (LONG x=0; x<c+1; x++) {
898*8af74909SZhong Yang section.SetSymmetric(x, c - x, valEmpty);
899*8af74909SZhong Yang }
900*8af74909SZhong Yang }
901*8af74909SZhong Yang
902*8af74909SZhong Yang // Draw the corner frame pieces
903*8af74909SZhong Yang for (LONG x=1; x<corner; x++) {
904*8af74909SZhong Yang section.SetSymmetric(x, corner - x, valOutline);
905*8af74909SZhong Yang }
906*8af74909SZhong Yang
907*8af74909SZhong Yang AlphaBlend(hdc, rcw.left, rcw.top, size.cx, size.cy, section.DC(), 0, 0, size.cx, size.cy, mergeAlpha);
908*8af74909SZhong Yang }
909*8af74909SZhong Yang } else {
910*8af74909SZhong Yang BrushColour(outline);
911*8af74909SZhong Yang FrameRect(hdc, &rcw, brush);
912*8af74909SZhong Yang }
913*8af74909SZhong Yang }
914*8af74909SZhong Yang
GradientRectangle(PRectangle rc,const std::vector<ColourStop> & stops,GradientOptions options)915*8af74909SZhong Yang void SurfaceGDI::GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) {
916*8af74909SZhong Yang
917*8af74909SZhong Yang const RECT rcw = RectFromPRectangle(rc);
918*8af74909SZhong Yang const SIZE size = SizeOfRect(rcw);
919*8af74909SZhong Yang
920*8af74909SZhong Yang DIBSection section(hdc, size);
921*8af74909SZhong Yang
922*8af74909SZhong Yang if (section) {
923*8af74909SZhong Yang
924*8af74909SZhong Yang if (options == GradientOptions::topToBottom) {
925*8af74909SZhong Yang for (LONG y = 0; y < size.cy; y++) {
926*8af74909SZhong Yang // Find y/height proportional colour
927*8af74909SZhong Yang const float proportion = y / (rc.Height() - 1.0f);
928*8af74909SZhong Yang const ColourAlpha mixed = GradientValue(stops, proportion);
929*8af74909SZhong Yang const DWORD valFill = dwordMultiplied(mixed, mixed.GetAlpha());
930*8af74909SZhong Yang for (LONG x = 0; x < size.cx; x++) {
931*8af74909SZhong Yang section.SetPixel(x, y, valFill);
932*8af74909SZhong Yang }
933*8af74909SZhong Yang }
934*8af74909SZhong Yang } else {
935*8af74909SZhong Yang for (LONG x = 0; x < size.cx; x++) {
936*8af74909SZhong Yang // Find x/width proportional colour
937*8af74909SZhong Yang const float proportion = x / (rc.Width() - 1.0f);
938*8af74909SZhong Yang const ColourAlpha mixed = GradientValue(stops, proportion);
939*8af74909SZhong Yang const DWORD valFill = dwordMultiplied(mixed, mixed.GetAlpha());
940*8af74909SZhong Yang for (LONG y = 0; y < size.cy; y++) {
941*8af74909SZhong Yang section.SetPixel(x, y, valFill);
942*8af74909SZhong Yang }
943*8af74909SZhong Yang }
944*8af74909SZhong Yang }
945*8af74909SZhong Yang
946*8af74909SZhong Yang AlphaBlend(hdc, rcw.left, rcw.top, size.cx, size.cy, section.DC(), 0, 0, size.cx, size.cy, mergeAlpha);
947*8af74909SZhong Yang }
948*8af74909SZhong Yang }
949*8af74909SZhong Yang
DrawRGBAImage(PRectangle rc,int width,int height,const unsigned char * pixelsImage)950*8af74909SZhong Yang void SurfaceGDI::DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) {
951*8af74909SZhong Yang if (rc.Width() > 0) {
952*8af74909SZhong Yang if (rc.Width() > width)
953*8af74909SZhong Yang rc.left += std::floor((rc.Width() - width) / 2);
954*8af74909SZhong Yang rc.right = rc.left + width;
955*8af74909SZhong Yang if (rc.Height() > height)
956*8af74909SZhong Yang rc.top += std::floor((rc.Height() - height) / 2);
957*8af74909SZhong Yang rc.bottom = rc.top + height;
958*8af74909SZhong Yang
959*8af74909SZhong Yang const SIZE size { width, height };
960*8af74909SZhong Yang DIBSection section(hdc, size);
961*8af74909SZhong Yang if (section) {
962*8af74909SZhong Yang RGBAImage::BGRAFromRGBA(section.Bytes(), pixelsImage, width * height);
963*8af74909SZhong Yang AlphaBlend(hdc, static_cast<int>(rc.left), static_cast<int>(rc.top),
964*8af74909SZhong Yang static_cast<int>(rc.Width()), static_cast<int>(rc.Height()), section.DC(),
965*8af74909SZhong Yang 0, 0, width, height, mergeAlpha);
966*8af74909SZhong Yang }
967*8af74909SZhong Yang }
968*8af74909SZhong Yang }
969*8af74909SZhong Yang
Ellipse(PRectangle rc,ColourDesired fore,ColourDesired back)970*8af74909SZhong Yang void SurfaceGDI::Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) {
971*8af74909SZhong Yang PenColour(fore);
972*8af74909SZhong Yang BrushColour(back);
973*8af74909SZhong Yang const RECT rcw = RectFromPRectangle(rc);
974*8af74909SZhong Yang ::Ellipse(hdc, rcw.left, rcw.top, rcw.right, rcw.bottom);
975*8af74909SZhong Yang }
976*8af74909SZhong Yang
Copy(PRectangle rc,Point from,Surface & surfaceSource)977*8af74909SZhong Yang void SurfaceGDI::Copy(PRectangle rc, Point from, Surface &surfaceSource) {
978*8af74909SZhong Yang ::BitBlt(hdc,
979*8af74909SZhong Yang static_cast<int>(rc.left), static_cast<int>(rc.top),
980*8af74909SZhong Yang static_cast<int>(rc.Width()), static_cast<int>(rc.Height()),
981*8af74909SZhong Yang dynamic_cast<SurfaceGDI &>(surfaceSource).hdc,
982*8af74909SZhong Yang static_cast<int>(from.x), static_cast<int>(from.y), SRCCOPY);
983*8af74909SZhong Yang }
984*8af74909SZhong Yang
Layout(const IScreenLine *)985*8af74909SZhong Yang std::unique_ptr<IScreenLineLayout> SurfaceGDI::Layout(const IScreenLine *) {
986*8af74909SZhong Yang return {};
987*8af74909SZhong Yang }
988*8af74909SZhong Yang
989*8af74909SZhong Yang typedef VarBuffer<int, stackBufferLength> TextPositionsI;
990*8af74909SZhong Yang
DrawTextCommon(PRectangle rc,const Font & font_,XYPOSITION ybase,std::string_view text,UINT fuOptions)991*8af74909SZhong Yang void SurfaceGDI::DrawTextCommon(PRectangle rc, const Font &font_, XYPOSITION ybase, std::string_view text, UINT fuOptions) {
992*8af74909SZhong Yang SetFont(font_);
993*8af74909SZhong Yang const RECT rcw = RectFromPRectangle(rc);
994*8af74909SZhong Yang const int x = static_cast<int>(rc.left);
995*8af74909SZhong Yang const int yBaseInt = static_cast<int>(ybase);
996*8af74909SZhong Yang
997*8af74909SZhong Yang if (unicodeMode) {
998*8af74909SZhong Yang const TextWide tbuf(text, unicodeMode, codePage);
999*8af74909SZhong Yang ::ExtTextOutW(hdc, x, yBaseInt, fuOptions, &rcw, tbuf.buffer, tbuf.tlen, nullptr);
1000*8af74909SZhong Yang } else {
1001*8af74909SZhong Yang ::ExtTextOutA(hdc, x, yBaseInt, fuOptions, &rcw, text.data(), static_cast<UINT>(text.length()), nullptr);
1002*8af74909SZhong Yang }
1003*8af74909SZhong Yang }
1004*8af74909SZhong Yang
DrawTextNoClip(PRectangle rc,Font & font_,XYPOSITION ybase,std::string_view text,ColourDesired fore,ColourDesired back)1005*8af74909SZhong Yang void SurfaceGDI::DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text,
1006*8af74909SZhong Yang ColourDesired fore, ColourDesired back) {
1007*8af74909SZhong Yang ::SetTextColor(hdc, fore.AsInteger());
1008*8af74909SZhong Yang ::SetBkColor(hdc, back.AsInteger());
1009*8af74909SZhong Yang DrawTextCommon(rc, font_, ybase, text, ETO_OPAQUE);
1010*8af74909SZhong Yang }
1011*8af74909SZhong Yang
DrawTextClipped(PRectangle rc,Font & font_,XYPOSITION ybase,std::string_view text,ColourDesired fore,ColourDesired back)1012*8af74909SZhong Yang void SurfaceGDI::DrawTextClipped(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text,
1013*8af74909SZhong Yang ColourDesired fore, ColourDesired back) {
1014*8af74909SZhong Yang ::SetTextColor(hdc, fore.AsInteger());
1015*8af74909SZhong Yang ::SetBkColor(hdc, back.AsInteger());
1016*8af74909SZhong Yang DrawTextCommon(rc, font_, ybase, text, ETO_OPAQUE | ETO_CLIPPED);
1017*8af74909SZhong Yang }
1018*8af74909SZhong Yang
DrawTextTransparent(PRectangle rc,Font & font_,XYPOSITION ybase,std::string_view text,ColourDesired fore)1019*8af74909SZhong Yang void SurfaceGDI::DrawTextTransparent(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text,
1020*8af74909SZhong Yang ColourDesired fore) {
1021*8af74909SZhong Yang // Avoid drawing spaces in transparent mode
1022*8af74909SZhong Yang for (const char ch : text) {
1023*8af74909SZhong Yang if (ch != ' ') {
1024*8af74909SZhong Yang ::SetTextColor(hdc, fore.AsInteger());
1025*8af74909SZhong Yang ::SetBkMode(hdc, TRANSPARENT);
1026*8af74909SZhong Yang DrawTextCommon(rc, font_, ybase, text, 0);
1027*8af74909SZhong Yang ::SetBkMode(hdc, OPAQUE);
1028*8af74909SZhong Yang return;
1029*8af74909SZhong Yang }
1030*8af74909SZhong Yang }
1031*8af74909SZhong Yang }
1032*8af74909SZhong Yang
WidthText(Font & font_,std::string_view text)1033*8af74909SZhong Yang XYPOSITION SurfaceGDI::WidthText(Font &font_, std::string_view text) {
1034*8af74909SZhong Yang SetFont(font_);
1035*8af74909SZhong Yang SIZE sz={0,0};
1036*8af74909SZhong Yang if (!unicodeMode) {
1037*8af74909SZhong Yang ::GetTextExtentPoint32A(hdc, text.data(), std::min(static_cast<int>(text.length()), maxLenText), &sz);
1038*8af74909SZhong Yang } else {
1039*8af74909SZhong Yang const TextWide tbuf(text, unicodeMode, codePage);
1040*8af74909SZhong Yang ::GetTextExtentPoint32W(hdc, tbuf.buffer, tbuf.tlen, &sz);
1041*8af74909SZhong Yang }
1042*8af74909SZhong Yang return static_cast<XYPOSITION>(sz.cx);
1043*8af74909SZhong Yang }
1044*8af74909SZhong Yang
MeasureWidths(Font & font_,std::string_view text,XYPOSITION * positions)1045*8af74909SZhong Yang void SurfaceGDI::MeasureWidths(Font &font_, std::string_view text, XYPOSITION *positions) {
1046*8af74909SZhong Yang // Zero positions to avoid random behaviour on failure.
1047*8af74909SZhong Yang std::fill(positions, positions + text.length(), 0.0f);
1048*8af74909SZhong Yang SetFont(font_);
1049*8af74909SZhong Yang SIZE sz={0,0};
1050*8af74909SZhong Yang int fit = 0;
1051*8af74909SZhong Yang int i = 0;
1052*8af74909SZhong Yang const int len = static_cast<int>(text.length());
1053*8af74909SZhong Yang if (unicodeMode) {
1054*8af74909SZhong Yang const TextWide tbuf(text, unicodeMode, codePage);
1055*8af74909SZhong Yang TextPositionsI poses(tbuf.tlen);
1056*8af74909SZhong Yang if (!::GetTextExtentExPointW(hdc, tbuf.buffer, tbuf.tlen, maxWidthMeasure, &fit, poses.buffer, &sz)) {
1057*8af74909SZhong Yang // Failure
1058*8af74909SZhong Yang return;
1059*8af74909SZhong Yang }
1060*8af74909SZhong Yang // Map the widths given for UTF-16 characters back onto the UTF-8 input string
1061*8af74909SZhong Yang for (int ui = 0; ui < fit; ui++) {
1062*8af74909SZhong Yang const unsigned char uch = text[i];
1063*8af74909SZhong Yang const unsigned int byteCount = UTF8BytesOfLead[uch];
1064*8af74909SZhong Yang if (byteCount == 4) { // Non-BMP
1065*8af74909SZhong Yang ui++;
1066*8af74909SZhong Yang }
1067*8af74909SZhong Yang for (unsigned int bytePos=0; (bytePos<byteCount) && (i<len); bytePos++) {
1068*8af74909SZhong Yang positions[i++] = static_cast<XYPOSITION>(poses.buffer[ui]);
1069*8af74909SZhong Yang }
1070*8af74909SZhong Yang }
1071*8af74909SZhong Yang } else {
1072*8af74909SZhong Yang TextPositionsI poses(len);
1073*8af74909SZhong Yang if (!::GetTextExtentExPointA(hdc, text.data(), len, maxWidthMeasure, &fit, poses.buffer, &sz)) {
1074*8af74909SZhong Yang // Eeek - a NULL DC or other foolishness could cause this.
1075*8af74909SZhong Yang return;
1076*8af74909SZhong Yang }
1077*8af74909SZhong Yang while (i<fit) {
1078*8af74909SZhong Yang positions[i] = static_cast<XYPOSITION>(poses.buffer[i]);
1079*8af74909SZhong Yang i++;
1080*8af74909SZhong Yang }
1081*8af74909SZhong Yang }
1082*8af74909SZhong Yang // If any positions not filled in then use the last position for them
1083*8af74909SZhong Yang const XYPOSITION lastPos = (fit > 0) ? positions[fit - 1] : 0.0f;
1084*8af74909SZhong Yang std::fill(positions+i, positions + text.length(), lastPos);
1085*8af74909SZhong Yang }
1086*8af74909SZhong Yang
Ascent(Font & font_)1087*8af74909SZhong Yang XYPOSITION SurfaceGDI::Ascent(Font &font_) {
1088*8af74909SZhong Yang SetFont(font_);
1089*8af74909SZhong Yang TEXTMETRIC tm;
1090*8af74909SZhong Yang ::GetTextMetrics(hdc, &tm);
1091*8af74909SZhong Yang return static_cast<XYPOSITION>(tm.tmAscent);
1092*8af74909SZhong Yang }
1093*8af74909SZhong Yang
Descent(Font & font_)1094*8af74909SZhong Yang XYPOSITION SurfaceGDI::Descent(Font &font_) {
1095*8af74909SZhong Yang SetFont(font_);
1096*8af74909SZhong Yang TEXTMETRIC tm;
1097*8af74909SZhong Yang ::GetTextMetrics(hdc, &tm);
1098*8af74909SZhong Yang return static_cast<XYPOSITION>(tm.tmDescent);
1099*8af74909SZhong Yang }
1100*8af74909SZhong Yang
InternalLeading(Font & font_)1101*8af74909SZhong Yang XYPOSITION SurfaceGDI::InternalLeading(Font &font_) {
1102*8af74909SZhong Yang SetFont(font_);
1103*8af74909SZhong Yang TEXTMETRIC tm;
1104*8af74909SZhong Yang ::GetTextMetrics(hdc, &tm);
1105*8af74909SZhong Yang return static_cast<XYPOSITION>(tm.tmInternalLeading);
1106*8af74909SZhong Yang }
1107*8af74909SZhong Yang
Height(Font & font_)1108*8af74909SZhong Yang XYPOSITION SurfaceGDI::Height(Font &font_) {
1109*8af74909SZhong Yang SetFont(font_);
1110*8af74909SZhong Yang TEXTMETRIC tm;
1111*8af74909SZhong Yang ::GetTextMetrics(hdc, &tm);
1112*8af74909SZhong Yang return static_cast<XYPOSITION>(tm.tmHeight);
1113*8af74909SZhong Yang }
1114*8af74909SZhong Yang
AverageCharWidth(Font & font_)1115*8af74909SZhong Yang XYPOSITION SurfaceGDI::AverageCharWidth(Font &font_) {
1116*8af74909SZhong Yang SetFont(font_);
1117*8af74909SZhong Yang TEXTMETRIC tm;
1118*8af74909SZhong Yang ::GetTextMetrics(hdc, &tm);
1119*8af74909SZhong Yang return static_cast<XYPOSITION>(tm.tmAveCharWidth);
1120*8af74909SZhong Yang }
1121*8af74909SZhong Yang
SetClip(PRectangle rc)1122*8af74909SZhong Yang void SurfaceGDI::SetClip(PRectangle rc) {
1123*8af74909SZhong Yang ::IntersectClipRect(hdc, static_cast<int>(rc.left), static_cast<int>(rc.top),
1124*8af74909SZhong Yang static_cast<int>(rc.right), static_cast<int>(rc.bottom));
1125*8af74909SZhong Yang }
1126*8af74909SZhong Yang
FlushCachedState()1127*8af74909SZhong Yang void SurfaceGDI::FlushCachedState() {
1128*8af74909SZhong Yang pen = {};
1129*8af74909SZhong Yang brush = {};
1130*8af74909SZhong Yang }
1131*8af74909SZhong Yang
SetUnicodeMode(bool unicodeMode_)1132*8af74909SZhong Yang void SurfaceGDI::SetUnicodeMode(bool unicodeMode_) {
1133*8af74909SZhong Yang unicodeMode=unicodeMode_;
1134*8af74909SZhong Yang }
1135*8af74909SZhong Yang
SetDBCSMode(int codePage_)1136*8af74909SZhong Yang void SurfaceGDI::SetDBCSMode(int codePage_) {
1137*8af74909SZhong Yang // No action on window as automatically handled by system.
1138*8af74909SZhong Yang codePage = codePage_;
1139*8af74909SZhong Yang }
1140*8af74909SZhong Yang
SetBidiR2L(bool)1141*8af74909SZhong Yang void SurfaceGDI::SetBidiR2L(bool) {
1142*8af74909SZhong Yang }
1143*8af74909SZhong Yang
1144*8af74909SZhong Yang #if defined(USE_D2D)
1145*8af74909SZhong Yang
1146*8af74909SZhong Yang namespace {
1147*8af74909SZhong Yang
RectangleFromPRectangle(PRectangle rc)1148*8af74909SZhong Yang constexpr D2D1_RECT_F RectangleFromPRectangle(PRectangle rc) noexcept {
1149*8af74909SZhong Yang return { rc.left, rc.top, rc.right, rc.bottom };
1150*8af74909SZhong Yang }
1151*8af74909SZhong Yang
1152*8af74909SZhong Yang }
1153*8af74909SZhong Yang
1154*8af74909SZhong Yang class BlobInline;
1155*8af74909SZhong Yang
1156*8af74909SZhong Yang class SurfaceD2D : public Surface {
1157*8af74909SZhong Yang bool unicodeMode;
1158*8af74909SZhong Yang int x, y;
1159*8af74909SZhong Yang
1160*8af74909SZhong Yang int codePage;
1161*8af74909SZhong Yang int codePageText;
1162*8af74909SZhong Yang
1163*8af74909SZhong Yang ID2D1RenderTarget *pRenderTarget;
1164*8af74909SZhong Yang ID2D1BitmapRenderTarget *pBitmapRenderTarget;
1165*8af74909SZhong Yang bool ownRenderTarget;
1166*8af74909SZhong Yang int clipsActive;
1167*8af74909SZhong Yang
1168*8af74909SZhong Yang IDWriteTextFormat *pTextFormat;
1169*8af74909SZhong Yang FLOAT yAscent;
1170*8af74909SZhong Yang FLOAT yDescent;
1171*8af74909SZhong Yang FLOAT yInternalLeading;
1172*8af74909SZhong Yang
1173*8af74909SZhong Yang ID2D1SolidColorBrush *pBrush;
1174*8af74909SZhong Yang
1175*8af74909SZhong Yang int logPixelsY;
1176*8af74909SZhong Yang
1177*8af74909SZhong Yang void Clear() noexcept;
1178*8af74909SZhong Yang void SetFont(const Font &font_) noexcept;
1179*8af74909SZhong Yang HRESULT GetBitmap(ID2D1Bitmap **ppBitmap);
1180*8af74909SZhong Yang
1181*8af74909SZhong Yang public:
1182*8af74909SZhong Yang SurfaceD2D() noexcept;
1183*8af74909SZhong Yang // Deleted so SurfaceD2D objects can not be copied.
1184*8af74909SZhong Yang SurfaceD2D(const SurfaceD2D &) = delete;
1185*8af74909SZhong Yang SurfaceD2D(SurfaceD2D &&) = delete;
1186*8af74909SZhong Yang SurfaceD2D &operator=(const SurfaceD2D &) = delete;
1187*8af74909SZhong Yang SurfaceD2D &operator=(SurfaceD2D &&) = delete;
1188*8af74909SZhong Yang ~SurfaceD2D() override;
1189*8af74909SZhong Yang
1190*8af74909SZhong Yang void SetScale(WindowID wid) noexcept;
1191*8af74909SZhong Yang void Init(WindowID wid) override;
1192*8af74909SZhong Yang void Init(SurfaceID sid, WindowID wid) override;
1193*8af74909SZhong Yang void InitPixMap(int width, int height, Surface *surface_, WindowID wid) override;
1194*8af74909SZhong Yang
1195*8af74909SZhong Yang void Release() override;
1196*8af74909SZhong Yang bool Initialised() override;
1197*8af74909SZhong Yang
1198*8af74909SZhong Yang HRESULT FlushDrawing();
1199*8af74909SZhong Yang
1200*8af74909SZhong Yang void PenColour(ColourDesired fore) override;
1201*8af74909SZhong Yang void D2DPenColour(ColourDesired fore, int alpha=255);
1202*8af74909SZhong Yang int LogPixelsY() override;
1203*8af74909SZhong Yang int DeviceHeightFont(int points) override;
1204*8af74909SZhong Yang void MoveTo(int x_, int y_) override;
1205*8af74909SZhong Yang void LineTo(int x_, int y_) override;
1206*8af74909SZhong Yang void Polygon(Point *pts, size_t npts, ColourDesired fore, ColourDesired back) override;
1207*8af74909SZhong Yang void RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) override;
1208*8af74909SZhong Yang void FillRectangle(PRectangle rc, ColourDesired back) override;
1209*8af74909SZhong Yang void FillRectangle(PRectangle rc, Surface &surfacePattern) override;
1210*8af74909SZhong Yang void RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back) override;
1211*8af74909SZhong Yang void AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill,
1212*8af74909SZhong Yang ColourDesired outline, int alphaOutline, int flags) override;
1213*8af74909SZhong Yang void GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) override;
1214*8af74909SZhong Yang void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) override;
1215*8af74909SZhong Yang void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) override;
1216*8af74909SZhong Yang void Copy(PRectangle rc, Point from, Surface &surfaceSource) override;
1217*8af74909SZhong Yang
1218*8af74909SZhong Yang std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override;
1219*8af74909SZhong Yang
1220*8af74909SZhong Yang void DrawTextCommon(PRectangle rc, const Font &font_, XYPOSITION ybase, std::string_view text, UINT fuOptions);
1221*8af74909SZhong Yang void DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override;
1222*8af74909SZhong Yang void DrawTextClipped(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore, ColourDesired back) override;
1223*8af74909SZhong Yang void DrawTextTransparent(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text, ColourDesired fore) override;
1224*8af74909SZhong Yang void MeasureWidths(Font &font_, std::string_view text, XYPOSITION *positions) override;
1225*8af74909SZhong Yang XYPOSITION WidthText(Font &font_, std::string_view text) override;
1226*8af74909SZhong Yang XYPOSITION Ascent(Font &font_) override;
1227*8af74909SZhong Yang XYPOSITION Descent(Font &font_) override;
1228*8af74909SZhong Yang XYPOSITION InternalLeading(Font &font_) override;
1229*8af74909SZhong Yang XYPOSITION Height(Font &font_) override;
1230*8af74909SZhong Yang XYPOSITION AverageCharWidth(Font &font_) override;
1231*8af74909SZhong Yang
1232*8af74909SZhong Yang void SetClip(PRectangle rc) override;
1233*8af74909SZhong Yang void FlushCachedState() override;
1234*8af74909SZhong Yang
1235*8af74909SZhong Yang void SetUnicodeMode(bool unicodeMode_) override;
1236*8af74909SZhong Yang void SetDBCSMode(int codePage_) override;
1237*8af74909SZhong Yang void SetBidiR2L(bool bidiR2L_) override;
1238*8af74909SZhong Yang };
1239*8af74909SZhong Yang
SurfaceD2D()1240*8af74909SZhong Yang SurfaceD2D::SurfaceD2D() noexcept :
1241*8af74909SZhong Yang unicodeMode(false),
1242*8af74909SZhong Yang x(0), y(0) {
1243*8af74909SZhong Yang
1244*8af74909SZhong Yang codePage = 0;
1245*8af74909SZhong Yang codePageText = 0;
1246*8af74909SZhong Yang
1247*8af74909SZhong Yang pRenderTarget = nullptr;
1248*8af74909SZhong Yang pBitmapRenderTarget = nullptr;
1249*8af74909SZhong Yang ownRenderTarget = false;
1250*8af74909SZhong Yang clipsActive = 0;
1251*8af74909SZhong Yang
1252*8af74909SZhong Yang // From selected font
1253*8af74909SZhong Yang pTextFormat = nullptr;
1254*8af74909SZhong Yang yAscent = 2;
1255*8af74909SZhong Yang yDescent = 1;
1256*8af74909SZhong Yang yInternalLeading = 0;
1257*8af74909SZhong Yang
1258*8af74909SZhong Yang pBrush = nullptr;
1259*8af74909SZhong Yang
1260*8af74909SZhong Yang logPixelsY = USER_DEFAULT_SCREEN_DPI;
1261*8af74909SZhong Yang }
1262*8af74909SZhong Yang
~SurfaceD2D()1263*8af74909SZhong Yang SurfaceD2D::~SurfaceD2D() {
1264*8af74909SZhong Yang Clear();
1265*8af74909SZhong Yang }
1266*8af74909SZhong Yang
Clear()1267*8af74909SZhong Yang void SurfaceD2D::Clear() noexcept {
1268*8af74909SZhong Yang ReleaseUnknown(pBrush);
1269*8af74909SZhong Yang if (pRenderTarget) {
1270*8af74909SZhong Yang while (clipsActive) {
1271*8af74909SZhong Yang pRenderTarget->PopAxisAlignedClip();
1272*8af74909SZhong Yang clipsActive--;
1273*8af74909SZhong Yang }
1274*8af74909SZhong Yang if (ownRenderTarget) {
1275*8af74909SZhong Yang pRenderTarget->EndDraw();
1276*8af74909SZhong Yang ReleaseUnknown(pRenderTarget);
1277*8af74909SZhong Yang ownRenderTarget = false;
1278*8af74909SZhong Yang }
1279*8af74909SZhong Yang pRenderTarget = nullptr;
1280*8af74909SZhong Yang }
1281*8af74909SZhong Yang pBitmapRenderTarget = nullptr;
1282*8af74909SZhong Yang }
1283*8af74909SZhong Yang
Release()1284*8af74909SZhong Yang void SurfaceD2D::Release() {
1285*8af74909SZhong Yang Clear();
1286*8af74909SZhong Yang }
1287*8af74909SZhong Yang
SetScale(WindowID wid)1288*8af74909SZhong Yang void SurfaceD2D::SetScale(WindowID wid) noexcept {
1289*8af74909SZhong Yang logPixelsY = DpiForWindow(wid);
1290*8af74909SZhong Yang }
1291*8af74909SZhong Yang
Initialised()1292*8af74909SZhong Yang bool SurfaceD2D::Initialised() {
1293*8af74909SZhong Yang return pRenderTarget != nullptr;
1294*8af74909SZhong Yang }
1295*8af74909SZhong Yang
FlushDrawing()1296*8af74909SZhong Yang HRESULT SurfaceD2D::FlushDrawing() {
1297*8af74909SZhong Yang return pRenderTarget->Flush();
1298*8af74909SZhong Yang }
1299*8af74909SZhong Yang
Init(WindowID wid)1300*8af74909SZhong Yang void SurfaceD2D::Init(WindowID wid) {
1301*8af74909SZhong Yang Release();
1302*8af74909SZhong Yang SetScale(wid);
1303*8af74909SZhong Yang }
1304*8af74909SZhong Yang
Init(SurfaceID sid,WindowID wid)1305*8af74909SZhong Yang void SurfaceD2D::Init(SurfaceID sid, WindowID wid) {
1306*8af74909SZhong Yang Release();
1307*8af74909SZhong Yang SetScale(wid);
1308*8af74909SZhong Yang pRenderTarget = static_cast<ID2D1RenderTarget *>(sid);
1309*8af74909SZhong Yang }
1310*8af74909SZhong Yang
InitPixMap(int width,int height,Surface * surface_,WindowID wid)1311*8af74909SZhong Yang void SurfaceD2D::InitPixMap(int width, int height, Surface *surface_, WindowID wid) {
1312*8af74909SZhong Yang Release();
1313*8af74909SZhong Yang SetScale(wid);
1314*8af74909SZhong Yang SurfaceD2D *psurfOther = dynamic_cast<SurfaceD2D *>(surface_);
1315*8af74909SZhong Yang // Should only ever be called with a SurfaceD2D, not a SurfaceGDI
1316*8af74909SZhong Yang PLATFORM_ASSERT(psurfOther);
1317*8af74909SZhong Yang const D2D1_SIZE_F desiredSize = D2D1::SizeF(static_cast<float>(width), static_cast<float>(height));
1318*8af74909SZhong Yang D2D1_PIXEL_FORMAT desiredFormat;
1319*8af74909SZhong Yang #ifdef __MINGW32__
1320*8af74909SZhong Yang desiredFormat.format = DXGI_FORMAT_UNKNOWN;
1321*8af74909SZhong Yang #else
1322*8af74909SZhong Yang desiredFormat = psurfOther->pRenderTarget->GetPixelFormat();
1323*8af74909SZhong Yang #endif
1324*8af74909SZhong Yang desiredFormat.alphaMode = D2D1_ALPHA_MODE_IGNORE;
1325*8af74909SZhong Yang const HRESULT hr = psurfOther->pRenderTarget->CreateCompatibleRenderTarget(
1326*8af74909SZhong Yang &desiredSize, nullptr, &desiredFormat, D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE, &pBitmapRenderTarget);
1327*8af74909SZhong Yang if (SUCCEEDED(hr)) {
1328*8af74909SZhong Yang pRenderTarget = pBitmapRenderTarget;
1329*8af74909SZhong Yang pRenderTarget->BeginDraw();
1330*8af74909SZhong Yang ownRenderTarget = true;
1331*8af74909SZhong Yang }
1332*8af74909SZhong Yang SetUnicodeMode(psurfOther->unicodeMode);
1333*8af74909SZhong Yang SetDBCSMode(psurfOther->codePage);
1334*8af74909SZhong Yang }
1335*8af74909SZhong Yang
GetBitmap(ID2D1Bitmap ** ppBitmap)1336*8af74909SZhong Yang HRESULT SurfaceD2D::GetBitmap(ID2D1Bitmap **ppBitmap) {
1337*8af74909SZhong Yang PLATFORM_ASSERT(pBitmapRenderTarget);
1338*8af74909SZhong Yang return pBitmapRenderTarget->GetBitmap(ppBitmap);
1339*8af74909SZhong Yang }
1340*8af74909SZhong Yang
PenColour(ColourDesired fore)1341*8af74909SZhong Yang void SurfaceD2D::PenColour(ColourDesired fore) {
1342*8af74909SZhong Yang D2DPenColour(fore);
1343*8af74909SZhong Yang }
1344*8af74909SZhong Yang
D2DPenColour(ColourDesired fore,int alpha)1345*8af74909SZhong Yang void SurfaceD2D::D2DPenColour(ColourDesired fore, int alpha) {
1346*8af74909SZhong Yang if (pRenderTarget) {
1347*8af74909SZhong Yang D2D_COLOR_F col;
1348*8af74909SZhong Yang col.r = fore.GetRedComponent();
1349*8af74909SZhong Yang col.g = fore.GetGreenComponent();
1350*8af74909SZhong Yang col.b = fore.GetBlueComponent();
1351*8af74909SZhong Yang col.a = alpha / 255.0f;
1352*8af74909SZhong Yang if (pBrush) {
1353*8af74909SZhong Yang pBrush->SetColor(col);
1354*8af74909SZhong Yang } else {
1355*8af74909SZhong Yang const HRESULT hr = pRenderTarget->CreateSolidColorBrush(col, &pBrush);
1356*8af74909SZhong Yang if (!SUCCEEDED(hr)) {
1357*8af74909SZhong Yang ReleaseUnknown(pBrush);
1358*8af74909SZhong Yang }
1359*8af74909SZhong Yang }
1360*8af74909SZhong Yang }
1361*8af74909SZhong Yang }
1362*8af74909SZhong Yang
SetFont(const Font & font_)1363*8af74909SZhong Yang void SurfaceD2D::SetFont(const Font &font_) noexcept {
1364*8af74909SZhong Yang const FormatAndMetrics *pfm = FamFromFontID(font_.GetID());
1365*8af74909SZhong Yang PLATFORM_ASSERT(pfm->technology == SCWIN_TECH_DIRECTWRITE);
1366*8af74909SZhong Yang pTextFormat = pfm->pTextFormat;
1367*8af74909SZhong Yang yAscent = pfm->yAscent;
1368*8af74909SZhong Yang yDescent = pfm->yDescent;
1369*8af74909SZhong Yang yInternalLeading = pfm->yInternalLeading;
1370*8af74909SZhong Yang codePageText = codePage;
1371*8af74909SZhong Yang if (!unicodeMode && pfm->characterSet) {
1372*8af74909SZhong Yang codePageText = Scintilla::CodePageFromCharSet(pfm->characterSet, codePage);
1373*8af74909SZhong Yang }
1374*8af74909SZhong Yang if (pRenderTarget) {
1375*8af74909SZhong Yang D2D1_TEXT_ANTIALIAS_MODE aaMode;
1376*8af74909SZhong Yang aaMode = DWriteMapFontQuality(pfm->extraFontFlag);
1377*8af74909SZhong Yang
1378*8af74909SZhong Yang if (aaMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && customClearTypeRenderingParams)
1379*8af74909SZhong Yang pRenderTarget->SetTextRenderingParams(customClearTypeRenderingParams);
1380*8af74909SZhong Yang else if (defaultRenderingParams)
1381*8af74909SZhong Yang pRenderTarget->SetTextRenderingParams(defaultRenderingParams);
1382*8af74909SZhong Yang
1383*8af74909SZhong Yang pRenderTarget->SetTextAntialiasMode(aaMode);
1384*8af74909SZhong Yang }
1385*8af74909SZhong Yang }
1386*8af74909SZhong Yang
LogPixelsY()1387*8af74909SZhong Yang int SurfaceD2D::LogPixelsY() {
1388*8af74909SZhong Yang return logPixelsY;
1389*8af74909SZhong Yang }
1390*8af74909SZhong Yang
DeviceHeightFont(int points)1391*8af74909SZhong Yang int SurfaceD2D::DeviceHeightFont(int points) {
1392*8af74909SZhong Yang return ::MulDiv(points, LogPixelsY(), 72);
1393*8af74909SZhong Yang }
1394*8af74909SZhong Yang
MoveTo(int x_,int y_)1395*8af74909SZhong Yang void SurfaceD2D::MoveTo(int x_, int y_) {
1396*8af74909SZhong Yang x = x_;
1397*8af74909SZhong Yang y = y_;
1398*8af74909SZhong Yang }
1399*8af74909SZhong Yang
Delta(int difference)1400*8af74909SZhong Yang static constexpr int Delta(int difference) noexcept {
1401*8af74909SZhong Yang if (difference < 0)
1402*8af74909SZhong Yang return -1;
1403*8af74909SZhong Yang else if (difference > 0)
1404*8af74909SZhong Yang return 1;
1405*8af74909SZhong Yang else
1406*8af74909SZhong Yang return 0;
1407*8af74909SZhong Yang }
1408*8af74909SZhong Yang
LineTo(int x_,int y_)1409*8af74909SZhong Yang void SurfaceD2D::LineTo(int x_, int y_) {
1410*8af74909SZhong Yang if (pRenderTarget) {
1411*8af74909SZhong Yang const int xDiff = x_ - x;
1412*8af74909SZhong Yang const int xDelta = Delta(xDiff);
1413*8af74909SZhong Yang const int yDiff = y_ - y;
1414*8af74909SZhong Yang const int yDelta = Delta(yDiff);
1415*8af74909SZhong Yang if ((xDiff == 0) || (yDiff == 0)) {
1416*8af74909SZhong Yang // Horizontal or vertical lines can be more precisely drawn as a filled rectangle
1417*8af74909SZhong Yang const int xEnd = x_ - xDelta;
1418*8af74909SZhong Yang const int left = std::min(x, xEnd);
1419*8af74909SZhong Yang const int width = std::abs(x - xEnd) + 1;
1420*8af74909SZhong Yang const int yEnd = y_ - yDelta;
1421*8af74909SZhong Yang const int top = std::min(y, yEnd);
1422*8af74909SZhong Yang const int height = std::abs(y - yEnd) + 1;
1423*8af74909SZhong Yang const D2D1_RECT_F rectangle1 = D2D1::RectF(static_cast<float>(left), static_cast<float>(top),
1424*8af74909SZhong Yang static_cast<float>(left+width), static_cast<float>(top+height));
1425*8af74909SZhong Yang pRenderTarget->FillRectangle(&rectangle1, pBrush);
1426*8af74909SZhong Yang } else if ((std::abs(xDiff) == std::abs(yDiff))) {
1427*8af74909SZhong Yang // 45 degree slope
1428*8af74909SZhong Yang pRenderTarget->DrawLine(D2D1::Point2F(x + 0.5f, y + 0.5f),
1429*8af74909SZhong Yang D2D1::Point2F(x_ + 0.5f - xDelta, y_ + 0.5f - yDelta), pBrush);
1430*8af74909SZhong Yang } else {
1431*8af74909SZhong Yang // Line has a different slope so difficult to avoid last pixel
1432*8af74909SZhong Yang pRenderTarget->DrawLine(D2D1::Point2F(x + 0.5f, y + 0.5f),
1433*8af74909SZhong Yang D2D1::Point2F(x_ + 0.5f, y_ + 0.5f), pBrush);
1434*8af74909SZhong Yang }
1435*8af74909SZhong Yang x = x_;
1436*8af74909SZhong Yang y = y_;
1437*8af74909SZhong Yang }
1438*8af74909SZhong Yang }
1439*8af74909SZhong Yang
Polygon(Point * pts,size_t npts,ColourDesired fore,ColourDesired back)1440*8af74909SZhong Yang void SurfaceD2D::Polygon(Point *pts, size_t npts, ColourDesired fore, ColourDesired back) {
1441*8af74909SZhong Yang PLATFORM_ASSERT(pRenderTarget && (npts > 2));
1442*8af74909SZhong Yang if (pRenderTarget) {
1443*8af74909SZhong Yang ID2D1PathGeometry *geometry = nullptr;
1444*8af74909SZhong Yang HRESULT hr = pD2DFactory->CreatePathGeometry(&geometry);
1445*8af74909SZhong Yang PLATFORM_ASSERT(geometry);
1446*8af74909SZhong Yang if (SUCCEEDED(hr) && geometry) {
1447*8af74909SZhong Yang ID2D1GeometrySink *sink = nullptr;
1448*8af74909SZhong Yang hr = geometry->Open(&sink);
1449*8af74909SZhong Yang PLATFORM_ASSERT(sink);
1450*8af74909SZhong Yang if (SUCCEEDED(hr) && sink) {
1451*8af74909SZhong Yang sink->BeginFigure(D2D1::Point2F(pts[0].x + 0.5f, pts[0].y + 0.5f), D2D1_FIGURE_BEGIN_FILLED);
1452*8af74909SZhong Yang for (size_t i=1; i<npts; i++) {
1453*8af74909SZhong Yang sink->AddLine(D2D1::Point2F(pts[i].x + 0.5f, pts[i].y + 0.5f));
1454*8af74909SZhong Yang }
1455*8af74909SZhong Yang sink->EndFigure(D2D1_FIGURE_END_CLOSED);
1456*8af74909SZhong Yang sink->Close();
1457*8af74909SZhong Yang ReleaseUnknown(sink);
1458*8af74909SZhong Yang
1459*8af74909SZhong Yang D2DPenColour(back);
1460*8af74909SZhong Yang pRenderTarget->FillGeometry(geometry,pBrush);
1461*8af74909SZhong Yang D2DPenColour(fore);
1462*8af74909SZhong Yang pRenderTarget->DrawGeometry(geometry,pBrush);
1463*8af74909SZhong Yang }
1464*8af74909SZhong Yang ReleaseUnknown(geometry);
1465*8af74909SZhong Yang }
1466*8af74909SZhong Yang }
1467*8af74909SZhong Yang }
1468*8af74909SZhong Yang
RectangleDraw(PRectangle rc,ColourDesired fore,ColourDesired back)1469*8af74909SZhong Yang void SurfaceD2D::RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) {
1470*8af74909SZhong Yang if (pRenderTarget) {
1471*8af74909SZhong Yang const D2D1_RECT_F rectangle1 = D2D1::RectF(std::round(rc.left) + 0.5f, rc.top+0.5f, std::round(rc.right) - 0.5f, rc.bottom-0.5f);
1472*8af74909SZhong Yang D2DPenColour(back);
1473*8af74909SZhong Yang pRenderTarget->FillRectangle(&rectangle1, pBrush);
1474*8af74909SZhong Yang D2DPenColour(fore);
1475*8af74909SZhong Yang pRenderTarget->DrawRectangle(&rectangle1, pBrush);
1476*8af74909SZhong Yang }
1477*8af74909SZhong Yang }
1478*8af74909SZhong Yang
FillRectangle(PRectangle rc,ColourDesired back)1479*8af74909SZhong Yang void SurfaceD2D::FillRectangle(PRectangle rc, ColourDesired back) {
1480*8af74909SZhong Yang if (pRenderTarget) {
1481*8af74909SZhong Yang D2DPenColour(back);
1482*8af74909SZhong Yang const D2D1_RECT_F rectangle1 = D2D1::RectF(std::round(rc.left), rc.top, std::round(rc.right), rc.bottom);
1483*8af74909SZhong Yang pRenderTarget->FillRectangle(&rectangle1, pBrush);
1484*8af74909SZhong Yang }
1485*8af74909SZhong Yang }
1486*8af74909SZhong Yang
FillRectangle(PRectangle rc,Surface & surfacePattern)1487*8af74909SZhong Yang void SurfaceD2D::FillRectangle(PRectangle rc, Surface &surfacePattern) {
1488*8af74909SZhong Yang SurfaceD2D *psurfOther = dynamic_cast<SurfaceD2D *>(&surfacePattern);
1489*8af74909SZhong Yang PLATFORM_ASSERT(psurfOther);
1490*8af74909SZhong Yang psurfOther->FlushDrawing();
1491*8af74909SZhong Yang ID2D1Bitmap *pBitmap = nullptr;
1492*8af74909SZhong Yang HRESULT hr = psurfOther->GetBitmap(&pBitmap);
1493*8af74909SZhong Yang if (SUCCEEDED(hr) && pBitmap) {
1494*8af74909SZhong Yang ID2D1BitmapBrush *pBitmapBrush = nullptr;
1495*8af74909SZhong Yang const D2D1_BITMAP_BRUSH_PROPERTIES brushProperties =
1496*8af74909SZhong Yang D2D1::BitmapBrushProperties(D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_WRAP,
1497*8af74909SZhong Yang D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
1498*8af74909SZhong Yang // Create the bitmap brush.
1499*8af74909SZhong Yang hr = pRenderTarget->CreateBitmapBrush(pBitmap, brushProperties, &pBitmapBrush);
1500*8af74909SZhong Yang ReleaseUnknown(pBitmap);
1501*8af74909SZhong Yang if (SUCCEEDED(hr) && pBitmapBrush) {
1502*8af74909SZhong Yang pRenderTarget->FillRectangle(
1503*8af74909SZhong Yang D2D1::RectF(rc.left, rc.top, rc.right, rc.bottom),
1504*8af74909SZhong Yang pBitmapBrush);
1505*8af74909SZhong Yang ReleaseUnknown(pBitmapBrush);
1506*8af74909SZhong Yang }
1507*8af74909SZhong Yang }
1508*8af74909SZhong Yang }
1509*8af74909SZhong Yang
RoundedRectangle(PRectangle rc,ColourDesired fore,ColourDesired back)1510*8af74909SZhong Yang void SurfaceD2D::RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back) {
1511*8af74909SZhong Yang if (pRenderTarget) {
1512*8af74909SZhong Yang D2D1_ROUNDED_RECT roundedRectFill = {
1513*8af74909SZhong Yang D2D1::RectF(rc.left+1.0f, rc.top+1.0f, rc.right-1.0f, rc.bottom-1.0f),
1514*8af74909SZhong Yang 4, 4};
1515*8af74909SZhong Yang D2DPenColour(back);
1516*8af74909SZhong Yang pRenderTarget->FillRoundedRectangle(roundedRectFill, pBrush);
1517*8af74909SZhong Yang
1518*8af74909SZhong Yang D2D1_ROUNDED_RECT roundedRect = {
1519*8af74909SZhong Yang D2D1::RectF(rc.left + 0.5f, rc.top+0.5f, rc.right - 0.5f, rc.bottom-0.5f),
1520*8af74909SZhong Yang 4, 4};
1521*8af74909SZhong Yang D2DPenColour(fore);
1522*8af74909SZhong Yang pRenderTarget->DrawRoundedRectangle(roundedRect, pBrush);
1523*8af74909SZhong Yang }
1524*8af74909SZhong Yang }
1525*8af74909SZhong Yang
AlphaRectangle(PRectangle rc,int cornerSize,ColourDesired fill,int alphaFill,ColourDesired outline,int alphaOutline,int)1526*8af74909SZhong Yang void SurfaceD2D::AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill,
1527*8af74909SZhong Yang ColourDesired outline, int alphaOutline, int /* flags*/ ) {
1528*8af74909SZhong Yang if (pRenderTarget) {
1529*8af74909SZhong Yang if (cornerSize == 0) {
1530*8af74909SZhong Yang // When corner size is zero, draw square rectangle to prevent blurry pixels at corners
1531*8af74909SZhong Yang const D2D1_RECT_F rectFill = D2D1::RectF(std::round(rc.left) + 1.0f, rc.top + 1.0f, std::round(rc.right) - 1.0f, rc.bottom - 1.0f);
1532*8af74909SZhong Yang D2DPenColour(fill, alphaFill);
1533*8af74909SZhong Yang pRenderTarget->FillRectangle(rectFill, pBrush);
1534*8af74909SZhong Yang
1535*8af74909SZhong Yang const D2D1_RECT_F rectOutline = D2D1::RectF(std::round(rc.left) + 0.5f, rc.top + 0.5f, std::round(rc.right) - 0.5f, rc.bottom - 0.5f);
1536*8af74909SZhong Yang D2DPenColour(outline, alphaOutline);
1537*8af74909SZhong Yang pRenderTarget->DrawRectangle(rectOutline, pBrush);
1538*8af74909SZhong Yang } else {
1539*8af74909SZhong Yang const float cornerSizeF = static_cast<float>(cornerSize);
1540*8af74909SZhong Yang D2D1_ROUNDED_RECT roundedRectFill = {
1541*8af74909SZhong Yang D2D1::RectF(std::round(rc.left) + 1.0f, rc.top + 1.0f, std::round(rc.right) - 1.0f, rc.bottom - 1.0f),
1542*8af74909SZhong Yang cornerSizeF - 1.0f, cornerSizeF - 1.0f };
1543*8af74909SZhong Yang D2DPenColour(fill, alphaFill);
1544*8af74909SZhong Yang pRenderTarget->FillRoundedRectangle(roundedRectFill, pBrush);
1545*8af74909SZhong Yang
1546*8af74909SZhong Yang D2D1_ROUNDED_RECT roundedRect = {
1547*8af74909SZhong Yang D2D1::RectF(std::round(rc.left) + 0.5f, rc.top + 0.5f, std::round(rc.right) - 0.5f, rc.bottom - 0.5f),
1548*8af74909SZhong Yang cornerSizeF, cornerSizeF};
1549*8af74909SZhong Yang D2DPenColour(outline, alphaOutline);
1550*8af74909SZhong Yang pRenderTarget->DrawRoundedRectangle(roundedRect, pBrush);
1551*8af74909SZhong Yang }
1552*8af74909SZhong Yang }
1553*8af74909SZhong Yang }
1554*8af74909SZhong Yang
1555*8af74909SZhong Yang namespace {
1556*8af74909SZhong Yang
ColorFromColourAlpha(ColourAlpha colour)1557*8af74909SZhong Yang D2D_COLOR_F ColorFromColourAlpha(ColourAlpha colour) noexcept {
1558*8af74909SZhong Yang D2D_COLOR_F col;
1559*8af74909SZhong Yang col.r = colour.GetRedComponent();
1560*8af74909SZhong Yang col.g = colour.GetGreenComponent();
1561*8af74909SZhong Yang col.b = colour.GetBlueComponent();
1562*8af74909SZhong Yang col.a = colour.GetAlphaComponent();
1563*8af74909SZhong Yang return col;
1564*8af74909SZhong Yang }
1565*8af74909SZhong Yang
1566*8af74909SZhong Yang }
1567*8af74909SZhong Yang
GradientRectangle(PRectangle rc,const std::vector<ColourStop> & stops,GradientOptions options)1568*8af74909SZhong Yang void SurfaceD2D::GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) {
1569*8af74909SZhong Yang if (pRenderTarget) {
1570*8af74909SZhong Yang D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES lgbp;
1571*8af74909SZhong Yang lgbp.startPoint = D2D1::Point2F(rc.left, rc.top);
1572*8af74909SZhong Yang switch (options) {
1573*8af74909SZhong Yang case GradientOptions::leftToRight:
1574*8af74909SZhong Yang lgbp.endPoint = D2D1::Point2F(rc.right, rc.top);
1575*8af74909SZhong Yang break;
1576*8af74909SZhong Yang case GradientOptions::topToBottom:
1577*8af74909SZhong Yang default:
1578*8af74909SZhong Yang lgbp.endPoint = D2D1::Point2F(rc.left, rc.bottom);
1579*8af74909SZhong Yang break;
1580*8af74909SZhong Yang }
1581*8af74909SZhong Yang
1582*8af74909SZhong Yang std::vector<D2D1_GRADIENT_STOP> gradientStops;
1583*8af74909SZhong Yang for (const ColourStop &stop : stops) {
1584*8af74909SZhong Yang gradientStops.push_back({ stop.position, ColorFromColourAlpha(stop.colour) });
1585*8af74909SZhong Yang }
1586*8af74909SZhong Yang ID2D1GradientStopCollection *pGradientStops = nullptr;
1587*8af74909SZhong Yang HRESULT hr = pRenderTarget->CreateGradientStopCollection(
1588*8af74909SZhong Yang gradientStops.data(), static_cast<UINT32>(gradientStops.size()), &pGradientStops);
1589*8af74909SZhong Yang if (FAILED(hr) || !pGradientStops) {
1590*8af74909SZhong Yang return;
1591*8af74909SZhong Yang }
1592*8af74909SZhong Yang ID2D1LinearGradientBrush *pBrushLinear = nullptr;
1593*8af74909SZhong Yang hr = pRenderTarget->CreateLinearGradientBrush(
1594*8af74909SZhong Yang lgbp, pGradientStops, &pBrushLinear);
1595*8af74909SZhong Yang if (SUCCEEDED(hr) && pBrushLinear) {
1596*8af74909SZhong Yang const D2D1_RECT_F rectangle = D2D1::RectF(std::round(rc.left), rc.top, std::round(rc.right), rc.bottom);
1597*8af74909SZhong Yang pRenderTarget->FillRectangle(&rectangle, pBrushLinear);
1598*8af74909SZhong Yang ReleaseUnknown(pBrushLinear);
1599*8af74909SZhong Yang }
1600*8af74909SZhong Yang ReleaseUnknown(pGradientStops);
1601*8af74909SZhong Yang }
1602*8af74909SZhong Yang }
1603*8af74909SZhong Yang
DrawRGBAImage(PRectangle rc,int width,int height,const unsigned char * pixelsImage)1604*8af74909SZhong Yang void SurfaceD2D::DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) {
1605*8af74909SZhong Yang if (pRenderTarget) {
1606*8af74909SZhong Yang if (rc.Width() > width)
1607*8af74909SZhong Yang rc.left += std::floor((rc.Width() - width) / 2);
1608*8af74909SZhong Yang rc.right = rc.left + width;
1609*8af74909SZhong Yang if (rc.Height() > height)
1610*8af74909SZhong Yang rc.top += std::floor((rc.Height() - height) / 2);
1611*8af74909SZhong Yang rc.bottom = rc.top + height;
1612*8af74909SZhong Yang
1613*8af74909SZhong Yang std::vector<unsigned char> image(RGBAImage::bytesPerPixel * height * width);
1614*8af74909SZhong Yang RGBAImage::BGRAFromRGBA(image.data(), pixelsImage, static_cast<ptrdiff_t>(height) * width);
1615*8af74909SZhong Yang
1616*8af74909SZhong Yang ID2D1Bitmap *bitmap = nullptr;
1617*8af74909SZhong Yang const D2D1_SIZE_U size = D2D1::SizeU(width, height);
1618*8af74909SZhong Yang D2D1_BITMAP_PROPERTIES props = {{DXGI_FORMAT_B8G8R8A8_UNORM,
1619*8af74909SZhong Yang D2D1_ALPHA_MODE_PREMULTIPLIED}, 72.0, 72.0};
1620*8af74909SZhong Yang const HRESULT hr = pRenderTarget->CreateBitmap(size, image.data(),
1621*8af74909SZhong Yang width * 4, &props, &bitmap);
1622*8af74909SZhong Yang if (SUCCEEDED(hr)) {
1623*8af74909SZhong Yang const D2D1_RECT_F rcDestination = RectangleFromPRectangle(rc);
1624*8af74909SZhong Yang pRenderTarget->DrawBitmap(bitmap, rcDestination);
1625*8af74909SZhong Yang ReleaseUnknown(bitmap);
1626*8af74909SZhong Yang }
1627*8af74909SZhong Yang }
1628*8af74909SZhong Yang }
1629*8af74909SZhong Yang
Ellipse(PRectangle rc,ColourDesired fore,ColourDesired back)1630*8af74909SZhong Yang void SurfaceD2D::Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) {
1631*8af74909SZhong Yang if (pRenderTarget) {
1632*8af74909SZhong Yang const FLOAT radius = rc.Width() / 2.0f;
1633*8af74909SZhong Yang D2D1_ELLIPSE ellipse = {
1634*8af74909SZhong Yang D2D1::Point2F((rc.left + rc.right) / 2.0f, (rc.top + rc.bottom) / 2.0f),
1635*8af74909SZhong Yang radius,radius};
1636*8af74909SZhong Yang
1637*8af74909SZhong Yang PenColour(back);
1638*8af74909SZhong Yang pRenderTarget->FillEllipse(ellipse, pBrush);
1639*8af74909SZhong Yang PenColour(fore);
1640*8af74909SZhong Yang pRenderTarget->DrawEllipse(ellipse, pBrush);
1641*8af74909SZhong Yang }
1642*8af74909SZhong Yang }
1643*8af74909SZhong Yang
Copy(PRectangle rc,Point from,Surface & surfaceSource)1644*8af74909SZhong Yang void SurfaceD2D::Copy(PRectangle rc, Point from, Surface &surfaceSource) {
1645*8af74909SZhong Yang SurfaceD2D &surfOther = dynamic_cast<SurfaceD2D &>(surfaceSource);
1646*8af74909SZhong Yang surfOther.FlushDrawing();
1647*8af74909SZhong Yang ID2D1Bitmap *pBitmap = nullptr;
1648*8af74909SZhong Yang HRESULT hr = surfOther.GetBitmap(&pBitmap);
1649*8af74909SZhong Yang if (SUCCEEDED(hr) && pBitmap) {
1650*8af74909SZhong Yang const D2D1_RECT_F rcDestination = RectangleFromPRectangle(rc);
1651*8af74909SZhong Yang D2D1_RECT_F rcSource = {from.x, from.y, from.x + rc.Width(), from.y + rc.Height()};
1652*8af74909SZhong Yang pRenderTarget->DrawBitmap(pBitmap, rcDestination, 1.0f,
1653*8af74909SZhong Yang D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, rcSource);
1654*8af74909SZhong Yang hr = pRenderTarget->Flush();
1655*8af74909SZhong Yang if (FAILED(hr)) {
1656*8af74909SZhong Yang Platform::DebugPrintf("Failed Flush 0x%lx\n", hr);
1657*8af74909SZhong Yang }
1658*8af74909SZhong Yang ReleaseUnknown(pBitmap);
1659*8af74909SZhong Yang }
1660*8af74909SZhong Yang }
1661*8af74909SZhong Yang
1662*8af74909SZhong Yang class BlobInline : public IDWriteInlineObject {
1663*8af74909SZhong Yang XYPOSITION width;
1664*8af74909SZhong Yang
1665*8af74909SZhong Yang // IUnknown
1666*8af74909SZhong Yang STDMETHODIMP QueryInterface(REFIID riid, PVOID *ppv) override;
1667*8af74909SZhong Yang STDMETHODIMP_(ULONG)AddRef() override;
1668*8af74909SZhong Yang STDMETHODIMP_(ULONG)Release() override;
1669*8af74909SZhong Yang
1670*8af74909SZhong Yang // IDWriteInlineObject
1671*8af74909SZhong Yang COM_DECLSPEC_NOTHROW STDMETHODIMP Draw(
1672*8af74909SZhong Yang void *clientDrawingContext,
1673*8af74909SZhong Yang IDWriteTextRenderer *renderer,
1674*8af74909SZhong Yang FLOAT originX,
1675*8af74909SZhong Yang FLOAT originY,
1676*8af74909SZhong Yang BOOL isSideways,
1677*8af74909SZhong Yang BOOL isRightToLeft,
1678*8af74909SZhong Yang IUnknown *clientDrawingEffect
1679*8af74909SZhong Yang ) override;
1680*8af74909SZhong Yang COM_DECLSPEC_NOTHROW STDMETHODIMP GetMetrics(DWRITE_INLINE_OBJECT_METRICS *metrics) override;
1681*8af74909SZhong Yang COM_DECLSPEC_NOTHROW STDMETHODIMP GetOverhangMetrics(DWRITE_OVERHANG_METRICS *overhangs) override;
1682*8af74909SZhong Yang COM_DECLSPEC_NOTHROW STDMETHODIMP GetBreakConditions(
1683*8af74909SZhong Yang DWRITE_BREAK_CONDITION *breakConditionBefore,
1684*8af74909SZhong Yang DWRITE_BREAK_CONDITION *breakConditionAfter) override;
1685*8af74909SZhong Yang public:
BlobInline(XYPOSITION width_=0.0f)1686*8af74909SZhong Yang BlobInline(XYPOSITION width_=0.0f) noexcept : width(width_) {
1687*8af74909SZhong Yang }
1688*8af74909SZhong Yang // Defaulted so BlobInline objects can be copied.
1689*8af74909SZhong Yang BlobInline(const BlobInline &) = default;
1690*8af74909SZhong Yang BlobInline(BlobInline &&) = default;
1691*8af74909SZhong Yang BlobInline &operator=(const BlobInline &) = default;
1692*8af74909SZhong Yang BlobInline &operator=(BlobInline &&) = default;
~BlobInline()1693*8af74909SZhong Yang virtual ~BlobInline() {
1694*8af74909SZhong Yang }
1695*8af74909SZhong Yang };
1696*8af74909SZhong Yang
1697*8af74909SZhong Yang /// Implement IUnknown
QueryInterface(REFIID riid,PVOID * ppv)1698*8af74909SZhong Yang STDMETHODIMP BlobInline::QueryInterface(REFIID riid, PVOID *ppv) {
1699*8af74909SZhong Yang if (!ppv)
1700*8af74909SZhong Yang return E_POINTER;
1701*8af74909SZhong Yang // Never called so not checked.
1702*8af74909SZhong Yang *ppv = nullptr;
1703*8af74909SZhong Yang if (riid == IID_IUnknown)
1704*8af74909SZhong Yang *ppv = static_cast<IUnknown *>(this);
1705*8af74909SZhong Yang if (riid == __uuidof(IDWriteInlineObject))
1706*8af74909SZhong Yang *ppv = static_cast<IDWriteInlineObject *>(this);
1707*8af74909SZhong Yang if (!*ppv)
1708*8af74909SZhong Yang return E_NOINTERFACE;
1709*8af74909SZhong Yang return S_OK;
1710*8af74909SZhong Yang }
1711*8af74909SZhong Yang
STDMETHODIMP_(ULONG)1712*8af74909SZhong Yang STDMETHODIMP_(ULONG) BlobInline::AddRef() {
1713*8af74909SZhong Yang // Lifetime tied to Platform methods so ignore any reference operations.
1714*8af74909SZhong Yang return 1;
1715*8af74909SZhong Yang }
1716*8af74909SZhong Yang
STDMETHODIMP_(ULONG)1717*8af74909SZhong Yang STDMETHODIMP_(ULONG) BlobInline::Release() {
1718*8af74909SZhong Yang // Lifetime tied to Platform methods so ignore any reference operations.
1719*8af74909SZhong Yang return 1;
1720*8af74909SZhong Yang }
1721*8af74909SZhong Yang
1722*8af74909SZhong Yang /// Implement IDWriteInlineObject
Draw(void *,IDWriteTextRenderer *,FLOAT,FLOAT,BOOL,BOOL,IUnknown *)1723*8af74909SZhong Yang COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE BlobInline::Draw(
1724*8af74909SZhong Yang void*,
1725*8af74909SZhong Yang IDWriteTextRenderer*,
1726*8af74909SZhong Yang FLOAT,
1727*8af74909SZhong Yang FLOAT,
1728*8af74909SZhong Yang BOOL,
1729*8af74909SZhong Yang BOOL,
1730*8af74909SZhong Yang IUnknown*) {
1731*8af74909SZhong Yang // Since not performing drawing, not necessary to implement
1732*8af74909SZhong Yang // Could be implemented by calling back into platform-independent code.
1733*8af74909SZhong Yang // This would allow more of the drawing to be mediated through DirectWrite.
1734*8af74909SZhong Yang return S_OK;
1735*8af74909SZhong Yang }
1736*8af74909SZhong Yang
GetMetrics(DWRITE_INLINE_OBJECT_METRICS * metrics)1737*8af74909SZhong Yang COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE BlobInline::GetMetrics(
1738*8af74909SZhong Yang DWRITE_INLINE_OBJECT_METRICS *metrics
1739*8af74909SZhong Yang ) {
1740*8af74909SZhong Yang if (!metrics)
1741*8af74909SZhong Yang return E_POINTER;
1742*8af74909SZhong Yang metrics->width = width;
1743*8af74909SZhong Yang metrics->height = 2;
1744*8af74909SZhong Yang metrics->baseline = 1;
1745*8af74909SZhong Yang metrics->supportsSideways = FALSE;
1746*8af74909SZhong Yang return S_OK;
1747*8af74909SZhong Yang }
1748*8af74909SZhong Yang
GetOverhangMetrics(DWRITE_OVERHANG_METRICS * overhangs)1749*8af74909SZhong Yang COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE BlobInline::GetOverhangMetrics(
1750*8af74909SZhong Yang DWRITE_OVERHANG_METRICS *overhangs
1751*8af74909SZhong Yang ) {
1752*8af74909SZhong Yang if (!overhangs)
1753*8af74909SZhong Yang return E_POINTER;
1754*8af74909SZhong Yang overhangs->left = 0;
1755*8af74909SZhong Yang overhangs->top = 0;
1756*8af74909SZhong Yang overhangs->right = 0;
1757*8af74909SZhong Yang overhangs->bottom = 0;
1758*8af74909SZhong Yang return S_OK;
1759*8af74909SZhong Yang }
1760*8af74909SZhong Yang
GetBreakConditions(DWRITE_BREAK_CONDITION * breakConditionBefore,DWRITE_BREAK_CONDITION * breakConditionAfter)1761*8af74909SZhong Yang COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE BlobInline::GetBreakConditions(
1762*8af74909SZhong Yang DWRITE_BREAK_CONDITION *breakConditionBefore,
1763*8af74909SZhong Yang DWRITE_BREAK_CONDITION *breakConditionAfter
1764*8af74909SZhong Yang ) {
1765*8af74909SZhong Yang if (!breakConditionBefore || !breakConditionAfter)
1766*8af74909SZhong Yang return E_POINTER;
1767*8af74909SZhong Yang // Since not performing 2D layout, not necessary to implement
1768*8af74909SZhong Yang *breakConditionBefore = DWRITE_BREAK_CONDITION_NEUTRAL;
1769*8af74909SZhong Yang *breakConditionAfter = DWRITE_BREAK_CONDITION_NEUTRAL;
1770*8af74909SZhong Yang return S_OK;
1771*8af74909SZhong Yang }
1772*8af74909SZhong Yang
1773*8af74909SZhong Yang class ScreenLineLayout : public IScreenLineLayout {
1774*8af74909SZhong Yang IDWriteTextLayout *textLayout = nullptr;
1775*8af74909SZhong Yang std::string text;
1776*8af74909SZhong Yang std::wstring buffer;
1777*8af74909SZhong Yang std::vector<BlobInline> blobs;
1778*8af74909SZhong Yang static void FillTextLayoutFormats(const IScreenLine *screenLine, IDWriteTextLayout *textLayout, std::vector<BlobInline> &blobs);
1779*8af74909SZhong Yang static std::wstring ReplaceRepresentation(std::string_view text);
1780*8af74909SZhong Yang static size_t GetPositionInLayout(std::string_view text, size_t position);
1781*8af74909SZhong Yang public:
1782*8af74909SZhong Yang ScreenLineLayout(const IScreenLine *screenLine);
1783*8af74909SZhong Yang // Deleted so ScreenLineLayout objects can not be copied
1784*8af74909SZhong Yang ScreenLineLayout(const ScreenLineLayout &) = delete;
1785*8af74909SZhong Yang ScreenLineLayout(ScreenLineLayout &&) = delete;
1786*8af74909SZhong Yang ScreenLineLayout &operator=(const ScreenLineLayout &) = delete;
1787*8af74909SZhong Yang ScreenLineLayout &operator=(ScreenLineLayout &&) = delete;
1788*8af74909SZhong Yang ~ScreenLineLayout() override;
1789*8af74909SZhong Yang size_t PositionFromX(XYPOSITION xDistance, bool charPosition) override;
1790*8af74909SZhong Yang XYPOSITION XFromPosition(size_t caretPosition) override;
1791*8af74909SZhong Yang std::vector<Interval> FindRangeIntervals(size_t start, size_t end) override;
1792*8af74909SZhong Yang };
1793*8af74909SZhong Yang
1794*8af74909SZhong Yang // Each char can have its own style, so we fill the textLayout with the textFormat of each char
1795*8af74909SZhong Yang
FillTextLayoutFormats(const IScreenLine * screenLine,IDWriteTextLayout * textLayout,std::vector<BlobInline> & blobs)1796*8af74909SZhong Yang void ScreenLineLayout::FillTextLayoutFormats(const IScreenLine *screenLine, IDWriteTextLayout *textLayout, std::vector<BlobInline> &blobs) {
1797*8af74909SZhong Yang // Reserve enough entries up front so they are not moved and the pointers handed
1798*8af74909SZhong Yang // to textLayout remain valid.
1799*8af74909SZhong Yang const ptrdiff_t numRepresentations = screenLine->RepresentationCount();
1800*8af74909SZhong Yang std::string_view text = screenLine->Text();
1801*8af74909SZhong Yang const ptrdiff_t numTabs = std::count(std::begin(text), std::end(text), '\t');
1802*8af74909SZhong Yang blobs.reserve(numRepresentations + numTabs);
1803*8af74909SZhong Yang
1804*8af74909SZhong Yang UINT32 layoutPosition = 0;
1805*8af74909SZhong Yang
1806*8af74909SZhong Yang for (size_t bytePosition = 0; bytePosition < screenLine->Length();) {
1807*8af74909SZhong Yang const unsigned char uch = screenLine->Text()[bytePosition];
1808*8af74909SZhong Yang const unsigned int byteCount = UTF8BytesOfLead[uch];
1809*8af74909SZhong Yang const UINT32 codeUnits = UTF16LengthFromUTF8ByteCount(byteCount);
1810*8af74909SZhong Yang const DWRITE_TEXT_RANGE textRange = { layoutPosition, codeUnits };
1811*8af74909SZhong Yang
1812*8af74909SZhong Yang XYPOSITION representationWidth = screenLine->RepresentationWidth(bytePosition);
1813*8af74909SZhong Yang if ((representationWidth == 0.0f) && (screenLine->Text()[bytePosition] == '\t')) {
1814*8af74909SZhong Yang Point realPt;
1815*8af74909SZhong Yang DWRITE_HIT_TEST_METRICS realCaretMetrics {};
1816*8af74909SZhong Yang textLayout->HitTestTextPosition(
1817*8af74909SZhong Yang layoutPosition,
1818*8af74909SZhong Yang false, // trailing if false, else leading edge
1819*8af74909SZhong Yang &realPt.x,
1820*8af74909SZhong Yang &realPt.y,
1821*8af74909SZhong Yang &realCaretMetrics
1822*8af74909SZhong Yang );
1823*8af74909SZhong Yang
1824*8af74909SZhong Yang const XYPOSITION nextTab = screenLine->TabPositionAfter(realPt.x);
1825*8af74909SZhong Yang representationWidth = nextTab - realPt.x;
1826*8af74909SZhong Yang }
1827*8af74909SZhong Yang if (representationWidth > 0.0f) {
1828*8af74909SZhong Yang blobs.push_back(BlobInline(representationWidth));
1829*8af74909SZhong Yang textLayout->SetInlineObject(&blobs.back(), textRange);
1830*8af74909SZhong Yang };
1831*8af74909SZhong Yang
1832*8af74909SZhong Yang FormatAndMetrics *pfm =
1833*8af74909SZhong Yang static_cast<FormatAndMetrics *>(screenLine->FontOfPosition(bytePosition)->GetID());
1834*8af74909SZhong Yang
1835*8af74909SZhong Yang const unsigned int fontFamilyNameSize = pfm->pTextFormat->GetFontFamilyNameLength();
1836*8af74909SZhong Yang std::wstring fontFamilyName(fontFamilyNameSize, 0);
1837*8af74909SZhong Yang const HRESULT hrFamily = pfm->pTextFormat->GetFontFamilyName(fontFamilyName.data(), fontFamilyNameSize + 1);
1838*8af74909SZhong Yang if (SUCCEEDED(hrFamily)) {
1839*8af74909SZhong Yang textLayout->SetFontFamilyName(fontFamilyName.c_str(), textRange);
1840*8af74909SZhong Yang }
1841*8af74909SZhong Yang
1842*8af74909SZhong Yang textLayout->SetFontSize(pfm->pTextFormat->GetFontSize(), textRange);
1843*8af74909SZhong Yang textLayout->SetFontWeight(pfm->pTextFormat->GetFontWeight(), textRange);
1844*8af74909SZhong Yang textLayout->SetFontStyle(pfm->pTextFormat->GetFontStyle(), textRange);
1845*8af74909SZhong Yang
1846*8af74909SZhong Yang const unsigned int localeNameSize = pfm->pTextFormat->GetLocaleNameLength();
1847*8af74909SZhong Yang std::wstring localeName(localeNameSize, 0);
1848*8af74909SZhong Yang const HRESULT hrLocale = pfm->pTextFormat->GetLocaleName(localeName.data(), localeNameSize + 1);
1849*8af74909SZhong Yang if (SUCCEEDED(hrLocale)) {
1850*8af74909SZhong Yang textLayout->SetLocaleName(localeName.c_str(), textRange);
1851*8af74909SZhong Yang }
1852*8af74909SZhong Yang
1853*8af74909SZhong Yang textLayout->SetFontStretch(pfm->pTextFormat->GetFontStretch(), textRange);
1854*8af74909SZhong Yang
1855*8af74909SZhong Yang IDWriteFontCollection *fontCollection = nullptr;
1856*8af74909SZhong Yang if (SUCCEEDED(pfm->pTextFormat->GetFontCollection(&fontCollection))) {
1857*8af74909SZhong Yang textLayout->SetFontCollection(fontCollection, textRange);
1858*8af74909SZhong Yang }
1859*8af74909SZhong Yang
1860*8af74909SZhong Yang bytePosition += byteCount;
1861*8af74909SZhong Yang layoutPosition += codeUnits;
1862*8af74909SZhong Yang }
1863*8af74909SZhong Yang
1864*8af74909SZhong Yang }
1865*8af74909SZhong Yang
1866*8af74909SZhong Yang /* Convert to a wide character string and replace tabs with X to stop DirectWrite tab expansion */
1867*8af74909SZhong Yang
ReplaceRepresentation(std::string_view text)1868*8af74909SZhong Yang std::wstring ScreenLineLayout::ReplaceRepresentation(std::string_view text) {
1869*8af74909SZhong Yang const TextWide wideText(text, true);
1870*8af74909SZhong Yang std::wstring ws(wideText.buffer, wideText.tlen);
1871*8af74909SZhong Yang std::replace(ws.begin(), ws.end(), L'\t', L'X');
1872*8af74909SZhong Yang return ws;
1873*8af74909SZhong Yang }
1874*8af74909SZhong Yang
1875*8af74909SZhong Yang // Finds the position in the wide character version of the text.
1876*8af74909SZhong Yang
GetPositionInLayout(std::string_view text,size_t position)1877*8af74909SZhong Yang size_t ScreenLineLayout::GetPositionInLayout(std::string_view text, size_t position) {
1878*8af74909SZhong Yang const std::string_view textUptoPosition = text.substr(0, position);
1879*8af74909SZhong Yang return UTF16Length(textUptoPosition);
1880*8af74909SZhong Yang }
1881*8af74909SZhong Yang
ScreenLineLayout(const IScreenLine * screenLine)1882*8af74909SZhong Yang ScreenLineLayout::ScreenLineLayout(const IScreenLine *screenLine) {
1883*8af74909SZhong Yang // If the text is empty, then no need to go through this function
1884*8af74909SZhong Yang if (!screenLine || !screenLine->Length())
1885*8af74909SZhong Yang return;
1886*8af74909SZhong Yang
1887*8af74909SZhong Yang text = screenLine->Text();
1888*8af74909SZhong Yang
1889*8af74909SZhong Yang // Get textFormat
1890*8af74909SZhong Yang FormatAndMetrics *pfm = static_cast<FormatAndMetrics *>(screenLine->FontOfPosition(0)->GetID());
1891*8af74909SZhong Yang
1892*8af74909SZhong Yang if (!pIDWriteFactory || !pfm->pTextFormat) {
1893*8af74909SZhong Yang return;
1894*8af74909SZhong Yang }
1895*8af74909SZhong Yang
1896*8af74909SZhong Yang // Convert the string to wstring and replace the original control characters with their representative chars.
1897*8af74909SZhong Yang buffer = ReplaceRepresentation(screenLine->Text());
1898*8af74909SZhong Yang
1899*8af74909SZhong Yang // Create a text layout
1900*8af74909SZhong Yang const HRESULT hrCreate = pIDWriteFactory->CreateTextLayout(buffer.c_str(), static_cast<UINT32>(buffer.length()),
1901*8af74909SZhong Yang pfm->pTextFormat, screenLine->Width(), screenLine->Height(), &textLayout);
1902*8af74909SZhong Yang if (!SUCCEEDED(hrCreate)) {
1903*8af74909SZhong Yang return;
1904*8af74909SZhong Yang }
1905*8af74909SZhong Yang
1906*8af74909SZhong Yang // Fill the textLayout chars with their own formats
1907*8af74909SZhong Yang FillTextLayoutFormats(screenLine, textLayout, blobs);
1908*8af74909SZhong Yang }
1909*8af74909SZhong Yang
~ScreenLineLayout()1910*8af74909SZhong Yang ScreenLineLayout::~ScreenLineLayout() {
1911*8af74909SZhong Yang ReleaseUnknown(textLayout);
1912*8af74909SZhong Yang }
1913*8af74909SZhong Yang
1914*8af74909SZhong Yang // Get the position from the provided x
1915*8af74909SZhong Yang
PositionFromX(XYPOSITION xDistance,bool charPosition)1916*8af74909SZhong Yang size_t ScreenLineLayout::PositionFromX(XYPOSITION xDistance, bool charPosition) {
1917*8af74909SZhong Yang if (!textLayout) {
1918*8af74909SZhong Yang return 0;
1919*8af74909SZhong Yang }
1920*8af74909SZhong Yang
1921*8af74909SZhong Yang // Returns the text position corresponding to the mouse x,y.
1922*8af74909SZhong Yang // If hitting the trailing side of a cluster, return the
1923*8af74909SZhong Yang // leading edge of the following text position.
1924*8af74909SZhong Yang
1925*8af74909SZhong Yang BOOL isTrailingHit = FALSE;
1926*8af74909SZhong Yang BOOL isInside = FALSE;
1927*8af74909SZhong Yang DWRITE_HIT_TEST_METRICS caretMetrics {};
1928*8af74909SZhong Yang
1929*8af74909SZhong Yang textLayout->HitTestPoint(
1930*8af74909SZhong Yang xDistance,
1931*8af74909SZhong Yang 0.0f,
1932*8af74909SZhong Yang &isTrailingHit,
1933*8af74909SZhong Yang &isInside,
1934*8af74909SZhong Yang &caretMetrics
1935*8af74909SZhong Yang );
1936*8af74909SZhong Yang
1937*8af74909SZhong Yang DWRITE_HIT_TEST_METRICS hitTestMetrics {};
1938*8af74909SZhong Yang if (isTrailingHit) {
1939*8af74909SZhong Yang FLOAT caretX = 0.0f;
1940*8af74909SZhong Yang FLOAT caretY = 0.0f;
1941*8af74909SZhong Yang
1942*8af74909SZhong Yang // Uses hit-testing to align the current caret position to a whole cluster,
1943*8af74909SZhong Yang // rather than residing in the middle of a base character + diacritic,
1944*8af74909SZhong Yang // surrogate pair, or character + UVS.
1945*8af74909SZhong Yang
1946*8af74909SZhong Yang // Align the caret to the nearest whole cluster.
1947*8af74909SZhong Yang textLayout->HitTestTextPosition(
1948*8af74909SZhong Yang caretMetrics.textPosition,
1949*8af74909SZhong Yang false,
1950*8af74909SZhong Yang &caretX,
1951*8af74909SZhong Yang &caretY,
1952*8af74909SZhong Yang &hitTestMetrics
1953*8af74909SZhong Yang );
1954*8af74909SZhong Yang }
1955*8af74909SZhong Yang
1956*8af74909SZhong Yang size_t pos;
1957*8af74909SZhong Yang if (charPosition) {
1958*8af74909SZhong Yang pos = isTrailingHit ? hitTestMetrics.textPosition : caretMetrics.textPosition;
1959*8af74909SZhong Yang } else {
1960*8af74909SZhong Yang pos = isTrailingHit ? hitTestMetrics.textPosition + hitTestMetrics.length : caretMetrics.textPosition;
1961*8af74909SZhong Yang }
1962*8af74909SZhong Yang
1963*8af74909SZhong Yang // Get the character position in original string
1964*8af74909SZhong Yang return UTF8PositionFromUTF16Position(text, pos);
1965*8af74909SZhong Yang }
1966*8af74909SZhong Yang
1967*8af74909SZhong Yang // Finds the point of the caret position
1968*8af74909SZhong Yang
XFromPosition(size_t caretPosition)1969*8af74909SZhong Yang XYPOSITION ScreenLineLayout::XFromPosition(size_t caretPosition) {
1970*8af74909SZhong Yang if (!textLayout) {
1971*8af74909SZhong Yang return 0.0;
1972*8af74909SZhong Yang }
1973*8af74909SZhong Yang // Convert byte positions to wchar_t positions
1974*8af74909SZhong Yang const size_t position = GetPositionInLayout(text, caretPosition);
1975*8af74909SZhong Yang
1976*8af74909SZhong Yang // Translate text character offset to point x,y.
1977*8af74909SZhong Yang DWRITE_HIT_TEST_METRICS caretMetrics {};
1978*8af74909SZhong Yang Point pt;
1979*8af74909SZhong Yang
1980*8af74909SZhong Yang textLayout->HitTestTextPosition(
1981*8af74909SZhong Yang static_cast<UINT32>(position),
1982*8af74909SZhong Yang false, // trailing if false, else leading edge
1983*8af74909SZhong Yang &pt.x,
1984*8af74909SZhong Yang &pt.y,
1985*8af74909SZhong Yang &caretMetrics
1986*8af74909SZhong Yang );
1987*8af74909SZhong Yang
1988*8af74909SZhong Yang return pt.x;
1989*8af74909SZhong Yang }
1990*8af74909SZhong Yang
1991*8af74909SZhong Yang // Find the selection range rectangles
1992*8af74909SZhong Yang
FindRangeIntervals(size_t start,size_t end)1993*8af74909SZhong Yang std::vector<Interval> ScreenLineLayout::FindRangeIntervals(size_t start, size_t end) {
1994*8af74909SZhong Yang std::vector<Interval> ret;
1995*8af74909SZhong Yang
1996*8af74909SZhong Yang if (!textLayout || (start == end)) {
1997*8af74909SZhong Yang return ret;
1998*8af74909SZhong Yang }
1999*8af74909SZhong Yang
2000*8af74909SZhong Yang // Convert byte positions to wchar_t positions
2001*8af74909SZhong Yang const size_t startPos = GetPositionInLayout(text, start);
2002*8af74909SZhong Yang const size_t endPos = GetPositionInLayout(text, end);
2003*8af74909SZhong Yang
2004*8af74909SZhong Yang // Find selection range length
2005*8af74909SZhong Yang const size_t rangeLength = (endPos > startPos) ? (endPos - startPos) : (startPos - endPos);
2006*8af74909SZhong Yang
2007*8af74909SZhong Yang // Determine actual number of hit-test ranges
2008*8af74909SZhong Yang UINT32 actualHitTestCount = 0;
2009*8af74909SZhong Yang
2010*8af74909SZhong Yang // First try with 2 elements and if more needed, allocate.
2011*8af74909SZhong Yang std::vector<DWRITE_HIT_TEST_METRICS> hitTestMetrics(2);
2012*8af74909SZhong Yang textLayout->HitTestTextRange(
2013*8af74909SZhong Yang static_cast<UINT32>(startPos),
2014*8af74909SZhong Yang static_cast<UINT32>(rangeLength),
2015*8af74909SZhong Yang 0, // x
2016*8af74909SZhong Yang 0, // y
2017*8af74909SZhong Yang hitTestMetrics.data(),
2018*8af74909SZhong Yang static_cast<UINT32>(hitTestMetrics.size()),
2019*8af74909SZhong Yang &actualHitTestCount
2020*8af74909SZhong Yang );
2021*8af74909SZhong Yang
2022*8af74909SZhong Yang if (actualHitTestCount == 0) {
2023*8af74909SZhong Yang return ret;
2024*8af74909SZhong Yang }
2025*8af74909SZhong Yang
2026*8af74909SZhong Yang if (hitTestMetrics.size() < actualHitTestCount) {
2027*8af74909SZhong Yang // Allocate enough room to return all hit-test metrics.
2028*8af74909SZhong Yang hitTestMetrics.resize(actualHitTestCount);
2029*8af74909SZhong Yang textLayout->HitTestTextRange(
2030*8af74909SZhong Yang static_cast<UINT32>(startPos),
2031*8af74909SZhong Yang static_cast<UINT32>(rangeLength),
2032*8af74909SZhong Yang 0, // x
2033*8af74909SZhong Yang 0, // y
2034*8af74909SZhong Yang hitTestMetrics.data(),
2035*8af74909SZhong Yang static_cast<UINT32>(hitTestMetrics.size()),
2036*8af74909SZhong Yang &actualHitTestCount
2037*8af74909SZhong Yang );
2038*8af74909SZhong Yang }
2039*8af74909SZhong Yang
2040*8af74909SZhong Yang // Get the selection ranges behind the text.
2041*8af74909SZhong Yang
2042*8af74909SZhong Yang for (size_t i = 0; i < actualHitTestCount; ++i) {
2043*8af74909SZhong Yang // Store selection rectangle
2044*8af74909SZhong Yang const DWRITE_HIT_TEST_METRICS &htm = hitTestMetrics[i];
2045*8af74909SZhong Yang Interval selectionInterval;
2046*8af74909SZhong Yang
2047*8af74909SZhong Yang selectionInterval.left = htm.left;
2048*8af74909SZhong Yang selectionInterval.right = htm.left + htm.width;
2049*8af74909SZhong Yang
2050*8af74909SZhong Yang ret.push_back(selectionInterval);
2051*8af74909SZhong Yang }
2052*8af74909SZhong Yang
2053*8af74909SZhong Yang return ret;
2054*8af74909SZhong Yang }
2055*8af74909SZhong Yang
Layout(const IScreenLine * screenLine)2056*8af74909SZhong Yang std::unique_ptr<IScreenLineLayout> SurfaceD2D::Layout(const IScreenLine *screenLine) {
2057*8af74909SZhong Yang return std::make_unique<ScreenLineLayout>(screenLine);
2058*8af74909SZhong Yang }
2059*8af74909SZhong Yang
DrawTextCommon(PRectangle rc,const Font & font_,XYPOSITION ybase,std::string_view text,UINT fuOptions)2060*8af74909SZhong Yang void SurfaceD2D::DrawTextCommon(PRectangle rc, const Font &font_, XYPOSITION ybase, std::string_view text, UINT fuOptions) {
2061*8af74909SZhong Yang SetFont(font_);
2062*8af74909SZhong Yang
2063*8af74909SZhong Yang // Use Unicode calls
2064*8af74909SZhong Yang const TextWide tbuf(text, unicodeMode, codePageText);
2065*8af74909SZhong Yang if (pRenderTarget && pTextFormat && pBrush) {
2066*8af74909SZhong Yang if (fuOptions & ETO_CLIPPED) {
2067*8af74909SZhong Yang const D2D1_RECT_F rcClip = RectangleFromPRectangle(rc);
2068*8af74909SZhong Yang pRenderTarget->PushAxisAlignedClip(rcClip, D2D1_ANTIALIAS_MODE_ALIASED);
2069*8af74909SZhong Yang }
2070*8af74909SZhong Yang
2071*8af74909SZhong Yang // Explicitly creating a text layout appears a little faster
2072*8af74909SZhong Yang IDWriteTextLayout *pTextLayout = nullptr;
2073*8af74909SZhong Yang const HRESULT hr = pIDWriteFactory->CreateTextLayout(tbuf.buffer, tbuf.tlen, pTextFormat,
2074*8af74909SZhong Yang rc.Width(), rc.Height(), &pTextLayout);
2075*8af74909SZhong Yang if (SUCCEEDED(hr)) {
2076*8af74909SZhong Yang D2D1_POINT_2F origin = {rc.left, ybase-yAscent};
2077*8af74909SZhong Yang pRenderTarget->DrawTextLayout(origin, pTextLayout, pBrush, d2dDrawTextOptions);
2078*8af74909SZhong Yang ReleaseUnknown(pTextLayout);
2079*8af74909SZhong Yang }
2080*8af74909SZhong Yang
2081*8af74909SZhong Yang if (fuOptions & ETO_CLIPPED) {
2082*8af74909SZhong Yang pRenderTarget->PopAxisAlignedClip();
2083*8af74909SZhong Yang }
2084*8af74909SZhong Yang }
2085*8af74909SZhong Yang }
2086*8af74909SZhong Yang
DrawTextNoClip(PRectangle rc,Font & font_,XYPOSITION ybase,std::string_view text,ColourDesired fore,ColourDesired back)2087*8af74909SZhong Yang void SurfaceD2D::DrawTextNoClip(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text,
2088*8af74909SZhong Yang ColourDesired fore, ColourDesired back) {
2089*8af74909SZhong Yang if (pRenderTarget) {
2090*8af74909SZhong Yang FillRectangle(rc, back);
2091*8af74909SZhong Yang D2DPenColour(fore);
2092*8af74909SZhong Yang DrawTextCommon(rc, font_, ybase, text, ETO_OPAQUE);
2093*8af74909SZhong Yang }
2094*8af74909SZhong Yang }
2095*8af74909SZhong Yang
DrawTextClipped(PRectangle rc,Font & font_,XYPOSITION ybase,std::string_view text,ColourDesired fore,ColourDesired back)2096*8af74909SZhong Yang void SurfaceD2D::DrawTextClipped(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text,
2097*8af74909SZhong Yang ColourDesired fore, ColourDesired back) {
2098*8af74909SZhong Yang if (pRenderTarget) {
2099*8af74909SZhong Yang FillRectangle(rc, back);
2100*8af74909SZhong Yang D2DPenColour(fore);
2101*8af74909SZhong Yang DrawTextCommon(rc, font_, ybase, text, ETO_OPAQUE | ETO_CLIPPED);
2102*8af74909SZhong Yang }
2103*8af74909SZhong Yang }
2104*8af74909SZhong Yang
DrawTextTransparent(PRectangle rc,Font & font_,XYPOSITION ybase,std::string_view text,ColourDesired fore)2105*8af74909SZhong Yang void SurfaceD2D::DrawTextTransparent(PRectangle rc, Font &font_, XYPOSITION ybase, std::string_view text,
2106*8af74909SZhong Yang ColourDesired fore) {
2107*8af74909SZhong Yang // Avoid drawing spaces in transparent mode
2108*8af74909SZhong Yang for (const char ch : text) {
2109*8af74909SZhong Yang if (ch != ' ') {
2110*8af74909SZhong Yang if (pRenderTarget) {
2111*8af74909SZhong Yang D2DPenColour(fore);
2112*8af74909SZhong Yang DrawTextCommon(rc, font_, ybase, text, 0);
2113*8af74909SZhong Yang }
2114*8af74909SZhong Yang return;
2115*8af74909SZhong Yang }
2116*8af74909SZhong Yang }
2117*8af74909SZhong Yang }
2118*8af74909SZhong Yang
WidthText(Font & font_,std::string_view text)2119*8af74909SZhong Yang XYPOSITION SurfaceD2D::WidthText(Font &font_, std::string_view text) {
2120*8af74909SZhong Yang FLOAT width = 1.0;
2121*8af74909SZhong Yang SetFont(font_);
2122*8af74909SZhong Yang const TextWide tbuf(text, unicodeMode, codePageText);
2123*8af74909SZhong Yang if (pIDWriteFactory && pTextFormat) {
2124*8af74909SZhong Yang // Create a layout
2125*8af74909SZhong Yang IDWriteTextLayout *pTextLayout = nullptr;
2126*8af74909SZhong Yang const HRESULT hr = pIDWriteFactory->CreateTextLayout(tbuf.buffer, tbuf.tlen, pTextFormat, 1000.0, 1000.0, &pTextLayout);
2127*8af74909SZhong Yang if (SUCCEEDED(hr) && pTextLayout) {
2128*8af74909SZhong Yang DWRITE_TEXT_METRICS textMetrics;
2129*8af74909SZhong Yang if (SUCCEEDED(pTextLayout->GetMetrics(&textMetrics)))
2130*8af74909SZhong Yang width = textMetrics.widthIncludingTrailingWhitespace;
2131*8af74909SZhong Yang ReleaseUnknown(pTextLayout);
2132*8af74909SZhong Yang }
2133*8af74909SZhong Yang }
2134*8af74909SZhong Yang return width;
2135*8af74909SZhong Yang }
2136*8af74909SZhong Yang
MeasureWidths(Font & font_,std::string_view text,XYPOSITION * positions)2137*8af74909SZhong Yang void SurfaceD2D::MeasureWidths(Font &font_, std::string_view text, XYPOSITION *positions) {
2138*8af74909SZhong Yang SetFont(font_);
2139*8af74909SZhong Yang if (!pIDWriteFactory || !pTextFormat) {
2140*8af74909SZhong Yang // SetFont failed or no access to DirectWrite so give up.
2141*8af74909SZhong Yang return;
2142*8af74909SZhong Yang }
2143*8af74909SZhong Yang const TextWide tbuf(text, unicodeMode, codePageText);
2144*8af74909SZhong Yang TextPositions poses(tbuf.tlen);
2145*8af74909SZhong Yang // Initialize poses for safety.
2146*8af74909SZhong Yang std::fill(poses.buffer, poses.buffer + tbuf.tlen, 0.0f);
2147*8af74909SZhong Yang // Create a layout
2148*8af74909SZhong Yang IDWriteTextLayout *pTextLayout = nullptr;
2149*8af74909SZhong Yang const HRESULT hrCreate = pIDWriteFactory->CreateTextLayout(tbuf.buffer, tbuf.tlen, pTextFormat, 10000.0, 1000.0, &pTextLayout);
2150*8af74909SZhong Yang if (!SUCCEEDED(hrCreate) || !pTextLayout) {
2151*8af74909SZhong Yang return;
2152*8af74909SZhong Yang }
2153*8af74909SZhong Yang constexpr int clusters = stackBufferLength;
2154*8af74909SZhong Yang DWRITE_CLUSTER_METRICS clusterMetrics[clusters];
2155*8af74909SZhong Yang UINT32 count = 0;
2156*8af74909SZhong Yang const HRESULT hrGetCluster = pTextLayout->GetClusterMetrics(clusterMetrics, clusters, &count);
2157*8af74909SZhong Yang ReleaseUnknown(pTextLayout);
2158*8af74909SZhong Yang if (!SUCCEEDED(hrGetCluster)) {
2159*8af74909SZhong Yang return;
2160*8af74909SZhong Yang }
2161*8af74909SZhong Yang // A cluster may be more than one WCHAR, such as for "ffi" which is a ligature in the Candara font
2162*8af74909SZhong Yang FLOAT position = 0.0f;
2163*8af74909SZhong Yang int ti=0;
2164*8af74909SZhong Yang for (unsigned int ci=0; ci<count; ci++) {
2165*8af74909SZhong Yang for (unsigned int inCluster=0; inCluster<clusterMetrics[ci].length; inCluster++) {
2166*8af74909SZhong Yang poses.buffer[ti++] = position + clusterMetrics[ci].width * (inCluster + 1) / clusterMetrics[ci].length;
2167*8af74909SZhong Yang }
2168*8af74909SZhong Yang position += clusterMetrics[ci].width;
2169*8af74909SZhong Yang }
2170*8af74909SZhong Yang PLATFORM_ASSERT(ti == tbuf.tlen);
2171*8af74909SZhong Yang if (unicodeMode) {
2172*8af74909SZhong Yang // Map the widths given for UTF-16 characters back onto the UTF-8 input string
2173*8af74909SZhong Yang int ui=0;
2174*8af74909SZhong Yang size_t i=0;
2175*8af74909SZhong Yang while (ui<tbuf.tlen) {
2176*8af74909SZhong Yang const unsigned char uch = text[i];
2177*8af74909SZhong Yang const unsigned int byteCount = UTF8BytesOfLead[uch];
2178*8af74909SZhong Yang if (byteCount == 4) { // Non-BMP
2179*8af74909SZhong Yang ui++;
2180*8af74909SZhong Yang }
2181*8af74909SZhong Yang for (unsigned int bytePos=0; (bytePos<byteCount) && (i<text.length()) && (ui<tbuf.tlen); bytePos++) {
2182*8af74909SZhong Yang positions[i++] = poses.buffer[ui];
2183*8af74909SZhong Yang }
2184*8af74909SZhong Yang ui++;
2185*8af74909SZhong Yang }
2186*8af74909SZhong Yang XYPOSITION lastPos = 0.0f;
2187*8af74909SZhong Yang if (i > 0)
2188*8af74909SZhong Yang lastPos = positions[i-1];
2189*8af74909SZhong Yang while (i<text.length()) {
2190*8af74909SZhong Yang positions[i++] = lastPos;
2191*8af74909SZhong Yang }
2192*8af74909SZhong Yang } else if (!IsDBCSCodePage(codePageText)) {
2193*8af74909SZhong Yang
2194*8af74909SZhong Yang // One char per position
2195*8af74909SZhong Yang PLATFORM_ASSERT(text.length() == static_cast<size_t>(tbuf.tlen));
2196*8af74909SZhong Yang for (int kk=0; kk<tbuf.tlen; kk++) {
2197*8af74909SZhong Yang positions[kk] = poses.buffer[kk];
2198*8af74909SZhong Yang }
2199*8af74909SZhong Yang
2200*8af74909SZhong Yang } else {
2201*8af74909SZhong Yang
2202*8af74909SZhong Yang // May be one or two bytes per position
2203*8af74909SZhong Yang int ui = 0;
2204*8af74909SZhong Yang for (size_t i=0; i<text.length() && ui<tbuf.tlen;) {
2205*8af74909SZhong Yang positions[i] = poses.buffer[ui];
2206*8af74909SZhong Yang if (DBCSIsLeadByte(codePageText, text[i])) {
2207*8af74909SZhong Yang positions[i+1] = poses.buffer[ui];
2208*8af74909SZhong Yang i += 2;
2209*8af74909SZhong Yang } else {
2210*8af74909SZhong Yang i++;
2211*8af74909SZhong Yang }
2212*8af74909SZhong Yang
2213*8af74909SZhong Yang ui++;
2214*8af74909SZhong Yang }
2215*8af74909SZhong Yang }
2216*8af74909SZhong Yang }
2217*8af74909SZhong Yang
Ascent(Font & font_)2218*8af74909SZhong Yang XYPOSITION SurfaceD2D::Ascent(Font &font_) {
2219*8af74909SZhong Yang SetFont(font_);
2220*8af74909SZhong Yang return std::ceil(yAscent);
2221*8af74909SZhong Yang }
2222*8af74909SZhong Yang
Descent(Font & font_)2223*8af74909SZhong Yang XYPOSITION SurfaceD2D::Descent(Font &font_) {
2224*8af74909SZhong Yang SetFont(font_);
2225*8af74909SZhong Yang return std::ceil(yDescent);
2226*8af74909SZhong Yang }
2227*8af74909SZhong Yang
InternalLeading(Font & font_)2228*8af74909SZhong Yang XYPOSITION SurfaceD2D::InternalLeading(Font &font_) {
2229*8af74909SZhong Yang SetFont(font_);
2230*8af74909SZhong Yang return std::floor(yInternalLeading);
2231*8af74909SZhong Yang }
2232*8af74909SZhong Yang
Height(Font & font_)2233*8af74909SZhong Yang XYPOSITION SurfaceD2D::Height(Font &font_) {
2234*8af74909SZhong Yang return Ascent(font_) + Descent(font_);
2235*8af74909SZhong Yang }
2236*8af74909SZhong Yang
AverageCharWidth(Font & font_)2237*8af74909SZhong Yang XYPOSITION SurfaceD2D::AverageCharWidth(Font &font_) {
2238*8af74909SZhong Yang FLOAT width = 1.0;
2239*8af74909SZhong Yang SetFont(font_);
2240*8af74909SZhong Yang if (pIDWriteFactory && pTextFormat) {
2241*8af74909SZhong Yang // Create a layout
2242*8af74909SZhong Yang IDWriteTextLayout *pTextLayout = nullptr;
2243*8af74909SZhong Yang const WCHAR wszAllAlpha[] = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
2244*8af74909SZhong Yang const size_t lenAllAlpha = wcslen(wszAllAlpha);
2245*8af74909SZhong Yang const HRESULT hr = pIDWriteFactory->CreateTextLayout(wszAllAlpha, static_cast<UINT32>(lenAllAlpha),
2246*8af74909SZhong Yang pTextFormat, 1000.0, 1000.0, &pTextLayout);
2247*8af74909SZhong Yang if (SUCCEEDED(hr) && pTextLayout) {
2248*8af74909SZhong Yang DWRITE_TEXT_METRICS textMetrics;
2249*8af74909SZhong Yang if (SUCCEEDED(pTextLayout->GetMetrics(&textMetrics)))
2250*8af74909SZhong Yang width = textMetrics.width / lenAllAlpha;
2251*8af74909SZhong Yang ReleaseUnknown(pTextLayout);
2252*8af74909SZhong Yang }
2253*8af74909SZhong Yang }
2254*8af74909SZhong Yang return width;
2255*8af74909SZhong Yang }
2256*8af74909SZhong Yang
SetClip(PRectangle rc)2257*8af74909SZhong Yang void SurfaceD2D::SetClip(PRectangle rc) {
2258*8af74909SZhong Yang if (pRenderTarget) {
2259*8af74909SZhong Yang const D2D1_RECT_F rcClip = RectangleFromPRectangle(rc);
2260*8af74909SZhong Yang pRenderTarget->PushAxisAlignedClip(rcClip, D2D1_ANTIALIAS_MODE_ALIASED);
2261*8af74909SZhong Yang clipsActive++;
2262*8af74909SZhong Yang }
2263*8af74909SZhong Yang }
2264*8af74909SZhong Yang
FlushCachedState()2265*8af74909SZhong Yang void SurfaceD2D::FlushCachedState() {
2266*8af74909SZhong Yang }
2267*8af74909SZhong Yang
SetUnicodeMode(bool unicodeMode_)2268*8af74909SZhong Yang void SurfaceD2D::SetUnicodeMode(bool unicodeMode_) {
2269*8af74909SZhong Yang unicodeMode=unicodeMode_;
2270*8af74909SZhong Yang }
2271*8af74909SZhong Yang
SetDBCSMode(int codePage_)2272*8af74909SZhong Yang void SurfaceD2D::SetDBCSMode(int codePage_) {
2273*8af74909SZhong Yang // No action on window as automatically handled by system.
2274*8af74909SZhong Yang codePage = codePage_;
2275*8af74909SZhong Yang }
2276*8af74909SZhong Yang
SetBidiR2L(bool)2277*8af74909SZhong Yang void SurfaceD2D::SetBidiR2L(bool) {
2278*8af74909SZhong Yang }
2279*8af74909SZhong Yang
2280*8af74909SZhong Yang #endif
2281*8af74909SZhong Yang
Allocate(int technology)2282*8af74909SZhong Yang Surface *Surface::Allocate(int technology) {
2283*8af74909SZhong Yang #if defined(USE_D2D)
2284*8af74909SZhong Yang if (technology == SCWIN_TECH_GDI)
2285*8af74909SZhong Yang return new SurfaceGDI;
2286*8af74909SZhong Yang else
2287*8af74909SZhong Yang return new SurfaceD2D;
2288*8af74909SZhong Yang #else
2289*8af74909SZhong Yang return new SurfaceGDI;
2290*8af74909SZhong Yang #endif
2291*8af74909SZhong Yang }
2292*8af74909SZhong Yang
~Window()2293*8af74909SZhong Yang Window::~Window() {
2294*8af74909SZhong Yang }
2295*8af74909SZhong Yang
Destroy()2296*8af74909SZhong Yang void Window::Destroy() {
2297*8af74909SZhong Yang if (wid)
2298*8af74909SZhong Yang ::DestroyWindow(HwndFromWindowID(wid));
2299*8af74909SZhong Yang wid = nullptr;
2300*8af74909SZhong Yang }
2301*8af74909SZhong Yang
GetPosition() const2302*8af74909SZhong Yang PRectangle Window::GetPosition() const {
2303*8af74909SZhong Yang RECT rc;
2304*8af74909SZhong Yang ::GetWindowRect(HwndFromWindowID(wid), &rc);
2305*8af74909SZhong Yang return PRectangle::FromInts(rc.left, rc.top, rc.right, rc.bottom);
2306*8af74909SZhong Yang }
2307*8af74909SZhong Yang
SetPosition(PRectangle rc)2308*8af74909SZhong Yang void Window::SetPosition(PRectangle rc) {
2309*8af74909SZhong Yang ::SetWindowPos(HwndFromWindowID(wid),
2310*8af74909SZhong Yang 0, static_cast<int>(rc.left), static_cast<int>(rc.top),
2311*8af74909SZhong Yang static_cast<int>(rc.Width()), static_cast<int>(rc.Height()), SWP_NOZORDER | SWP_NOACTIVATE);
2312*8af74909SZhong Yang }
2313*8af74909SZhong Yang
2314*8af74909SZhong Yang namespace {
2315*8af74909SZhong Yang
RectFromMonitor(HMONITOR hMonitor)2316*8af74909SZhong Yang static RECT RectFromMonitor(HMONITOR hMonitor) noexcept {
2317*8af74909SZhong Yang MONITORINFO mi = {};
2318*8af74909SZhong Yang mi.cbSize = sizeof(mi);
2319*8af74909SZhong Yang if (GetMonitorInfo(hMonitor, &mi)) {
2320*8af74909SZhong Yang return mi.rcWork;
2321*8af74909SZhong Yang }
2322*8af74909SZhong Yang RECT rc = {0, 0, 0, 0};
2323*8af74909SZhong Yang if (::SystemParametersInfoA(SPI_GETWORKAREA, 0, &rc, 0) == 0) {
2324*8af74909SZhong Yang rc.left = 0;
2325*8af74909SZhong Yang rc.top = 0;
2326*8af74909SZhong Yang rc.right = 0;
2327*8af74909SZhong Yang rc.bottom = 0;
2328*8af74909SZhong Yang }
2329*8af74909SZhong Yang return rc;
2330*8af74909SZhong Yang }
2331*8af74909SZhong Yang
2332*8af74909SZhong Yang }
2333*8af74909SZhong Yang
SetPositionRelative(PRectangle rc,const Window * relativeTo)2334*8af74909SZhong Yang void Window::SetPositionRelative(PRectangle rc, const Window *relativeTo) {
2335*8af74909SZhong Yang const DWORD style = GetWindowStyle(HwndFromWindowID(wid));
2336*8af74909SZhong Yang if (style & WS_POPUP) {
2337*8af74909SZhong Yang POINT ptOther = {0, 0};
2338*8af74909SZhong Yang ::ClientToScreen(HwndFromWindow(*relativeTo), &ptOther);
2339*8af74909SZhong Yang rc.Move(static_cast<XYPOSITION>(ptOther.x), static_cast<XYPOSITION>(ptOther.y));
2340*8af74909SZhong Yang
2341*8af74909SZhong Yang const RECT rcMonitor = RectFromPRectangle(rc);
2342*8af74909SZhong Yang
2343*8af74909SZhong Yang HMONITOR hMonitor = MonitorFromRect(&rcMonitor, MONITOR_DEFAULTTONEAREST);
2344*8af74909SZhong Yang // If hMonitor is NULL, that's just the main screen anyways.
2345*8af74909SZhong Yang const RECT rcWork = RectFromMonitor(hMonitor);
2346*8af74909SZhong Yang
2347*8af74909SZhong Yang if (rcWork.left < rcWork.right) {
2348*8af74909SZhong Yang // Now clamp our desired rectangle to fit inside the work area
2349*8af74909SZhong Yang // This way, the menu will fit wholly on one screen. An improvement even
2350*8af74909SZhong Yang // if you don't have a second monitor on the left... Menu's appears half on
2351*8af74909SZhong Yang // one screen and half on the other are just U.G.L.Y.!
2352*8af74909SZhong Yang if (rc.right > rcWork.right)
2353*8af74909SZhong Yang rc.Move(rcWork.right - rc.right, 0);
2354*8af74909SZhong Yang if (rc.bottom > rcWork.bottom)
2355*8af74909SZhong Yang rc.Move(0, rcWork.bottom - rc.bottom);
2356*8af74909SZhong Yang if (rc.left < rcWork.left)
2357*8af74909SZhong Yang rc.Move(rcWork.left - rc.left, 0);
2358*8af74909SZhong Yang if (rc.top < rcWork.top)
2359*8af74909SZhong Yang rc.Move(0, rcWork.top - rc.top);
2360*8af74909SZhong Yang }
2361*8af74909SZhong Yang }
2362*8af74909SZhong Yang SetPosition(rc);
2363*8af74909SZhong Yang }
2364*8af74909SZhong Yang
GetClientPosition() const2365*8af74909SZhong Yang PRectangle Window::GetClientPosition() const {
2366*8af74909SZhong Yang RECT rc={0,0,0,0};
2367*8af74909SZhong Yang if (wid)
2368*8af74909SZhong Yang ::GetClientRect(HwndFromWindowID(wid), &rc);
2369*8af74909SZhong Yang return PRectangle::FromInts(rc.left, rc.top, rc.right, rc.bottom);
2370*8af74909SZhong Yang }
2371*8af74909SZhong Yang
Show(bool show)2372*8af74909SZhong Yang void Window::Show(bool show) {
2373*8af74909SZhong Yang if (show)
2374*8af74909SZhong Yang ::ShowWindow(HwndFromWindowID(wid), SW_SHOWNOACTIVATE);
2375*8af74909SZhong Yang else
2376*8af74909SZhong Yang ::ShowWindow(HwndFromWindowID(wid), SW_HIDE);
2377*8af74909SZhong Yang }
2378*8af74909SZhong Yang
InvalidateAll()2379*8af74909SZhong Yang void Window::InvalidateAll() {
2380*8af74909SZhong Yang ::InvalidateRect(HwndFromWindowID(wid), nullptr, FALSE);
2381*8af74909SZhong Yang }
2382*8af74909SZhong Yang
InvalidateRectangle(PRectangle rc)2383*8af74909SZhong Yang void Window::InvalidateRectangle(PRectangle rc) {
2384*8af74909SZhong Yang const RECT rcw = RectFromPRectangle(rc);
2385*8af74909SZhong Yang ::InvalidateRect(HwndFromWindowID(wid), &rcw, FALSE);
2386*8af74909SZhong Yang }
2387*8af74909SZhong Yang
SetFont(Font & font)2388*8af74909SZhong Yang void Window::SetFont(Font &font) {
2389*8af74909SZhong Yang SetWindowFont(HwndFromWindowID(wid), font.GetID(), 0);
2390*8af74909SZhong Yang }
2391*8af74909SZhong Yang
2392*8af74909SZhong Yang namespace {
2393*8af74909SZhong Yang
FlipBitmap(HBITMAP bitmap,int width,int height)2394*8af74909SZhong Yang void FlipBitmap(HBITMAP bitmap, int width, int height) noexcept {
2395*8af74909SZhong Yang HDC hdc = ::CreateCompatibleDC({});
2396*8af74909SZhong Yang if (hdc) {
2397*8af74909SZhong Yang HBITMAP prevBmp = SelectBitmap(hdc, bitmap);
2398*8af74909SZhong Yang ::StretchBlt(hdc, width - 1, 0, -width, height, hdc, 0, 0, width, height, SRCCOPY);
2399*8af74909SZhong Yang SelectBitmap(hdc, prevBmp);
2400*8af74909SZhong Yang ::DeleteDC(hdc);
2401*8af74909SZhong Yang }
2402*8af74909SZhong Yang }
2403*8af74909SZhong Yang
2404*8af74909SZhong Yang }
2405*8af74909SZhong Yang
LoadReverseArrowCursor(UINT dpi)2406*8af74909SZhong Yang HCURSOR LoadReverseArrowCursor(UINT dpi) noexcept {
2407*8af74909SZhong Yang HCURSOR reverseArrowCursor {};
2408*8af74909SZhong Yang
2409*8af74909SZhong Yang bool created = false;
2410*8af74909SZhong Yang HCURSOR cursor = ::LoadCursor({}, IDC_ARROW);
2411*8af74909SZhong Yang
2412*8af74909SZhong Yang if (dpi != uSystemDPI) {
2413*8af74909SZhong Yang const int width = SystemMetricsForDpi(SM_CXCURSOR, dpi);
2414*8af74909SZhong Yang const int height = SystemMetricsForDpi(SM_CYCURSOR, dpi);
2415*8af74909SZhong Yang HCURSOR copy = static_cast<HCURSOR>(::CopyImage(cursor, IMAGE_CURSOR, width, height, LR_COPYFROMRESOURCE | LR_COPYRETURNORG));
2416*8af74909SZhong Yang if (copy) {
2417*8af74909SZhong Yang created = copy != cursor;
2418*8af74909SZhong Yang cursor = copy;
2419*8af74909SZhong Yang }
2420*8af74909SZhong Yang }
2421*8af74909SZhong Yang
2422*8af74909SZhong Yang ICONINFO info;
2423*8af74909SZhong Yang if (::GetIconInfo(cursor, &info)) {
2424*8af74909SZhong Yang BITMAP bmp;
2425*8af74909SZhong Yang if (::GetObject(info.hbmMask, sizeof(bmp), &bmp)) {
2426*8af74909SZhong Yang FlipBitmap(info.hbmMask, bmp.bmWidth, bmp.bmHeight);
2427*8af74909SZhong Yang if (info.hbmColor)
2428*8af74909SZhong Yang FlipBitmap(info.hbmColor, bmp.bmWidth, bmp.bmHeight);
2429*8af74909SZhong Yang info.xHotspot = bmp.bmWidth - 1 - info.xHotspot;
2430*8af74909SZhong Yang
2431*8af74909SZhong Yang reverseArrowCursor = ::CreateIconIndirect(&info);
2432*8af74909SZhong Yang }
2433*8af74909SZhong Yang
2434*8af74909SZhong Yang ::DeleteObject(info.hbmMask);
2435*8af74909SZhong Yang if (info.hbmColor)
2436*8af74909SZhong Yang ::DeleteObject(info.hbmColor);
2437*8af74909SZhong Yang }
2438*8af74909SZhong Yang
2439*8af74909SZhong Yang if (created) {
2440*8af74909SZhong Yang ::DestroyCursor(cursor);
2441*8af74909SZhong Yang }
2442*8af74909SZhong Yang return reverseArrowCursor;
2443*8af74909SZhong Yang }
2444*8af74909SZhong Yang
SetCursor(Cursor curs)2445*8af74909SZhong Yang void Window::SetCursor(Cursor curs) {
2446*8af74909SZhong Yang switch (curs) {
2447*8af74909SZhong Yang case cursorText:
2448*8af74909SZhong Yang ::SetCursor(::LoadCursor(NULL,IDC_IBEAM));
2449*8af74909SZhong Yang break;
2450*8af74909SZhong Yang case cursorUp:
2451*8af74909SZhong Yang ::SetCursor(::LoadCursor(NULL,IDC_UPARROW));
2452*8af74909SZhong Yang break;
2453*8af74909SZhong Yang case cursorWait:
2454*8af74909SZhong Yang ::SetCursor(::LoadCursor(NULL,IDC_WAIT));
2455*8af74909SZhong Yang break;
2456*8af74909SZhong Yang case cursorHoriz:
2457*8af74909SZhong Yang ::SetCursor(::LoadCursor(NULL,IDC_SIZEWE));
2458*8af74909SZhong Yang break;
2459*8af74909SZhong Yang case cursorVert:
2460*8af74909SZhong Yang ::SetCursor(::LoadCursor(NULL,IDC_SIZENS));
2461*8af74909SZhong Yang break;
2462*8af74909SZhong Yang case cursorHand:
2463*8af74909SZhong Yang ::SetCursor(::LoadCursor(NULL,IDC_HAND));
2464*8af74909SZhong Yang break;
2465*8af74909SZhong Yang case cursorReverseArrow:
2466*8af74909SZhong Yang case cursorArrow:
2467*8af74909SZhong Yang case cursorInvalid: // Should not occur, but just in case.
2468*8af74909SZhong Yang ::SetCursor(::LoadCursor(NULL,IDC_ARROW));
2469*8af74909SZhong Yang break;
2470*8af74909SZhong Yang }
2471*8af74909SZhong Yang }
2472*8af74909SZhong Yang
2473*8af74909SZhong Yang /* Returns rectangle of monitor pt is on, both rect and pt are in Window's
2474*8af74909SZhong Yang coordinates */
GetMonitorRect(Point pt)2475*8af74909SZhong Yang PRectangle Window::GetMonitorRect(Point pt) {
2476*8af74909SZhong Yang const PRectangle rcPosition = GetPosition();
2477*8af74909SZhong Yang POINT ptDesktop = {static_cast<LONG>(pt.x + rcPosition.left),
2478*8af74909SZhong Yang static_cast<LONG>(pt.y + rcPosition.top)};
2479*8af74909SZhong Yang HMONITOR hMonitor = MonitorFromPoint(ptDesktop, MONITOR_DEFAULTTONEAREST);
2480*8af74909SZhong Yang
2481*8af74909SZhong Yang const RECT rcWork = RectFromMonitor(hMonitor);
2482*8af74909SZhong Yang if (rcWork.left < rcWork.right) {
2483*8af74909SZhong Yang PRectangle rcMonitor(
2484*8af74909SZhong Yang rcWork.left - rcPosition.left,
2485*8af74909SZhong Yang rcWork.top - rcPosition.top,
2486*8af74909SZhong Yang rcWork.right - rcPosition.left,
2487*8af74909SZhong Yang rcWork.bottom - rcPosition.top);
2488*8af74909SZhong Yang return rcMonitor;
2489*8af74909SZhong Yang } else {
2490*8af74909SZhong Yang return PRectangle();
2491*8af74909SZhong Yang }
2492*8af74909SZhong Yang }
2493*8af74909SZhong Yang
2494*8af74909SZhong Yang struct ListItemData {
2495*8af74909SZhong Yang const char *text;
2496*8af74909SZhong Yang int pixId;
2497*8af74909SZhong Yang };
2498*8af74909SZhong Yang
2499*8af74909SZhong Yang class LineToItem {
2500*8af74909SZhong Yang std::vector<char> words;
2501*8af74909SZhong Yang
2502*8af74909SZhong Yang std::vector<ListItemData> data;
2503*8af74909SZhong Yang
2504*8af74909SZhong Yang public:
Clear()2505*8af74909SZhong Yang void Clear() noexcept {
2506*8af74909SZhong Yang words.clear();
2507*8af74909SZhong Yang data.clear();
2508*8af74909SZhong Yang }
2509*8af74909SZhong Yang
Get(size_t index) const2510*8af74909SZhong Yang ListItemData Get(size_t index) const noexcept {
2511*8af74909SZhong Yang if (index < data.size()) {
2512*8af74909SZhong Yang return data[index];
2513*8af74909SZhong Yang } else {
2514*8af74909SZhong Yang ListItemData missing = {"", -1};
2515*8af74909SZhong Yang return missing;
2516*8af74909SZhong Yang }
2517*8af74909SZhong Yang }
Count() const2518*8af74909SZhong Yang int Count() const noexcept {
2519*8af74909SZhong Yang return static_cast<int>(data.size());
2520*8af74909SZhong Yang }
2521*8af74909SZhong Yang
AllocItem(const char * text,int pixId)2522*8af74909SZhong Yang void AllocItem(const char *text, int pixId) {
2523*8af74909SZhong Yang ListItemData lid = { text, pixId };
2524*8af74909SZhong Yang data.push_back(lid);
2525*8af74909SZhong Yang }
2526*8af74909SZhong Yang
SetWords(const char * s)2527*8af74909SZhong Yang char *SetWords(const char *s) {
2528*8af74909SZhong Yang words = std::vector<char>(s, s+strlen(s)+1);
2529*8af74909SZhong Yang return &words[0];
2530*8af74909SZhong Yang }
2531*8af74909SZhong Yang };
2532*8af74909SZhong Yang
2533*8af74909SZhong Yang const TCHAR ListBoxX_ClassName[] = TEXT("ListBoxX");
2534*8af74909SZhong Yang
ListBox()2535*8af74909SZhong Yang ListBox::ListBox() noexcept {
2536*8af74909SZhong Yang }
2537*8af74909SZhong Yang
~ListBox()2538*8af74909SZhong Yang ListBox::~ListBox() {
2539*8af74909SZhong Yang }
2540*8af74909SZhong Yang
2541*8af74909SZhong Yang class ListBoxX : public ListBox {
2542*8af74909SZhong Yang int lineHeight;
2543*8af74909SZhong Yang FontID fontCopy;
2544*8af74909SZhong Yang int technology;
2545*8af74909SZhong Yang RGBAImageSet images;
2546*8af74909SZhong Yang LineToItem lti;
2547*8af74909SZhong Yang HWND lb;
2548*8af74909SZhong Yang bool unicodeMode;
2549*8af74909SZhong Yang int desiredVisibleRows;
2550*8af74909SZhong Yang unsigned int maxItemCharacters;
2551*8af74909SZhong Yang unsigned int aveCharWidth;
2552*8af74909SZhong Yang Window *parent;
2553*8af74909SZhong Yang int ctrlID;
2554*8af74909SZhong Yang UINT dpi;
2555*8af74909SZhong Yang IListBoxDelegate *delegate;
2556*8af74909SZhong Yang const char *widestItem;
2557*8af74909SZhong Yang unsigned int maxCharWidth;
2558*8af74909SZhong Yang WPARAM resizeHit;
2559*8af74909SZhong Yang PRectangle rcPreSize;
2560*8af74909SZhong Yang Point dragOffset;
2561*8af74909SZhong Yang Point location; // Caret location at which the list is opened
2562*8af74909SZhong Yang int wheelDelta; // mouse wheel residue
2563*8af74909SZhong Yang
2564*8af74909SZhong Yang HWND GetHWND() const noexcept;
2565*8af74909SZhong Yang void AppendListItem(const char *text, const char *numword);
2566*8af74909SZhong Yang static void AdjustWindowRect(PRectangle *rc, UINT dpi) noexcept;
2567*8af74909SZhong Yang int ItemHeight() const;
2568*8af74909SZhong Yang int MinClientWidth() const noexcept;
2569*8af74909SZhong Yang int TextOffset() const;
2570*8af74909SZhong Yang POINT GetClientExtent() const noexcept;
2571*8af74909SZhong Yang POINT MinTrackSize() const;
2572*8af74909SZhong Yang POINT MaxTrackSize() const;
2573*8af74909SZhong Yang void SetRedraw(bool on) noexcept;
2574*8af74909SZhong Yang void OnDoubleClick();
2575*8af74909SZhong Yang void OnSelChange();
2576*8af74909SZhong Yang void ResizeToCursor();
2577*8af74909SZhong Yang void StartResize(WPARAM);
2578*8af74909SZhong Yang LRESULT NcHitTest(WPARAM, LPARAM) const;
2579*8af74909SZhong Yang void CentreItem(int n);
2580*8af74909SZhong Yang void Paint(HDC) noexcept;
2581*8af74909SZhong Yang static LRESULT PASCAL ControlWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
2582*8af74909SZhong Yang
2583*8af74909SZhong Yang static constexpr Point ItemInset {0, 0}; // Padding around whole item
2584*8af74909SZhong Yang static constexpr Point TextInset {2, 0}; // Padding around text
2585*8af74909SZhong Yang static constexpr Point ImageInset {1, 0}; // Padding around image
2586*8af74909SZhong Yang
2587*8af74909SZhong Yang public:
ListBoxX()2588*8af74909SZhong Yang ListBoxX() : lineHeight(10), fontCopy{}, technology(0), lb{}, unicodeMode(false),
2589*8af74909SZhong Yang desiredVisibleRows(9), maxItemCharacters(0), aveCharWidth(8),
2590*8af74909SZhong Yang parent(nullptr), ctrlID(0), dpi(USER_DEFAULT_SCREEN_DPI),
2591*8af74909SZhong Yang delegate(nullptr),
2592*8af74909SZhong Yang widestItem(nullptr), maxCharWidth(1), resizeHit(0), wheelDelta(0) {
2593*8af74909SZhong Yang }
~ListBoxX()2594*8af74909SZhong Yang ~ListBoxX() override {
2595*8af74909SZhong Yang if (fontCopy) {
2596*8af74909SZhong Yang ::DeleteObject(fontCopy);
2597*8af74909SZhong Yang fontCopy = 0;
2598*8af74909SZhong Yang }
2599*8af74909SZhong Yang }
2600*8af74909SZhong Yang void SetFont(Font &font) override;
2601*8af74909SZhong Yang void Create(Window &parent_, int ctrlID_, Point location_, int lineHeight_, bool unicodeMode_, int technology_) override;
2602*8af74909SZhong Yang void SetAverageCharWidth(int width) override;
2603*8af74909SZhong Yang void SetVisibleRows(int rows) override;
2604*8af74909SZhong Yang int GetVisibleRows() const override;
2605*8af74909SZhong Yang PRectangle GetDesiredRect() override;
2606*8af74909SZhong Yang int CaretFromEdge() override;
2607*8af74909SZhong Yang void Clear() override;
2608*8af74909SZhong Yang void Append(char *s, int type = -1) override;
2609*8af74909SZhong Yang int Length() override;
2610*8af74909SZhong Yang void Select(int n) override;
2611*8af74909SZhong Yang int GetSelection() override;
2612*8af74909SZhong Yang int Find(const char *prefix) override;
2613*8af74909SZhong Yang void GetValue(int n, char *value, int len) override;
2614*8af74909SZhong Yang void RegisterImage(int type, const char *xpm_data) override;
2615*8af74909SZhong Yang void RegisterRGBAImage(int type, int width, int height, const unsigned char *pixelsImage) override;
2616*8af74909SZhong Yang void ClearRegisteredImages() override;
2617*8af74909SZhong Yang void SetDelegate(IListBoxDelegate *lbDelegate) override;
2618*8af74909SZhong Yang void SetList(const char *list, char separator, char typesep) override;
2619*8af74909SZhong Yang void Draw(DRAWITEMSTRUCT *pDrawItem);
2620*8af74909SZhong Yang LRESULT WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
2621*8af74909SZhong Yang static LRESULT PASCAL StaticWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam);
2622*8af74909SZhong Yang };
2623*8af74909SZhong Yang
Allocate()2624*8af74909SZhong Yang ListBox *ListBox::Allocate() {
2625*8af74909SZhong Yang ListBoxX *lb = new ListBoxX();
2626*8af74909SZhong Yang return lb;
2627*8af74909SZhong Yang }
2628*8af74909SZhong Yang
Create(Window & parent_,int ctrlID_,Point location_,int lineHeight_,bool unicodeMode_,int technology_)2629*8af74909SZhong Yang void ListBoxX::Create(Window &parent_, int ctrlID_, Point location_, int lineHeight_, bool unicodeMode_, int technology_) {
2630*8af74909SZhong Yang parent = &parent_;
2631*8af74909SZhong Yang ctrlID = ctrlID_;
2632*8af74909SZhong Yang location = location_;
2633*8af74909SZhong Yang lineHeight = lineHeight_;
2634*8af74909SZhong Yang unicodeMode = unicodeMode_;
2635*8af74909SZhong Yang technology = technology_;
2636*8af74909SZhong Yang HWND hwndParent = HwndFromWindow(*parent);
2637*8af74909SZhong Yang HINSTANCE hinstanceParent = GetWindowInstance(hwndParent);
2638*8af74909SZhong Yang // Window created as popup so not clipped within parent client area
2639*8af74909SZhong Yang wid = ::CreateWindowEx(
2640*8af74909SZhong Yang WS_EX_WINDOWEDGE, ListBoxX_ClassName, TEXT(""),
2641*8af74909SZhong Yang WS_POPUP | WS_THICKFRAME,
2642*8af74909SZhong Yang 100,100, 150,80, hwndParent,
2643*8af74909SZhong Yang NULL,
2644*8af74909SZhong Yang hinstanceParent,
2645*8af74909SZhong Yang this);
2646*8af74909SZhong Yang
2647*8af74909SZhong Yang dpi = DpiForWindow(hwndParent);
2648*8af74909SZhong Yang POINT locationw = POINTFromPoint(location);
2649*8af74909SZhong Yang ::MapWindowPoints(hwndParent, NULL, &locationw, 1);
2650*8af74909SZhong Yang location = PointFromPOINT(locationw);
2651*8af74909SZhong Yang }
2652*8af74909SZhong Yang
SetFont(Font & font)2653*8af74909SZhong Yang void ListBoxX::SetFont(Font &font) {
2654*8af74909SZhong Yang if (font.GetID()) {
2655*8af74909SZhong Yang if (fontCopy) {
2656*8af74909SZhong Yang ::DeleteObject(fontCopy);
2657*8af74909SZhong Yang fontCopy = 0;
2658*8af74909SZhong Yang }
2659*8af74909SZhong Yang FormatAndMetrics *pfm = static_cast<FormatAndMetrics *>(font.GetID());
2660*8af74909SZhong Yang fontCopy = pfm->HFont();
2661*8af74909SZhong Yang SetWindowFont(lb, fontCopy, 0);
2662*8af74909SZhong Yang }
2663*8af74909SZhong Yang }
2664*8af74909SZhong Yang
SetAverageCharWidth(int width)2665*8af74909SZhong Yang void ListBoxX::SetAverageCharWidth(int width) {
2666*8af74909SZhong Yang aveCharWidth = width;
2667*8af74909SZhong Yang }
2668*8af74909SZhong Yang
SetVisibleRows(int rows)2669*8af74909SZhong Yang void ListBoxX::SetVisibleRows(int rows) {
2670*8af74909SZhong Yang desiredVisibleRows = rows;
2671*8af74909SZhong Yang }
2672*8af74909SZhong Yang
GetVisibleRows() const2673*8af74909SZhong Yang int ListBoxX::GetVisibleRows() const {
2674*8af74909SZhong Yang return desiredVisibleRows;
2675*8af74909SZhong Yang }
2676*8af74909SZhong Yang
GetHWND() const2677*8af74909SZhong Yang HWND ListBoxX::GetHWND() const noexcept {
2678*8af74909SZhong Yang return HwndFromWindowID(GetID());
2679*8af74909SZhong Yang }
2680*8af74909SZhong Yang
GetDesiredRect()2681*8af74909SZhong Yang PRectangle ListBoxX::GetDesiredRect() {
2682*8af74909SZhong Yang PRectangle rcDesired = GetPosition();
2683*8af74909SZhong Yang
2684*8af74909SZhong Yang int rows = Length();
2685*8af74909SZhong Yang if ((rows == 0) || (rows > desiredVisibleRows))
2686*8af74909SZhong Yang rows = desiredVisibleRows;
2687*8af74909SZhong Yang rcDesired.bottom = rcDesired.top + ItemHeight() * rows;
2688*8af74909SZhong Yang
2689*8af74909SZhong Yang int width = MinClientWidth();
2690*8af74909SZhong Yang HDC hdc = ::GetDC(lb);
2691*8af74909SZhong Yang HFONT oldFont = SelectFont(hdc, fontCopy);
2692*8af74909SZhong Yang SIZE textSize = {0, 0};
2693*8af74909SZhong Yang int len = 0;
2694*8af74909SZhong Yang if (widestItem) {
2695*8af74909SZhong Yang len = static_cast<int>(strlen(widestItem));
2696*8af74909SZhong Yang if (unicodeMode) {
2697*8af74909SZhong Yang const TextWide tbuf(widestItem, unicodeMode);
2698*8af74909SZhong Yang ::GetTextExtentPoint32W(hdc, tbuf.buffer, tbuf.tlen, &textSize);
2699*8af74909SZhong Yang } else {
2700*8af74909SZhong Yang ::GetTextExtentPoint32A(hdc, widestItem, len, &textSize);
2701*8af74909SZhong Yang }
2702*8af74909SZhong Yang }
2703*8af74909SZhong Yang TEXTMETRIC tm;
2704*8af74909SZhong Yang ::GetTextMetrics(hdc, &tm);
2705*8af74909SZhong Yang maxCharWidth = tm.tmMaxCharWidth;
2706*8af74909SZhong Yang SelectFont(hdc, oldFont);
2707*8af74909SZhong Yang ::ReleaseDC(lb, hdc);
2708*8af74909SZhong Yang
2709*8af74909SZhong Yang const int widthDesired = std::max(textSize.cx, (len + 1) * tm.tmAveCharWidth);
2710*8af74909SZhong Yang if (width < widthDesired)
2711*8af74909SZhong Yang width = widthDesired;
2712*8af74909SZhong Yang
2713*8af74909SZhong Yang rcDesired.right = rcDesired.left + TextOffset() + width + (TextInset.x * 2);
2714*8af74909SZhong Yang if (Length() > rows)
2715*8af74909SZhong Yang rcDesired.right += SystemMetricsForDpi(SM_CXVSCROLL, dpi);
2716*8af74909SZhong Yang
2717*8af74909SZhong Yang AdjustWindowRect(&rcDesired, dpi);
2718*8af74909SZhong Yang return rcDesired;
2719*8af74909SZhong Yang }
2720*8af74909SZhong Yang
TextOffset() const2721*8af74909SZhong Yang int ListBoxX::TextOffset() const {
2722*8af74909SZhong Yang const int pixWidth = images.GetWidth();
2723*8af74909SZhong Yang return static_cast<int>(pixWidth == 0 ? ItemInset.x : ItemInset.x + pixWidth + (ImageInset.x * 2));
2724*8af74909SZhong Yang }
2725*8af74909SZhong Yang
CaretFromEdge()2726*8af74909SZhong Yang int ListBoxX::CaretFromEdge() {
2727*8af74909SZhong Yang PRectangle rc;
2728*8af74909SZhong Yang AdjustWindowRect(&rc, dpi);
2729*8af74909SZhong Yang return TextOffset() + static_cast<int>(TextInset.x + (0 - rc.left) - 1);
2730*8af74909SZhong Yang }
2731*8af74909SZhong Yang
Clear()2732*8af74909SZhong Yang void ListBoxX::Clear() {
2733*8af74909SZhong Yang ListBox_ResetContent(lb);
2734*8af74909SZhong Yang maxItemCharacters = 0;
2735*8af74909SZhong Yang widestItem = nullptr;
2736*8af74909SZhong Yang lti.Clear();
2737*8af74909SZhong Yang }
2738*8af74909SZhong Yang
Append(char *,int)2739*8af74909SZhong Yang void ListBoxX::Append(char *, int) {
2740*8af74909SZhong Yang // This method is no longer called in Scintilla
2741*8af74909SZhong Yang PLATFORM_ASSERT(false);
2742*8af74909SZhong Yang }
2743*8af74909SZhong Yang
Length()2744*8af74909SZhong Yang int ListBoxX::Length() {
2745*8af74909SZhong Yang return lti.Count();
2746*8af74909SZhong Yang }
2747*8af74909SZhong Yang
Select(int n)2748*8af74909SZhong Yang void ListBoxX::Select(int n) {
2749*8af74909SZhong Yang // We are going to scroll to centre on the new selection and then select it, so disable
2750*8af74909SZhong Yang // redraw to avoid flicker caused by a painting new selection twice in unselected and then
2751*8af74909SZhong Yang // selected states
2752*8af74909SZhong Yang SetRedraw(false);
2753*8af74909SZhong Yang CentreItem(n);
2754*8af74909SZhong Yang ListBox_SetCurSel(lb, n);
2755*8af74909SZhong Yang OnSelChange();
2756*8af74909SZhong Yang SetRedraw(true);
2757*8af74909SZhong Yang }
2758*8af74909SZhong Yang
GetSelection()2759*8af74909SZhong Yang int ListBoxX::GetSelection() {
2760*8af74909SZhong Yang return ListBox_GetCurSel(lb);
2761*8af74909SZhong Yang }
2762*8af74909SZhong Yang
2763*8af74909SZhong Yang // This is not actually called at present
Find(const char *)2764*8af74909SZhong Yang int ListBoxX::Find(const char *) {
2765*8af74909SZhong Yang return LB_ERR;
2766*8af74909SZhong Yang }
2767*8af74909SZhong Yang
GetValue(int n,char * value,int len)2768*8af74909SZhong Yang void ListBoxX::GetValue(int n, char *value, int len) {
2769*8af74909SZhong Yang const ListItemData item = lti.Get(n);
2770*8af74909SZhong Yang strncpy(value, item.text, len);
2771*8af74909SZhong Yang value[len-1] = '\0';
2772*8af74909SZhong Yang }
2773*8af74909SZhong Yang
RegisterImage(int type,const char * xpm_data)2774*8af74909SZhong Yang void ListBoxX::RegisterImage(int type, const char *xpm_data) {
2775*8af74909SZhong Yang XPM xpmImage(xpm_data);
2776*8af74909SZhong Yang images.Add(type, new RGBAImage(xpmImage));
2777*8af74909SZhong Yang }
2778*8af74909SZhong Yang
RegisterRGBAImage(int type,int width,int height,const unsigned char * pixelsImage)2779*8af74909SZhong Yang void ListBoxX::RegisterRGBAImage(int type, int width, int height, const unsigned char *pixelsImage) {
2780*8af74909SZhong Yang images.Add(type, new RGBAImage(width, height, 1.0, pixelsImage));
2781*8af74909SZhong Yang }
2782*8af74909SZhong Yang
ClearRegisteredImages()2783*8af74909SZhong Yang void ListBoxX::ClearRegisteredImages() {
2784*8af74909SZhong Yang images.Clear();
2785*8af74909SZhong Yang }
2786*8af74909SZhong Yang
Draw(DRAWITEMSTRUCT * pDrawItem)2787*8af74909SZhong Yang void ListBoxX::Draw(DRAWITEMSTRUCT *pDrawItem) {
2788*8af74909SZhong Yang if ((pDrawItem->itemAction == ODA_SELECT) || (pDrawItem->itemAction == ODA_DRAWENTIRE)) {
2789*8af74909SZhong Yang RECT rcBox = pDrawItem->rcItem;
2790*8af74909SZhong Yang rcBox.left += TextOffset();
2791*8af74909SZhong Yang if (pDrawItem->itemState & ODS_SELECTED) {
2792*8af74909SZhong Yang RECT rcImage = pDrawItem->rcItem;
2793*8af74909SZhong Yang rcImage.right = rcBox.left;
2794*8af74909SZhong Yang // The image is not highlighted
2795*8af74909SZhong Yang ::FillRect(pDrawItem->hDC, &rcImage, reinterpret_cast<HBRUSH>(COLOR_WINDOW+1));
2796*8af74909SZhong Yang ::FillRect(pDrawItem->hDC, &rcBox, reinterpret_cast<HBRUSH>(COLOR_HIGHLIGHT+1));
2797*8af74909SZhong Yang ::SetBkColor(pDrawItem->hDC, ::GetSysColor(COLOR_HIGHLIGHT));
2798*8af74909SZhong Yang ::SetTextColor(pDrawItem->hDC, ::GetSysColor(COLOR_HIGHLIGHTTEXT));
2799*8af74909SZhong Yang } else {
2800*8af74909SZhong Yang ::FillRect(pDrawItem->hDC, &pDrawItem->rcItem, reinterpret_cast<HBRUSH>(COLOR_WINDOW+1));
2801*8af74909SZhong Yang ::SetBkColor(pDrawItem->hDC, ::GetSysColor(COLOR_WINDOW));
2802*8af74909SZhong Yang ::SetTextColor(pDrawItem->hDC, ::GetSysColor(COLOR_WINDOWTEXT));
2803*8af74909SZhong Yang }
2804*8af74909SZhong Yang
2805*8af74909SZhong Yang const ListItemData item = lti.Get(pDrawItem->itemID);
2806*8af74909SZhong Yang const int pixId = item.pixId;
2807*8af74909SZhong Yang const char *text = item.text;
2808*8af74909SZhong Yang const int len = static_cast<int>(strlen(text));
2809*8af74909SZhong Yang
2810*8af74909SZhong Yang RECT rcText = rcBox;
2811*8af74909SZhong Yang ::InsetRect(&rcText, static_cast<int>(TextInset.x), static_cast<int>(TextInset.y));
2812*8af74909SZhong Yang
2813*8af74909SZhong Yang if (unicodeMode) {
2814*8af74909SZhong Yang const TextWide tbuf(text, unicodeMode);
2815*8af74909SZhong Yang ::DrawTextW(pDrawItem->hDC, tbuf.buffer, tbuf.tlen, &rcText, DT_NOPREFIX|DT_END_ELLIPSIS|DT_SINGLELINE|DT_NOCLIP);
2816*8af74909SZhong Yang } else {
2817*8af74909SZhong Yang ::DrawTextA(pDrawItem->hDC, text, len, &rcText, DT_NOPREFIX|DT_END_ELLIPSIS|DT_SINGLELINE|DT_NOCLIP);
2818*8af74909SZhong Yang }
2819*8af74909SZhong Yang
2820*8af74909SZhong Yang // Draw the image, if any
2821*8af74909SZhong Yang const RGBAImage *pimage = images.Get(pixId);
2822*8af74909SZhong Yang if (pimage) {
2823*8af74909SZhong Yang std::unique_ptr<Surface> surfaceItem(Surface::Allocate(technology));
2824*8af74909SZhong Yang if (technology == SCWIN_TECH_GDI) {
2825*8af74909SZhong Yang surfaceItem->Init(pDrawItem->hDC, pDrawItem->hwndItem);
2826*8af74909SZhong Yang const long left = pDrawItem->rcItem.left + static_cast<int>(ItemInset.x + ImageInset.x);
2827*8af74909SZhong Yang const PRectangle rcImage = PRectangle::FromInts(left, pDrawItem->rcItem.top,
2828*8af74909SZhong Yang left + images.GetWidth(), pDrawItem->rcItem.bottom);
2829*8af74909SZhong Yang surfaceItem->DrawRGBAImage(rcImage,
2830*8af74909SZhong Yang pimage->GetWidth(), pimage->GetHeight(), pimage->Pixels());
2831*8af74909SZhong Yang ::SetTextAlign(pDrawItem->hDC, TA_TOP);
2832*8af74909SZhong Yang } else {
2833*8af74909SZhong Yang #if defined(USE_D2D)
2834*8af74909SZhong Yang const D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
2835*8af74909SZhong Yang D2D1_RENDER_TARGET_TYPE_DEFAULT,
2836*8af74909SZhong Yang D2D1::PixelFormat(
2837*8af74909SZhong Yang DXGI_FORMAT_B8G8R8A8_UNORM,
2838*8af74909SZhong Yang D2D1_ALPHA_MODE_IGNORE),
2839*8af74909SZhong Yang 0,
2840*8af74909SZhong Yang 0,
2841*8af74909SZhong Yang D2D1_RENDER_TARGET_USAGE_NONE,
2842*8af74909SZhong Yang D2D1_FEATURE_LEVEL_DEFAULT
2843*8af74909SZhong Yang );
2844*8af74909SZhong Yang ID2D1DCRenderTarget *pDCRT = nullptr;
2845*8af74909SZhong Yang HRESULT hr = pD2DFactory->CreateDCRenderTarget(&props, &pDCRT);
2846*8af74909SZhong Yang if (SUCCEEDED(hr) && pDCRT) {
2847*8af74909SZhong Yang RECT rcWindow;
2848*8af74909SZhong Yang GetClientRect(pDrawItem->hwndItem, &rcWindow);
2849*8af74909SZhong Yang hr = pDCRT->BindDC(pDrawItem->hDC, &rcWindow);
2850*8af74909SZhong Yang if (SUCCEEDED(hr)) {
2851*8af74909SZhong Yang surfaceItem->Init(pDCRT, pDrawItem->hwndItem);
2852*8af74909SZhong Yang pDCRT->BeginDraw();
2853*8af74909SZhong Yang const long left = pDrawItem->rcItem.left + static_cast<long>(ItemInset.x + ImageInset.x);
2854*8af74909SZhong Yang const PRectangle rcImage = PRectangle::FromInts(left, pDrawItem->rcItem.top,
2855*8af74909SZhong Yang left + images.GetWidth(), pDrawItem->rcItem.bottom);
2856*8af74909SZhong Yang surfaceItem->DrawRGBAImage(rcImage,
2857*8af74909SZhong Yang pimage->GetWidth(), pimage->GetHeight(), pimage->Pixels());
2858*8af74909SZhong Yang pDCRT->EndDraw();
2859*8af74909SZhong Yang ReleaseUnknown(pDCRT);
2860*8af74909SZhong Yang }
2861*8af74909SZhong Yang }
2862*8af74909SZhong Yang #endif
2863*8af74909SZhong Yang }
2864*8af74909SZhong Yang }
2865*8af74909SZhong Yang }
2866*8af74909SZhong Yang }
2867*8af74909SZhong Yang
AppendListItem(const char * text,const char * numword)2868*8af74909SZhong Yang void ListBoxX::AppendListItem(const char *text, const char *numword) {
2869*8af74909SZhong Yang int pixId = -1;
2870*8af74909SZhong Yang if (numword) {
2871*8af74909SZhong Yang pixId = 0;
2872*8af74909SZhong Yang char ch;
2873*8af74909SZhong Yang while ((ch = *++numword) != '\0') {
2874*8af74909SZhong Yang pixId = 10 * pixId + (ch - '0');
2875*8af74909SZhong Yang }
2876*8af74909SZhong Yang }
2877*8af74909SZhong Yang
2878*8af74909SZhong Yang lti.AllocItem(text, pixId);
2879*8af74909SZhong Yang const unsigned int len = static_cast<unsigned int>(strlen(text));
2880*8af74909SZhong Yang if (maxItemCharacters < len) {
2881*8af74909SZhong Yang maxItemCharacters = len;
2882*8af74909SZhong Yang widestItem = text;
2883*8af74909SZhong Yang }
2884*8af74909SZhong Yang }
2885*8af74909SZhong Yang
SetDelegate(IListBoxDelegate * lbDelegate)2886*8af74909SZhong Yang void ListBoxX::SetDelegate(IListBoxDelegate *lbDelegate) {
2887*8af74909SZhong Yang delegate = lbDelegate;
2888*8af74909SZhong Yang }
2889*8af74909SZhong Yang
SetList(const char * list,char separator,char typesep)2890*8af74909SZhong Yang void ListBoxX::SetList(const char *list, char separator, char typesep) {
2891*8af74909SZhong Yang // Turn off redraw while populating the list - this has a significant effect, even if
2892*8af74909SZhong Yang // the listbox is not visible.
2893*8af74909SZhong Yang SetRedraw(false);
2894*8af74909SZhong Yang Clear();
2895*8af74909SZhong Yang const size_t size = strlen(list);
2896*8af74909SZhong Yang char *words = lti.SetWords(list);
2897*8af74909SZhong Yang char *startword = words;
2898*8af74909SZhong Yang char *numword = nullptr;
2899*8af74909SZhong Yang for (size_t i=0; i < size; i++) {
2900*8af74909SZhong Yang if (words[i] == separator) {
2901*8af74909SZhong Yang words[i] = '\0';
2902*8af74909SZhong Yang if (numword)
2903*8af74909SZhong Yang *numword = '\0';
2904*8af74909SZhong Yang AppendListItem(startword, numword);
2905*8af74909SZhong Yang startword = words + i + 1;
2906*8af74909SZhong Yang numword = nullptr;
2907*8af74909SZhong Yang } else if (words[i] == typesep) {
2908*8af74909SZhong Yang numword = words + i;
2909*8af74909SZhong Yang }
2910*8af74909SZhong Yang }
2911*8af74909SZhong Yang if (startword) {
2912*8af74909SZhong Yang if (numword)
2913*8af74909SZhong Yang *numword = '\0';
2914*8af74909SZhong Yang AppendListItem(startword, numword);
2915*8af74909SZhong Yang }
2916*8af74909SZhong Yang
2917*8af74909SZhong Yang // Finally populate the listbox itself with the correct number of items
2918*8af74909SZhong Yang const int count = lti.Count();
2919*8af74909SZhong Yang ::SendMessage(lb, LB_INITSTORAGE, count, 0);
2920*8af74909SZhong Yang for (intptr_t j=0; j<count; j++) {
2921*8af74909SZhong Yang ListBox_AddItemData(lb, j+1);
2922*8af74909SZhong Yang }
2923*8af74909SZhong Yang SetRedraw(true);
2924*8af74909SZhong Yang }
2925*8af74909SZhong Yang
AdjustWindowRect(PRectangle * rc,UINT dpi)2926*8af74909SZhong Yang void ListBoxX::AdjustWindowRect(PRectangle *rc, UINT dpi) noexcept {
2927*8af74909SZhong Yang RECT rcw = RectFromPRectangle(*rc);
2928*8af74909SZhong Yang if (fnAdjustWindowRectExForDpi) {
2929*8af74909SZhong Yang fnAdjustWindowRectExForDpi(&rcw, WS_THICKFRAME, false, WS_EX_WINDOWEDGE, dpi);
2930*8af74909SZhong Yang } else {
2931*8af74909SZhong Yang ::AdjustWindowRectEx(&rcw, WS_THICKFRAME, false, WS_EX_WINDOWEDGE);
2932*8af74909SZhong Yang }
2933*8af74909SZhong Yang *rc = PRectangle::FromInts(rcw.left, rcw.top, rcw.right, rcw.bottom);
2934*8af74909SZhong Yang }
2935*8af74909SZhong Yang
ItemHeight() const2936*8af74909SZhong Yang int ListBoxX::ItemHeight() const {
2937*8af74909SZhong Yang int itemHeight = lineHeight + (static_cast<int>(TextInset.y) * 2);
2938*8af74909SZhong Yang const int pixHeight = images.GetHeight() + (static_cast<int>(ImageInset.y) * 2);
2939*8af74909SZhong Yang if (itemHeight < pixHeight) {
2940*8af74909SZhong Yang itemHeight = pixHeight;
2941*8af74909SZhong Yang }
2942*8af74909SZhong Yang return itemHeight;
2943*8af74909SZhong Yang }
2944*8af74909SZhong Yang
MinClientWidth() const2945*8af74909SZhong Yang int ListBoxX::MinClientWidth() const noexcept {
2946*8af74909SZhong Yang return 12 * (aveCharWidth+aveCharWidth/3);
2947*8af74909SZhong Yang }
2948*8af74909SZhong Yang
MinTrackSize() const2949*8af74909SZhong Yang POINT ListBoxX::MinTrackSize() const {
2950*8af74909SZhong Yang PRectangle rc = PRectangle::FromInts(0, 0, MinClientWidth(), ItemHeight());
2951*8af74909SZhong Yang AdjustWindowRect(&rc, dpi);
2952*8af74909SZhong Yang POINT ret = {static_cast<LONG>(rc.Width()), static_cast<LONG>(rc.Height())};
2953*8af74909SZhong Yang return ret;
2954*8af74909SZhong Yang }
2955*8af74909SZhong Yang
MaxTrackSize() const2956*8af74909SZhong Yang POINT ListBoxX::MaxTrackSize() const {
2957*8af74909SZhong Yang PRectangle rc = PRectangle::FromInts(0, 0,
2958*8af74909SZhong Yang std::max(static_cast<unsigned int>(MinClientWidth()),
2959*8af74909SZhong Yang maxCharWidth * maxItemCharacters + static_cast<int>(TextInset.x) * 2 +
2960*8af74909SZhong Yang TextOffset() + SystemMetricsForDpi(SM_CXVSCROLL, dpi)),
2961*8af74909SZhong Yang ItemHeight() * lti.Count());
2962*8af74909SZhong Yang AdjustWindowRect(&rc, dpi);
2963*8af74909SZhong Yang POINT ret = {static_cast<LONG>(rc.Width()), static_cast<LONG>(rc.Height())};
2964*8af74909SZhong Yang return ret;
2965*8af74909SZhong Yang }
2966*8af74909SZhong Yang
SetRedraw(bool on)2967*8af74909SZhong Yang void ListBoxX::SetRedraw(bool on) noexcept {
2968*8af74909SZhong Yang ::SendMessage(lb, WM_SETREDRAW, on, 0);
2969*8af74909SZhong Yang if (on)
2970*8af74909SZhong Yang ::InvalidateRect(lb, nullptr, TRUE);
2971*8af74909SZhong Yang }
2972*8af74909SZhong Yang
ResizeToCursor()2973*8af74909SZhong Yang void ListBoxX::ResizeToCursor() {
2974*8af74909SZhong Yang PRectangle rc = GetPosition();
2975*8af74909SZhong Yang POINT ptw;
2976*8af74909SZhong Yang ::GetCursorPos(&ptw);
2977*8af74909SZhong Yang const Point pt = PointFromPOINT(ptw) + dragOffset;
2978*8af74909SZhong Yang
2979*8af74909SZhong Yang switch (resizeHit) {
2980*8af74909SZhong Yang case HTLEFT:
2981*8af74909SZhong Yang rc.left = pt.x;
2982*8af74909SZhong Yang break;
2983*8af74909SZhong Yang case HTRIGHT:
2984*8af74909SZhong Yang rc.right = pt.x;
2985*8af74909SZhong Yang break;
2986*8af74909SZhong Yang case HTTOP:
2987*8af74909SZhong Yang rc.top = pt.y;
2988*8af74909SZhong Yang break;
2989*8af74909SZhong Yang case HTTOPLEFT:
2990*8af74909SZhong Yang rc.top = pt.y;
2991*8af74909SZhong Yang rc.left = pt.x;
2992*8af74909SZhong Yang break;
2993*8af74909SZhong Yang case HTTOPRIGHT:
2994*8af74909SZhong Yang rc.top = pt.y;
2995*8af74909SZhong Yang rc.right = pt.x;
2996*8af74909SZhong Yang break;
2997*8af74909SZhong Yang case HTBOTTOM:
2998*8af74909SZhong Yang rc.bottom = pt.y;
2999*8af74909SZhong Yang break;
3000*8af74909SZhong Yang case HTBOTTOMLEFT:
3001*8af74909SZhong Yang rc.bottom = pt.y;
3002*8af74909SZhong Yang rc.left = pt.x;
3003*8af74909SZhong Yang break;
3004*8af74909SZhong Yang case HTBOTTOMRIGHT:
3005*8af74909SZhong Yang rc.bottom = pt.y;
3006*8af74909SZhong Yang rc.right = pt.x;
3007*8af74909SZhong Yang break;
3008*8af74909SZhong Yang }
3009*8af74909SZhong Yang
3010*8af74909SZhong Yang const POINT ptMin = MinTrackSize();
3011*8af74909SZhong Yang const POINT ptMax = MaxTrackSize();
3012*8af74909SZhong Yang // We don't allow the left edge to move at present, but just in case
3013*8af74909SZhong Yang rc.left = std::clamp(rc.left, rcPreSize.right - ptMax.x, rcPreSize.right - ptMin.x);
3014*8af74909SZhong Yang rc.top = std::clamp(rc.top, rcPreSize.bottom - ptMax.y, rcPreSize.bottom - ptMin.y);
3015*8af74909SZhong Yang rc.right = std::clamp(rc.right, rcPreSize.left + ptMin.x, rcPreSize.left + ptMax.x);
3016*8af74909SZhong Yang rc.bottom = std::clamp(rc.bottom, rcPreSize.top + ptMin.y, rcPreSize.top + ptMax.y);
3017*8af74909SZhong Yang
3018*8af74909SZhong Yang SetPosition(rc);
3019*8af74909SZhong Yang }
3020*8af74909SZhong Yang
StartResize(WPARAM hitCode)3021*8af74909SZhong Yang void ListBoxX::StartResize(WPARAM hitCode) {
3022*8af74909SZhong Yang rcPreSize = GetPosition();
3023*8af74909SZhong Yang POINT cursorPos;
3024*8af74909SZhong Yang ::GetCursorPos(&cursorPos);
3025*8af74909SZhong Yang
3026*8af74909SZhong Yang switch (hitCode) {
3027*8af74909SZhong Yang case HTRIGHT:
3028*8af74909SZhong Yang case HTBOTTOM:
3029*8af74909SZhong Yang case HTBOTTOMRIGHT:
3030*8af74909SZhong Yang dragOffset.x = rcPreSize.right - cursorPos.x;
3031*8af74909SZhong Yang dragOffset.y = rcPreSize.bottom - cursorPos.y;
3032*8af74909SZhong Yang break;
3033*8af74909SZhong Yang
3034*8af74909SZhong Yang case HTTOPRIGHT:
3035*8af74909SZhong Yang dragOffset.x = rcPreSize.right - cursorPos.x;
3036*8af74909SZhong Yang dragOffset.y = rcPreSize.top - cursorPos.y;
3037*8af74909SZhong Yang break;
3038*8af74909SZhong Yang
3039*8af74909SZhong Yang // Note that the current hit test code prevents the left edge cases ever firing
3040*8af74909SZhong Yang // as we don't want the left edge to be movable
3041*8af74909SZhong Yang case HTLEFT:
3042*8af74909SZhong Yang case HTTOP:
3043*8af74909SZhong Yang case HTTOPLEFT:
3044*8af74909SZhong Yang dragOffset.x = rcPreSize.left - cursorPos.x;
3045*8af74909SZhong Yang dragOffset.y = rcPreSize.top - cursorPos.y;
3046*8af74909SZhong Yang break;
3047*8af74909SZhong Yang case HTBOTTOMLEFT:
3048*8af74909SZhong Yang dragOffset.x = rcPreSize.left - cursorPos.x;
3049*8af74909SZhong Yang dragOffset.y = rcPreSize.bottom - cursorPos.y;
3050*8af74909SZhong Yang break;
3051*8af74909SZhong Yang
3052*8af74909SZhong Yang default:
3053*8af74909SZhong Yang return;
3054*8af74909SZhong Yang }
3055*8af74909SZhong Yang
3056*8af74909SZhong Yang ::SetCapture(GetHWND());
3057*8af74909SZhong Yang resizeHit = hitCode;
3058*8af74909SZhong Yang }
3059*8af74909SZhong Yang
NcHitTest(WPARAM wParam,LPARAM lParam) const3060*8af74909SZhong Yang LRESULT ListBoxX::NcHitTest(WPARAM wParam, LPARAM lParam) const {
3061*8af74909SZhong Yang const PRectangle rc = GetPosition();
3062*8af74909SZhong Yang
3063*8af74909SZhong Yang LRESULT hit = ::DefWindowProc(GetHWND(), WM_NCHITTEST, wParam, lParam);
3064*8af74909SZhong Yang // There is an apparent bug in the DefWindowProc hit test code whereby it will
3065*8af74909SZhong Yang // return HTTOPXXX if the window in question is shorter than the default
3066*8af74909SZhong Yang // window caption height + frame, even if one is hovering over the bottom edge of
3067*8af74909SZhong Yang // the frame, so workaround that here
3068*8af74909SZhong Yang if (hit >= HTTOP && hit <= HTTOPRIGHT) {
3069*8af74909SZhong Yang const int minHeight = SystemMetricsForDpi(SM_CYMINTRACK, dpi);
3070*8af74909SZhong Yang const int yPos = GET_Y_LPARAM(lParam);
3071*8af74909SZhong Yang if ((rc.Height() < minHeight) && (yPos > ((rc.top + rc.bottom)/2))) {
3072*8af74909SZhong Yang hit += HTBOTTOM - HTTOP;
3073*8af74909SZhong Yang }
3074*8af74909SZhong Yang }
3075*8af74909SZhong Yang
3076*8af74909SZhong Yang // Never permit resizing that moves the left edge. Allow movement of top or bottom edge
3077*8af74909SZhong Yang // depending on whether the list is above or below the caret
3078*8af74909SZhong Yang switch (hit) {
3079*8af74909SZhong Yang case HTLEFT:
3080*8af74909SZhong Yang case HTTOPLEFT:
3081*8af74909SZhong Yang case HTBOTTOMLEFT:
3082*8af74909SZhong Yang hit = HTERROR;
3083*8af74909SZhong Yang break;
3084*8af74909SZhong Yang
3085*8af74909SZhong Yang case HTTOP:
3086*8af74909SZhong Yang case HTTOPRIGHT: {
3087*8af74909SZhong Yang // Valid only if caret below list
3088*8af74909SZhong Yang if (location.y < rc.top)
3089*8af74909SZhong Yang hit = HTERROR;
3090*8af74909SZhong Yang }
3091*8af74909SZhong Yang break;
3092*8af74909SZhong Yang
3093*8af74909SZhong Yang case HTBOTTOM:
3094*8af74909SZhong Yang case HTBOTTOMRIGHT: {
3095*8af74909SZhong Yang // Valid only if caret above list
3096*8af74909SZhong Yang if (rc.bottom <= location.y)
3097*8af74909SZhong Yang hit = HTERROR;
3098*8af74909SZhong Yang }
3099*8af74909SZhong Yang break;
3100*8af74909SZhong Yang }
3101*8af74909SZhong Yang
3102*8af74909SZhong Yang return hit;
3103*8af74909SZhong Yang }
3104*8af74909SZhong Yang
OnDoubleClick()3105*8af74909SZhong Yang void ListBoxX::OnDoubleClick() {
3106*8af74909SZhong Yang if (delegate) {
3107*8af74909SZhong Yang ListBoxEvent event(ListBoxEvent::EventType::doubleClick);
3108*8af74909SZhong Yang delegate->ListNotify(&event);
3109*8af74909SZhong Yang }
3110*8af74909SZhong Yang }
3111*8af74909SZhong Yang
OnSelChange()3112*8af74909SZhong Yang void ListBoxX::OnSelChange() {
3113*8af74909SZhong Yang if (delegate) {
3114*8af74909SZhong Yang ListBoxEvent event(ListBoxEvent::EventType::selectionChange);
3115*8af74909SZhong Yang delegate->ListNotify(&event);
3116*8af74909SZhong Yang }
3117*8af74909SZhong Yang }
3118*8af74909SZhong Yang
GetClientExtent() const3119*8af74909SZhong Yang POINT ListBoxX::GetClientExtent() const noexcept {
3120*8af74909SZhong Yang RECT rc;
3121*8af74909SZhong Yang ::GetWindowRect(HwndFromWindowID(wid), &rc);
3122*8af74909SZhong Yang POINT ret { rc.right - rc.left, rc.bottom - rc.top };
3123*8af74909SZhong Yang return ret;
3124*8af74909SZhong Yang }
3125*8af74909SZhong Yang
CentreItem(int n)3126*8af74909SZhong Yang void ListBoxX::CentreItem(int n) {
3127*8af74909SZhong Yang // If below mid point, scroll up to centre, but with more items below if uneven
3128*8af74909SZhong Yang if (n >= 0) {
3129*8af74909SZhong Yang const POINT extent = GetClientExtent();
3130*8af74909SZhong Yang const int visible = extent.y/ItemHeight();
3131*8af74909SZhong Yang if (visible < Length()) {
3132*8af74909SZhong Yang const int top = ListBox_GetTopIndex(lb);
3133*8af74909SZhong Yang const int half = (visible - 1) / 2;
3134*8af74909SZhong Yang if (n > (top + half))
3135*8af74909SZhong Yang ListBox_SetTopIndex(lb, n - half);
3136*8af74909SZhong Yang }
3137*8af74909SZhong Yang }
3138*8af74909SZhong Yang }
3139*8af74909SZhong Yang
3140*8af74909SZhong Yang // Performs a double-buffered paint operation to avoid flicker
Paint(HDC hDC)3141*8af74909SZhong Yang void ListBoxX::Paint(HDC hDC) noexcept {
3142*8af74909SZhong Yang const POINT extent = GetClientExtent();
3143*8af74909SZhong Yang HBITMAP hBitmap = ::CreateCompatibleBitmap(hDC, extent.x, extent.y);
3144*8af74909SZhong Yang HDC bitmapDC = ::CreateCompatibleDC(hDC);
3145*8af74909SZhong Yang HBITMAP hBitmapOld = SelectBitmap(bitmapDC, hBitmap);
3146*8af74909SZhong Yang // The list background is mainly erased during painting, but can be a small
3147*8af74909SZhong Yang // unpainted area when at the end of a non-integrally sized list with a
3148*8af74909SZhong Yang // vertical scroll bar
3149*8af74909SZhong Yang const RECT rc = { 0, 0, extent.x, extent.y };
3150*8af74909SZhong Yang ::FillRect(bitmapDC, &rc, reinterpret_cast<HBRUSH>(COLOR_WINDOW+1));
3151*8af74909SZhong Yang // Paint the entire client area and vertical scrollbar
3152*8af74909SZhong Yang ::SendMessage(lb, WM_PRINT, reinterpret_cast<WPARAM>(bitmapDC), PRF_CLIENT|PRF_NONCLIENT);
3153*8af74909SZhong Yang ::BitBlt(hDC, 0, 0, extent.x, extent.y, bitmapDC, 0, 0, SRCCOPY);
3154*8af74909SZhong Yang // Select a stock brush to prevent warnings from BoundsChecker
3155*8af74909SZhong Yang SelectBrush(bitmapDC, GetStockBrush(WHITE_BRUSH));
3156*8af74909SZhong Yang SelectBitmap(bitmapDC, hBitmapOld);
3157*8af74909SZhong Yang ::DeleteDC(bitmapDC);
3158*8af74909SZhong Yang ::DeleteObject(hBitmap);
3159*8af74909SZhong Yang }
3160*8af74909SZhong Yang
ControlWndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)3161*8af74909SZhong Yang LRESULT PASCAL ListBoxX::ControlWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
3162*8af74909SZhong Yang try {
3163*8af74909SZhong Yang ListBoxX *lbx = static_cast<ListBoxX *>(PointerFromWindow(::GetParent(hWnd)));
3164*8af74909SZhong Yang switch (iMessage) {
3165*8af74909SZhong Yang case WM_ERASEBKGND:
3166*8af74909SZhong Yang return TRUE;
3167*8af74909SZhong Yang
3168*8af74909SZhong Yang case WM_PAINT: {
3169*8af74909SZhong Yang PAINTSTRUCT ps;
3170*8af74909SZhong Yang HDC hDC = ::BeginPaint(hWnd, &ps);
3171*8af74909SZhong Yang if (lbx) {
3172*8af74909SZhong Yang lbx->Paint(hDC);
3173*8af74909SZhong Yang }
3174*8af74909SZhong Yang ::EndPaint(hWnd, &ps);
3175*8af74909SZhong Yang }
3176*8af74909SZhong Yang return 0;
3177*8af74909SZhong Yang
3178*8af74909SZhong Yang case WM_MOUSEACTIVATE:
3179*8af74909SZhong Yang // This prevents the view activating when the scrollbar is clicked
3180*8af74909SZhong Yang return MA_NOACTIVATE;
3181*8af74909SZhong Yang
3182*8af74909SZhong Yang case WM_LBUTTONDOWN: {
3183*8af74909SZhong Yang // We must take control of selection to prevent the ListBox activating
3184*8af74909SZhong Yang // the popup
3185*8af74909SZhong Yang const LRESULT lResult = ::SendMessage(hWnd, LB_ITEMFROMPOINT, 0, lParam);
3186*8af74909SZhong Yang const int item = LOWORD(lResult);
3187*8af74909SZhong Yang if (HIWORD(lResult) == 0 && item >= 0) {
3188*8af74909SZhong Yang ListBox_SetCurSel(hWnd, item);
3189*8af74909SZhong Yang if (lbx) {
3190*8af74909SZhong Yang lbx->OnSelChange();
3191*8af74909SZhong Yang }
3192*8af74909SZhong Yang }
3193*8af74909SZhong Yang }
3194*8af74909SZhong Yang return 0;
3195*8af74909SZhong Yang
3196*8af74909SZhong Yang case WM_LBUTTONUP:
3197*8af74909SZhong Yang return 0;
3198*8af74909SZhong Yang
3199*8af74909SZhong Yang case WM_LBUTTONDBLCLK: {
3200*8af74909SZhong Yang if (lbx) {
3201*8af74909SZhong Yang lbx->OnDoubleClick();
3202*8af74909SZhong Yang }
3203*8af74909SZhong Yang }
3204*8af74909SZhong Yang return 0;
3205*8af74909SZhong Yang
3206*8af74909SZhong Yang case WM_MBUTTONDOWN:
3207*8af74909SZhong Yang // disable the scroll wheel button click action
3208*8af74909SZhong Yang return 0;
3209*8af74909SZhong Yang }
3210*8af74909SZhong Yang
3211*8af74909SZhong Yang WNDPROC prevWndProc = reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
3212*8af74909SZhong Yang if (prevWndProc) {
3213*8af74909SZhong Yang return ::CallWindowProc(prevWndProc, hWnd, iMessage, wParam, lParam);
3214*8af74909SZhong Yang } else {
3215*8af74909SZhong Yang return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3216*8af74909SZhong Yang }
3217*8af74909SZhong Yang } catch (...) {
3218*8af74909SZhong Yang }
3219*8af74909SZhong Yang return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3220*8af74909SZhong Yang }
3221*8af74909SZhong Yang
WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)3222*8af74909SZhong Yang LRESULT ListBoxX::WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
3223*8af74909SZhong Yang switch (iMessage) {
3224*8af74909SZhong Yang case WM_CREATE: {
3225*8af74909SZhong Yang HINSTANCE hinstanceParent = GetWindowInstance(HwndFromWindow(*parent));
3226*8af74909SZhong Yang // Note that LBS_NOINTEGRALHEIGHT is specified to fix cosmetic issue when resizing the list
3227*8af74909SZhong Yang // but has useful side effect of speeding up list population significantly
3228*8af74909SZhong Yang lb = ::CreateWindowEx(
3229*8af74909SZhong Yang 0, TEXT("listbox"), TEXT(""),
3230*8af74909SZhong Yang WS_CHILD | WS_VSCROLL | WS_VISIBLE |
3231*8af74909SZhong Yang LBS_OWNERDRAWFIXED | LBS_NODATA | LBS_NOINTEGRALHEIGHT,
3232*8af74909SZhong Yang 0, 0, 150,80, hWnd,
3233*8af74909SZhong Yang reinterpret_cast<HMENU>(static_cast<ptrdiff_t>(ctrlID)),
3234*8af74909SZhong Yang hinstanceParent,
3235*8af74909SZhong Yang 0);
3236*8af74909SZhong Yang WNDPROC prevWndProc = SubclassWindow(lb, ControlWndProc);
3237*8af74909SZhong Yang ::SetWindowLongPtr(lb, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(prevWndProc));
3238*8af74909SZhong Yang }
3239*8af74909SZhong Yang break;
3240*8af74909SZhong Yang
3241*8af74909SZhong Yang case WM_SIZE:
3242*8af74909SZhong Yang if (lb) {
3243*8af74909SZhong Yang SetRedraw(false);
3244*8af74909SZhong Yang ::SetWindowPos(lb, 0, 0,0, LOWORD(lParam), HIWORD(lParam), SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE);
3245*8af74909SZhong Yang // Ensure the selection remains visible
3246*8af74909SZhong Yang CentreItem(GetSelection());
3247*8af74909SZhong Yang SetRedraw(true);
3248*8af74909SZhong Yang }
3249*8af74909SZhong Yang break;
3250*8af74909SZhong Yang
3251*8af74909SZhong Yang case WM_PAINT: {
3252*8af74909SZhong Yang PAINTSTRUCT ps;
3253*8af74909SZhong Yang ::BeginPaint(hWnd, &ps);
3254*8af74909SZhong Yang ::EndPaint(hWnd, &ps);
3255*8af74909SZhong Yang }
3256*8af74909SZhong Yang break;
3257*8af74909SZhong Yang
3258*8af74909SZhong Yang case WM_COMMAND:
3259*8af74909SZhong Yang // This is not actually needed now - the registered double click action is used
3260*8af74909SZhong Yang // directly to action a choice from the list.
3261*8af74909SZhong Yang ::SendMessage(HwndFromWindow(*parent), iMessage, wParam, lParam);
3262*8af74909SZhong Yang break;
3263*8af74909SZhong Yang
3264*8af74909SZhong Yang case WM_MEASUREITEM: {
3265*8af74909SZhong Yang MEASUREITEMSTRUCT *pMeasureItem = reinterpret_cast<MEASUREITEMSTRUCT *>(lParam);
3266*8af74909SZhong Yang pMeasureItem->itemHeight = ItemHeight();
3267*8af74909SZhong Yang }
3268*8af74909SZhong Yang break;
3269*8af74909SZhong Yang
3270*8af74909SZhong Yang case WM_DRAWITEM:
3271*8af74909SZhong Yang Draw(reinterpret_cast<DRAWITEMSTRUCT *>(lParam));
3272*8af74909SZhong Yang break;
3273*8af74909SZhong Yang
3274*8af74909SZhong Yang case WM_DESTROY:
3275*8af74909SZhong Yang lb = 0;
3276*8af74909SZhong Yang SetWindowPointer(hWnd, nullptr);
3277*8af74909SZhong Yang return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3278*8af74909SZhong Yang
3279*8af74909SZhong Yang case WM_ERASEBKGND:
3280*8af74909SZhong Yang // To reduce flicker we can elide background erasure since this window is
3281*8af74909SZhong Yang // completely covered by its child.
3282*8af74909SZhong Yang return TRUE;
3283*8af74909SZhong Yang
3284*8af74909SZhong Yang case WM_GETMINMAXINFO: {
3285*8af74909SZhong Yang MINMAXINFO *minMax = reinterpret_cast<MINMAXINFO*>(lParam);
3286*8af74909SZhong Yang minMax->ptMaxTrackSize = MaxTrackSize();
3287*8af74909SZhong Yang minMax->ptMinTrackSize = MinTrackSize();
3288*8af74909SZhong Yang }
3289*8af74909SZhong Yang break;
3290*8af74909SZhong Yang
3291*8af74909SZhong Yang case WM_MOUSEACTIVATE:
3292*8af74909SZhong Yang return MA_NOACTIVATE;
3293*8af74909SZhong Yang
3294*8af74909SZhong Yang case WM_NCHITTEST:
3295*8af74909SZhong Yang return NcHitTest(wParam, lParam);
3296*8af74909SZhong Yang
3297*8af74909SZhong Yang case WM_NCLBUTTONDOWN:
3298*8af74909SZhong Yang // We have to implement our own window resizing because the DefWindowProc
3299*8af74909SZhong Yang // implementation insists on activating the resized window
3300*8af74909SZhong Yang StartResize(wParam);
3301*8af74909SZhong Yang return 0;
3302*8af74909SZhong Yang
3303*8af74909SZhong Yang case WM_MOUSEMOVE: {
3304*8af74909SZhong Yang if (resizeHit == 0) {
3305*8af74909SZhong Yang return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3306*8af74909SZhong Yang } else {
3307*8af74909SZhong Yang ResizeToCursor();
3308*8af74909SZhong Yang }
3309*8af74909SZhong Yang }
3310*8af74909SZhong Yang break;
3311*8af74909SZhong Yang
3312*8af74909SZhong Yang case WM_LBUTTONUP:
3313*8af74909SZhong Yang case WM_CANCELMODE:
3314*8af74909SZhong Yang if (resizeHit != 0) {
3315*8af74909SZhong Yang resizeHit = 0;
3316*8af74909SZhong Yang ::ReleaseCapture();
3317*8af74909SZhong Yang }
3318*8af74909SZhong Yang return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3319*8af74909SZhong Yang case WM_MOUSEWHEEL:
3320*8af74909SZhong Yang wheelDelta -= GET_WHEEL_DELTA_WPARAM(wParam);
3321*8af74909SZhong Yang if (std::abs(wheelDelta) >= WHEEL_DELTA) {
3322*8af74909SZhong Yang const int nRows = GetVisibleRows();
3323*8af74909SZhong Yang int linesToScroll = 1;
3324*8af74909SZhong Yang if (nRows > 1) {
3325*8af74909SZhong Yang linesToScroll = nRows - 1;
3326*8af74909SZhong Yang }
3327*8af74909SZhong Yang if (linesToScroll > 3) {
3328*8af74909SZhong Yang linesToScroll = 3;
3329*8af74909SZhong Yang }
3330*8af74909SZhong Yang linesToScroll *= (wheelDelta / WHEEL_DELTA);
3331*8af74909SZhong Yang int top = ListBox_GetTopIndex(lb) + linesToScroll;
3332*8af74909SZhong Yang if (top < 0) {
3333*8af74909SZhong Yang top = 0;
3334*8af74909SZhong Yang }
3335*8af74909SZhong Yang ListBox_SetTopIndex(lb, top);
3336*8af74909SZhong Yang // update wheel delta residue
3337*8af74909SZhong Yang if (wheelDelta >= 0)
3338*8af74909SZhong Yang wheelDelta = wheelDelta % WHEEL_DELTA;
3339*8af74909SZhong Yang else
3340*8af74909SZhong Yang wheelDelta = - (-wheelDelta % WHEEL_DELTA);
3341*8af74909SZhong Yang }
3342*8af74909SZhong Yang break;
3343*8af74909SZhong Yang
3344*8af74909SZhong Yang default:
3345*8af74909SZhong Yang return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3346*8af74909SZhong Yang }
3347*8af74909SZhong Yang
3348*8af74909SZhong Yang return 0;
3349*8af74909SZhong Yang }
3350*8af74909SZhong Yang
StaticWndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)3351*8af74909SZhong Yang LRESULT PASCAL ListBoxX::StaticWndProc(
3352*8af74909SZhong Yang HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
3353*8af74909SZhong Yang if (iMessage == WM_CREATE) {
3354*8af74909SZhong Yang CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT *>(lParam);
3355*8af74909SZhong Yang SetWindowPointer(hWnd, pCreate->lpCreateParams);
3356*8af74909SZhong Yang }
3357*8af74909SZhong Yang // Find C++ object associated with window.
3358*8af74909SZhong Yang ListBoxX *lbx = static_cast<ListBoxX *>(PointerFromWindow(hWnd));
3359*8af74909SZhong Yang if (lbx) {
3360*8af74909SZhong Yang return lbx->WndProc(hWnd, iMessage, wParam, lParam);
3361*8af74909SZhong Yang } else {
3362*8af74909SZhong Yang return ::DefWindowProc(hWnd, iMessage, wParam, lParam);
3363*8af74909SZhong Yang }
3364*8af74909SZhong Yang }
3365*8af74909SZhong Yang
3366*8af74909SZhong Yang namespace {
3367*8af74909SZhong Yang
ListBoxX_Register()3368*8af74909SZhong Yang bool ListBoxX_Register() noexcept {
3369*8af74909SZhong Yang WNDCLASSEX wndclassc {};
3370*8af74909SZhong Yang wndclassc.cbSize = sizeof(wndclassc);
3371*8af74909SZhong Yang // We need CS_HREDRAW and CS_VREDRAW because of the ellipsis that might be drawn for
3372*8af74909SZhong Yang // truncated items in the list and the appearance/disappearance of the vertical scroll bar.
3373*8af74909SZhong Yang // The list repaint is double-buffered to avoid the flicker this would otherwise cause.
3374*8af74909SZhong Yang wndclassc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;
3375*8af74909SZhong Yang wndclassc.cbWndExtra = sizeof(ListBoxX *);
3376*8af74909SZhong Yang wndclassc.hInstance = hinstPlatformRes;
3377*8af74909SZhong Yang wndclassc.lpfnWndProc = ListBoxX::StaticWndProc;
3378*8af74909SZhong Yang wndclassc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
3379*8af74909SZhong Yang wndclassc.lpszClassName = ListBoxX_ClassName;
3380*8af74909SZhong Yang
3381*8af74909SZhong Yang return ::RegisterClassEx(&wndclassc) != 0;
3382*8af74909SZhong Yang }
3383*8af74909SZhong Yang
ListBoxX_Unregister()3384*8af74909SZhong Yang void ListBoxX_Unregister() noexcept {
3385*8af74909SZhong Yang if (hinstPlatformRes) {
3386*8af74909SZhong Yang ::UnregisterClass(ListBoxX_ClassName, hinstPlatformRes);
3387*8af74909SZhong Yang }
3388*8af74909SZhong Yang }
3389*8af74909SZhong Yang
3390*8af74909SZhong Yang }
3391*8af74909SZhong Yang
Menu()3392*8af74909SZhong Yang Menu::Menu() noexcept : mid{} {
3393*8af74909SZhong Yang }
3394*8af74909SZhong Yang
CreatePopUp()3395*8af74909SZhong Yang void Menu::CreatePopUp() {
3396*8af74909SZhong Yang Destroy();
3397*8af74909SZhong Yang mid = ::CreatePopupMenu();
3398*8af74909SZhong Yang }
3399*8af74909SZhong Yang
Destroy()3400*8af74909SZhong Yang void Menu::Destroy() {
3401*8af74909SZhong Yang if (mid)
3402*8af74909SZhong Yang ::DestroyMenu(static_cast<HMENU>(mid));
3403*8af74909SZhong Yang mid = 0;
3404*8af74909SZhong Yang }
3405*8af74909SZhong Yang
Show(Point pt,Window & w)3406*8af74909SZhong Yang void Menu::Show(Point pt, Window &w) {
3407*8af74909SZhong Yang ::TrackPopupMenu(static_cast<HMENU>(mid),
3408*8af74909SZhong Yang TPM_RIGHTBUTTON, static_cast<int>(pt.x - 4), static_cast<int>(pt.y), 0,
3409*8af74909SZhong Yang HwndFromWindow(w), nullptr);
3410*8af74909SZhong Yang Destroy();
3411*8af74909SZhong Yang }
3412*8af74909SZhong Yang
3413*8af74909SZhong Yang class DynamicLibraryImpl : public DynamicLibrary {
3414*8af74909SZhong Yang protected:
3415*8af74909SZhong Yang HMODULE h;
3416*8af74909SZhong Yang public:
DynamicLibraryImpl(const char * modulePath)3417*8af74909SZhong Yang explicit DynamicLibraryImpl(const char *modulePath) noexcept {
3418*8af74909SZhong Yang h = ::LoadLibraryA(modulePath);
3419*8af74909SZhong Yang }
3420*8af74909SZhong Yang
~DynamicLibraryImpl()3421*8af74909SZhong Yang ~DynamicLibraryImpl() override {
3422*8af74909SZhong Yang if (h)
3423*8af74909SZhong Yang ::FreeLibrary(h);
3424*8af74909SZhong Yang }
3425*8af74909SZhong Yang
3426*8af74909SZhong Yang // Use GetProcAddress to get a pointer to the relevant function.
FindFunction(const char * name)3427*8af74909SZhong Yang Function FindFunction(const char *name) noexcept override {
3428*8af74909SZhong Yang if (h) {
3429*8af74909SZhong Yang // Use memcpy as it doesn't invoke undefined or conditionally defined behaviour.
3430*8af74909SZhong Yang FARPROC fp = ::GetProcAddress(h, name);
3431*8af74909SZhong Yang Function f = nullptr;
3432*8af74909SZhong Yang static_assert(sizeof(f) == sizeof(fp));
3433*8af74909SZhong Yang memcpy(&f, &fp, sizeof(f));
3434*8af74909SZhong Yang return f;
3435*8af74909SZhong Yang } else {
3436*8af74909SZhong Yang return nullptr;
3437*8af74909SZhong Yang }
3438*8af74909SZhong Yang }
3439*8af74909SZhong Yang
IsValid()3440*8af74909SZhong Yang bool IsValid() noexcept override {
3441*8af74909SZhong Yang return h != NULL;
3442*8af74909SZhong Yang }
3443*8af74909SZhong Yang };
3444*8af74909SZhong Yang
Load(const char * modulePath)3445*8af74909SZhong Yang DynamicLibrary *DynamicLibrary::Load(const char *modulePath) {
3446*8af74909SZhong Yang return static_cast<DynamicLibrary *>(new DynamicLibraryImpl(modulePath));
3447*8af74909SZhong Yang }
3448*8af74909SZhong Yang
Chrome()3449*8af74909SZhong Yang ColourDesired Platform::Chrome() {
3450*8af74909SZhong Yang return ColourDesired(::GetSysColor(COLOR_3DFACE));
3451*8af74909SZhong Yang }
3452*8af74909SZhong Yang
ChromeHighlight()3453*8af74909SZhong Yang ColourDesired Platform::ChromeHighlight() {
3454*8af74909SZhong Yang return ColourDesired(::GetSysColor(COLOR_3DHIGHLIGHT));
3455*8af74909SZhong Yang }
3456*8af74909SZhong Yang
DefaultFont()3457*8af74909SZhong Yang const char *Platform::DefaultFont() {
3458*8af74909SZhong Yang return "Verdana";
3459*8af74909SZhong Yang }
3460*8af74909SZhong Yang
DefaultFontSize()3461*8af74909SZhong Yang int Platform::DefaultFontSize() {
3462*8af74909SZhong Yang return 8;
3463*8af74909SZhong Yang }
3464*8af74909SZhong Yang
DoubleClickTime()3465*8af74909SZhong Yang unsigned int Platform::DoubleClickTime() {
3466*8af74909SZhong Yang return ::GetDoubleClickTime();
3467*8af74909SZhong Yang }
3468*8af74909SZhong Yang
DebugDisplay(const char * s)3469*8af74909SZhong Yang void Platform::DebugDisplay(const char *s) {
3470*8af74909SZhong Yang ::OutputDebugStringA(s);
3471*8af74909SZhong Yang }
3472*8af74909SZhong Yang
3473*8af74909SZhong Yang //#define TRACE
3474*8af74909SZhong Yang
3475*8af74909SZhong Yang #ifdef TRACE
DebugPrintf(const char * format,...)3476*8af74909SZhong Yang void Platform::DebugPrintf(const char *format, ...) {
3477*8af74909SZhong Yang char buffer[2000];
3478*8af74909SZhong Yang va_list pArguments;
3479*8af74909SZhong Yang va_start(pArguments, format);
3480*8af74909SZhong Yang vsprintf(buffer,format,pArguments);
3481*8af74909SZhong Yang va_end(pArguments);
3482*8af74909SZhong Yang Platform::DebugDisplay(buffer);
3483*8af74909SZhong Yang }
3484*8af74909SZhong Yang #else
DebugPrintf(const char *,...)3485*8af74909SZhong Yang void Platform::DebugPrintf(const char *, ...) {
3486*8af74909SZhong Yang }
3487*8af74909SZhong Yang #endif
3488*8af74909SZhong Yang
3489*8af74909SZhong Yang static bool assertionPopUps = true;
3490*8af74909SZhong Yang
ShowAssertionPopUps(bool assertionPopUps_)3491*8af74909SZhong Yang bool Platform::ShowAssertionPopUps(bool assertionPopUps_) {
3492*8af74909SZhong Yang const bool ret = assertionPopUps;
3493*8af74909SZhong Yang assertionPopUps = assertionPopUps_;
3494*8af74909SZhong Yang return ret;
3495*8af74909SZhong Yang }
3496*8af74909SZhong Yang
Assert(const char * c,const char * file,int line)3497*8af74909SZhong Yang void Platform::Assert(const char *c, const char *file, int line) {
3498*8af74909SZhong Yang char buffer[2000] {};
3499*8af74909SZhong Yang sprintf(buffer, "Assertion [%s] failed at %s %d%s", c, file, line, assertionPopUps ? "" : "\r\n");
3500*8af74909SZhong Yang if (assertionPopUps) {
3501*8af74909SZhong Yang const int idButton = ::MessageBoxA(0, buffer, "Assertion failure",
3502*8af74909SZhong Yang MB_ABORTRETRYIGNORE|MB_ICONHAND|MB_SETFOREGROUND|MB_TASKMODAL);
3503*8af74909SZhong Yang if (idButton == IDRETRY) {
3504*8af74909SZhong Yang ::DebugBreak();
3505*8af74909SZhong Yang } else if (idButton == IDIGNORE) {
3506*8af74909SZhong Yang // all OK
3507*8af74909SZhong Yang } else {
3508*8af74909SZhong Yang abort();
3509*8af74909SZhong Yang }
3510*8af74909SZhong Yang } else {
3511*8af74909SZhong Yang Platform::DebugDisplay(buffer);
3512*8af74909SZhong Yang ::DebugBreak();
3513*8af74909SZhong Yang abort();
3514*8af74909SZhong Yang }
3515*8af74909SZhong Yang }
3516*8af74909SZhong Yang
Platform_Initialise(void * hInstance)3517*8af74909SZhong Yang void Platform_Initialise(void *hInstance) noexcept {
3518*8af74909SZhong Yang hinstPlatformRes = static_cast<HINSTANCE>(hInstance);
3519*8af74909SZhong Yang LoadDpiForWindow();
3520*8af74909SZhong Yang ListBoxX_Register();
3521*8af74909SZhong Yang }
3522*8af74909SZhong Yang
Platform_Finalise(bool fromDllMain)3523*8af74909SZhong Yang void Platform_Finalise(bool fromDllMain) noexcept {
3524*8af74909SZhong Yang #if defined(USE_D2D)
3525*8af74909SZhong Yang if (!fromDllMain) {
3526*8af74909SZhong Yang ReleaseUnknown(defaultRenderingParams);
3527*8af74909SZhong Yang ReleaseUnknown(customClearTypeRenderingParams);
3528*8af74909SZhong Yang ReleaseUnknown(pIDWriteFactory);
3529*8af74909SZhong Yang ReleaseUnknown(pD2DFactory);
3530*8af74909SZhong Yang if (hDLLDWrite) {
3531*8af74909SZhong Yang FreeLibrary(hDLLDWrite);
3532*8af74909SZhong Yang hDLLDWrite = {};
3533*8af74909SZhong Yang }
3534*8af74909SZhong Yang if (hDLLD2D) {
3535*8af74909SZhong Yang FreeLibrary(hDLLD2D);
3536*8af74909SZhong Yang hDLLD2D = {};
3537*8af74909SZhong Yang }
3538*8af74909SZhong Yang }
3539*8af74909SZhong Yang #endif
3540*8af74909SZhong Yang if (!fromDllMain && hDLLShcore) {
3541*8af74909SZhong Yang FreeLibrary(hDLLShcore);
3542*8af74909SZhong Yang hDLLShcore = {};
3543*8af74909SZhong Yang }
3544*8af74909SZhong Yang ListBoxX_Unregister();
3545*8af74909SZhong Yang }
3546*8af74909SZhong Yang
3547*8af74909SZhong Yang }
3548