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