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 QList<
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, [
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);
519 }
else if (
const auto model = d->m_treeModelToTableModel.model()) {
521 for (
int r = 0; r < model->rowCount(); ++r) {
522 const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0));
524 expandRowRecursively(rootRow);
528 emit expanded(row, depth);
531void QQuickTreeView::expandToIndex(
const QModelIndex &index)
535 if (!index.isValid()) {
536 qmlWarning(
this) <<
"index is not valid: " << index;
540 if (index.model() != d->m_treeModelToTableModel.model()) {
541 qmlWarning(
this) <<
"index doesn't belong to correct model: " << index;
545 if (rowAtIndex(index) != -1) {
551 QModelIndex parent = index.parent();
552 int row = rowAtIndex(parent);
554 while (parent.isValid()) {
557 d->m_treeModelToTableModel.expandRow(row);
560 for (
int c = leftColumn(); c <= rightColumn(); ++c) {
561 const QPoint treeNodeCell(c, row);
562 if (
const auto item = itemAtCell(treeNodeCell))
563 d->setRequiredProperty(
"expanded",
true, d->modelIndexAtCell(treeNodeCell), item,
false);
570 d->m_treeModelToTableModel.expand(parent);
571 parent = parent.parent();
572 row = rowAtIndex(parent);
577 emit expanded(row, depth);
580void QQuickTreeView::collapse(
int row)
583 if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
586 if (!d->m_treeModelToTableModel.isExpanded(row))
589 d_func()->m_treeModelToTableModel.collapseRow(row);
591 for (
int c = leftColumn(); c <= rightColumn(); ++c) {
592 const QPoint treeNodeCell(c, row);
593 if (
const auto item = itemAtCell(treeNodeCell))
594 d->setRequiredProperty(
"expanded",
false, d->modelIndexAtCell(treeNodeCell), item,
false);
597 emit collapsed(row,
false);
600void QQuickTreeView::collapseRecursively(
int row)
603 if (row >= d->m_treeModelToTableModel.rowCount())
605 if (row < 0 && row != -1)
608 auto collapseRowRecursive = [
this, d](
int startRow) {
611 d->m_treeModelToTableModel.collapseRecursively(startRow);
613 for (
int c = leftColumn(); c <= rightColumn(); ++c) {
614 const QPoint treeNodeCell(c, startRow);
615 if (
const auto item = itemAtCell(treeNodeCell))
616 d->setRequiredProperty(
"expanded",
false, d->modelIndexAtCell(treeNodeCell), item,
false);
621 collapseRowRecursive(row);
622 }
else if (
const auto model = d->m_treeModelToTableModel.model()) {
624 for (
int r = 0; r < model->rowCount(); ++r) {
625 const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0));
627 collapseRowRecursive(rootRow);
631 emit collapsed(row,
true);
634void QQuickTreeView::toggleExpanded(
int row)
642QModelIndex QQuickTreeView::modelIndex(
const QPoint &cell)
const
644 Q_D(
const QQuickTreeView);
645 const QModelIndex tableIndex = d->m_treeModelToTableModel.index(cell.y(), cell.x());
646 return d->m_treeModelToTableModel.mapToModel(tableIndex);
649QPoint QQuickTreeView::cellAtIndex(
const QModelIndex &index)
const
651 const QModelIndex tableIndex = d_func()->m_treeModelToTableModel.mapFromModel(index);
652 return QPoint(tableIndex.column(), tableIndex.row());
655#if QT_DEPRECATED_SINCE(6
, 4
)
656QModelIndex QQuickTreeView::modelIndex(
int row,
int column)
const
658 static const bool compat6_4 = qEnvironmentVariable(
"QT_QUICK_TABLEVIEW_COMPAT_VERSION") == QStringLiteral(
"6.4");
665 return modelIndex({row, column});
667 qmlWarning(
this) <<
"modelIndex(row, column) is deprecated. "
668 "Use index(row, column) instead. For more information, see "
669 "https://doc.qt.io/qt-6/qml-qtquick-tableview-obsolete.html";
670 return modelIndex({column, row});
675void QQuickTreeView::keyPressEvent(QKeyEvent *event)
679 if (!keyNavigationEnabled())
681 if (!selectionModel())
684 const int row = cellAtIndex(selectionModel()->currentIndex()).y();
685 switch (event->key()) {
698 if (!event->isAccepted()) {
700 QQuickTableView::keyPressEvent(event);
706#include "moc_qquicktreeview_p.cpp"
static const int kTreeColumn
\qmlproperty QModelIndex QtQuick::TreeView::rootIndex