QQuickTreeView: add expandToIndex()

Add a function that lets you expand the tree recursively
from an index in the model, and all the way up to the root.

[ChangeLog][QtQuick] New function "expandToIndex()"
has been added to TreeView.

Change-Id: I063ef698e37f44730438e1638d3b7c1b4edaa0d0
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
This commit is contained in:
Richard Moe Gustavsen 2022-02-07 11:00:21 +01:00
parent 7eeeb605f0
commit c013439598
4 changed files with 131 additions and 0 deletions

View File

@ -175,6 +175,24 @@
\sa collapseRecursively(), expand(), collapse(), isExpanded(), depth()
*/
/*!
\qmlmethod QtQuick::TreeView::expandToIndex(QModelIndex index)
\since 6.4
Expands the tree from the given model \a index, and recursively all the way up
to the root. The result will be that the delegate item that represents \a index
becomes visible in the view (unless it ends up outside the viewport). To
ensure that the row ends up visible in the viewport, you can do:
\code
expandToIndex(index)
forceLayout()
positionViewAtRow(rowAtIndex(index), Qt.AlignVCenter)
\endcode
\sa expand(), expandRecursively()
*/
/*!
\qmlmethod QtQuick::TreeView::collapse(row)
@ -302,6 +320,8 @@
\a row and \a depth will be equal to the arguments given to the call
that caused the expansion to happen (\l expand() or \l expandRecursively()).
In case of \l expand(), \a depth will always be \c 1.
In case of \l expandToIndex(), \a depth will be the depth of the
target index.
\note when a row is expanded recursively, the expanded signal will
only be emitted for that one row, and not for its descendants.
@ -519,6 +539,55 @@ void QQuickTreeView::expandRecursively(int row, int depth)
emit expanded(row, depth);
}
void QQuickTreeView::expandToIndex(const QModelIndex &index)
{
Q_D(QQuickTreeView);
if (!index.isValid()) {
qmlWarning(this) << "index is not valid: " << index;
return;
}
if (index.model() != d->m_treeModelToTableModel.model()) {
qmlWarning(this) << "index doesn't belong to correct model: " << index;
return;
}
if (rowAtIndex(index) != -1) {
// index is already visible
return;
}
int depth = 1;
QModelIndex parent = index.parent();
int row = rowAtIndex(parent);
while (parent.isValid()) {
if (row != -1) {
// The node is already visible, since it maps to a row in the table!
d->m_treeModelToTableModel.expandRow(row);
// Update the state of the already existing delegate item
for (int c = leftColumn(); c <= rightColumn(); ++c) {
const QPoint treeNodeCell(c, row);
if (const auto item = itemAtCell(treeNodeCell))
d->setRequiredProperty("expanded", true, d->modelIndexAtCell(treeNodeCell), item, false);
}
// When we hit a node that is visible, we know that all other nodes
// up to the parent have to be visible as well, so we can stop.
break;
} else {
d->m_treeModelToTableModel.expand(parent);
parent = parent.parent();
row = rowAtIndex(parent);
depth++;
}
}
emit expanded(row, depth);
}
void QQuickTreeView::collapse(int row)
{
Q_D(QQuickTreeView);

View File

@ -77,6 +77,7 @@ public:
Q_REVISION(6, 4) Q_INVOKABLE void expandRecursively(int row = -1, int depth = -1);
Q_REVISION(6, 4) Q_INVOKABLE void collapseRecursively(int row = -1);
Q_REVISION(6, 4) Q_INVOKABLE void expandToIndex(const QModelIndex &index);
Q_INVOKABLE QModelIndex modelIndex(int row, int column) const;
Q_INVOKABLE QModelIndex modelIndex(const QPoint &cell) const;

View File

@ -103,6 +103,7 @@ private slots:
void collapseRecursivelyRoot();
void collapseRecursivelyChild();
void collapseRecursivelyWholeTree();
void expandToIndex();
void requiredPropertiesRoot();
void requiredPropertiesChildren();
void emptyModel();
@ -692,6 +693,45 @@ void tst_qquicktreeview::collapseRecursivelyWholeTree()
QCOMPARE(treeView->rows(), 1); // root
}
void tst_qquicktreeview::expandToIndex()
{
// Check that expandToIndex(index) expands the tree so
// that index becomes visible in the view
LOAD_TREEVIEW("normaltreeview.qml");
QSignalSpy spy(treeView, SIGNAL(expanded(int, int)));
const QModelIndex root = model->index(0, 0);
const QModelIndex child1 = model->index(3, 0, root);
const QModelIndex child2 = model->index(3, 0, child1);
QVERIFY(model->hasChildren(root));
QVERIFY(model->hasChildren(child1));
QVERIFY(model->hasChildren(child2));
QVERIFY(!treeView->isExpanded(treeView->rowAtIndex(root)));
QVERIFY(!treeView->isExpanded(treeView->rowAtIndex(child1)));
QVERIFY(!treeView->isExpanded(treeView->rowAtIndex(child2)));
const QModelIndex childToExpand = model->index(1, 0, child2);
treeView->expandToIndex(childToExpand);
QVERIFY(treeView->isExpanded(treeView->rowAtIndex(root)));
QVERIFY(treeView->isExpanded(treeView->rowAtIndex(child1)));
QVERIFY(treeView->isExpanded(treeView->rowAtIndex(child2)));
QCOMPARE(spy.count(), 1);
auto signalArgs = spy.takeFirst();
QVERIFY(signalArgs.at(0).toInt() == 0);
QVERIFY(signalArgs.at(1).toInt() == 3);
WAIT_UNTIL_POLISHED;
// The view should now have 13 rows:
// root + 3 expanded nodes that each have 4 children
QCOMPARE(treeView->rows(), 13);
}
QTEST_MAIN(tst_qquicktreeview)
#include "tst_qquicktreeview.moc"

View File

@ -48,6 +48,7 @@ ApplicationWindow {
visible: true
property alias treeView: treeView
property var selectedIndex: undefined
UICallback { id: callback }
@ -92,6 +93,16 @@ ApplicationWindow {
treeView.model.removeRows(index.row, 1, index.parent);
}
}
Button {
text: "Expand to"
enabled: selectedIndex != undefined
onClicked: {
treeView.expandToIndex(selectedIndex);
treeView.forceLayout()
let row = treeView.rowAtIndex(selectedIndex)
treeView.positionViewAtRow(row, Qt.AlignVCenter)
}
}
}
TreeView {
@ -126,6 +137,16 @@ ApplicationWindow {
treeView.expandRecursively(row)
}
}
TapHandler {
acceptedModifiers: Qt.ShiftModifier
onTapped: selectedIndex = treeView.modelIndex(row, 0)
}
Rectangle {
anchors.fill: parent
border.color: "red"
border.width: 1
visible: treeView.modelIndex(row, column) === selectedIndex
}
}
}