Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquicktreeview.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include <QtCore/qobject.h>
8#include <QtQml/qqmlcontext.h>
9#include <QtQuick/private/qquicktaphandler_p.h>
10
11#include <QtQmlModels/private/qqmltreemodeltotablemodel_p_p.h>
12
13/*!
14 \qmltype TreeView
15 \inqmlmodule QtQuick
16 \ingroup qtquick-views
17 \since 6.3
18 \inherits TableView
19 \brief Provides a tree view to display data from a QAbstractItemModel.
20
21 A TreeView has a \l model that defines the data to be displayed, and a
22 \l delegate that defines how the data should be displayed.
23
24 TreeView inherits \l TableView. This means that, despite the model
25 has a parent-child tree structure, TreeView is internally using a
26 proxy model that converts that structure into a flat table
27 model that can be rendered by TableView. Each node in the tree ends up
28 occupying one row in the table, where the first column renders the tree
29 itself. By indenting each delegate item in that column according to its
30 parent-child depth in the model, it will end up looking like a tree, even
31 if it's technically still just a flat list of items.
32
33 \section2 Declare a TreeView
34
35 TreeView is a data bound control, so it cannot show anything without a
36 data model. You cannot declare tree nodes in QML.
37
38 When you declare a TreeView, you need to specify:
39
40 \list
41 \li \b{A data model}. TreeView can work with data models that derive from
42 \l QAbstractItemModel.
43 \li \b{A delegate}. A delegate is a template that specifies how the tree
44 nodes are displayed in the UI.
45 \endlist
46
47 \qml
48 TreeView {
49 // The model needs to be a QAbstractItemModel
50 model: myTreeModel
51 // You can set a custom delegate or use a built-in TreeViewDelegate
52 delegate: TreeViewDelegate {}
53 }
54 \endqml
55
56 \section2 Creating a data model
57
58 A TreeView only accepts a model that inherits \l QAbstractItemModel.
59
60 For information on how to create and use a custom tree model, see the
61 example: \l {Qt Quick Controls - Table of Contents}.
62
63 \section2 Customize tree nodes
64
65 For better flexibility, TreeView itself doesn't position the delegate items
66 into a tree structure. This burden is placed on the delegate.
67 \l {Qt Quick Controls} offers a ready-made \l TreeViewDelegate that can be
68 used for this, which has the advantage that it works out-of-the-box and
69 renders a tree which follows the style of the platform where the application
70 runs.
71
72 Even though \l TreeViewDelegate is customizable, there might be situations
73 where you want to render the tree in a different way, or ensure that
74 the delegate ends up as minimal as possible, perhaps for performance reasons.
75 Creating your own delegate from scratch is easy, since TreeView offers
76 a set of properties that can be used to position and render each node
77 in the tree correctly.
78
79 An example of a custom delegate with an animating indicator is shown below:
80
81 \snippet qml/treeview/qml-customdelegate.qml 0
82
83
84 The properties that are marked as \c required will be filled in by
85 TreeView, and are similar to attached properties. By marking them as
86 required, the delegate indirectly informs TreeView that it should take
87 responsibility for assigning them values. The following required properties
88 can be added to a delegate:
89
90 \list
91 \li \c {required property TreeView treeView}
92 - Points to the TreeView that contains the delegate item.
93 \li \c {required property bool isTreeNode}
94 - Is \c true if the delegate item represents a node in
95 the tree. Only one column in the view will be used to draw the tree, and
96 therefore, only delegate items in that column will have this
97 property set to \c true.
98 A node in the tree should typically be indented according to its
99 \c depth, and show an indicator if \c hasChildren is \c true.
100 Delegate items in other columns will have this property set to
101 \c false, and will show data from the remaining columns
102 in the model (and typically not be indented).
103 \li \c {required property bool expanded}
104 - Is \c true if the model item drawn by the delegate is expanded
105 in the view.
106 \li \c {required property bool hasChildren}
107 - Is \c true if the model item drawn by the delegate has children
108 in the model.
109 \li \c {required property int depth}
110 - Contains the depth of the model item drawn by the delegate.
111 The depth of a model item is the same as the number of ancestors
112 it has in the model.
113 \endlist
114
115 See also \l {Required Properties}.
116
117 \section2 End-user interaction
118
119 By default, TreeView \l {toggleExpanded()}{toggles} the expanded state
120 of a row when you double tap on it. Since this is in conflict with
121 double tapping to edit a cell, TreeView sets \l {TableView::}{editTriggers} to
122 \c TableView.EditKeyPressed by default (which is different from TableView,
123 which uses \c {TableView.EditKeyPressed | TableView.DoubleTapped}.
124 If you change \l {TableView::}{editTriggers} to also contain \c TableView.DoubleTapped,
125 toggling the expanded state with a double tap will be disabled.
126
127*/
128
129/*!
130 \qmlproperty QModelIndex QtQuick::TreeView::rootIndex
131 \since 6.6
132
133 This property holds the model index of the root item in the tree.
134 By default, this is the same as the root index in the model, but you can
135 set it to be a child index instead, to show only a branch of the tree.
136 Set it to \c undefined to show the whole model.
137*/
138
139/*!
140 \qmlmethod int QtQuick::TreeView::depth(row)
141
142 Returns the depth (the number of parents up to the root) of the given \a row.
143
144 \a row should be the row in the view (table row), and not a row in the model.
145 If \a row is not between \c 0 and \l {TableView::}{rows}, the return value will
146 be \c -1.
147
148 \sa {TableView::}{modelIndex()}
149*/
150
151/*!
152 \qmlmethod bool QtQuick::TreeView::isExpanded(row)
153
154 Returns if the given \a row in the view is shown as expanded.
155
156 \a row should be the row in the view (table row), and not a row in the model.
157 If \a row is not between \c 0 and \l {TableView::}{rows}, the return value will
158 be \c false.
159*/
160
161/*!
162 \qmlmethod QtQuick::TreeView::expand(row)
163
164 Expands the tree node at the given \a row in the view.
165
166 \a row should be the row in the view (table row), and not a row in the model.
167
168 \note this function will not affect the model, only
169 the visual representation in the view.
170
171 \sa collapse(), isExpanded(), expandRecursively()
172*/
173
174/*!
175 \qmlmethod QtQuick::TreeView::expandRecursively(row = -1, depth = -1)
176 \since 6.4
177
178 Expands the tree node at the given \a row in the view recursively down to
179 \a depth. \a depth should be relative to the depth of \a row. If
180 \a depth is \c -1, the tree will be expanded all the way down to all leaves.
181
182 For a model that has more than one root, you can also call this function
183 with \a row equal to \c -1. This will expand all roots. Hence, calling
184 expandRecursively(-1, -1), or simply expandRecursively(), will expand
185 all nodes in the model.
186
187 \a row should be the row in the view (table row), and not a row in the model.
188
189 \note This function will not try to \l{QAbstractItemModel::fetchMore}{fetch more} data.
190 \note This function will not affect the model, only the visual representation in the view.
191 \warning If the model contains a large number of items, this function will
192 take some time to execute.
193
194 \sa collapseRecursively(), expand(), collapse(), isExpanded(), depth()
195*/
196
197/*!
198 \qmlmethod QtQuick::TreeView::expandToIndex(QModelIndex index)
199 \since 6.4
200
201 Expands the tree from the given model \a index, and recursively all the way up
202 to the root. The result will be that the delegate item that represents \a index
203 becomes visible in the view (unless it ends up outside the viewport). To
204 ensure that the row ends up visible in the viewport, you can do:
205
206 \code
207 expandToIndex(index)
208 forceLayout()
209 positionViewAtRow(rowAtIndex(index), Qt.AlignVCenter)
210 \endcode
211
212 \sa expand(), expandRecursively()
213*/
214
215/*!
216 \qmlmethod QtQuick::TreeView::collapse(row)
217
218 Collapses the tree node at the given \a row in the view.
219
220 \a row should be the row in the view (table row), and not a row in the model.
221
222 \note this function will not affect the model, only
223 the visual representation in the view.
224
225 \sa expand(), isExpanded()
226*/
227
228/*!
229 \qmlmethod QtQuick::TreeView::collapseRecursively(row = -1)
230 \since 6.4
231
232 Collapses the tree node at the given \a row in the view recursively down to
233 all leaves.
234
235 For a model has more than one root, you can also call this function
236 with \a row equal to \c -1. This will collapse all roots. Hence, calling
237 collapseRecursively(-1), or simply collapseRecursively(), will collapse
238 all nodes in the model.
239
240 \a row should be the row in the view (table row), and not a row in the model.
241
242 \note this function will not affect the model, only
243 the visual representation in the view.
244
245 \sa expandRecursively(), expand(), collapse(), isExpanded(), depth()
246*/
247
248/*!
249 \qmlmethod QtQuick::TreeView::toggleExpanded(row)
250
251 Toggles if the tree node at the given \a row should be expanded.
252 This is a convenience for doing:
253
254 \code
255 if (isExpanded(row))
256 collapse(row)
257 else
258 expand(row)
259 \endcode
260
261 \a row should be the row in the view (table row), and not a row in the model.
262*/
263
264/*!
265 \qmlsignal QtQuick::TreeView::expanded(row, depth)
266
267 This signal is emitted when a \a row is expanded in the view.
268 \a row and \a depth will be equal to the arguments given to the call
269 that caused the expansion to happen (\l expand() or \l expandRecursively()).
270 In case of \l expand(), \a depth will always be \c 1.
271 In case of \l expandToIndex(), \a depth will be the depth of the
272 target index.
273
274 \note when a row is expanded recursively, the expanded signal will
275 only be emitted for that one row, and not for its descendants.
276
277 \sa collapsed(), expand(), collapse(), toggleExpanded()
278*/
279
280/*!
281 \qmlsignal QtQuick::TreeView::collapsed(row, recursively)
282
283 This signal is emitted when a \a row is collapsed in the view.
284 \a row will be equal to the argument given to the call that caused
285 the collapse to happen (\l collapse() or \l collapseRecursively()).
286 If the row was collapsed recursively, \a recursively will be \c true.
287
288 \note when a row is collapsed recursively, the collapsed signal will
289 only be emitted for that one row, and not for its descendants.
290
291 \sa expanded(), expand(), collapse(), toggleExpanded()
292*/
293
294// Hard-code the tree column to be 0 for now
295static const int kTreeColumn = 0;
296
297QT_BEGIN_NAMESPACE
298
299QQuickTreeViewPrivate::QQuickTreeViewPrivate()
300 : QQuickTableViewPrivate()
301{
302}
303
304QQuickTreeViewPrivate::~QQuickTreeViewPrivate()
305{
306}
307
308QVariant QQuickTreeViewPrivate::modelImpl() const
309{
310 return m_assignedModel;
311}
312
313void QQuickTreeViewPrivate::setModelImpl(const QVariant &newModel)
314{
315 Q_Q(QQuickTreeView);
316
317 m_assignedModel = newModel;
318 if (m_assignedModel.isNull())
319 m_treeModelToTableModel.setModel(nullptr);
320 else if (const auto qaim = qvariant_cast<QAbstractItemModel *>(m_assignedModel))
321 m_treeModelToTableModel.setModel(qaim);
322 else
323 qmlWarning(q) << "TreeView only accepts a model of type QAbstractItemModel";
324
325
326 scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::All);
327 emit q->modelChanged();
328}
329
330void QQuickTreeViewPrivate::initItemCallback(int serializedModelIndex, QObject *object)
331{
332 updateRequiredProperties(serializedModelIndex, object, true);
333 QQuickTableViewPrivate::initItemCallback(serializedModelIndex, object);
334}
335
336void QQuickTreeViewPrivate::itemReusedCallback(int serializedModelIndex, QObject *object)
337{
338 updateRequiredProperties(serializedModelIndex, object, false);
339 QQuickTableViewPrivate::itemReusedCallback(serializedModelIndex, object);
340}
341
342void QQuickTreeViewPrivate::dataChangedCallback(
343 const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
344{
345 Q_Q(QQuickTreeView);
346 Q_UNUSED(roles);
347
348 for (int row = topLeft.row(); row <= bottomRight.row(); ++row) {
349 for (int column = topLeft.column(); column <= bottomRight.column(); ++column) {
350 const QPoint cell(column, row);
351 auto item = q->itemAtCell(cell);
352 if (!item)
353 continue;
354
355 const int serializedModelIndex = modelIndexAtCell(QPoint(column, row));
356 updateRequiredProperties(serializedModelIndex, item, false);
357 }
358 }
359}
360
361void QQuickTreeViewPrivate::updateRequiredProperties(int serializedModelIndex, QObject *object, bool init)
362{
363 Q_Q(QQuickTreeView);
364 const QPoint cell = cellAtModelIndex(serializedModelIndex);
365 const int row = cell.y();
366 const int column = cell.x();
367
368 setRequiredProperty("treeView", QVariant::fromValue(q), serializedModelIndex, object, init);
369 setRequiredProperty("isTreeNode", column == kTreeColumn, serializedModelIndex, object, init);
370 setRequiredProperty("hasChildren", m_treeModelToTableModel.hasChildren(row), serializedModelIndex, object, init);
371 setRequiredProperty("expanded", q->isExpanded(row), serializedModelIndex, object, init);
372 setRequiredProperty("depth", m_treeModelToTableModel.depthAtRow(row), serializedModelIndex, object, init);
373}
374
375void QQuickTreeViewPrivate::updateSelection(const QRect &oldSelection, const QRect &newSelection)
376{
377 Q_Q(QQuickTreeView);
378
379 if (oldSelection == newSelection)
380 return;
381
382 QItemSelection select;
383 QItemSelection deselect;
384
385 // Because each row can have a different parent, we need to create separate QItemSelections
386 // per row. But all the cells in a given row have the same parent, so they can be combined.
387 // As a result, the final QItemSelection can end up more fragmented compared to a selection
388 // in QQuickTableView, where all cells have the same parent. In the end, if TreeView has
389 // a lot of columns and the selection mode is "SelectCells", using the mouse to adjust
390 // a selection containing a _large_ number of columns can be slow.
391 const QRect cells = newSelection.normalized();
392 for (int row = cells.y(); row <= cells.y() + cells.height(); ++row) {
393 const QModelIndex startIndex = q->index(row, cells.x());
394 const QModelIndex endIndex = q->index(row, cells.x() + cells.width());
395 select.merge(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select);
396 }
397
398 const QModelIndexList indexes = selectionModel->selection().indexes();
399 for (const QModelIndex &index : indexes) {
400 if (!select.contains(index) && !existingSelection.contains(index))
401 deselect.merge(QItemSelection(index, index), QItemSelectionModel::Select);
402 }
403
404 if (selectionFlag == QItemSelectionModel::Select) {
405 selectionModel->select(deselect, QItemSelectionModel::Deselect);
406 selectionModel->select(select, QItemSelectionModel::Select);
407 } else {
408 QItemSelection oldSelection = existingSelection;
409 oldSelection.merge(select, QItemSelectionModel::Deselect);
410 selectionModel->select(oldSelection, QItemSelectionModel::Select);
411 selectionModel->select(select, QItemSelectionModel::Deselect);
412 }
413}
414
415QQuickTreeView::QQuickTreeView(QQuickItem *parent)
416 : QQuickTableView(*(new QQuickTreeViewPrivate), parent)
417{
418 Q_D(QQuickTreeView);
419
420 setSelectionBehavior(SelectRows);
421 setEditTriggers(EditKeyPressed);
422
423 // Note: QQuickTableView will only ever see the table model m_treeModelToTableModel, and
424 // never the actual tree model that is assigned to us by the application.
425 const auto modelAsVariant = QVariant::fromValue(std::addressof(d->m_treeModelToTableModel));
426 d->QQuickTableViewPrivate::setModelImpl(modelAsVariant);
427 QObjectPrivate::connect(&d->m_treeModelToTableModel, &QAbstractItemModel::dataChanged,
428 d, &QQuickTreeViewPrivate::dataChangedCallback);
429 QObject::connect(&d->m_treeModelToTableModel, &QQmlTreeModelToTableModel::rootIndexChanged,
430 this, &QQuickTreeView::rootIndexChanged);
431
432 auto tapHandler = new QQuickTapHandler(this);
433 tapHandler->setAcceptedModifiers(Qt::NoModifier);
434 connect(tapHandler, &QQuickTapHandler::doubleTapped, [this, tapHandler]{
435 if (!pointerNavigationEnabled())
436 return;
437 if (editTriggers() & DoubleTapped)
438 return;
439
440 const int row = cellAtPosition(tapHandler->point().pressPosition()).y();
441 toggleExpanded(row);
442 });
443}
444
445QQuickTreeView::~QQuickTreeView()
446{
447}
448
449QModelIndex QQuickTreeView::rootIndex() const
450{
451 return d_func()->m_treeModelToTableModel.rootIndex();
452}
453
454void QQuickTreeView::setRootIndex(const QModelIndex &index)
455{
456 Q_D(QQuickTreeView);
457 d->m_treeModelToTableModel.setRootIndex(index);
458 positionViewAtCell({0, 0}, QQuickTableView::AlignTop | QQuickTableView::AlignLeft);
459}
460
461void QQuickTreeView::resetRootIndex()
462{
463 Q_D(QQuickTreeView);
464 d->m_treeModelToTableModel.resetRootIndex();
465 positionViewAtCell({0, 0}, QQuickTableView::AlignTop | QQuickTableView::AlignLeft);
466}
467
468int QQuickTreeView::depth(int row) const
469{
470 Q_D(const QQuickTreeView);
471 if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
472 return -1;
473
474 return d->m_treeModelToTableModel.depthAtRow(row);
475}
476
477bool QQuickTreeView::isExpanded(int row) const
478{
479 Q_D(const QQuickTreeView);
480 if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
481 return false;
482
483 return d->m_treeModelToTableModel.isExpanded(row);
484}
485
486void QQuickTreeView::expand(int row)
487{
488 if (row >= 0)
489 expandRecursively(row, 1);
490}
491
492void QQuickTreeView::expandRecursively(int row, int depth)
493{
494 Q_D(QQuickTreeView);
495 if (row >= d->m_treeModelToTableModel.rowCount())
496 return;
497 if (row < 0 && row != -1)
498 return;
499 if (depth == 0 || depth < -1)
500 return;
501
502 auto expandRowRecursively = [this, d, depth](int startRow) {
503 d->m_treeModelToTableModel.expandRecursively(startRow, depth);
504 // Update the expanded state of the startRow. The descendant rows that gets
505 // expanded will get the correct state set from initItem/itemReused instead.
506 for (int c = leftColumn(); c <= rightColumn(); ++c) {
507 const QPoint treeNodeCell(c, startRow);
508 if (const auto item = itemAtCell(treeNodeCell))
509 d->setRequiredProperty("expanded", true, d->modelIndexAtCell(treeNodeCell), item, false);
510 }
511 };
512
513 if (row >= 0) {
514 // Expand only one row recursively
515 const bool isExpanded = d->m_treeModelToTableModel.isExpanded(row);
516 if (isExpanded && depth == 1)
517 return;
518 expandRowRecursively(row);
519 } else {
520 // Expand all root nodes recursively
521 const auto model = d->m_treeModelToTableModel.model();
522 for (int r = 0; r < model->rowCount(); ++r) {
523 const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0));
524 if (rootRow != -1)
525 expandRowRecursively(rootRow);
526 }
527 }
528
529 emit expanded(row, depth);
530}
531
532void QQuickTreeView::expandToIndex(const QModelIndex &index)
533{
534 Q_D(QQuickTreeView);
535
536 if (!index.isValid()) {
537 qmlWarning(this) << "index is not valid: " << index;
538 return;
539 }
540
541 if (index.model() != d->m_treeModelToTableModel.model()) {
542 qmlWarning(this) << "index doesn't belong to correct model: " << index;
543 return;
544 }
545
546 if (rowAtIndex(index) != -1) {
547 // index is already visible
548 return;
549 }
550
551 int depth = 1;
552 QModelIndex parent = index.parent();
553 int row = rowAtIndex(parent);
554
555 while (parent.isValid()) {
556 if (row != -1) {
557 // The node is already visible, since it maps to a row in the table!
558 d->m_treeModelToTableModel.expandRow(row);
559
560 // Update the state of the already existing delegate item
561 for (int c = leftColumn(); c <= rightColumn(); ++c) {
562 const QPoint treeNodeCell(c, row);
563 if (const auto item = itemAtCell(treeNodeCell))
564 d->setRequiredProperty("expanded", true, d->modelIndexAtCell(treeNodeCell), item, false);
565 }
566
567 // When we hit a node that is visible, we know that all other nodes
568 // up to the parent have to be visible as well, so we can stop.
569 break;
570 } else {
571 d->m_treeModelToTableModel.expand(parent);
572 parent = parent.parent();
573 row = rowAtIndex(parent);
574 depth++;
575 }
576 }
577
578 emit expanded(row, depth);
579}
580
581void QQuickTreeView::collapse(int row)
582{
583 Q_D(QQuickTreeView);
584 if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
585 return;
586
587 if (!d->m_treeModelToTableModel.isExpanded(row))
588 return;
589
590 d_func()->m_treeModelToTableModel.collapseRow(row);
591
592 for (int c = leftColumn(); c <= rightColumn(); ++c) {
593 const QPoint treeNodeCell(c, row);
594 if (const auto item = itemAtCell(treeNodeCell))
595 d->setRequiredProperty("expanded", false, d->modelIndexAtCell(treeNodeCell), item, false);
596 }
597
598 emit collapsed(row, false);
599}
600
601void QQuickTreeView::collapseRecursively(int row)
602{
603 Q_D(QQuickTreeView);
604 if (row >= d->m_treeModelToTableModel.rowCount())
605 return;
606 if (row < 0 && row != -1)
607 return;
608
609 auto collapseRowRecursive = [this, d](int startRow) {
610 // Always collapse descendants recursively,
611 // even if the top row itself is already collapsed.
612 d->m_treeModelToTableModel.collapseRecursively(startRow);
613 // Update the expanded state of the (still visible) startRow
614 for (int c = leftColumn(); c <= rightColumn(); ++c) {
615 const QPoint treeNodeCell(c, startRow);
616 if (const auto item = itemAtCell(treeNodeCell))
617 d->setRequiredProperty("expanded", false, d->modelIndexAtCell(treeNodeCell), item, false);
618 }
619 };
620
621 if (row >= 0) {
622 collapseRowRecursive(row);
623 } else {
624 // Collapse all root nodes recursively
625 const auto model = d->m_treeModelToTableModel.model();
626 for (int r = 0; r < model->rowCount(); ++r) {
627 const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0));
628 if (rootRow != -1)
629 collapseRowRecursive(rootRow);
630 }
631 }
632
633 emit collapsed(row, true);
634}
635
636void QQuickTreeView::toggleExpanded(int row)
637{
638 if (isExpanded(row))
639 collapse(row);
640 else
641 expand(row);
642}
643
644QModelIndex QQuickTreeView::modelIndex(const QPoint &cell) const
645{
646 Q_D(const QQuickTreeView);
647 const QModelIndex tableIndex = d->m_treeModelToTableModel.index(cell.y(), cell.x());
648 return d->m_treeModelToTableModel.mapToModel(tableIndex);
649}
650
651QPoint QQuickTreeView::cellAtIndex(const QModelIndex &index) const
652{
653 const QModelIndex tableIndex = d_func()->m_treeModelToTableModel.mapFromModel(index);
654 return QPoint(tableIndex.column(), tableIndex.row());
655}
656
657#if QT_DEPRECATED_SINCE(6, 4)
658QModelIndex QQuickTreeView::modelIndex(int row, int column) const
659{
660 static const bool compat6_4 = qEnvironmentVariable("QT_QUICK_TABLEVIEW_COMPAT_VERSION") == QStringLiteral("6.4");
661 if (compat6_4) {
662 // XXX Qt 7: Remove this compatibility path here and in QQuickTableView.
663 // In Qt 6.4.0 and 6.4.1, a source incompatible change led to row and column
664 // being documented to be specified in the opposite order.
665 // QT_QUICK_TABLEVIEW_COMPAT_VERSION can therefore be set to force tableview
666 // to continue accepting calls to modelIndex(column, row).
667 return modelIndex({row, column});
668 } else {
669 qmlWarning(this) << "modelIndex(row, column) is deprecated. "
670 "Use index(row, column) instead. For more information, see "
671 "https://doc.qt.io/qt-6/qml-qtquick-tableview-obsolete.html";
672 return modelIndex({column, row});
673 }
674}
675#endif
676
677void QQuickTreeView::keyPressEvent(QKeyEvent *event)
678{
679 event->ignore();
680
681 if (!keyNavigationEnabled())
682 return;
683 if (!selectionModel())
684 return;
685
686 const int row = cellAtIndex(selectionModel()->currentIndex()).y();
687 switch (event->key()) {
688 case Qt::Key_Left:
689 collapse(row);
690 event->accept();
691 break;
692 case Qt::Key_Right:
693 expand(row);
694 event->accept();
695 break;
696 default:
697 break;
698 }
699
700 if (!event->isAccepted()) {
701 event->accept();
702 QQuickTableView::keyPressEvent(event);
703 }
704}
705
706QT_END_NAMESPACE
707
708#include "moc_qquicktreeview_p.cpp"
static const int kTreeColumn
\qmlproperty QModelIndex QtQuick::TreeView::rootIndex