diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index ebbfc9ca83a1c3192ff882832796a9199dd914a9..a60743d3df6f2b6701afc6b1cd2cb31697f56390 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -1395,7 +1395,8 @@ public: ItemIsDropEnabled = 8, ItemIsUserCheckable = 16, ItemIsEnabled = 32, - ItemIsTristate = 64 + ItemIsTristate = 64, + ItemNeverHasChildren = 128 }; Q_DECLARE_FLAGS(ItemFlags, ItemFlag) diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc index f1571905914c79c9754286567c8f6726a070113d..b271620ee0a9a86e6b66904adcf44111657570dd 100644 --- a/src/corelib/global/qnamespace.qdoc +++ b/src/corelib/global/qnamespace.qdoc @@ -2441,6 +2441,7 @@ \value ItemIsUserCheckable It can be checked or unchecked by the user. \value ItemIsEnabled The user can interact with the item. \value ItemIsTristate The item is checkable with three separate states. + \value ItemNeverHasChildren The item never has child items. Note that checkable items need to be given both a suitable set of flags and an initial state, indicating whether the item is checked or not. diff --git a/src/corelib/itemmodels/qabstractitemmodel.cpp b/src/corelib/itemmodels/qabstractitemmodel.cpp index ebe38a97cdb955dd44ab101cc4e9286c2a5540b2..ad9be5419bf042871462cc1e7a0608ec28f491d8 100644 --- a/src/corelib/itemmodels/qabstractitemmodel.cpp +++ b/src/corelib/itemmodels/qabstractitemmodel.cpp @@ -3271,6 +3271,17 @@ bool QAbstractTableModel::hasChildren(const QModelIndex &parent) const return false; } +/*! + \reimp + */ +Qt::ItemFlags QAbstractTableModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags f = QAbstractItemModel::flags(index); + if (index.isValid()) + f |= Qt::ItemNeverHasChildren; + return f; +} + /*! \class QAbstractListModel \inmodule QtCore @@ -3391,6 +3402,17 @@ QModelIndex QAbstractListModel::parent(const QModelIndex & /* index */) const return QModelIndex(); } +/*! + \reimp + */ +Qt::ItemFlags QAbstractListModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags f = QAbstractItemModel::flags(index); + if (index.isValid()) + f |= Qt::ItemNeverHasChildren; + return f; +} + /*! \internal diff --git a/src/corelib/itemmodels/qabstractitemmodel.h b/src/corelib/itemmodels/qabstractitemmodel.h index f138f53487930c8f1538469eb41e8bcc2ccd3a31..8e4f12e9ea3a795bdf2c738fc4dbc43e43556a69 100644 --- a/src/corelib/itemmodels/qabstractitemmodel.h +++ b/src/corelib/itemmodels/qabstractitemmodel.h @@ -419,6 +419,7 @@ public: bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; protected: QAbstractTableModel(QAbstractItemModelPrivate &dd, QObject *parent); @@ -439,6 +440,8 @@ public: QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + + Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; protected: QAbstractListModel(QAbstractItemModelPrivate &dd, QObject *parent); diff --git a/src/widgets/dialogs/qfilesystemmodel.cpp b/src/widgets/dialogs/qfilesystemmodel.cpp index 4d3c7a24ec2be191648083522ef4ab79ca1ae11b..251af273b9bda6119705e08e5c81d9f645a7efc4 100644 --- a/src/widgets/dialogs/qfilesystemmodel.cpp +++ b/src/widgets/dialogs/qfilesystemmodel.cpp @@ -964,6 +964,8 @@ Qt::ItemFlags QFileSystemModel::flags(const QModelIndex &index) const flags |= Qt::ItemIsEditable; if (indexNode->isDir()) flags |= Qt::ItemIsDropEnabled; + else + flags |= Qt::ItemNeverHasChildren; } return flags; } diff --git a/src/widgets/itemviews/qtreeview.cpp b/src/widgets/itemviews/qtreeview.cpp index 1f922dd6e390265dd6bdad90e536c87f03ed47a7..cee47faab4ca951df650da3901731150ded95277 100644 --- a/src/widgets/itemviews/qtreeview.cpp +++ b/src/widgets/itemviews/qtreeview.cpp @@ -749,6 +749,8 @@ void QTreeView::expand(const QModelIndex &index) Q_D(QTreeView); if (!d->isIndexValid(index)) return; + if (index.flags() & Qt::ItemNeverHasChildren) + return; if (d->delayedPendingLayout) { //A complete relayout is going to be performed, just store the expanded index, no need to layout. if (d->storeExpanded(index)) @@ -2887,6 +2889,9 @@ void QTreeViewPrivate::expand(int item, bool emitSignal) if (item == -1 || viewItems.at(item).expanded) return; + const QModelIndex index = viewItems.at(item).index; + if (index.flags() & Qt::ItemNeverHasChildren) + return; #ifndef QT_NO_ANIMATION if (emitSignal && animationsEnabled) @@ -2896,7 +2901,6 @@ void QTreeViewPrivate::expand(int item, bool emitSignal) if (state != QAbstractItemView::AnimatingState) stateBeforeAnimation = state; q->setState(QAbstractItemView::ExpandingState); - const QModelIndex index = viewItems.at(item).index; storeExpanded(index); viewItems[item].expanded = true; layout(item); @@ -3189,7 +3193,7 @@ void QTreeViewPrivate::layout(int i, bool recursiveExpanding, bool afterIsUninit item->expanded = false; item->total = 0; item->hasMoreSiblings = false; - if (recursiveExpanding || isIndexExpanded(current)) { + if ((recursiveExpanding && !(current.flags() & Qt::ItemNeverHasChildren)) || isIndexExpanded(current)) { if (recursiveExpanding) expandedIndexes.insert(current); item->expanded = true; diff --git a/src/widgets/itemviews/qtreeview_p.h b/src/widgets/itemviews/qtreeview_p.h index 5a0821c9cf0d255d73d317f697e2b1e0c8fb2dbc..6778446ed3497376b0648172925929adb4e070a7 100644 --- a/src/widgets/itemviews/qtreeview_p.h +++ b/src/widgets/itemviews/qtreeview_p.h @@ -203,7 +203,7 @@ public: inline bool isIndexExpanded(const QModelIndex &idx) const { //We first check if the idx is a QPersistentModelIndex, because creating QPersistentModelIndex is slow - return isPersistent(idx) && expandedIndexes.contains(idx); + return !(idx.flags() & Qt::ItemNeverHasChildren) && isPersistent(idx) && expandedIndexes.contains(idx); } // used when hiding and showing items diff --git a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp index 0f69e951bfbdc561dc02abc5cd1d0be5cb8bf78b..a1de5f190c35ba36f4eac690c81f2a22e706d39a 100644 --- a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp +++ b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp @@ -177,6 +177,7 @@ private slots: void emptyModel(); void removeRows(); void removeCols(); + void limitedExpand(); void expandAndCollapse_data(); void expandAndCollapse(); void expandAndCollapseAll(); @@ -1414,6 +1415,45 @@ void tst_QTreeView::removeCols() QCOMPARE(view.header()->count(), model.cols); } +void tst_QTreeView::limitedExpand() +{ + { + QStandardItemModel model; + QStandardItem *parentItem = model.invisibleRootItem(); + parentItem->appendRow(new QStandardItem); + parentItem->appendRow(new QStandardItem); + parentItem->appendRow(new QStandardItem); + + QStandardItem *firstItem = model.item(0, 0); + firstItem->setFlags(firstItem->flags() | Qt::ItemNeverHasChildren); + + QTreeView view; + view.setModel(&model); + + QSignalSpy spy(&view, SIGNAL(expanded(QModelIndex))); + QVERIFY(spy.isValid()); + + view.expand(model.index(0, 0)); + QCOMPARE(spy.count(), 0); + + view.expand(model.index(1, 0)); + QCOMPARE(spy.count(), 1); + } + { + QStringListModel model(QStringList() << "one" << "two"); + QTreeView view; + view.setModel(&model); + + QSignalSpy spy(&view, SIGNAL(expanded(QModelIndex))); + QVERIFY(spy.isValid()); + + view.expand(model.index(0, 0)); + QCOMPARE(spy.count(), 0); + view.expandAll(); + QCOMPARE(spy.count(), 0); + } +} + void tst_QTreeView::expandAndCollapse_data() { QTest::addColumn<bool>("animationEnabled");