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);
121 if (!validated || rowConversionFailed(
"TreeModel::setRows"_L1))
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)
246 const QVariant data =
247 row.userType() == QMetaType::QVariantMap
249 : row.value<QJSValue>().toVariant();
251 if (rowConversionFailed(
"TreeModel::appendRow"_L1))
254 if (!validateNewRow(
"TreeModel::appendRow"_L1, row))
257 if (parent.isValid()) {
258 auto *parentRow =
static_cast<QQmlTreeRow *>(parent.internalPointer());
259 auto *newChild =
new QQmlTreeRow(data);
261 beginInsertRows(parent,
static_cast<
int>(parentRow->rowCount()),
static_cast<
int>(parentRow->rowCount()));
262 parentRow->addChild(newChild);
265 if (mColumnMetadata.isEmpty())
266 fetchColumnMetadata();
270 qmlWarning(
this) <<
"append: could not find any node at the specified index"
271 <<
" - the new row will be appended to root";
273 if (data.canConvert<QVariantMap>()) {
274 beginInsertRows(QModelIndex(),
275 static_cast<
int>(mRows.size()),
276 static_cast<
int>(mRows.size()));
278 mRows.push_back(std::make_unique<QQmlTreeRow>(data));
281 if (mColumnMetadata.isEmpty())
282 fetchColumnMetadata();
286 qmlWarning(
this) <<
"Cannot create tree row as the row does not contain "
287 <<
"key-value pairs";
296
297
298
299
300
301
302void QQmlTreeModel::appendRow(
const QVariant &row)
308
309
310
311
312
313
314void QQmlTreeModel::clear()
326
327
328
329
330
331
332
333
334
335
336QVariant QQmlTreeModel::getRow(
const QModelIndex &rowIndex)
const
338 if (rowIndex.isValid())
339 return static_cast<QQmlTreeRow*>(rowIndex.internalPointer())->toVariant();
341 qmlWarning(
this) <<
"getRow: could not find any node at the specified index";
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
365void QQmlTreeModel::insertRow(
int rowIndex, QModelIndex parent,
const QVariant &row)
368 qmlWarning(
this).noquote() <<
"insertRow(): rowIndex cannot be negative";
371 if (rowIndex > rowCount(parent)) {
372 qmlWarning(
this).noquote() <<
"insertRow(): rowIndex " << rowIndex
373 <<
" is greater than rowCount() of "
381 const QVariant data =
382 row.userType() == QMetaType::QVariantMap
384 : row.value<QJSValue>().toVariant();
385 if (rowConversionFailed(
"insertRow()"_L1))
388 if (!validateNewRow(
"insertRow()"_L1, row))
391 doInsert(parent, rowIndex, data);
395
396
397
398
399
400
401
402
403void QQmlTreeModel::insertRow(
int rowIndex,
const QVariant &row)
405 insertRow(rowIndex, {}, row);
408void QQmlTreeModel::doInsert(
const QModelIndex &parent,
int rowIndex,
const QVariant &row)
410 beginInsertRows(parent, rowIndex, rowIndex);
412 const QVariant data =
413 row.userType() == QMetaType::QVariantMap
415 : row.value<QJSValue>().toVariant();
417 auto newChild = std::make_unique<QQmlTreeRow>(data);
418 if (parent.isValid())
419 static_cast<QQmlTreeRow *>(parent.internalPointer())->insertChild(rowIndex, std::move(newChild));
421 mRows.insert(mRows.begin() + rowIndex, std::move(newChild));
423 qCDebug(lcTreeModel).nospace() <<
"inserted the following row to the row "
424 << parent <<
" at index "
425 << rowIndex <<
" (child rows omitted):\n"
426 << [&data] { QVariantMap map = data.toMap(); map.remove(ROWS_PROPERTY_NAME);
return map; }();
429 if (mColumnMetadata.isEmpty())
430 fetchColumnMetadata();
436QVariant QQmlTreeModel::firstRow()
const
439 qmlWarning(
this) <<
"TreeModel::firstRow(): model is empty";
443 return mRows.front().get()->data();
446void QQmlTreeModel::setInitialRows()
448 setRowsPrivate(mInitialRows);
452
453
454
455
456
457
458
459
460
461
462
463void QQmlTreeModel::removeRow(QModelIndex rowIndex)
465 if (rowIndex.isValid()) {
466 QModelIndex mIndexParent = rowIndex.parent();
468 beginRemoveRows(mIndexParent, rowIndex.row(), rowIndex.row());
470 if (mIndexParent.isValid()) {
471 auto *parent =
static_cast<QQmlTreeRow *>(mIndexParent.internalPointer());
472 parent->removeChildAt(rowIndex.row());
474 mRows.erase(std::next(mRows.begin(), rowIndex.row()));
479 qmlWarning(
this) <<
"TreeModel::removeRow could not find any node at the specified index";
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512void QQmlTreeModel::setRow(QModelIndex rowIndex,
const QVariant &rowData)
514 if (!rowIndex.isValid()) {
515 qmlWarning(
this) <<
"TreeModel::setRow: invalid modelIndex";
519 const QVariantMap rowAsMap = rowData.toMap();
520 if (rowAsMap.contains(ROWS_PROPERTY_NAME) && rowAsMap[ROWS_PROPERTY_NAME].userType() == QMetaType::Type::QVariantList) {
521 qmlWarning(
this) <<
"TreeModel::setRow: child rows are not allowed";
525 if (!validateNewRow(
"TreeModel::setRow"_L1, rowData))
528 const QVariant rowAsVariant = rowData.userType() == QMetaType::QVariantMap ? rowData : rowData.value<QJSValue>().toVariant();
529 if (rowConversionFailed(
"TreeModel::setRow"_L1))
532 auto *row =
static_cast<QQmlTreeRow *>(rowIndex.internalPointer());
533 row->setData(rowAsVariant);
535 const QModelIndex topLeftModelIndex(createIndex(rowIndex.row(), 0, rowIndex.internalPointer()));
536 const QModelIndex bottomRightModelIndex(createIndex(rowIndex.row(), mColumnCount-1, rowIndex.internalPointer()));
538 emit dataChanged(topLeftModelIndex, bottomRightModelIndex);
543
544
545
546
547
548
549
550
551
552
553QModelIndex QQmlTreeModel::index(
int row,
int column,
const QModelIndex &parent)
const
555 if (!parent.isValid()){
556 if (
static_cast<size_t>(row) >= mRows.size())
559 return createIndex(row, column, mRows.at(row).get());
562 const auto *treeRow =
static_cast<
const QQmlTreeRow *>(parent.internalPointer());
563 if (treeRow->rowCount() <=
static_cast<size_t>(row))
566 return createIndex(row, column, treeRow->getRow(row));
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616QModelIndex QQmlTreeModel::index(
const std::vector<
int> &treeIndex,
int column)
619 QQmlTreeRow *row = getPointerToTreeRow(mIndex, treeIndex);
622 return createIndex(treeIndex.back(), column, row);
624 qmlWarning(
this) <<
"TreeModel::index: could not find any node at the specified index: "
629QModelIndex QQmlTreeModel::parent(
const QModelIndex &index)
const
631 if (!index.isValid())
634 const auto *thisRow =
static_cast<
const QQmlTreeRow *>(index.internalPointer());
635 const QQmlTreeRow *parentRow = thisRow->parent();
640 const QQmlTreeRow *grandparentRow = parentRow->parent();
642 if (!grandparentRow) {
643 for (size_t i = 0; i < mRows.size(); i++) {
644 if (mRows[i].get() == parentRow)
645 return createIndex(
static_cast<
int>(i), 0, parentRow);
647 Q_UNREACHABLE_RETURN(QModelIndex());
650 for (size_t i = 0; i < grandparentRow->rowCount(); i++) {
651 if (grandparentRow->getRow(
static_cast<
int>(i)) == parentRow)
652 return createIndex(
static_cast<
int>(i), 0, parentRow);
654 Q_UNREACHABLE_RETURN(QModelIndex());
658int QQmlTreeModel::rowCount(
const QModelIndex &parent)
const
660 if (!parent.isValid())
661 return static_cast<
int>(mRows.size());
663 const auto *row =
static_cast<
const QQmlTreeRow *>(parent.internalPointer());
664 return static_cast<
int>(row->rowCount());
668
669
670
671
672
673
674
675
676
677int QQmlTreeModel::columnCount(
const QModelIndex &parent)
const
685
686
687
688
689
690
691
692
693
696
697
698
699
700
701
702
703
704
706bool QQmlTreeModel::validateNewRow(QLatin1StringView functionName,
const QVariant &row,
707 NewRowOperationFlag operation)
const
715 NewRowOperationFlag operation;
716 QVariantList children;
717 qsizetype nextChild = 0;
720 std::vector<Frame> pending;
721 const auto push = [&pending](
const QVariant &newRow, NewRowOperationFlag newRowOperation) {
722 const bool isVariantMap = (newRow.userType() == QMetaType::QVariantMap);
723 const QVariant rowAsVariant = newRowOperation == SetRowsOperation || isVariantMap
724 ? newRow : newRow.value<QJSValue>().toVariant();
725 const QVariantMap rowAsMap = rowAsVariant.toMap();
727 QVariantList children;
728 const auto it = rowAsMap.constFind(ROWS_PROPERTY_NAME);
729 if (it != rowAsMap.cend() && it->userType() == QMetaType::Type::QVariantList)
730 children = it->toList();
732 pending.push_back({ newRow, newRowOperation, std::move(children) });
735 push(row, operation);
736 while (!pending.empty()) {
737 Frame &top = pending.back();
738 if (top.nextChild < top.children.size()) {
739 const QVariant child = top.children.at(top.nextChild++);
740 push(child, OtherOperation);
744 if (!QQmlAbstractColumnModel::validateNewRow(functionName, top.row, top.operation))
753
754
755
756
757bool QQmlTreeModel::rowConversionFailed(QLatin1StringView functionName)
const
759 const QJSEngine *engine = qjsEngine(
this);
760 if (!engine || !engine->hasError())
763 qmlWarning(
this).nospace() << functionName
764 <<
": rows are nested too deeply to be converted";
768int QQmlTreeModel::treeSize()
const
772 for (
const auto &treeRow : mRows)
773 treeSize += treeRow->subTreeSize();
778QQmlTreeRow *QQmlTreeModel::getPointerToTreeRow(QModelIndex &modIndex,
779 const std::vector<
int> &rowIndex)
const
781 for (
int r : rowIndex) {
782 modIndex = index(r, 0, modIndex);
783 if (!modIndex.isValid())
787 return static_cast<QQmlTreeRow*>(modIndex.internalPointer());
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810bool QQmlTreeModel::moveRows(
const QModelIndex &sourceParent,
int sourceRow,
int count,
811 const QModelIndex &destinationParent,
int destinationChild)
818 if (sourceRow < 0 || sourceRow + count > rowCount(sourceParent))
820 if (destinationChild < 0 || destinationChild > rowCount(destinationParent))
823 if (!beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1,
824 destinationParent, destinationChild))
829 auto takeChild = [
this](
const QModelIndex &parent,
int row) -> std::unique_ptr<QQmlTreeRow> {
830 if (parent.isValid())
831 return static_cast<QQmlTreeRow *>(parent.internalPointer())->takeChild(row);
832 auto it = std::next(mRows.begin(), row);
833 std::unique_ptr<QQmlTreeRow> child = std::move(*it);
838 auto insertChild = [
this](
const QModelIndex &parent,
int row, std::unique_ptr<QQmlTreeRow> child) {
839 if (parent.isValid()) {
840 static_cast<QQmlTreeRow *>(parent.internalPointer())->insertChild(row, std::move(child));
842 child->setParent(
nullptr);
843 mRows.insert(std::next(mRows.begin(), row), std::move(child));
847 std::vector<std::unique_ptr<QQmlTreeRow>> buffer;
848 buffer.reserve(count);
849 for (
int i = 0; i < count; ++i)
850 buffer.push_back(takeChild(sourceParent, sourceRow));
855 int insertAt = destinationChild;
856 if (sourceParent == destinationParent && destinationChild > sourceRow)
859 for (
int i = 0; i < count; ++i)
860 insertChild(destinationParent, insertAt + i, std::move(buffer[i]));
869#include "moc_qqmltreemodel_p.cpp"
Combined button and popup list for selecting options.