diff --git a/dlls/comctl32/button.c b/dlls/comctl32/button.c index a9ca619e890..5621fda4f36 100644 --- a/dlls/comctl32/button.c +++ b/dlls/comctl32/button.c @@ -28,7 +28,6 @@ * - WM_SETFOCUS: For (manual or automatic) radio buttons, send the parent window BN_CLICKED * - WM_NCCREATE: Turns any BS_OWNERDRAW button into a BS_PUSHBUTTON button. * - WM_SYSKEYUP - * - BCM_GETIDEALSIZE * * Notifications * - BCN_HOTITEMCHANGE @@ -42,7 +41,6 @@ * * Structures/Macros/Definitions * - NMBCHOTITEM - * - Button_GetIdealSize */ #include @@ -187,6 +185,32 @@ static const pfThemedPaint btnThemedPaintFunc[MAX_BTN_TYPE] = NULL, /* BS_DEFCOMMANDLINK */ }; +typedef BOOL (*pfGetIdealSize)(BUTTON_INFO *infoPtr, SIZE *size); + +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 const pfGetIdealSize btnGetIdealSizeFunc[MAX_BTN_TYPE] = { + PB_GetIdealSize, /* BS_PUSHBUTTON */ + PB_GetIdealSize, /* BS_DEFPUSHBUTTON */ + CB_GetIdealSize, /* BS_CHECKBOX */ + CB_GetIdealSize, /* BS_AUTOCHECKBOX */ + CB_GetIdealSize, /* BS_RADIOBUTTON */ + GB_GetIdealSize, /* BS_3STATE */ + GB_GetIdealSize, /* BS_AUTO3STATE */ + GB_GetIdealSize, /* BS_GROUPBOX */ + PB_GetIdealSize, /* BS_USERBUTTON */ + CB_GetIdealSize, /* BS_AUTORADIOBUTTON */ + GB_GetIdealSize, /* BS_PUSHBOX */ + GB_GetIdealSize, /* BS_OWNERDRAW */ + /* GetIdealSize() for following types are unimplemented, use BS_PUSHBUTTON's for now */ + PB_GetIdealSize, /* BS_SPLITBUTTON */ + PB_GetIdealSize, /* BS_DEFSPLITBUTTON */ + PB_GetIdealSize, /* BS_COMMANDLINK */ + PB_GetIdealSize /* BS_DEFCOMMANDLINK */ +}; + static inline UINT get_button_type( LONG window_style ) { return (window_style & BS_TYPEMASK); @@ -846,6 +870,15 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L return TRUE; } + case BCM_GETIDEALSIZE: + { + SIZE *size = (SIZE *)lParam; + + if (!size) return FALSE; + + return btnGetIdealSizeFunc[btn_type](infoPtr, size); + } + case WM_NCHITTEST: if(btn_type == BS_GROUPBOX) return HTTRANSPARENT; /* fall through */ @@ -1038,6 +1071,164 @@ static const RECT *BUTTON_GetTextMargin(const BUTTON_INFO *infoPtr) return &oneMargin; } +static void BUTTON_GetClientRectSize(BUTTON_INFO *infoPtr, SIZE *size) +{ + RECT rect; + GetClientRect(infoPtr->hwnd, &rect); + size->cx = rect.right - rect.left; + size->cy = rect.bottom - rect.top; +} + +static void BUTTON_GetTextIdealSize(BUTTON_INFO *infoPtr, LONG maxWidth, SIZE *size) +{ + WCHAR *text = get_button_text(infoPtr); + HDC hdc; + RECT rect; + const RECT *margin = BUTTON_GetTextMargin(infoPtr); + + if (maxWidth != 0) + { + maxWidth -= margin->right + margin->right; + if (maxWidth <= 0) maxWidth = 1; + } + + hdc = GetDC(infoPtr->hwnd); + rect = BUTTON_GetTextRect(infoPtr, hdc, text, maxWidth); + ReleaseDC(infoPtr->hwnd, hdc); + heap_free(text); + + size->cx = rect.right - rect.left + margin->left + margin->right; + size->cy = rect.bottom - rect.top + margin->top + margin->bottom; +} + +static void BUTTON_GetLabelIdealSize(BUTTON_INFO *infoPtr, LONG maxWidth, SIZE *size) +{ + LONG style = GetWindowLongW(infoPtr->hwnd, GWL_STYLE); + SIZE imageSize; + SIZE textSize; + BOOL horizontal; + + imageSize = BUTTON_GetImageSize(infoPtr); + if (infoPtr->imagelist.himl) + { + imageSize.cx += infoPtr->imagelist.margin.left + infoPtr->imagelist.margin.right; + imageSize.cy += infoPtr->imagelist.margin.top + infoPtr->imagelist.margin.bottom; + if (infoPtr->imagelist.uAlign == BUTTON_IMAGELIST_ALIGN_TOP + || infoPtr->imagelist.uAlign == BUTTON_IMAGELIST_ALIGN_BOTTOM) + horizontal = FALSE; + else + horizontal = TRUE; + } + else + { + /* horizontal alignment flags has priority over vertical ones if both are specified */ + if (!(style & (BS_CENTER | BS_VCENTER)) || ((style & BS_CENTER) && (style & BS_CENTER) != BS_CENTER) + || !(style & BS_VCENTER) || (style & BS_VCENTER) == BS_VCENTER) + horizontal = TRUE; + else + horizontal = FALSE; + } + + if (horizontal) + { + if (maxWidth != 0) + { + maxWidth -= imageSize.cx; + if (maxWidth <= 0) maxWidth = 1; + } + BUTTON_GetTextIdealSize(infoPtr, maxWidth, &textSize); + size->cx = textSize.cx + imageSize.cx; + size->cy = max(textSize.cy, imageSize.cy); + } + else + { + BUTTON_GetTextIdealSize(infoPtr, maxWidth, &textSize); + size->cx = max(textSize.cx, imageSize.cx); + size->cy = textSize.cy + imageSize.cy; + } +} + +static BOOL GB_GetIdealSize(BUTTON_INFO *infoPtr, SIZE *size) +{ + BUTTON_GetClientRectSize(infoPtr, size); + return TRUE; +} + +static BOOL CB_GetIdealSize(BUTTON_INFO *infoPtr, SIZE *size) +{ + LONG style = GetWindowLongW(infoPtr->hwnd, GWL_STYLE); + WCHAR *text = get_button_text(infoPtr); + HDC hdc; + HFONT hfont; + SIZE labelSize; + INT textOffset; + INT textLength = 0; + double scaleX; + double scaleY; + LONG checkboxWidth, checkboxHeight; + LONG maxWidth = 0; + + if (text) textLength = lstrlenW(text); + heap_free(text); + if (textLength == 0) + { + BUTTON_GetClientRectSize(infoPtr, size); + return TRUE; + } + + hdc = GetDC(infoPtr->hwnd); + scaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0; + scaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0; + if ((hfont = infoPtr->font)) SelectObject(hdc, hfont); + GetCharWidthW(hdc, '0', '0', &textOffset); + textOffset /= 2; + ReleaseDC(infoPtr->hwnd, hdc); + + checkboxWidth = 12 * scaleX + 1; + checkboxHeight = 12 * scaleY + 1; + if (size->cx) + { + maxWidth = size->cx - checkboxWidth - textOffset; + if (maxWidth <= 0) maxWidth = 1; + } + + /* Checkbox doesn't support both image(but not image list) and text */ + if (!(style & (BS_ICON | BS_BITMAP)) && infoPtr->u.image) + BUTTON_GetTextIdealSize(infoPtr, maxWidth, &labelSize); + else + BUTTON_GetLabelIdealSize(infoPtr, maxWidth, &labelSize); + + size->cx = labelSize.cx + checkboxWidth + textOffset; + size->cy = max(labelSize.cy, checkboxHeight); + + return TRUE; +} + +static BOOL PB_GetIdealSize(BUTTON_INFO *infoPtr, SIZE *size) +{ + WCHAR *text = get_button_text(infoPtr); + SIZE labelSize; + INT textLength = 0; + + if (text) textLength = lstrlenW(text); + + if (textLength == 0) + { + BUTTON_GetClientRectSize(infoPtr, size); + heap_free(text); + return TRUE; + } + heap_free(text); + + /* Ideal size include text size even if image only flags(BS_ICON, BS_BITMAP) are specified */ + BUTTON_GetLabelIdealSize(infoPtr, size->cx, &labelSize); + + size->cx = labelSize.cx; + size->cy = labelSize.cy; + + return TRUE; +} + /********************************************************************** * BUTTON_CalcLayoutRects * diff --git a/dlls/comctl32/tests/button.c b/dlls/comctl32/tests/button.c index 1fd644b6f27..81461dee44a 100644 --- a/dlls/comctl32/tests/button.c +++ b/dlls/comctl32/tests/button.c @@ -1422,6 +1422,301 @@ static void test_state(void) } } +static void test_bcm_get_ideal_size(void) +{ + static const WCHAR text2_w[] = {'t', 'e', 'x', 't', '\n', 't', 'e', 'x', 't', 0}; + static const WCHAR text_w[] = {'t', 'e', 'x', 't', 0}; + static const DWORD imagelist_aligns[] = {BUTTON_IMAGELIST_ALIGN_LEFT, BUTTON_IMAGELIST_ALIGN_RIGHT, + BUTTON_IMAGELIST_ALIGN_TOP, BUTTON_IMAGELIST_ALIGN_BOTTOM, + BUTTON_IMAGELIST_ALIGN_CENTER}; + static const DWORD aligns[] = {0, BS_TOP, BS_LEFT, BS_RIGHT, BS_BOTTOM, + BS_CENTER, BS_VCENTER, BS_RIGHTBUTTON, WS_EX_RIGHT}; + DWORD default_style = WS_TABSTOP | WS_POPUP | WS_VISIBLE; + LONG client_width = 200, client_height = 200; + LONG width, height, line_count; + DWORD style, type; + BOOL ret; + HWND hwnd; + HDC hdc; + HFONT hfont; + LOGFONTA lf; + TEXTMETRICA tm; + SIZE size; + HBITMAP hmask, hbmp; + ICONINFO icon_info; + HICON hicon; + HIMAGELIST himl; + BUTTON_IMAGELIST biml = {0}; + INT i, j; + + /* Check for NULL pointer handling */ + hwnd = CreateWindowW(WC_BUTTONW, text_w, BS_PUSHBUTTON | default_style, 0, 0, client_width, client_height, NULL, + NULL, 0, NULL); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, 0); + ok(!ret, "Expect BCM_GETIDEALSIZE message to return false.\n"); + + /* Set font so that the test is consistent on Wine and Windows */ + ZeroMemory(&lf, sizeof(lf)); + lf.lfWeight = FW_NORMAL; + lf.lfHeight = 20; + lstrcpyA(lf.lfFaceName, "Tahoma"); + hfont = CreateFontIndirectA(&lf); + + /* Get tmHeight */ + hdc = GetDC(hwnd); + GetTextMetricsA(hdc, &tm); + ReleaseDC(hwnd, hdc); + DestroyWindow(hwnd); + + /* XP and 2003 doesn't support command links, getting ideal size with button having only text just return client size on these platforms */ + hwnd = CreateWindowA(WC_BUTTONA, "test", BS_DEFCOMMANDLINK | default_style, 0, 0, client_width, client_height, NULL, + NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + if (size.cx == client_width && size.cy == client_width) + { + /* on XP and 2003, buttons with image are not supported */ + win_skip("Skipping further tests on XP and 2003\n"); + return; + } + + /* Tests for image placements */ + /* Prepare bitmap */ + width = 48; + height = 48; + hdc = GetDC(0); + hmask = CreateCompatibleBitmap(hdc, width, height); + hbmp = CreateCompatibleBitmap(hdc, width, height); + + /* Only bitmap for push button, ideal size should be enough for image and text */ + hwnd = CreateWindowA(WC_BUTTONA, "WWWW", BS_DEFPUSHBUTTON | BS_BITMAP | default_style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + /* Ideal size contains text rect even show bitmap only */ + ok((size.cx >= width + 4 * tm.tmMaxCharWidth && size.cy >= max(height, tm.tmHeight)), + "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, width + 4 * tm.tmMaxCharWidth, size.cy, + max(height, tm.tmHeight)); + DestroyWindow(hwnd); + + /* Image alignments when button has bitmap and text*/ + for (i = 0; i < ARRAY_SIZE(aligns); i++) + for (j = 0; j < ARRAY_SIZE(aligns); j++) + { + style = BS_DEFPUSHBUTTON | default_style | aligns[i] | aligns[j]; + hwnd = CreateWindowA(WC_BUTTONA, "WWWW", style, 0, 0, client_width, client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + if (!(style & (BS_CENTER | BS_VCENTER)) || ((style & BS_CENTER) && (style & BS_CENTER) != BS_CENTER) + || !(style & BS_VCENTER) || (style & BS_VCENTER) == BS_VCENTER) + ok((size.cx >= width + 4 * tm.tmMaxCharWidth && size.cy >= max(height, tm.tmHeight)), + "Style: 0x%08x expect ideal cx %d >= %d and ideal cy %d >= %d\n", style, size.cx, + width + 4 * tm.tmMaxCharWidth, size.cy, max(height, tm.tmHeight)); + else + ok((size.cx >= max(4 * tm.tmMaxCharWidth, height) && size.cy >= height + tm.tmHeight), + "Style: 0x%08x expect ideal cx %d >= %d and ideal cy %d >= %d\n", style, size.cx, + max(4 * tm.tmMaxCharWidth, height), size.cy, height + tm.tmHeight); + DestroyWindow(hwnd); + } + + /* Image list alignments */ + himl = pImageList_Create(width, height, ILC_COLOR, 1, 1); + pImageList_Add(himl, hbmp, 0); + biml.himl = himl; + for (i = 0; i < ARRAY_SIZE(imagelist_aligns); i++) + { + biml.uAlign = imagelist_aligns[i]; + hwnd = CreateWindowA(WC_BUTTONA, "WWWW", BS_DEFPUSHBUTTON | default_style, 0, 0, client_width, client_height, + NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + if (biml.uAlign == BUTTON_IMAGELIST_ALIGN_TOP || biml.uAlign == BUTTON_IMAGELIST_ALIGN_BOTTOM) + ok((size.cx >= max(4 * tm.tmMaxCharWidth, height) && size.cy >= height + tm.tmHeight), + "Align:%d expect ideal cx %d >= %d and ideal cy %d >= %d\n", biml.uAlign, size.cx, + max(4 * tm.tmMaxCharWidth, height), size.cy, height + tm.tmHeight); + else if (biml.uAlign == BUTTON_IMAGELIST_ALIGN_LEFT || biml.uAlign == BUTTON_IMAGELIST_ALIGN_RIGHT) + ok((size.cx >= width + 4 * tm.tmMaxCharWidth && size.cy >= max(height, tm.tmHeight)), + "Align:%d expect ideal cx %d >= %d and ideal cy %d >= %d\n", biml.uAlign, size.cx, + width + 4 * tm.tmMaxCharWidth, size.cy, max(height, tm.tmHeight)); + else + ok(size.cx >= width && size.cy >= height, "Align:%d expect ideal cx %d >= %d and ideal cy %d >= %d\n", + biml.uAlign, size.cx, width, size.cy, height); + DestroyWindow(hwnd); + } + + /* Icon as image */ + /* Create icon from bitmap */ + ZeroMemory(&icon_info, sizeof(icon_info)); + icon_info.fIcon = TRUE; + icon_info.hbmMask = hmask; + icon_info.hbmColor = hbmp; + hicon = CreateIconIndirect(&icon_info); + + /* Only icon, ideal size should be enough for image and text */ + hwnd = CreateWindowA(WC_BUTTONA, "WWWW", BS_DEFPUSHBUTTON | BS_ICON | default_style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hicon); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + /* Ideal size contains text rect even show icons only */ + ok((size.cx >= width + 4 * tm.tmMaxCharWidth && size.cy >= max(height, tm.tmHeight)), + "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, width + 4 * tm.tmMaxCharWidth, size.cy, + max(height, tm.tmHeight)); + DestroyWindow(hwnd); + + /* Show icon and text */ + hwnd = CreateWindowA(WC_BUTTONA, "WWWW", BS_DEFPUSHBUTTON | default_style, 0, 0, client_width, client_height, NULL, + NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hicon); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + ok((size.cx >= width + 4 * tm.tmMaxCharWidth && size.cy >= max(height, tm.tmHeight)), + "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, width + 4 * tm.tmMaxCharWidth, size.cy, + max(height, tm.tmHeight)); + DestroyWindow(hwnd); + + /* Checkbox */ + /* Both bitmap and text for checkbox, ideal size is only enough for text because it doesn't support image(but not image list)*/ + hwnd = CreateWindowA(WC_BUTTONA, "WWWW", BS_AUTOCHECKBOX | default_style, 0, 0, client_width, client_height, NULL, + NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + ok((size.cx <= width + 4 * tm.tmMaxCharWidth && size.cx >= 4 * tm.tmMaxCharWidth + && size.cy <= max(height, tm.tmHeight) && size.cy >= tm.tmHeight), + "Expect ideal cx %d within range (%d, %d ) and ideal cy %d within range (%d, %d )\n", size.cx, + 4 * tm.tmMaxCharWidth, width + 4 * tm.tmMaxCharWidth, size.cy, tm.tmHeight, max(height, tm.tmHeight)); + DestroyWindow(hwnd); + + /* Both image list and text for checkbox, ideal size should have enough for image list and text */ + biml.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT; + hwnd = CreateWindowA(WC_BUTTONA, "WWWW", BS_AUTOCHECKBOX | BS_BITMAP | default_style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + ok((size.cx >= width + 4 * tm.tmMaxCharWidth && size.cy >= max(height, tm.tmHeight)), + "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, width + 4 * tm.tmMaxCharWidth, size.cy, + max(height, tm.tmHeight)); + DestroyWindow(hwnd); + + /* Only bitmap for checkbox, ideal size should have enough for image and text */ + hwnd = CreateWindowA(WC_BUTTONA, "WWWW", BS_AUTOCHECKBOX | BS_BITMAP | default_style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + ok((size.cx >= width + 4 * tm.tmMaxCharWidth && size.cy >= max(height, tm.tmHeight)), + "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, width + 4 * tm.tmMaxCharWidth, size.cy, + max(height, tm.tmHeight)); + DestroyWindow(hwnd); + + /* Test button with only text */ + /* No text */ + for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++) + { + style = type | default_style; + hwnd = CreateWindowA(WC_BUTTONA, "", style, 0, 0, client_width, client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + + 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); + } + else + { + ok(size.cx == client_width && size.cy == client_height, + "Style 0x%08x expect size.cx == %d and size.cy == %d, got size.cx: %d size.cy: %d\n", style, + client_width, client_height, size.cx, size.cy); + } + DestroyWindow(hwnd); + } + + /* Single line and multiple lines text */ + for (line_count = 1; line_count <= 2; line_count++) + { + for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++) + { + style = line_count > 1 ? type | BS_MULTILINE : type; + style |= default_style; + + hwnd = CreateWindowW(WC_BUTTONW, (line_count == 2 ? text2_w : text_w), style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + + if (type == BS_3STATE || type == BS_AUTO3STATE || type == BS_GROUPBOX || type == BS_PUSHBOX + || type == BS_OWNERDRAW) + { + ok(size.cx == client_width && size.cy == client_height, + "Style 0x%08x expect ideal size (%d,%d), got (%d,%d)\n", style, client_width, client_height, size.cx, + size.cy); + } + 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); + } + else + { + width = 0; + height = line_count == 2 ? 2 * tm.tmHeight : tm.tmHeight; + ok(size.cx >= width && size.cy >= height, + "Style 0x%08x expect ideal cx %d >= %d and ideal cy %d >= %d\n", style, size.cx, width, size.cy, + height); + } + DestroyWindow(hwnd); + } + } + + pImageList_Destroy(himl); + DestroyIcon(hicon); + DeleteObject(hbmp); + DeleteObject(hmask); + ReleaseDC(0, hdc); + DeleteObject(hfont); +} + START_TEST(button) { ULONG_PTR ctx_cookie; @@ -1443,6 +1738,7 @@ START_TEST(button) test_get_set_imagelist(); test_get_set_textmargin(); test_state(); + test_bcm_get_ideal_size(); unload_v6_module(ctx_cookie, hCtx); } diff --git a/include/commctrl.h b/include/commctrl.h index 936b2e4026d..77f460a99d0 100644 --- a/include/commctrl.h +++ b/include/commctrl.h @@ -1109,6 +1109,8 @@ typedef struct tagNMBCHOTITEM (BOOL)SNDMSG(button, BCM_GETTEXTMARGIN, 0, (LPARAM)(margin)) #define Button_SetTextMargin(button, margin) \ (BOOL)SNDMSG(button, BCM_SETTEXTMARGIN, 0, (LPARAM)(margin)) +#define Button_GetIdealSize(button, size) \ + (BOOL)SNDMSG(button, BCM_GETIDEALSIZE, 0, (LPARAM)(size)) /* Toolbar */