From 1069c22d0da82ba81a9fb1242a808e0a65316b55 Mon Sep 17 00:00:00 2001 From: Jonas Kümmerlin Date: Sat, 23 May 2020 12:28:31 +0200 Subject: move checkbox into button --- alf/alfbutton.cpp | 592 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 506 insertions(+), 86 deletions(-) (limited to 'alf/alfbutton.cpp') diff --git a/alf/alfbutton.cpp b/alf/alfbutton.cpp index 2c67074..e63b5ce 100644 --- a/alf/alfbutton.cpp +++ b/alf/alfbutton.cpp @@ -10,6 +10,20 @@ #define PBS_DEFAULTED 5 #define PBS_DEFAULTED_ANIMATING 6 +#define BP_CHECKBOX 3 +#define CBS_UNCHECKEDNORMAL 1 +#define CBS_UNCHECKEDHOT 2 +#define CBS_UNCHECKEDPRESSED 3 +#define CBS_UNCHECKEDDISABLED 4 +#define CBS_CHECKEDNORMAL 5 +#define CBS_CHECKEDHOT 6 +#define CBS_CHECKEDPRESSED 7 +#define CBS_CHECKEDDISABLED 8 +#define CBS_MIXEDNORMAL 9 +#define CBS_MIXEDHOT 10 +#define CBS_MIXEDPRESSED 11 +#define CBS_MIXEDDISABLED 12 + #define ALF_NTBTN_FLAG_UXTHEME 1 #define ALF_NTBTN_FLAG_IS_DISABLED 2 #define ALF_NTBTN_FLAG_IS_DEFAULT 4 @@ -19,6 +33,10 @@ #define ALF_NTBTN_FLAG_HIDEFOCUS 64 #define ALF_NTBTN_FLAG_HIDEACCEL 128 #define ALF_NTBTN_FLAG_DEFAULT_ANIMATING 256 +#define ALF_NTBTN_FLAG_IS_CHECKED 512 +#define ALF_NTBTN_FLAG_DRAW_CHECKBOX 1024 +#define ALF_NTBTN_FLAG_DRAW_RADIOBTN 2048 +#define ALF_NTBTN_FLAG_DRAW_TRISTATE 4096 typedef struct { DWORD drawFlags; @@ -28,9 +46,12 @@ typedef struct { DWORD uxDrawFlagsCurrent; DWORD uxDrawFlagsPrev; DWORD uxDefaultAnimationDuration; + int uxPartPrev; + int uxPartCurrent; int dpi; HFONT font; WCHAR *text; + ALFColor bgcolor; } ALFNtButtonPriv; TCHAR *_alf_buttonClass = NULL; @@ -115,6 +136,21 @@ ALF_Button_DrawDisabledText31(HDC hdc, return r; } +// FIXME! this doesn’t really look correct +static int +ALF_Button_CalcCheckboxTextSpace(HDC hdc) +{ + SIZE s = {0,0}; + GetTextExtentPoint(hdc, TEXT("0"), 1, &s); + + s.cx /= 2; + + if (s.cx < 2) + s.cx = 2; + + return s.cx; +} + static ALFNtButtonPriv * ALF_NtButton_InitializePriv(CREATESTRUCTW *cs) @@ -125,6 +161,7 @@ ALF_NtButton_InitializePriv(CREATESTRUCTW *cs) priv->uxStateCurrent = -1; priv->uxStatePrev = -1; priv->dpi = 96; + priv->bgcolor = ALF_COLOR_SYS(COLOR_BTNFACE); SIZE_T l = (SIZE_T)lstrlenW(cs->lpszName); priv->text = ALF_New(WCHAR, l + 1); @@ -205,19 +242,35 @@ ALF_NtButton_CalculateSize(HWND hwnd, ALFNtButtonPriv *priv, SIZE *pSize) DrawTextW(hdc, priv->text, -1, &r, format); - int xpadding = ALF_Compat_GetSystemMetricsForDpi(SM_CXEDGE, - (UINT)priv->dpi) * 2 + 6; - int ypadding = ALF_Compat_GetSystemMetricsForDpi(SM_CYEDGE, - (UINT)priv->dpi) * 2 + 4; + if (priv->drawFlags & ALF_NTBTN_FLAG_DRAW_CHECKBOX) { + int checkwidth = 12 * priv->dpi / 96 + 1; - if (pSize->cx < r.right - r.left + xpadding) { - pSize->cx = r.right - r.left + xpadding; - } - if (pSize->cy < r.bottom - r.top + ypadding) { - pSize->cy = r.bottom - r.top + ypadding; - } - if (pSize->cx < pSize->cy) { - pSize->cx = pSize->cy; + int space = ALF_Button_CalcCheckboxTextSpace(hdc); + int cx = checkwidth + space + r.right - r.left + 1; + int cy = r.bottom - r.top; + if (cy < checkwidth) + cy = checkwidth; + + if (pSize->cx < cx) + pSize->cx = cx; + + if (pSize->cy < cy) + pSize->cy = cy; + } else { + int xpadding = ALF_Compat_GetSystemMetricsForDpi(SM_CXEDGE, + (UINT)priv->dpi) * 2 + 6; + int ypadding = ALF_Compat_GetSystemMetricsForDpi(SM_CYEDGE, + (UINT)priv->dpi) * 2 + 4; + + if (pSize->cx < r.right - r.left + xpadding) { + pSize->cx = r.right - r.left + xpadding; + } + if (pSize->cy < r.bottom - r.top + ypadding) { + pSize->cy = r.bottom - r.top + ypadding; + } + if (pSize->cx < pSize->cy) { + pSize->cx = pSize->cy; + } } SelectFont(hdc, oldFont); @@ -240,46 +293,162 @@ ALF_NtButton_DefaultAnimatingTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, D } static void -ALF_NtButton_RenderUxtheme(HWND hwnd, ALFNtButtonPriv *priv, int uxstate, DWORD drawFlags, HDC hDC, RECT *rcPaint) +ALF_NtButton_RenderUxtheme(HWND hwnd, ALFNtButtonPriv *priv, int uxpart, int uxstate, DWORD drawFlags, HDC hDC, RECT *rcPaint) { HFONT oldfont = SelectFont(hDC, priv->font); - if (ALF_Compat_IsThemeBackgroundPartiallyTransparent(priv->hTheme, BP_PUSHBUTTON, uxstate)) { - ALF_Compat_DrawThemeParentBackground(hwnd, hDC, rcPaint); - } - RECT r; GetClientRect(hwnd, &r); - ALF_Compat_DrawThemeBackground(priv->hTheme, hDC, BP_PUSHBUTTON, uxstate, &r, rcPaint); + if (uxpart == BP_CHECKBOX) { + if (priv->bgcolor == ALF_COLOR_TRANSPARENT) { + ALF_Compat_DrawThemeParentBackground(hwnd, hDC, rcPaint); + } else { + ALF_FillRect(hDC, rcPaint, priv->bgcolor); + } - RECT content = r; - ALF_Compat_GetThemeBackgroundContentRect(priv->hTheme, hDC, BP_PUSHBUTTON, uxstate, &r, &content); + int checkwidth = 12 * priv->dpi / 96 + 1; - if ((drawFlags & ALF_NTBTN_FLAG_IS_FOCUSED) && !(drawFlags & ALF_NTBTN_FLAG_HIDEFOCUS)) - DrawFocusRect(hDC, &content); + RECT rcCheckmark = { 0, 0, checkwidth, checkwidth }; + rcCheckmark.top += (r.bottom - r.top - checkwidth) / 2; + rcCheckmark.bottom = rcCheckmark.top + checkwidth; - UINT style = DT_CENTER | DT_NOCLIP; + int space = ALF_Button_CalcCheckboxTextSpace(hDC); - if (drawFlags & ALF_NTBTN_FLAG_HIDEACCEL) - style |= DT_HIDEPREFIX; + RECT rcText = { checkwidth + space, + 0, + r.right - r.left, + r.bottom - r.top }; - RECT textbounds = content; - ALF_Compat_GetThemeTextExtent(priv->hTheme, hDC, BP_PUSHBUTTON, uxstate, priv->text, -1, style, &content, &textbounds); + ALF_Compat_DrawThemeBackground(priv->hTheme, hDC, BP_CHECKBOX, uxstate, &rcCheckmark, rcPaint); - RECT texttarget = content; - texttarget.top += ((content.bottom - content.top) - (textbounds.bottom - textbounds.top)) / 2; - texttarget.left += ((content.right - content.left) - (textbounds.right - textbounds.left)) / 2; - texttarget.right = texttarget.left + (textbounds.right - textbounds.left); - texttarget.bottom = texttarget.top + (textbounds.bottom - textbounds.top); + RECT textbounds = rcText; + DrawTextW(hDC, priv->text, -1, &textbounds, DT_LEFT | DT_CALCRECT | DT_EXPANDTABS); + + RECT texttarget = rcText; + texttarget.top += (texttarget.bottom - texttarget.top - textbounds.bottom + textbounds.top) / 2; + texttarget.bottom = texttarget.top + (textbounds.bottom - textbounds.top); + texttarget.right = texttarget.left + textbounds.right - textbounds.left; + + UINT style = DT_LEFT | DT_NOCLIP | DT_EXPANDTABS; + + if (priv->drawFlags & ALF_NTBTN_FLAG_HIDEACCEL) + style |= DT_HIDEPREFIX; + + ALF_Compat_DrawThemeText(priv->hTheme, hDC, BP_CHECKBOX, uxstate, priv->text, -1, style, 0, &texttarget); + + if ((priv->drawFlags & ALF_NTBTN_FLAG_IS_FOCUSED) && !(priv->drawFlags & ALF_NTBTN_FLAG_HIDEFOCUS)) { + RECT f = texttarget; + InflateRect(&f, 1, 0); + DrawFocusRect(hDC, &f); + } + } else { + if (ALF_Compat_IsThemeBackgroundPartiallyTransparent(priv->hTheme, uxpart, uxstate)) { + if (priv->bgcolor == ALF_COLOR_TRANSPARENT) { + ALF_Compat_DrawThemeParentBackground(hwnd, hDC, rcPaint); + } else { + ALF_FillRect(hDC, rcPaint, priv->bgcolor); + } + } + + ALF_Compat_DrawThemeBackground(priv->hTheme, hDC, BP_PUSHBUTTON, uxstate, &r, rcPaint); - ALF_Compat_DrawThemeText(priv->hTheme, hDC, BP_PUSHBUTTON, uxstate, priv->text, -1, style, 0, &texttarget); + RECT content = r; + ALF_Compat_GetThemeBackgroundContentRect(priv->hTheme, hDC, BP_PUSHBUTTON, uxstate, &r, &content); + + if ((drawFlags & ALF_NTBTN_FLAG_IS_FOCUSED) && !(drawFlags & ALF_NTBTN_FLAG_HIDEFOCUS)) + DrawFocusRect(hDC, &content); + + UINT style = DT_CENTER | DT_NOCLIP; + + if (drawFlags & ALF_NTBTN_FLAG_HIDEACCEL) + style |= DT_HIDEPREFIX; + + RECT textbounds = content; + ALF_Compat_GetThemeTextExtent(priv->hTheme, hDC, BP_PUSHBUTTON, uxstate, priv->text, -1, style, &content, &textbounds); + + RECT texttarget = content; + texttarget.top += ((content.bottom - content.top) - (textbounds.bottom - textbounds.top)) / 2; + texttarget.left += ((content.right - content.left) - (textbounds.right - textbounds.left)) / 2; + texttarget.right = texttarget.left + (textbounds.right - textbounds.left); + texttarget.bottom = texttarget.top + (textbounds.bottom - textbounds.top); + + ALF_Compat_DrawThemeText(priv->hTheme, hDC, BP_PUSHBUTTON, uxstate, priv->text, -1, style, 0, &texttarget); + } SelectFont(hDC, oldfont); } static void -ALF_NtButton_RenderClassic(HWND hwnd, ALFNtButtonPriv *priv, HDC dc, RECT *rcPaint) +ALF_NtButton_RenderClassicCheckbox(HWND hwnd, ALFNtButtonPriv *priv, HDC dc, RECT *rcPaint) +{ + int checkwidth = 12 * priv->dpi / 96 + 1; + + RECT rcClient; + GetClientRect(hwnd, &rcClient); + + if (priv->bgcolor == ALF_COLOR_TRANSPARENT) { + ALF_Compat_DrawThemeParentBackground(hwnd, dc, rcPaint); + } else { + ALF_FillRect(dc, rcPaint, priv->bgcolor); + } + + HFONT oldfont = SelectFont(dc, priv->font); + + UINT dfcs = DFCS_BUTTONCHECK; + if (priv->drawFlags & ALF_NTBTN_FLAG_IS_CHECKED) + dfcs |= DFCS_CHECKED; + if (priv->drawFlags & ALF_NTBTN_FLAG_IS_PRESSED) + dfcs |= DFCS_PUSHED; + if (priv->drawFlags & ALF_NTBTN_FLAG_IS_DISABLED) + dfcs |= DFCS_INACTIVE; + + RECT rcCheckmark = { 0, 0, checkwidth, checkwidth }; + rcCheckmark.top += (rcClient.bottom - rcClient.top - checkwidth) / 2; + rcCheckmark.bottom = rcCheckmark.top + checkwidth; + + // lol + int space = ALF_Button_CalcCheckboxTextSpace(dc); + + RECT rcText = { checkwidth + space, + 0, + rcClient.right - rcClient.left, + rcClient.bottom - rcClient.top }; + + DrawFrameControl(dc, &rcCheckmark, DFC_BUTTON, dfcs); + + RECT textbounds = rcText; + DrawTextW(dc, priv->text, -1, &textbounds, DT_LEFT | DT_CALCRECT | DT_EXPANDTABS); + + RECT texttarget = rcText; + texttarget.top += (texttarget.bottom - texttarget.top - textbounds.bottom + textbounds.top) / 2; + texttarget.bottom = texttarget.top + (textbounds.bottom - textbounds.top); + texttarget.right = texttarget.left + textbounds.right - textbounds.left; + + UINT style = DT_LEFT | DT_NOCLIP | DT_EXPANDTABS; + + if (priv->drawFlags & ALF_NTBTN_FLAG_HIDEACCEL) + style |= DT_HIDEPREFIX; + + COLORREF oldBkColor = SetBkColor(dc, GetSysColor(COLOR_BTNFACE)); + if (priv->drawFlags & ALF_NTBTN_FLAG_IS_DISABLED) { + ALF_Button_DrawDisabledTextNtW(dc, priv->text, -1, &texttarget, style); + } else { + DrawTextW(dc, priv->text, -1, &texttarget, style); + } + SetBkColor(dc, oldBkColor); + + if ((priv->drawFlags & ALF_NTBTN_FLAG_IS_FOCUSED) && !(priv->drawFlags & ALF_NTBTN_FLAG_HIDEFOCUS)) { + RECT f = texttarget; + InflateRect(&f, 1, 0); + DrawFocusRect(dc, &f); + } + + SelectFont(dc, oldfont); +} + +static void +ALF_NtButton_RenderClassicPushBtn(HWND hwnd, ALFNtButtonPriv *priv, HDC dc, RECT *rcPaint) { (void)rcPaint; @@ -345,32 +514,72 @@ ALF_NtButton_RenderClassic(HWND hwnd, ALFNtButtonPriv *priv, HDC dc, RECT *rcPai SelectFont(dc, oldfont); } +static void +ALF_NtButton_RenderClassic(HWND hwnd, ALFNtButtonPriv *priv, HDC dc, RECT *rcPaint) +{ + if (priv->drawFlags & ALF_NTBTN_FLAG_DRAW_CHECKBOX) { + ALF_NtButton_RenderClassicCheckbox(hwnd, priv, dc, rcPaint); + } else { + ALF_NtButton_RenderClassicPushBtn(hwnd, priv, dc, rcPaint); + } +} + static void ALF_NtButton_Paint(HWND hwnd, ALFNtButtonPriv *priv, HDC dc, RECT *r) { if (!ALF_Compat_BufferedPaintRenderAnimation(hwnd, dc)) { if ((priv->drawFlags & ALF_NTBTN_FLAG_UXTHEME) && priv->hTheme) { // Draw XP style themed button - int stateid = PBS_NORMAL; + int partid = 0; + int stateid = 0; + + if (priv->drawFlags & ALF_NTBTN_FLAG_DRAW_CHECKBOX) { + partid = BP_CHECKBOX; - if (priv->drawFlags & ALF_NTBTN_FLAG_IS_DEFAULT) - stateid = (priv->drawFlags & ALF_NTBTN_FLAG_DEFAULT_ANIMATING) ? PBS_DEFAULTED_ANIMATING : PBS_DEFAULTED; - if (priv->drawFlags & ALF_NTBTN_FLAG_IS_HOT) - stateid = PBS_HOT; - if (priv->drawFlags & ALF_NTBTN_FLAG_IS_PRESSED) - stateid = PBS_PRESSED; - if (priv->drawFlags & ALF_NTBTN_FLAG_IS_DISABLED) - stateid = PBS_DISABLED; + if (priv->drawFlags & ALF_NTBTN_FLAG_IS_CHECKED) { + stateid = CBS_CHECKEDNORMAL; + + if (priv->drawFlags & ALF_NTBTN_FLAG_IS_HOT) + stateid = CBS_CHECKEDHOT; + if (priv->drawFlags & ALF_NTBTN_FLAG_IS_PRESSED) + stateid = CBS_CHECKEDPRESSED; + if (priv->drawFlags & ALF_NTBTN_FLAG_IS_DISABLED) + stateid = CBS_CHECKEDDISABLED; + } else { + stateid = CBS_UNCHECKEDNORMAL; + + if (priv->drawFlags & ALF_NTBTN_FLAG_IS_HOT) + stateid = CBS_UNCHECKEDHOT; + if (priv->drawFlags & ALF_NTBTN_FLAG_IS_PRESSED) + stateid = CBS_UNCHECKEDPRESSED; + if (priv->drawFlags & ALF_NTBTN_FLAG_IS_DISABLED) + stateid = CBS_UNCHECKEDDISABLED; + } + } else { + partid = BP_PUSHBUTTON; + stateid = PBS_NORMAL; + + if (priv->drawFlags & ALF_NTBTN_FLAG_IS_DEFAULT) + stateid = (priv->drawFlags & ALF_NTBTN_FLAG_DEFAULT_ANIMATING) ? PBS_DEFAULTED_ANIMATING : PBS_DEFAULTED; + if (priv->drawFlags & ALF_NTBTN_FLAG_IS_HOT) + stateid = PBS_HOT; + if (priv->drawFlags & ALF_NTBTN_FLAG_IS_PRESSED) + stateid = PBS_PRESSED; + if (priv->drawFlags & ALF_NTBTN_FLAG_IS_DISABLED) + stateid = PBS_DISABLED; + } if (priv->uxStatePrev == -1) { // initial draw priv->uxStateCurrent = stateid; + priv->uxPartCurrent = partid; priv->uxDrawFlagsCurrent = priv->drawFlags; if (priv->uxStateCurrent == PBS_DEFAULTED || priv->uxStateCurrent == PBS_DEFAULTED_ANIMATING) priv->uxStateCurrent = PBS_NORMAL; } + priv->uxPartPrev = priv->uxPartCurrent; priv->uxStatePrev = priv->uxStateCurrent; priv->uxStateCurrent = stateid; priv->uxDrawFlagsPrev = priv->uxDrawFlagsCurrent; @@ -381,11 +590,12 @@ ALF_NtButton_Paint(HWND hwnd, ALFNtButtonPriv *priv, HDC dc, RECT *r) animParams.cbSize = sizeof(animParams); animParams.style = ALF_Compat_BPAS_LINEAR; - if (priv->uxStateCurrent != priv->uxStatePrev) { - if ((priv->uxStateCurrent == PBS_DEFAULTED && priv->uxStatePrev == PBS_DEFAULTED_ANIMATING) || - (priv->uxStatePrev == PBS_DEFAULTED && priv->uxStateCurrent == PBS_DEFAULTED_ANIMATING)) { + if (priv->uxStateCurrent != priv->uxStatePrev && priv->uxPartCurrent == priv->uxPartPrev) { + if (priv->uxPartCurrent == BP_PUSHBUTTON && + ((priv->uxStateCurrent == PBS_DEFAULTED && priv->uxStatePrev == PBS_DEFAULTED_ANIMATING) || + (priv->uxStatePrev == PBS_DEFAULTED && priv->uxStateCurrent == PBS_DEFAULTED_ANIMATING))) { animParams.dwDuration = priv->uxDefaultAnimationDuration; - } else if (priv->uxStateCurrent == PBS_DEFAULTED_ANIMATING) { + } else if (priv->uxPartCurrent == BP_PUSHBUTTON && priv->uxStateCurrent == PBS_DEFAULTED_ANIMATING) { // Win7 misses these transition times, use the one for PBS_DEFAULTED ALF_Compat_GetThemeTransitionDuration(priv->hTheme, BP_PUSHBUTTON, @@ -393,7 +603,7 @@ ALF_NtButton_Paint(HWND hwnd, ALFNtButtonPriv *priv, HDC dc, RECT *r) PBS_DEFAULTED, TMT_TRANSITIONDURATION, &animParams.dwDuration); - } else if (priv->uxStatePrev == PBS_DEFAULTED_ANIMATING) { + } else if (priv->uxPartCurrent == BP_PUSHBUTTON && priv->uxStatePrev == PBS_DEFAULTED_ANIMATING) { // Win7 misses these transition times, use the one for PBS_DEFAULTED ALF_Compat_GetThemeTransitionDuration(priv->hTheme, BP_PUSHBUTTON, @@ -403,22 +613,24 @@ ALF_NtButton_Paint(HWND hwnd, ALFNtButtonPriv *priv, HDC dc, RECT *r) &animParams.dwDuration); } else { ALF_Compat_GetThemeTransitionDuration(priv->hTheme, - BP_PUSHBUTTON, + priv->uxPartCurrent, priv->uxStatePrev, priv->uxStateCurrent, TMT_TRANSITIONDURATION, &animParams.dwDuration); } - if ((priv->uxStatePrev == PBS_DEFAULTED || priv->uxStatePrev == PBS_DEFAULTED_ANIMATING) - && !(priv->uxStateCurrent == PBS_DEFAULTED || priv->uxStateCurrent == PBS_DEFAULTED_ANIMATING)) { + if ((priv->uxPartCurrent == BP_PUSHBUTTON || priv->uxPartPrev == BP_PUSHBUTTON) && + ((priv->uxStatePrev == PBS_DEFAULTED || priv->uxStatePrev == PBS_DEFAULTED_ANIMATING) + && !(priv->uxPartCurrent == BP_PUSHBUTTON && (priv->uxStateCurrent == PBS_DEFAULTED || priv->uxStateCurrent == PBS_DEFAULTED_ANIMATING)))) { KillTimer(hwnd, (UINT_PTR)priv); ALF_NtButton_ModifyDrawFlags(hwnd, priv, 0, ALF_NTBTN_FLAG_DEFAULT_ANIMATING, FALSE); } - if (!(priv->uxStatePrev == PBS_DEFAULTED || priv->uxStatePrev == PBS_DEFAULTED_ANIMATING) + if (priv->uxPartCurrent == BP_PUSHBUTTON && + (!(priv->uxStatePrev == PBS_DEFAULTED || priv->uxStatePrev == PBS_DEFAULTED_ANIMATING) && (priv->uxStateCurrent == PBS_DEFAULTED || priv->uxStateCurrent == PBS_DEFAULTED_ANIMATING) - && priv->uxDefaultAnimationDuration) { + && priv->uxDefaultAnimationDuration)) { SetTimer(hwnd, (UINT_PTR)priv, priv->uxDefaultAnimationDuration, ALF_NtButton_DefaultAnimatingTimerProc); } } @@ -428,21 +640,17 @@ ALF_NtButton_Paint(HWND hwnd, ALFNtButtonPriv *priv, HDC dc, RECT *r) hbpAnimation = ALF_Compat_BeginBufferedAnimation(hwnd, dc, r, 0, NULL, &animParams, &hdcFrom, &hdcTo); if (hbpAnimation) { - HFONT font = (HFONT)GetCurrentObject(dc, OBJ_FONT); - if (hdcFrom) { - SelectFont(hdcFrom, font); - ALF_NtButton_RenderUxtheme(hwnd, priv, priv->uxStatePrev, priv->uxDrawFlagsPrev, hdcFrom, r); + ALF_NtButton_RenderUxtheme(hwnd, priv, priv->uxPartPrev, priv->uxStatePrev, priv->uxDrawFlagsPrev, hdcFrom, r); } if (hdcTo) { - SelectFont(hdcTo, font); - ALF_NtButton_RenderUxtheme(hwnd, priv, priv->uxStateCurrent, priv->uxDrawFlagsCurrent, hdcTo, r); + ALF_NtButton_RenderUxtheme(hwnd, priv, priv->uxPartCurrent, priv->uxStateCurrent, priv->uxDrawFlagsCurrent, hdcTo, r); } ALF_Compat_EndBufferedAnimation(hbpAnimation, TRUE); } else { - ALF_NtButton_RenderUxtheme(hwnd, priv, stateid, priv->uxDrawFlagsCurrent, dc, r); + ALF_NtButton_RenderUxtheme(hwnd, priv, priv->uxPartCurrent, priv->uxStateCurrent, priv->uxDrawFlagsCurrent, dc, r); } } else { // Draw classic unthemed button @@ -454,7 +662,14 @@ ALF_NtButton_Paint(HWND hwnd, ALFNtButtonPriv *priv, HDC dc, RECT *r) static void ALF_NtButton_Clicked(HWND hwnd, ALFNtButtonPriv *priv) { - (void)priv; + if (priv->drawFlags & ALF_NTBTN_FLAG_DRAW_CHECKBOX) { + if (priv->drawFlags & ALF_NTBTN_FLAG_IS_CHECKED) { + ALF_NtButton_ModifyDrawFlags(hwnd, priv, 0, ALF_NTBTN_FLAG_IS_CHECKED, TRUE); + } else { + ALF_NtButton_ModifyDrawFlags(hwnd, priv, ALF_NTBTN_FLAG_IS_CHECKED, 0, TRUE); + } + } + SendMessageW(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(GetWindowLong(hwnd, GWL_ID), BN_CLICKED), (LPARAM)hwnd); } @@ -466,6 +681,9 @@ ALF_NtButton_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) if (uMsg == WM_CREATE) { priv = ALF_NtButton_InitializePriv((CREATESTRUCTW *)lParam); + if (((CREATESTRUCTW *)lParam)->style & BS_CHECKBOX) + priv->drawFlags |= ALF_NTBTN_FLAG_DRAW_CHECKBOX; + SetWindowLongPtrW(hwnd, 0, (LONG_PTR)priv); ALF_NtButton_HandleThemeChange(hwnd, priv); @@ -488,7 +706,11 @@ ALF_NtButton_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ALF_NtButton_Paint(hwnd, priv, (HDC)wParam, &rc); } else if (uMsg == WM_GETDLGCODE) { - if (priv->drawFlags & ALF_NTBTN_FLAG_IS_DEFAULT) { + if (priv->drawFlags & ALF_NTBTN_FLAG_DRAW_CHECKBOX || priv->drawFlags & ALF_NTBTN_FLAG_DRAW_TRISTATE) { + return DLGC_BUTTON; + } else if (priv->drawFlags & ALF_NTBTN_FLAG_DRAW_RADIOBTN) { + return DLGC_RADIOBUTTON; + } else if (priv->drawFlags & ALF_NTBTN_FLAG_IS_DEFAULT) { return DLGC_DEFPUSHBUTTON | DLGC_BUTTON; } else { return DLGC_UNDEFPUSHBUTTON | DLGC_BUTTON; @@ -501,6 +723,21 @@ ALF_NtButton_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } else { ALF_NtButton_ModifyDrawFlags(hwnd, priv, 0, ALF_NTBTN_FLAG_IS_DEFAULT, TRUE); } + } else if (uMsg == BM_SETCHECK) { + if (wParam & BST_CHECKED) { + ALF_NtButton_ModifyDrawFlags(hwnd, priv, ALF_NTBTN_FLAG_IS_CHECKED, 0, TRUE); + } else { + ALF_NtButton_ModifyDrawFlags(hwnd, priv, 0, ALF_NTBTN_FLAG_IS_CHECKED, TRUE); + } + return 0; + } else if (uMsg == BM_GETCHECK) { + if (priv->drawFlags & ALF_NTBTN_FLAG_IS_CHECKED) + return BST_CHECKED; + else + return BST_UNCHECKED; + } else if (uMsg == BM_CLICK) { + ALF_NtButton_Clicked(hwnd, priv); + return 0; } else if (uMsg == WM_STYLECHANGED) { BOOL newDefault = (GetWindowLong(hwnd, GWL_STYLE) & BS_DEFPUSHBUTTON) ? 1 : 0; @@ -620,8 +857,23 @@ ALF_NtButton_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) return (LRESULT)priv->font; } else if (uMsg == ALF_WM_DPICHANGE) { priv->dpi = (int)lParam; + ALF_NtButton_HandleThemeChange(hwnd, priv); // theme caches bitmaps, need to reload them ALF_InvalidateLayout(GetParent(hwnd)); return TRUE; + } else if (uMsg == ALF_WM_SETBGCOLOR) { + ALFColor newcolor = (ALFColor)lParam; + if (priv->bgcolor != newcolor) { + priv->bgcolor = newcolor; + // FIXME! optimize classic button which is not transparent + InvalidateRect(hwnd, NULL, TRUE); + } + } else if (uMsg == ALF_WM_GETBGCOLOR) { + return (LRESULT)priv->bgcolor; + } else if (uMsg == ALF_WM_BACKGROUNDCHANGE) { + if (priv->bgcolor == ALF_COLOR_TRANSPARENT) { + // FIXME! optimize classic button which is not transparent + InvalidateRect(hwnd, NULL, TRUE); + } } else if (uMsg == WM_SIZE) { InvalidateRect(hwnd, NULL, TRUE); } else if (uMsg == WM_UPDATEUISTATE) { @@ -683,19 +935,27 @@ ALF_RegisterButtonClass(void) } static HWND -ALF_NtButton_Create(HWND win, WORD id, int x, int y, const TCHAR *text) +ALF_NtButton_Create(HWND win, WORD id, int x, int y, const TCHAR *text, DWORD addstyle) { HWND hwndButton = CreateWindowEx(0, _alf_buttonClass, text, - WS_CHILD | WS_TABSTOP | WS_VISIBLE, + WS_CHILD | WS_VISIBLE | addstyle, 0, 0, 100, 100, win, (HMENU)(ULONG_PTR)id, ALF_HINSTANCE, NULL); - ALF_AddWidget(win, x, y, hwndButton, 5625, 1725, ALF_LAYOUT_SIZE_QUERY | ALF_LAYOUT_INHERITFONT | ALF_LAYOUT_SENDDPICHANGE | ALF_LAYOUT_TRANSPARENTBG); + int minwidth = 5625; + int minheight = 1725; + + if (addstyle & (BS_CHECKBOX | BS_RADIOBUTTON)) { + minwidth = 0; + minheight = 0; + } + + ALF_AddWidget(win, x, y, hwndButton, minwidth, minheight, ALF_LAYOUT_SIZE_QUERY | ALF_LAYOUT_INHERITFONT | ALF_LAYOUT_SENDDPICHANGE | ALF_LAYOUT_INHERITBGCOLOR | ALF_LAYOUT_SENDBGCHANGE); return hwndButton; } @@ -704,9 +964,13 @@ ALF_NtButton_Create(HWND win, WORD id, int x, int y, const TCHAR *text) /* Classic Button for Win9x and NT3.51 */ // Win9x/Win32s absolutely needs a real button window, otherwise the dialog manager will get confused +#define ALF_CLSCBTN_FLAG_IS_DEFAULT ((DWORD)1) +#define ALF_CLSCBTN_FLAG_DRAW_CHECKBOX ((DWORD)2) +#define ALF_CLSCBTN_FLAG_IS_CHECKED ((DWORD)4) + typedef struct { WNDPROC origWndProc; - BOOL isDefault; + DWORD flags; BYTE thunk[12]; } ALFClassicButtonPriv; @@ -740,7 +1004,7 @@ ALF_ClassicButton_FreePriv(ALFClassicButtonPriv *priv) } static void -ALF_ClassicButton_CalculateSize(HWND hwnd, SIZE *pSize) +ALF_ClassicButton_CalculateSize(HWND hwnd, ALFClassicButtonPriv *priv, SIZE *pSize) { HDC hdc = GetDC(hwnd); HFONT font = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0); @@ -757,17 +1021,33 @@ ALF_ClassicButton_CalculateSize(HWND hwnd, SIZE *pSize) ALF_Free(textbuf); - int xpadding = GetSystemMetrics(SM_CXEDGE) * 2 + 6; - int ypadding = GetSystemMetrics(SM_CYEDGE) * 2 + 4; + if (priv->flags & ALF_CLSCBTN_FLAG_DRAW_CHECKBOX) { + int checkwidth = 13; - if (pSize->cx < r.right - r.left + xpadding) { - pSize->cx = r.right - r.left + xpadding; - } - if (pSize->cy < r.bottom - r.top + ypadding) { - pSize->cy = r.bottom - r.top + ypadding; - } - if (pSize->cx < pSize->cy) { - pSize->cx = pSize->cy; + int space = ALF_Button_CalcCheckboxTextSpace(hdc); + int cx = checkwidth + space + r.right - r.left + 1; + int cy = r.bottom - r.top; + if (cy < checkwidth) + cy = checkwidth; + + if (pSize->cx < cx) + pSize->cx = cx; + + if (pSize->cy < cy) + pSize->cy = cy; + } else { + int xpadding = GetSystemMetrics(SM_CXEDGE) * 2 + 6; + int ypadding = GetSystemMetrics(SM_CYEDGE) * 2 + 4; + + if (pSize->cx < r.right - r.left + xpadding) { + pSize->cx = r.right - r.left + xpadding; + } + if (pSize->cy < r.bottom - r.top + ypadding) { + pSize->cy = r.bottom - r.top + ypadding; + } + if (pSize->cx < pSize->cy) { + pSize->cx = pSize->cy; + } } SelectFont(hdc, oldFont); @@ -776,13 +1056,86 @@ ALF_ClassicButton_CalculateSize(HWND hwnd, SIZE *pSize) } static void -ALF_ClassicButton_Paint(HWND hwnd, ALFClassicButtonPriv *priv, DRAWITEMSTRUCT *dis) +ALF_ClassicButton_PaintCheckbox(HWND hwnd, ALFClassicButtonPriv *priv, DRAWITEMSTRUCT *dis) +{ + int checkwidth = 13; + + RECT rcClient; + GetClientRect(hwnd, &rcClient); + + if (dis->itemAction & ODA_DRAWENTIRE) { + //if (priv->bgcolor == ALF_COLOR_TRANSPARENT) { + // ALF_Compat_DrawThemeParentBackground(hwnd, dc, rcPaint); + //} else { + ALF_FillRect(dis->hDC, &dis->rcItem, ALF_COLOR_SYS(COLOR_BTNFACE)); + //} + } + + if (dis->itemAction & (ODA_DRAWENTIRE | ODA_SELECT)) { + UINT dfcs = DFCS_BUTTONCHECK; + if (priv->flags & ALF_CLSCBTN_FLAG_IS_CHECKED) + dfcs |= DFCS_CHECKED; + if (dis->itemState & ODS_SELECTED) + dfcs |= DFCS_PUSHED; + if (dis->itemState & ODS_DISABLED) + dfcs |= DFCS_INACTIVE; + + RECT rcCheckmark = { 0, 0, checkwidth, checkwidth }; + rcCheckmark.top += (rcClient.bottom - rcClient.top - checkwidth) / 2; + rcCheckmark.bottom = rcCheckmark.top + checkwidth; + + DrawFrameControl(dis->hDC, &rcCheckmark, DFC_BUTTON, dfcs); + } + + if (dis->itemAction & (ODA_DRAWENTIRE | ODA_FOCUS)) { + // lol + int space = ALF_Button_CalcCheckboxTextSpace(dis->hDC); + + RECT rcText = { checkwidth + space, + 0, + rcClient.right - rcClient.left, + rcClient.bottom - rcClient.top }; + + TCHAR *textbuf = ALF_Text(hwnd); + + RECT textbounds = rcText; + DrawText(dis->hDC, textbuf, -1, &textbounds, DT_LEFT | DT_CALCRECT | DT_EXPANDTABS); + + RECT texttarget = rcText; + texttarget.top += (texttarget.bottom - texttarget.top - textbounds.bottom + textbounds.top) / 2; + texttarget.bottom = texttarget.top + (textbounds.bottom - textbounds.top); + texttarget.right = texttarget.left + textbounds.right - textbounds.left; + + if (dis->itemAction & ODA_DRAWENTIRE) { + UINT style = DT_LEFT | DT_NOCLIP | DT_EXPANDTABS; + + COLORREF oldBkColor = SetBkColor(dis->hDC, GetSysColor(COLOR_BTNFACE)); + if (dis->itemState & ODS_DISABLED) { + ALF_Button_DrawDisabledText(dis->hDC, textbuf, -1, &texttarget, style); + } else { + DrawText(dis->hDC, textbuf, -1, &texttarget, style); + } + SetBkColor(dis->hDC, oldBkColor); + } + + if (dis->itemState & ODS_FOCUS) { + RECT f = texttarget; + InflateRect(&f, 1, 0); + DrawFocusRect(dis->hDC, &f); + } + + ALF_Free(textbuf); + } +} + +static void +ALF_ClassicButton_PaintButton(HWND hwnd, ALFClassicButtonPriv *priv, DRAWITEMSTRUCT *dis) { RECT r = dis->rcItem; TCHAR *textbuf = ALF_Text(hwnd); - if (priv->isDefault) { + if (priv->flags & ALF_CLSCBTN_FLAG_IS_DEFAULT) { HBRUSH framecolor = GetSysColorBrush(COLOR_WINDOWFRAME); FrameRect(dis->hDC, &r, framecolor); InflateRect(&r, -1, -1); @@ -836,6 +1189,16 @@ ALF_ClassicButton_Paint(HWND hwnd, ALFClassicButtonPriv *priv, DRAWITEMSTRUCT *d ALF_Free(textbuf); } +static void +ALF_ClassicButton_Paint(HWND hwnd, ALFClassicButtonPriv *priv, DRAWITEMSTRUCT *dis) +{ + if (priv->flags & ALF_CLSCBTN_FLAG_DRAW_CHECKBOX) { + ALF_ClassicButton_PaintCheckbox(hwnd, priv, dis); + } else { + ALF_ClassicButton_PaintButton(hwnd, priv, dis); + } +} + static LRESULT CALLBACK ALF_ClassicButton_WndProc(ALFClassicButtonPriv *priv, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { @@ -843,7 +1206,7 @@ ALF_ClassicButton_WndProc(ALFClassicButtonPriv *priv, HWND hwnd, UINT uMsg, WPAR return DefWindowProc(hwnd, uMsg, wParam, lParam); if (uMsg == ALF_WM_QUERYSIZE) { - ALF_ClassicButton_CalculateSize(hwnd, (SIZE*)lParam);; + ALF_ClassicButton_CalculateSize(hwnd, priv, (SIZE*)lParam); return TRUE; } else if (uMsg == 0x2000 + WM_DRAWITEM) { LPDRAWITEMSTRUCT dis = (DRAWITEMSTRUCT *)lParam; @@ -851,13 +1214,49 @@ ALF_ClassicButton_WndProc(ALFClassicButtonPriv *priv, HWND hwnd, UINT uMsg, WPAR ALF_ClassicButton_Paint(hwnd, priv, dis); return TRUE; + } else if (uMsg == 0x2000 + WM_COMMAND) { + if (HIWORD(wParam) == BN_CLICKED && priv->flags & ALF_CLSCBTN_FLAG_DRAW_CHECKBOX) { + if (priv->flags & ALF_CLSCBTN_FLAG_IS_CHECKED) { + priv->flags &= ~ALF_CLSCBTN_FLAG_IS_CHECKED; + } else { + priv->flags |= ALF_CLSCBTN_FLAG_IS_CHECKED; + } + + RECT rcCheck; + RECT rcClient; + GetClientRect(hwnd, &rcClient); + + rcCheck.left = 0; + rcCheck.top = 0; + rcCheck.right = 13; + rcCheck.bottom = rcClient.bottom - rcClient.top; + InvalidateRect(hwnd, &rcCheck, TRUE); + } } else if (uMsg == WM_ERASEBKGND) { return TRUE; } else if (uMsg == BM_SETSTYLE) { - priv->isDefault = (wParam & BS_DEFPUSHBUTTON); + priv->flags &= ~ALF_CLSCBTN_FLAG_IS_DEFAULT; + if (wParam & BS_DEFPUSHBUTTON) + priv->flags |= ALF_CLSCBTN_FLAG_IS_DEFAULT; + wParam = BS_OWNERDRAW; + } else if (uMsg == BM_SETCHECK) { + priv->flags &= ~ALF_CLSCBTN_FLAG_IS_CHECKED; + if (wParam & BST_CHECKED) + priv->flags |= ALF_CLSCBTN_FLAG_IS_CHECKED; + + InvalidateRect(hwnd, NULL, TRUE); + return 0; + } else if (uMsg == BM_GETCHECK) { + if (priv->flags & ALF_CLSCBTN_FLAG_IS_CHECKED) { + return BST_CHECKED; + } else { + return BST_UNCHECKED; + } } else if (uMsg == WM_GETDLGCODE) { - if (priv->isDefault) { + if (priv->flags & ALF_CLSCBTN_FLAG_DRAW_CHECKBOX) { + return (LRESULT)DLGC_BUTTON; + } else if (priv->flags & ALF_CLSCBTN_FLAG_IS_DEFAULT) { return (LRESULT)DLGC_DEFPUSHBUTTON; } else { return (LRESULT)DLGC_UNDEFPUSHBUTTON; @@ -876,7 +1275,7 @@ ALF_ClassicButton_WndProc(ALFClassicButtonPriv *priv, HWND hwnd, UINT uMsg, WPAR } static HWND -ALF_ClassicButton_Create(HWND win, WORD id, int x, int y, const TCHAR *text) +ALF_ClassicButton_Create(HWND win, WORD id, int x, int y, const TCHAR *text, DWORD style) { HWND hwndButton = CreateWindowEx(0, TEXT("BUTTON"), @@ -889,10 +1288,21 @@ ALF_ClassicButton_Create(HWND win, WORD id, int x, int y, const TCHAR *text) NULL); ALFClassicButtonPriv *priv = ALF_ClassicButton_InitializePriv(); + + int minwidth = 5625; + int minheight = 1725; + + if (style & BS_CHECKBOX) { + minwidth = 0; + minheight = 0; + priv->flags |= ALF_CLSCBTN_FLAG_DRAW_CHECKBOX; + } + + priv->origWndProc = (WNDPROC)GetWindowLongPtr(hwndButton, GWLP_WNDPROC); SetWindowLongPtr(hwndButton, GWLP_WNDPROC, (LONG_PTR)priv->thunk); - ALF_AddWidget(win, x, y, hwndButton, 5625, 1725, ALF_LAYOUT_SIZE_QUERY | ALF_LAYOUT_INHERITFONT); + ALF_AddWidget(win, x, y, hwndButton, minwidth, minheight, ALF_LAYOUT_SIZE_QUERY | ALF_LAYOUT_INHERITFONT); return hwndButton; } @@ -903,9 +1313,19 @@ HWND ALF_AddButton(HWND win, WORD id, int x, int y, const TCHAR *text) { if (_alf_buttonClass) { - return ALF_NtButton_Create(win, id, x, y, text); + return ALF_NtButton_Create(win, id, x, y, text, WS_TABSTOP); + } else { + return ALF_ClassicButton_Create(win, id, x, y, text, 0); + } +} + +HWND +ALF_AddCheckbox(HWND win, WORD id, int x, int y, const TCHAR *text) +{ + if (_alf_buttonClass) { + return ALF_NtButton_Create(win, id, x, y, text, WS_TABSTOP | BS_CHECKBOX); } else { - return ALF_ClassicButton_Create(win, id, x, y, text); + return ALF_ClassicButton_Create(win, id, x, y, text, BS_CHECKBOX); } } -- cgit v1.2.3