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

Zhiyi Zhang zzhang at codeweavers.com
Tue Jan 25 01:58:59 CST 2022


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 at 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 */
-- 
2.32.0



More information about the wine-devel mailing list