From 09d9ce27ab9ac29aaa36c6d54fe89064dfcde69f Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Tue, 21 Jul 2015 17:07:14 +0200 Subject: TreeView: Add rootIndex property Its purpose is the same as QAbstractItemView::rootIndex and allows to display only the part of the model data that is descendant of this index. The filesystembrowser example has been updated to only show files reachable from the user's home directory. [ChangeLog][TreeView] Added rootIndex property Change-Id: Ib8d9af4ce9d1f341ab509de3cc991773830ba9f4 Reviewed-by: Friedemann Kleint Reviewed-by: Mitch Curtis --- examples/quick/controls/filesystembrowser/main.cpp | 1 + examples/quick/controls/filesystembrowser/main.qml | 6 ++- src/controls/Private/qquicktreemodeladaptor.cpp | 52 ++++++++++++++++------ src/controls/Private/qquicktreemodeladaptor_p.h | 6 +++ src/controls/TreeView.qml | 1 + src/controls/doc/src/qtquickcontrols-treeview.qdoc | 13 ++++++ src/controls/plugin.cpp | 3 +- .../tst_qquicktreemodeladaptor.cpp | 47 ++++++++++++++++--- 8 files changed, 107 insertions(+), 22 deletions(-) diff --git a/examples/quick/controls/filesystembrowser/main.cpp b/examples/quick/controls/filesystembrowser/main.cpp index 176d334c..1322bc49 100644 --- a/examples/quick/controls/filesystembrowser/main.cpp +++ b/examples/quick/controls/filesystembrowser/main.cpp @@ -137,6 +137,7 @@ int main(int argc, char *argv[]) fsm->setRootPath(QDir::homePath()); fsm->setResolveSymlinks(true); engine.rootContext()->setContextProperty("fileSystemModel", fsm); + engine.rootContext()->setContextProperty("rootPathIndex", fsm->index(fsm->rootPath())); engine.load(QUrl(QStringLiteral("qrc:///main.qml"))); return app.exec(); diff --git a/examples/quick/controls/filesystembrowser/main.qml b/examples/quick/controls/filesystembrowser/main.qml index abc3c20c..b85002d5 100644 --- a/examples/quick/controls/filesystembrowser/main.qml +++ b/examples/quick/controls/filesystembrowser/main.qml @@ -39,7 +39,7 @@ ****************************************************************************/ import QtQuick 2.2 -import QtQuick.Controls 1.4 +import QtQuick.Controls 1.5 import QtQml.Models 2.2 ApplicationWindow { @@ -90,6 +90,7 @@ ApplicationWindow { anchors.fill: parent anchors.margins: 2 * 12 + row.height model: fileSystemModel + rootIndex: rootPathIndex selection: sel TableViewColumn { @@ -103,12 +104,14 @@ ApplicationWindow { role: "size" resizable: true horizontalAlignment : Text.AlignRight + width: 70 } TableViewColumn { title: "Permissions" role: "displayableFilePermissions" resizable: true + width: 100 } TableViewColumn { @@ -117,7 +120,6 @@ ApplicationWindow { resizable: true } - onDoubleClicked: isExpanded(index) ? collapse(index) : expand(index) onActivated : Qt.openUrlExternally(fileSystemModel.data(index, 263)) } } diff --git a/src/controls/Private/qquicktreemodeladaptor.cpp b/src/controls/Private/qquicktreemodeladaptor.cpp index dddcdd01..0fda5704 100644 --- a/src/controls/Private/qquicktreemodeladaptor.cpp +++ b/src/controls/Private/qquicktreemodeladaptor.cpp @@ -120,6 +120,29 @@ void QQuickTreeModelAdaptor::clearModelData() endResetModel(); } +const QModelIndex &QQuickTreeModelAdaptor::rootIndex() const +{ + return m_rootIndex; +} + +void QQuickTreeModelAdaptor::setRootIndex(const QModelIndex &idx) +{ + if (m_rootIndex == idx) + return; + + if (m_model) + clearModelData(); + m_rootIndex = idx; + if (m_model) + showModelTopLevelItems(); + emit rootIndexChanged(); +} + +void QQuickTreeModelAdaptor::resetRootIndex() +{ + setRootIndex(QModelIndex()); +} + QHash QQuickTreeModelAdaptor::roleNames() const { if (!m_model) @@ -180,7 +203,7 @@ bool QQuickTreeModelAdaptor::setData(const QModelIndex &index, const QVariant &v int QQuickTreeModelAdaptor::itemIndex(const QModelIndex &index) const { // This is basically a plagiarism of QTreeViewPrivate::viewIndex() - if (!index.isValid() || m_items.isEmpty()) + if (!index.isValid() || index == m_rootIndex || m_items.isEmpty()) return -1; const int totalCount = m_items.count(); @@ -226,7 +249,7 @@ bool QQuickTreeModelAdaptor::isVisible(const QModelIndex &index) bool QQuickTreeModelAdaptor::childrenVisible(const QModelIndex &index) { - return (!index.isValid() && !m_items.isEmpty()) + return (index == m_rootIndex && !m_items.isEmpty()) || (m_expandedItems.contains(index) && isVisible(index)); } @@ -302,21 +325,21 @@ void QQuickTreeModelAdaptor::showModelTopLevelItems(bool doInsertRows) if (!m_model) return; - if (m_model->hasChildren(QModelIndex()) && m_model->canFetchMore(QModelIndex())) - m_model->fetchMore(QModelIndex()); - const long topLevelRowCount = m_model->rowCount(); + if (m_model->hasChildren(m_rootIndex) && m_model->canFetchMore(m_rootIndex)) + m_model->fetchMore(m_rootIndex); + const long topLevelRowCount = m_model->rowCount(m_rootIndex); if (topLevelRowCount == 0) return; - showModelChildItems(TreeItem(), 0, topLevelRowCount - 1, doInsertRows); + showModelChildItems(TreeItem(m_rootIndex), 0, topLevelRowCount - 1, doInsertRows); } void QQuickTreeModelAdaptor::showModelChildItems(const TreeItem &parentItem, int start, int end, bool doInsertRows, bool doExpandPendingRows) { const QModelIndex &parentIndex = parentItem.index; - int rowIdx = parentIndex.isValid() ? itemIndex(parentIndex) + 1 : 0; + int rowIdx = parentIndex.isValid() && parentIndex != m_rootIndex ? itemIndex(parentIndex) + 1 : 0; Q_ASSERT(rowIdx == 0 || parentItem.expanded); - if (parentIndex.isValid() && (rowIdx == 0 || !parentItem.expanded)) + if (parentIndex.isValid() && parentIndex != m_rootIndex && (rowIdx == 0 || !parentItem.expanded)) return; if (m_model->rowCount(parentIndex) == 0) { @@ -603,8 +626,11 @@ void QQuickTreeModelAdaptor::modelRowsInserted(const QModelIndex & parent, int s ASSERT_CONSISTENCY(); return; } - } else if (parent.isValid()) { + } else if (parent == m_rootIndex) { item = TreeItem(parent); + } else { + ASSERT_CONSISTENCY(); + return; } showModelChildItems(item, start, end); ASSERT_CONSISTENCY(); @@ -612,10 +638,8 @@ void QQuickTreeModelAdaptor::modelRowsInserted(const QModelIndex & parent, int s void QQuickTreeModelAdaptor::modelRowsAboutToBeRemoved(const QModelIndex & parent, int start, int end) { - Q_UNUSED(start); - Q_UNUSED(end); ASSERT_CONSISTENCY(); - if (!parent.isValid() || childrenVisible(parent)) { + if (parent == m_rootIndex || childrenVisible(parent)) { const QModelIndex &smi = m_model->index(start, 0, parent); int startIndex = itemIndex(smi); const QModelIndex &emi = m_model->index(end, 0, parent); @@ -756,9 +780,9 @@ bool QQuickTreeModelAdaptor::testConsistency(bool dumpOnFail) const } return true; } - QModelIndex parent; + QModelIndex parent = m_rootIndex; QStack ancestors; - QModelIndex idx = m_model->index(0, 0); + QModelIndex idx = m_model->index(0, 0, parent); for (int i = 0; i < m_items.count(); i++) { bool isConsistent = true; const TreeItem &item = m_items.at(i); diff --git a/src/controls/Private/qquicktreemodeladaptor_p.h b/src/controls/Private/qquicktreemodeladaptor_p.h index 2297c365..3eefbe77 100644 --- a/src/controls/Private/qquicktreemodeladaptor_p.h +++ b/src/controls/Private/qquicktreemodeladaptor_p.h @@ -61,6 +61,7 @@ class QQuickTreeModelAdaptor : public QAbstractListModel { Q_OBJECT Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QModelIndex rootIndex READ rootIndex WRITE setRootIndex RESET resetRootIndex NOTIFY rootIndexChanged) struct TreeItem; @@ -68,6 +69,9 @@ public: explicit QQuickTreeModelAdaptor(QObject *parent = 0); QAbstractItemModel *model() const; + const QModelIndex &rootIndex() const; + void setRootIndex(const QModelIndex &idx); + void resetRootIndex(); enum { DepthRole = Qt::UserRole - 4, @@ -110,6 +114,7 @@ public: signals: void modelChanged(QAbstractItemModel *model); + void rootIndexChanged(); void expanded(const QModelIndex &index); void collapsed(const QModelIndex &index); @@ -149,6 +154,7 @@ private: }; QPointer m_model; + QPersistentModelIndex m_rootIndex; QList m_items; QSet m_expandedItems; QList m_itemsToExpand; diff --git a/src/controls/TreeView.qml b/src/controls/TreeView.qml index c97930f3..637c46c3 100644 --- a/src/controls/TreeView.qml +++ b/src/controls/TreeView.qml @@ -44,6 +44,7 @@ BasicTableView { id: root property var model: null + property alias rootIndex: modelAdaptor.rootIndex readonly property var currentIndex: modelAdaptor.mapRowToModelIndex(__currentRow) property ItemSelectionModel selection: null diff --git a/src/controls/doc/src/qtquickcontrols-treeview.qdoc b/src/controls/doc/src/qtquickcontrols-treeview.qdoc index fb186059..a9d41b70 100644 --- a/src/controls/doc/src/qtquickcontrols-treeview.qdoc +++ b/src/controls/doc/src/qtquickcontrols-treeview.qdoc @@ -141,6 +141,19 @@ The TreeView accept models derived from the QAbstractItemModel class. */ +/*! + \qmlproperty QModelIndex TreeView::rootIndex + The model index of the root item in the tree view. The root item is the + parent item to the view's top-level items. Only items descending from the + root item will be visible in the view. + + Its default value is an invalid QModelIndex, which means the whole + model data is shown by the tree view (assigning \c undefined to this + proprety resets it to its default value.) + + \since QtQuick.Controls 1.5 +*/ + /*! \qmlproperty QModelIndex TreeView::currentIndex The model index of the current row in the tree view. diff --git a/src/controls/plugin.cpp b/src/controls/plugin.cpp index f20cada1..802dacb0 100644 --- a/src/controls/plugin.cpp +++ b/src/controls/plugin.cpp @@ -113,7 +113,8 @@ static const struct { { "TreeView", 1, 4 }, - { "TextArea", 1, 5 } + { "TextArea", 1, 5 }, + { "TreeView", 1, 5 } }; void QtQuickControlsPlugin::registerTypes(const char *uri) diff --git a/tests/auto/qquicktreemodeladaptor/tst_qquicktreemodeladaptor.cpp b/tests/auto/qquicktreemodeladaptor/tst_qquicktreemodeladaptor.cpp index b484665d..13a92ea7 100644 --- a/tests/auto/qquicktreemodeladaptor/tst_qquicktreemodeladaptor.cpp +++ b/tests/auto/qquicktreemodeladaptor/tst_qquicktreemodeladaptor.cpp @@ -57,6 +57,8 @@ private slots: void modelDestroyed(); void modelReset(); + void rootIndex(); + void dataAccess(); void dataChange(); void groupedDataChange(); @@ -96,9 +98,10 @@ void tst_QQuickTreeModelAdaptor::cleanup() void tst_QQuickTreeModelAdaptor::compareData(int row, QQuickTreeModelAdaptor &tma, const QModelIndex &modelIdx, TestModel &model, bool expanded) { const QModelIndex &tmaIdx = tma.index(row); + const int indexDepth = model.level(modelIdx) - model.level(tma.rootIndex()) - 1; QCOMPARE(tma.mapToModel(tmaIdx), modelIdx); QCOMPARE(tma.data(tmaIdx, Qt::DisplayRole).toString(), model.displayData(modelIdx)); - QCOMPARE(tma.data(tmaIdx, QQuickTreeModelAdaptor::DepthRole).toInt(), model.level(modelIdx)); + QCOMPARE(tma.data(tmaIdx, QQuickTreeModelAdaptor::DepthRole).toInt(), indexDepth); QCOMPARE(tma.data(tmaIdx, QQuickTreeModelAdaptor::ExpandedRole).toBool(), expanded); QCOMPARE(tma.data(tmaIdx, QQuickTreeModelAdaptor::HasChildrenRole).toBool(), model.hasChildren(modelIdx)); } @@ -117,7 +120,7 @@ void tst_QQuickTreeModelAdaptor::expandAndTest(const QModelIndex &idx, QQuickTre const QModelIndex &tmaIdx = tma.index(tma.itemIndex(idx)); QCOMPARE(tma.data(tmaIdx, QQuickTreeModelAdaptor::ExpandedRole).toBool(), expandable); - if (expandable) { + if (expandable && expectedRowCountDifference != 0) { // Rows were added below the parent QCOMPARE(tma.rowCount(), oldRowCount + expectedRowCountDifference); QCOMPARE(rowsAboutToBeInsertedSpy.count(), rowsInsertedSpy.count()); @@ -160,7 +163,7 @@ void tst_QQuickTreeModelAdaptor::collapseAndTest(const QModelIndex &idx, QQuickT if (tmaIdx.isValid()) QCOMPARE(tma.data(tmaIdx, QQuickTreeModelAdaptor::ExpandedRole).toBool(), false); - if (expandable) { + if (expandable && expectedRowCountDifference != 0) { // Rows were removed below the parent QCOMPARE(tma.rowCount(), oldRowCount - expectedRowCountDifference); QCOMPARE(rowsAboutToBeRemovedSpy.count(), 1); @@ -188,9 +191,9 @@ void tst_QQuickTreeModelAdaptor::collapseAndTest(const QModelIndex &idx, QQuickT void tst_QQuickTreeModelAdaptor::compareModels(QQuickTreeModelAdaptor &tma, TestModel &model) { - QModelIndex parent; + QModelIndex parent = tma.rootIndex(); QStack parents; - QModelIndex idx = model.index(0, 0); + QModelIndex idx = model.index(0, 0, parent); int modelVisibleRows = model.rowCount(parent); for (int i = 0; i < tma.rowCount(); i++) { bool expanded = tma.isExpanded(i); @@ -283,6 +286,40 @@ void tst_QQuickTreeModelAdaptor::modelReset() compareModels(tma, model); } +void tst_QQuickTreeModelAdaptor::rootIndex() +{ + TestModel model(5, 1); + + QQuickTreeModelAdaptor tma; + tma.setModel(&model); + + QVERIFY(!tma.rootIndex().isValid()); + compareModels(tma, model); + + QSignalSpy rootIndexSpy(&tma, SIGNAL(rootIndexChanged())); + QModelIndex rootIndex = model.index(0, 0); + tma.setRootIndex(rootIndex); + QCOMPARE(tma.rootIndex(), rootIndex); + QCOMPARE(rootIndexSpy.count(), 1); + compareModels(tma, model); + + rootIndexSpy.clear(); + rootIndex = model.index(2, 2, tma.rootIndex()); + tma.setRootIndex(rootIndex); + QCOMPARE(tma.rootIndex(), rootIndex); + QCOMPARE(rootIndexSpy.count(), 1); + compareModels(tma, model); + + // Expand 1st visible item, business as usual + expandAndTest(model.index(0, 0, rootIndex), tma, true /*expandable*/, 5); + // Expand non root item descendant item, nothing should happen + expandAndTest(model.index(0, 0), tma, true /*expandable*/, 0); + // Collapse 1st visible item, business as usual + collapseAndTest(model.index(0, 0, rootIndex), tma, true /*expandable*/, 5); + // Collapse non root item descendant item, nothing should happen + collapseAndTest(model.index(0, 0), tma, true /*expandable*/, 0); +} + void tst_QQuickTreeModelAdaptor::dataAccess() { TestModel model(5, 1); -- cgit v1.2.1