From fc9654eb821f926576c40de84808fff7ece50a83 Mon Sep 17 00:00:00 2001 From: ali kettab Date: Fri, 9 Dec 2022 18:37:55 +0100 Subject: [PATCH 01/20] wxQt: Fix bug in wxListCtrl with checkboxes Don't return the boolean value (m_checked) from wxQtListModel::data() since doing so will actually return Qt::PartiallyChecked if m_checked is true, and not Qt::Checked (enum value 2) which is not what we want. --- src/qt/listctrl.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index 12cc8056fb..5cf6e98f0b 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -226,7 +226,7 @@ public: case Qt::CheckStateRole: return col == 0 && m_listCtrl->HasCheckBoxes() - ? rowItem.m_checked + ? rowItem.CheckState() : QVariant(); default: @@ -804,6 +804,11 @@ private: return m_columns[index]; } + Qt::CheckState CheckState() const + { + return m_checked ? Qt::Checked : Qt::Unchecked; + } + std::vector m_columns; void *m_data; bool m_checked; From b0d7f2309fefb34e7408ebf4e4ae0ad539a0a4bd Mon Sep 17 00:00:00 2001 From: ali kettab Date: Fri, 9 Dec 2022 18:52:41 +0100 Subject: [PATCH 02/20] wxQt compatibility fix: wxListCtrl::GetColumnCount() always return 1 for wxLC_LIST --- src/qt/listctrl.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index 5cf6e98f0b..14aaa60cc2 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -1364,7 +1364,11 @@ int wxListCtrl::GetItemCount() const int wxListCtrl::GetColumnCount() const { - return m_model->columnCount(QModelIndex()); + // wxLC_LIST is special as we want to return 1 for it, for compatibility + // with the native wxMSW version and not the real number of columns, which + // is 0. For the other non-wxLC_REPORT modes returning 0 is fine, however, + // as wxMSW does it too. + return HasFlag(wxLC_LIST) ? 1 : m_model->columnCount(QModelIndex()); } wxSize wxListCtrl::GetItemSpacing() const From 074b272ee9d6f921b349532a4101aa29da7ed756 Mon Sep 17 00:00:00 2001 From: ali kettab Date: Fri, 9 Dec 2022 19:21:45 +0100 Subject: [PATCH 03/20] wxQt: implement wxListCtrl::IsVisible() --- include/wx/qt/listctrl.h | 2 ++ src/qt/listctrl.cpp | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/include/wx/qt/listctrl.h b/include/wx/qt/listctrl.h index dc5414cd87..0f1541581b 100644 --- a/include/wx/qt/listctrl.h +++ b/include/wx/qt/listctrl.h @@ -209,6 +209,8 @@ public: // Ensures this item is visible bool EnsureVisible(long item); + bool IsVisible(long item) const override; + // Find an item whose label matches this string, starting from the item after 'start' // or the beginning if 'start' is -1. long FindItem(long start, const wxString& str, bool partial = false); diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index 14aaa60cc2..c4f4f29d84 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -1671,6 +1671,17 @@ bool wxListCtrl::EnsureVisible(long item) return true; } +bool wxListCtrl::IsVisible(long item) const +{ + wxRect itemRect; + if ( !GetItemRect(item, itemRect) ) + return false; + + wxRect viewportRect = wxQtConvertRect( m_qtTreeWidget->viewport()->rect() ); + viewportRect.y += m_qtTreeWidget->GetHeaderHeight(); + return !viewportRect.Intersect(itemRect).IsEmpty(); +} + long wxListCtrl::FindItem(long start, const wxString& str, bool partial) { return m_model->FindItem(start, wxQtConvertString(str), partial); From 4192b28a7f04a1b66c1bfbe2957a8fe48f82566b Mon Sep 17 00:00:00 2001 From: ali kettab Date: Fri, 9 Dec 2022 20:29:48 +0100 Subject: [PATCH 04/20] wxQt: fix wxListCtrl::GetCountPerPage() implementation Use QTreeView's viewport height to get the right count per page Also document that the control must contain at least one item in order to return a valid value from the function. --- interface/wx/listctrl.h | 4 ++++ src/qt/listctrl.cpp | 27 +++++++++++++++++++++------ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/interface/wx/listctrl.h b/interface/wx/listctrl.h index 730b9ce3bc..6e74f5e71e 100644 --- a/interface/wx/listctrl.h +++ b/interface/wx/listctrl.h @@ -632,6 +632,10 @@ public: Gets the number of items that can fit vertically in the visible area of the list control (list or report view) or the total number of items in the list control (icon or small icon view). + + @note The caller must ensure that there is at least one item in the control + to be able to calculate the count per page under wxQt, otherwise 0 will + be returned. */ int GetCountPerPage() const; diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index c4f4f29d84..3a2aa39e3f 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -985,6 +985,24 @@ public: return header() != nullptr ? header()->height() : 0; } + int GetRowCount() const + { + if ( model() ) + return model()->rowCount(); + + return 0; + } + + int GetCountPerPage() const + { + // this may not be exact but should be a good approximation: + const int h = rowHeight(model()->index(0, 0)); + if ( h ) + return viewport()->height() / h; + else + return 0; + } + private: void itemClicked(const QModelIndex &index); void itemActivated(const QModelIndex &index); @@ -1170,12 +1188,9 @@ bool wxListCtrl::SetColumnsOrder(const wxArrayInt& WXUNUSED(orders)) int wxListCtrl::GetCountPerPage() const { - // this may not be exact but should be a good approximation: - const int h = m_qtTreeWidget->visualRect(m_model->index(0, 0)).height(); - if ( h ) - return m_qtTreeWidget->height() / h; - else - return 0; + wxCHECK_MSG(m_qtTreeWidget->GetRowCount() > 0, 0, + "wxListCtrl needs at least one item to calculate the count per page"); + return m_qtTreeWidget->GetCountPerPage(); } wxRect wxListCtrl::GetViewRect() const From 51d813cab7633243dd04ba05b3eacf60f6af8407 Mon Sep 17 00:00:00 2001 From: ali kettab Date: Fri, 9 Dec 2022 23:13:36 +0100 Subject: [PATCH 05/20] wxQt: Make wxListCtrl::SetColumnWidth() conformant to documentation Also update the listctrl sample to show it's effect on the second column wxLIST_AUTOSIZE (Ctrl+R) to resize the column to content wxLIST_AUTOSIZE_USEHEADER (Ctrl+Shift+R) to resize it to header section --- interface/wx/listctrl.h | 2 +- samples/listctrl/listtest.cpp | 19 +++++++++++++++++ samples/listctrl/listtest.h | 3 +++ src/qt/listctrl.cpp | 39 +++++++++++++++++++++++++++++++---- 4 files changed, 58 insertions(+), 5 deletions(-) diff --git a/interface/wx/listctrl.h b/interface/wx/listctrl.h index 6e74f5e71e..4d8e88ceae 100644 --- a/interface/wx/listctrl.h +++ b/interface/wx/listctrl.h @@ -1077,7 +1077,7 @@ public: @c wxLIST_AUTOSIZE will resize the column to the length of its longest item. @c wxLIST_AUTOSIZE_USEHEADER will resize the column to the length of the - header (Win32) or 80 pixels (other platforms). + header (wxMSW and wxQt) or 80 pixels (other platforms). In small or normal icon view, @a col must be -1, and the column width is set for all columns. diff --git a/samples/listctrl/listtest.cpp b/samples/listctrl/listtest.cpp index 86376f59fe..963c0168cd 100644 --- a/samples/listctrl/listtest.cpp +++ b/samples/listctrl/listtest.cpp @@ -151,6 +151,8 @@ wxBEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(LIST_TOGGLE_HEADER, MyFrame::OnToggleHeader) EVT_MENU(LIST_TOGGLE_BELL, MyFrame::OnToggleBell) EVT_MENU(LIST_CHECKVISIBILITY, MyFrame::OnCheckVisibility) + EVT_MENU(LIST_AUTOSIZE, MyFrame::OnAutoResize) + EVT_MENU(LIST_AUTOSIZE_USEHEADER, MyFrame::OnAutoResize) EVT_MENU(LIST_FIND, MyFrame::OnFind) EVT_MENU(LIST_TOGGLE_CHECKBOX, MyFrame::OnToggleItemCheckBox) EVT_MENU(LIST_GET_CHECKBOX, MyFrame::OnGetItemCheckBox) @@ -260,6 +262,9 @@ MyFrame::MyFrame(const wxString& title) menuList->AppendCheckItem(LIST_TOGGLE_BELL, "Toggle &bell on no match"); menuList->Append( LIST_CHECKVISIBILITY, "Check if lines 2 and 9 are visible" ); menuList->AppendSeparator(); + menuList->Append( LIST_AUTOSIZE, "Auto resize column 2\tCtrl-R" ); + menuList->Append( LIST_AUTOSIZE_USEHEADER, "Auto resize column 2 (use header)\tCtrl-Shift-R" ); + menuList->AppendSeparator(); menuList->AppendCheckItem(LIST_TOGGLE_CHECKBOXES, "&Enable Checkboxes"); menuList->Check(LIST_TOGGLE_CHECKBOXES, true); @@ -379,6 +384,20 @@ void MyFrame::OnCheckVisibility(wxCommandEvent& WXUNUSED(event)) wxLogMessage( "Line 9 is not visible" ); } +void MyFrame::OnAutoResize(wxCommandEvent& event) +{ + if ( event.GetId() == LIST_AUTOSIZE ) + { + wxLogMessage( "Column 2 resized to content" ); + m_listCtrl->SetColumnWidth(1, wxLIST_AUTOSIZE); + } + else + { + wxLogMessage( "Column 2 resized to header" ); + m_listCtrl->SetColumnWidth(1, wxLIST_AUTOSIZE_USEHEADER); + } +} + void MyFrame::OnGoTo(wxCommandEvent& WXUNUSED(event)) { if ( m_listCtrl->IsEmpty() ) diff --git a/samples/listctrl/listtest.h b/samples/listctrl/listtest.h index 9b53636415..1a7c32b9af 100644 --- a/samples/listctrl/listtest.h +++ b/samples/listctrl/listtest.h @@ -117,6 +117,7 @@ protected: void OnVirtualView(wxCommandEvent& event); void OnSmallVirtualView(wxCommandEvent& event); void OnCheckVisibility(wxCommandEvent& event); + void OnAutoResize(wxCommandEvent& event); void OnSetItemsCount(wxCommandEvent& event); @@ -243,5 +244,7 @@ enum LIST_THAW, LIST_TOGGLE_LINES, LIST_CHECKVISIBILITY, + LIST_AUTOSIZE, + LIST_AUTOSIZE_USEHEADER, LIST_CTRL = 1000 }; diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index 3a2aa39e3f..eb4ad1eacb 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -669,6 +669,8 @@ public: endInsertColumns(); + m_listCtrl->SetColumnWidth(newColumnIndex, info.m_width); + return true; } @@ -1156,13 +1158,42 @@ int wxListCtrl::GetColumnWidth(int col) const bool wxListCtrl::SetColumnWidth(int col, int width) { - if ( width < 0 ) + const auto header = m_qtTreeWidget->header(); + + if ( header && + col == GetColumnIndexFromOrder(col) && + col == GetColumnCount() - 1 ) { - m_qtTreeWidget->resizeColumnToContents(width); - return true; + // Always stretch the last section if _with_ is either + // wxLIST_AUTOSIZE or wxLIST_AUTOSIZE_USEHEADER + header->setStretchLastSection( width < 0 ); } - m_qtTreeWidget->setColumnWidth(col, width); + if ( width >= 0 ) + m_qtTreeWidget->setColumnWidth(col, width); + else + { + if ( width == wxLIST_AUTOSIZE_USEHEADER ) + { + const auto header = m_qtTreeWidget->header(); + const QHeaderView::ResizeMode oldResizeMode = header->sectionResizeMode(col); + + header->setSectionResizeMode(col, QHeaderView::ResizeToContents); + header->resizeSection(col, header->defaultSectionSize()); // passing any value > 0 is ok + header->setSectionResizeMode(col, oldResizeMode); + } + else // wxLIST_AUTOSIZE + { + // Temporarily hide the header if it's shown as we don't want the header section + // to be considered by resizeColumnToContents() because it's size will be honored + // if it's larger than the column content. + const bool wasHidden = m_qtTreeWidget->isHeaderHidden(); + m_qtTreeWidget->setHeaderHidden(true); + m_qtTreeWidget->resizeColumnToContents(col); + m_qtTreeWidget->setHeaderHidden(wasHidden); + } + } + return true; } From e6ad3e13401d5b76b290d5158874cb1c9f043d0f Mon Sep 17 00:00:00 2001 From: ali kettab Date: Fri, 9 Dec 2022 23:47:16 +0100 Subject: [PATCH 06/20] wxQt: wxListCtrl with icons support get done --- src/qt/listctrl.cpp | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index eb4ad1eacb..177a22c63a 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -299,6 +299,25 @@ public: case Qt::TextAlignmentRole: return header.m_align; + + case Qt::DecorationRole: + { + wxImageList *imageList = GetImageList(); + if ( imageList == nullptr ) + return QVariant(); + + int imageIndex = -1; + + if ( imageIndex == -1 ) + imageIndex = header.m_image; + + if ( imageIndex == -1 ) + return QVariant(); + + wxBitmap image = imageList->GetBitmap(imageIndex); + wxCHECK_MSG(image.IsOk(), QVariant(), "Invalid image"); + return QVariant::fromValue(*image.GetHandle()); + } } return QVariant(); } @@ -714,7 +733,7 @@ public: protected: wxImageList *GetImageList() const { - const int requiredList = m_listCtrl->HasFlag(wxLC_SMALL_ICON) + const int requiredList = m_listCtrl->HasFlag(wxLC_SMALL_ICON | wxLC_LIST | wxLC_REPORT) ? wxIMAGE_LIST_SMALL : wxIMAGE_LIST_NORMAL; return m_listCtrl->GetImageList(requiredList); @@ -1597,9 +1616,17 @@ long wxListCtrl::GetNextItem(long item, int WXUNUSED(geometry), int state) const return -1; } -void wxListCtrl::DoUpdateImages(int WXUNUSED(which)) +void wxListCtrl::DoUpdateImages(int which) { - // TODO: Ensure the icons are actually updated. + wxImageList* const imageList = GetUpdatedImageList(which); + + if ( imageList ) + { + int width, height; + imageList->GetSize(0, width, height); + m_qtTreeWidget->setIconSize(QSize(width, height)); + m_qtTreeWidget->update(); + } } void wxListCtrl::RefreshItem(long item) From 81cf0a4fe4209a452744777da6af2426dd07e052 Mon Sep 17 00:00:00 2001 From: ali kettab Date: Sat, 10 Dec 2022 00:07:21 +0100 Subject: [PATCH 07/20] wxQt: fix wxQtListModel::SetColumn() implementation Using the wxListItem's mask and state mask, one can change only selected attributes of a wxListCtrl item. --- src/qt/listctrl.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index 177a22c63a..1e8604d809 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -386,8 +386,13 @@ public: false, "Invalid column"); ColumnItem &column = m_headers[index]; - column.m_label = wxQtConvertString(info.GetText()); - column.m_align = wxQtConvertTextAlign(info.GetAlign()); + + if ( info.m_mask & wxLIST_MASK_TEXT ) + column.m_label = wxQtConvertString(info.GetText()); + if ( info.m_mask & wxLIST_MASK_FORMAT ) + column.m_align = wxQtConvertTextAlign(info.GetAlign()); + if ( info.m_mask & wxLIST_MASK_IMAGE ) + column.m_image = info.m_image; headerDataChanged(Qt::Horizontal, index, index); return true; From 7e14c7b7739e14fe8df59f21ab6f201606a9e952 Mon Sep 17 00:00:00 2001 From: ali kettab Date: Sat, 10 Dec 2022 19:23:12 +0100 Subject: [PATCH 08/20] wxQt: handle wxLIST_RECT_XXX code passed to wxListCtrl::GetSubItemRect() This makes the test pass now as GetSubItemRect() can report subitem rectangles correctly, labels or icons. icons support is added in this commit e6ad3e1340 (wxListCtrl with icons support get done) --- src/qt/listctrl.cpp | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index 1e8604d809..8996419bee 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -1376,7 +1376,7 @@ bool wxListCtrl::SetItemData(long item, long data) bool wxListCtrl::GetItemRect(long item, wxRect& rect, int WXUNUSED(code)) const { wxCHECK_MSG(item >= 0 && (item < GetItemCount()), false, - "invalid item in GetSubItemRect"); + "invalid item in GetItemRect"); const int columnCount = m_model->columnCount(QModelIndex()); if ( columnCount == 0 ) @@ -1395,7 +1395,7 @@ bool wxListCtrl::GetItemRect(long item, wxRect& rect, int WXUNUSED(code)) const bool wxListCtrl::GetSubItemRect(long item, long subItem, wxRect& rect, - int WXUNUSED(code)) const + int code) const { wxCHECK_MSG(item >= 0 && item < GetItemCount(), false, "invalid row index in GetSubItemRect"); @@ -1406,6 +1406,45 @@ bool wxListCtrl::GetSubItemRect(long item, const QModelIndex index = m_qtTreeWidget->model()->index(item, subItem); rect = wxQtConvertRect(m_qtTreeWidget->visualRect(index)); rect.Offset(0, m_qtTreeWidget->GetHeaderHeight()); + + switch ( code ) + { + case wxLIST_RECT_BOUNDS: + // Nothing to do. + break; + + case wxLIST_RECT_ICON: + case wxLIST_RECT_LABEL: + { + QVariant var = index.data(Qt::DecorationRole); + if ( var.isValid() ) + { + const int iconWidth = m_qtTreeWidget->iconSize().width(); + + if ( code == wxLIST_RECT_ICON ) + { + rect.width = iconWidth; + } + else // wxLIST_RECT_LABEL + { + rect.x += iconWidth; + rect.width -= iconWidth; + } + } + else // No icon + { + if ( code == wxLIST_RECT_ICON ) + rect = wxRect(); + //else: label rect is the same as the full one + } + } + break; + + default: + wxFAIL_MSG(wxS("Unknown rectangle requested")); + return false; + } + return true; } From 3e5e0bac81cef086e0e55baf5f4828e3b9a2472a Mon Sep 17 00:00:00 2001 From: ali kettab Date: Sat, 10 Dec 2022 21:26:25 +0100 Subject: [PATCH 09/20] wxQt: fix crash when SetItem() is called for virtual wxListCtrl --- src/qt/listctrl.cpp | 56 ++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index 8996419bee..4e8e52b30e 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -152,11 +152,11 @@ public: { } - int rowCount(const QModelIndex& WXUNUSED(parent)) const override + int rowCount(const QModelIndex& WXUNUSED(parent) = QModelIndex()) const override { return static_cast(m_rows.size()); } - int columnCount(const QModelIndex& WXUNUSED(parent)) const override + int columnCount(const QModelIndex& WXUNUSED(parent) = QModelIndex()) const override { return static_cast(m_headers.size()); } @@ -166,7 +166,7 @@ public: const int row = index.row(); const int col = index.column(); - wxCHECK_MSG(row >= 0 && static_cast(row) < m_rows.size(), + wxCHECK_MSG(row >= 0 && row < rowCount(), QVariant(), "Invalid row index" ); @@ -441,17 +441,37 @@ public: const int row = static_cast(info.GetId()); const int col = info.m_col; - wxCHECK_MSG( static_cast(row) < m_rows.size(), - false, "Invalid row"); - wxCHECK_MSG( static_cast(col) < m_headers.size(), - false, "Invalid col"); + wxCHECK_MSG( row < rowCount(), false, "Invalid row"); + wxCHECK_MSG( col < columnCount(), false, "Invalid col"); const QModelIndex modelIndex = index(row, col); + QVector roles; + + if ( info.m_mask & wxLIST_MASK_STATE ) + { + if ( (info.m_stateMask & wxLIST_STATE_FOCUSED) && + (info.m_state & wxLIST_STATE_FOCUSED) ) + m_view->setCurrentIndex(modelIndex); + if ( info.m_stateMask & wxLIST_STATE_SELECTED ) + { + QItemSelectionModel *selection = m_view->selectionModel(); + const QItemSelectionModel::SelectionFlag flag = + info.m_state & wxLIST_STATE_SELECTED + ? QItemSelectionModel::Select + : QItemSelectionModel::Deselect; + selection->select(modelIndex, flag|QItemSelectionModel::Rows); + } + } + + if ( IsVirtual() ) + { + // wxLIST_MASK_STATE is the only mask supported by virtual wxListCtrl + return true; + } + RowItem &rowItem = m_rows[row]; ColumnItem &columnItem = rowItem[col]; - QVector roles; - if ( (info.m_mask & wxLIST_MASK_TEXT) && !info.GetText().empty() ) { columnItem.m_label = wxQtConvertString(info.GetText()); @@ -470,22 +490,6 @@ public: roles.push_back(Qt::UserRole); } - if ( info.m_mask & wxLIST_MASK_STATE ) - { - if ( (info.m_stateMask & wxLIST_STATE_FOCUSED) && - (info.m_state & wxLIST_STATE_FOCUSED) ) - m_view->setCurrentIndex(modelIndex); - if ( info.m_stateMask & wxLIST_STATE_SELECTED ) - { - QItemSelectionModel *selection = m_view->selectionModel(); - const QItemSelectionModel::SelectionFlag flag = - info.m_state & wxLIST_STATE_SELECTED - ? QItemSelectionModel::Select - : QItemSelectionModel::Deselect; - selection->select(modelIndex, flag|QItemSelectionModel::Rows); - } - } - if ( info.m_mask & wxLIST_MASK_IMAGE ) { columnItem.m_image = info.m_image; @@ -876,7 +880,7 @@ public: { } - int rowCount(const QModelIndex& WXUNUSED(parent)) const override + int rowCount(const QModelIndex& WXUNUSED(parent) = QModelIndex()) const override { return m_rowCount; } From a6149cc5a2e83b89c65d76aefc29f849f1955e79 Mon Sep 17 00:00:00 2001 From: ali kettab Date: Sat, 10 Dec 2022 21:35:58 +0100 Subject: [PATCH 10/20] wxQt: remove redundant code which is also done in SetWindowStyleFlag() --- src/qt/listctrl.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index 4e8e52b30e..00c77cdf4a 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -1128,9 +1128,6 @@ bool wxListCtrl::Create(wxWindow *parent, m_qtTreeWidget->setModel(m_model); m_model->SetView(m_qtTreeWidget); - if (style & wxLC_NO_HEADER) - m_qtTreeWidget->setHeaderHidden(true); - m_qtTreeWidget->setRootIsDecorated(false); m_qtTreeWidget->setSelectionBehavior(QAbstractItemView::SelectRows); From 3ffa2cad0ca5418f48d0625d8bb51d8adc191cc9 Mon Sep 17 00:00:00 2001 From: ali kettab Date: Sat, 10 Dec 2022 22:05:20 +0100 Subject: [PATCH 11/20] wxQt: fix creating the wxListCtrl with wxLC_EDIT_LABELS flag set Also make EditLabel() implicitly selects and focuses the specified item as the native control under wxMSW does. this actualy fixes two bugs in wxQt 1) EndEditLabel() will be able to find the item being edited. 2) The control display multiple in-place editors in different areas the first time EditLabel() is called. --- src/qt/listctrl.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index 00c77cdf4a..a7487aa538 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -1005,6 +1005,17 @@ public: return m_itemDelegate.GetEditControl(); } + void EnableEditLabel(bool enable) + { + if ( enable ) + { + setItemDelegate(&m_itemDelegate); + setEditTriggers(SelectedClicked | EditKeyPressed); + } + else + setEditTriggers(NoEditTriggers); + } + virtual void paintEvent(QPaintEvent *event) override { QTreeView::paintEvent(event); @@ -1050,9 +1061,6 @@ wxQtListTreeWidget::wxQtListTreeWidget( wxWindow *parent, wxListCtrl *handler ) connect(this, &QTreeView::clicked, this, &wxQtListTreeWidget::itemClicked); connect(this, &QTreeView::pressed, this, &wxQtListTreeWidget::itemPressed); connect(this, &QTreeView::activated, this, &wxQtListTreeWidget::itemActivated); - - setItemDelegate(&m_itemDelegate); - setEditTriggers(NoEditTriggers); } void wxQtListTreeWidget::EmitListEvent(wxEventType typ, @@ -1614,6 +1622,7 @@ void wxListCtrl::SetWindowStyleFlag(long style) { m_windowStyle = style; m_qtTreeWidget->setHeaderHidden((style & wxLC_NO_HEADER) != 0); + m_qtTreeWidget->EnableEditLabel((style & wxLC_EDIT_LABELS) != 0); m_qtTreeWidget->setSelectionMode((style & wxLC_SINGLE_SEL) != 0 ? QAbstractItemView::SingleSelection : QAbstractItemView::ExtendedSelection @@ -1758,6 +1767,7 @@ wxTextCtrl* wxListCtrl::EditLabel(long item, // Open the editor first so that it's available when handling events as per // wx standard. const QModelIndex index = m_model->index(item, 0); + m_qtTreeWidget->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select); m_qtTreeWidget->openPersistentEditor(index); wxListEvent event; From 0ba76530135b877ce04421e37a0b8e2cbd168fa8 Mon Sep 17 00:00:00 2001 From: ali kettab Date: Sat, 10 Dec 2022 23:01:40 +0100 Subject: [PATCH 12/20] wxQt: enable navigation between items using TAB key --- src/qt/listctrl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index a7487aa538..23bcb41b45 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -1138,6 +1138,7 @@ bool wxListCtrl::Create(wxWindow *parent, m_qtTreeWidget->setRootIsDecorated(false); m_qtTreeWidget->setSelectionBehavior(QAbstractItemView::SelectRows); + m_qtTreeWidget->setTabKeyNavigation(true); if ( !QtCreateControl(parent, id, pos, size, style, validator, name) ) return false; From 4001647ebe3b38800adf0b296c332a4f053d4264 Mon Sep 17 00:00:00 2001 From: ali kettab Date: Sun, 11 Dec 2022 14:05:27 +0100 Subject: [PATCH 13/20] wxQt: Enhance EmitListEvent() to support more list-event types We will need it in the upcoming commits --- src/qt/listctrl.cpp | 49 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index 23bcb41b45..cae199ba47 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -946,10 +946,20 @@ private: class wxQtListTreeWidget : public wxQtEventSignalHandler< QTreeView, wxListCtrl > { + using BaseClass = wxQtEventSignalHandler< QTreeView, wxListCtrl >; + + // Data type passed to EmitListEvent() as extra data which is essentially + // a pair of two ints with these meanings: + // first = column or first (de)selected row + // second = column width or last (de)selected row + using ListEventData = std::pair; + public: wxQtListTreeWidget( wxWindow *parent, wxListCtrl *handler ); - void EmitListEvent(wxEventType typ, const QModelIndex &index) const; + bool EmitListEvent(wxEventType type, + const QModelIndex &index, + const ListEventData* data = nullptr) const; void closeEditor( QWidget *editor, @@ -1063,8 +1073,9 @@ wxQtListTreeWidget::wxQtListTreeWidget( wxWindow *parent, wxListCtrl *handler ) connect(this, &QTreeView::activated, this, &wxQtListTreeWidget::itemActivated); } -void wxQtListTreeWidget::EmitListEvent(wxEventType typ, - const QModelIndex &index) const +bool wxQtListTreeWidget::EmitListEvent(wxEventType type, + const QModelIndex &index, + const ListEventData* data) const { wxListCtrl *handler = GetHandler(); if ( handler ) @@ -1072,9 +1083,37 @@ void wxQtListTreeWidget::EmitListEvent(wxEventType typ, // prepare the event // ----------------- wxListEvent event; - InitListEvent(event, handler, typ, index); - EmitEvent(event); + InitListEvent(event, handler, type, index); + + if ( !index.isValid() && data ) + { + if ( type == wxEVT_LIST_ITEM_SELECTED || + type == wxEVT_LIST_ITEM_DESELECTED ) + { + // Instead of sending hundreds of (de)selection messages, send only + // one for each range which is more efficient (see issue #4541) + // data->first is the first row in the (de)selection + // data->second is the last row in the (de)selection + wxFAIL_MSG("No implemention yet"); + } + else if ( data->first >= 0 && data->first < handler->GetColumnCount() ) + { + event.m_col = data->first; + event.m_item.m_width = data->second; + event.m_pointDrag = wxQtConvertPoint( QCursor::pos() ); + + if ( type == wxEVT_LIST_COL_RIGHT_CLICK ) + { + // handlers of this event expect m_pointDrag in client coordinates + event.m_pointDrag = handler->ScreenToClient(event.m_pointDrag); + } + } + } + + return !EmitEvent(event) || event.IsAllowed(); } + + return false; } void wxQtListTreeWidget::itemClicked(const QModelIndex &index) From 4a00d99652d0791977988f5fe5ffc9b43f8d39ea Mon Sep 17 00:00:00 2001 From: ali kettab Date: Sun, 11 Dec 2022 14:44:33 +0100 Subject: [PATCH 14/20] wxQt: fix wxEVT_LIST_ITEM_XXX events generation currentChanged() generates wxEVT_LIST_ITEM_FOCUSED event itemActivated() generates wxEVT_LIST_ITEM_ACTIVATED event selectionChanged() generates wxEVT_LIST_ITEM_SELECTED and wxEVT_LIST_ITEM_DESELECTED events itemPressed() generates wxEVT_LIST_ITEM_RIGHT_CLICK and wxEVT_LIST_ITEM_MIDDLE_CLICK events --- src/qt/listctrl.cpp | 76 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 11 deletions(-) diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index cae199ba47..b296804a1b 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #ifndef WX_PRECOMP #include "wx/bitmap.h" @@ -1054,9 +1055,34 @@ public: return 0; } +protected: + virtual void currentChanged(const QModelIndex& current, + const QModelIndex& previous) override + { + EmitListEvent(wxEVT_LIST_ITEM_FOCUSED, current); + QTreeView::currentChanged(current, previous); + } + + virtual void selectionChanged(const QItemSelection &selected, + const QItemSelection &deselected) override; + + // Event handlers + virtual void mousePressEvent(QMouseEvent* event) override + { + const QModelIndex itemPressed = indexAt(event->pos()); + + BaseClass::mousePressEvent(event); + + if ( !itemPressed.isValid() ) + { + // wx: deselect all if clicking on empty space + clearSelection(); + } + } + private: - void itemClicked(const QModelIndex &index); - void itemActivated(const QModelIndex &index); + void itemActivated(const QModelIndex &index) + { EmitListEvent(wxEVT_LIST_ITEM_ACTIVATED, index); } void itemPressed(const QModelIndex &index); wxQtStyledItemDelegate m_itemDelegate; @@ -1068,7 +1094,6 @@ wxQtListTreeWidget::wxQtListTreeWidget( wxWindow *parent, wxListCtrl *handler ) m_itemDelegate(handler), m_closingEditor(0) { - connect(this, &QTreeView::clicked, this, &wxQtListTreeWidget::itemClicked); connect(this, &QTreeView::pressed, this, &wxQtListTreeWidget::itemPressed); connect(this, &QTreeView::activated, this, &wxQtListTreeWidget::itemActivated); } @@ -1116,19 +1141,48 @@ bool wxQtListTreeWidget::EmitListEvent(wxEventType type, return false; } -void wxQtListTreeWidget::itemClicked(const QModelIndex &index) -{ - EmitListEvent(wxEVT_LIST_ITEM_SELECTED, index); -} - void wxQtListTreeWidget::itemPressed(const QModelIndex &index) { - EmitListEvent(wxEVT_LIST_ITEM_SELECTED, index); + wxEventType eventType; + Qt::MouseButtons mouseButton = QGuiApplication::mouseButtons(); + + switch( mouseButton ) + { + case Qt::RightButton: + eventType = wxEVT_LIST_ITEM_RIGHT_CLICK; + break; + case Qt::MiddleButton: + eventType = wxEVT_LIST_ITEM_MIDDLE_CLICK; + break; + default: + return; + } + + EmitListEvent(eventType, index); } -void wxQtListTreeWidget::itemActivated(const QModelIndex &index) +void wxQtListTreeWidget::selectionChanged(const QItemSelection& selected, + const QItemSelection& deselected) { - EmitListEvent(wxEVT_LIST_ITEM_ACTIVATED, index); + // A QItemSelection is basically a list of selection ranges, i.e. QItemSelectionRange. + + for ( const auto& range : deselected ) + { + for ( int row = range.top(); row <= range.bottom(); ++row ) + { + EmitListEvent(wxEVT_LIST_ITEM_DESELECTED, model()->index(row, 0)); + } + } + + for ( const auto& range : selected ) + { + for ( int row = range.top(); row <= range.bottom(); ++row ) + { + EmitListEvent(wxEVT_LIST_ITEM_SELECTED, model()->index(row, 0)); + } + } + + QTreeView::selectionChanged(selected, deselected); } // Specialization: to safely remove and delete the model associated with QTreeView From eedc6f7bad7e487e401d5e33836f0c228d7fa740 Mon Sep 17 00:00:00 2001 From: ali kettab Date: Sun, 11 Dec 2022 16:59:01 +0100 Subject: [PATCH 15/20] wxQt: add wxQtHeaderView for wxEVT_LIST_COL_XXX events generation --- src/qt/listctrl.cpp | 95 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index b296804a1b..58c92c3570 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -1080,6 +1080,99 @@ protected: } } + // wxQtHeaderView is for wxEVT_LIST_COL_XXX events generation only, + // i.e. it doesn't try to add anything fancy or new functionality + // not supported by QHeaderView + class wxQtHeaderView : public QHeaderView + { + public: + wxQtHeaderView(wxQtListTreeWidget* parent) + : QHeaderView(Qt::Horizontal, parent) + , m_parent(parent), m_isDragging(false) + { + setSectionsClickable(true); + setContextMenuPolicy(Qt::CustomContextMenu); + + connect(this, &QHeaderView::sectionClicked, + this, &wxQtHeaderView::sectionClicked); + connect(this, &QHeaderView::customContextMenuRequested, + this, &wxQtHeaderView::sectionRightClicked); + connect(this, &QHeaderView::sectionResized, + this, &wxQtHeaderView::sectionResized); + } + + protected: + virtual void mouseReleaseEvent(QMouseEvent* event) override + { + if ( m_isDragging ) + { + m_isDragging = false; + + const ListEventData data = + std::make_pair(m_parent->columnAt(event->x()), -1); + + m_parent->EmitListEvent(wxEVT_LIST_COL_END_DRAG, QModelIndex(), &data); + } + + QHeaderView::mouseReleaseEvent(event); + } + + private: + void sectionClicked(int logicalIndex) + { + const ListEventData data = std::make_pair(logicalIndex, -1); + + m_parent->EmitListEvent(wxEVT_LIST_COL_CLICK, QModelIndex(), &data); + } + + void sectionRightClicked(const QPoint& pos) + { + const ListEventData data = + std::make_pair(m_parent->columnAt(pos.x()), -1); + + m_parent->EmitListEvent(wxEVT_LIST_COL_RIGHT_CLICK, QModelIndex(), &data); + } + + void sectionResized(int logicalIndex, int oldSize, int newSize) + { + ListEventData data; + + if ( m_isDragging ) + { + data = std::make_pair(logicalIndex, newSize); + m_parent->EmitListEvent(wxEVT_LIST_COL_DRAGGING, QModelIndex(), &data); + return; + } + + if ( !underMouse() ) + { + // sectionResized() also called if the control is being resized and + // the last section in the header is strechable, so just return and + // do nothing in this case. + return; + } + + data = std::make_pair(logicalIndex, oldSize); + + if ( sectionResizeMode(logicalIndex) == QHeaderView::Fixed || + !m_parent->EmitListEvent(wxEVT_LIST_COL_BEGIN_DRAG, QModelIndex(), &data) ) + { + wxQtEnsureSignalsBlocked blocker(this); + resizeSection(logicalIndex, oldSize); + // This only takes effect after wxEVT_LIST_COL_END_DRAG is generated, + // after which the user can no longer drag the column. + setSectionResizeMode(logicalIndex, QHeaderView::Fixed); + return; + } + + m_isDragging = true; + } + + wxQtListTreeWidget* const m_parent; + + bool m_isDragging; + }; + private: void itemActivated(const QModelIndex &index) { EmitListEvent(wxEVT_LIST_ITEM_ACTIVATED, index); } @@ -1094,6 +1187,8 @@ wxQtListTreeWidget::wxQtListTreeWidget( wxWindow *parent, wxListCtrl *handler ) m_itemDelegate(handler), m_closingEditor(0) { + setHeader(new wxQtHeaderView(this)); + connect(this, &QTreeView::pressed, this, &wxQtListTreeWidget::itemPressed); connect(this, &QTreeView::activated, this, &wxQtListTreeWidget::itemActivated); } From 24e87f34643950b27d6a459c7997a2e3199a3045 Mon Sep 17 00:00:00 2001 From: ali kettab Date: Sun, 11 Dec 2022 20:16:09 +0100 Subject: [PATCH 16/20] wxQt: added support for sort indicators --- include/wx/qt/listctrl.h | 5 +++++ src/qt/listctrl.cpp | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/include/wx/qt/listctrl.h b/include/wx/qt/listctrl.h index 0f1541581b..7bb46d23e8 100644 --- a/include/wx/qt/listctrl.h +++ b/include/wx/qt/listctrl.h @@ -264,6 +264,11 @@ public: // data is arbitrary data to be passed to the sort function. bool SortItems(wxListCtrlCompare fn, wxIntPtr data); + // Sort indicator in header. + virtual void ShowSortIndicator(int col, bool ascending = true) override; + virtual int GetSortIndicator() const override; + virtual bool IsAscendingSortIndicator() const override; + virtual QWidget *GetHandle() const override; protected: diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index 58c92c3570..82a1724692 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -706,7 +706,9 @@ public: void SortItems(wxListCtrlCompare fn, wxIntPtr data) { CompareAdapter compare(fn, data); + beginResetModel(); std::sort(m_rows.begin(), m_rows.end(), compare); + endResetModel(); } bool IsItemChecked(long item) const @@ -1092,6 +1094,7 @@ protected: { setSectionsClickable(true); setContextMenuPolicy(Qt::CustomContextMenu); + setSortIndicatorShown(true); connect(this, &QHeaderView::sectionClicked, this, &wxQtHeaderView::sectionClicked); @@ -1189,6 +1192,8 @@ wxQtListTreeWidget::wxQtListTreeWidget( wxWindow *parent, wxListCtrl *handler ) { setHeader(new wxQtHeaderView(this)); + setSortingEnabled(true); + connect(this, &QTreeView::pressed, this, &wxQtListTreeWidget::itemPressed); connect(this, &QTreeView::activated, this, &wxQtListTreeWidget::itemActivated); } @@ -2108,6 +2113,36 @@ bool wxListCtrl::SortItems(wxListCtrlCompare fn, wxIntPtr data) return true; } +void wxListCtrl::ShowSortIndicator(int col, bool ascending) +{ + const auto header = m_qtTreeWidget->header(); + if ( header ) + header->setSortIndicator(col, ascending ? Qt::DescendingOrder + : Qt::AscendingOrder); +} + +int wxListCtrl::GetSortIndicator() const +{ + const auto header = m_qtTreeWidget->header(); + if ( header && header->isSortIndicatorShown() ) + { + // If no section has a sort indicator, sortIndicatorSection() + // returns section 0 by default. + return header->sortIndicatorSection(); + } + + return -1; +} + +bool wxListCtrl::IsAscendingSortIndicator() const +{ + const auto header = m_qtTreeWidget->header(); + if ( header ) + return header->sortIndicatorOrder() == Qt::AscendingOrder; + + return true; +} + QWidget *wxListCtrl::GetHandle() const { return m_qtTreeWidget; From 5059a84f55982f7043f84b11d44eb0973ddef3c3 Mon Sep 17 00:00:00 2001 From: ali kettab Date: Mon, 12 Dec 2022 12:46:09 +0100 Subject: [PATCH 17/20] Apply review suggestions --- src/qt/listctrl.cpp | 64 ++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index 82a1724692..e74e75a545 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -446,7 +446,6 @@ public: wxCHECK_MSG( col < columnCount(), false, "Invalid col"); const QModelIndex modelIndex = index(row, col); - QVector roles; if ( info.m_mask & wxLIST_MASK_STATE ) { @@ -473,6 +472,8 @@ public: RowItem &rowItem = m_rows[row]; ColumnItem &columnItem = rowItem[col]; + QVector roles; + if ( (info.m_mask & wxLIST_MASK_TEXT) && !info.GetText().empty() ) { columnItem.m_label = wxQtConvertString(info.GetText()); @@ -951,11 +952,12 @@ class wxQtListTreeWidget : public wxQtEventSignalHandler< QTreeView, wxListCtrl { using BaseClass = wxQtEventSignalHandler< QTreeView, wxListCtrl >; - // Data type passed to EmitListEvent() as extra data which is essentially - // a pair of two ints with these meanings: - // first = column or first (de)selected row - // second = column width or last (de)selected row - using ListEventData = std::pair; + // Data type passed to EmitListEvent() as extra data + struct ListEventData + { + int m_colOrFirstRow = -1; // column index or first (de)selected row + int m_colWidthOrLastRow = -1; // column width or last (de)selected row + }; public: wxQtListTreeWidget( wxWindow *parent, wxListCtrl *handler ); @@ -1068,20 +1070,6 @@ protected: virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override; - // Event handlers - virtual void mousePressEvent(QMouseEvent* event) override - { - const QModelIndex itemPressed = indexAt(event->pos()); - - BaseClass::mousePressEvent(event); - - if ( !itemPressed.isValid() ) - { - // wx: deselect all if clicking on empty space - clearSelection(); - } - } - // wxQtHeaderView is for wxEVT_LIST_COL_XXX events generation only, // i.e. it doesn't try to add anything fancy or new functionality // not supported by QHeaderView @@ -1090,7 +1078,7 @@ protected: public: wxQtHeaderView(wxQtListTreeWidget* parent) : QHeaderView(Qt::Horizontal, parent) - , m_parent(parent), m_isDragging(false) + , m_parent(parent) { setSectionsClickable(true); setContextMenuPolicy(Qt::CustomContextMenu); @@ -1111,8 +1099,7 @@ protected: { m_isDragging = false; - const ListEventData data = - std::make_pair(m_parent->columnAt(event->x()), -1); + const ListEventData data { m_parent->columnAt(event->x()), -1 }; m_parent->EmitListEvent(wxEVT_LIST_COL_END_DRAG, QModelIndex(), &data); } @@ -1123,15 +1110,14 @@ protected: private: void sectionClicked(int logicalIndex) { - const ListEventData data = std::make_pair(logicalIndex, -1); + const ListEventData data { logicalIndex, -1 }; m_parent->EmitListEvent(wxEVT_LIST_COL_CLICK, QModelIndex(), &data); } void sectionRightClicked(const QPoint& pos) { - const ListEventData data = - std::make_pair(m_parent->columnAt(pos.x()), -1); + const ListEventData data { m_parent->columnAt(pos.x()), -1 }; m_parent->EmitListEvent(wxEVT_LIST_COL_RIGHT_CLICK, QModelIndex(), &data); } @@ -1142,7 +1128,7 @@ protected: if ( m_isDragging ) { - data = std::make_pair(logicalIndex, newSize); + data = { logicalIndex, newSize }; m_parent->EmitListEvent(wxEVT_LIST_COL_DRAGGING, QModelIndex(), &data); return; } @@ -1155,7 +1141,7 @@ protected: return; } - data = std::make_pair(logicalIndex, oldSize); + data = { logicalIndex, oldSize }; if ( sectionResizeMode(logicalIndex) == QHeaderView::Fixed || !m_parent->EmitListEvent(wxEVT_LIST_COL_BEGIN_DRAG, QModelIndex(), &data) ) @@ -1173,7 +1159,7 @@ protected: wxQtListTreeWidget* const m_parent; - bool m_isDragging; + bool m_isDragging = false; }; private: @@ -1217,14 +1203,15 @@ bool wxQtListTreeWidget::EmitListEvent(wxEventType type, { // Instead of sending hundreds of (de)selection messages, send only // one for each range which is more efficient (see issue #4541) - // data->first is the first row in the (de)selection - // data->second is the last row in the (de)selection - wxFAIL_MSG("No implemention yet"); + // data->m_colOrFirstRow is the first row in the (de)selection + // data->m_colWidthOrLastRow is the last row in the (de)selection + wxFAIL_MSG("No implementation yet"); } - else if ( data->first >= 0 && data->first < handler->GetColumnCount() ) + else if ( data->m_colOrFirstRow >= 0 && + data->m_colOrFirstRow < handler->GetColumnCount() ) { - event.m_col = data->first; - event.m_item.m_width = data->second; + event.m_col = data->m_colOrFirstRow; + event.m_item.m_width = data->m_colWidthOrLastRow; event.m_pointDrag = wxQtConvertPoint( QCursor::pos() ); if ( type == wxEVT_LIST_COL_RIGHT_CLICK ) @@ -1391,13 +1378,15 @@ bool wxListCtrl::SetColumnWidth(int col, int width) col == GetColumnIndexFromOrder(col) && col == GetColumnCount() - 1 ) { - // Always stretch the last section if _with_ is either + // Always stretch the last section if _width_ is either // wxLIST_AUTOSIZE or wxLIST_AUTOSIZE_USEHEADER header->setStretchLastSection( width < 0 ); } if ( width >= 0 ) + { m_qtTreeWidget->setColumnWidth(col, width); + } else { if ( width == wxLIST_AUTOSIZE_USEHEADER ) @@ -2115,6 +2104,9 @@ bool wxListCtrl::SortItems(wxListCtrlCompare fn, wxIntPtr data) void wxListCtrl::ShowSortIndicator(int col, bool ascending) { + // It seems that wx and Qt are not using the same meaning for the + // "ascending" order, for that we pass to setSortIndicator() the + // inverted logic to make things work correctly. const auto header = m_qtTreeWidget->header(); if ( header ) header->setSortIndicator(col, ascending ? Qt::DescendingOrder From a9b17545b8868f834b907ea55e71aac757f81a3b Mon Sep 17 00:00:00 2001 From: ali kettab Date: Tue, 13 Dec 2022 11:21:32 +0100 Subject: [PATCH 18/20] fix compilation with msvc2015 --- src/qt/listctrl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index e74e75a545..fa0642ea73 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -955,8 +955,8 @@ class wxQtListTreeWidget : public wxQtEventSignalHandler< QTreeView, wxListCtrl // Data type passed to EmitListEvent() as extra data struct ListEventData { - int m_colOrFirstRow = -1; // column index or first (de)selected row - int m_colWidthOrLastRow = -1; // column width or last (de)selected row + int m_colOrFirstRow; // column index or first (de)selected row + int m_colWidthOrLastRow; // column width or last (de)selected row }; public: From fccdce2e204d4f82df2acf766cb6eb005c7f8976 Mon Sep 17 00:00:00 2001 From: ali kettab Date: Wed, 14 Dec 2022 14:20:59 +0100 Subject: [PATCH 19/20] wxQt: fix crash when asterisk key (Qt::Key_Asterisk) is pressed The crash happens inside QTreeView::keyPressEvent(QKeyEvent*) when handling Qt::Key_Asterisk key press. The reason is the non conformant rowCount() and columnCount() implementations which according to Qt documentation should return 0 when the parent is valid. --- src/qt/listctrl.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index fa0642ea73..8dbb320cac 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -153,12 +153,18 @@ public: { } - int rowCount(const QModelIndex& WXUNUSED(parent) = QModelIndex()) const override + int rowCount(const QModelIndex& parent = QModelIndex()) const override { + if ( parent.isValid() ) + return 0; + return static_cast(m_rows.size()); } - int columnCount(const QModelIndex& WXUNUSED(parent) = QModelIndex()) const override + int columnCount(const QModelIndex& parent = QModelIndex()) const override { + if ( parent.isValid() ) + return 0; + return static_cast(m_headers.size()); } @@ -884,8 +890,11 @@ public: { } - int rowCount(const QModelIndex& WXUNUSED(parent) = QModelIndex()) const override + int rowCount(const QModelIndex& parent = QModelIndex()) const override { + if ( parent.isValid() ) + return 0; + return m_rowCount; } From f7efe297f32f31f92f68c458ca726ba515e97bd5 Mon Sep 17 00:00:00 2001 From: ali kettab Date: Wed, 14 Dec 2022 14:36:29 +0100 Subject: [PATCH 20/20] wxQt: added wxEVT_LIST_KEY_DOWN event generation to wxListCtrl --- src/qt/listctrl.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/qt/listctrl.cpp b/src/qt/listctrl.cpp index 8dbb320cac..19b32d3228 100644 --- a/src/qt/listctrl.cpp +++ b/src/qt/listctrl.cpp @@ -1176,6 +1176,8 @@ private: { EmitListEvent(wxEVT_LIST_ITEM_ACTIVATED, index); } void itemPressed(const QModelIndex &index); + void OnKeyDown(wxKeyEvent& event); // to generate wxEVT_LIST_KEY_DOWN event + wxQtStyledItemDelegate m_itemDelegate; wxRecursionGuardFlag m_closingEditor; }; @@ -1191,6 +1193,8 @@ wxQtListTreeWidget::wxQtListTreeWidget( wxWindow *parent, wxListCtrl *handler ) connect(this, &QTreeView::pressed, this, &wxQtListTreeWidget::itemPressed); connect(this, &QTreeView::activated, this, &wxQtListTreeWidget::itemActivated); + + handler->Bind(wxEVT_KEY_DOWN, &wxQtListTreeWidget::OnKeyDown, this); } bool wxQtListTreeWidget::EmitListEvent(wxEventType type, @@ -1281,6 +1285,28 @@ void wxQtListTreeWidget::selectionChanged(const QItemSelection& selected, QTreeView::selectionChanged(selected, deselected); } +void wxQtListTreeWidget::OnKeyDown(wxKeyEvent& event) +{ + // send a list event + wxListEvent le; + InitListEvent(le, GetHandler(), wxEVT_LIST_KEY_DOWN, currentIndex()); + + const long itemId = le.m_item.m_itemId; + + if ( itemId != -1 ) + { + // fill the other fields too + le.m_item.m_text = GetHandler()->GetItemText(itemId, 0); + le.m_item.m_data = GetHandler()->GetItemData(itemId); + } + + le.m_code = event.GetKeyCode(); + + EmitEvent( le ); + + event.Skip(); +} + // Specialization: to safely remove and delete the model associated with QTreeView template<> void wxQtEventSignalHandler< QTreeView, wxListCtrl >::HandleDestroyedSignal()