[PATCH v6 0/2] MR10340: comctl32/tooltips: suppress icon and title when title is NULL or empty.
Per MSDN, TTM_SETTITLE with a NULL or empty title string should suppress both the title row and any associated icon. Previously an empty string was treated the same as a non-empty title, causing the icon to still be displayed and the tooltip to be incorrectly sized. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58236 Signed-off-by: Larry Starr <Larry_A_Starr@yahoo.com> -- v6: comctl32/tooltips: suppress icon and title when title is NULL or empty. comctl32/tooltips/tests: add regression test for TTM_SETTITLE with NULL or empty title. https://gitlab.winehq.org/wine/wine/-/merge_requests/10340
From: Larry Starr <Larry_A_Starr@yahoo.com> Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58236 Signed-off-by: Larry Starr <Larry_A_Starr@yahoo.com> --- dlls/comctl32/tests/tooltips.c | 68 ++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/dlls/comctl32/tests/tooltips.c b/dlls/comctl32/tests/tooltips.c index d34f83806b5..ed393b724b2 100644 --- a/dlls/comctl32/tests/tooltips.c +++ b/dlls/comctl32/tests/tooltips.c @@ -1179,6 +1179,72 @@ static const struct message ttn_show_parent_seq[] = { 0 } }; +/* Helper: activate a tracked tooltip and measure its window size. */ +static void get_tracked_size(HWND tt, TTTOOLINFOW *info, SIZE *sz) +{ + RECT rc; + SendMessageW(tt, TTM_TRACKACTIVATE, TRUE, (LPARAM)info); + SendMessageW(tt, TTM_TRACKPOSITION, 0, MAKELPARAM(10, 10)); + flush_events(100); + GetWindowRect(tt, &rc); + sz->cx = rc.right - rc.left; + sz->cy = rc.bottom - rc.top; + SendMessageW(tt, TTM_TRACKACTIVATE, FALSE, (LPARAM)info); + flush_events(50); +} + +static void test_TTM_SETTITLE(void) +{ + HWND parent, tt; + TTTOOLINFOW info = { 0 }; + SIZE sz_no_title, sz_null_icon, sz_empty_icon; + LRESULT res; + + parent = CreateWindowExW(0, WC_STATICW, NULL, WS_CAPTION | WS_VISIBLE, + 50, 50, 400, 400, NULL, NULL, GetModuleHandleW(NULL), 0); + ok(parent != NULL, "failed to create parent window\n"); + + tt = CreateWindowExW(WS_EX_TOPMOST, TOOLTIPS_CLASSW, NULL, + TTS_NOPREFIX | TTS_ALWAYSTIP, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + parent, NULL, GetModuleHandleW(NULL), 0); + ok(tt != NULL, "failed to create tooltip window\n"); + + info.cbSize = TTTOOLINFOW_V1_SIZE; + info.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_ABSOLUTE; + info.hwnd = parent; + info.hinst = GetModuleHandleW(NULL); + info.lpszText = (WCHAR *)L"x"; + info.uId = (UINT_PTR)parent; + GetClientRect(parent, &info.rect); + + res = SendMessageW(tt, TTM_ADDTOOLW, 0, (LPARAM)&info); + ok(res, "TTM_ADDTOOLW failed\n"); + + /* Baseline: no title, no icon */ + SendMessageW(tt, TTM_SETTITLEW, TTI_NONE, (LPARAM)NULL); + get_tracked_size(tt, &info, &sz_no_title); + + /* Icon requested with NULL title — must be suppressed */ + SendMessageW(tt, TTM_SETTITLEW, TTI_WARNING, (LPARAM)NULL); + get_tracked_size(tt, &info, &sz_null_icon); + todo_wine + ok(sz_null_icon.cx == sz_no_title.cx && sz_null_icon.cy == sz_no_title.cy, + "NULL title + icon: size %ldx%ld, expected %ldx%ld\n", + sz_null_icon.cx, sz_null_icon.cy, sz_no_title.cx, sz_no_title.cy); + + /* Icon requested with empty string title — must also be suppressed */ + SendMessageW(tt, TTM_SETTITLEW, TTI_WARNING, (LPARAM)L""); + get_tracked_size(tt, &info, &sz_empty_icon); + todo_wine + ok(sz_empty_icon.cx == sz_no_title.cx && sz_empty_icon.cy == sz_no_title.cy, + "empty title + icon: size %ldx%ld, expected %ldx%ld\n", + sz_empty_icon.cx, sz_empty_icon.cy, sz_no_title.cx, sz_no_title.cy); + + DestroyWindow(tt); + DestroyWindow(parent); +} + static void test_TTN_SHOW(void) { HWND hwndTip, hwnd; @@ -1255,6 +1321,7 @@ START_TEST(tooltips) test_margin(); test_TTM_ADDTOOL(FALSE); test_TTN_SHOW(); + test_TTM_SETTITLE(); if (!load_v6_module(&ctx_cookie, &hCtx)) return; @@ -1267,6 +1334,7 @@ START_TEST(tooltips) test_margin(); test_TTM_ADDTOOL(TRUE); test_TTN_SHOW(); + test_TTM_SETTITLE(); unload_v6_module(ctx_cookie, hCtx); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10340
From: Larry Starr <Larry_A_Starr@yahoo.com> Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58236 Signed-off-by: Larry Starr <Larry_A_Starr@yahoo.com> --- dlls/comctl32/tests/tooltips.c | 2 -- dlls/comctl32/tooltips.c | 14 ++++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/dlls/comctl32/tests/tooltips.c b/dlls/comctl32/tests/tooltips.c index ed393b724b2..4bd78e47c08 100644 --- a/dlls/comctl32/tests/tooltips.c +++ b/dlls/comctl32/tests/tooltips.c @@ -1228,7 +1228,6 @@ static void test_TTM_SETTITLE(void) /* Icon requested with NULL title — must be suppressed */ SendMessageW(tt, TTM_SETTITLEW, TTI_WARNING, (LPARAM)NULL); get_tracked_size(tt, &info, &sz_null_icon); - todo_wine ok(sz_null_icon.cx == sz_no_title.cx && sz_null_icon.cy == sz_no_title.cy, "NULL title + icon: size %ldx%ld, expected %ldx%ld\n", sz_null_icon.cx, sz_null_icon.cy, sz_no_title.cx, sz_no_title.cy); @@ -1236,7 +1235,6 @@ static void test_TTM_SETTITLE(void) /* Icon requested with empty string title — must also be suppressed */ SendMessageW(tt, TTM_SETTITLEW, TTI_WARNING, (LPARAM)L""); get_tracked_size(tt, &info, &sz_empty_icon); - todo_wine ok(sz_empty_icon.cx == sz_no_title.cx && sz_empty_icon.cy == sz_no_title.cy, "empty title + icon: size %ldx%ld, expected %ldx%ld\n", sz_empty_icon.cx, sz_empty_icon.cy, sz_no_title.cx, sz_no_title.cy); diff --git a/dlls/comctl32/tooltips.c b/dlls/comctl32/tooltips.c index 876736b11e4..f150efeabc1 100644 --- a/dlls/comctl32/tooltips.c +++ b/dlls/comctl32/tooltips.c @@ -1638,7 +1638,8 @@ TOOLTIPS_SetTitleT (TOOLTIPS_INFO *infoPtr, UINT_PTR uTitleIcon, LPCWSTR pszTitl Free(infoPtr->pszTitle); - if (pszTitle) + /* Suppress title and icon if title is NULL or empty (MSDN, bug 58236). */ + if (pszTitle && (isW ? ((LPCWSTR)pszTitle)[0] : ((LPCSTR)pszTitle)[0])) { if (isW) { @@ -1671,10 +1672,15 @@ TOOLTIPS_SetTitleT (TOOLTIPS_INFO *infoPtr, UINT_PTR uTitleIcon, LPCWSTR pszTitl infoPtr->iconHeight = GetSystemMetrics(SM_CYSMICON); } - if (uTitleIcon <= TTI_ERROR_LARGE) - infoPtr->hTitleIcon = hTooltipIcons[uTitleIcon]; + if (infoPtr->pszTitle) + { + if (uTitleIcon <= TTI_ERROR_LARGE) + infoPtr->hTitleIcon = hTooltipIcons[uTitleIcon]; + else + infoPtr->hTitleIcon = CopyIcon((HICON)uTitleIcon); + } else - infoPtr->hTitleIcon = CopyIcon((HICON)uTitleIcon); + infoPtr->hTitleIcon = NULL; TRACE("icon = %p\n", infoPtr->hTitleIcon); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10340
On Tue Apr 28 04:01:42 2026 +0000, Zhiyi Zhang wrote:
Are these flush_events() necessary? My experience with windows programming is limited, but my understanding is we need them to ensure the gui has time to finish the drawing before we then go and check things. Test appears to work as is so I do not see a need to remove them. Do you see any issue with them?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10340#note_138119
The urlmon/protocol.c CI failures are HTTP timeout flakes unrelated to this patch. Happy to address any further feedback. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10340#note_138123
On Wed Apr 29 02:52:20 2026 +0000, Larry Starr wrote:
My experience with windows programming is limited, but my understanding is we need them to ensure the gui has time to finish the drawing before we then go and check things. Test appears to work as is so I do not see a need to remove them. Do you see any issue with them? flush_events() is mostly needed when we need to wait for things like x11 events or rendering things on screen like you said. However, I don't think this is either of those cases. You are testing win32 states, which should be the same without `flush_events()`. I tried removing them and the tests run fine. I just don't see the need for them.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10340#note_138195
participants (3)
-
Larry Starr -
Larry Starr (@starrl) -
Zhiyi Zhang (@zhiyi)