From: Gabriel Ivăncescu Subject: [PATCH 6/7] comctl32/button: Implement BCM_GETIDEALSIZE for command links Message-Id: <2820c7bdd66ee4800f8f437777273a11e02a7041.1555675661.git.gabrielopcode@gmail.com> Date: Fri, 19 Apr 2019 15:14:02 +0300 In-Reply-To: <5661132d2a0d8bc5045970f0130ea53e19f4d549.1555675661.git.gabrielopcode@gmail.com> References: <5661132d2a0d8bc5045970f0130ea53e19f4d549.1555675661.git.gabrielopcode@gmail.com> Signed-off-by: Gabriel Ivăncescu --- Command Links seem to have weird behavior with BCM_GETIDEALSIZE (as evidenced by tests on this and the next patch) and the ideal size *does* depend on the theme. This is exact as on Windows in what testing I did with reasonably-sized fonts (there may be extreme corner cases I didn't bother to check). dlls/comctl32/button.c | 137 ++++++++++++++++++++++++++++++++++- dlls/comctl32/tests/button.c | 10 +-- 2 files changed, 139 insertions(+), 8 deletions(-) diff --git a/dlls/comctl32/button.c b/dlls/comctl32/button.c index 37212fb..0a8bdf1 100644 --- a/dlls/comctl32/button.c +++ b/dlls/comctl32/button.c @@ -200,6 +200,7 @@ static BOOL PB_GetIdealSize(BUTTON_INFO *infoPtr, SIZE *size); static BOOL CB_GetIdealSize(BUTTON_INFO *infoPtr, SIZE *size); static BOOL GB_GetIdealSize(BUTTON_INFO *infoPtr, SIZE *size); static BOOL SB_GetIdealSize(BUTTON_INFO *infoPtr, SIZE *size); +static BOOL CL_GetIdealSize(BUTTON_INFO *infoPtr, SIZE *size); static const pfGetIdealSize btnGetIdealSizeFunc[MAX_BTN_TYPE] = { PB_GetIdealSize, /* BS_PUSHBUTTON */ @@ -216,9 +217,8 @@ static const pfGetIdealSize btnGetIdealSizeFunc[MAX_BTN_TYPE] = { GB_GetIdealSize, /* BS_OWNERDRAW */ SB_GetIdealSize, /* BS_SPLITBUTTON */ SB_GetIdealSize, /* BS_DEFSPLITBUTTON */ - /* GetIdealSize() for following types are unimplemented, use BS_PUSHBUTTON's for now */ - PB_GetIdealSize, /* BS_COMMANDLINK */ - PB_GetIdealSize /* BS_DEFCOMMANDLINK */ + CL_GetIdealSize, /* BS_COMMANDLINK */ + CL_GetIdealSize /* BS_DEFCOMMANDLINK */ }; /* Fixed margin for command links, regardless of DPI (based on tests done on Windows) */ @@ -1363,6 +1363,137 @@ static BOOL SB_GetIdealSize(BUTTON_INFO *infoPtr, SIZE *size) return TRUE; } +static BOOL CL_GetIdealSize(BUTTON_INFO *infoPtr, SIZE *size) +{ + HTHEME theme = GetWindowTheme(infoPtr->hwnd); + HDC hdc = GetDC(infoPtr->hwnd); + LONG w, text_w = 0, text_h = 0; + UINT flags = DT_TOP | DT_LEFT; + HFONT font, old_font = NULL; + RECT text_bound = { 0 }; + SIZE img_size; + RECT margin; + WCHAR *text; + + /* Get the image size */ + if (infoPtr->u.image || infoPtr->imagelist.himl) + img_size = BUTTON_GetImageSize(infoPtr); + else + { + if (theme) + GetThemePartSize(theme, NULL, BP_COMMANDLINKGLYPH, CMDLS_NORMAL, NULL, TS_DRAW, &img_size); + else + img_size.cx = img_size.cy = command_link_defglyph_size; + } + + /* Get the content margins */ + if (theme) + { + RECT r = { 0, 0, 0xffff, 0xffff }; + GetThemeBackgroundContentRect(theme, hdc, BP_COMMANDLINK, CMDLS_NORMAL, &r, &margin); + margin.left -= r.left; + margin.top -= r.top; + margin.right = r.right - margin.right; + margin.bottom = r.bottom - margin.bottom; + } + else + { + margin.left = margin.right = command_link_margin; + margin.top = margin.bottom = command_link_margin; + } + + /* Account for the border margins and the margin between image and text */ + w = margin.left + margin.right + (img_size.cx ? (img_size.cx + command_link_margin) : 0); + + /* If a rectangle with a specific width was requested, bound the text to it */ + if (size->cx > w) + { + text_bound.right = size->cx - w; + flags |= DT_WORDBREAK; + } + + if (theme) + { + if (infoPtr->font) old_font = SelectObject(hdc, infoPtr->font); + + /* Find the text's rect */ + if ((text = get_button_text(infoPtr))) + { + RECT r; + GetThemeTextExtent(theme, hdc, BP_COMMANDLINK, CMDLS_NORMAL, + text, -1, flags, &text_bound, &r); + heap_free(text); + text_w = r.right - r.left; + text_h = r.bottom - r.top; + } + + /* Find the note's rect */ + if (infoPtr->note) + { + DTTOPTS opts; + + opts.dwSize = sizeof(opts); + opts.dwFlags = DTT_FONTPROP | DTT_CALCRECT; + opts.iFontPropId = TMT_BODYFONT; + DrawThemeTextEx(theme, hdc, BP_COMMANDLINK, CMDLS_NORMAL, + infoPtr->note, infoPtr->note_length, + flags | DT_NOPREFIX | DT_CALCRECT, &text_bound, &opts); + text_w = max(text_w, text_bound.right - text_bound.left); + text_h += text_bound.bottom - text_bound.top; + } + } + else + { + NONCLIENTMETRICSW ncm; + + ncm.cbSize = sizeof(ncm); + if (SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0)) + { + LONG note_weight = ncm.lfMessageFont.lfWeight; + + /* Find the text's rect */ + ncm.lfMessageFont.lfWeight = FW_BOLD; + if ((font = CreateFontIndirectW(&ncm.lfMessageFont))) + { + if ((text = get_button_text(infoPtr))) + { + RECT r = text_bound; + old_font = SelectObject(hdc, font); + DrawTextW(hdc, text, -1, &r, flags | DT_CALCRECT); + heap_free(text); + + text_w = r.right - r.left; + text_h = r.bottom - r.top; + } + DeleteObject(font); + } + + /* Find the note's rect */ + ncm.lfMessageFont.lfWeight = note_weight; + if (infoPtr->note && (font = CreateFontIndirectW(&ncm.lfMessageFont))) + { + HFONT tmp = SelectObject(hdc, font); + if (!old_font) old_font = tmp; + + DrawTextW(hdc, infoPtr->note, infoPtr->note_length, &text_bound, + flags | DT_NOPREFIX | DT_CALCRECT); + DeleteObject(font); + + text_w = max(text_w, text_bound.right - text_bound.left); + text_h += text_bound.bottom - text_bound.top + 2; + } + } + } + w += text_w; + + size->cx = min(size->cx, w); + size->cy = max(text_h, img_size.cy) + margin.top + margin.bottom; + + if (old_font) SelectObject(hdc, old_font); + ReleaseDC(infoPtr->hwnd, hdc); + return TRUE; +} + /********************************************************************** * BUTTON_CalcLayoutRects * diff --git a/dlls/comctl32/tests/button.c b/dlls/comctl32/tests/button.c index e0c4736..c31603f 100644 --- a/dlls/comctl32/tests/button.c +++ b/dlls/comctl32/tests/button.c @@ -2162,8 +2162,8 @@ static void test_bcm_get_ideal_size(void) if (type == BS_COMMANDLINK || type == BS_DEFCOMMANDLINK) { - todo_wine ok((size.cx == 0 && size.cy > 0), "Style 0x%08x expect ideal cx %d >= %d and ideal cy %d >= %d\n", - style, size.cx, 0, size.cy, 0); + ok((size.cx == 0 && size.cy > 0), "Style 0x%08x expect ideal cx %d == %d and ideal cy %d > %d\n", + style, size.cx, 0, size.cy, 0); } else { @@ -2199,9 +2199,9 @@ static void test_bcm_get_ideal_size(void) } else if (type == BS_COMMANDLINK || type == BS_DEFCOMMANDLINK) { - todo_wine ok((size.cx == 0 && size.cy > 0), - "Style 0x%08x expect ideal cx %d >= %d and ideal cy %d >= %d\n", style, size.cx, 0, - size.cy, 0); + ok((size.cx == 0 && size.cy > 0), + "Style 0x%08x expect ideal cx %d == %d and ideal cy %d > %d\n", style, size.cx, 0, + size.cy, 0); } else { -- 2.21.0