diff options
Diffstat (limited to 'src/widgets/itemviews/qtreeview.cpp')
-rw-r--r-- | src/widgets/itemviews/qtreeview.cpp | 301 |
1 files changed, 201 insertions, 100 deletions
diff --git a/src/widgets/itemviews/qtreeview.cpp b/src/widgets/itemviews/qtreeview.cpp index 2280cdaa2e..1ae81023c5 100644 --- a/src/widgets/itemviews/qtreeview.cpp +++ b/src/widgets/itemviews/qtreeview.cpp @@ -3,7 +3,7 @@ #include "qtreeview.h" #include <qheaderview.h> -#include <qitemdelegate.h> +#include <qabstractitemdelegate.h> #include <qapplication.h> #include <qscrollbar.h> #include <qpainter.h> @@ -124,8 +124,7 @@ QT_BEGIN_NAMESPACE that can be taken for views that are intended to display items with equal heights is to set the \l uniformRowHeights property to true. - \sa QListView, QTreeWidget, {View Classes}, QAbstractItemModel, QAbstractItemView, - {Dir View Example} + \sa QListView, QTreeWidget, {View Classes}, QAbstractItemModel, QAbstractItemView */ @@ -170,6 +169,8 @@ QTreeView::QTreeView(QTreeViewPrivate &dd, QWidget *parent) */ QTreeView::~QTreeView() { + Q_D(QTreeView); + d->clearConnections(); } /*! @@ -181,18 +182,12 @@ void QTreeView::setModel(QAbstractItemModel *model) if (model == d->model) return; if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) { - disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)), - this, SLOT(rowsRemoved(QModelIndex,int,int))); - - disconnect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_modelAboutToBeReset())); + for (const QMetaObject::Connection &connection : d->modelConnections) + QObject::disconnect(connection); } if (d->selectionModel) { // support row editing - disconnect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), - d->model, SLOT(submit())); - disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)), - this, SLOT(rowsRemoved(QModelIndex,int,int))); - disconnect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_modelAboutToBeReset())); + QObject::disconnect(d->selectionmodelConnection); } d->viewItems.clear(); d->expandedIndexes.clear(); @@ -202,20 +197,24 @@ void QTreeView::setModel(QAbstractItemModel *model) d->geometryRecursionBlock = false; QAbstractItemView::setModel(model); - // QAbstractItemView connects to a private slot - disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)), - this, SLOT(_q_rowsRemoved(QModelIndex,int,int))); - // do header layout after the tree - disconnect(d->model, SIGNAL(layoutChanged()), - d->header, SLOT(_q_layoutChanged())); - // QTreeView has a public slot for this - connect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)), - this, SLOT(rowsRemoved(QModelIndex,int,int))); - - connect(d->model, SIGNAL(modelAboutToBeReset()), SLOT(_q_modelAboutToBeReset())); - + if (d->model) { + // QAbstractItemView connects to a private slot + QObjectPrivate::disconnect(d->model, &QAbstractItemModel::rowsRemoved, + d, &QAbstractItemViewPrivate::rowsRemoved); + // do header layout after the tree + QObjectPrivate::disconnect(d->model, &QAbstractItemModel::layoutChanged, + d->header->d_func(), &QAbstractItemViewPrivate::layoutChanged); + + d->modelConnections = { + // QTreeView has a public slot for this + QObject::connect(d->model, &QAbstractItemModel::rowsRemoved, + this, &QTreeView::rowsRemoved), + QObjectPrivate::connect(d->model, &QAbstractItemModel::modelAboutToBeReset, + d, &QTreeViewPrivate::modelAboutToBeReset) + }; + } if (d->sortingEnabled) - d->_q_sortIndicatorChanged(header()->sortIndicatorSection(), header()->sortIndicatorOrder()); + d->sortIndicatorChanged(header()->sortIndicatorSection(), header()->sortIndicatorOrder()); } /*! @@ -237,8 +236,7 @@ void QTreeView::setSelectionModel(QItemSelectionModel *selectionModel) Q_ASSERT(selectionModel); if (d->selectionModel) { // support row editing - disconnect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), - d->model, SLOT(submit())); + QObject::disconnect(d->selectionmodelConnection); } d->header->setSelectionModel(selectionModel); @@ -246,8 +244,9 @@ void QTreeView::setSelectionModel(QItemSelectionModel *selectionModel) if (d->selectionModel) { // support row editing - connect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), - d->model, SLOT(submit())); + d->selectionmodelConnection = + connect(d->selectionModel, &QItemSelectionModel::currentRowChanged, + d->model, &QAbstractItemModel::submit); } } @@ -287,16 +286,18 @@ void QTreeView::setHeader(QHeaderView *header) d->header->setSelectionModel(d->selectionModel); } - connect(d->header, SIGNAL(sectionResized(int,int,int)), - this, SLOT(columnResized(int,int,int))); - connect(d->header, SIGNAL(sectionMoved(int,int,int)), - this, SLOT(columnMoved())); - connect(d->header, SIGNAL(sectionCountChanged(int,int)), - this, SLOT(columnCountChanged(int,int))); - connect(d->header, SIGNAL(sectionHandleDoubleClicked(int)), - this, SLOT(resizeColumnToContents(int))); - connect(d->header, SIGNAL(geometriesChanged()), - this, SLOT(updateGeometries())); + d->headerConnections = { + connect(d->header, &QHeaderView::sectionResized, + this, &QTreeView::columnResized), + connect(d->header, &QHeaderView::sectionMoved, + this, &QTreeView::columnMoved), + connect(d->header, &QHeaderView::sectionCountChanged, + this, &QTreeView::columnCountChanged), + connect(d->header, &QHeaderView::sectionHandleDoubleClicked, + this, &QTreeView::resizeColumnToContents), + connect(d->header, &QHeaderView::geometriesChanged, + this, &QTreeView::updateGeometries) + }; setSortingEnabled(d->sortingEnabled); d->updateGeometry(); @@ -840,11 +841,12 @@ void QTreeView::setSortingEnabled(bool enable) //sortByColumn has to be called before we connect or set the sortingEnabled flag // because otherwise it will not call sort on the model. sortByColumn(header()->sortIndicatorSection(), header()->sortIndicatorOrder()); - connect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), - this, SLOT(_q_sortIndicatorChanged(int,Qt::SortOrder)), Qt::UniqueConnection); + d->sortHeaderConnection = + QObjectPrivate::connect(header(), &QHeaderView::sortIndicatorChanged, + d, &QTreeViewPrivate::sortIndicatorChanged, + Qt::UniqueConnection); } else { - disconnect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), - this, SLOT(_q_sortIndicatorChanged(int,Qt::SortOrder))); + QObject::disconnect(d->sortHeaderConnection); } d->sortingEnabled = enable; } @@ -1068,33 +1070,64 @@ void QTreeView::keyboardSearch(const QString &search) QRect QTreeView::visualRect(const QModelIndex &index) const { Q_D(const QTreeView); + return d->visualRect(index, QTreeViewPrivate::SingleSection); +} - if (!d->isIndexValid(index) || isIndexHidden(index)) +/*! + \internal + \return the visual rectangle at \param index, according to \param rule. + \list + \li SingleSection + The return value matches the section, which \a index points to. + \li FullRow + Return the rectangle of the entire row, no matter which section + \a index points to. + \li AddRowIndicatorToFirstSection + Like SingleSection. If \index points to the first section, add the + row indicator and its margins. + \endlist + */ +QRect QTreeViewPrivate::visualRect(const QModelIndex &index, RectRule rule) const +{ + Q_Q(const QTreeView); + + if (!isIndexValid(index)) return QRect(); - d->executePostedLayout(); + // Calculate the entire row's rectangle, even if one of the elements is hidden + if (q->isIndexHidden(index) && rule != FullRow) + return QRect(); + + executePostedLayout(); - int vi = d->viewIndex(index); - if (vi < 0) + const int viewIndex = this->viewIndex(index); + if (viewIndex < 0) return QRect(); - bool spanning = d->viewItems.at(vi).spanning; + const bool spanning = viewItems.at(viewIndex).spanning; + const int column = index.column(); // if we have a spanning item, make the selection stretch from left to right - int x = (spanning ? 0 : columnViewportPosition(index.column())); - int w = (spanning ? d->header->length() : columnWidth(index.column())); - // handle indentation - if (d->isTreePosition(index.column())) { - int i = d->indentationForItem(vi); - w -= i; - if (!isRightToLeft()) - x += i; + int x = (spanning ? 0 : q->columnViewportPosition(column)); + int width = (spanning ? header->length() : q->columnWidth(column)); + + const bool addIndentation = isTreePosition(column) && (column > 0 || rule == SingleSection); + + if (rule == FullRow) { + x = 0; + width = q->viewport()->width(); + } else if (addIndentation) { + // calculate indentation + const int indentation = indentationForItem(viewIndex); + width -= indentation; + if (!q->isRightToLeft()) + x += indentation; } - int y = d->coordinateForItem(vi); - int h = d->itemHeight(vi); + const int y = coordinateForItem(viewIndex); + const int height = itemHeight(viewIndex); - return QRect(x, y, w, h); + return QRect(x, y, width, height); } /*! @@ -1277,16 +1310,13 @@ bool QTreeView::viewportEvent(QEvent *event) case QEvent::HoverLeave: case QEvent::HoverMove: { QHoverEvent *he = static_cast<QHoverEvent*>(event); - int oldBranch = d->hoverBranch; + const int oldBranch = d->hoverBranch; d->hoverBranch = d->itemDecorationAt(he->position().toPoint()); QModelIndex newIndex = indexAt(he->position().toPoint()); if (d->hover != newIndex || d->hoverBranch != oldBranch) { // Update the whole hovered over row. No need to update the old hovered // row, that is taken care in superclass hover handling. - QRect rect = visualRect(newIndex); - rect.setX(0); - rect.setWidth(viewport()->width()); - viewport()->update(rect); + viewport()->update(d->visualRect(newIndex, QTreeViewPrivate::FullRow)); } break; } default: @@ -1369,28 +1399,61 @@ bool QTreeViewPrivate::expandOrCollapseItemAtPos(const QPoint &pos) return false; } -void QTreeViewPrivate::_q_modelDestroyed() +void QTreeViewPrivate::modelDestroyed() { //we need to clear the viewItems because it contains QModelIndexes to //the model currently being destroyed viewItems.clear(); - QAbstractItemViewPrivate::_q_modelDestroyed(); + QAbstractItemViewPrivate::modelDestroyed(); } QRect QTreeViewPrivate::intersectedRect(const QRect rect, const QModelIndex &topLeft, const QModelIndex &bottomRight) const { - Q_Q(const QTreeView); - const auto parentIdx = topLeft.parent(); executePostedLayout(); QRect updateRect; - for (int r = topLeft.row(); r <= bottomRight.row(); ++r) { - if (isRowHidden(model->index(r, 0, parentIdx))) + int left = std::numeric_limits<int>::max(); + int right = std::numeric_limits<int>::min(); + for (int row = topLeft.row(); row <= bottomRight.row(); ++row) { + const auto idxCol0 = model->index(row, 0, parentIdx); + if (isRowHidden(idxCol0)) continue; - for (int c = topLeft.column(); c <= bottomRight.column(); ++c) { - const QModelIndex idx(model->index(r, c, parentIdx)); - updateRect |= q->visualRect(idx); + QRect rowRect; + if (left != std::numeric_limits<int>::max()) { + // we already know left and right boundary of the rect to update + rowRect = visualRect(idxCol0, FullRow); + if (!rowRect.intersects(rect)) + continue; + rowRect = QRect(left, rowRect.top(), right, rowRect.bottom()); + } else if (!spanningIndexes.isEmpty() && spanningIndexes.contains(idxCol0)) { + // isFirstColumnSpanned re-creates the child index so take a shortcut here + // spans the whole row, therefore ask for FullRow instead for every cell + rowRect = visualRect(idxCol0, FullRow); + if (!rowRect.intersects(rect)) + continue; + } else { + for (int col = topLeft.column(); col <= bottomRight.column(); ++col) { + if (header->isSectionHidden(col)) + continue; + const QModelIndex idx(model->index(row, col, parentIdx)); + const QRect idxRect = visualRect(idx, SingleSection); + if (idxRect.isNull()) + continue; + // early exit when complete row is out of viewport + if (idxRect.top() > rect.bottom() && idxRect.bottom() < rect.top()) + break; + if (!idxRect.intersects(rect)) + continue; + rowRect = rowRect.united(idxRect); + if (rowRect.left() < rect.left() && rowRect.right() > rect.right()) + break; + } + left = std::min(left, rowRect.left()); + right = std::max(right, rowRect.right()); } + updateRect = updateRect.united(rowRect); + if (updateRect.contains(rect)) // already full rect covered? + break; } return rect.intersected(updateRect); } @@ -1496,8 +1559,9 @@ void QTreeView::drawTree(QPainter *painter, const QRegion ®ion) const // paint the visible rows for (; i < viewItems.size() && y <= area.bottom(); ++i) { + const QModelIndex &index = viewItems.at(i).index; const int itemHeight = d->itemHeight(i); - option.rect.setRect(0, y, viewportWidth, itemHeight); + option.rect = d->visualRect(index, QTreeViewPrivate::FullRow); option.state = state | (viewItems.at(i).expanded ? QStyle::State_Open : QStyle::State_None) | (viewItems.at(i).hasChildren ? QStyle::State_Children : QStyle::State_None) | (viewItems.at(i).hasMoreSiblings ? QStyle::State_Sibling : QStyle::State_None); @@ -2116,6 +2180,7 @@ void QTreeView::doItemsLayout() } QAbstractItemView::doItemsLayout(); d->header->doItemsLayout(); + d->updateAccessibility(); } /*! @@ -2367,7 +2432,7 @@ QRegion QTreeView::visualRegionForSelection(const QItemSelection &selection) con } if (!leftIndex.isValid()) continue; - const QRect leftRect = visualRect(leftIndex); + const QRect leftRect = d->visualRect(leftIndex, QTreeViewPrivate::SingleSection); int top = leftRect.top(); QModelIndex rightIndex = range.bottomRight(); while (rightIndex.isValid() && isIndexHidden(rightIndex)) { @@ -2378,7 +2443,7 @@ QRegion QTreeView::visualRegionForSelection(const QItemSelection &selection) con } if (!rightIndex.isValid()) continue; - const QRect rightRect = visualRect(rightIndex); + const QRect rightRect = d->visualRect(rightIndex, QTreeViewPrivate::SingleSection); int bottom = rightRect.bottom(); if (top > bottom) qSwap<int>(top, bottom); @@ -2554,7 +2619,7 @@ void QTreeView::rowsRemoved(const QModelIndex &parent, int start, int end) d->viewItems.clear(); d->doDelayedItemsLayout(); d->hasRemovedItems = true; - d->_q_rowsRemoved(parent, start, end); + d->rowsRemoved(parent, start, end); } /*! @@ -2645,7 +2710,8 @@ QSize QTreeView::viewportSizeHint() const return QAbstractItemView::viewportSizeHint(); // Get rect for last item - const QRect deepestRect = visualRect(d->viewItems.last().index); + const QRect deepestRect = d->visualRect(d->viewItems.last().index, + QTreeViewPrivate::SingleSection); if (!deepestRect.isValid()) return QAbstractItemView::viewportSizeHint(); @@ -2678,6 +2744,7 @@ void QTreeView::expandAll() d->layout(-1, true); updateGeometries(); d->viewport->update(); + d->updateAccessibility(); } /*! @@ -2801,6 +2868,7 @@ void QTreeView::expandToDepth(int depth) updateGeometries(); d->viewport->update(); + d->updateAccessibility(); } /*! @@ -3061,10 +3129,25 @@ void QTreeViewPrivate::initialize() q->setHeader(header); #if QT_CONFIG(animation) animationsEnabled = q->style()->styleHint(QStyle::SH_Widget_Animation_Duration, nullptr, q) > 0; - QObject::connect(&animatedOperation, SIGNAL(finished()), q, SLOT(_q_endAnimatedOperation())); + animationConnection = + QObjectPrivate::connect(&animatedOperation, &QVariantAnimation::finished, + this, &QTreeViewPrivate::endAnimatedOperation); #endif // animation } +void QTreeViewPrivate::clearConnections() +{ + for (const QMetaObject::Connection &connection : modelConnections) + QObject::disconnect(connection); + for (const QMetaObject::Connection &connection : headerConnections) + QObject::disconnect(connection); + QObject::disconnect(selectionmodelConnection); + QObject::disconnect(sortHeaderConnection); +#if QT_CONFIG(animation) + QObject::disconnect(animationConnection); +#endif +} + void QTreeViewPrivate::expand(int item, bool emitSignal) { Q_Q(QTreeView); @@ -3097,6 +3180,7 @@ void QTreeViewPrivate::expand(int item, bool emitSignal) beginAnimatedOperation(); #endif // animation } + updateAccessibility(); } void QTreeViewPrivate::insertViewItems(int pos, int count, const QTreeViewItem &viewItem) @@ -3257,7 +3341,7 @@ QPixmap QTreeViewPrivate::renderTreeToPixmapForAnimation(const QRect &rect) cons for (QEditorIndexHash::const_iterator it = editorIndexHash.constBegin(); it != editorIndexHash.constEnd(); ++it) { QWidget *editor = it.key(); const QModelIndex &index = it.value(); - option.rect = q->visualRect(index); + option.rect = visualRect(index, SingleSection); if (option.rect.isValid()) { if (QAbstractItemDelegate *delegate = q->itemDelegateForIndex(index)) @@ -3277,7 +3361,7 @@ QPixmap QTreeViewPrivate::renderTreeToPixmapForAnimation(const QRect &rect) cons return pixmap; } -void QTreeViewPrivate::_q_endAnimatedOperation() +void QTreeViewPrivate::endAnimatedOperation() { Q_Q(QTreeView); q->setState(stateBeforeAnimation); @@ -3286,25 +3370,40 @@ void QTreeViewPrivate::_q_endAnimatedOperation() } #endif // animation -void QTreeViewPrivate::_q_modelAboutToBeReset() +void QTreeViewPrivate::modelAboutToBeReset() { viewItems.clear(); } -void QTreeViewPrivate::_q_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +void QTreeViewPrivate::columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { if (start <= 0 && 0 <= end) viewItems.clear(); - QAbstractItemViewPrivate::_q_columnsAboutToBeRemoved(parent, start, end); + QAbstractItemViewPrivate::columnsAboutToBeRemoved(parent, start, end); } -void QTreeViewPrivate::_q_columnsRemoved(const QModelIndex &parent, int start, int end) +void QTreeViewPrivate::columnsRemoved(const QModelIndex &parent, int start, int end) { if (start <= 0 && 0 <= end) doDelayedItemsLayout(); - QAbstractItemViewPrivate::_q_columnsRemoved(parent, start, end); + QAbstractItemViewPrivate::columnsRemoved(parent, start, end); +} + +void QTreeViewPrivate::updateAccessibility() +{ +#if QT_CONFIG(accessibility) + Q_Q(QTreeView); + if (pendingAccessibilityUpdate) { + pendingAccessibilityUpdate = false; + if (QAccessible::isActive()) { + QAccessibleTableModelChangeEvent event(q, QAccessibleTableModelChangeEvent::ModelReset); + QAccessible::updateAccessibility(&event); + } + } +#endif } + /** \internal creates and initialize the viewItem structure of the children of the element \li @@ -3325,6 +3424,15 @@ void QTreeViewPrivate::layout(int i, bool recursiveExpanding, bool afterIsUninit return; } +#if QT_CONFIG(accessibility) + // QAccessibleTree's rowCount implementation uses viewItems.size(), so + // we need to invalidate any cached accessibility data structures if + // that value changes during the run of this function. + const auto resetModelIfNeeded = qScopeGuard([oldViewItemsSize = viewItems.size(), this]{ + pendingAccessibilityUpdate |= oldViewItemsSize != viewItems.size(); + }); +#endif + int count = 0; if (model->hasChildren(parent)) { if (model->canFetchMore(parent)) { @@ -3963,7 +4071,7 @@ bool QTreeViewPrivate::hasVisibleChildren(const QModelIndex& parent) const return false; } -void QTreeViewPrivate::_q_sortIndicatorChanged(int column, Qt::SortOrder order) +void QTreeViewPrivate::sortIndicatorChanged(int column, Qt::SortOrder order) { model->sort(column, order); } @@ -3987,24 +4095,17 @@ void QTreeViewPrivate::updateIndentationFromStyle() */ void QTreeView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { + Q_D(QTreeView); QAbstractItemView::currentChanged(current, previous); if (allColumnsShowFocus()) { - if (previous.isValid()) { - QRect previousRect = visualRect(previous); - previousRect.setX(0); - previousRect.setWidth(viewport()->width()); - viewport()->update(previousRect); - } - if (current.isValid()) { - QRect currentRect = visualRect(current); - currentRect.setX(0); - currentRect.setWidth(viewport()->width()); - viewport()->update(currentRect); - } + if (previous.isValid()) + viewport()->update(d->visualRect(previous, QTreeViewPrivate::FullRow)); + if (current.isValid()) + viewport()->update(d->visualRect(current, QTreeViewPrivate::FullRow)); } #if QT_CONFIG(accessibility) - if (QAccessible::isActive() && current.isValid()) { + if (QAccessible::isActive() && current.isValid() && hasFocus()) { Q_D(QTreeView); QAccessibleEvent event(this, QAccessible::Focus); |