7#include <QtCore/qobject.h>
8#include <QtQml/qqmlcontext.h>
9#include <QtQuick/private/qquicktaphandler_p.h>
11#include <QtQmlModels/private/qqmltreemodeltotablemodel_p_p.h>
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
130
131
132
133
134
135
136
137
140
141
142
143
144
145
146
147
148
149
152
153
154
155
156
157
158
159
162
163
164
165
166
167
168
169
170
171
172
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
216
217
218
219
220
221
222
223
224
225
226
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
249
250
251
252
253
254
255
256
257
258
259
260
261
262
265
266
267
268
269
270
271
272
273
274
275
276
277
278
281
282
283
284
285
286
287
288
289
290
291
292
299QQuickTreeViewPrivate::QQuickTreeViewPrivate()
300 : QQuickTableViewPrivate()
304QQuickTreeViewPrivate::~QQuickTreeViewPrivate()
308QVariant QQuickTreeViewPrivate::modelImpl()
const
310 return m_assignedModel;
313void QQuickTreeViewPrivate::setModelImpl(
const QVariant &newModel)
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);
323 qmlWarning(q) <<
"TreeView only accepts a model of type QAbstractItemModel";
326 scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::All);
327 emit q->modelChanged();
330void QQuickTreeViewPrivate::dataChangedCallback(
331 const QModelIndex &topLeft,
const QModelIndex &bottomRight,
const QList<
int> &roles)
336 for (
int row = topLeft.row(); row <= bottomRight.row(); ++row) {
337 for (
int column = topLeft.column(); column <= bottomRight.column(); ++column) {
338 const QPoint cell(column, row);
339 auto item = q->itemAtCell(cell);
343 const int flatIndex = column * m_treeModelToTableModel.rowCount() + row;
344 updateItemProperties(flatIndex, item,
false);
349void QQuickTreeViewPrivate::updateItemProperties(
int flatIndex, QObject *object,
bool init)
352 QQuickTableViewPrivate::updateItemProperties(flatIndex, object, init);
356 const int availableRows = m_treeModelToTableModel.rowCount();
357 const int column = flatIndex / availableRows;
358 const int row = flatIndex % availableRows;
360 setRequiredProperty(
"treeView", QVariant::fromValue(q), flatIndex, object, init);
361 setRequiredProperty(
"isTreeNode", column == kTreeColumn, flatIndex, object, init);
362 setRequiredProperty(
"hasChildren", m_treeModelToTableModel.hasChildren(row), flatIndex, object, init);
363 setRequiredProperty(
"expanded", q->isExpanded(row), flatIndex, object, init);
364 setRequiredProperty(
"depth", m_treeModelToTableModel.depthAtRow(row), flatIndex, object, init);
367void QQuickTreeViewPrivate::updateSelection(
const QRect &oldSelection,
const QRect &newSelection)
371 if (oldSelection == newSelection)
374 QItemSelection select;
375 QItemSelection deselect;
383 const QRect cells = newSelection.normalized();
384 for (
int row = cells.y(); row <= cells.y() + cells.height(); ++row) {
385 const QModelIndex startIndex = q->index(row, cells.x());
386 const QModelIndex endIndex = q->index(row, cells.x() + cells.width());
387 select.merge(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select);
390 const QModelIndexList indexes = selectionModel->selection().indexes();
391 for (
const QModelIndex &index : indexes) {
392 if (!select.contains(index) && !existingSelection.contains(index))
393 deselect.merge(QItemSelection(index, index), QItemSelectionModel::Select);
396 if (selectionFlag == QItemSelectionModel::Select) {
397 selectionModel->select(deselect, QItemSelectionModel::Deselect);
398 selectionModel->select(select, QItemSelectionModel::Select);
400 QItemSelection oldSelection = existingSelection;
401 oldSelection.merge(select, QItemSelectionModel::Deselect);
402 selectionModel->select(oldSelection, QItemSelectionModel::Select);
403 selectionModel->select(select, QItemSelectionModel::Deselect);
407QQuickTreeView::QQuickTreeView(QQuickItem *parent)
408 : QQuickTableView(*(
new QQuickTreeViewPrivate), parent)
412 setSelectionBehavior(SelectRows);
413 setEditTriggers(EditKeyPressed);
417 const auto modelAsVariant = QVariant::fromValue(std::addressof(d->m_treeModelToTableModel));
418 d->QQuickTableViewPrivate::setModelImpl(modelAsVariant);
419 QObjectPrivate::connect(&d->m_treeModelToTableModel, &QAbstractItemModel::dataChanged,
420 d, &QQuickTreeViewPrivate::dataChangedCallback);
421 QObject::connect(&d->m_treeModelToTableModel, &QQmlTreeModelToTableModel::rootIndexChanged,
422 this, &QQuickTreeView::rootIndexChanged);
424 auto tapHandler =
new QQuickTapHandler(
this);
425 tapHandler->setAcceptedModifiers(Qt::NoModifier);
426 connect(tapHandler, &QQuickTapHandler::doubleTapped,
this, [
this, tapHandler] {
427 if (!pointerNavigationEnabled())
429 if (editTriggers() & DoubleTapped)
432 const int row = cellAtPosition(tapHandler->point().pressPosition()).y();
437QQuickTreeView::~QQuickTreeView()
441QModelIndex QQuickTreeView::rootIndex()
const
443 return d_func()->m_treeModelToTableModel.rootIndex();
446void QQuickTreeView::setRootIndex(
const QModelIndex &index)
449 d->m_treeModelToTableModel.setRootIndex(index);
450 positionViewAtCell({0, 0}, QQuickTableView::AlignTop | QQuickTableView::AlignLeft);
453void QQuickTreeView::resetRootIndex()
456 d->m_treeModelToTableModel.resetRootIndex();
457 positionViewAtCell({0, 0}, QQuickTableView::AlignTop | QQuickTableView::AlignLeft);
460int QQuickTreeView::depth(
int row)
const
462 Q_D(
const QQuickTreeView);
463 if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
466 return d->m_treeModelToTableModel.depthAtRow(row);
469bool QQuickTreeView::isExpanded(
int row)
const
471 Q_D(
const QQuickTreeView);
472 if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
475 return d->m_treeModelToTableModel.isExpanded(row);
478void QQuickTreeView::expand(
int row)
481 expandRecursively(row, 1);
484void QQuickTreeView::expandRecursively(
int row,
int depth)
487 if (row >= d->m_treeModelToTableModel.rowCount())
489 if (row < 0 && row != -1)
491 if (depth == 0 || depth < -1)
494 auto expandRowRecursively = [
this, d, depth](
int startRow) {
495 d->m_treeModelToTableModel.expandRecursively(startRow, depth);
498 for (
int c = leftColumn(); c <= rightColumn(); ++c) {
499 const QPoint treeNodeCell(c, startRow);
500 if (
const auto item = itemAtCell(treeNodeCell))
501 d->setRequiredProperty(
"expanded",
true, d->modelIndexAtCell(treeNodeCell), item,
false);
507 const bool isExpanded = d->m_treeModelToTableModel.isExpanded(row);
508 if (isExpanded && depth == 1)
510 expandRowRecursively(row);
511 }
else if (
const auto model = d->m_treeModelToTableModel.model()) {
513 for (
int r = 0; r < model->rowCount(); ++r) {
514 const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0));
516 expandRowRecursively(rootRow);
520 emit expanded(row, depth);
523void QQuickTreeView::expandToIndex(
const QModelIndex &index)
527 if (!index.isValid()) {
528 qmlWarning(
this) <<
"index is not valid: " << index;
532 if (index.model() != d->m_treeModelToTableModel.model()) {
533 qmlWarning(
this) <<
"index doesn't belong to correct model: " << index;
537 if (rowAtIndex(index) != -1) {
543 QModelIndex parent = index.parent();
544 int row = rowAtIndex(parent);
546 while (parent.isValid()) {
549 d->m_treeModelToTableModel.expandRow(row);
552 for (
int c = leftColumn(); c <= rightColumn(); ++c) {
553 const QPoint treeNodeCell(c, row);
554 if (
const auto item = itemAtCell(treeNodeCell))
555 d->setRequiredProperty(
"expanded",
true, d->modelIndexAtCell(treeNodeCell), item,
false);
562 d->m_treeModelToTableModel.expand(parent);
563 parent = parent.parent();
564 row = rowAtIndex(parent);
569 emit expanded(row, depth);
572void QQuickTreeView::collapse(
int row)
575 if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
578 if (!d->m_treeModelToTableModel.isExpanded(row))
581 d_func()->m_treeModelToTableModel.collapseRow(row);
583 for (
int c = leftColumn(); c <= rightColumn(); ++c) {
584 const QPoint treeNodeCell(c, row);
585 if (
const auto item = itemAtCell(treeNodeCell))
586 d->setRequiredProperty(
"expanded",
false, d->modelIndexAtCell(treeNodeCell), item,
false);
589 emit collapsed(row,
false);
592void QQuickTreeView::collapseRecursively(
int row)
595 if (row >= d->m_treeModelToTableModel.rowCount())
597 if (row < 0 && row != -1)
600 auto collapseRowRecursive = [
this, d](
int startRow) {
603 d->m_treeModelToTableModel.collapseRecursively(startRow);
605 for (
int c = leftColumn(); c <= rightColumn(); ++c) {
606 const QPoint treeNodeCell(c, startRow);
607 if (
const auto item = itemAtCell(treeNodeCell))
608 d->setRequiredProperty(
"expanded",
false, d->modelIndexAtCell(treeNodeCell), item,
false);
613 collapseRowRecursive(row);
614 }
else if (
const auto model = d->m_treeModelToTableModel.model()) {
616 for (
int r = 0; r < model->rowCount(); ++r) {
617 const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0));
619 collapseRowRecursive(rootRow);
623 emit collapsed(row,
true);
626void QQuickTreeView::toggleExpanded(
int row)
634QModelIndex QQuickTreeView::modelIndex(
const QPoint &cell)
const
636 Q_D(
const QQuickTreeView);
637 const QModelIndex tableIndex = d->m_treeModelToTableModel.index(cell.y(), cell.x());
638 return d->m_treeModelToTableModel.mapToModel(tableIndex);
641QPoint QQuickTreeView::cellAtIndex(
const QModelIndex &index)
const
643 const QModelIndex tableIndex = d_func()->m_treeModelToTableModel.mapFromModel(index);
644 return QPoint(tableIndex.column(), tableIndex.row());
647#if QT_DEPRECATED_SINCE(6
, 4
)
648QModelIndex QQuickTreeView::modelIndex(
int row,
int column)
const
650 static const bool compat6_4 = qEnvironmentVariable(
"QT_QUICK_TABLEVIEW_COMPAT_VERSION") == QStringLiteral(
"6.4");
657 return modelIndex({row, column});
659 qmlWarning(
this) <<
"modelIndex(row, column) is deprecated. "
660 "Use index(row, column) instead. For more information, see "
661 "https://doc.qt.io/qt-6/qml-qtquick-tableview-obsolete.html";
662 return modelIndex({column, row});
667void QQuickTreeView::keyPressEvent(QKeyEvent *event)
671 if (!keyNavigationEnabled())
673 if (!selectionModel())
676 const int row = cellAtIndex(selectionModel()->currentIndex()).y();
677 switch (event->key()) {
690 if (!event->isAccepted()) {
692 QQuickTableView::keyPressEvent(event);
698#include "moc_qquicktreeview_p.cpp"
static const int kTreeColumn
\qmlproperty QModelIndex QtQuick::TreeView::rootIndex