[PATCH v7 0/4] MR11491: comctl32/listbox: Fix SetRedraw top item handling
The old code was not correct for multicolumn listboxes, so it caused the items to be displayed at an incorrect offset. LISTBOX_GetMaxTopIndex implements special cases for MULTICOLUMN and OWNERDRAWVARIABLE listboxes, allowing the top item to remain consistent. The fix is taken from the following bug report: https://bugs.winehq.org/show_bug.cgi?id=59591 -- v7: user32/listbox: Fix SetRedraw top item handling comctl32/listbox: Fix SetRedraw top item handling user32/tests: Check multicolumn listbox redraw comctl32/tests: Check multicolumn listbox redraw https://gitlab.winehq.org/wine/wine/-/merge_requests/11491
From: Tobiasz Laskowski <tlaskowski@codeweavers.com> Wine-bug: https://bugs.winehq.org/show_bug.cgi?id=59591 --- dlls/comctl32/tests/listbox.c | 46 +++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/dlls/comctl32/tests/listbox.c b/dlls/comctl32/tests/listbox.c index 8ac595a355b..568ed727424 100644 --- a/dlls/comctl32/tests/listbox.c +++ b/dlls/comctl32/tests/listbox.c @@ -2996,6 +2996,51 @@ static void test_integral_resize(void) DestroyWindow(parent); } +static void test_multicolumn_redraw(void) +{ + HWND parent, listbox; + int top_index_before, top_index_after; + int i, initial_items = 7; + int ret; + + parent = create_parent(); + listbox = CreateWindowA(WC_LISTBOXA, "TestList", + WS_CHILD | WS_VISIBLE | LBS_MULTICOLUMN, + 0, 0, 200, 100, parent, NULL, NULL, 0); + ok(!!listbox, "got error %lu\n", GetLastError()); + + /* Add enough items to span multiple columns */ + for (i = 0; i < initial_items; i++) + { + SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item"); + } + + /* Scroll to bottom of list */ + ret = SendMessageA(listbox, LB_SETTOPINDEX, initial_items - 1, 0); + ok(!ret, "got %d\n", ret); + + top_index_before = SendMessageA(listbox, LB_GETTOPINDEX, 0, 0); + ok(top_index_before > 0, "Expected non-zero top index.\n"); + + ret = SendMessageA(listbox, WM_SETREDRAW, FALSE, 0); + ok(!ret, "got %d\n", ret); + + /* Force LBS_DISPLAYCHANGED */ + SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"new item"); + + ret = SendMessageA(listbox, WM_SETREDRAW, TRUE, 0); + ok(!ret, "got %d\n", ret); + + /* Ensure top_index is correct after redraw */ + top_index_after = SendMessageA(listbox, LB_GETTOPINDEX, 0, 0); + todo_wine + ok(top_index_after == top_index_before, + "Expected top index %d after WM_SETREDRAW, got %d\n", + top_index_before, top_index_after); + + DestroyWindow(parent); +} + START_TEST(listbox) { ULONG_PTR ctx_cookie; @@ -3030,6 +3075,7 @@ START_TEST(listbox) test_LB_FINDSTRING(); test_keypresses(); test_integral_resize(); + test_multicolumn_redraw(); uninit_winevent_hook(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11491
From: Tobiasz Laskowski <tlaskowski@codeweavers.com> --- dlls/user32/tests/listbox.c | 46 +++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/dlls/user32/tests/listbox.c b/dlls/user32/tests/listbox.c index 3aba6a297bf..3aa5da17dd3 100644 --- a/dlls/user32/tests/listbox.c +++ b/dlls/user32/tests/listbox.c @@ -2579,6 +2579,51 @@ static void test_integral_resize(void) DestroyWindow(parent); } +static void test_multicolumn_redraw(void) +{ + HWND parent, listbox; + int top_index_before, top_index_after; + int i, initial_items = 7; + int ret; + + parent = create_parent(); + listbox = CreateWindowA("listbox", "TestList", + WS_CHILD | WS_VISIBLE | LBS_MULTICOLUMN, + 0, 0, 200, 100, parent, NULL, NULL, 0); + ok(!!listbox, "got error %lu\n", GetLastError()); + + /* Add enough items to span multiple columns */ + for (i = 0; i < initial_items; i++) + { + SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item"); + } + + /* Scroll to bottom of list */ + ret = SendMessageA(listbox, LB_SETTOPINDEX, initial_items - 1, 0); + ok(!ret, "got %d\n", ret); + + top_index_before = SendMessageA(listbox, LB_GETTOPINDEX, 0, 0); + ok(top_index_before > 0, "Expected non-zero top index.\n"); + + ret = SendMessageA(listbox, WM_SETREDRAW, FALSE, 0); + ok(!ret, "got %d\n", ret); + + /* Force LBS_DISPLAYCHANGED */ + SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"new item"); + + ret = SendMessageA(listbox, WM_SETREDRAW, TRUE, 0); + ok(!ret, "got %d\n", ret); + + /* Ensure top_index is correct after redraw */ + top_index_after = SendMessageA(listbox, LB_GETTOPINDEX, 0, 0); + todo_wine + ok(top_index_after == top_index_before, + "Expected top index %d after WM_SETREDRAW, got %d\n", + top_index_before, top_index_after); + + DestroyWindow(parent); +} + START_TEST(listbox) { const struct listbox_test SS = @@ -2680,4 +2725,5 @@ START_TEST(listbox) test_LBS_NODATA(); test_LB_FINDSTRING(); test_integral_resize(); + test_multicolumn_redraw(); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11491
From: Tobiasz Laskowski <tlaskowski@codeweavers.com> The old code was not correct for multicolumn listboxes, so it caused the items to be displayed at an incorrect offset. LISTBOX_GetMaxTopIndex implements special cases for MULTICOLUMN and OWNERDRAWVARIABLE listboxes, allowing the top item to remain consistent. Wine-bug: https://bugs.winehq.org/show_bug.cgi?id=59591 --- dlls/comctl32/tests/listbox.c | 1 - dlls/comctl32_v6/listbox.c | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/dlls/comctl32/tests/listbox.c b/dlls/comctl32/tests/listbox.c index 568ed727424..80f0f74a3f7 100644 --- a/dlls/comctl32/tests/listbox.c +++ b/dlls/comctl32/tests/listbox.c @@ -3033,7 +3033,6 @@ static void test_multicolumn_redraw(void) /* Ensure top_index is correct after redraw */ top_index_after = SendMessageA(listbox, LB_GETTOPINDEX, 0, 0); - todo_wine ok(top_index_after == top_index_before, "Expected top index %d after WM_SETREDRAW, got %d\n", top_index_before, top_index_after); diff --git a/dlls/comctl32_v6/listbox.c b/dlls/comctl32_v6/listbox.c index 7dec0e2d999..a46f7b03f16 100644 --- a/dlls/comctl32_v6/listbox.c +++ b/dlls/comctl32_v6/listbox.c @@ -730,8 +730,7 @@ static void LISTBOX_SetRedraw( LB_DESCR *descr, BOOL on ) InvalidateRect(descr->self, NULL, TRUE); if ((descr->top_item + descr->page_size) > descr->nb_items) { /* reset top of page if less than number of items/page */ - descr->top_item = descr->nb_items - descr->page_size; - if (descr->top_item < 0) descr->top_item = 0; + descr->top_item = LISTBOX_GetMaxTopIndex(descr); } descr->style &= ~LBS_DISPLAYCHANGED; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11491
From: Tobiasz Laskowski <tlaskowski@codeweavers.com> Based on comctl32 patch --- dlls/user32/listbox.c | 3 +-- dlls/user32/tests/listbox.c | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/dlls/user32/listbox.c b/dlls/user32/listbox.c index c569b296842..7338dd12bc5 100644 --- a/dlls/user32/listbox.c +++ b/dlls/user32/listbox.c @@ -739,8 +739,7 @@ static void LISTBOX_SetRedraw( LB_DESCR *descr, BOOL on ) NtUserInvalidateRect(descr->self, NULL, TRUE); if ((descr->top_item + descr->page_size) > descr->nb_items) { /* reset top of page if less than number of items/page */ - descr->top_item = descr->nb_items - descr->page_size; - if (descr->top_item < 0) descr->top_item = 0; + descr->top_item = LISTBOX_GetMaxTopIndex(descr); } descr->style &= ~LBS_DISPLAYCHANGED; } diff --git a/dlls/user32/tests/listbox.c b/dlls/user32/tests/listbox.c index 3aa5da17dd3..98ddc9c0377 100644 --- a/dlls/user32/tests/listbox.c +++ b/dlls/user32/tests/listbox.c @@ -2616,7 +2616,6 @@ static void test_multicolumn_redraw(void) /* Ensure top_index is correct after redraw */ top_index_after = SendMessageA(listbox, LB_GETTOPINDEX, 0, 0); - todo_wine ok(top_index_after == top_index_before, "Expected top index %d after WM_SETREDRAW, got %d\n", top_index_before, top_index_after); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11491
On Tue Aug 4 08:32:28 2026 +0000, Tobi Laskowski wrote:
changed this line in [version 7 of the diff](/wine/wine/-/merge_requests/11491/diffs?diff_id=287013&start_sha=a69b2efd9257475f6c1d0c621290f975243b7309#6038eb798096cf8eaf0a865e0367ac489e849710_3040_3040) I've updated accordingly
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11491#note_147809
participants (2)
-
Tobi Laskowski (@tobil) -
Tobiasz Laskowski