diff options
Diffstat (limited to 'src/widgets/itemviews/qtreeview.cpp')
-rw-r--r-- | src/widgets/itemviews/qtreeview.cpp | 397 |
1 files changed, 237 insertions, 160 deletions
diff --git a/src/widgets/itemviews/qtreeview.cpp b/src/widgets/itemviews/qtreeview.cpp index da7bbe7d34..744f29ca17 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> @@ -15,7 +15,7 @@ #include <qdebug.h> #include <QMetaMethod> #include <private/qscrollbar_p.h> -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) #include <qaccessible.h> #endif @@ -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; } @@ -1002,9 +1004,9 @@ void QTreeView::keyboardSearch(const QString &search) // special case for searches with same key like 'aaaaa' bool sameKey = false; - if (d->keyboardInput.length() > 1) { - int c = d->keyboardInput.count(d->keyboardInput.at(d->keyboardInput.length() - 1)); - sameKey = (c == d->keyboardInput.length()); + if (d->keyboardInput.size() > 1) { + int c = d->keyboardInput.count(d->keyboardInput.at(d->keyboardInput.size() - 1)); + sameKey = (c == d->keyboardInput.size()); if (sameKey) skipRow = true; } @@ -1029,7 +1031,7 @@ void QTreeView::keyboardSearch(const QString &search) int bestAbove = -1; int bestBelow = -1; QString searchString = sameKey ? QString(d->keyboardInput.at(0)) : d->keyboardInput; - for (int i = 0; i < d->viewItems.count(); ++i) { + for (int i = 0; i < d->viewItems.size(); ++i) { if ((int)d->viewItems.at(i).level > previousLevel) { QModelIndex searchFrom = d->viewItems.at(i).index; if (start.column() > 0) @@ -1037,7 +1039,7 @@ void QTreeView::keyboardSearch(const QString &search) if (searchFrom.parent() == start.parent()) searchFrom = start; QModelIndexList match = d->model->match(searchFrom, Qt::DisplayRole, searchString); - if (match.count()) { + if (match.size()) { int hitIndex = d->viewIndex(match.at(0)); if (hitIndex >= 0 && hitIndex < startIndex) bestAbove = bestAbove == -1 ? hitIndex : qMin(hitIndex, bestAbove); @@ -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,18 +1399,16 @@ 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; @@ -1389,7 +1417,7 @@ QRect QTreeViewPrivate::intersectedRect(const QRect rect, const QModelIndex &top continue; for (int c = topLeft.column(); c <= bottomRight.column(); ++c) { const QModelIndex idx(model->index(r, c, parentIdx)); - updateRect |= q->visualRect(idx); + updateRect |= visualRect(idx, SingleSection); } } return rect.intersected(updateRect); @@ -1458,7 +1486,7 @@ void QTreeView::drawTree(QPainter *painter, const QRegion ®ion) const const QStyle::State state = option.state; d->current = 0; - if (viewItems.count() == 0 || d->header->count() == 0 || !d->itemDelegate) { + if (viewItems.size() == 0 || d->header->count() == 0 || !d->itemDelegate) { d->paintAlternatingRowColors(painter, &option, 0, region.boundingRect().bottom()+1); return; } @@ -1487,7 +1515,7 @@ void QTreeView::drawTree(QPainter *painter, const QRegion ®ion) const int y = firstVisibleItemOffset; // we may only see part of the first item // start at the top of the viewport and iterate down to the update area - for (; i < viewItems.count(); ++i) { + for (; i < viewItems.size(); ++i) { const int itemHeight = d->itemHeight(i); if (y + itemHeight > area.top()) break; @@ -1495,9 +1523,10 @@ void QTreeView::drawTree(QPainter *painter, const QRegion ®ion) const } // paint the visible rows - for (; i < viewItems.count() && y <= area.bottom(); ++i) { + 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); @@ -1556,11 +1585,11 @@ void QTreeViewPrivate::calcLogicalIndices( } } - itemPositions->resize(logicalIndices->count()); - for (int currentLogicalSection = 0; currentLogicalSection < logicalIndices->count(); ++currentLogicalSection) { + itemPositions->resize(logicalIndices->size()); + for (int currentLogicalSection = 0; currentLogicalSection < logicalIndices->size(); ++currentLogicalSection) { const int headerSection = logicalIndices->at(currentLogicalSection); // determine the viewItemPosition depending on the position of column 0 - int nextLogicalSection = currentLogicalSection + 1 >= logicalIndices->count() + int nextLogicalSection = currentLogicalSection + 1 >= logicalIndices->size() ? logicalIndexAfterRight : logicalIndices->at(currentLogicalSection + 1); int prevLogicalSection = currentLogicalSection - 1 < 0 @@ -1659,8 +1688,11 @@ void QTreeView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, } } - // ### special case: treeviews with multiple columns draw - // the selections differently than with only one column + // ### special case: if we select entire rows, then we need to draw the + // selection in the first column all the way to the second column, rather + // than just around the item text. We abuse showDecorationSelected to + // indicate this to the style. Below we will reset this value temporarily + // to only respect the styleHint while we are rendering the decoration. opt.showDecorationSelected = (d->selectionBehavior & SelectRows) || option.showDecorationSelected; @@ -1676,7 +1708,7 @@ void QTreeView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, viewItemPosList; // vector of left/middle/end for each logicalIndex d->calcLogicalIndices(&logicalIndices, &viewItemPosList, left, right); - for (int currentLogicalSection = 0; currentLogicalSection < logicalIndices.count(); ++currentLogicalSection) { + for (int currentLogicalSection = 0; currentLogicalSection < logicalIndices.size(); ++currentLogicalSection) { int headerSection = logicalIndices.at(currentLogicalSection); position = columnViewportPosition(headerSection) + offset.x(); width = header->sectionSize(headerSection); @@ -1745,8 +1777,16 @@ void QTreeView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, } // draw background for the branch (selection + alternate row) opt.rect = branches; - if (style()->styleHint(QStyle::SH_ItemView_ShowDecorationSelected, &opt, this)) - style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, this); + + // We use showDecorationSelected both to store the style hint, and to indicate + // that the entire row has to be selected (see overrides of the value if + // selectionBehavior == SelectRow). + // While we are only painting the background we don't care for the + // selectionBehavior factor, so respect only the style value, and reset later. + const bool oldShowDecorationSelected = opt.showDecorationSelected; + opt.showDecorationSelected = style()->styleHint(QStyle::SH_ItemView_ShowDecorationSelected, + &opt, this); + style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, this); // draw background of the item (only alternate row). rest of the background // is provided by the delegate @@ -1755,6 +1795,7 @@ void QTreeView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, opt.rect.setRect(reverse ? position : i + position, y, width - i, height); style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, this); opt.state = oldState; + opt.showDecorationSelected = oldShowDecorationSelected; if (d->indent != 0) drawBranches(painter, branches, index); @@ -1961,13 +2002,13 @@ void QTreeView::mouseDoubleClickEvent(QMouseEvent *event) if (d->itemsExpandable && d->expandsOnDoubleClick && d->hasVisibleChildren(persistent)) { - if (!((i < d->viewItems.count()) && (d->viewItems.at(i).index == firstColumnIndex))) { + if (!((i < d->viewItems.size()) && (d->viewItems.at(i).index == firstColumnIndex))) { // find the new index of the item - for (i = 0; i < d->viewItems.count(); ++i) { + for (i = 0; i < d->viewItems.size(); ++i) { if (d->viewItems.at(i).index == firstColumnIndex) break; } - if (i == d->viewItems.count()) + if (i == d->viewItems.size()) return; } if (d->viewItems.at(i).expanded) @@ -2065,7 +2106,7 @@ QModelIndex QTreeView::indexBelow(const QModelIndex &index) const return QModelIndex(); d->executePostedLayout(); int i = d->viewIndex(index); - if (++i >= d->viewItems.count()) + if (++i >= d->viewItems.size()) return QModelIndex(); const QModelIndex firstColumnIndex = d->viewItems.at(i).index; return firstColumnIndex.sibling(firstColumnIndex.row(), index.column()); @@ -2104,6 +2145,7 @@ void QTreeView::doItemsLayout() } QAbstractItemView::doItemsLayout(); d->header->doItemsLayout(); + d->updateAccessibility(); } /*! @@ -2149,7 +2191,7 @@ int QTreeView::verticalOffset() const // ### find a faster way to do this d->executePostedLayout(); int offset = 0; - const int cnt = qMin(d->viewItems.count(), verticalScrollBar()->value()); + const int cnt = qMin(d->viewItems.size(), verticalScrollBar()->value()); for (int i = 0; i < cnt; ++i) offset += d->itemHeight(i); return offset; @@ -2175,14 +2217,13 @@ QModelIndex QTreeView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifie int c = 0; while (c < d->header->count() && d->header->isSectionHidden(d->header->logicalIndex(c))) ++c; - if (i < d->viewItems.count() && c < d->header->count()) { + if (i < d->viewItems.size() && c < d->header->count()) { return d->modelIndex(i, d->header->logicalIndex(c)); } return QModelIndex(); } - int vi = -1; - if (vi < 0) - vi = qMax(0, d->viewIndex(current)); + + const int vi = qMax(0, d->viewIndex(current)); if (isRightToLeft()) { if (cursorAction == MoveRight) @@ -2207,7 +2248,7 @@ QModelIndex QTreeView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifie return d->modelIndex(d->above(vi), current.column()); case MoveLeft: { QScrollBar *sb = horizontalScrollBar(); - if (vi < d->viewItems.count() && d->viewItems.at(vi).expanded && d->itemsExpandable && sb->value() == sb->minimum()) { + if (vi < d->viewItems.size() && d->viewItems.at(vi).expanded && d->itemsExpandable && sb->value() == sb->minimum()) { d->collapse(vi, true); d->moveCursorUpdatedView = true; } else { @@ -2242,7 +2283,7 @@ QModelIndex QTreeView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifie break; } case MoveRight: - if (vi < d->viewItems.count() && !d->viewItems.at(vi).expanded && d->itemsExpandable + if (vi < d->viewItems.size() && !d->viewItems.at(vi).expanded && d->itemsExpandable && d->hasVisibleChildren(d->viewItems.at(vi).index)) { d->expand(vi, true); d->moveCursorUpdatedView = true; @@ -2356,7 +2397,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)) { @@ -2367,7 +2408,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); @@ -2397,7 +2438,7 @@ QModelIndexList QTreeView::selectedIndexes() const QModelIndexList modelSelected; if (selectionModel()) modelSelected = selectionModel()->selectedIndexes(); - for (int i = 0; i < modelSelected.count(); ++i) { + for (int i = 0; i < modelSelected.size(); ++i) { // check that neither the parents nor the index is hidden before we add QModelIndex index = modelSelected.at(i); while (index.isValid() && !isIndexHidden(index)) @@ -2434,7 +2475,7 @@ void QTreeView::scrollContentsBy(int dx, int dy) // guestimate the number of items in the viewport int viewCount = d->viewport->height() / itemHeight; - int maxDeltaY = qMin(d->viewItems.count(), viewCount); + int maxDeltaY = qMin(d->viewItems.size(), viewCount); // no need to do a lot of work if we are going to redraw the whole thing anyway if (qAbs(dy) > qAbs(maxDeltaY) && d->editorIndexHash.isEmpty()) { verticalScrollBar()->update(); @@ -2450,12 +2491,12 @@ void QTreeView::scrollContentsBy(int dx, int dy) dy = 0; if (previousViewIndex < currentViewIndex) { // scrolling down for (int i = previousViewIndex; i < currentViewIndex; ++i) { - if (i < d->viewItems.count()) + if (i < d->viewItems.size()) dy -= d->itemHeight(i); } } else if (previousViewIndex > currentViewIndex) { // scrolling up for (int i = previousViewIndex - 1; i >= currentViewIndex; --i) { - if (i < d->viewItems.count()) + if (i < d->viewItems.size()) dy += d->itemHeight(i); } } @@ -2543,7 +2584,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); } /*! @@ -2634,7 +2675,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(); @@ -2667,6 +2709,7 @@ void QTreeView::expandAll() d->layout(-1, true); updateGeometries(); d->viewport->update(); + d->updateAccessibility(); } /*! @@ -2758,7 +2801,7 @@ void QTreeView::expandToDepth(int depth) d->expandedIndexes.clear(); d->interruptDelayedItemsLayout(); d->layout(-1); - for (int i = 0; i < d->viewItems.count(); ++i) { + for (int i = 0; i < d->viewItems.size(); ++i) { if (d->viewItems.at(i).level <= (uint)depth) { d->viewItems[i].expanded = true; d->layout(i); @@ -2790,6 +2833,7 @@ void QTreeView::expandToDepth(int depth) updateGeometries(); d->viewport->update(); + d->updateAccessibility(); } /*! @@ -3050,10 +3094,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); @@ -3086,13 +3145,14 @@ void QTreeViewPrivate::expand(int item, bool emitSignal) beginAnimatedOperation(); #endif // animation } + updateAccessibility(); } void QTreeViewPrivate::insertViewItems(int pos, int count, const QTreeViewItem &viewItem) { viewItems.insert(pos, count, viewItem); QTreeViewItem *items = viewItems.data(); - for (int i = pos + count; i < viewItems.count(); i++) + for (int i = pos + count; i < viewItems.size(); i++) if (items[i].parentItem >= pos) items[i].parentItem += count; } @@ -3101,7 +3161,7 @@ void QTreeViewPrivate::removeViewItems(int pos, int count) { viewItems.remove(pos, count); QTreeViewItem *items = viewItems.data(); - for (int i = pos; i < viewItems.count(); i++) + for (int i = pos; i < viewItems.size(); i++) if (items[i].parentItem >= pos) items[i].parentItem -= count; } @@ -3246,7 +3306,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)) @@ -3266,7 +3326,7 @@ QPixmap QTreeViewPrivate::renderTreeToPixmapForAnimation(const QRect &rect) cons return pixmap; } -void QTreeViewPrivate::_q_endAnimatedOperation() +void QTreeViewPrivate::endAnimatedOperation() { Q_Q(QTreeView); q->setState(stateBeforeAnimation); @@ -3275,25 +3335,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 @@ -3314,6 +3389,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)) { @@ -3345,7 +3429,7 @@ void QTreeViewPrivate::layout(int i, bool recursiveExpanding, bool afterIsUninit if (!afterIsUninitialized) insertViewItems(i + 1, count, QTreeViewItem()); // expand else if (count > 0) - viewItems.resize(viewItems.count() + count); + viewItems.resize(viewItems.size() + count); } else { expanding = false; } @@ -3415,7 +3499,7 @@ int QTreeViewPrivate::pageUp(int i) const index = 0; while (isItemHiddenOrDisabled(index)) index++; - return index >= viewItems.count() ? 0 : index; + return index >= viewItems.size() ? 0 : index; } int QTreeViewPrivate::pageDown(int i) const @@ -3423,11 +3507,11 @@ int QTreeViewPrivate::pageDown(int i) const int index = itemAtCoordinate(coordinateForItem(i) + viewport->height()); while (isItemHiddenOrDisabled(index)) index++; - if (index == -1 || index >= viewItems.count()) - index = viewItems.count() - 1; + if (index == -1 || index >= viewItems.size()) + index = viewItems.size() - 1; while (isItemHiddenOrDisabled(index)) index--; - return index == -1 ? viewItems.count() - 1 : index; + return index == -1 ? viewItems.size() - 1 : index; } int QTreeViewPrivate::itemForKeyHome() const @@ -3435,20 +3519,20 @@ int QTreeViewPrivate::itemForKeyHome() const int index = 0; while (isItemHiddenOrDisabled(index)) index++; - return index >= viewItems.count() ? 0 : index; + return index >= viewItems.size() ? 0 : index; } int QTreeViewPrivate::itemForKeyEnd() const { - int index = viewItems.count() - 1; + int index = viewItems.size() - 1; while (isItemHiddenOrDisabled(index)) index--; - return index == -1 ? viewItems.count() - 1 : index; + return index == -1 ? viewItems.size() - 1 : index; } int QTreeViewPrivate::indentationForItem(int item) const { - if (item < 0 || item >= viewItems.count()) + if (item < 0 || item >= viewItems.size()) return 0; int level = viewItems.at(item).level; if (rootDecoration) @@ -3458,7 +3542,7 @@ int QTreeViewPrivate::indentationForItem(int item) const int QTreeViewPrivate::itemHeight(int item) const { - Q_ASSERT(item < viewItems.count()); + Q_ASSERT(item < viewItems.size()); if (uniformRowHeights) return defaultItemHeight; if (viewItems.isEmpty()) @@ -3486,7 +3570,7 @@ int QTreeViewPrivate::coordinateForItem(int item) const return (item * defaultItemHeight) - vbar->value(); // ### optimize (maybe do like QHeaderView by letting items have startposition) int y = 0; - for (int i = 0; i < viewItems.count(); ++i) { + for (int i = 0; i < viewItems.size(); ++i) { if (i == item) return y - vbar->value(); y += itemHeight(i); @@ -3500,7 +3584,7 @@ int QTreeViewPrivate::coordinateForItem(int item) const // ### slow if the item is not visible int viewItemCoordinate = 0; int viewItemIndex = topViewItemIndex; - while (viewItemIndex < viewItems.count()) { + while (viewItemIndex < viewItems.size()) { if (viewItemIndex == item) return viewItemCoordinate; viewItemCoordinate += itemHeight(viewItemIndex); @@ -3532,7 +3616,7 @@ int QTreeViewPrivate::coordinateForItem(int item) const */ int QTreeViewPrivate::itemAtCoordinate(int coordinate) const { - const int itemCount = viewItems.count(); + const int itemCount = viewItems.size(); if (itemCount == 0) return -1; if (uniformRowHeights && defaultItemHeight <= 0) @@ -3545,7 +3629,7 @@ int QTreeViewPrivate::itemAtCoordinate(int coordinate) const // ### optimize int viewItemCoordinate = 0; const int contentsCoordinate = coordinate + vbar->value(); - for (int viewItemIndex = 0; viewItemIndex < viewItems.count(); ++viewItemIndex) { + for (int viewItemIndex = 0; viewItemIndex < viewItems.size(); ++viewItemIndex) { viewItemCoordinate += itemHeight(viewItemIndex); if (viewItemCoordinate > contentsCoordinate) return (viewItemIndex >= itemCount ? -1 : viewItemIndex); @@ -3561,7 +3645,7 @@ int QTreeViewPrivate::itemAtCoordinate(int coordinate) const if (coordinate >= 0) { // the coordinate is in or below the viewport int viewItemCoordinate = 0; - for (int viewItemIndex = topViewItemIndex; viewItemIndex < viewItems.count(); ++viewItemIndex) { + for (int viewItemIndex = topViewItemIndex; viewItemIndex < viewItems.size(); ++viewItemIndex) { viewItemCoordinate += itemHeight(viewItemIndex); if (viewItemCoordinate > coordinate) return (viewItemIndex >= itemCount ? -1 : viewItemIndex); @@ -3584,7 +3668,7 @@ int QTreeViewPrivate::viewIndex(const QModelIndex &_index) const if (!_index.isValid() || viewItems.isEmpty()) return -1; - const int totalCount = viewItems.count(); + const int totalCount = viewItems.size(); const QModelIndex index = _index.sibling(_index.row(), 0); const int row = index.row(); const quintptr internalId = index.internalId(); @@ -3625,7 +3709,7 @@ int QTreeViewPrivate::viewIndex(const QModelIndex &_index) const QModelIndex QTreeViewPrivate::modelIndex(int i, int column) const { - if (i < 0 || i >= viewItems.count()) + if (i < 0 || i >= viewItems.size()) return QModelIndex(); QModelIndex ret = viewItems.at(i).index; @@ -3640,7 +3724,7 @@ int QTreeViewPrivate::firstVisibleItem(int *offset) const if (verticalScrollMode == QAbstractItemView::ScrollPerItem) { if (offset) *offset = 0; - return (value < 0 || value >= viewItems.count()) ? -1 : value; + return (value < 0 || value >= viewItems.size()) ? -1 : value; } // ScrollMode == ScrollPerPixel if (uniformRowHeights) { @@ -3652,7 +3736,7 @@ int QTreeViewPrivate::firstVisibleItem(int *offset) const return value / defaultItemHeight; } int y = 0; // ### (maybe do like QHeaderView by letting items have startposition) - for (int i = 0; i < viewItems.count(); ++i) { + for (int i = 0; i < viewItems.size(); ++i) { y += itemHeight(i); // the height value is cached if (y > value) { if (offset) @@ -3673,7 +3757,7 @@ int QTreeViewPrivate::lastVisibleItem(int firstVisual, int offset) const int y = - offset; int value = viewport->height(); - for (int i = firstVisual; i < viewItems.count(); ++i) { + for (int i = firstVisual; i < viewItems.size(); ++i) { y += itemHeight(i); // the height value is cached if (y > value) return i; @@ -3701,11 +3785,11 @@ void QTreeViewPrivate::updateScrollBars() int itemsInViewport = 0; if (uniformRowHeights) { if (defaultItemHeight <= 0) - itemsInViewport = viewItems.count(); + itemsInViewport = viewItems.size(); else itemsInViewport = viewportSize.height() / defaultItemHeight; } else { - const int itemsCount = viewItems.count(); + const int itemsCount = viewItems.size(); const int viewportHeight = viewportSize.height(); for (int height = 0, item = itemsCount - 1; item >= 0; --item) { height += itemHeight(item); @@ -3717,15 +3801,15 @@ void QTreeViewPrivate::updateScrollBars() if (verticalScrollMode == QAbstractItemView::ScrollPerItem) { if (!viewItems.isEmpty()) itemsInViewport = qMax(1, itemsInViewport); - vbar->setRange(0, viewItems.count() - itemsInViewport); + vbar->setRange(0, viewItems.size() - itemsInViewport); vbar->setPageStep(itemsInViewport); vbar->setSingleStep(1); } else { // scroll per pixel int contentsHeight = 0; if (uniformRowHeights) { - contentsHeight = defaultItemHeight * viewItems.count(); + contentsHeight = defaultItemHeight * viewItems.size(); } else { // ### (maybe do like QHeaderView by letting items have startposition) - for (int i = 0; i < viewItems.count(); ++i) + for (int i = 0; i < viewItems.size(); ++i) contentsHeight += itemHeight(i); } vbar->setRange(0, contentsHeight - viewportSize.height()); @@ -3834,7 +3918,7 @@ QList<QPair<int, int>> QTreeViewPrivate::columnRanges(const QModelIndex &topInde QPair<int, int> current; current.first = -2; // -1 is not enough because -1+1 = 0 current.second = -2; - for(int i = 0; i < logicalIndexes.count(); ++i) { + for(int i = 0; i < logicalIndexes.size(); ++i) { const int logicalColumn = logicalIndexes.at(i); if (current.second + 1 != logicalColumn) { if (current.first != -2) { @@ -3910,7 +3994,7 @@ void QTreeViewPrivate::select(const QModelIndex &topIndex, const QModelIndex &bo } if (currentRange.isValid()) selection.append(currentRange); - for (int i = 0; i < rangeStack.count(); ++i) + for (int i = 0; i < rangeStack.size(); ++i) selection.append(rangeStack.at(i)); } q->selectionModel()->select(selection, command); @@ -3952,7 +4036,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); } @@ -3976,24 +4060,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)); } -#ifndef QT_NO_ACCESSIBILITY - if (QAccessible::isActive() && current.isValid()) { +#if QT_CONFIG(accessibility) + if (QAccessible::isActive() && current.isValid() && hasFocus()) { Q_D(QTreeView); QAccessibleEvent event(this, QAccessible::Focus); @@ -4010,7 +4087,7 @@ void QTreeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { QAbstractItemView::selectionChanged(selected, deselected); -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) if (QAccessible::isActive()) { Q_D(QTreeView); |