summaryrefslogtreecommitdiffstats
path: root/src/widgets/itemviews/qtreeview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets/itemviews/qtreeview.cpp')
-rw-r--r--src/widgets/itemviews/qtreeview.cpp456
1 files changed, 254 insertions, 202 deletions
diff --git a/src/widgets/itemviews/qtreeview.cpp b/src/widgets/itemviews/qtreeview.cpp
index c0b0ee4f2d..744f29ca17 100644
--- a/src/widgets/itemviews/qtreeview.cpp
+++ b/src/widgets/itemviews/qtreeview.cpp
@@ -1,45 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtWidgets module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qtreeview.h"
#include <qheaderview.h>
-#include <qitemdelegate.h>
+#include <qabstractitemdelegate.h>
#include <qapplication.h>
#include <qscrollbar.h>
#include <qpainter.h>
@@ -51,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
@@ -160,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
*/
@@ -206,6 +169,8 @@ QTreeView::QTreeView(QTreeViewPrivate &dd, QWidget *parent)
*/
QTreeView::~QTreeView()
{
+ Q_D(QTreeView);
+ d->clearConnections();
}
/*!
@@ -217,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();
@@ -238,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());
}
/*!
@@ -273,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);
@@ -282,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);
}
}
@@ -323,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();
@@ -876,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;
}
@@ -1038,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;
}
@@ -1065,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)
@@ -1073,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);
@@ -1104,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);
+}
+
+/*!
+ \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 (!d->isIndexValid(index) || isIndexHidden(index))
+ 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);
}
/*!
@@ -1313,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:
@@ -1405,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;
@@ -1425,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);
@@ -1486,14 +1478,15 @@ void QTreeViewPrivate::adjustViewOptionsForIndex(QStyleOptionViewItem *option, c
void QTreeView::drawTree(QPainter *painter, const QRegion &region) const
{
Q_D(const QTreeView);
- const QList<QTreeViewItem> viewItems = d->viewItems;
+ // d->viewItems changes when posted layouts are executed in itemDecorationAt, so don't copy
+ const QList<QTreeViewItem> &viewItems = d->viewItems;
QStyleOptionViewItem option;
initViewItemOption(&option);
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;
}
@@ -1522,7 +1515,7 @@ void QTreeView::drawTree(QPainter *painter, const QRegion &region) 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;
@@ -1530,9 +1523,10 @@ void QTreeView::drawTree(QPainter *painter, const QRegion &region) 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);
@@ -1591,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
@@ -1694,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;
@@ -1711,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);
@@ -1768,7 +1765,7 @@ void QTreeView::drawRow(QPainter *painter, const QStyleOptionViewItem &option,
/* Prior to Qt 4.3, the background of the branch (in selected state and
alternate row color was provided by the view. For backward compatibility,
- this is now delegated to the style using PE_PanelViewItemRow which
+ this is now delegated to the style using PE_PanelItemViewRow which
does the appropriate fill */
if (d->isTreePosition(headerSection)) {
const int i = d->indentationForItem(d->current);
@@ -1780,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
@@ -1790,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);
@@ -1991,18 +1997,18 @@ void QTreeView::mouseDoubleClickEvent(QMouseEvent *event)
if (!style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, this))
emit activated(persistent);
- d->pressedIndex = QModelIndex();
+ d->releaseFromDoubleClick = true;
d->executePostedLayout(); // we need to make sure viewItems is updated
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)
@@ -2100,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());
@@ -2139,6 +2145,7 @@ void QTreeView::doItemsLayout()
}
QAbstractItemView::doItemsLayout();
d->header->doItemsLayout();
+ d->updateAccessibility();
}
/*!
@@ -2184,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;
@@ -2210,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)
@@ -2242,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 {
@@ -2277,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;
@@ -2391,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)) {
@@ -2402,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);
@@ -2432,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))
@@ -2469,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();
@@ -2485,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);
}
}
@@ -2578,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);
}
/*!
@@ -2669,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();
@@ -2686,7 +2693,10 @@ QSize QTreeView::viewportSizeHint() const
\since 4.2
Expands all expandable items.
- \warning: if the model contains a large number of items,
+ \note This function will not try to \l{QAbstractItemModel::fetchMore}{fetch more}
+ data.
+
+ \warning If the model contains a large number of items,
this function will take some time to execute.
\sa collapseAll(), expand(), collapse(), setExpanded()
@@ -2699,6 +2709,7 @@ void QTreeView::expandAll()
d->layout(-1, true);
updateGeometries();
d->viewport->update();
+ d->updateAccessibility();
}
/*!
@@ -2708,7 +2719,10 @@ void QTreeView::expandAll()
A \a depth of -1 will expand all children, a \a depth of 0 will
only expand the given \a index.
- \warning: if the model contains a large number of items,
+ \note This function will not try to \l{QAbstractItemModel::fetchMore}{fetch more}
+ data.
+
+ \warning If the model contains a large number of items,
this function will take some time to execute.
\sa expandAll()
@@ -2773,6 +2787,9 @@ void QTreeView::collapseAll()
\since 4.3
Expands all expandable items to the given \a depth.
+ \note This function will not try to \l{QAbstractItemModel::fetchMore}{fetch more}
+ data.
+
\sa expandAll(), collapseAll(), expand(), collapse(), setExpanded()
*/
void QTreeView::expandToDepth(int depth)
@@ -2784,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);
@@ -2816,6 +2833,7 @@ void QTreeView::expandToDepth(int depth)
updateGeometries();
d->viewport->update();
+ d->updateAccessibility();
}
/*!
@@ -3076,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);
@@ -3112,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;
}
@@ -3127,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;
}
@@ -3272,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))
@@ -3292,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);
@@ -3301,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
@@ -3340,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)) {
@@ -3371,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;
}
@@ -3441,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
@@ -3449,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
@@ -3461,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)
@@ -3484,6 +3542,7 @@ int QTreeViewPrivate::indentationForItem(int item) const
int QTreeViewPrivate::itemHeight(int item) const
{
+ Q_ASSERT(item < viewItems.size());
if (uniformRowHeights)
return defaultItemHeight;
if (viewItems.isEmpty())
@@ -3511,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);
@@ -3525,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);
@@ -3557,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)
@@ -3570,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);
@@ -3586,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);
@@ -3609,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();
@@ -3650,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;
@@ -3665,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) {
@@ -3677,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)
@@ -3698,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;
@@ -3726,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);
@@ -3742,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());
@@ -3859,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) {
@@ -3935,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);
@@ -3977,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);
}
@@ -4001,24 +4060,17 @@ void QTreeViewPrivate::updateIndentationFromStyle()
*/
void QTreeView::currentChanged(const QModelIndex &current, 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);
@@ -4035,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);