// sktoolslib - common files for SK tools // Copyright (C) 2012, 2014, 2017 - Stefan Kueng // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software Foundation, // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // #include "stdafx.h" #include "AeroControls.h" enum ControlType { Static, Button, Progressbar }; #ifndef RECTWIDTH #define RECTWIDTH(rc) ((rc).right-(rc).left) #endif #ifndef RECTHEIGHT #define RECTHEIGHT(rc) ((rc).bottom-(rc).top) #endif using namespace Gdiplus; #pragma comment(lib, "gdiplus.lib") #pragma comment(lib, "comctl32.lib") AeroControlBase::AeroControlBase() { GdiplusStartupInput gdiplusStartupInput; m_dwm.Initialize(); m_theme.Initialize(); GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr); } AeroControlBase::~AeroControlBase() { for (std::map::const_iterator it = subclassedControls.begin(); it != subclassedControls.end(); ++it) { RemoveWindowSubclass(it->first, SubclassProc, it->second); } GdiplusShutdown(gdiplusToken); } bool AeroControlBase::SubclassControl(HWND hControl) { bool bRet = false; if (!m_dwm.IsDwmCompositionEnabled()) return bRet; TCHAR szWndClassName[MAX_PATH]; if (GetClassName(hControl, szWndClassName, _countof(szWndClassName))) { if (!_tcscmp(szWndClassName, _T("Static"))) { bRet = !!SetWindowSubclass(hControl, SubclassProc, Static, (DWORD_PTR)this); subclassedControls[hControl] = Static; } if (!_tcscmp(szWndClassName, _T("Button"))) { bRet = !!SetWindowSubclass(hControl, SubclassProc, Button, (DWORD_PTR)this); subclassedControls[hControl] = Button; } if(!_tcscmp(szWndClassName, _T("msctls_progress32"))) { bRet = !!SetWindowSubclass(hControl, SubclassProc, Progressbar, (DWORD_PTR)this); subclassedControls[hControl] = Progressbar; } } return bRet; } LRESULT AeroControlBase::SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uidSubclass, DWORD_PTR dwRefData) { AeroControlBase * pThis = (AeroControlBase*)dwRefData; if (pThis) { if (pThis->m_dwm.IsDwmCompositionEnabled()) { switch (uidSubclass) { case Static: return pThis->StaticWindowProc(hWnd, uMsg, wParam, lParam); break; case Button: return pThis->ButtonWindowProc(hWnd, uMsg, wParam, lParam); break; case Progressbar: return pThis->ProgressbarWindowProc(hWnd, uMsg, wParam, lParam); break; } } } return DefSubclassProc(hWnd, uMsg, wParam, lParam); } LRESULT AeroControlBase::StaticWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_SETTEXT: case WM_ENABLE: case WM_STYLECHANGED: { LRESULT res = DefSubclassProc(hWnd, uMsg, wParam, lParam); InvalidateRgn(hWnd, nullptr, FALSE); return res; } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); if (hdc) { HDC hdcPaint = nullptr; RECT rcClient; GetClientRect(hWnd, &rcClient); LONG_PTR dwStyle = GetWindowLongPtr(hWnd, GWL_STYLE); LONG_PTR dwStyleEx = GetWindowLongPtr(hWnd, GWL_EXSTYLE); HTHEME hTheme = m_theme.OpenThemeData(nullptr, L"ControlPanelStyle"); if (hTheme) { HPAINTBUFFER hBufferedPaint = m_theme.BeginBufferedPaint(hdc, &rcClient, BPBF_TOPDOWNDIB, nullptr, &hdcPaint); if (hdcPaint) { PatBlt(hdcPaint, 0, 0, RECTWIDTH(rcClient), RECTHEIGHT(rcClient), BLACKNESS); m_theme.BufferedPaintSetAlpha(hBufferedPaint, &ps.rcPaint, 0x00); LONG_PTR dwStaticStyle = dwStyle&0x1F; if (dwStaticStyle == SS_ICON || dwStaticStyle == SS_BITMAP) { bool bIcon = dwStaticStyle == SS_ICON; HANDLE hBmpIco = (HANDLE)SendMessage(hWnd, STM_GETIMAGE, bIcon ? IMAGE_ICON:IMAGE_BITMAP, NULL); if (hBmpIco) { std::unique_ptr pBmp( bIcon ? new Bitmap((HICON)hBmpIco) : new Bitmap((HBITMAP)hBmpIco, nullptr) ); std::unique_ptr myGraphics( new Graphics(hdcPaint) ); std::unique_ptr pcbmp( new CachedBitmap(pBmp.get(), myGraphics.get()) ); myGraphics->DrawCachedBitmap(pcbmp.get(), 0,0); } } else if (SS_BLACKRECT == dwStaticStyle || SS_GRAYRECT == dwStaticStyle || SS_WHITERECT == dwStaticStyle) { ARGB argb = 0L; switch (dwStaticStyle) { case SS_BLACKRECT: argb = 0xFF000000; break; case SS_GRAYRECT: argb = 0xFF808080; break; case SS_WHITERECT: argb = 0xFFFFFFFF; break; default: break; } Color clr(argb); FillRect(&rcClient, hdcPaint, clr); } else if (SS_BLACKFRAME == dwStaticStyle || SS_GRAYFRAME == dwStaticStyle || SS_WHITEFRAME == dwStaticStyle) { ARGB argb = 0L; switch (dwStaticStyle) { case SS_BLACKFRAME: argb = 0xFF000000; break; case SS_GRAYFRAME: argb = 0xFF808080; break; case SS_WHITEFRAME: argb = 0xFFFFFFFF; break; default: break; } Color clr(argb); DrawRect(&rcClient, hdcPaint, DashStyleSolid, clr, 1.0); } else { DTTOPTS DttOpts = {sizeof(DTTOPTS)}; DttOpts.dwFlags = DTT_COMPOSITED | DTT_GLOWSIZE; DttOpts.crText = RGB(255, 255, 255); DttOpts.iGlowSize = 12; // Default value m_theme.DetermineGlowSize(&DttOpts.iGlowSize); HFONT hFontOld = (HFONT)SendMessage(hWnd, WM_GETFONT, 0L, 0L); if (hFontOld) hFontOld = (HFONT) SelectObject(hdcPaint, hFontOld); int iLen = GetWindowTextLength(hWnd); if (iLen) { iLen+=5; // 1 for terminating zero, 4 for DT_MODIFYSTRING LPWSTR szText = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*iLen); if (szText) { iLen = GetWindowTextW(hWnd, szText, iLen); if (iLen) { DWORD dwFlags = DT_WORDBREAK; switch (dwStaticStyle) { case SS_CENTER: dwFlags |= DT_CENTER; break; case SS_RIGHT: dwFlags |= DT_RIGHT; break; case SS_LEFTNOWORDWRAP: dwFlags &= ~DT_WORDBREAK; break; } if (dwStyle & SS_CENTERIMAGE) { dwFlags |= DT_VCENTER; dwFlags &= ~DT_WORDBREAK; } if (dwStyle & SS_ENDELLIPSIS) dwFlags |= DT_END_ELLIPSIS|DT_MODIFYSTRING; else if (dwStyle & SS_PATHELLIPSIS) dwFlags |= DT_PATH_ELLIPSIS|DT_MODIFYSTRING; else if (dwStyle & SS_WORDELLIPSIS) dwFlags |= DT_WORD_ELLIPSIS|DT_MODIFYSTRING; if (dwStyleEx&WS_EX_RIGHT) dwFlags |= DT_RIGHT; if (dwStyle & SS_NOPREFIX) dwFlags |= DT_NOPREFIX; m_theme.DrawThemeTextEx(hTheme, hdcPaint, 0, 0, szText, -1, dwFlags, &rcClient, &DttOpts); if (dwStyle&SS_SUNKEN || dwStyle&WS_BORDER) DrawRect(&rcClient, hdcPaint, DashStyleSolid, Color(0xFF, 0,0,0), 1.0); } LocalFree(szText); } } if (hFontOld) { SelectObject(hdcPaint, hFontOld); hFontOld = nullptr; } } m_theme.EndBufferedPaint(hBufferedPaint, TRUE); } m_theme.CloseThemeData(hTheme); } } EndPaint(hWnd, &ps); return 1; } break; case WM_NCDESTROY: case WM_DESTROY: RemoveWindowSubclass(hWnd, SubclassProc, Static); subclassedControls.erase(hWnd); break; } return DefSubclassProc(hWnd, uMsg, wParam, lParam); } LRESULT AeroControlBase::ButtonWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_SETTEXT: case WM_ENABLE: case WM_STYLECHANGED: { LRESULT res = DefSubclassProc(hWnd, uMsg, wParam, lParam); InvalidateRgn(hWnd, nullptr, FALSE); return res; } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); if (hdc) { LONG_PTR dwStyle = GetWindowLongPtr(hWnd, GWL_STYLE); LONG_PTR dwButtonStyle = LOWORD(dwStyle); LONG_PTR dwButtonType = dwButtonStyle&0xF; RECT rcClient; GetClientRect(hWnd, &rcClient); if((dwButtonType&BS_GROUPBOX) == BS_GROUPBOX) { /// /// it must be a group box /// HTHEME hTheme = m_theme.OpenThemeData(hWnd, L"Button"); if (hTheme) { HDC hdcPaint = nullptr; BP_PAINTPARAMS params = { sizeof(BP_PAINTPARAMS) }; params.dwFlags = BPPF_ERASE; RECT rcExclusion = rcClient; params.prcExclude = &rcExclusion; /// /// We have to calculate the exclusion rect and therefore /// calculate the font height. We select the control's font /// into the DC and fake a drawing operation: /// HFONT hFontOld = (HFONT)SendMessage(hWnd, WM_GETFONT, 0L, 0L); if (hFontOld) hFontOld = (HFONT) SelectObject(hdc, hFontOld); RECT rcDraw = rcClient; DWORD dwFlags = DT_SINGLELINE; /// /// we use uppercase A to determine the height of text, so we /// can draw the upper line of the groupbox: /// DrawTextW(hdc, L"A", -1, &rcDraw, dwFlags|DT_CALCRECT); if (hFontOld) { SelectObject(hdc, hFontOld); hFontOld = nullptr; } InflateRect(&rcExclusion, -1, -1*RECTHEIGHT(rcDraw)); HPAINTBUFFER hBufferedPaint = m_theme.BeginBufferedPaint(hdc, &rcClient, BPBF_TOPDOWNDIB, ¶ms, &hdcPaint); if (hdcPaint) { /// /// now we again retrieve the font, but this time we select it into /// the buffered DC: /// hFontOld = (HFONT)SendMessage(hWnd, WM_GETFONT, 0L, 0L); if (hFontOld) hFontOld = (HFONT) SelectObject(hdcPaint, hFontOld); PatBlt(hdcPaint, 0, 0, RECTWIDTH(rcClient), RECTHEIGHT(rcClient), BLACKNESS); m_theme.BufferedPaintSetAlpha(hBufferedPaint, &ps.rcPaint, 0x00); int iPartId = BP_GROUPBOX; int iState = GetStateFromBtnState(dwStyle, FALSE, FALSE, 0L, iPartId, FALSE); DTTOPTS DttOpts = {sizeof(DTTOPTS)}; DttOpts.dwFlags = DTT_COMPOSITED | DTT_GLOWSIZE; DttOpts.crText = RGB(255, 255, 255); DttOpts.iGlowSize = 12; // Default value m_theme.DetermineGlowSize(&DttOpts.iGlowSize); COLORREF cr = RGB(0x00, 0x00, 0x00); GetEditBorderColor(hWnd, &cr); /// /// add the alpha value: /// cr |= 0xff000000; std::unique_ptr myPen( new Pen(Color(cr), 1) ); std::unique_ptr myGraphics( new Graphics(hdcPaint) ); int iY = RECTHEIGHT(rcDraw)/2; Rect rr = Rect(rcClient.left, rcClient.top+iY, RECTWIDTH(rcClient), RECTHEIGHT(rcClient)-iY-1); GraphicsPath path; GetRoundRectPath(&path, rr, 10); myGraphics->DrawPath(myPen.get(), &path); //myGraphics->DrawRectangle(myPen, rcClient.left, rcClient.top + iY, // RECTWIDTH(rcClient)-1, RECTHEIGHT(rcClient) - iY-1); myGraphics.reset(); myPen.reset(); int iLen = GetWindowTextLength(hWnd); if (iLen) { iLen+=5; // 1 for terminating zero, 4 for DT_MODIFYSTRING LPWSTR szText = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*iLen); if (szText) { iLen = GetWindowTextW(hWnd, szText, iLen); if (iLen) { int iX = RECTWIDTH(rcDraw); rcDraw = rcClient; rcDraw.left += iX; DrawTextW(hdcPaint, szText, -1, &rcDraw, dwFlags|DT_CALCRECT); PatBlt(hdcPaint, rcDraw.left, rcDraw.top , RECTWIDTH(rcDraw) + 3, RECTHEIGHT(rcDraw), BLACKNESS); rcDraw.left++; rcDraw.right++; m_theme.DrawThemeTextEx(hTheme, hdcPaint, iPartId, iState, szText, -1, dwFlags, &rcDraw, &DttOpts); } LocalFree(szText); } } if (hFontOld) { SelectObject(hdcPaint, hFontOld); hFontOld = nullptr; } m_theme.EndBufferedPaint(hBufferedPaint, TRUE); } m_theme.CloseThemeData(hTheme); } } else if (dwButtonType == BS_CHECKBOX || dwButtonType == BS_AUTOCHECKBOX || dwButtonType == BS_3STATE || dwButtonType == BS_AUTO3STATE || dwButtonType == BS_RADIOBUTTON || dwButtonType == BS_AUTORADIOBUTTON) { HTHEME hTheme = m_theme.OpenThemeData(hWnd, L"Button"); if (hTheme) { HDC hdcPaint = nullptr; BP_PAINTPARAMS params = { sizeof(BP_PAINTPARAMS) }; params.dwFlags = BPPF_ERASE; HPAINTBUFFER hBufferedPaint = m_theme.BeginBufferedPaint(hdc, &rcClient, BPBF_TOPDOWNDIB, ¶ms, &hdcPaint); if (hdcPaint) { PatBlt(hdcPaint, 0, 0, RECTWIDTH(rcClient), RECTHEIGHT(rcClient), BLACKNESS); m_theme.BufferedPaintSetAlpha(hBufferedPaint, &ps.rcPaint, 0x00); int iState = CBS_UNCHECKEDNORMAL; LRESULT dwCheckState = SendMessage(hWnd, BM_GETCHECK, 0, 0L); POINT pt; RECT rc; GetWindowRect(hWnd, &rc); GetCursorPos(&pt); BOOL bHot = PtInRect(&rc, pt); BOOL bFocus = GetFocus() == hWnd; int iPartId = BP_CHECKBOX; if (dwButtonType == BS_RADIOBUTTON || dwButtonType == BS_AUTORADIOBUTTON) iPartId = BP_RADIOBUTTON; iState = GetStateFromBtnState(dwStyle, bHot, bFocus, dwCheckState, iPartId, FALSE); int bmWidth = int(ceil(13.0 * GetDeviceCaps(hdcPaint, LOGPIXELSX) / USER_DEFAULT_SCREEN_DPI)); UINT uiHalfWidth = (RECTWIDTH(rcClient) - bmWidth)/2; /// /// we have to use the whole client area, otherwise we get only partially /// drawn areas: /// RECT rcPaint = rcClient; if (dwButtonStyle & BS_LEFTTEXT) { rcPaint.left += uiHalfWidth; rcPaint.right += uiHalfWidth; } else { rcPaint.left -= uiHalfWidth; rcPaint.right -= uiHalfWidth; } /// /// we assume that bm.bmWidth is both the horizontal and the vertical /// dimension of the control bitmap and that it is square. bm.bmHeight /// seems to be the height of a striped bitmap because it is an absurdly /// high dimension value /// if((dwButtonStyle&BS_VCENTER) == BS_VCENTER) /// BS_VCENTER is BS_TOP|BS_BOTTOM { int h = RECTHEIGHT(rcPaint); rcPaint.top = (h - bmWidth) / 2; rcPaint.bottom = rcPaint.top + bmWidth; } else if (dwButtonStyle&BS_TOP) { rcPaint.bottom = rcPaint.top + bmWidth; } else if (dwButtonStyle&BS_BOTTOM) { rcPaint.top = rcPaint.bottom - bmWidth; } else // default: center the checkbox/radiobutton vertically { int h = RECTHEIGHT(rcPaint); rcPaint.top = (h - bmWidth) / 2; rcPaint.bottom = rcPaint.top + bmWidth; } m_theme.DrawThemeBackground(hTheme, hdcPaint, iPartId, iState, &rcPaint, nullptr); rcPaint = rcClient; m_theme.GetThemeBackgroundContentRect(hTheme, hdcPaint, iPartId, iState, &rcPaint, &rc); if(dwButtonStyle & BS_LEFTTEXT) rc.right -= bmWidth + 2 * GetSystemMetrics(SM_CXEDGE); else rc.left += bmWidth + 2 * GetSystemMetrics(SM_CXEDGE); DTTOPTS DttOpts = {sizeof(DTTOPTS)}; DttOpts.dwFlags = DTT_COMPOSITED | DTT_GLOWSIZE; DttOpts.crText = RGB(255, 255, 255); DttOpts.iGlowSize = 12; // Default value m_theme.DetermineGlowSize(&DttOpts.iGlowSize); HFONT hFontOld = (HFONT)SendMessage(hWnd, WM_GETFONT, 0L, 0L); if (hFontOld) hFontOld = (HFONT) SelectObject(hdcPaint, hFontOld); int iLen = GetWindowTextLength(hWnd); if (iLen) { iLen+=5; // 1 for terminating zero, 4 for DT_MODIFYSTRING LPWSTR szText = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*iLen); if (szText) { iLen = GetWindowTextW(hWnd, szText, iLen); if (iLen) { DWORD dwFlags = DT_SINGLELINE /*|DT_VCENTER*/; if (dwButtonStyle&BS_MULTILINE) { dwFlags|=DT_WORDBREAK; dwFlags&= ~(DT_SINGLELINE |DT_VCENTER); } if((dwButtonStyle&BS_CENTER) == BS_CENTER) /// BS_CENTER is BS_LEFT|BS_RIGHT dwFlags|=DT_CENTER; else if (dwButtonStyle&BS_LEFT) dwFlags|=DT_LEFT; else if (dwButtonStyle&BS_RIGHT) dwFlags|=DT_RIGHT; if((dwButtonStyle&BS_VCENTER) == BS_VCENTER) /// BS_VCENTER is BS_TOP|BS_BOTTOM dwFlags|=DT_VCENTER; else if (dwButtonStyle&BS_TOP) dwFlags|=DT_TOP; else if (dwButtonStyle&BS_BOTTOM) dwFlags|=DT_BOTTOM; else dwFlags|=DT_VCENTER; m_theme.DrawThemeTextEx(hTheme, hdcPaint, iPartId, iState, szText, -1, dwFlags, &rc, &DttOpts); /// /// if our control has the focus, we also have to draw the focus rectangle: /// if (bFocus) { /// /// now calculate the text size: /// RECT rcDraw = rc; /// /// we use GDI's good old DrawText, because it returns much more /// accurate data than DrawThemeTextEx, which takes the glow /// into account which we don't want: /// DrawTextW(hdcPaint, szText, -1, &rcDraw, dwFlags|DT_CALCRECT); if (dwFlags&DT_SINGLELINE) { dwFlags &= ~DT_VCENTER; RECT rcDrawTop; DrawTextW(hdcPaint, szText, -1, &rcDrawTop, dwFlags|DT_CALCRECT); rcDraw.top = rcDraw.bottom - RECTHEIGHT(rcDrawTop); } if (dwFlags & DT_RIGHT) { int iWidth = RECTWIDTH(rcDraw); rcDraw.right = rc.right; rcDraw.left = rcDraw.right - iWidth; } RECT rcFocus; IntersectRect(&rcFocus, &rc, &rcDraw); DrawFocusRect(&rcFocus, hdcPaint); } } LocalFree(szText); } } if (hFontOld) { SelectObject(hdcPaint, hFontOld); hFontOld = nullptr; } m_theme.EndBufferedPaint(hBufferedPaint, TRUE); } m_theme.CloseThemeData(hTheme); } } else if (BS_PUSHBUTTON == dwButtonType || BS_DEFPUSHBUTTON == dwButtonType) { /// /// it is a push button /// HTHEME hTheme = m_theme.OpenThemeData(hWnd, L"Button"); if (hTheme) { HDC hdcPaint = nullptr; BP_PAINTPARAMS params = { sizeof(BP_PAINTPARAMS) }; params.dwFlags = BPPF_ERASE; HPAINTBUFFER hBufferedPaint = m_theme.BeginBufferedPaint(hdc, &rcClient, BPBF_TOPDOWNDIB, ¶ms, &hdcPaint); if (hdcPaint) { PatBlt(hdcPaint, 0, 0, RECTWIDTH(rcClient), RECTHEIGHT(rcClient), BLACKNESS); m_theme.BufferedPaintSetAlpha(hBufferedPaint, &ps.rcPaint, 0x00); int iState = CBS_UNCHECKEDNORMAL; LRESULT dwCheckState = SendMessage(hWnd, BM_GETCHECK, 0, 0L); POINT pt; RECT rc; GetWindowRect(hWnd, &rc); GetCursorPos(&pt); BOOL bHot = PtInRect(&rc, pt); BOOL bFocus = GetFocus() == hWnd; int iPartId = BP_PUSHBUTTON; if (dwButtonStyle == BS_RADIOBUTTON || dwButtonStyle == BS_AUTORADIOBUTTON) iPartId = BP_RADIOBUTTON; iState = GetStateFromBtnState(dwStyle, bHot, bFocus, dwCheckState, iPartId, GetCapture() == hWnd); /// /// we have to use the whole client area, otherwise we get only partially /// drawn areas: /// RECT rcPaint = rcClient; m_theme.DrawThemeBackground(hTheme, hdcPaint, iPartId, iState, &rcPaint, nullptr); m_theme.GetThemeBackgroundContentRect(hTheme, hdcPaint, iPartId, iState, &rcPaint, &rc); DTTOPTS DttOpts = {sizeof(DTTOPTS)}; DttOpts.dwFlags = DTT_COMPOSITED; DttOpts.crText = RGB(255, 255, 255); DttOpts.dwFlags |= DTT_GLOWSIZE; DttOpts.iGlowSize = 12; // Default value m_theme.DetermineGlowSize(&DttOpts.iGlowSize); HFONT hFontOld = (HFONT)SendMessage(hWnd, WM_GETFONT, 0L, 0L); if (hFontOld) hFontOld = (HFONT) SelectObject(hdcPaint, hFontOld); int iLen = GetWindowTextLength(hWnd); if (iLen) { iLen+=5; // 1 for terminating zero, 4 for DT_MODIFYSTRING LPWSTR szText = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*iLen); if (szText) { iLen = GetWindowTextW(hWnd, szText, iLen); if (iLen) { DWORD dwFlags = DT_SINGLELINE | DT_CENTER | DT_VCENTER; m_theme.DrawThemeTextEx(hTheme, hdcPaint, iPartId, iState, szText, -1, dwFlags, &rc, &DttOpts); /// /// if our control has the focus, we also have to draw the focus rectangle: /// if (bFocus) { RECT rcDraw = rcClient; InflateRect(&rcDraw, -3, -3); DrawFocusRect(&rcDraw, hdcPaint); } } LocalFree(szText); } } if (hFontOld) { SelectObject(hdcPaint, hFontOld); hFontOld = nullptr; } m_theme.EndBufferedPaint(hBufferedPaint, TRUE); } m_theme.CloseThemeData(hTheme); } } else //PaintControl(hWnd, hdc, &ps.rcPaint, (m_dwFlags & WD_DRAW_BORDER) != 0); PaintControl(hWnd, hdc, &ps.rcPaint, false); } EndPaint(hWnd, &ps); return 0; } break; case WM_DESTROY: case WM_NCDESTROY: RemoveWindowSubclass(hWnd, SubclassProc, Button); subclassedControls.erase(hWnd); break; } return DefSubclassProc(hWnd, uMsg, wParam, lParam); } LRESULT AeroControlBase::ProgressbarWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_ENABLE: case WM_STYLECHANGED: { LRESULT res = DefSubclassProc(hWnd, uMsg, wParam, lParam); InvalidateRgn(hWnd, nullptr, FALSE); return res; } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); RECT rc; GetWindowRect(hWnd, &rc); MapWindowPoints(nullptr, hWnd, (LPPOINT) &rc, 2); if (hdc) { PaintControl(hWnd, hdc, &rc, false); BP_PAINTPARAMS params = { sizeof(BP_PAINTPARAMS) }; params.dwFlags = 0L; HDC hdcPaint = nullptr; HPAINTBUFFER hBufferedPaint = m_theme.BeginBufferedPaint(hdc, &rc, BPBF_TOPDOWNDIB, ¶ms, &hdcPaint); if (hdcPaint) { COLORREF cr = RGB(0x00, 0x00, 0x00); SetPixel(hdcPaint, 0, 0, cr); SetPixel(hdcPaint, 0, RECTHEIGHT(rc) - 1, cr); SetPixel(hdcPaint, RECTWIDTH(rc) - 1, 0, cr); SetPixel(hdcPaint, RECTWIDTH(rc) - 1, RECTHEIGHT(rc) - 1, cr); m_theme.EndBufferedPaint(hBufferedPaint, TRUE); } } EndPaint(hWnd, &ps); return 1; } break; case WM_NCDESTROY: case WM_DESTROY: RemoveWindowSubclass(hWnd, SubclassProc, Static); subclassedControls.erase(hWnd); break; } return DefSubclassProc(hWnd, uMsg, wParam, lParam); } void AeroControlBase::FillRect(LPRECT prc, HDC hdcPaint, Color clr) const { auto pBrush = std::make_unique(clr); if (pBrush) { auto myGraphics = std::make_unique(hdcPaint); if (myGraphics) { myGraphics->FillRectangle(pBrush.get(), prc->left, prc->top, prc->right - 1 - prc->left, prc->bottom - 1 - prc->top); } } } int AeroControlBase::GetStateFromBtnState(LONG_PTR dwStyle, BOOL bHot, BOOL bFocus, LRESULT dwCheckState, int iPartId, BOOL bHasMouseCapture) const { int iState = 0; switch (iPartId) { case BP_PUSHBUTTON: iState = PBS_NORMAL; if (dwStyle&WS_DISABLED) iState = PBS_DISABLED; else { if (dwStyle&BS_DEFPUSHBUTTON) iState = PBS_DEFAULTED; if (bHasMouseCapture && bHot) iState = PBS_PRESSED; else if (bHasMouseCapture || bHot) iState = PBS_HOT; } break; case BP_GROUPBOX: iState = (dwStyle & WS_DISABLED)?GBS_DISABLED:GBS_NORMAL; break; case BP_RADIOBUTTON: iState = RBS_UNCHECKEDNORMAL; switch(dwCheckState) { case BST_CHECKED: if (dwStyle&WS_DISABLED) iState = RBS_CHECKEDDISABLED; else if (bFocus) iState = RBS_CHECKEDPRESSED; else if (bHot) iState = RBS_CHECKEDHOT; else iState = RBS_CHECKEDNORMAL; break; case BST_UNCHECKED: if (dwStyle&WS_DISABLED) iState = RBS_UNCHECKEDDISABLED; else if (bFocus) iState = RBS_UNCHECKEDPRESSED; else if (bHot) iState = RBS_UNCHECKEDHOT; else iState = RBS_UNCHECKEDNORMAL; break; } break; case BP_CHECKBOX: switch(dwCheckState) { case BST_CHECKED: if (dwStyle&WS_DISABLED) iState = CBS_CHECKEDDISABLED; else if (bFocus) iState = CBS_CHECKEDPRESSED; else if (bHot) iState = CBS_CHECKEDHOT; else iState = CBS_CHECKEDNORMAL; break; case BST_INDETERMINATE: if (dwStyle&WS_DISABLED) iState = CBS_MIXEDDISABLED; else if (bFocus) iState = CBS_MIXEDPRESSED; else if (bHot) iState = CBS_MIXEDHOT; else iState = CBS_MIXEDNORMAL; break; case BST_UNCHECKED: if (dwStyle&WS_DISABLED) iState = CBS_UNCHECKEDDISABLED; else if (bFocus) iState = CBS_UNCHECKEDPRESSED; else if (bHot) iState = CBS_UNCHECKEDHOT; else iState = CBS_UNCHECKEDNORMAL; break; } break; default: break; } return iState; } void AeroControlBase::DrawRect(LPRECT prc, HDC hdcPaint, DashStyle dashStyle, Color clr, REAL width) const { auto myPen = std::make_unique(clr, width); myPen->SetDashStyle(dashStyle); auto myGraphics = std::make_unique(hdcPaint); myGraphics->DrawRectangle(myPen.get(), prc->left, prc->top, prc->right - 1 - prc->left, prc->bottom - 1 - prc->top); } void AeroControlBase::DrawFocusRect(LPRECT prcFocus, HDC hdcPaint) { DrawRect(prcFocus, hdcPaint, DashStyleDot, Color(0xFF, 0,0,0), 1.0); }; void AeroControlBase::PaintControl(HWND hWnd, HDC hdc, RECT* prc, bool bDrawBorder) { HDC hdcPaint = nullptr; if (bDrawBorder) InflateRect(prc, 1, 1); HPAINTBUFFER hBufferedPaint = m_theme.BeginBufferedPaint(hdc, prc, BPBF_TOPDOWNDIB, nullptr, &hdcPaint); if (hdcPaint) { RECT rc; GetWindowRect(hWnd, &rc); PatBlt(hdcPaint, 0, 0, RECTWIDTH(rc), RECTHEIGHT(rc), BLACKNESS); m_theme.BufferedPaintSetAlpha(hBufferedPaint, &rc, 0x00); /// /// first blit white so list ctrls don't look ugly: /// PatBlt(hdcPaint, 0, 0, RECTWIDTH(rc), RECTHEIGHT(rc), WHITENESS); if (bDrawBorder) InflateRect(prc, -1, -1); // Tell the control to paint itself in our memory buffer SendMessage(hWnd, WM_PRINTCLIENT, (WPARAM) hdcPaint, PRF_CLIENT|PRF_ERASEBKGND |PRF_NONCLIENT|PRF_CHECKVISIBLE); if (bDrawBorder) { InflateRect(prc, 1, 1); FrameRect(hdcPaint, prc, (HBRUSH)GetStockObject(BLACK_BRUSH)); } // Make every pixel opaque m_theme.BufferedPaintMakeOpaque_(hBufferedPaint, prc); m_theme.EndBufferedPaint(hBufferedPaint, TRUE); } } BOOL AeroControlBase::GetEditBorderColor(HWND hWnd, COLORREF *pClr) { HTHEME hTheme = m_theme.OpenThemeData(hWnd, L"Edit"); if (hTheme) { m_theme.GetThemeColor(hTheme, EP_BACKGROUNDWITHBORDER, EBWBS_NORMAL, TMT_BORDERCOLOR, pClr); m_theme.CloseThemeData(hTheme); return TRUE; } return FALSE; } void AeroControlBase::DrawEditBorder(HWND hWnd) { LONG_PTR dwStyleEx = GetWindowLongPtr(hWnd, GWL_EXSTYLE); if(!(dwStyleEx&WS_EX_CLIENTEDGE)) return; COLORREF cr = RGB(0x00, 0x00, 0x00); GetEditBorderColor(hWnd, &cr); Color clr; clr.SetFromCOLORREF(cr); DrawSolidWndRectOnParent(hWnd, clr ); } void AeroControlBase::DrawSolidWndRectOnParent(HWND hWnd, Color clr) { RECT rcWnd; GetWindowRect(hWnd, &rcWnd); HWND hParent = GetParent(hWnd); if (hParent) { ScreenToClient(hParent, &rcWnd); HDC hdc = GetDC(hParent); if (hdc) { DrawRect(&rcWnd, hdc, DashStyleSolid, clr, 1.0); ReleaseDC(hWnd, hdc); } } } void AeroControlBase::ScreenToClient(HWND hWnd, LPRECT lprc) { POINT pt; pt.x = lprc->left; pt.y = lprc->top; ::ScreenToClient(hWnd, &pt); lprc->left = pt.x; lprc->top = pt.y; pt.x = lprc->right; pt.y = lprc->bottom; ::ScreenToClient(hWnd, &pt); lprc->right = pt.x; lprc->bottom = pt.y; } void AeroControlBase::GetRoundRectPath(GraphicsPath *pPath, const Rect& r, int dia) const { // diameter can't exceed width or height if(dia > r.Width) dia = r.Width; if(dia > r.Height) dia = r.Height; // define a corner Rect Corner(r.X, r.Y, dia, dia); // begin path pPath->Reset(); pPath->StartFigure(); // top left pPath->AddArc(Corner, 180, 90); // top right Corner.X += (r.Width - dia - 1); pPath->AddArc(Corner, 270, 90); // bottom right Corner.Y += (r.Height - dia - 1); pPath->AddArc(Corner, 0, 90); // bottom left Corner.X -= (r.Width - dia - 1); pPath->AddArc(Corner, 90, 90); // end path pPath->CloseFigure(); }