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
128
129
130
131
132
133
134
135
136
137
138
139
140
143
144
145
146
147
148
149
150
153
154
155
156
157
158
159
160
161
162
165
166
167
168
169
170
171
172
175
176
177
178
179
180
181
182
183
184
185
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
229
230
231
232
233
234
235
236
237
238
239
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
262
263
264
265
266
267
268
269
270
271
272
273
274
275
278
279
280
281
282
283
284
285
286
287
288
289
290
291
294
295
296
297
298
299
300
301
302
303
304
305
312QQuickTreeViewPrivate::QQuickTreeViewPrivate()
313 : QQuickTableViewPrivate()
317QQuickTreeViewPrivate::~QQuickTreeViewPrivate()
321QVariant QQuickTreeViewPrivate::modelImpl()
const
323 return m_assignedModel;
326void QQuickTreeViewPrivate::setModelImpl(
const QVariant &newModel)
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);
336 qmlWarning(q) <<
"TreeView only accepts a model of type QAbstractItemModel";
339 scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::All);
340 emit q->modelChanged();
343void QQuickTreeViewPrivate::dataChangedCallback(
344 const QModelIndex &topLeft,
const QModelIndex &bottomRight,
const QList<
int> &roles)
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);
356 const int flatIndex = column * m_treeModelToTableModel.rowCount() + row;
357 updateItemProperties(flatIndex, item,
false);
362void QQuickTreeViewPrivate::updateItemProperties(
int flatIndex, QObject *object,
bool init)
365 QQuickTableViewPrivate::updateItemProperties(flatIndex, object, init);
369 const int availableRows = m_treeModelToTableModel.rowCount();
370 const int column = flatIndex / availableRows;
371 const int row = flatIndex % availableRows;
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);
380void QQuickTreeViewPrivate::updateSelection(
const QRect &oldSelection,
const QRect &newSelection)
384 if (oldSelection == newSelection)
387 QItemSelection select;
388 QItemSelection deselect;
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);
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);
409 if (selectionFlag == QItemSelectionModel::Select) {
410 selectionModel->select(deselect, QItemSelectionModel::Deselect);
411 selectionModel->select(select, QItemSelectionModel::Select);
413 QItemSelection oldSelection = existingSelection;
414 oldSelection.merge(select, QItemSelectionModel::Deselect);
415 selectionModel->select(oldSelection, QItemSelectionModel::Select);
416 selectionModel->select(select, QItemSelectionModel::Deselect);
420QQuickTreeView::QQuickTreeView(QQuickItem *parent)
421 : QQuickTableView(*(
new QQuickTreeViewPrivate), parent)
425 setSelectionBehavior(SelectRows);
426 setEditTriggers(EditKeyPressed);
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);
437 auto tapHandler =
new QQuickTapHandler(
this);
438 tapHandler->setAcceptedModifiers(Qt::NoModifier);
439 connect(tapHandler, &QQuickTapHandler::doubleTapped,
this, [
this, tapHandler] {
440 if (!pointerNavigationEnabled())
442 if (editTriggers() & DoubleTapped)
445 const int row = cellAtPosition(tapHandler->point().pressPosition()).y();
450QQuickTreeView::~QQuickTreeView()
454QModelIndex QQuickTreeView::rootIndex()
const
456 return d_func()->m_treeModelToTableModel.rootIndex();
459void QQuickTreeView::setRootIndex(
const QModelIndex &index)
462 d->m_treeModelToTableModel.setRootIndex(index);
463 positionViewAtCell({0, 0}, QQuickTableView::AlignTop | QQuickTableView::AlignLeft);
466void QQuickTreeView::resetRootIndex()
469 d->m_treeModelToTableModel.resetRootIndex();
470 positionViewAtCell({0, 0}, QQuickTableView::AlignTop | QQuickTableView::AlignLeft);
473int QQuickTreeView::depth(
int row)
const
475 Q_D(
const QQuickTreeView);
476 if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
479 return d->m_treeModelToTableModel.depthAtRow(row);
482bool QQuickTreeView::isExpanded(
int row)
const
484 Q_D(
const QQuickTreeView);
485 if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
488 return d->m_treeModelToTableModel.isExpanded(row);
491void QQuickTreeView::expand(
int row)
494 expandRecursively(row, 1);
497void QQuickTreeView::expandRecursively(
int row,
int depth)
500 if (row >= d->m_treeModelToTableModel.rowCount())
502 if (row < 0 && row != -1)
504 if (depth == 0 || depth < -1)
507 auto expandRowRecursively = [
this, d, depth](
int startRow) {
508 d->m_treeModelToTableModel.expandRecursively(startRow, depth);
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);
520 const bool isExpanded = d->m_treeModelToTableModel.isExpanded(row);
521 if (isExpanded && depth == 1)
523 expandRowRecursively(row);
524 }
else if (
const auto model = d->m_treeModelToTableModel.model()) {
526 for (
int r = 0; r < model->rowCount(); ++r) {
527 const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0));
529 expandRowRecursively(rootRow);
533 emit expanded(row, depth);
536void QQuickTreeView::expandToIndex(
const QModelIndex &index)
540 if (!index.isValid()) {
541 qmlWarning(
this) <<
"index is not valid: " << index;
545 if (index.model() != d->m_treeModelToTableModel.model()) {
546 qmlWarning(
this) <<
"index doesn't belong to correct model: " << index;
550 if (rowAtIndex(index) != -1) {
556 QModelIndex parent = index.parent();
557 int row = rowAtIndex(parent);
559 while (parent.isValid()) {
562 d->m_treeModelToTableModel.expandRow(row);
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);
575 d->m_treeModelToTableModel.expand(parent);
576 parent = parent.parent();
577 row = rowAtIndex(parent);
582 emit expanded(row, depth);
585void QQuickTreeView::collapse(
int row)
588 if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
591 if (!d->m_treeModelToTableModel.isExpanded(row))
594 d_func()->m_treeModelToTableModel.collapseRow(row);
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);
602 emit collapsed(row,
false);
605void QQuickTreeView::collapseRecursively(
int row)
608 if (row >= d->m_treeModelToTableModel.rowCount())
610 if (row < 0 && row != -1)
613 auto collapseRowRecursive = [
this, d](
int startRow) {
616 d->m_treeModelToTableModel.collapseRecursively(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);
626 collapseRowRecursive(row);
627 }
else if (
const auto model = d->m_treeModelToTableModel.model()) {
629 for (
int r = 0; r < model->rowCount(); ++r) {
630 const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0));
632 collapseRowRecursive(rootRow);
636 emit collapsed(row,
true);
639void QQuickTreeView::toggleExpanded(
int row)
647QModelIndex QQuickTreeView::modelIndex(
const QPoint &cell)
const
649 Q_D(
const QQuickTreeView);
650 const QModelIndex tableIndex = d->m_treeModelToTableModel.index(cell.y(), cell.x());
651 return d->m_treeModelToTableModel.mapToModel(tableIndex);
654QPoint QQuickTreeView::cellAtIndex(
const QModelIndex &index)
const
656 const QModelIndex tableIndex = d_func()->m_treeModelToTableModel.mapFromModel(index);
657 return QPoint(tableIndex.column(), tableIndex.row());
660#if QT_DEPRECATED_SINCE(6
, 4
)
661QModelIndex QQuickTreeView::modelIndex(
int row,
int column)
const
663 static const bool compat6_4 = qEnvironmentVariable(
"QT_QUICK_TABLEVIEW_COMPAT_VERSION") == QStringLiteral(
"6.4");
670 return modelIndex({row, column});
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});
680void QQuickTreeView::keyPressEvent(QKeyEvent *event)
684 if (!keyNavigationEnabled())
686 if (!selectionModel())
689 const int row = cellAtIndex(selectionModel()->currentIndex()).y();
690 switch (event->key()) {
703 if (!event->isAccepted()) {
705 QQuickTableView::keyPressEvent(event);
711#include "moc_qquicktreeview_p.cpp"
static const int kTreeColumn
\qmlproperty QModelIndex QtQuick::TreeView::rootIndex