Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- include/commctrl.h | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/include/commctrl.h b/include/commctrl.h index 73bb9fa..9b86bdb 100644 --- a/include/commctrl.h +++ b/include/commctrl.h @@ -216,11 +216,13 @@ typedef struct tagNMCHAR #define CDRF_DODEFAULT 0x0 #define CDRF_NEWFONT 0x00000002 #define CDRF_SKIPDEFAULT 0x00000004 +#define CDRF_DOERASE 0x00000008 #define CDRF_NOTIFYPOSTPAINT 0x00000010 #define CDRF_NOTIFYITEMDRAW 0x00000020 #define CDRF_NOTIFYSUBITEMDRAW 0x00000020 #define CDRF_NOTIFYPOSTERASE 0x00000040 #define CDRF_NOTIFYITEMERASE 0x00000080 /* obsolete ??? */ +#define CDRF_SKIPPOSTPAINT 0x00000100
/* drawstage flags */
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=10531 Signed-off-by: Gabriel Ivăncescu gabrielopcode@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 );
On 3/4/19 7:25 PM, Gabriel Ivăncescu wrote:
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=10531 Signed-off-by: Gabriel Ivăncescu gabrielopcode@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? */
+}
For consistency, could you make it 'infoPtr' and 'hdc'? Other minor simplifications, separate 'hwnd' variable seems unnecessary, and uItemState condition could be flipped to get rid of negation.
On 3/4/19 8:19 PM, Nikolay Sivov wrote:
On 3/4/19 7:25 PM, Gabriel Ivăncescu wrote:
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=10531 Signed-off-by: Gabriel Ivăncescu gabrielopcode@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? */ +}
For consistency, could you make it 'infoPtr' and 'hdc'? Other minor simplifications, separate 'hwnd' variable seems unnecessary, and uItemState condition could be flipped to get rid of negation.
Hi Nikolay,
Sure I'll do that and the other changes, thanks for the fast review. The local var is just a habit I have to shorten the code when accessing a field more than a couple times.
Now I'm trying to see if there's another way to avoid the double-painting special case in the tests, since it doesn't happen on my Win7 VM (but does on the testbot, for some reason).
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=10531 Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
This also fixes WinXP calc when themes are enabled.
dlls/comctl32/button.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-)
diff --git a/dlls/comctl32/button.c b/dlls/comctl32/button.c index 25a46a4..4f0128c 100644 --- a/dlls/comctl32/button.c +++ b/dlls/comctl32/button.c @@ -1906,19 +1906,50 @@ static void PB_ThemedPaint(HTHEME theme, const BUTTON_INFO *infoPtr, HDC hDC, in HFONT font = infoPtr->font; HFONT hPrevFont = font ? SelectObject(hDC, font) : NULL; WCHAR *text = get_button_text(infoPtr); + NMCUSTOMDRAW nmcd; + LRESULT cdrf; + HWND parent;
GetClientRect(infoPtr->hwnd, &bgRect); + init_custom_draw(&nmcd, infoPtr, hDC, &bgRect); GetThemeBackgroundContentRect(theme, hDC, BP_PUSHBUTTON, state, &bgRect, &textRect);
+ parent = GetParent(infoPtr->hwnd); + if (!parent) parent = infoPtr->hwnd; + + /* Send erase notifications */ + cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd); + if (cdrf & CDRF_SKIPDEFAULT) goto cleanup; + if (IsThemeBackgroundPartiallyTransparent(theme, BP_PUSHBUTTON, state)) DrawThemeParentBackground(infoPtr->hwnd, hDC, NULL); DrawThemeBackground(theme, hDC, BP_PUSHBUTTON, state, &bgRect, NULL); + + if (cdrf & CDRF_NOTIFYPOSTERASE) + { + nmcd.dwDrawStage = CDDS_POSTERASE; + SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd); + } + + /* Send paint notifications */ + nmcd.dwDrawStage = CDDS_PREPAINT; + cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd); + if (cdrf & CDRF_SKIPDEFAULT) goto cleanup; + if (text) { - DrawThemeText(theme, hDC, BP_PUSHBUTTON, state, text, lstrlenW(text), dtFlags, 0, &textRect); + if (!(cdrf & CDRF_DOERASE)) + DrawThemeText(theme, hDC, BP_PUSHBUTTON, state, text, lstrlenW(text), dtFlags, 0, &textRect); heap_free(text); }
+ if (cdrf & CDRF_NOTIFYPOSTPAINT) + { + nmcd.dwDrawStage = CDDS_POSTPAINT; + SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd); + } + if (cdrf & CDRF_SKIPPOSTPAINT) goto cleanup; + if (focused) { MARGINS margins; @@ -1934,6 +1965,7 @@ static void PB_ThemedPaint(HTHEME theme, const BUTTON_INFO *infoPtr, HDC hDC, in DrawFocusRect( hDC, &focusRect ); }
+cleanup: if (hPrevFont) SelectObject(hDC, hPrevFont); }
On 3/4/19 7:25 PM, Gabriel Ivăncescu wrote:
GetClientRect(infoPtr->hwnd, &bgRect);
- init_custom_draw(&nmcd, infoPtr, hDC, &bgRect); GetThemeBackgroundContentRect(theme, hDC, BP_PUSHBUTTON, state, &bgRect, &textRect);
This looks like a workaround, while it is not. Could you move your init call after uxtheme one? I know that bgRect is [in] argument, but it's not obvious.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Most of this is indentation change. I took the liberty of fixing formatting also since it's indented already.
dlls/comctl32/button.c | 129 ++++++++++++++++++++++++++--------------- 1 file changed, 83 insertions(+), 46 deletions(-)
diff --git a/dlls/comctl32/button.c b/dlls/comctl32/button.c index 4f0128c..88f8f2f 100644 --- a/dlls/comctl32/button.c +++ b/dlls/comctl32/button.c @@ -1621,7 +1621,9 @@ static void CB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action ) HBRUSH hBrush; int delta, text_offset, checkBoxWidth, checkBoxHeight; UINT dtFlags; + LRESULT cdrf; HFONT hFont; + NMCUSTOMDRAW nmcd; LONG state = infoPtr->state; LONG style = GetWindowLongW( infoPtr->hwnd, GWL_STYLE ); LONG ex_style = GetWindowLongW( infoPtr->hwnd, GWL_EXSTYLE ); @@ -1662,9 +1664,20 @@ static void CB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action ) rbox.right = checkBoxWidth; }
+ init_custom_draw(&nmcd, infoPtr, hDC, &client); + + /* Send erase notifications */ + cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd); + if (cdrf & CDRF_SKIPDEFAULT) goto cleanup; + /* Since WM_ERASEBKGND does nothing, first prepare background */ if (action == ODA_SELECT) FillRect( hDC, &rbox, hBrush ); if (action == ODA_DRAWENTIRE) FillRect( hDC, &client, hBrush ); + if (cdrf & CDRF_NOTIFYPOSTERASE) + { + nmcd.dwDrawStage = CDDS_POSTERASE; + SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd); + }
/* Draw label */ client = labelRect; @@ -1677,57 +1690,79 @@ static void CB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action ) rbox.bottom = labelRect.bottom; }
+ /* Send paint notifications */ + nmcd.dwDrawStage = CDDS_PREPAINT; + cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd); + if (cdrf & CDRF_SKIPDEFAULT) goto cleanup; + /* Draw the check-box bitmap */ - if (action == ODA_DRAWENTIRE || action == ODA_SELECT) + if (!(cdrf & CDRF_DOERASE)) { - UINT flags; - - if ((get_button_type(style) == BS_RADIOBUTTON) || - (get_button_type(style) == BS_AUTORADIOBUTTON)) flags = DFCS_BUTTONRADIO; - else if (state & BST_INDETERMINATE) flags = DFCS_BUTTON3STATE; - else flags = DFCS_BUTTONCHECK; - - if (state & (BST_CHECKED | BST_INDETERMINATE)) flags |= DFCS_CHECKED; - if (state & BST_PUSHED) flags |= DFCS_PUSHED; - - if (style & WS_DISABLED) flags |= DFCS_INACTIVE; - - /* rbox must have the correct height */ - delta = rbox.bottom - rbox.top - checkBoxHeight; - - if ((style & BS_VCENTER) == BS_TOP) { - if (delta > 0) { - rbox.bottom = rbox.top + checkBoxHeight; - } else { - rbox.top -= -delta/2 + 1; - rbox.bottom = rbox.top + checkBoxHeight; - } - } else if ((style & BS_VCENTER) == BS_BOTTOM) { - if (delta > 0) { - rbox.top = rbox.bottom - checkBoxHeight; - } else { - rbox.bottom += -delta/2 + 1; - rbox.top = rbox.bottom - checkBoxHeight; - } - } else { /* Default */ - if (delta > 0) { - int ofs = (delta / 2); - rbox.bottom -= ofs + 1; - rbox.top = rbox.bottom - checkBoxHeight; - } else if (delta < 0) { - int ofs = (-delta / 2); - rbox.top -= ofs + 1; - rbox.bottom = rbox.top + checkBoxHeight; - } - } + if (action == ODA_DRAWENTIRE || action == ODA_SELECT) + { + UINT flags;
- DrawFrameControl( hDC, &rbox, DFC_BUTTON, flags ); - } + if ((get_button_type(style) == BS_RADIOBUTTON) || + (get_button_type(style) == BS_AUTORADIOBUTTON)) flags = DFCS_BUTTONRADIO; + else if (state & BST_INDETERMINATE) flags = DFCS_BUTTON3STATE; + else flags = DFCS_BUTTONCHECK;
- if (dtFlags == (UINT)-1L) /* Noting to draw */ - return; + if (state & (BST_CHECKED | BST_INDETERMINATE)) flags |= DFCS_CHECKED; + if (state & BST_PUSHED) flags |= DFCS_PUSHED; + if (style & WS_DISABLED) flags |= DFCS_INACTIVE;
- if (action == ODA_DRAWENTIRE) BUTTON_DrawLabel(infoPtr, hDC, dtFlags, &imageRect, &textRect); + /* rbox must have the correct height */ + delta = rbox.bottom - rbox.top - checkBoxHeight; + + if ((style & BS_VCENTER) == BS_TOP) + { + if (delta > 0) + rbox.bottom = rbox.top + checkBoxHeight; + else + { + rbox.top -= -delta / 2 + 1; + rbox.bottom = rbox.top + checkBoxHeight; + } + } + else if ((style & BS_VCENTER) == BS_BOTTOM) + { + if (delta > 0) + rbox.top = rbox.bottom - checkBoxHeight; + else + { + rbox.bottom += -delta / 2 + 1; + rbox.top = rbox.bottom - checkBoxHeight; + } + } + else /* Default */ + { + if (delta > 0) + { + int ofs = delta / 2; + rbox.bottom -= ofs + 1; + rbox.top = rbox.bottom - checkBoxHeight; + } + else if (delta < 0) + { + int ofs = -delta / 2; + rbox.top -= ofs + 1; + rbox.bottom = rbox.top + checkBoxHeight; + } + } + + DrawFrameControl(hDC, &rbox, DFC_BUTTON, flags); + } + + if (dtFlags != (UINT)-1L) /* Something to draw */ + if (action == ODA_DRAWENTIRE) BUTTON_DrawLabel(infoPtr, hDC, dtFlags, &imageRect, &textRect); + } + + 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;
/* ... and focus */ if (action == ODA_FOCUS || (state & BST_FOCUS)) @@ -1737,6 +1772,8 @@ static void CB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action ) IntersectRect(&labelRect, &labelRect, &client); DrawFocusRect(hDC, &labelRect); } + +cleanup: SelectClipRgn( hDC, hrgn ); if (hrgn) DeleteObject( hrgn ); }
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/comctl32/button.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-)
diff --git a/dlls/comctl32/button.c b/dlls/comctl32/button.c index 88f8f2f..cb5aa83 100644 --- a/dlls/comctl32/button.c +++ b/dlls/comctl32/button.c @@ -2015,7 +2015,10 @@ static void CB_ThemedPaint(HTHEME theme, const BUTTON_INFO *infoPtr, HDC hDC, in UINT btn_type = get_button_type( dwStyle ); int part = (btn_type == BS_RADIOBUTTON) || (btn_type == BS_AUTORADIOBUTTON) ? BP_RADIOBUTTON : BP_CHECKBOX; WCHAR *text = get_button_text(infoPtr); + NMCUSTOMDRAW nmcd; + LRESULT cdrf; LOGFONTW lf; + HWND parent; BOOL created_font = FALSE;
HRESULT hr = GetThemeFont(theme, hDC, part, state, TMT_FONT, &lf); @@ -2037,6 +2040,7 @@ static void CB_ThemedPaint(HTHEME theme, const BUTTON_INFO *infoPtr, HDC hDC, in sz.cx = sz.cy = 13;
GetClientRect(infoPtr->hwnd, &bgRect); + init_custom_draw(&nmcd, infoPtr, hDC, &bgRect); GetThemeBackgroundContentRect(theme, hDC, part, state, &bgRect, &textRect);
if (dtFlags & DT_SINGLELINE) /* Center the checkbox / radio button to the text. */ @@ -2047,13 +2051,39 @@ static void CB_ThemedPaint(HTHEME theme, const BUTTON_INFO *infoPtr, HDC hDC, in bgRect.right = bgRect.left + sz.cx; textRect.left = bgRect.right + 6;
- DrawThemeParentBackground(infoPtr->hwnd, hDC, NULL); + parent = GetParent(infoPtr->hwnd); + if (!parent) parent = infoPtr->hwnd;
+ /* Send erase notifications */ + cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd); + if (cdrf & CDRF_SKIPDEFAULT) goto cleanup; + + DrawThemeParentBackground(infoPtr->hwnd, hDC, NULL); DrawThemeBackground(theme, hDC, part, state, &bgRect, NULL); - if (text) + + if (cdrf & CDRF_NOTIFYPOSTERASE) { + nmcd.dwDrawStage = CDDS_POSTERASE; + SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd); + } + + /* Send paint notifications */ + nmcd.dwDrawStage = CDDS_PREPAINT; + cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd); + if (cdrf & CDRF_SKIPDEFAULT) goto cleanup; + + if (!(cdrf & CDRF_DOERASE) && text) DrawThemeText(theme, hDC, part, state, text, lstrlenW(text), dtFlags, 0, &textRect);
+ if (cdrf & CDRF_NOTIFYPOSTPAINT) + { + nmcd.dwDrawStage = CDDS_POSTPAINT; + SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd); + } + if (cdrf & CDRF_SKIPPOSTPAINT) goto cleanup; + + if (text) + { if (focused) { RECT focusRect; @@ -2071,6 +2101,7 @@ static void CB_ThemedPaint(HTHEME theme, const BUTTON_INFO *infoPtr, HDC hDC, in heap_free(text); }
+cleanup: if (created_font) DeleteObject(font); if (hPrevFont) SelectObject(hDC, hPrevFont); }
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
The behavior seems odd but it's needed to pass the tests just like Windows does.
dlls/comctl32/button.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-)
diff --git a/dlls/comctl32/button.c b/dlls/comctl32/button.c index cb5aa83..d40e318 100644 --- a/dlls/comctl32/button.c +++ b/dlls/comctl32/button.c @@ -37,7 +37,6 @@ * - BN_PAINT * + BN_SETFOCUS: is it OK? * - BN_UNPUSHED/BN_UNHILITE - * - NM_CUSTOMDRAW * * Structures/Macros/Definitions * - NMBCHOTITEM @@ -1866,7 +1865,9 @@ static void UB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action ) { RECT rc; HBRUSH hBrush; + LRESULT cdrf; HFONT hFont; + NMCUSTOMDRAW nmcd; LONG state = infoPtr->state; HWND parent;
@@ -1880,10 +1881,39 @@ static void UB_Paint( const BUTTON_INFO *infoPtr, HDC hDC, UINT action ) if (!hBrush) /* did the app forget to call defwindowproc ? */ hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)infoPtr->hwnd);
+ if (action == ODA_FOCUS || (state & BST_FOCUS)) + { + 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 notify; + } + FillRect( hDC, &rc, hBrush ); if (action == ODA_FOCUS || (state & BST_FOCUS)) - DrawFocusRect( hDC, &rc ); + { + if (cdrf & CDRF_NOTIFYPOSTERASE) + { + nmcd.dwDrawStage = CDDS_POSTERASE; + SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd); + } + + /* Send paint notifications */ + nmcd.dwDrawStage = CDDS_PREPAINT; + cdrf = SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd); + if (cdrf & CDRF_SKIPDEFAULT) goto notify; + if (cdrf & CDRF_NOTIFYPOSTPAINT) + { + nmcd.dwDrawStage = CDDS_POSTPAINT; + SendMessageW(parent, WM_NOTIFY, nmcd.hdr.idFrom, (LPARAM)&nmcd); + } + + if (!(cdrf & CDRF_SKIPPOSTPAINT)) + DrawFocusRect( hDC, &rc ); + }
+notify: switch (action) { case ODA_FOCUS:
It's already invalidated at the end so there's no reason to draw it before that.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
This also fixes a TODO in the tests and is needed for CustomDraw tests to work properly (next patch).
dlls/comctl32/button.c | 1 - dlls/comctl32/tests/button.c | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/dlls/comctl32/button.c b/dlls/comctl32/button.c index d40e318..6610b82 100644 --- a/dlls/comctl32/button.c +++ b/dlls/comctl32/button.c @@ -772,7 +772,6 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L case WM_KILLFOCUS: TRACE("WM_KILLFOCUS %p\n",hWnd); infoPtr->state &= ~BST_FOCUS; - paint_button( infoPtr, btn_type, ODA_FOCUS );
if ((infoPtr->state & BUTTON_BTNPRESSED) && GetCapture() == hWnd) ReleaseCapture(); diff --git a/dlls/comctl32/tests/button.c b/dlls/comctl32/tests/button.c index ce673af..753aa24 100644 --- a/dlls/comctl32/tests/button.c +++ b/dlls/comctl32/tests/button.c @@ -600,11 +600,10 @@ static void test_button_messages(void) while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setfocus, "SetFocus(hwnd) on a button", todo);
- todo = button[i].style == BS_OWNERDRAW; SetFocus(0); SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); - ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].killfocus, "SetFocus(0) on a button", todo); + ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].killfocus, "SetFocus(0) on a button", FALSE); ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus());
SendMessageA(hwnd, BM_SETSTYLE, button[i].style | BS_BOTTOM, TRUE);
On 3/4/19 7:25 PM, Gabriel Ivăncescu wrote:
@@ -772,7 +772,6 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L case WM_KILLFOCUS: TRACE("WM_KILLFOCUS %p\n",hWnd); infoPtr->state &= ~BST_FOCUS;
paint_button( infoPtr, btn_type, ODA_FOCUS ); if ((infoPtr->state & BUTTON_BTNPRESSED) && GetCapture() == hWnd) ReleaseCapture();
That looks like an artifact of user32 behavior when it often repaints directly without going through invalidation. If it's not how it works, it's obviously good to change.
On 3/4/19 8:23 PM, Nikolay Sivov wrote:
On 3/4/19 7:25 PM, Gabriel Ivăncescu wrote:
@@ -772,7 +772,6 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L case WM_KILLFOCUS: TRACE("WM_KILLFOCUS %p\n",hWnd); infoPtr->state &= ~BST_FOCUS; - paint_button( infoPtr, btn_type, ODA_FOCUS ); if ((infoPtr->state & BUTTON_BTNPRESSED) && GetCapture() == hWnd) ReleaseCapture();
That looks like an artifact of user32 behavior when it often repaints directly without going through invalidation. If it's not how it works, it's obviously good to change.
Just to be clear, I shouldn't make this change later in the user32 code, right?
On 3/5/19 2:03 PM, Gabriel Ivăncescu wrote:
On 3/4/19 8:23 PM, Nikolay Sivov wrote:
On 3/4/19 7:25 PM, Gabriel Ivăncescu wrote:
@@ -772,7 +772,6 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L case WM_KILLFOCUS: TRACE("WM_KILLFOCUS %p\n",hWnd); infoPtr->state &= ~BST_FOCUS; - paint_button( infoPtr, btn_type, ODA_FOCUS ); if ((infoPtr->state & BUTTON_BTNPRESSED) && GetCapture() == hWnd) ReleaseCapture();
That looks like an artifact of user32 behavior when it often repaints directly without going through invalidation. If it's not how it works, it's obviously good to change.
Just to be clear, I shouldn't make this change later in the user32 code, right?
I don't know, depends on whether we can have a test for it.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/comctl32/tests/button.c | 240 ++++++++++++++++++++++++++++++++--- 1 file changed, 222 insertions(+), 18 deletions(-)
diff --git a/dlls/comctl32/tests/button.c b/dlls/comctl32/tests/button.c index 753aa24..25208b9 100644 --- a/dlls/comctl32/tests/button.c +++ b/dlls/comctl32/tests/button.c @@ -39,8 +39,9 @@ static BOOL (WINAPI *pImageList_Destroy)(HIMAGELIST); /****************** button message test *************************/ #define ID_BUTTON 0x000e
-#define COMBINED_SEQ_INDEX 0 -#define NUM_MSG_SEQUENCES 1 +#define COMBINED_SEQ_INDEX 0 +#define PARENT_CD_SEQ_INDEX 1 +#define NUM_MSG_SEQUENCES 2
static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
@@ -152,11 +153,38 @@ static LRESULT CALLBACK button_subclass_proc(HWND hwnd, UINT message, WPARAM wPa return ret; }
+static struct +{ + DWORD button; + UINT line; + UINT state; + DWORD ret; + BOOL empty; +} test_cd; + +#define set_test_cd_state(s) do { \ + test_cd.state = (s); \ + test_cd.empty = TRUE; \ + test_cd.line = __LINE__; \ +} while (0) + +#define set_test_cd_ret(r) do { \ + test_cd.ret = (r); \ + test_cd.empty = TRUE; \ + test_cd.line = __LINE__; \ +} while (0) + +static void disable_test_cd(void) +{ + test_cd.line = 0; +} + static LRESULT WINAPI test_parent_wndproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static LONG defwndproc_counter = 0; static LONG beginpaint_counter = 0; struct message msg = { 0 }; + NMCUSTOMDRAW *cd = (NMCUSTOMDRAW*)lParam; LRESULT ret;
if (ignore_message( message )) return 0; @@ -176,6 +204,46 @@ static LRESULT WINAPI test_parent_wndproc(HWND hwnd, UINT message, WPARAM wParam add_message(sequences, COMBINED_SEQ_INDEX, &msg); }
+ if (message == WM_NOTIFY && cd->hdr.code == NM_CUSTOMDRAW && test_cd.line) + { + /* Ignore an inconsistency across Windows versions */ + UINT state = cd->uItemState & ~CDIS_SHOWKEYBOARDCUES; + test_cd.empty = FALSE; + + ok_(__FILE__,test_cd.line)(!(cd->dwDrawStage & CDDS_ITEM), + "[%u] CDDS_ITEM is set\n", test_cd.button); + + ok_(__FILE__,test_cd.line)(state == test_cd.state, + "[%u] expected uItemState %u, got %u\n", test_cd.button, + test_cd.state, state); + + msg.message = message; + msg.flags = sent|parent|wparam|lparam|id|custdraw; + msg.wParam = wParam; + msg.lParam = lParam; + msg.id = NM_CUSTOMDRAW; + msg.stage = cd->dwDrawStage; + add_message(sequences, PARENT_CD_SEQ_INDEX, &msg); + + ret = test_cd.ret; + switch (msg.stage) + { + case CDDS_PREERASE: + ret &= ~CDRF_NOTIFYPOSTPAINT; + cd->dwItemSpec = 0xdeadbeef; + break; + case CDDS_PREPAINT: + ret &= ~CDRF_NOTIFYPOSTERASE; + break; + case CDDS_POSTERASE: + case CDDS_POSTPAINT: + ok_(__FILE__,test_cd.line)(cd->dwItemSpec == 0xdeadbeef, + "[%u] NMCUSTOMDRAW was not shared, stage %u\n", test_cd.button, msg.stage); + break; + } + return ret; + } + if (message == WM_PAINT) { PAINTSTRUCT ps; @@ -453,6 +521,50 @@ static const struct message setcheck_radio_redraw_seq[] = { 0 } };
+static const struct message empty_cd_seq[] = { { 0 } }; + +static const struct message pre_cd_seq[] = +{ + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREERASE }, + { 0 } +}; + +static const struct message pre_pre_cd_seq[] = +{ + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREERASE }, + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREPAINT }, + + /* Some Windows configurations paint twice under certain conditions */ + { WM_NOTIFY, sent|parent|id|custdraw|optional, 0, 0, NM_CUSTOMDRAW, CDDS_PREERASE }, + { WM_NOTIFY, sent|parent|id|custdraw|optional, 0, 0, NM_CUSTOMDRAW, CDDS_PREPAINT }, + { 0 } +}; + +static const struct message pre_post_pre_cd_seq[] = +{ + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREERASE }, + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_POSTERASE }, + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREPAINT }, + { 0 } +}; + +static const struct message pre_pre_post_cd_seq[] = +{ + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREERASE }, + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREPAINT }, + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_POSTPAINT }, + { 0 } +}; + +static const struct message pre_post_pre_post_cd_seq[] = +{ + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREERASE }, + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_POSTERASE }, + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREPAINT }, + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_POSTPAINT }, + { 0 } +}; + static HWND create_button(DWORD style, HWND parent) { HMENU menuid = 0; @@ -471,6 +583,13 @@ static HWND create_button(DWORD style, HWND parent)
static void test_button_messages(void) { + enum cd_seq_type + { + cd_seq_empty, + cd_seq_normal, + cd_seq_optional + }; + static const struct { DWORD style; @@ -481,55 +600,74 @@ static void test_button_messages(void) const struct message *setstate; const struct message *clearstate; const struct message *setcheck; + enum cd_seq_type cd_setfocus_type; + enum cd_seq_type cd_setstyle_type; + enum cd_seq_type cd_setstate_type; + enum cd_seq_type cd_setcheck_type; } button[] = { { BS_PUSHBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON, setfocus_seq, killfocus_seq, setstyle_seq, - setstate_seq, setstate_seq, setcheck_ignored_seq }, + setstate_seq, setstate_seq, setcheck_ignored_seq, + cd_seq_normal, cd_seq_normal, cd_seq_normal, cd_seq_optional }, { BS_DEFPUSHBUTTON, DLGC_BUTTON | DLGC_DEFPUSHBUTTON, setfocus_seq, killfocus_seq, setstyle_seq, - setstate_seq, setstate_seq, setcheck_ignored_seq }, + setstate_seq, setstate_seq, setcheck_ignored_seq, + cd_seq_normal, cd_seq_normal, cd_seq_normal, cd_seq_optional }, { BS_CHECKBOX, DLGC_BUTTON, setfocus_static_seq, killfocus_static_seq, setstyle_static_seq, - setstate_static_seq, setstate_static_seq, setcheck_static_seq }, + setstate_static_seq, setstate_static_seq, setcheck_static_seq, + cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_optional }, { BS_AUTOCHECKBOX, DLGC_BUTTON, setfocus_static_seq, killfocus_static_seq, setstyle_static_seq, - setstate_static_seq, setstate_static_seq, setcheck_static_seq }, + setstate_static_seq, setstate_static_seq, setcheck_static_seq, + cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_optional }, { BS_RADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON, setfocus_static_seq, killfocus_static_seq, setstyle_static_seq, - setstate_static_seq, setstate_static_seq, setcheck_radio_redraw_seq }, + setstate_static_seq, setstate_static_seq, setcheck_radio_redraw_seq, + cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_optional }, { BS_3STATE, DLGC_BUTTON, setfocus_static_seq, killfocus_static_seq, setstyle_static_seq, - setstate_static_seq, setstate_static_seq, setcheck_static_seq }, + setstate_static_seq, setstate_static_seq, setcheck_static_seq, + cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_optional }, { BS_AUTO3STATE, DLGC_BUTTON, setfocus_static_seq, killfocus_static_seq, setstyle_static_seq, - setstate_static_seq, setstate_static_seq, setcheck_static_seq }, + setstate_static_seq, setstate_static_seq, setcheck_static_seq, + cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_optional }, { BS_GROUPBOX, DLGC_STATIC, setfocus_groupbox_seq, killfocus_static_seq, setstyle_static_seq, - setstate_static_seq, setstate_static_seq, setcheck_ignored_seq }, + setstate_static_seq, setstate_static_seq, setcheck_ignored_seq, + cd_seq_empty, cd_seq_empty, cd_seq_empty, cd_seq_empty }, { BS_USERBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON, setfocus_seq, killfocus_seq, setstyle_user_seq, - setstate_user_seq, clearstate_seq, setcheck_ignored_seq }, + setstate_user_seq, clearstate_seq, setcheck_ignored_seq, + cd_seq_normal, cd_seq_empty, cd_seq_empty, cd_seq_empty }, { BS_AUTORADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON, setfocus_static_seq, killfocus_static_seq, setstyle_static_seq, - setstate_static_seq, setstate_static_seq, setcheck_radio_redraw_seq }, + setstate_static_seq, setstate_static_seq, setcheck_radio_redraw_seq, + cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_optional }, { BS_OWNERDRAW, DLGC_BUTTON, setfocus_ownerdraw_seq, killfocus_ownerdraw_seq, setstyle_ownerdraw_seq, - setstate_ownerdraw_seq, clearstate_ownerdraw_seq, setcheck_ignored_seq }, + setstate_ownerdraw_seq, clearstate_ownerdraw_seq, setcheck_ignored_seq, + cd_seq_empty, cd_seq_empty, cd_seq_empty, cd_seq_empty }, { BS_SPLITBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON | DLGC_WANTARROWS, setfocus_seq, killfocus_seq, setstyle_seq, - setstate_seq, setstate_seq, setcheck_ignored_seq }, + setstate_seq, setstate_seq, setcheck_ignored_seq, + cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_empty }, { BS_DEFSPLITBUTTON, DLGC_BUTTON | DLGC_DEFPUSHBUTTON | DLGC_WANTARROWS, setfocus_seq, killfocus_seq, setstyle_seq, - setstate_seq, setstate_seq, setcheck_ignored_seq }, + setstate_seq, setstate_seq, setcheck_ignored_seq, + cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_empty }, { BS_COMMANDLINK, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON, setfocus_seq, killfocus_seq, setstyle_seq, - setstate_seq, setstate_seq, setcheck_ignored_seq }, + setstate_seq, setstate_seq, setcheck_ignored_seq, + cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_empty }, { BS_DEFCOMMANDLINK, DLGC_BUTTON | DLGC_DEFPUSHBUTTON, setfocus_seq, killfocus_seq, setstyle_seq, - setstate_seq, setstate_seq, setcheck_ignored_seq }, + setstate_seq, setstate_seq, setcheck_ignored_seq, + cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_empty } }; LOGFONTA logfont = { 0 }; - const struct message *seq; + const struct message *seq, *cd_seq; HFONT zfont, hfont2; unsigned int i; HWND hwnd, parent; @@ -557,6 +695,11 @@ static void test_button_messages(void) hfont2 = CreateFontIndirectA(&logfont); ok(hfont2 != NULL, "Failed to create Tahoma font\n");
+ #define check_cd_seq(type, context) do { \ + if (button[i].type != cd_seq_optional || !test_cd.empty) \ + ok_sequence(sequences, PARENT_CD_SEQ_INDEX, cd_seq, "[CustomDraw] " context, FALSE); \ + } while(0) + for (i = 0; i < ARRAY_SIZE(button); i++) { HFONT prevfont, hfont; @@ -564,6 +707,7 @@ static void test_button_messages(void) DWORD style, state; HDC hdc;
+ test_cd.button = button[i].style; hwnd = create_button(button[i].style, parent); ok(hwnd != NULL, "Failed to create a button.\n");
@@ -591,7 +735,10 @@ static void test_button_messages(void) SetFocus(0); flush_events(); SetFocus(0); + cd_seq = (button[i].cd_setfocus_type == cd_seq_empty) ? empty_cd_seq : pre_pre_cd_seq; flush_sequences(sequences, NUM_MSG_SEQUENCES); + set_test_cd_ret(CDRF_DODEFAULT); + set_test_cd_state(CDIS_FOCUS);
todo = button[i].style != BS_OWNERDRAW; ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus()); @@ -599,18 +746,25 @@ static void test_button_messages(void) SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setfocus, "SetFocus(hwnd) on a button", todo); + check_cd_seq(cd_setfocus_type, "SetFocus(hwnd)");
+ set_test_cd_state(0); SetFocus(0); SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].killfocus, "SetFocus(0) on a button", FALSE); + check_cd_seq(cd_setfocus_type, "SetFocus(0)"); ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus());
+ cd_seq = (button[i].cd_setstyle_type == cd_seq_empty) ? empty_cd_seq : pre_pre_cd_seq; + set_test_cd_state(0); + SendMessageA(hwnd, BM_SETSTYLE, button[i].style | BS_BOTTOM, TRUE); SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); todo = button[i].style == BS_OWNERDRAW; ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setstyle, "BM_SETSTYLE on a button", todo); + check_cd_seq(cd_setstyle_type, "BM_SETSTYLE");
style = GetWindowLongA(hwnd, GWL_STYLE); style &= ~(WS_VISIBLE | WS_CHILD | BS_NOTIFY); @@ -620,12 +774,15 @@ static void test_button_messages(void) state = SendMessageA(hwnd, BM_GETSTATE, 0, 0); ok(state == 0, "expected state 0, got %04x\n", state);
+ cd_seq = (button[i].cd_setstate_type == cd_seq_empty) ? empty_cd_seq : pre_pre_cd_seq; flush_sequences(sequences, NUM_MSG_SEQUENCES); + set_test_cd_state(CDIS_SELECTED);
SendMessageA(hwnd, BM_SETSTATE, TRUE, 0); SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setstate, "BM_SETSTATE/TRUE on a button", FALSE); + check_cd_seq(cd_setstate_type, "BM_SETSTATE/TRUE");
state = SendMessageA(hwnd, BM_GETSTATE, 0, 0); ok(state == BST_PUSHED, "expected state 0x0004, got %04x\n", state); @@ -635,11 +792,13 @@ static void test_button_messages(void) ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
flush_sequences(sequences, NUM_MSG_SEQUENCES); + set_test_cd_state(0);
SendMessageA(hwnd, BM_SETSTATE, FALSE, 0); SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].clearstate, "BM_SETSTATE/FALSE on a button", FALSE); + check_cd_seq(cd_setstate_type, "BM_SETSTATE/FALSE");
state = SendMessageA(hwnd, BM_GETSTATE, 0, 0); ok(state == 0, "expected state 0, got %04x\n", state); @@ -651,7 +810,9 @@ static void test_button_messages(void) state = SendMessageA(hwnd, BM_GETCHECK, 0, 0); ok(state == BST_UNCHECKED, "expected BST_UNCHECKED, got %04x\n", state);
+ cd_seq = (button[i].cd_setcheck_type == cd_seq_empty) ? empty_cd_seq : pre_pre_cd_seq; flush_sequences(sequences, NUM_MSG_SEQUENCES); + set_test_cd_state(0);
if (button[i].style == BS_RADIOBUTTON || button[i].style == BS_AUTORADIOBUTTON) @@ -665,6 +826,7 @@ static void test_button_messages(void) SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); ok_sequence(sequences, COMBINED_SEQ_INDEX, seq, "BM_SETCHECK on a button", FALSE); + check_cd_seq(cd_setcheck_type, "BM_SETCHECK");
state = SendMessageA(hwnd, BM_GETCHECK, 0, 0); ok(state == BST_UNCHECKED, "expected BST_UNCHECKED, got %04x\n", state); @@ -674,11 +836,13 @@ static void test_button_messages(void) ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
flush_sequences(sequences, NUM_MSG_SEQUENCES); + set_test_cd_state(0);
SendMessageA(hwnd, BM_SETCHECK, BST_CHECKED, 0); SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setcheck, "BM_SETCHECK on a button", FALSE); + check_cd_seq(cd_setcheck_type, "BM_SETCHECK");
state = SendMessageA(hwnd, BM_GETCHECK, 0, 0); if (button[i].style == BS_PUSHBUTTON || @@ -726,9 +890,49 @@ static void test_button_messages(void)
DeleteDC(hdc);
+ /* Test Custom Draw return values */ + if (button[i].cd_setfocus_type != cd_seq_empty && + broken(button[i].style != BS_USERBUTTON) /* WinXP */) + { + static const struct + { + const char *context; + LRESULT val; + const struct message *seq; + } ret[] = { + { "CDRF_DODEFAULT", CDRF_DODEFAULT, pre_pre_cd_seq }, + { "CDRF_DOERASE", CDRF_DOERASE, pre_pre_cd_seq }, + { "CDRF_SKIPDEFAULT", CDRF_SKIPDEFAULT, pre_cd_seq }, + { "CDRF_SKIPDEFAULT | CDRF_NOTIFYPOSTERASE | CDRF_NOTIFYPOSTPAINT", + CDRF_SKIPDEFAULT | CDRF_NOTIFYPOSTERASE | CDRF_NOTIFYPOSTPAINT, pre_cd_seq }, + { "CDRF_NOTIFYPOSTERASE", CDRF_NOTIFYPOSTERASE, pre_post_pre_cd_seq }, + { "CDRF_NOTIFYPOSTPAINT", CDRF_NOTIFYPOSTPAINT, pre_pre_post_cd_seq }, + { "CDRF_NOTIFYPOSTERASE | CDRF_NOTIFYPOSTPAINT", + CDRF_NOTIFYPOSTERASE | CDRF_NOTIFYPOSTPAINT, pre_post_pre_post_cd_seq }, + }; + UINT k = 0; + + for (k = 0; k < ARRAY_SIZE(ret); k++) + { + disable_test_cd(); + SetFocus(0); + set_test_cd_ret(ret[k].val); + set_test_cd_state(CDIS_FOCUS); + SetFocus(hwnd); + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + if (button[i].cd_setfocus_type != cd_seq_optional || !test_cd.empty) + ok_sequence(sequences, PARENT_CD_SEQ_INDEX, ret[k].seq, ret[k].context, FALSE); + } + } + + disable_test_cd(); DestroyWindow(hwnd); }
+ #undef check_cd_seq + DeleteObject(hfont2); DestroyWindow(parent);