[PATCH 2/8] comctl32/button: Implement NM_CUSTOMDRAW for Push Buttons

Gabriel Ivăncescu gabrielopcode at gmail.com
Mon Mar 4 10:25:15 CST 2019


Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=10531
Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
---

This fixes WinXP calc perfectly, it seems it's how Windows does it (yes,
the frame drawing is before pre-paint notification, otherwise it won't
look correctly).

Tests for all of them are at the end of the patch series, because they are
done in an existing loop and it would be a nightmare otherwise to add todos
only for some buttons but not others. The MSDN documentation is wrong and
a mess for some CDRF_* values and many other info online gets it wrong,
as the tests demonstrate.

 dlls/comctl32/button.c | 105 +++++++++++++++++++++++++++++++----------
 1 file changed, 79 insertions(+), 26 deletions(-)

diff --git a/dlls/comctl32/button.c b/dlls/comctl32/button.c
index 56a4730..25a46a4 100644
--- a/dlls/comctl32/button.c
+++ b/dlls/comctl32/button.c
@@ -237,6 +237,30 @@ static inline WCHAR *get_button_text( const BUTTON_INFO *infoPtr )
     return buffer;
 }
 
+static void init_custom_draw(NMCUSTOMDRAW *nmcd, const BUTTON_INFO *info, HDC hDC, const RECT *rc)
+{
+    HWND hwnd = info->hwnd;
+
+    nmcd->hdr.hwndFrom = hwnd;
+    nmcd->hdr.idFrom   = GetWindowLongPtrW(hwnd, GWLP_ID);
+    nmcd->hdr.code     = NM_CUSTOMDRAW;
+    nmcd->hdc          = hDC;
+    nmcd->rc           = *rc;
+    nmcd->dwDrawStage  = CDDS_PREERASE;
+    nmcd->dwItemSpec   = 0;
+    nmcd->lItemlParam  = 0;
+    nmcd->uItemState   = !IsWindowEnabled(hwnd) ? CDIS_DISABLED : 0;
+    if (info->state & BST_PUSHED)  nmcd->uItemState |= CDIS_SELECTED;
+    if (info->state & BST_FOCUS)   nmcd->uItemState |= CDIS_FOCUS;
+    if (info->state & BST_HOT)     nmcd->uItemState |= CDIS_HOT;
+    if (info->state & BST_INDETERMINATE)
+        nmcd->uItemState |= CDIS_INDETERMINATE;
+
+    /* Windows doesn't seem to send CDIS_CHECKED (it fails the tests) */
+    /* CDIS_SHOWKEYBOARDCUES is misleading, as the meaning is reversed */
+    /* FIXME: Handle it properly when we support keyboard cues? */
+}
+
 HRGN set_control_clipping( HDC hdc, const RECT *rect )
 {
     RECT rc = *rect;
@@ -1476,7 +1500,9 @@ static void PB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action )
     HBRUSH   hOldBrush;
     INT      oldBkMode;
     COLORREF oldTxtColor;
+    LRESULT  cdrf;
     HFONT hFont;
+    NMCUSTOMDRAW nmcd;
     LONG state = infoPtr->state;
     LONG style = GetWindowLongW( infoPtr->hwnd, GWL_STYLE );
     BOOL pushedState = (state & BST_PUSHED);
@@ -1498,6 +1524,12 @@ static void PB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action )
     hOldBrush = SelectObject(hDC,GetSysColorBrush(COLOR_BTNFACE));
     oldBkMode = SetBkMode(hDC, TRANSPARENT);
 
+    init_custom_draw(&nmcd, infoPtr, hDC, &rc);
+
+    /* Send erase notifications */
+    cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
+    if (cdrf & CDRF_SKIPDEFAULT) goto cleanup;
+
     if (get_button_type(style) == BS_DEFPUSHBUTTON)
     {
         if (action != ODA_FOCUS)
@@ -1505,44 +1537,65 @@ static void PB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action )
 	InflateRect( &rc, -1, -1 );
     }
 
