From 29c30d591792b35ccce679ea27186bd7811c6665 Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Tue, 26 Aug 2025 21:12:48 +0200 Subject: [PATCH] Improve HitTest in generic wxListCtrl Detect when a checkbox is hit and return wxLIST_HITTEST_ONITEMSTATEICON. Determine the correct location of the icon, it might be preceded by a checkbox. Return only the icon rectangle in GetSubItemRect with wxLIST_RECT_ICON, not the padding or margins above/below the icon. Closes #25764. --- include/wx/generic/private/listctrl.h | 2 +- samples/listctrl/listtest.cpp | 1 + src/generic/listctrl.cpp | 43 ++++++++++++++++++--------- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/include/wx/generic/private/listctrl.h b/include/wx/generic/private/listctrl.h index 30ace42328..014c612374 100644 --- a/include/wx/generic/private/listctrl.h +++ b/include/wx/generic/private/listctrl.h @@ -914,7 +914,7 @@ private: // Check if the given point is inside the checkbox of this item. // // Always returns false if there are no checkboxes. - bool IsInsideCheckBox(long item, int x, int y); + bool IsInsideCheckBox(long item, int x, int y) const; // the height of one line using the current font wxCoord m_lineHeight; diff --git a/samples/listctrl/listtest.cpp b/samples/listctrl/listtest.cpp index 8a055b0a96..6cb49bcf87 100644 --- a/samples/listctrl/listtest.cpp +++ b/samples/listctrl/listtest.cpp @@ -1491,6 +1491,7 @@ void MyListCtrl::OnRightClick(wxMouseEvent& event) case wxLIST_HITTEST_NOWHERE: where = "nowhere near"; break; case wxLIST_HITTEST_ONITEMICON: where = "on icon of"; break; case wxLIST_HITTEST_ONITEMLABEL: where = "on label of"; break; + case wxLIST_HITTEST_ONITEMSTATEICON: where = "on checkbox of"; break; case wxLIST_HITTEST_TOLEFT: where = "to the left of"; break; case wxLIST_HITTEST_TORIGHT: where = "to the right of"; break; default: where = "not clear exactly where on"; break; diff --git a/src/generic/listctrl.cpp b/src/generic/listctrl.cpp index 68a3ff3de1..e4f18e1c12 100644 --- a/src/generic/listctrl.cpp +++ b/src/generic/listctrl.cpp @@ -751,8 +751,8 @@ void wxListLineData::DrawInReportMode( wxDC *dc, ApplyAttributes(dc, rectHL, highlighted, current); - wxCoord x = rect.x + HEADER_OFFSET_X + ICON_OFFSET_X, - yMid = rect.y + rect.height/2; + wxCoord x = rect.x; + wxCoord yMid = rect.y + rect.height/2; if ( m_owner->HasCheckBoxes() ) { @@ -769,6 +769,8 @@ void wxListLineData::DrawInReportMode( wxDC *dc, x += cbSize.GetWidth() + (2 * MARGIN_AROUND_CHECKBOX); } + x += ICON_OFFSET_X; + size_t col = 0; for ( const auto& item : m_items ) { @@ -1754,10 +1756,19 @@ wxRect wxListMainWindow::GetLineIconRect(size_t line) const wxListLineData *ld = GetLine(line); wxASSERT_MSG( ld->HasImage(), wxT("should have an image") ); - wxRect rect; - rect.x = HEADER_OFFSET_X; - rect.y = GetLineY(line); - GetImageSize(ld->GetImage(), rect.width, rect.height); + wxRect rect = GetLineRect(line); + rect.x += ICON_OFFSET_X; + + if ( HasCheckBoxes() ) + { + wxSize cbSize = wxRendererNative::Get().GetCheckBoxSize(const_cast(this)); + rect.x += cbSize.GetWidth() + (2 * MARGIN_AROUND_CHECKBOX); + } + + // use full height of the line, same as win32 listctrl + int ix, iy; + GetImageSize(ld->GetImage(), ix, iy); + rect.width = ix; return rect; } @@ -1772,6 +1783,9 @@ long wxListMainWindow::HitTestLine(size_t line, int x, int y) const { wxASSERT_MSG( line < GetItemCount(), wxT("invalid line in HitTestLine") ); + if ( IsInsideCheckBox(line, x, y) ) + return wxLIST_HITTEST_ONITEMSTATEICON; + wxListLineData *ld = GetLine(line); if ( ld->HasImage() && GetLineIconRect(line).Contains(x, y) ) @@ -3860,16 +3874,17 @@ wxListMainWindow::GetSubItemRect(long item, long subItem, wxRect& rect, int ix, iy; GetImageSize(line->GetImage(), ix, iy); - const int iconWidth = ix + IMAGE_MARGIN_IN_REPORT_MODE; - if ( code == wxLIST_RECT_ICON ) { - rect.width = iconWidth; + rect.y += (rect.height - iy) / 2; + rect.width = ix; + rect.height = iy; } else // wxLIST_RECT_LABEL { - rect.x += iconWidth; - rect.width -= iconWidth; + // this includes the margin between icon and label (IMAGE_MARGIN_IN_REPORT_MODE) + rect.x += ix; + rect.width -= ix; } } else // No icon @@ -3956,14 +3971,14 @@ bool wxListMainWindow::IsItemChecked(long item) const } } -bool wxListMainWindow::IsInsideCheckBox(long item, int x, int y) +bool wxListMainWindow::IsInsideCheckBox(long item, int x, int y) const { if ( HasCheckBoxes() ) { wxRect lineRect = GetLineRect(item); - wxSize cbSize = wxRendererNative::Get().GetCheckBoxSize(this); + wxSize cbSize = wxRendererNative::Get().GetCheckBoxSize(const_cast(this)); int yOffset = (lineRect.height - cbSize.GetHeight()) / 2; - wxRect rr(wxPoint(MARGIN_AROUND_CHECKBOX, lineRect.y + yOffset), cbSize); + wxRect rr(wxPoint(lineRect.x + MARGIN_AROUND_CHECKBOX, lineRect.y + yOffset), cbSize); return rr.Contains(wxPoint(x, y)); }