From: Gabriel Ivăncescu Subject: [PATCH 5/7] comctl32/button: Implement themed command links Message-Id: <7a2993387c97325d2a2833846d5c805eb23b18e9.1555675661.git.gabrielopcode@gmail.com> Date: Fri, 19 Apr 2019 15:14:01 +0300 In-Reply-To: <5661132d2a0d8bc5045970f0130ea53e19f4d549.1555675661.git.gabrielopcode@gmail.com> References: <5661132d2a0d8bc5045970f0130ea53e19f4d549.1555675661.git.gabrielopcode@gmail.com> Signed-off-by: Gabriel Ivăncescu --- To avoid differences between themes (and because I didn't have a theme that supported command links), this has been verified to be very close to Windows 7 by subclassing a button on a Windows test app and using this function to paint it, then comparing with Windows' own command link. Most of the margin and font calculations were, to my knowledge, pixel perfect even on different DPI settings, but for some reason the background was slightly different in color (which isn't a big deal, since the code is displaying the theme's BP_COMMANDLINK background, so maybe it's a Windows 7 bug here). dlls/comctl32/button.c | 128 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 2 deletions(-) diff --git a/dlls/comctl32/button.c b/dlls/comctl32/button.c index 4f2fbce..37212fb 100644 --- a/dlls/comctl32/button.c +++ b/dlls/comctl32/button.c @@ -172,6 +172,7 @@ static void PB_ThemedPaint( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, i static void CB_ThemedPaint( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, int drawState, UINT dtflags, BOOL focused); static void GB_ThemedPaint( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, int drawState, UINT dtflags, BOOL focused); static void SB_ThemedPaint( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, int drawState, UINT dtflags, BOOL focused); +static void CL_ThemedPaint( HTHEME theme, const BUTTON_INFO *infoPtr, HDC hdc, int drawState, UINT dtflags, BOOL focused); static const pfThemedPaint btnThemedPaintFunc[MAX_BTN_TYPE] = { @@ -189,8 +190,8 @@ static const pfThemedPaint btnThemedPaintFunc[MAX_BTN_TYPE] = NULL, /* BS_OWNERDRAW */ SB_ThemedPaint, /* BS_SPLITBUTTON */ SB_ThemedPaint, /* BS_DEFSPLITBUTTON */ - NULL, /* BS_COMMANDLINK */ - NULL, /* BS_DEFCOMMANDLINK */ + CL_ThemedPaint, /* BS_COMMANDLINK */ + CL_ThemedPaint /* BS_DEFCOMMANDLINK */ }; typedef BOOL (*pfGetIdealSize)(BUTTON_INFO *infoPtr, SIZE *size); @@ -2820,6 +2821,129 @@ cleanup: if (old_font) SelectObject(hDC, old_font); } +static void CL_ThemedPaint(HTHEME theme, const BUTTON_INFO *infoPtr, HDC hDC, int state, UINT dtFlags, BOOL focused) +{ + HFONT old_font = infoPtr->font ? SelectObject(hDC, infoPtr->font) : NULL; + NMCUSTOMDRAW nmcd; + LRESULT cdrf; + HWND parent; + RECT rc; + + GetClientRect(infoPtr->hwnd, &rc); + init_custom_draw(&nmcd, infoPtr, hDC, &rc); + + parent = GetParent(infoPtr->hwnd); + if (!parent) parent = infoPtr->hwnd; + + /* Send erase notifications */ + cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd); + if (cdrf & CDRF_SKIPDEFAULT) goto cleanup; + + if (IsThemeBackgroundPartiallyTransparent(theme, BP_COMMANDLINK, state)) + DrawThemeParentBackground(infoPtr->hwnd, hDC, NULL); + DrawThemeBackground(theme, hDC, BP_COMMANDLINK, state, &rc, NULL); + + if (cdrf & CDRF_NOTIFYPOSTERASE) + { + nmcd.dwDrawStage = CDDS_POSTERASE; + SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd); + } + + /* Send paint notifications */ + nmcd.dwDrawStage = CDDS_PREPAINT; + cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd); + if (cdrf & CDRF_SKIPDEFAULT) goto cleanup; + + if (!(cdrf & CDRF_DOERASE)) + { + RECT r, img_rect; + UINT txt_h = 0; + SIZE img_size; + WCHAR *text; + + GetThemeBackgroundContentRect(theme, hDC, BP_COMMANDLINK, state, &rc, &r); + + /* The text alignment and styles are fixed and don't depend on button styles */ + dtFlags = DT_TOP | DT_LEFT | DT_WORDBREAK; + + /* Command Links ignore the margins of the image list or its alignment */ + if (infoPtr->u.image || infoPtr->imagelist.himl) + img_size = BUTTON_GetImageSize(infoPtr); + else + GetThemePartSize(theme, NULL, BP_COMMANDLINKGLYPH, state, NULL, TS_DRAW, &img_size); + + img_rect = r; + if (img_size.cx) r.left += img_size.cx + command_link_margin; + + /* Draw the text */ + if ((text = get_button_text(infoPtr))) + { + UINT len = lstrlenW(text); + RECT text_rect; + + GetThemeTextExtent(theme, hDC, BP_COMMANDLINK, state, text, len, + dtFlags | DT_END_ELLIPSIS, &r, &text_rect); + DrawThemeText(theme, hDC, BP_COMMANDLINK, state, text, len, + dtFlags | DT_END_ELLIPSIS, 0, &r); + + txt_h = text_rect.bottom - text_rect.top; + heap_free(text); + } + + /* Draw the note */ + if (infoPtr->note) + { + DTTOPTS opts; + + r.top += txt_h; + opts.dwSize = sizeof(opts); + opts.dwFlags = DTT_FONTPROP; + opts.iFontPropId = TMT_BODYFONT; + DrawThemeTextEx(theme, hDC, BP_COMMANDLINK, state, + infoPtr->note, infoPtr->note_length, + dtFlags | DT_NOPREFIX, &r, &opts); + } + + /* Position the image at the vertical center of the drawn text (not note) */ + txt_h = min(txt_h, img_rect.bottom - img_rect.top); + if (img_size.cy < txt_h) img_rect.top += (txt_h - img_size.cy) / 2; + + img_rect.right = img_rect.left + img_size.cx; + img_rect.bottom = img_rect.top + img_size.cy; + + if (infoPtr->u.image || infoPtr->imagelist.himl) + BUTTON_DrawImage(infoPtr, hDC, NULL, + (state == CMDLS_DISABLED) ? DSS_DISABLED : DSS_NORMAL, + &img_rect); + else + DrawThemeBackground(theme, hDC, BP_COMMANDLINKGLYPH, state, &img_rect, NULL); + } + + if (cdrf & CDRF_NOTIFYPOSTPAINT) + { + nmcd.dwDrawStage = CDDS_POSTPAINT; + SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd); + } + if (cdrf & CDRF_SKIPPOSTPAINT) goto cleanup; + + if (focused) + { + MARGINS margins; + + /* The focus rect has margins of a push button rather than command link... */ + GetThemeMargins(theme, hDC, BP_PUSHBUTTON, state, TMT_CONTENTMARGINS, NULL, &margins); + + rc.left += margins.cxLeftWidth; + rc.top += margins.cyTopHeight; + rc.right -= margins.cxRightWidth; + rc.bottom -= margins.cyBottomHeight; + DrawFocusRect(hDC, &rc); + } + +cleanup: + if (old_font) SelectObject(hDC, old_font); +} + void BUTTON_Register(void) { WNDCLASSW wndClass; -- 2.21.0