-    /* completely skip the drawing if only focus has changed */
-    if (action == ODA_FOCUS) goto draw_focus;
+    /* Skip the frame drawing if only focus has changed */
+    if (action != ODA_FOCUS)
+    {
+        uState = DFCS_BUTTONPUSH;
 
-    uState = DFCS_BUTTONPUSH;
+        if (style & BS_FLAT)
+            uState |= DFCS_MONO;
+        else if (pushedState)
+        {
+            if (get_button_type(style) == BS_DEFPUSHBUTTON )
+                uState |= DFCS_FLAT;
+            else
+                uState |= DFCS_PUSHED;
+        }
 
-    if (style & BS_FLAT)
-        uState |= DFCS_MONO;
-    else if (pushedState)
-    {
-	if (get_button_type(style) == BS_DEFPUSHBUTTON )
-	    uState |= DFCS_FLAT;
-	else
-	    uState |= DFCS_PUSHED;
+        if (state & (BST_CHECKED | BST_INDETERMINATE))
+            uState |= DFCS_CHECKED;
+
+        DrawFrameControl( hDC, &rc, DFC_BUTTON, uState );
     }
 
-    if (state & (BST_CHECKED | BST_INDETERMINATE))
-        uState |= DFCS_CHECKED;
+    if (cdrf & CDRF_NOTIFYPOSTERASE)
+    {
+        nmcd.dwDrawStage = CDDS_POSTERASE;
+        SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
+    }
 
-    DrawFrameControl( hDC, &rc, DFC_BUTTON, uState );
+    /* Send paint notifications */
+    nmcd.dwDrawStage = CDDS_PREPAINT;
+    cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
+    if (cdrf & CDRF_SKIPDEFAULT) goto cleanup;
 
-    /* draw button label */
-    labelRect = rc;
-    /* Shrink label rect at all sides by 2 so that the content won't touch the surrounding frame */
-    InflateRect(&labelRect, -2, -2);
-    dtFlags = BUTTON_CalcLayoutRects(infoPtr, hDC, &labelRect, &imageRect, &textRect);
+    if (!(cdrf & CDRF_DOERASE) && action != ODA_FOCUS)
+    {
+        /* draw button label */
+        labelRect = rc;
+        /* Shrink label rect at all sides by 2 so that the content won't touch the surrounding frame */
+        InflateRect(&labelRect, -2, -2);
+        dtFlags = BUTTON_CalcLayoutRects(infoPtr, hDC, &labelRect, &imageRect, &textRect);
 
-    if (dtFlags == (UINT)-1L)
-       goto cleanup;
+        if (dtFlags != (UINT)-1L)
+        {
+            if (pushedState) OffsetRect(&labelRect, 1, 1);
 
-    if (pushedState) OffsetRect(&labelRect, 1, 1);
+            oldTxtColor = SetTextColor( hDC, GetSysColor(COLOR_BTNTEXT) );
 
-    oldTxtColor = SetTextColor( hDC, GetSysColor(COLOR_BTNTEXT) );
+            BUTTON_DrawLabel(infoPtr, hDC, dtFlags, &imageRect, &textRect);
 
-    BUTTON_DrawLabel(infoPtr, hDC, dtFlags, &imageRect, &textRect);
+            SetTextColor( hDC, oldTxtColor );
+        }
+    }
 
-    SetTextColor( hDC, oldTxtColor );
+    if (cdrf & CDRF_NOTIFYPOSTPAINT)
+    {
+        nmcd.dwDrawStage = CDDS_POSTPAINT;
+        SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd);
+    }
+    if ((cdrf & CDRF_SKIPPOSTPAINT) || dtFlags == (UINT)-1L) goto cleanup;
 
-draw_focus:
     if (action == ODA_FOCUS || (state & BST_FOCUS))
     {
         InflateRect( &rc, -2, -2 );
-- 
2.20.1




More information about the wine-devel mailing list