Merge branch 'qt-listctrl-fixes' of https://github.com/AliKet/wxWidgets

wxListCtrl fixes for wxQt.

See #23029.
This commit is contained in:
Vadim Zeitlin 2022-12-15 01:26:37 +01:00
commit b47c46a8d7
5 changed files with 497 additions and 65 deletions

View file

@ -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);
@ -262,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:

View file

@ -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;
@ -1073,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.

View file

@ -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() )

View file

@ -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
};

View file

@ -13,6 +13,7 @@
#include <QtWidgets/QTreeView>
#include <QtWidgets/QItemDelegate>
#include <QtWidgets/QStyledItemDelegate>
#include <QtGui/QGuiApplication>
#ifndef WX_PRECOMP
#include "wx/bitmap.h"
@ -152,12 +153,18 @@ public:
{
}
int rowCount(const QModelIndex& WXUNUSED(parent)) const override
int rowCount(const QModelIndex& parent = QModelIndex()) const override
{
if ( parent.isValid() )
return 0;
return static_cast<int>(m_rows.size());
}
int columnCount(const QModelIndex& WXUNUSED(parent)) const override
int columnCount(const QModelIndex& parent = QModelIndex()) const override
{
if ( parent.isValid() )
return 0;
return static_cast<int>(m_headers.size());
}
@ -166,7 +173,7 @@ public:
const int row = index.row();
const int col = index.column();
wxCHECK_MSG(row >= 0 && static_cast<size_t>(row) < m_rows.size(),
wxCHECK_MSG(row >= 0 && row < rowCount(),
QVariant(),
"Invalid row index"
);
@ -226,7 +233,7 @@ public:
case Qt::CheckStateRole:
return col == 0 && m_listCtrl->HasCheckBoxes()
? rowItem.m_checked
? rowItem.CheckState()
: QVariant();
default:
@ -299,6 +306,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();
}
@ -367,8 +393,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;
@ -417,12 +448,33 @@ public:
const int row = static_cast<int>(info.GetId());
const int col = info.m_col;
wxCHECK_MSG( static_cast<size_t>(row) < m_rows.size(),
false, "Invalid row");
wxCHECK_MSG( static_cast<size_t>(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);
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];
@ -446,22 +498,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;
@ -669,13 +705,17 @@ public:
endInsertColumns();
m_listCtrl->SetColumnWidth(newColumnIndex, info.m_width);
return true;
}
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
@ -712,7 +752,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);
@ -804,6 +844,11 @@ private:
return m_columns[index];
}
Qt::CheckState CheckState() const
{
return m_checked ? Qt::Checked : Qt::Unchecked;
}
std::vector<ColumnItem> m_columns;
void *m_data;
bool m_checked;
@ -845,8 +890,11 @@ public:
{
}
int rowCount(const QModelIndex& WXUNUSED(parent)) const override
int rowCount(const QModelIndex& parent = QModelIndex()) const override
{
if ( parent.isValid() )
return 0;
return m_rowCount;
}
@ -911,10 +959,21 @@ private:
class wxQtListTreeWidget : public wxQtEventSignalHandler< QTreeView, wxListCtrl >
{
using BaseClass = wxQtEventSignalHandler< QTreeView, wxListCtrl >;
// Data type passed to EmitListEvent() as extra data
struct ListEventData
{
int m_colOrFirstRow; // column index or first (de)selected row
int m_colWidthOrLastRow; // column width or last (de)selected row
};
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,
@ -970,6 +1029,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);
@ -980,11 +1050,134 @@ 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;
}
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;
// 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)
{
setSectionsClickable(true);
setContextMenuPolicy(Qt::CustomContextMenu);
setSortIndicatorShown(true);
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 { 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 { logicalIndex, -1 };
m_parent->EmitListEvent(wxEVT_LIST_COL_CLICK, QModelIndex(), &data);
}
void sectionRightClicked(const QPoint& pos)
{
const ListEventData data { 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 = { 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 = { 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 = false;
};
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);
void OnKeyDown(wxKeyEvent& event); // to generate wxEVT_LIST_KEY_DOWN event
wxQtStyledItemDelegate m_itemDelegate;
wxRecursionGuardFlag m_closingEditor;
};
@ -994,16 +1187,19 @@ wxQtListTreeWidget::wxQtListTreeWidget( wxWindow *parent, wxListCtrl *handler )
m_itemDelegate(handler),
m_closingEditor(0)
{
connect(this, &QTreeView::clicked, this, &wxQtListTreeWidget::itemClicked);
setHeader(new wxQtHeaderView(this));
setSortingEnabled(true);
connect(this, &QTreeView::pressed, this, &wxQtListTreeWidget::itemPressed);
connect(this, &QTreeView::activated, this, &wxQtListTreeWidget::itemActivated);
setItemDelegate(&m_itemDelegate);
setEditTriggers(NoEditTriggers);
handler->Bind(wxEVT_KEY_DOWN, &wxQtListTreeWidget::OnKeyDown, this);
}
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 )
@ -1011,24 +1207,104 @@ void wxQtListTreeWidget::EmitListEvent(wxEventType typ,
// prepare the event
// -----------------
wxListEvent event;
InitListEvent(event, handler, typ, index);
EmitEvent(event);
}
}
InitListEvent(event, handler, type, index);
void wxQtListTreeWidget::itemClicked(const QModelIndex &index)
{
EmitListEvent(wxEVT_LIST_ITEM_SELECTED, 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->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->m_colOrFirstRow >= 0 &&
data->m_colOrFirstRow < handler->GetColumnCount() )
{
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 )
{
// 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::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);
}
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
@ -1075,11 +1351,9 @@ 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);
m_qtTreeWidget->setTabKeyNavigation(true);
if ( !QtCreateControl(parent, id, pos, size, style, validator, name) )
return false;
@ -1133,13 +1407,44 @@ 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 _width_ 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;
}
@ -1165,12 +1470,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
@ -1301,7 +1603,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 )
@ -1320,7 +1622,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");
@ -1331,6 +1633,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;
}
@ -1359,7 +1700,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
@ -1495,6 +1840,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
@ -1542,9 +1888,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)
@ -1631,6 +1985,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;
@ -1662,6 +2017,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);
@ -1771,6 +2137,39 @@ bool wxListCtrl::SortItems(wxListCtrlCompare fn, wxIntPtr data)
return true;
}
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
: 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;