BambuStudio/slic3r/GUI/dark_mode.cpp

822 lines
23 KiB
C++
Raw Normal View History

2024-12-20 06:44:50 +00:00
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#include "wx/settings.h"
#include "wx/font.h"
#include "wx/msw/colour.h"
#include "dark_mode.hpp"
#include "dark_mode/dark_mode.hpp"
#include "dark_mode/UAHMenuBar.hpp"
#include <Shlwapi.h>
#include "windowsx.h"
#include "stdlib.h"
#ifdef __GNUC__
#include <cmath>
#define WINAPI_LAMBDA WINAPI
#else
#define WINAPI_LAMBDA
#endif
#ifdef __BORLANDC__
#pragma comment(lib, "uxtheme.lib")
#endif
namespace NppDarkMode
{
bool IsEnabled()
{
return g_darkModeEnabled;
}
bool IsSupported()
{
return g_darkModeSupported;
}
bool IsSystemMenuEnabled()
{
return g_SystemMenuEnabled;
}
COLORREF InvertLightness(COLORREF c)
{
WORD h = 0;
WORD s = 0;
WORD l = 0;
ColorRGBToHLS(c, &h, &l, &s);
l = 240 - l;
COLORREF invert_c = ColorHLSToRGB(h, l, s);
return invert_c;
}
COLORREF InvertLightnessSofter(COLORREF c)
{
WORD h = 0;
WORD s = 0;
WORD l = 0;
ColorRGBToHLS(c, &h, &l, &s);
l = std::min(240 - l, 211);
COLORREF invert_c = ColorHLSToRGB(h, l, s);
return invert_c;
}
COLORREF GetBackgroundColor()
{
return IsEnabled() ? RGB(0x2B, 0x2B, 0x2B) : wxSystemSettings::GetColour(wxSYS_COLOUR_MENUBAR).GetRGB();
}
COLORREF GetSofterBackgroundColor()
{
return IsEnabled() ? RGB(0x40, 0x40, 0x40) : RGB(0xD9, 0xD9, 0xD9); //RGB(0x78, 0x78, 0x78);
}
COLORREF GetTextColor()
{
return IsEnabled() ? RGB(0xF0, 0xF0, 0xF0) : wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT).GetRGB();
}
COLORREF GetHotTextColor()
{
return IsEnabled() ? RGB(0xFF, 0xFF, 0xFF) : wxSystemSettings::GetColour(wxSYS_COLOUR_ACTIVEBORDER).GetRGB();
}
COLORREF GetSofterTextColor()
{
return IsEnabled() ? RGB(0xF0, 0xF0, 0xF0) : RGB(0x64, 0x64, 0x64);
}
COLORREF GetDarkerTextColor()
{
return RGB(0xC0, 0xC0, 0xC0);
}
COLORREF GetEdgeColor()
{
return RGB(0x80, 0x80, 0x80);
}
HBRUSH GetBackgroundBrush()
{
static HBRUSH g_hbrBackground = ::CreateSolidBrush(GetBackgroundColor());
return g_hbrBackground;
}
HPEN GetDarkerTextPen()
{
static HPEN g_hpDarkerText = ::CreatePen(PS_SOLID, 1, GetDarkerTextColor());
return g_hpDarkerText;
}
HPEN GetEdgePen()
{
static HPEN g_hpEdgePen = ::CreatePen(PS_SOLID, 1, GetEdgeColor());
return g_hpEdgePen;
}
HBRUSH GetSofterBackgroundBrush()
{
static HBRUSH g_hbrSofterBackground = ::CreateSolidBrush(GetSofterBackgroundColor());
return g_hbrSofterBackground;
}
// handle events
bool OnSettingChange(HWND hwnd, LPARAM lParam) // true if dark mode toggled
{
bool toggled = false;
if (IsColorSchemeChangeMessage(lParam))
{
bool darkModeWasEnabled = g_darkModeEnabled;
g_darkModeEnabled = _ShouldAppsUseDarkMode() && !IsHighContrast();
NppDarkMode::RefreshTitleBarThemeColor(hwnd);
if (!!darkModeWasEnabled != !!g_darkModeEnabled) {
toggled = true;
RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN);
}
}
return toggled;
}
// processes messages related to UAH / custom menubar drawing.
// return true if handled, false to continue with normal processing in your wndproc
bool UAHWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* lr)
{
if (!IsEnabled())
return false;
static HTHEME g_menuTheme = nullptr;
UNREFERENCED_PARAMETER(wParam);
switch (message)
{
case WM_UAHDRAWMENU:
{
UAHMENU* pUDM = (UAHMENU*)lParam;
RECT rc = { 0 };
// get the menubar rect
{
MENUBARINFO mbi = { sizeof(mbi) };
GetMenuBarInfo(hWnd, OBJID_MENU, 0, &mbi);
RECT rcWindow;
GetWindowRect(hWnd, &rcWindow);
// the rcBar is offset by the window rect
rc = mbi.rcBar;
OffsetRect(&rc, -rcWindow.left, -rcWindow.top);
rc.top -= 1;
}
FillRect(pUDM->hdc, &rc, GetBackgroundBrush());
*lr = 0;
return true;
}
case WM_UAHDRAWMENUITEM:
{
UAHDRAWMENUITEM* pUDMI = (UAHDRAWMENUITEM*)lParam;
// get the menu item string
wchar_t menuString[256] = { 0 };
MENUITEMINFO mii = { sizeof(mii), MIIM_STRING };
{
mii.dwTypeData = menuString;
mii.cch = (sizeof(menuString) / 2) - 1;
GetMenuItemInfo(pUDMI->um.hmenu, pUDMI->umi.iPosition, TRUE, &mii);
}
// get the item state for drawing
DWORD dwFlags = DT_CENTER | DT_SINGLELINE | DT_VCENTER;
int iTextStateID = MPI_NORMAL;
int iBackgroundStateID = MPI_NORMAL;
{
if ((pUDMI->dis.itemState & ODS_INACTIVE) | (pUDMI->dis.itemState & ODS_DEFAULT)) {
// normal display
iTextStateID = MPI_NORMAL;
iBackgroundStateID = MPI_NORMAL;
}
if (pUDMI->dis.itemState & ODS_HOTLIGHT) {
// hot tracking
iTextStateID = MPI_HOT;
iBackgroundStateID = MPI_HOT;
}
if (pUDMI->dis.itemState & ODS_SELECTED) {
// clicked -- MENU_POPUPITEM has no state for this, though MENU_BARITEM does
iTextStateID = MPI_HOT;
iBackgroundStateID = MPI_HOT;
}
if ((pUDMI->dis.itemState & ODS_GRAYED) || (pUDMI->dis.itemState & ODS_DISABLED)) {
// disabled / grey text
iTextStateID = MPI_DISABLED;
iBackgroundStateID = MPI_DISABLED;
}
if (pUDMI->dis.itemState & ODS_NOACCEL) {
dwFlags |= DT_HIDEPREFIX;
}
}
if (!g_menuTheme) {
g_menuTheme = OpenThemeData(hWnd, L"Menu");
}
if (iBackgroundStateID == MPI_NORMAL || iBackgroundStateID == MPI_DISABLED) {
FillRect(pUDMI->um.hdc, &pUDMI->dis.rcItem, NppDarkMode::GetBackgroundBrush());
}
else if (iBackgroundStateID == MPI_HOT) {
FillRect(pUDMI->um.hdc, &pUDMI->dis.rcItem, NppDarkMode::GetSofterBackgroundBrush());
}
else {
DrawThemeBackground(g_menuTheme, pUDMI->um.hdc, MENU_POPUPITEM, iBackgroundStateID, &pUDMI->dis.rcItem, nullptr);
}
DTTOPTS dttopts = { sizeof(dttopts) };
if (iTextStateID == MPI_NORMAL || iTextStateID == MPI_HOT) {
dttopts.dwFlags |= DTT_TEXTCOLOR;
dttopts.crText = NppDarkMode::GetTextColor();
}
DrawThemeTextEx(g_menuTheme, pUDMI->um.hdc, MENU_POPUPITEM, iTextStateID, menuString, mii.cch, dwFlags, &pUDMI->dis.rcItem, &dttopts);
*lr = 0;
return true;
}
case WM_THEMECHANGED:
{
if (g_menuTheme) {
CloseThemeData(g_menuTheme);
g_menuTheme = nullptr;
}
// continue processing in main wndproc
return false;
}
default:
return false;
}
}
void DrawUAHMenuNCBottomLine(HWND hWnd)
{
MENUBARINFO mbi = { sizeof(mbi) };
if (!GetMenuBarInfo(hWnd, OBJID_MENU, 0, &mbi))
{
return;
}
RECT rcClient = { 0 };
GetClientRect(hWnd, &rcClient);
MapWindowPoints(hWnd, nullptr, (POINT*)&rcClient, 2);
RECT rcWindow = { 0 };
GetWindowRect(hWnd, &rcWindow);
OffsetRect(&rcClient, -rcWindow.left, -rcWindow.top);
// the rcBar is offset by the window rect
RECT rcAnnoyingLine = rcClient;
rcAnnoyingLine.bottom = rcAnnoyingLine.top;
rcAnnoyingLine.top--;
HDC hdc = GetWindowDC(hWnd);
FillRect(hdc, &rcAnnoyingLine, GetBackgroundBrush());
ReleaseDC(hWnd, hdc);
}
// from DarkMode.h
void InitDarkMode(bool set_dark_mode, bool set_sys_menu)
{
::InitDarkMode();
g_SystemMenuEnabled = set_sys_menu;
SetDarkMode(set_dark_mode);
}
void SetDarkMode(bool dark_mode)
{
g_darkModeEnabled = dark_mode;
if (!IsSupported())
return;
AllowDarkModeForApp(dark_mode);
if (g_SystemMenuEnabled && _FlushMenuThemes)
_FlushMenuThemes();
FixDarkScrollBar();
}
void SetSystemMenuForApp(bool set_sys_menu)
{
if (IsSupported())
g_SystemMenuEnabled = set_sys_menu;
}
void AllowDarkModeForApp(bool allow)
{
if (IsSupported())
::AllowDarkModeForApp(allow);
}
bool AllowDarkModeForWindow(HWND hWnd, bool allow)
{
if (IsSupported())
return ::AllowDarkModeForWindow(hWnd, allow);
return false;
}
void RefreshTitleBarThemeColor(HWND hWnd)
{
if (IsSupported())
::RefreshTitleBarThemeColor(hWnd);
}
void EnableDarkScrollBarForWindowAndChildren(HWND hwnd)
{
if (IsSupported())
::EnableDarkScrollBarForWindowAndChildren(hwnd);
}
void SetDarkTitleBar(HWND hwnd)
{
if (!IsSupported())
return;
::AllowDarkModeForWindow(hwnd, IsEnabled());
::RefreshTitleBarThemeColor(hwnd);
SetDarkExplorerTheme(hwnd);
}
void SetDarkExplorerTheme(HWND hwnd)
{
if (IsSupported())
SetWindowTheme(hwnd, IsEnabled() ? L"DarkMode_Explorer" : nullptr, nullptr);
}
void SetDarkListView(HWND hwnd)
{
if (!IsSupported())
return;
bool useDark = IsEnabled();
if (HWND hHeader = ListView_GetHeader(hwnd)) {
_AllowDarkModeForWindow(hHeader, useDark);
SetWindowTheme(hHeader, useDark ? L"ItemsView" : nullptr, nullptr);
}
_AllowDarkModeForWindow(hwnd, useDark);
SetWindowTheme(hwnd, L"Explorer", nullptr);
}
void SetDarkListViewHeader(HWND hHeader)
{
if (!IsSupported())
return;
bool useDark = IsEnabled();
_AllowDarkModeForWindow(hHeader, useDark);
SetWindowTheme(hHeader, useDark ? L"ItemsView" : nullptr, nullptr);
}
int scaled(HWND hwnd, int val)
{
float scale = 1.0;
// Both GetDpiForWindow and GetDpiForSystem shall be supported since Windows 10, version 1607
if (_GetDpiForWindow && _GetDpiForSystem)
scale = float(_GetDpiForWindow(hwnd)) / _GetDpiForSystem();
return std::round(scale * val);
}
struct ButtonData
{
HTHEME hTheme = nullptr;
int iStateID = 0;
POINT mouse_pos{ -1,-1 };
~ButtonData()
{
closeTheme();
}
bool ensureTheme(HWND hwnd)
{
if (!hTheme)
{
hTheme = OpenThemeData(hwnd, L"Button");
}
return hTheme != nullptr;
}
void closeTheme()
{
if (hTheme)
{
CloseThemeData(hTheme);
hTheme = nullptr;
}
}
};
void renderButton(HWND hwnd, HDC hdc, HTHEME hTheme, int iPartID, int iStateID)
{
RECT rcClient = { 0 };
DWORD nStyle = GetWindowLong(hwnd, GWL_STYLE);
/* WCHAR szText[256] = { 0 };
DWORD nState = static_cast<DWORD>(SendMessage(hwnd, BM_GETSTATE, 0, 0));
DWORD uiState = static_cast<DWORD>(SendMessage(hwnd, WM_QUERYUISTATE, 0, 0));
HFONT hFont = nullptr;
HFONT hOldFont = nullptr;
HFONT hCreatedFont = nullptr;
LOGFONT lf = { 0 };
if (SUCCEEDED(GetThemeFont(hTheme, hdc, iPartID, iStateID, TMT_FONT, &lf)))
{
hCreatedFont = CreateFontIndirect(&lf);
hFont = hCreatedFont;
}
if (!hFont) {
hFont = reinterpret_cast<HFONT>(SendMessage(hwnd, WM_GETFONT, 0, 0));
}
hOldFont = static_cast<HFONT>(SelectObject(hdc, hFont));
DWORD dtFlags = DT_LEFT; // DT_LEFT is 0
dtFlags |= (nStyle & BS_MULTILINE) ? DT_WORDBREAK : DT_SINGLELINE;
dtFlags |= ((nStyle & BS_CENTER) == BS_CENTER) ? DT_CENTER : (nStyle & BS_RIGHT) ? DT_RIGHT : 0;
dtFlags |= ((nStyle & BS_VCENTER) == BS_VCENTER) ? DT_VCENTER : (nStyle & BS_BOTTOM) ? DT_BOTTOM : 0;
dtFlags |= (uiState & UISF_HIDEACCEL) ? DT_HIDEPREFIX : 0;
if (!(nStyle & BS_MULTILINE) && !(nStyle & BS_BOTTOM) && !(nStyle & BS_TOP))
{
dtFlags |= DT_VCENTER;
}
GetClientRect(hwnd, &rcClient);
GetWindowText(hwnd, szText, _countof(szText));
SIZE szBox = { 13, 13 };
GetThemePartSize(hTheme, hdc, iPartID, iStateID, NULL, TS_DRAW, &szBox);
RECT rcText = rcClient;
GetThemeBackgroundContentRect(hTheme, hdc, iPartID, iStateID, &rcClient, &rcText);
RECT rcBackground = rcClient;
if (dtFlags & DT_SINGLELINE)
{
rcBackground.top += (rcText.bottom - rcText.top - szBox.cy) / 2;
}
rcBackground.bottom = rcBackground.top + szBox.cy;
rcBackground.right = rcBackground.left + szBox.cx;
rcText.left = rcBackground.right + 3;
DrawThemeParentBackground(hwnd, hdc, &rcClient);
DrawThemeBackground(hTheme, hdc, iPartID, iStateID, &rcBackground, nullptr);
DTTOPTS dtto = { sizeof(DTTOPTS), DTT_TEXTCOLOR };
dtto.crText = iPartID == SBP_ARROWBTN ? GetSofterTextColor() : GetTextColor();
if (nStyle & WS_DISABLED)
{
dtto.crText = GetSofterBackgroundColor();
}
*/
GetClientRect(hwnd, &rcClient);
DrawThemeParentBackground(hwnd, hdc, &rcClient);
if (iPartID == SBP_ARROWBTN)
{
{
HBRUSH hbrush = ::CreateSolidBrush(RGB(0x64, 0x64, 0x64));
::FrameRect(hdc, &rcClient, hbrush);
::DeleteObject(hbrush);
}
COLORREF color = nStyle & WS_DISABLED ? GetSofterBackgroundColor() : GetSofterTextColor();
HPEN hPen = CreatePen(PS_SOLID, 1, color);
HPEN hOldPen = SelectPen(hdc, hPen);
HBRUSH hBrush = CreateSolidBrush(color);
HBRUSH hOldBrush = SelectBrush(hdc, hBrush);
// Up arrow
RECT rcFocus = rcClient;
rcFocus.bottom *= 0.5;
rcFocus.left += 1;
InflateRect(&rcFocus, -1, -1);
int triangle_edge = int(0.25 * (rcClient.right - rcClient.left));
int left_pos = triangle_edge + 1;
int shift_from_center = 0.5 * triangle_edge;
int bottom_pos = rcFocus.bottom - shift_from_center;
rcFocus.bottom += 1;
POINT vertices_up[] = { {left_pos, bottom_pos }, {left_pos + triangle_edge, bottom_pos - triangle_edge}, {left_pos + 2*triangle_edge, bottom_pos} };
Polygon(hdc, vertices_up, 3);
if (iStateID == ARROWBTNSTATES::ABS_UPHOT)
DrawFocusRect(hdc, &rcFocus);
// Down arrow
rcFocus = rcClient;
rcFocus.top = 0.5 * rcFocus.bottom;
rcFocus.left += 1;
InflateRect(&rcFocus, -1, -1);
int top_pos = rcFocus.top + shift_from_center;
POINT vertices_down[] = { {left_pos, top_pos }, {left_pos + triangle_edge, top_pos + triangle_edge}, {left_pos + 2 * triangle_edge, top_pos} };
Polygon(hdc, vertices_down, 3);
if (iStateID == ARROWBTNSTATES::ABS_DOWNHOT)
DrawFocusRect(hdc, &rcFocus);
SelectBrush(hdc, hOldBrush);
DeleteObject(hBrush);
SelectPen(hdc, hOldPen);
DeleteObject(hPen);
}
/* else
DrawThemeTextEx(hTheme, hdc, iPartID, iStateID, szText, -1, dtFlags, &rcText, &dtto);
if ((nState & BST_FOCUS) && !(uiState & UISF_HIDEFOCUS))
{
RECT rcTextOut = rcText;
dtto.dwFlags |= DTT_CALCRECT;
DrawThemeTextEx(hTheme, hdc, iPartID, iStateID, szText, -1, dtFlags | DT_CALCRECT, &rcTextOut, &dtto);
RECT rcFocus = rcTextOut;
rcFocus.bottom++;
rcFocus.left--;
rcFocus.right++;
DrawFocusRect(hdc, &rcFocus);
}
if (hCreatedFont) DeleteObject(hCreatedFont);
SelectObject(hdc, hOldFont);
*/ }
void paintButton(HWND hwnd, HDC hdc, ButtonData& buttonData)
{
DWORD nState = static_cast<DWORD>(SendMessage(hwnd, BM_GETSTATE, 0, 0));
DWORD nStyle = GetWindowLong(hwnd, GWL_STYLE);
DWORD nButtonStyle = nStyle & 0xF;
int iPartID = BP_CHECKBOX;
if (nButtonStyle == BS_CHECKBOX || nButtonStyle == BS_AUTOCHECKBOX)
{
iPartID = BP_CHECKBOX;
}
else if (nButtonStyle == BS_RADIOBUTTON || nButtonStyle == BS_AUTORADIOBUTTON)
{
iPartID = BP_RADIOBUTTON;
}
else if (nButtonStyle == BS_AUTO3STATE)
{
iPartID = SBP_ARROWBTN;
}
else
{
assert(false);
}
// states of BP_CHECKBOX and BP_RADIOBUTTON are the same
int iStateID = RBS_UNCHECKEDNORMAL;
if (nStyle & WS_DISABLED) iStateID = RBS_UNCHECKEDDISABLED;
else if (nState & BST_PUSHED) iStateID = RBS_UNCHECKEDPRESSED;
else if (nState & BST_HOT) iStateID = RBS_UNCHECKEDHOT;
if (nState & BST_CHECKED) iStateID += 4;
if (BufferedPaintRenderAnimation(hwnd, hdc))
{
return;
}
BP_ANIMATIONPARAMS animParams = { sizeof(animParams) };
animParams.style = BPAS_LINEAR;
if (iStateID != buttonData.iStateID)
{
GetThemeTransitionDuration(buttonData.hTheme, iPartID, buttonData.iStateID, iStateID, TMT_TRANSITIONDURATIONS, &animParams.dwDuration);
}
RECT rcClient = { 0 };
GetClientRect(hwnd, &rcClient);
HDC hdcFrom = nullptr;
HDC hdcTo = nullptr;
HANIMATIONBUFFER hbpAnimation = BeginBufferedAnimation(hwnd, hdc, &rcClient, BPBF_COMPATIBLEBITMAP, nullptr, &animParams, &hdcFrom, &hdcTo);
if (hbpAnimation)
{
if (hdcFrom)
{
renderButton(hwnd, hdcFrom, buttonData.hTheme, iPartID, buttonData.iStateID);
}
if (hdcTo)
{
if (iPartID == SBP_ARROWBTN && (buttonData.iStateID == ARROWBTNSTATES::ABS_DOWNHOT || buttonData.iStateID == ARROWBTNSTATES::ABS_UPHOT) )
iStateID = buttonData.iStateID;
renderButton(hwnd, hdcTo, buttonData.hTheme, iPartID, iStateID);
}
buttonData.iStateID = iStateID;
EndBufferedAnimation(hbpAnimation, TRUE);
}
else
{
renderButton(hwnd, hdc, buttonData.hTheme, iPartID, iStateID);
buttonData.iStateID = iStateID;
}
}
constexpr UINT_PTR g_buttonSubclassID = 42;
LRESULT CALLBACK ButtonSubclass(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam,
UINT_PTR uIdSubclass,
DWORD_PTR dwRefData
)
{
UNREFERENCED_PARAMETER(uIdSubclass);
auto pButtonData = reinterpret_cast<ButtonData*>(dwRefData);
auto paint = [pButtonData](HWND hWnd, WPARAM wParam)
{
PAINTSTRUCT ps = { 0 };
HDC hdc = reinterpret_cast<HDC>(wParam);
if (!hdc)
{
hdc = BeginPaint(hWnd, &ps);
}
paintButton(hWnd, hdc, *pButtonData);
if (ps.hdc)
{
EndPaint(hWnd, &ps);
}
};
switch (uMsg)
{
case WM_UPDATEUISTATE:
if (HIWORD(wParam) & (UISF_HIDEACCEL | UISF_HIDEFOCUS))
{
InvalidateRect(hWnd, nullptr, FALSE);
}
break;
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, ButtonSubclass, g_buttonSubclassID);
delete pButtonData;
break;
case WM_ERASEBKGND:
if (IsEnabled() && pButtonData->ensureTheme(hWnd))
{
return TRUE;
}
else
{
break;
}
case WM_THEMECHANGED:
pButtonData->closeTheme();
break;
case WM_PRINTCLIENT:
case WM_PAINT:
if (pButtonData->ensureTheme(hWnd))
{
paint(hWnd, wParam);
return 0;
}
break;
case WM_MOUSEMOVE:
{
DWORD nStyle = GetWindowLong(hWnd, GWL_STYLE);
DWORD nButtonStyle = nStyle & 0xF;
if (nButtonStyle == BS_AUTO3STATE)
{
int xPos = GET_X_LPARAM(lParam);
int yPos = GET_Y_LPARAM(lParam);
RECT rcClient = { 0 };
GetClientRect(hWnd, &rcClient);
int iStateID = 0;
if (xPos <= rcClient.top || xPos >= rcClient.bottom ||
yPos <= rcClient.left || yPos >= rcClient.right)
iStateID = ARROWBTNSTATES::ABS_UPNORMAL;
else
iStateID = yPos > 0.5 * rcClient.bottom ? ARROWBTNSTATES::ABS_DOWNHOT : ARROWBTNSTATES::ABS_UPHOT;
if (pButtonData->iStateID != iStateID)
{
pButtonData->iStateID = iStateID;
paint(hWnd, wParam);
}
return 0;
}
break;
}
case WM_KEYUP:
case WM_CHAR:
case WM_KEYDOWN:
case WM_VSCROLL:
{
DWORD nState = GET_KEYSTATE_WPARAM(wParam);
if (nState == VK_UP || nState == VK_DOWN) {
int iStateID = nState == VK_DOWN ? ARROWBTNSTATES::ABS_DOWNHOT : ARROWBTNSTATES::ABS_UPHOT;
if (pButtonData->iStateID != iStateID)
{
pButtonData->iStateID = iStateID;
paint(hWnd, wParam);
}
return 0;
}
break;
}
case WM_SIZE:
case WM_DESTROY:
BufferedPaintStopAllAnimations(hWnd);
break;
case WM_ENABLE:
if (IsEnabled())
{
// skip the button's normal wndproc so it won't redraw out of wm_paint
LRESULT lr = DefWindowProc(hWnd, uMsg, wParam, lParam);
InvalidateRect(hWnd, nullptr, FALSE);
return lr;
}
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
void subclassButtonControl(HWND hwnd)
{
DWORD_PTR pButtonData = reinterpret_cast<DWORD_PTR>(new ButtonData());
SetWindowSubclass(hwnd, ButtonSubclass, g_buttonSubclassID, pButtonData);
}
void AutoSubclassAndThemeChildControls(HWND hwndParent, bool subclass, bool theme)
{
if (!IsSupported())
return;
struct Params
{
const wchar_t* themeClassName = nullptr;
bool subclass = false;
bool theme = false;
};
Params p{
IsEnabled() ? L"DarkMode_Explorer" : nullptr
, subclass
, theme
};
EnumChildWindows(hwndParent, [](HWND hwnd, LPARAM lParam) WINAPI_LAMBDA{
auto & p = *reinterpret_cast<Params*>(lParam);
const size_t classNameLen = 16;
TCHAR className[classNameLen] = { 0 };
GetClassName(hwnd, className, classNameLen);
if (wcscmp(className, UPDOWN_CLASS) == 0)
{
auto nButtonStyle = ::GetWindowLongPtr(hwnd, GWL_STYLE) & 0xF;
if (nButtonStyle == BS_AUTO3STATE && p.subclass)
{
subclassButtonControl(hwnd);
}
return TRUE;
}
return TRUE;
}, reinterpret_cast<LPARAM>(&p));
}
}