user32: Draw standard scroll bars in hovered state only when they were previously painted by DefWinProc().

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>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Zhiyi Zhang 2022-01-25 15:58:59 +08:00 committed by Alexandre Julliard
parent b9110622be
commit 78ac7ae24d
4 changed files with 52 additions and 4 deletions

View file

@ -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 );

View file

@ -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;

View file

@ -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);

View file

@ -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 */