8#include <QtCore/qloggingcategory.h>
10#include <QtQml/qqmlinfo.h>
11#include <QtQml/qqmlengine.h>
15Q_STATIC_LOGGING_CATEGORY(lcTreeModel,
"qt.qml.treemodel")
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
95QQmlTreeModel::QQmlTreeModel(QObject *parent)
96 : QQmlAbstractColumnModel(parent)
100QQmlTreeModel::~QQmlTreeModel() =
default;
103
104
105
106
107
108
109QVariant QQmlTreeModel::rows()
const
111 QVariantList rowsAsVariant;
112 for (
const auto &row : mRows)
113 rowsAsVariant.append(row->toVariant());
115 return rowsAsVariant;
118void QQmlTreeModel::setRows(
const QVariant &rows)
120 const std::optional<QVariantList> validated = validateRowsArgument(rows);
124 const QVariantList rowsAsVariantList = *validated;
126 if (!mComponentCompleted) {
128 mInitialRows = rowsAsVariantList;
132 setRowsPrivate(rowsAsVariantList);
135void QQmlTreeModel::setRowsPrivate(
const QVariantList &rowsAsVariantList)
137 Q_ASSERT(mComponentCompleted);
140 if (mColumns.isEmpty()) {
141 qmlWarning(
this) <<
"No TableModelColumns were set; model will be empty";
145 const bool firstTimeValidRowsHaveBeenSet = mColumnMetadata.isEmpty();
146 if (!firstTimeValidRowsHaveBeenSet) {
148 for (
const auto &row : rowsAsVariantList) {
151 const QVariant wrappedRow = QVariant::fromValue(row);
152 if (!validateNewRow(
"TreeModel::setRows"_L1, wrappedRow, SetRowsOperation))
161 const bool wasEmpty = mRows.empty();
167 for (
const auto &rowAsVariant : rowsAsVariantList) {
168 if (rowAsVariant.canConvert<QVariantMap>())
169 mRows.push_back(std::make_unique<QQmlTreeRow>(rowAsVariant));
171 qmlWarning(
this) <<
"Cannot create tree row as the row does not contain "
172 <<
"key-value pairs";
177 if (firstTimeValidRowsHaveBeenSet && (!mRows.empty() || !mInitialRows.isEmpty()))
178 fetchColumnMetadata();
187 if (!wasEmpty || !mRows.empty())
191QVariant QQmlTreeModel::dataPrivate(
const QModelIndex &index,
const QString &roleName)
const
193 const ColumnMetadata columnMetadata = mColumnMetadata.at(index.column());
194 const QString propertyName = columnMetadata.roles.value(roleName).name;
195 const auto *thisRow =
static_cast<
const QQmlTreeRow *>(index.internalPointer());
196 return thisRow->data(propertyName);
199void QQmlTreeModel::setDataPrivate(
const QModelIndex &index,
const QString &roleName, QVariant value)
201 auto *row =
static_cast<QQmlTreeRow *>(index.internalPointer());
202 row->setField(roleName, value);
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242void QQmlTreeModel::appendRow(QModelIndex parent,
const QVariant &row)
244 if (!validateNewRow(
"TreeModel::appendRow"_L1, row))
247 const QVariant data = row.userType() == QMetaType::QVariantMap ? row : row.value<QJSValue>().toVariant();
249 if (parent.isValid()) {
250 auto *parentRow =
static_cast<QQmlTreeRow *>(parent.internalPointer());
251 auto *newChild =
new QQmlTreeRow(data);
253 beginInsertRows(parent,
static_cast<
int>(parentRow->rowCount()),
static_cast<
int>(parentRow->rowCount()));
254 parentRow->addChild(newChild);
257 if (mColumnMetadata.isEmpty())
258 fetchColumnMetadata();
262 qmlWarning(
this) <<
"append: could not find any node at the specified index"
263 <<
" - the new row will be appended to root";
265 if (data.canConvert<QVariantMap>()) {
266 beginInsertRows(QModelIndex(),
267 static_cast<
int>(mRows.size()),
268 static_cast<
int>(mRows.size()));
270 mRows.push_back(std::make_unique<QQmlTreeRow>(data));
273 if (mColumnMetadata.isEmpty())
274 fetchColumnMetadata();
278 qmlWarning(
this) <<
"Cannot create tree row as the row does not contain "
279 <<
"key-value pairs";
288
289
290
291
292
293
294void QQmlTreeModel::appendRow(
const QVariant &row)
300
301
302
303
304
305
306void QQmlTreeModel::clear()
318
319
320
321
322
323
324
325
326
327
328QVariant QQmlTreeModel::getRow(
const QModelIndex &rowIndex)
const
330 if (rowIndex.isValid())
331 return static_cast<QQmlTreeRow*>(rowIndex.internalPointer())->toVariant();
333 qmlWarning(
this) <<
"getRow: could not find any node at the specified index";
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
357void QQmlTreeModel::insertRow(
int rowIndex, QModelIndex parent,
const QVariant &row)
360 qmlWarning(
this).noquote() <<
"insertRow(): rowIndex cannot be negative";
363 if (rowIndex > rowCount(parent)) {
364 qmlWarning(
this).noquote() <<
"insertRow(): rowIndex " << rowIndex
365 <<
" is greater than rowCount() of "
369 if (!validateNewRow(
"insertRow()"_L1, row))
372 doInsert(parent, rowIndex, row);
376
377
378
379
380
381
382
383
384void QQmlTreeModel::insertRow(
int rowIndex,
const QVariant &row)
386 insertRow(rowIndex, {}, row);
389void QQmlTreeModel::doInsert(
const QModelIndex &parent,
int rowIndex,
const QVariant &row)
391 beginInsertRows(parent, rowIndex, rowIndex);
393 const QVariant data =
394 row.userType() == QMetaType::QVariantMap
396 : row.value<QJSValue>().toVariant();
398 auto newChild = std::make_unique<QQmlTreeRow>(data);
399 if (parent.isValid())
400 static_cast<QQmlTreeRow *>(parent.internalPointer())->insertChild(rowIndex, std::move(newChild));
402 mRows.insert(mRows.begin() + rowIndex, std::move(newChild));
404 qCDebug(lcTreeModel).nospace() <<
"inserted the following row to the row "
405 << parent <<
" at index "
406 << rowIndex <<
":\n" << data.toMap();
409 if (mColumnMetadata.isEmpty())
410 fetchColumnMetadata();
416QVariant QQmlTreeModel::firstRow()
const
419 qmlWarning(
this) <<
"TreeModel::firstRow(): model is empty";
423 return mRows.front().get()->data();
426void QQmlTreeModel::setInitialRows()
428 setRowsPrivate(mInitialRows);
432
433
434
435
436
437
438
439
440
441
442
443void QQmlTreeModel::removeRow(QModelIndex rowIndex)
445 if (rowIndex.isValid()) {
446 QModelIndex mIndexParent = rowIndex.parent();
448 beginRemoveRows(mIndexParent, rowIndex.row(), rowIndex.row());
450 if (mIndexParent.isValid()) {
451 auto *parent =
static_cast<QQmlTreeRow *>(mIndexParent.internalPointer());
452 parent->removeChildAt(rowIndex.row());
454 mRows.erase(std::next(mRows.begin(), rowIndex.row()));
459 qmlWarning(
this) <<
"TreeModel::removeRow could not find any node at the specified index";
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492void QQmlTreeModel::setRow(QModelIndex rowIndex,
const QVariant &rowData)
494 if (!rowIndex.isValid()) {
495 qmlWarning(
this) <<
"TreeModel::setRow: invalid modelIndex";
499 const QVariantMap rowAsMap = rowData.toMap();
500 if (rowAsMap.contains(ROWS_PROPERTY_NAME) && rowAsMap[ROWS_PROPERTY_NAME].userType() == QMetaType::Type::QVariantList) {
501 qmlWarning(
this) <<
"TreeModel::setRow: child rows are not allowed";
505 if (!validateNewRow(
"TreeModel::setRow"_L1, rowData))
508 const QVariant rowAsVariant = rowData.userType() == QMetaType::QVariantMap ? rowData : rowData.value<QJSValue>().toVariant();
509 auto *row =
static_cast<QQmlTreeRow *>(rowIndex.internalPointer());
510 row->setData(rowAsVariant);
512 const QModelIndex topLeftModelIndex(createIndex(rowIndex.row(), 0, rowIndex.internalPointer()));
513 const QModelIndex bottomRightModelIndex(createIndex(rowIndex.row(), mColumnCount-1, rowIndex.internalPointer()));
515 emit dataChanged(topLeftModelIndex, bottomRightModelIndex);
520
521
522
523
524
525
526
527
528
529
530QModelIndex QQmlTreeModel::index(
int row,
int column,
const QModelIndex &parent)
const
532 if (!parent.isValid()){
533 if (
static_cast<size_t>(row) >= mRows.size())
536 return createIndex(row, column, mRows.at(row).get());
539 const auto *treeRow =
static_cast<
const QQmlTreeRow *>(parent.internalPointer());
540 if (treeRow->rowCount() <=
static_cast<size_t>(row))
543 return createIndex(row, column, treeRow->getRow(row));
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593QModelIndex QQmlTreeModel::index(
const std::vector<
int> &treeIndex,
int column)
596 QQmlTreeRow *row = getPointerToTreeRow(mIndex, treeIndex);
599 return createIndex(treeIndex.back(), column, row);
601 qmlWarning(
this) <<
"TreeModel::index: could not find any node at the specified index: "
606QModelIndex QQmlTreeModel::parent(
const QModelIndex &index)
const
608 if (!index.isValid())
611 const auto *thisRow =
static_cast<
const QQmlTreeRow *>(index.internalPointer());
612 const QQmlTreeRow *parentRow = thisRow->parent();
617 const QQmlTreeRow *grandparentRow = parentRow->parent();
619 if (!grandparentRow) {
620 for (size_t i = 0; i < mRows.size(); i++) {
621 if (mRows[i].get() == parentRow)
622 return createIndex(
static_cast<
int>(i), 0, parentRow);
624 Q_UNREACHABLE_RETURN(QModelIndex());
627 for (size_t i = 0; i < grandparentRow->rowCount(); i++) {
628 if (grandparentRow->getRow(
static_cast<
int>(i)) == parentRow)
629 return createIndex(
static_cast<
int>(i), 0, parentRow);
631 Q_UNREACHABLE_RETURN(QModelIndex());
635int QQmlTreeModel::rowCount(
const QModelIndex &parent)
const
637 if (!parent.isValid())
638 return static_cast<
int>(mRows.size());
640 const auto *row =
static_cast<
const QQmlTreeRow *>(parent.internalPointer());
641 return static_cast<
int>(row->rowCount());
645
646
647
648
649
650
651
652
653
654int QQmlTreeModel::columnCount(
const QModelIndex &parent)
const
662
663
664
665
666
667
668
669
670
673
674
675
676
677
678
679
680
681
683bool QQmlTreeModel::validateNewRow(QLatin1StringView functionName,
const QVariant &row,
684 NewRowOperationFlag operation)
const
686 const bool isVariantMap = (row.userType() == QMetaType::QVariantMap);
687 const QVariant rowAsVariant = operation == SetRowsOperation || isVariantMap
688 ? row : row.value<QJSValue>().toVariant();
689 const QVariantMap rowAsMap = rowAsVariant.toMap();
690 if (rowAsMap.contains(ROWS_PROPERTY_NAME) && rowAsMap[ROWS_PROPERTY_NAME].userType() == QMetaType::Type::QVariantList)
692 const QList<QVariant> variantList = rowAsMap[ROWS_PROPERTY_NAME].toList();
693 for (
const QVariant &rowAsVariant : variantList)
694 if (!validateNewRow(functionName, rowAsVariant))
698 return QQmlAbstractColumnModel::validateNewRow(functionName, row, operation);
701int QQmlTreeModel::treeSize()
const
705 for (
const auto &treeRow : mRows)
706 treeSize += treeRow->subTreeSize();
711QQmlTreeRow *QQmlTreeModel::getPointerToTreeRow(QModelIndex &modIndex,
712 const std::vector<
int> &rowIndex)
const
714 for (
int r : rowIndex) {
715 modIndex = index(r, 0, modIndex);
716 if (!modIndex.isValid())
720 return static_cast<QQmlTreeRow*>(modIndex.internalPointer());
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743bool QQmlTreeModel::moveRows(
const QModelIndex &sourceParent,
int sourceRow,
int count,
744 const QModelIndex &destinationParent,
int destinationChild)
751 if (sourceRow < 0 || sourceRow + count > rowCount(sourceParent))
753 if (destinationChild < 0 || destinationChild > rowCount(destinationParent))
756 if (!beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1,
757 destinationParent, destinationChild))
762 auto takeChild = [
this](
const QModelIndex &parent,
int row) -> std::unique_ptr<QQmlTreeRow> {
763 if (parent.isValid())
764 return static_cast<QQmlTreeRow *>(parent.internalPointer())->takeChild(row);
765 auto it = std::next(mRows.begin(), row);
766 std::unique_ptr<QQmlTreeRow> child = std::move(*it);
771 auto insertChild = [
this](
const QModelIndex &parent,
int row, std::unique_ptr<QQmlTreeRow> child) {
772 if (parent.isValid()) {
773 static_cast<QQmlTreeRow *>(parent.internalPointer())->insertChild(row, std::move(child));
775 child->setParent(
nullptr);
776 mRows.insert(std::next(mRows.begin(), row), std::move(child));
780 std::vector<std::unique_ptr<QQmlTreeRow>> buffer;
781 buffer.reserve(count);
782 for (
int i = 0; i < count; ++i)
783 buffer.push_back(takeChild(sourceParent, sourceRow));
788 int insertAt = destinationChild;
789 if (sourceParent == destinationParent && destinationChild > sourceRow)
792 for (
int i = 0; i < count; ++i)
793 insertChild(destinationParent, insertAt + i, std::move(buffer[i]));
802#include "moc_qqmltreemodel_p.cpp"
Combined button and popup list for selecting options.