When an application handles WM_NCPAINT by itself, standard scroll bars are not drawn even if WS_HSCROLL or WS_VSCROLL is used. In this case, when handling WM_NCMOUSEMOVE, DefWinProc() shouldn't repaint standard scroll bars to reflect hovered state, otherwise, DefWinProc() draws over the custom non-client area painted by the application. This is also the behavior on XP.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=51900 Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- dlls/user32/painting.c | 13 +++++++++++- dlls/user32/scroll.c | 41 ++++++++++++++++++++++++++++++++++++-- dlls/user32/tests/win.c | 1 - dlls/user32/user_private.h | 1 + 4 files changed, 52 insertions(+), 4 deletions(-)
diff --git a/dlls/user32/painting.c b/dlls/user32/painting.c index 41c0bb3c6e1..63a8e840998 100644 --- a/dlls/user32/painting.c +++ b/dlls/user32/painting.c @@ -639,6 +639,7 @@ static HRGN send_ncpaint( HWND hwnd, HWND *child, UINT *flags ) { HRGN whole_rgn = get_update_region( hwnd, flags, child ); HRGN client_rgn = 0; + DWORD style;
if (child) hwnd = *child;
@@ -678,7 +679,17 @@ static HRGN send_ncpaint( HWND hwnd, HWND *child, UINT *flags )
if (whole_rgn) /* NOTE: WM_NCPAINT allows wParam to be 1 */ { - if (*flags & UPDATE_NONCLIENT) SendMessageW( hwnd, WM_NCPAINT, (WPARAM)whole_rgn, 0 ); + if (*flags & UPDATE_NONCLIENT) + { + /* Mark standard scroll bars as not painted before sending WM_NCPAINT */ + style = GetWindowLongW( hwnd, GWL_STYLE ); + if (style & WS_HSCROLL) + SCROLL_SetStandardScrollPainted( hwnd, SB_HORZ, FALSE ); + if (style & WS_VSCROLL) + SCROLL_SetStandardScrollPainted( hwnd, SB_VERT, FALSE ); + + SendMessageW( hwnd, WM_NCPAINT, (WPARAM)whole_rgn, 0 ); + } if (whole_rgn > (HRGN)1) DeleteObject( whole_rgn ); } SetThreadDpiAwarenessContext( context ); diff --git a/dlls/user32/scroll.c b/dlls/user32/scroll.c index a17495d9ff0..a3f0becbbd6 100644 --- a/dlls/user32/scroll.c +++ b/dlls/user32/scroll.c @@ -39,6 +39,7 @@ typedef struct INT maxVal; /* Maximum scroll-bar value */ INT page; /* Page size of scroll bar (Win32) */ UINT flags; /* EnableScrollBar flags */ + BOOL painted; /* Whether the scroll bar is painted by DefWinProc() */ } SCROLLBAR_INFO, *LPSCROLLBAR_INFO;
/* data for window that has (one or two) scroll bars */ @@ -643,6 +644,29 @@ void WINAPI USER_ScrollBarDraw( HWND hwnd, HDC hdc, INT nBar, enum SCROLL_HITTES } }
+void SCROLL_SetStandardScrollPainted( HWND hwnd, INT bar, BOOL painted ) +{ + LPSCROLLBAR_INFO info; + + if (bar != SB_HORZ && bar != SB_VERT) + return; + + info = SCROLL_GetInternalInfo( hwnd, bar, FALSE ); + if (info) + info->painted = painted; +} + +static BOOL SCROLL_IsStandardScrollPainted( HWND hwnd, INT bar ) +{ + LPSCROLLBAR_INFO info; + + if (bar != SB_HORZ && bar != SB_VERT) + return FALSE; + + info = SCROLL_GetInternalInfo( hwnd, bar, FALSE ); + return info ? info->painted : FALSE; +} + /*********************************************************************** * SCROLL_DrawScrollBar * @@ -653,9 +677,9 @@ void SCROLL_DrawScrollBar( HWND hwnd, HDC hdc, INT bar, enum SCROLL_HITTEST hit_ BOOL draw_interior ) { INT arrow_size, thumb_size, thumb_pos; + RECT rect, clip_box, intersect; BOOL vertical; DWORD style; - RECT rect;
if (!(hwnd = WIN_GetFullHandle( hwnd ))) return; @@ -682,6 +706,13 @@ void SCROLL_DrawScrollBar( HWND hwnd, HDC hdc, INT bar, enum SCROLL_HITTEST hit_ GetCapture()); user_api->pScrollBarDraw( hwnd, hdc, bar, hit_test, tracking_info, draw_arrows, draw_interior, &rect, arrow_size, thumb_pos, thumb_size, vertical ); + + if (bar == SB_HORZ || bar == SB_VERT) + { + GetClipBox( hdc, &clip_box ); + if (IntersectRect(&intersect, &rect, &clip_box)) + SCROLL_SetStandardScrollPainted( hwnd, bar, TRUE ); + } }
void SCROLL_DrawNCScrollBar( HWND hwnd, HDC hdc, BOOL draw_horizontal, BOOL draw_vertical ) @@ -899,7 +930,13 @@ void SCROLL_HandleScrollEvent( HWND hwnd, INT nBar, UINT msg, POINT pt ) switch (g_tracking_info.hit_test) { case SCROLL_NOWHERE: /* No tracking in progress */ - if (msg == WM_MOUSEMOVE || msg == WM_MOUSELEAVE || msg == WM_NCMOUSEMOVE || msg == WM_NCMOUSELEAVE) + /* For standard scroll bars, hovered state gets painted only when the scroll bar was + * previously painted by DefWinProc(). If an application handles WM_NCPAINT by itself, then + * the scrollbar shouldn't be repainted here to avoid overwriting the application painted + * content */ + if (msg == WM_MOUSEMOVE || msg == WM_MOUSELEAVE + || ((msg == WM_NCMOUSEMOVE || msg == WM_NCMOUSELEAVE) + && SCROLL_IsStandardScrollPainted( hwnd, nBar))) SCROLL_DrawScrollBar( hwnd, hdc, nBar, hittest, &g_tracking_info, TRUE, TRUE ); break;
diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index ed90c5fbe5f..cb6b61e59c0 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -1269,7 +1269,6 @@ static void test_nonclient_area(HWND hwnd) RedrawWindow(child, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ERASENOW | RDW_FRAME); color = GetPixel(hdc, 50 - GetSystemMetrics(SM_CXVSCROLL) / 2, 50 - GetSystemMetrics(SM_CYHSCROLL) / 2); - todo_wine ok(color == old_color, "Expected color %#x, got %#x.\n", old_color, color);
ReleaseDC(parent, hdc); diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h index 7a9845adeb9..3b542ea1452 100644 --- a/dlls/user32/user_private.h +++ b/dlls/user32/user_private.h @@ -308,5 +308,6 @@ LRESULT WINAPI USER_ScrollBarProc(HWND, UINT, WPARAM, LPARAM, BOOL) DECLSPEC_HID void WINAPI USER_ScrollBarDraw(HWND, HDC, INT, enum SCROLL_HITTEST, const struct SCROLL_TRACKING_INFO *, BOOL, BOOL, RECT *, INT, INT, INT, BOOL) DECLSPEC_HIDDEN; +void SCROLL_SetStandardScrollPainted(HWND hwnd, INT bar, BOOL visible);
#endif /* __WINE_USER_PRIVATE_H */
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=105904
Your paranoid android.
=== w10pro64 (32 bit report) ===
user32: win.c:2392: Test failed: style 0x200000: expected !100 win.c:2392: Test failed: style 0x300000: expected !100
=== w10pro64 (64 bit report) ===
user32: win.c:2392: Test failed: style 0x200000: expected !100 win.c:2392: Test failed: style 0x300000: expected !100
=== w10pro64_he (64 bit report) ===
user32: win.c:9874: Test failed: didn't get start_event
=== debian11 (64 bit WoW report) ===
user32: menu.c:2337: Test failed: test 25