Merge branch 'qt-listctrl-fixes' of https://github.com/AliKet/wxWidgets
wxListCtrl fixes for wxQt. See #23029.
This commit is contained in:
commit
b47c46a8d7
5 changed files with 497 additions and 65 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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() )
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue