diff --git a/include/wx/msw/listctrl.h b/include/wx/msw/listctrl.h index 01d2df34f8..8b6e0ff52d 100644 --- a/include/wx/msw/listctrl.h +++ b/include/wx/msw/listctrl.h @@ -396,8 +396,6 @@ protected: virtual void MSWUpdateFontOnDPIChange(const wxSize& newDPI) override; - virtual void MSWAfterReparent() override; - virtual bool MSWGetDarkModeSupport(MSWDarkModeSupport& support) const override; virtual int MSWGetToolTipMessage() const override; diff --git a/include/wx/msw/private.h b/include/wx/msw/private.h index 4e954904b5..a6838de7e3 100644 --- a/include/wx/msw/private.h +++ b/include/wx/msw/private.h @@ -1007,6 +1007,7 @@ enum wxWinVersion wxWinVersion_8_1 = 0x603, wxWinVersion_10 = 0x1000, + wxWinVersion_11 = 0x1001, // Any version we can't recognize will be later than the last currently // known one, so give it a value greater than any in the known range. diff --git a/include/wx/msw/window.h b/include/wx/msw/window.h index ad3bc6dd32..1196337007 100644 --- a/include/wx/msw/window.h +++ b/include/wx/msw/window.h @@ -632,11 +632,6 @@ protected: virtual bool DoPopupMenu( wxMenu *menu, int x, int y ) override; #endif // wxUSE_MENUS_NATIVE - // Called by Reparent() after the window parent changes, i.e. GetParent() - // returns the new parent inside this function. - virtual void MSWAfterReparent(); - - // the window handle WXHWND m_hWnd; diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp index f833fd33df..e08f99ce35 100644 --- a/src/msw/listctrl.cpp +++ b/src/msw/listctrl.cpp @@ -257,12 +257,6 @@ bool wxListCtrl::Create(wxWindow *parent, if ( !MSWCreateControl(WC_LISTVIEW, wxEmptyString, pos, size) ) return false; - // LISTVIEW doesn't redraw correctly when WS_EX_COMPOSITED is used by - // either the control itself (which never happens now, see our overridden - // SetDoubleBuffered()) or even by any of its parents, so we must reset - // this style for them. - MSWDisableComposited(); - const wxVisualAttributes& defAttrs = GetDefaultAttributes(); if ( wxMSWDarkMode::IsActive() ) @@ -371,18 +365,6 @@ void wxListCtrl::MSWInitHeader() m_headerCustomDraw->UseHeaderThemeColors(hwndHdr); } -void wxListCtrl::MSWAfterReparent() -{ - // We did it for the original parent in our Create(), but we need to do it - // here for the new one. - MSWDisableComposited(); - - // Ideally we'd re-enable WS_EX_COMPOSITED for the old parent, but this is - // difficult to do correctly, as we'd need to track the number of list - // controls under it instead of just turning it on/off, so for now we don't - // do it. -} - WXDWORD wxListCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const { WXDWORD wstyle = wxListCtrlBase::MSWGetStyle(style, exstyle); @@ -3154,20 +3136,13 @@ void HandleItemPostpaint(NMCUSTOMDRAW nmcd) } } -// Flags for HandleItemPaint() -enum -{ - Paint_Default = 0, - Paint_OnlySelected = 1 -}; - // This function is normally called only if we use custom colours, but it's -// also called when using dark mode to draw the item if it's selected. In this -// case, Paint_OnlySelected is used and we do nothing and return false if the -// item is not selected. +// also called when using dark mode as we have to draw the selected item +// ourselves when using it, and if we do this, we have to paint all the items +// for consistency. // // pLVCD->clrText and clrTextBk should contain the colours to use -bool HandleItemPaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont, int flags = 0) +void HandleItemPaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont) { NMCUSTOMDRAW& nmcd = pLVCD->nmcd; // just a shortcut @@ -3226,18 +3201,12 @@ bool HandleItemPaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont, int flags = 0) pLVCD->clrText = wxColourToRGB(wxSystemSettings::GetColour(syscolFg)); pLVCD->clrTextBk = wxColourToRGB(wxSystemSettings::GetColour(syscolBg)); } - else // not selected - { - if ( flags & Paint_OnlySelected ) - return false; - - // Continue and use normal colours from pLVCD - } + //else: not selected, use normal colours from pLVCD HDC hdc = nmcd.hdc; RECT rc = GetCustomDrawnItemRect(nmcd); - ::SetTextColor(hdc, pLVCD->clrText); + COLORREF colTextOld = ::SetTextColor(hdc, pLVCD->clrText); ::FillRect(hdc, &rc, AutoHBRUSH(pLVCD->clrTextBk)); // we could use CDRF_NOTIFYSUBITEMDRAW here but it results in weird repaint @@ -3249,26 +3218,33 @@ bool HandleItemPaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont, int flags = 0) HandleSubItemPrepaint(pLVCD, hfont, colCount); } - HandleItemPostpaint(nmcd); + ::SetTextColor(hdc, colTextOld); - return true; + HandleItemPostpaint(nmcd); } WXLPARAM HandleItemPrepaint(wxListCtrl *listctrl, LPNMLVCUSTOMDRAW pLVCD, wxItemAttr *attr) { + if ( wxMSWDarkMode::IsActive() ) + { + // We need to always paint selected items ourselves as they come + // out completely wrong in DarkMode_Explorer theme, see the comment + // before MSWGetDarkModeSupport(). + pLVCD->clrText = attr && attr->HasTextColour() + ? wxColourToRGB(attr->GetTextColour()) + : wxColourToRGB(listctrl->GetTextColour()); + pLVCD->clrTextBk = attr && attr->HasBackgroundColour() + ? wxColourToRGB(attr->GetBackgroundColour()) + : wxColourToRGB(listctrl->GetBackgroundColour()); + + HandleItemPaint(pLVCD, nullptr); + return CDRF_SKIPDEFAULT; + } + if ( !attr ) { - if ( wxMSWDarkMode::IsActive() ) - { - // We need to always paint selected items ourselves as they come - // out completely wrong in DarkMode_Explorer theme, see the comment - // before MSWGetDarkModeSupport(). - if ( HandleItemPaint(pLVCD, nullptr, Paint_OnlySelected) ) - return CDRF_SKIPDEFAULT; - } - // nothing to do for this item return CDRF_DODEFAULT; } @@ -3361,30 +3337,40 @@ WXLPARAM wxListCtrl::OnCustomDraw(WXLPARAM lParam) return CDRF_DODEFAULT; } -// Necessary for drawing hrules and vrules, if specified +// We need to draw the control ourselves to make it work with WS_EX_COMPOSITED: +// by default, it's not redrawn correctly, apparently due to some optimizations +// used internally, but creating wxPaintDC ourselves seems to be sufficient to +// avoid them, so we do it even if we don't draw anything on it ourselves. void wxListCtrl::OnPaint(wxPaintEvent& event) { const int itemCount = GetItemCount(); const bool drawHRules = HasFlag(wxLC_HRULES); const bool drawVRules = HasFlag(wxLC_VRULES); - if (!InReportView() || !(drawHRules || drawVRules) || !itemCount) + // Check if we need to do anything ourselves: either draw the rules or, in + // case of using dark mode under Windows 11, erase the unwanted separator + // lines drawn below the items by default, which are ugly because they + // don't align with the separators drawn by the header control. + bool needToDraw = false, + needToErase = false; + if ( InReportView() ) { - event.Skip(); - return; + if ( (drawHRules || drawVRules) && itemCount ) + needToDraw = true; + else if ( wxMSWDarkMode::IsActive() && wxGetWinVersion() >= wxWinVersion_11 ) + needToErase = true; } wxPaintDC dc(this); wxListCtrlBase::OnPaint(event); + if ( !needToDraw && !needToErase ) + return; + // Reset the device origin since it may have been set dc.SetDeviceOrigin(0, 0); - wxPen pen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT)); - dc.SetPen(pen); - dc.SetBrush(* wxTRANSPARENT_BRUSH); - wxSize clientSize = GetClientSize(); const int countPerPage = GetCountPerPage(); @@ -3397,6 +3383,21 @@ void wxListCtrl::OnPaint(wxPaintEvent& event) const long top = GetTopItem(); const long bottom = wxMin(top + countPerPage, itemCount - 1); + if ( needToErase ) + { + wxRect lastRect; + GetItemRect(bottom, lastRect); + + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(GetBackgroundColour()); + dc.DrawRectangle(0, lastRect.y, clientSize.x, clientSize.y - lastRect.y); + return; + } + + wxPen pen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT)); + dc.SetPen(pen); + dc.SetBrush(* wxTRANSPARENT_BRUSH); + if (drawHRules) { wxRect itemRect; diff --git a/src/msw/utils.cpp b/src/msw/utils.cpp index 0d3fe4437d..b0d1c8aa79 100644 --- a/src/msw/utils.cpp +++ b/src/msw/utils.cpp @@ -1095,6 +1095,10 @@ int wxIsWindowsServer() return -1; } +// Windows 11 uses the same version as Windows 10 but its build numbers start +// from 22000, which provides a way to test for it. +static const int FIRST_WINDOWS11_BUILD = 22000; + } // anonymous namespace wxString wxGetOsDescription() @@ -1158,7 +1162,7 @@ wxString wxGetOsDescription() break; case 10: - if (info.dwBuildNumber >= 22000) + if (info.dwBuildNumber >= FIRST_WINDOWS11_BUILD) str = wxIsWindowsServer() == 1 ? "Windows Server 2022" : "Windows 11"; @@ -1269,8 +1273,9 @@ bool wxCheckOsVersion(int majorVsn, int minorVsn, int microVsn) wxWinVersion wxGetWinVersion() { int verMaj, - verMin; - switch ( wxGetOsVersion(&verMaj, &verMin) ) + verMin, + build; + switch ( wxGetOsVersion(&verMaj, &verMin, &build) ) { case wxOS_WINDOWS_NT: switch ( verMaj ) @@ -1305,7 +1310,8 @@ wxWinVersion wxGetWinVersion() break; case 10: - return wxWinVersion_10; + return build >= FIRST_WINDOWS11_BUILD ? wxWinVersion_11 + : wxWinVersion_10; } break; default: diff --git a/src/msw/window.cpp b/src/msw/window.cpp index e1cabfe8e7..ab9ad274f1 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -1705,17 +1705,12 @@ bool wxWindowMSW::Reparent(wxWindowBase *parent) ::SetParent(hWndChild, hWndParent); - MSWAfterReparent(); - - return true; -} - -void wxWindowMSW::MSWAfterReparent() -{ if ( wxHasWindowExStyle(this, WS_EX_CONTROLPARENT) ) { EnsureParentHasControlParentStyle(GetParent()); } + + return true; } void wxWindowMSW::MSWDisableComposited()