From 07df14378a8951a50aec42c9c57e6549a138cd95 Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Thu, 21 Sep 2023 14:40:53 +0200 Subject: [PATCH 1/7] Generate wxEVT_LIST_ITEM_[UN]CHECKED events for MSW virtual wxListCtrl Make it consistent with wxListCtrl in report mode, and the generic wxListCtrl implementation. Fixes #23869 --- src/msw/listctrl.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp index e08f99ce35..e7ebf18b8d 100644 --- a/src/msw/listctrl.cpp +++ b/src/msw/listctrl.cpp @@ -1494,7 +1494,19 @@ bool wxListCtrl::EnableCheckBoxes(bool enable) void wxListCtrl::CheckItem(long item, bool state) { - ListView_SetCheckState(GetHwnd(), (UINT)item, (BOOL)state); + if ( IsVirtual() ) + { + // ListView_SetCheckState does nothing for a virtual control, so generate + // the event that would normally be generated in the LVN_ITEMCHANGED callback. + wxListEvent le(state ? wxEVT_LIST_ITEM_CHECKED : wxEVT_LIST_ITEM_UNCHECKED, GetId()); + le.SetEventObject(this); + le.m_itemIndex = item; + GetEventHandler()->ProcessEvent(le); + } + else + { + ListView_SetCheckState(GetHwnd(), (UINT)item, (BOOL)state); + } } bool wxListCtrl::IsItemChecked(long item) const From eca7ec0c9f11e545fbfa31d7f3352a9528eb327e Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Thu, 21 Sep 2023 14:43:32 +0200 Subject: [PATCH 2/7] Support using IsItemChecked in MSW virtual wxListCtrl Forward the call to OnGetItemIsChecked. --- src/msw/listctrl.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp index e7ebf18b8d..9bdf321f1e 100644 --- a/src/msw/listctrl.cpp +++ b/src/msw/listctrl.cpp @@ -1511,7 +1511,10 @@ void wxListCtrl::CheckItem(long item, bool state) bool wxListCtrl::IsItemChecked(long item) const { - return ListView_GetCheckState(GetHwnd(), (UINT)item) != 0; + if ( IsVirtual() ) + return OnGetItemIsChecked(item); + else + return ListView_GetCheckState(GetHwnd(), (UINT)item) != 0; } void wxListCtrl::ShowSortIndicator(int idx, bool ascending) From fa26c8724dbc2fba21777a1523c8771abc7e4c29 Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Thu, 21 Sep 2023 15:14:13 +0200 Subject: [PATCH 3/7] Don't use internal check state for generic virtual wxListCtrl Just like the MSW virtual wxListCtrl, the checkbox state is determined by OnGetItemIsChecked. See #23869 --- include/wx/generic/private/listctrl.h | 3 ++- src/generic/listctrl.cpp | 29 +++++++++++++++++++-------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/include/wx/generic/private/listctrl.h b/include/wx/generic/private/listctrl.h index 4369446cce..b02eb17d84 100644 --- a/include/wx/generic/private/listctrl.h +++ b/include/wx/generic/private/listctrl.h @@ -282,7 +282,8 @@ public: const wxRect& rect, const wxRect& rectHL, bool highlighted, - bool current ); + bool current, + bool checked ); private: // set the line to contain num items (only can be > 1 in report mode) diff --git a/src/generic/listctrl.cpp b/src/generic/listctrl.cpp index 0f11b5ef19..30b4babe61 100644 --- a/src/generic/listctrl.cpp +++ b/src/generic/listctrl.cpp @@ -737,7 +737,8 @@ void wxListLineData::DrawInReportMode( wxDC *dc, const wxRect& rect, const wxRect& rectHL, bool highlighted, - bool current ) + bool current, + bool checked ) { // TODO: later we should support setting different attributes for // different columns - to do it, just add "col" argument to @@ -758,7 +759,7 @@ void wxListLineData::DrawInReportMode( wxDC *dc, rr.x += MARGIN_AROUND_CHECKBOX; int flags = 0; - if (m_checked) + if (checked) flags |= wxCONTROL_CHECKED; wxRendererNative::Get().DrawCheckBox(m_owner, *dc, rr, flags); @@ -2095,7 +2096,8 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) rectLine, GetLineHighlightRect(line), IsHighlighted(line), - line == m_current ); + line == m_current, + IsItemChecked(line) ); } if ( HasFlag(wxLC_HRULES) ) @@ -3904,10 +3906,13 @@ bool wxListMainWindow::EnableCheckBoxes(bool enable) void wxListMainWindow::CheckItem(long item, bool state) { - wxListLineData *line = GetLine((size_t)item); - line->Check(state); + if ( !IsVirtual() ) + { + wxListLineData* line = GetLine((size_t)item); + line->Check(state); - RefreshLine(item); + RefreshLine(item); + } SendNotify(item, state ? wxEVT_LIST_ITEM_CHECKED : wxEVT_LIST_ITEM_UNCHECKED); @@ -3915,8 +3920,16 @@ void wxListMainWindow::CheckItem(long item, bool state) bool wxListMainWindow::IsItemChecked(long item) const { - wxListLineData *line = GetLine((size_t)item); - return line->IsChecked(); + if ( !IsVirtual() ) + { + wxListLineData* line = GetLine((size_t)item); + return line->IsChecked(); + } + else + { + wxGenericListCtrl* listctrl = GetListCtrl(); + return listctrl->OnGetItemIsChecked(item); + } } bool wxListMainWindow::IsInsideCheckBox(long item, int x, int y) From 1b8f4986853181a8b6750cebf9f8ac126d7f3143 Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Thu, 21 Sep 2023 15:39:24 +0200 Subject: [PATCH 4/7] Ignore wxListCtrl CheckItem/IsItemChecked when checkboxes are disabled --- src/generic/listctrl.cpp | 5 +++++ src/msw/listctrl.cpp | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/generic/listctrl.cpp b/src/generic/listctrl.cpp index 30b4babe61..3587ef8399 100644 --- a/src/generic/listctrl.cpp +++ b/src/generic/listctrl.cpp @@ -3906,6 +3906,8 @@ bool wxListMainWindow::EnableCheckBoxes(bool enable) void wxListMainWindow::CheckItem(long item, bool state) { + wxCHECK_RET( HasCheckBoxes(), "checkboxes are disabled" ); + if ( !IsVirtual() ) { wxListLineData* line = GetLine((size_t)item); @@ -3920,6 +3922,9 @@ void wxListMainWindow::CheckItem(long item, bool state) bool wxListMainWindow::IsItemChecked(long item) const { + if ( !HasCheckBoxes() ) + return false; + if ( !IsVirtual() ) { wxListLineData* line = GetLine((size_t)item); diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp index 9bdf321f1e..b4a806dbe3 100644 --- a/src/msw/listctrl.cpp +++ b/src/msw/listctrl.cpp @@ -1494,6 +1494,8 @@ bool wxListCtrl::EnableCheckBoxes(bool enable) void wxListCtrl::CheckItem(long item, bool state) { + wxCHECK_RET( HasCheckBoxes(), "checkboxes are disabled" ); + if ( IsVirtual() ) { // ListView_SetCheckState does nothing for a virtual control, so generate @@ -1511,6 +1513,9 @@ void wxListCtrl::CheckItem(long item, bool state) bool wxListCtrl::IsItemChecked(long item) const { + if ( !HasCheckBoxes() ) + return false; + if ( IsVirtual() ) return OnGetItemIsChecked(item); else From abdbe661c482e00538f350fe33f5ca8aa56f1e40 Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Thu, 21 Sep 2023 15:39:59 +0200 Subject: [PATCH 5/7] Update listctrl example to recent wxListCtrl checkbox changes Don't override CheckItem/IsItemChecked, but handle the checked/unchecked events. --- samples/listctrl/listtest.cpp | 33 +++++---------------------------- samples/listctrl/listtest.h | 3 --- 2 files changed, 5 insertions(+), 31 deletions(-) diff --git a/samples/listctrl/listtest.cpp b/samples/listctrl/listtest.cpp index 963c0168cd..a27e291639 100644 --- a/samples/listctrl/listtest.cpp +++ b/samples/listctrl/listtest.cpp @@ -1276,7 +1276,8 @@ void MyListCtrl::OnChecked(wxListEvent& event) if ( IsVirtual() ) { - CheckItem(event.GetIndex(), true); + m_checked.SelectItem(event.GetIndex(), true); + RefreshItem(event.GetIndex()); } event.Skip(); @@ -1288,7 +1289,8 @@ void MyListCtrl::OnUnChecked(wxListEvent& event) if ( IsVirtual() ) { - CheckItem(event.GetIndex(), false); + m_checked.SelectItem(event.GetIndex(), false); + RefreshItem(event.GetIndex()); } event.Skip(); @@ -1513,34 +1515,9 @@ wxString MyListCtrl::OnGetItemText(long item, long column) const } } -void MyListCtrl::CheckItem(long item, bool check) -{ - if ( IsVirtual() ) - { - m_checked.SelectItem(item, check); - RefreshItem(item); - } - else - { - wxListCtrl::CheckItem(item, check); - } -} - -bool MyListCtrl::IsItemChecked(long item) const -{ - if ( IsVirtual() ) - { - return m_checked.IsSelected(item); - } - else - { - return wxListCtrl::IsItemChecked(item); - } -} - bool MyListCtrl::OnGetItemIsChecked(long item) const { - return IsItemChecked(item); + return m_checked.IsSelected(item); } int MyListCtrl::OnGetItemColumnImage(long item, long column) const diff --git a/samples/listctrl/listtest.h b/samples/listctrl/listtest.h index 1a7c32b9af..e596e0e00a 100644 --- a/samples/listctrl/listtest.h +++ b/samples/listctrl/listtest.h @@ -74,9 +74,6 @@ public: void OnRightClick(wxMouseEvent& event); - virtual void CheckItem(long item, bool check) override; - virtual bool IsItemChecked(long item) const override; - private: void ShowContextMenu(const wxPoint& pos, long item); void SetColumnImage(int col, int image); From 39873236bfa803894d21fe1caa6bb793cbed0b69 Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Thu, 21 Sep 2023 15:40:09 +0200 Subject: [PATCH 6/7] Disable WS_EX_COMPOSITED for virtual wxListCtrl It generates an endless stream of LVN_ODCACHEHINT events. Fixes #23878 --- src/msw/listctrl.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp index b4a806dbe3..11d6f5bd95 100644 --- a/src/msw/listctrl.cpp +++ b/src/msw/listctrl.cpp @@ -257,6 +257,11 @@ bool wxListCtrl::Create(wxWindow *parent, if ( !MSWCreateControl(WC_LISTVIEW, wxEmptyString, pos, size) ) return false; + // LISTVIEW generates and endless stream of LVN_ODCACHEHINT event for a + // virtual wxListCtrl, so disable WS_EX_COMPOSITED. + if ( IsVirtual() ) + MSWDisableComposited(); + const wxVisualAttributes& defAttrs = GetDefaultAttributes(); if ( wxMSWDarkMode::IsActive() ) From f68bb391ce844ac1805394719758fb7b101aadb7 Mon Sep 17 00:00:00 2001 From: Maarten Bent Date: Thu, 21 Sep 2023 15:48:17 +0200 Subject: [PATCH 7/7] Update documentation of wxListCtrl IsItemChecked and CheckItem --- interface/wx/listctrl.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/interface/wx/listctrl.h b/interface/wx/listctrl.h index 4d8e88ceae..e256ea9b75 100644 --- a/interface/wx/listctrl.h +++ b/interface/wx/listctrl.h @@ -1386,6 +1386,8 @@ public: Always returns false if checkboxes support hadn't been enabled. + For a control with @c wxLC_VIRTUAL style, this uses OnGetItemIsChecked(). + @param item Item (zero-based) index. @since 3.1.0 @@ -1398,6 +1400,10 @@ public: This method only works if checkboxes support had been successfully enabled using EnableCheckBoxes(). + For a control with @c wxLC_VIRTUAL style, this will only generate the + @c EVT_LIST_ITEM_CHECKED and @c EVT_LIST_ITEM_UNCHECKED events. See + OnGetItemIsChecked() for information on how to update the checkbox state. + @param item Item (zero-based) index. @param check If @true, check the item, otherwise uncheck.