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::initItemCallback(
int serializedModelIndex, QObject *object)
332 updateRequiredProperties(serializedModelIndex, object,
true);
333 QQuickTableViewPrivate::initItemCallback(serializedModelIndex, object);
336void QQuickTreeViewPrivate::itemReusedCallback(
int serializedModelIndex, QObject *object)
338 updateRequiredProperties(serializedModelIndex, object,
false);
339 QQuickTableViewPrivate::itemReusedCallback(serializedModelIndex, object);
342void QQuickTreeViewPrivate::dataChangedCallback(
343 const QModelIndex &topLeft,
const QModelIndex &bottomRight,
const QVector<
int> &roles)
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);
355 const int serializedModelIndex = modelIndexAtCell(QPoint(column, row));
356 updateRequiredProperties(serializedModelIndex, item,
false);
361void QQuickTreeViewPrivate::updateRequiredProperties(
int serializedModelIndex, QObject *object,
bool init)
364 const QPoint cell = cellAtModelIndex(serializedModelIndex);
365 const int row = cell.y();
366 const int column = cell.x();
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);
375void QQuickTreeViewPrivate::updateSelection(
const QRect &oldSelection,
const QRect &newSelection)
379 if (oldSelection == newSelection)
382 QItemSelection select;
383 QItemSelection deselect;
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);
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);
404 if (selectionFlag == QItemSelectionModel::Select) {
405 selectionModel->select(deselect, QItemSelectionModel::Deselect);
406 selectionModel->select(select, QItemSelectionModel::Select);
408 QItemSelection oldSelection = existingSelection;
409 oldSelection.merge(select, QItemSelectionModel::Deselect);
410 selectionModel->select(oldSelection, QItemSelectionModel::Select);
411 selectionModel->select(select, QItemSelectionModel::Deselect);
415QQuickTreeView::QQuickTreeView(QQuickItem *parent)
416 : QQuickTableView(*(
new QQuickTreeViewPrivate), parent)
420 setSelectionBehavior(SelectRows);
421 setEditTriggers(EditKeyPressed);
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);
432 auto tapHandler =
new QQuickTapHandler(
this);
433 tapHandler->setAcceptedModifiers(Qt::NoModifier);
434 connect(tapHandler, &QQuickTapHandler::doubleTapped, [
this, tapHandler]{
435 if (!pointerNavigationEnabled())
437 if (editTriggers() & DoubleTapped)
440 const int row = cellAtPosition(tapHandler->point().pressPosition()).y();
445QQuickTreeView::~QQuickTreeView()
449QModelIndex QQuickTreeView::rootIndex()
const
451 return d_func()->m_treeModelToTableModel.rootIndex();
454void QQuickTreeView::setRootIndex(
const QModelIndex &index)
457 d->m_treeModelToTableModel.setRootIndex(index);
458 positionViewAtCell({0, 0}, QQuickTableView::AlignTop | QQuickTableView::AlignLeft);
461void QQuickTreeView::resetRootIndex()
464 d->m_treeModelToTableModel.resetRootIndex();
465 positionViewAtCell({0, 0}, QQuickTableView::AlignTop | QQuickTableView::AlignLeft);
468int QQuickTreeView::depth(
int row)
const
470 Q_D(
const QQuickTreeView);
471 if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
474 return d->m_treeModelToTableModel.depthAtRow(row);
477bool QQuickTreeView::isExpanded(
int row)
const
479 Q_D(
const QQuickTreeView);
480 if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
483 return d->m_treeModelToTableModel.isExpanded(row);
486void QQuickTreeView::expand(
int row)
489 expandRecursively(row, 1);
492void QQuickTreeView::expandRecursively(
int row,
int depth)
495 if (row >= d->m_treeModelToTableModel.rowCount())
497 if (row < 0 && row != -1)
499 if (depth == 0 || depth < -1)
502 auto expandRowRecursively = [
this, d, depth](
int startRow) {
503 d->m_treeModelToTableModel.expandRecursively(startRow, depth);
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);
515 const bool isExpanded = d->m_treeModelToTableModel.isExpanded(row);
516 if (isExpanded && depth == 1)
518 expandRowRecursively(row);
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));
525 expandRowRecursively(rootRow);
529 emit expanded(row, depth);
532void QQuickTreeView::expandToIndex(
const QModelIndex &index)
536 if (!index.isValid()) {
537 qmlWarning(
this) <<
"index is not valid: " << index;
541 if (index.model() != d->m_treeModelToTableModel.model()) {
542 qmlWarning(
this) <<
"index doesn't belong to correct model: " << index;
546 if (rowAtIndex(index) != -1) {
552 QModelIndex parent = index.parent();
553 int row = rowAtIndex(parent);
555 while (parent.isValid()) {
558 d->m_treeModelToTableModel.expandRow(row);
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);
571 d->m_treeModelToTableModel.expand(parent);
572 parent = parent.parent();
573 row = rowAtIndex(parent);
578 emit expanded(row, depth);
581void QQuickTreeView::collapse(
int row)
584 if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
587 if (!d->m_treeModelToTableModel.isExpanded(row))
590 d_func()->m_treeModelToTableModel.collapseRow(row);
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);
598 emit collapsed(row,
false);
601void QQuickTreeView::collapseRecursively(
int row)
604 if (row >= d->m_treeModelToTableModel.rowCount())
606 if (row < 0 && row != -1)
609 auto collapseRowRecursive = [
this, d](
int startRow) {
612 d->m_treeModelToTableModel.collapseRecursively(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);
622 collapseRowRecursive(row);
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));
629 collapseRowRecursive(rootRow);
633 emit collapsed(row,
true);
636void QQuickTreeView::toggleExpanded(
int row)
644QModelIndex QQuickTreeView::modelIndex(
const QPoint &cell)
const
646 Q_D(
const QQuickTreeView);
647 const QModelIndex tableIndex = d->m_treeModelToTableModel.index(cell.y(), cell.x());
648 return d->m_treeModelToTableModel.mapToModel(tableIndex);
651QPoint QQuickTreeView::cellAtIndex(
const QModelIndex &index)
const
653 const QModelIndex tableIndex = d_func()->m_treeModelToTableModel.mapFromModel(index);
654 return QPoint(tableIndex.column(), tableIndex.row());
657#if QT_DEPRECATED_SINCE(6
, 4
)
658QModelIndex QQuickTreeView::modelIndex(
int row,
int column)
const
660 static const bool compat6_4 = qEnvironmentVariable(
"QT_QUICK_TABLEVIEW_COMPAT_VERSION") == QStringLiteral(
"6.4");
667 return modelIndex({row, column});
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});
677void QQuickTreeView::keyPressEvent(QKeyEvent *event)
681 if (!keyNavigationEnabled())
683 if (!selectionModel())
686 const int row = cellAtIndex(selectionModel()->currentIndex()).y();
687 switch (event->key()) {
700 if (!event->isAccepted()) {
702 QQuickTableView::keyPressEvent(event);
708#include "moc_qquicktreeview_p.cpp"
static const int kTreeColumn
\qmlproperty QModelIndex QtQuick::TreeView::rootIndex