From bb3177dd3b481293e424eaf35908cbe01f65254e Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 24 Jul 2020 01:32:44 +0200 Subject: [PATCH] Avoid infinite repaint loop in wxListCtrl with wxLC_HRULES A virtual wxListCtrl with wxLC_HRULES which wasn't fully visible on screen, i.e. didn't fit into the visible area of its parent window (which included the case when the parent was a wxScrolledWindow, for which it is normal and expected not to be able to fit all of its children) got into an infinite repaint loop because of a RefreshRect() call inside wxListCtrl::OnPaint(). Fix this by avoiding to call RefreshRect() added in 374db28747 (Fix wxMSW ListCtrl drawing of horizontal rules for new items, 2016-05-04), unless the current clipping rectangle is less than the actually effective visible width and not the full client width, which can be much bigger. This still doesn't ensure that we don't enter into an infinite recursion here, so it would be even better to call RefreshRect() at most once before the next control change, but it's not clear when exactly this "already refreshed" flag would need to be reset. See #17158. Closes #18850. --- src/msw/listctrl.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp index e0c492a810..ee2ffba2f3 100644 --- a/src/msw/listctrl.cpp +++ b/src/msw/listctrl.cpp @@ -3278,7 +3278,13 @@ void wxListCtrl::OnPaint(wxPaintEvent& event) dc.SetPen(pen); dc.SetBrush(* wxTRANSPARENT_BRUSH); - wxSize clientSize = GetClientSize(); + // Find the coordinate of the right most visible point: this is not the + // same as GetClientSize().x because the window might not be fully visible, + // it could be clipped by its parent. + const wxSize size = GetSize(); + const int availableWidth = GetParent()->GetClientSize().x - GetPosition().x; + int visibleWidth = wxMin(GetClientSize().x, + availableWidth - GetWindowBorderSize().x); const int countPerPage = GetCountPerPage(); if (countPerPage < 0) @@ -3312,10 +3318,10 @@ void wxListCtrl::OnPaint(wxPaintEvent& event) list control. In that case we request for the part to the right of the rightmost column to be drawn as well. */ - if ( clipRect.GetRight() != clientSize.x - 1 && clipRect.width ) + if ( clipRect.GetRight() < visibleWidth - 1 && clipRect.width ) { RefreshRect(wxRect(clipRect.GetRight(), clipRect.y, - clientSize.x - clipRect.width, clipRect.height), + visibleWidth - clipRect.width, clipRect.height), false /* don't erase background */); } } @@ -3359,7 +3365,7 @@ void wxListCtrl::OnPaint(wxPaintEvent& event) wxDCBrushChanger changeBrush(dc, GetBackgroundColour()); dc.DrawRectangle(0, topItemRect.GetY() - gap, - clientSize.GetWidth(), gap); + visibleWidth, gap); } const int numCols = GetColumnCount();