Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qquicktreeview.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <QtCore/qobject.h>
7#include <QtQml/qqmlcontext.h>
8#include <QtQuick/private/qquicktaphandler_p.h>
9
10#include <QtQmlModels/private/qqmltreemodeltotablemodel_p_p.h>
11
258// Hard-code the tree column to be 0 for now
259static const int kTreeColumn = 0;
260
262
267
271
276
278{
279 Q_Q(QQuickTreeView);
280
281 m_assignedModel = newModel;
282 QVariant effectiveModel = m_assignedModel;
283 if (effectiveModel.userType() == qMetaTypeId<QJSValue>())
284 effectiveModel = effectiveModel.value<QJSValue>().toVariant();
285
286 if (effectiveModel.isNull())
288 else if (const auto qaim = qvariant_cast<QAbstractItemModel*>(effectiveModel))
290 else
291 qmlWarning(q) << "TreeView only accepts a model of type QAbstractItemModel";
292
293
295 emit q->modelChanged();
296}
297
298void QQuickTreeViewPrivate::initItemCallback(int serializedModelIndex, QObject *object)
299{
300 updateRequiredProperties(serializedModelIndex, object, true);
301 QQuickTableViewPrivate::initItemCallback(serializedModelIndex, object);
302}
303
304void QQuickTreeViewPrivate::itemReusedCallback(int serializedModelIndex, QObject *object)
305{
306 updateRequiredProperties(serializedModelIndex, object, false);
307 QQuickTableViewPrivate::itemReusedCallback(serializedModelIndex, object);
308}
309
311 const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
312{
313 Q_Q(QQuickTreeView);
314 Q_UNUSED(roles);
315
316 for (int row = topLeft.row(); row <= bottomRight.row(); ++row) {
317 for (int column = topLeft.column(); column <= bottomRight.column(); ++column) {
318 const QPoint cell(column, row);
319 auto item = q->itemAtCell(cell);
320 if (!item)
321 continue;
322
323 const int serializedModelIndex = modelIndexAtCell(QPoint(column, row));
324 updateRequiredProperties(serializedModelIndex, item, false);
325 }
326 }
327}
328
329void QQuickTreeViewPrivate::updateRequiredProperties(int serializedModelIndex, QObject *object, bool init)
330{
331 Q_Q(QQuickTreeView);
332 const QPoint cell = cellAtModelIndex(serializedModelIndex);
333 const int row = cell.y();
334 const int column = cell.x();
335
336 setRequiredProperty("treeView", QVariant::fromValue(q), serializedModelIndex, object, init);
337 setRequiredProperty("isTreeNode", column == kTreeColumn, serializedModelIndex, object, init);
338 setRequiredProperty("hasChildren", m_treeModelToTableModel.hasChildren(row), serializedModelIndex, object, init);
339 setRequiredProperty("expanded", q->isExpanded(row), serializedModelIndex, object, init);
340 setRequiredProperty("depth", m_treeModelToTableModel.depthAtRow(row), serializedModelIndex, object, init);
341}
342
343void QQuickTreeViewPrivate::updateSelection(const QRect &oldSelection, const QRect &newSelection)
344{
345 Q_Q(QQuickTreeView);
346
347 if (oldSelection == newSelection)
348 return;
349
351 QItemSelection deselect;
352
353 // Because each row can have a different parent, we need to create separate QItemSelections
354 // per row. But all the cells in a given row have the same parent, so they can be combined.
355 // As a result, the final QItemSelection can end up more fragmented compared to a selection
356 // in QQuickTableView, where all cells have the same parent. In the end, if TreeView has
357 // a lot of columns and the selection mode is "SelectCells", using the mouse to adjust
358 // a selection containing a _large_ number of columns can be slow.
359 const QRect cells = newSelection.normalized();
360 for (int row = cells.y(); row <= cells.y() + cells.height(); ++row) {
361 const QModelIndex startIndex = q->index(row, cells.x());
362 const QModelIndex endIndex = q->index(row, cells.x() + cells.width());
363 select.merge(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select);
364 }
365
366 const QModelIndexList indexes = selectionModel->selection().indexes();
367 for (const QModelIndex &index : indexes) {
368 if (!select.contains(index) && !existingSelection.contains(index))
370 }
371
375 } else {
376 QItemSelection oldSelection = existingSelection;
380 }
381}
382
384 : QQuickTableView(*(new QQuickTreeViewPrivate), parent)
385{
386 Q_D(QQuickTreeView);
387
390
391 // Note: QQuickTableView will only ever see the table model m_treeModelToTableModel, and
392 // never the actual tree model that is assigned to us by the application.
393 const auto modelAsVariant = QVariant::fromValue(std::addressof(d->m_treeModelToTableModel));
394 d->QQuickTableViewPrivate::setModelImpl(modelAsVariant);
398 this, &QQuickTreeView::rootIndexChanged);
399
400 auto tapHandler = new QQuickTapHandler(this);
401 tapHandler->setAcceptedModifiers(Qt::NoModifier);
402 connect(tapHandler, &QQuickTapHandler::doubleTapped, [this, tapHandler]{
404 return;
406 return;
407
408 const int row = cellAtPosition(tapHandler->point().pressPosition()).y();
410 });
411}
412
416
418{
419 return d_func()->m_treeModelToTableModel.rootIndex();
420}
421
423{
424 Q_D(QQuickTreeView);
425 d->m_treeModelToTableModel.setRootIndex(index);
427}
428
430{
431 Q_D(QQuickTreeView);
432 d->m_treeModelToTableModel.resetRootIndex();
434}
435
436int QQuickTreeView::depth(int row) const
437{
438 Q_D(const QQuickTreeView);
439 if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
440 return -1;
441
442 return d->m_treeModelToTableModel.depthAtRow(row);
443}
444
446{
447 Q_D(const QQuickTreeView);
448 if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
449 return false;
450
451 return d->m_treeModelToTableModel.isExpanded(row);
452}
453
455{
456 if (row >= 0)
457 expandRecursively(row, 1);
458}
459
460void QQuickTreeView::expandRecursively(int row, int depth)
461{
462 Q_D(QQuickTreeView);
463 if (row >= d->m_treeModelToTableModel.rowCount())
464 return;
465 if (row < 0 && row != -1)
466 return;
467 if (depth == 0 || depth < -1)
468 return;
469
470 auto expandRowRecursively = [this, d, depth](int startRow) {
471 d->m_treeModelToTableModel.expandRecursively(startRow, depth);
472 // Update the expanded state of the startRow. The descendant rows that gets
473 // expanded will get the correct state set from initItem/itemReused instead.
474 for (int c = leftColumn(); c <= rightColumn(); ++c) {
475 const QPoint treeNodeCell(c, startRow);
476 if (const auto item = itemAtCell(treeNodeCell))
477 d->setRequiredProperty("expanded", true, d->modelIndexAtCell(treeNodeCell), item, false);
478 }
479 };
480
481 if (row >= 0) {
482 // Expand only one row recursively
483 const bool isExpanded = d->m_treeModelToTableModel.isExpanded(row);
484 if (isExpanded && depth == 1)
485 return;
486 expandRowRecursively(row);
487 } else {
488 // Expand all root nodes recursively
489 const auto model = d->m_treeModelToTableModel.model();
490 for (int r = 0; r < model->rowCount(); ++r) {
491 const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0));
492 if (rootRow != -1)
493 expandRowRecursively(rootRow);
494 }
495 }
496
498}
499
500void QQuickTreeView::expandToIndex(const QModelIndex &index)
501{
502 Q_D(QQuickTreeView);
503
504 if (!index.isValid()) {
505 qmlWarning(this) << "index is not valid: " << index;
506 return;
507 }
508
509 if (index.model() != d->m_treeModelToTableModel.model()) {
510 qmlWarning(this) << "index doesn't belong to correct model: " << index;
511 return;
512 }
513
514 if (rowAtIndex(index) != -1) {
515 // index is already visible
516 return;
517 }
518
519 int depth = 1;
521 int row = rowAtIndex(parent);
522
523 while (parent.isValid()) {
524 if (row != -1) {
525 // The node is already visible, since it maps to a row in the table!
526 d->m_treeModelToTableModel.expandRow(row);
527
528 // Update the state of the already existing delegate item
529 for (int c = leftColumn(); c <= rightColumn(); ++c) {
530 const QPoint treeNodeCell(c, row);
531 if (const auto item = itemAtCell(treeNodeCell))
532 d->setRequiredProperty("expanded", true, d->modelIndexAtCell(treeNodeCell), item, false);
533 }
534
535 // When we hit a node that is visible, we know that all other nodes
536 // up to the parent have to be visible as well, so we can stop.
537 break;
538 } else {
539 d->m_treeModelToTableModel.expand(parent);
540 parent = parent.parent();
541 row = rowAtIndex(parent);
542 depth++;
543 }
544 }
545
547}
548
550{
551 Q_D(QQuickTreeView);
552 if (row < 0 || row >= d->m_treeModelToTableModel.rowCount())
553 return;
554
555 if (!d->m_treeModelToTableModel.isExpanded(row))
556 return;
557
558 d_func()->m_treeModelToTableModel.collapseRow(row);
559
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", false, d->modelIndexAtCell(treeNodeCell), item, false);
564 }
565
566 emit collapsed(row, false);
567}
568
569void QQuickTreeView::collapseRecursively(int row)
570{
571 Q_D(QQuickTreeView);
572 if (row >= d->m_treeModelToTableModel.rowCount())
573 return;
574 if (row < 0 && row != -1)
575 return;
576
577 auto collapseRowRecursive = [this, d](int startRow) {
578 // Always collapse descendants recursively,
579 // even if the top row itself is already collapsed.
580 d->m_treeModelToTableModel.collapseRecursively(startRow);
581 // Update the expanded state of the (still visible) startRow
582 for (int c = leftColumn(); c <= rightColumn(); ++c) {
583 const QPoint treeNodeCell(c, startRow);
584 if (const auto item = itemAtCell(treeNodeCell))
585 d->setRequiredProperty("expanded", false, d->modelIndexAtCell(treeNodeCell), item, false);
586 }
587 };
588
589 if (row >= 0) {
590 collapseRowRecursive(row);
591 } else {
592 // Collapse all root nodes recursively
593 const auto model = d->m_treeModelToTableModel.model();
594 for (int r = 0; r < model->rowCount(); ++r) {
595 const int rootRow = d->m_treeModelToTableModel.itemIndex(model->index(r, 0));
596 if (rootRow != -1)
597 collapseRowRecursive(rootRow);
598 }
599 }
600
601 emit collapsed(row, true);
602}
603
605{
606 if (isExpanded(row))
607 collapse(row);
608 else
609 expand(row);
610}
611
613{
614 Q_D(const QQuickTreeView);
615 const QModelIndex tableIndex = d->m_treeModelToTableModel.index(cell.y(), cell.x());
616 return d->m_treeModelToTableModel.mapToModel(tableIndex);
617}
618
620{
621 const QModelIndex tableIndex = d_func()->m_treeModelToTableModel.mapFromModel(index);
622 return QPoint(tableIndex.column(), tableIndex.row());
623}
624
625#if QT_DEPRECATED_SINCE(6, 4)
627{
628 static const bool compat6_4 = qEnvironmentVariable("QT_QUICK_TABLEVIEW_COMPAT_VERSION") == QStringLiteral("6.4");
629 if (compat6_4) {
630 // XXX Qt 7: Remove this compatibility path here and in QQuickTableView.
631 // In Qt 6.4.0 and 6.4.1, a source incompatible change led to row and column
632 // being documented to be specified in the opposite order.
633 // QT_QUICK_TABLEVIEW_COMPAT_VERSION can therefore be set to force tableview
634 // to continue accepting calls to modelIndex(column, row).
635 return modelIndex({row, column});
636 } else {
637 qmlWarning(this) << "modelIndex(row, column) is deprecated. "
638 "Use index(row, column) instead. For more information, see "
639 "https://doc.qt.io/qt-6/qml-qtquick-tableview-obsolete.html";
640 return modelIndex({column, row});
641 }
642}
643#endif
644
646{
647 event->ignore();
648
650 return;
651 if (!selectionModel())
652 return;
653
654 const int row = cellAtIndex(selectionModel()->currentIndex()).y();
655 switch (event->key()) {
656 case Qt::Key_Left:
657 collapse(row);
658 event->accept();
659 break;
660 case Qt::Key_Right:
661 expand(row);
662 event->accept();
663 break;
664 default:
665 break;
666 }
667
668 if (!event->isAccepted())
670}
671
673
674#include "moc_qquicktreeview_p.cpp"
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles=QList< int >())
This signal is emitted whenever the data in an existing item changes.
virtual void select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
Selects the model item index using the specified command, and emits selectionChanged().
\inmodule QtCore
Q_CORE_EXPORT QModelIndexList indexes() const
Returns a list of model indexes that correspond to the selected items.
Q_CORE_EXPORT void merge(const QItemSelection &other, QItemSelectionModel::SelectionFlags command)
Merges the other selection with this QItemSelection using the command given.
Q_CORE_EXPORT bool contains(const QModelIndex &index) const
Returns true if the selection contains the given index; otherwise returns false.
The QJSValue class acts as a container for Qt/JavaScript data types.
Definition qjsvalue.h:31
The QKeyEvent class describes a key event.
Definition qevent.h:424
\inmodule QtCore
constexpr int row() const noexcept
Returns the row this model index refers to.
constexpr int column() const noexcept
Returns the column this model index refers to.
static QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer< Func1 >::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer< Func2 >::Object *receiverPrivate, Func2 slot, Qt::ConnectionType type=Qt::AutoConnection)
Definition qobject_p.h:299
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
\inmodule QtCore\reentrant
Definition qpoint.h:25
constexpr int x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:130
constexpr int y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:135
void setModel(QAbstractItemModel *model)
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:63
QQuickItem * parent
\qmlproperty Item QtQuick::Item::parent This property holds the visual parent of the item.
Definition qquickitem.h:67
QPoint cellAtModelIndex(int modelIndex) const
virtual void itemReusedCallback(int modelIndex, QObject *object)
void scheduleRebuildTable(QQuickTableViewPrivate::RebuildOptions options)
QItemSelectionModel::SelectionFlag selectionFlag
virtual void initItemCallback(int modelIndex, QObject *item)
QAbstractItemModel * qaim(QVariant modelAsVariant) const
int modelIndexAtCell(const QPoint &cell) const
QPointer< QItemSelectionModel > selectionModel
void setRequiredProperty(const char *property, const QVariant &value, int serializedModelIndex, QObject *object, bool init)
void setSelectionBehavior(SelectionBehavior selectionBehavior)
QItemSelectionModel * selectionModel
void setEditTriggers(EditTriggers editTriggers)
FINALEditTriggers editTriggers
Q_INVOKABLE QQuickItem * itemAtCell(const QPoint &cell) const
Q_INVOKABLE void positionViewAtCell(const QPoint &cell, PositionMode mode, const QPointF &offset=QPointF(), const QRectF &subRect=QRectF())
void keyPressEvent(QKeyEvent *e) override
This event handler can be reimplemented in a subclass to receive key press events for an item.
void doubleTapped(QEventPoint eventPoint, Qt::MouseButton)
void updateSelection(const QRect &oldSelection, const QRect &newSelection) override
QVariant modelImpl() const override
void setModelImpl(const QVariant &newModel) override
QQmlTreeModelToTableModel m_treeModelToTableModel
void updateRequiredProperties(int serializedModelIndex, QObject *object, bool init)
void initItemCallback(int serializedModelIndex, QObject *object) override
void itemReusedCallback(int serializedModelIndex, QObject *object) override
void dataChangedCallback(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector< int > &roles)
Q_INVOKABLE void collapse(int row)
Q_INVOKABLE void expand(int row)
Q_INVOKABLE bool isExpanded(int row) const
void expanded(int row, int depth)
QQuickTreeView(QQuickItem *parent=nullptr)
QModelIndex rootIndex
Q_INVOKABLE void toggleExpanded(int row)
void keyPressEvent(QKeyEvent *event) override
This event handler can be reimplemented in a subclass to receive key press events for an item.
void setRootIndex(const QModelIndex &index)
void collapsed(int row, bool recursively)
Q_INVOKABLE QModelIndex modelIndex(const QPoint &cell) const override
~QQuickTreeView() override
Q_INVOKABLE QPoint cellAtIndex(const QModelIndex &index) const override
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr int height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:239
QRect normalized() const noexcept
Returns a normalized rectangle; i.e., a rectangle that has a non-negative width and height.
Definition qrect.cpp:277
constexpr int x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:185
constexpr int width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:236
constexpr int y() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:188
\inmodule QtCore
Definition qvariant.h:65
T value() const &
Definition qvariant.h:516
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:536
Combined button and popup list for selecting options.
@ Key_Right
Definition qnamespace.h:679
@ Key_Left
Definition qnamespace.h:677
@ NoModifier
GLint GLenum GLsizei GLsizei GLsizei depth
GLuint index
[2]
GLboolean r
[2]
GLenum GLenum GLsizei void GLsizei void * column
struct _cl_event * event
const GLubyte * c
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLenum GLenum GLsizei void * row
Q_QML_EXPORT QQmlInfo qmlWarning(const QObject *me)
static const int kTreeColumn
\qmltype TreeView \inqmlmodule QtQuick
#define QStringLiteral(str)
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
#define emit
#define Q_UNUSED(x)
static QVariant toVariant(const QV4::Value &value, QMetaType typeHint, JSToQVariantConversionBehavior conversionBehavior, V4ObjectSet *visitedObjects)
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
QGraphicsItem * item
selection select(topLeft, bottomRight)