Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qtreeview.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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#include "qtreeview.h"
4
5#include <qheaderview.h>
6#include <qitemdelegate.h>
7#include <qapplication.h>
8#include <qscrollbar.h>
9#include <qpainter.h>
10#include <qstack.h>
11#include <qstyle.h>
12#include <qstyleoption.h>
13#include <qevent.h>
14#include <qpen.h>
15#include <qdebug.h>
16#include <QMetaMethod>
17#include <private/qscrollbar_p.h>
18#if QT_CONFIG(accessibility)
19#include <qaccessible.h>
20#endif
21
22#include <private/qapplication_p.h>
23#include <private/qtreeview_p.h>
24#include <private/qheaderview_p.h>
25
26#include <algorithm>
27
29
152{
153 Q_D(QTreeView);
154 d->initialize();
155}
156
162{
163 Q_D(QTreeView);
164 d->initialize();
165}
166
171{
172}
173
178{
179 Q_D(QTreeView);
180 if (model == d->model)
181 return;
182 if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) {
183 disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
184 this, SLOT(rowsRemoved(QModelIndex,int,int)));
185
186 disconnect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_modelAboutToBeReset()));
187 }
188
189 if (d->selectionModel) { // support row editing
190 disconnect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
191 d->model, SLOT(submit()));
192 disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
193 this, SLOT(rowsRemoved(QModelIndex,int,int)));
194 disconnect(d->model, SIGNAL(modelAboutToBeReset()), this, SLOT(_q_modelAboutToBeReset()));
195 }
196 d->viewItems.clear();
197 d->expandedIndexes.clear();
198 d->hiddenIndexes.clear();
199 d->geometryRecursionBlock = true; // do not update geometries due to signals from the headers
200 d->header->setModel(model);
201 d->geometryRecursionBlock = false;
203
204 // QAbstractItemView connects to a private slot
205 disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
206 this, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
207 // do header layout after the tree
208 disconnect(d->model, SIGNAL(layoutChanged()),
209 d->header, SLOT(_q_layoutChanged()));
210 // QTreeView has a public slot for this
211 connect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
212 this, SLOT(rowsRemoved(QModelIndex,int,int)));
213
214 connect(d->model, SIGNAL(modelAboutToBeReset()), SLOT(_q_modelAboutToBeReset()));
215
216 if (d->sortingEnabled)
217 d->_q_sortIndicatorChanged(header()->sortIndicatorSection(), header()->sortIndicatorOrder());
218}
219
224{
225 Q_D(QTreeView);
226 d->header->setRootIndex(index);
228}
229
234{
235 Q_D(QTreeView);
237 if (d->selectionModel) {
238 // support row editing
239 disconnect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
240 d->model, SLOT(submit()));
241 }
242
243 d->header->setSelectionModel(selectionModel);
245
246 if (d->selectionModel) {
247 // support row editing
248 connect(d->selectionModel, SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
249 d->model, SLOT(submit()));
250 }
251}
252
259{
260 Q_D(const QTreeView);
261 return d->header;
262}
263
273{
274 Q_D(QTreeView);
275 if (header == d->header || !header)
276 return;
277 if (d->header && d->header->parent() == this)
278 delete d->header;
279 d->header = header;
280 d->header->setParent(this);
281 d->header->setFirstSectionMovable(false);
282
283 if (!d->header->model()) {
284 d->header->setModel(d->model);
285 if (d->selectionModel)
286 d->header->setSelectionModel(d->selectionModel);
287 }
288
289 connect(d->header, SIGNAL(sectionResized(int,int,int)),
290 this, SLOT(columnResized(int,int,int)));
291 connect(d->header, SIGNAL(sectionMoved(int,int,int)),
292 this, SLOT(columnMoved()));
293 connect(d->header, SIGNAL(sectionCountChanged(int,int)),
294 this, SLOT(columnCountChanged(int,int)));
295 connect(d->header, SIGNAL(sectionHandleDoubleClicked(int)),
296 this, SLOT(resizeColumnToContents(int)));
297 connect(d->header, SIGNAL(geometriesChanged()),
298 this, SLOT(updateGeometries()));
299
300 setSortingEnabled(d->sortingEnabled);
301 d->updateGeometry();
302}
303
316{
317 Q_D(const QTreeView);
318 return d->autoExpandDelay;
319}
320
322{
323 Q_D(QTreeView);
324 d->autoExpandDelay = delay;
325}
326
341{
342 Q_D(const QTreeView);
343 return d->indent;
344}
345
347{
348 Q_D(QTreeView);
349 if (!d->customIndent || (i != d->indent)) {
350 d->indent = i;
351 d->customIndent = true;
352 d->viewport->update();
353 }
354}
355
357{
358 Q_D(QTreeView);
359 if (d->customIndent) {
360 d->updateIndentationFromStyle();
361 d->customIndent = false;
362 }
363}
364
377{
378 Q_D(const QTreeView);
379 return d->rootDecoration;
380}
381
383{
384 Q_D(QTreeView);
385 if (show != d->rootDecoration) {
386 d->rootDecoration = show;
387 d->viewport->update();
388 }
389}
390
408{
409 Q_D(const QTreeView);
410 return d->uniformRowHeights;
411}
412
414{
415 Q_D(QTreeView);
416 d->uniformRowHeights = uniform;
417}
418
430{
431 Q_D(const QTreeView);
432 return d->itemsExpandable;
433}
434
436{
437 Q_D(QTreeView);
438 d->itemsExpandable = enable;
439}
440
452{
453 Q_D(const QTreeView);
454 return d->expandsOnDoubleClick;
455}
456
458{
459 Q_D(QTreeView);
460 d->expandsOnDoubleClick = enable;
461}
462
467{
468 Q_D(const QTreeView);
469 return d->header->sectionViewportPosition(column);
470}
471
478{
479 Q_D(const QTreeView);
480 return d->header->sectionSize(column);
481}
482
491{
492 Q_D(QTreeView);
493 d->header->resizeSection(column, width);
494}
495
500int QTreeView::columnAt(int x) const
501{
502 Q_D(const QTreeView);
503 return d->header->logicalIndexAt(x);
504}
505
512{
513 Q_D(const QTreeView);
514 return d->header->isSectionHidden(column);
515}
516
523{
524 Q_D(QTreeView);
525 if (column < 0 || column >= d->header->count())
526 return;
527 d->header->setSectionHidden(column, hide);
528}
529
541{
542 Q_D(const QTreeView);
543 return d->header->isHidden();
544}
545
547{
548 Q_D(QTreeView);
549 d->header->setHidden(hide);
550}
551
559{
560 Q_D(const QTreeView);
561 if (!d->model)
562 return false;
563 return d->isRowHidden(d->model->index(row, 0, parent));
564}
565
572{
573 Q_D(QTreeView);
574 if (!d->model)
575 return;
576 QModelIndex index = d->model->index(row, 0, parent);
577 if (!index.isValid())
578 return;
579
580 if (hide) {
581 d->hiddenIndexes.insert(index);
582 } else if (d->isPersistent(index)) { //if the index is not persistent, it cannot be in the set
583 d->hiddenIndexes.remove(index);
584 }
585
586 d->doDelayedItemsLayout();
587}
588
598{
599 Q_D(const QTreeView);
600 if (d->spanningIndexes.isEmpty() || !d->model)
601 return false;
602 const QModelIndex index = d->model->index(row, 0, parent);
603 return d->spanningIndexes.contains(index);
604}
605
616{
617 Q_D(QTreeView);
618 if (!d->model)
619 return;
620 const QModelIndex index = d->model->index(row, 0, parent);
621 if (!index.isValid())
622 return;
623
624 if (span)
625 d->spanningIndexes.insert(index);
626 else
627 d->spanningIndexes.remove(index);
628
629 d->executePostedLayout();
630 int i = d->viewIndex(index);
631 if (i >= 0)
632 d->viewItems[i].spanning = span;
633
634 d->viewport->update();
635}
636
640void QTreeView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
641 const QList<int> &roles)
642{
643 Q_D(QTreeView);
644
645 // if we are going to do a complete relayout anyway, there is no need to update
646 if (d->delayedPendingLayout)
647 return;
648
649 // refresh the height cache here; we don't really lose anything by getting the size hint,
650 // since QAbstractItemView::dataChanged() will get the visualRect for the items anyway
651
652 bool sizeChanged = false;
653 int topViewIndex = d->viewIndex(topLeft);
654 if (topViewIndex == 0) {
655 int newDefaultItemHeight = indexRowSizeHint(topLeft);
656 sizeChanged = d->defaultItemHeight != newDefaultItemHeight;
657 d->defaultItemHeight = newDefaultItemHeight;
658 }
659
660 if (topViewIndex != -1) {
661 if (topLeft.row() == bottomRight.row()) {
662 int oldHeight = d->itemHeight(topViewIndex);
663 d->invalidateHeightCache(topViewIndex);
664 sizeChanged |= (oldHeight != d->itemHeight(topViewIndex));
665 if (topLeft.column() == 0)
666 d->viewItems[topViewIndex].hasChildren = d->hasVisibleChildren(topLeft);
667 } else {
668 int bottomViewIndex = d->viewIndex(bottomRight);
669 for (int i = topViewIndex; i <= bottomViewIndex; ++i) {
670 int oldHeight = d->itemHeight(i);
671 d->invalidateHeightCache(i);
672 sizeChanged |= (oldHeight != d->itemHeight(i));
673 if (topLeft.column() == 0)
674 d->viewItems[i].hasChildren = d->hasVisibleChildren(d->viewItems.at(i).index);
675 }
676 }
677 }
678
679 if (sizeChanged) {
680 d->updateScrollBars();
681 d->viewport->update();
682 }
683 QAbstractItemView::dataChanged(topLeft, bottomRight, roles);
684}
685
696{
697 Q_D(QTreeView);
698 if (d->header->isSectionHidden(column))
699 return;
700 d->header->hideSection(column);
702}
703
710{
711 Q_D(QTreeView);
712 if (!d->header->isSectionHidden(column))
713 return;
714 d->header->showSection(column);
716}
717
726{
727 Q_D(QTreeView);
728 if (!d->isIndexValid(index))
729 return;
730 if (index.flags() & Qt::ItemNeverHasChildren)
731 return;
732 if (d->isIndexExpanded(index))
733 return;
734 if (d->delayedPendingLayout) {
735 //A complete relayout is going to be performed, just store the expanded index, no need to layout.
736 if (d->storeExpanded(index))
738 return;
739 }
740
741 int i = d->viewIndex(index);
742 if (i != -1) { // is visible
743 d->expand(i, true);
744 if (!d->isAnimating()) {
746 d->viewport->update();
747 }
748 } else if (d->storeExpanded(index)) {
750 }
751}
752
761{
762 Q_D(QTreeView);
763 if (!d->isIndexValid(index))
764 return;
765 if (!d->isIndexExpanded(index))
766 return;
767 //if the current item is now invisible, the autoscroll will expand the tree to see it, so disable the autoscroll
768 d->delayedAutoScroll.stop();
769
770 if (d->delayedPendingLayout) {
771 //A complete relayout is going to be performed, just un-store the expanded index, no need to layout.
772 if (d->isPersistent(index) && d->expandedIndexes.remove(index))
774 return;
775 }
776 int i = d->viewIndex(index);
777 if (i != -1) { // is visible
778 d->collapse(i, true);
779 if (!d->isAnimating()) {
781 viewport()->update();
782 }
783 } else {
784 if (d->isPersistent(index) && d->expandedIndexes.remove(index))
786 }
787}
788
798{
799 Q_D(const QTreeView);
800 return d->isIndexExpanded(index);
801}
802
809void QTreeView::setExpanded(const QModelIndex &index, bool expanded)
810{
811 if (expanded)
812 this->expand(index);
813 else
814 this->collapse(index);
815}
816
834{
835 Q_D(QTreeView);
838 if (enable) {
839 //sortByColumn has to be called before we connect or set the sortingEnabled flag
840 // because otherwise it will not call sort on the model.
841 sortByColumn(header()->sortIndicatorSection(), header()->sortIndicatorOrder());
842 connect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
843 this, SLOT(_q_sortIndicatorChanged(int,Qt::SortOrder)), Qt::UniqueConnection);
844 } else {
845 disconnect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
846 this, SLOT(_q_sortIndicatorChanged(int,Qt::SortOrder)));
847 }
848 d->sortingEnabled = enable;
849}
850
852{
853 Q_D(const QTreeView);
854 return d->sortingEnabled;
855}
856
870void QTreeView::setAnimated(bool animate)
871{
872 Q_D(QTreeView);
873 d->animationsEnabled = animate;
874}
875
877{
878 Q_D(const QTreeView);
879 return d->animationsEnabled;
880}
881
894{
895 Q_D(QTreeView);
896 if (d->allColumnsShowFocus == enable)
897 return;
898 d->allColumnsShowFocus = enable;
899 d->viewport->update();
900}
901
903{
904 Q_D(const QTreeView);
905 return d->allColumnsShowFocus;
906}
907
922{
923 Q_D(QTreeView);
924 if (d->wrapItemText == on)
925 return;
926 d->wrapItemText = on;
927 d->doDelayedItemsLayout();
928}
929
931{
932 Q_D(const QTreeView);
933 return d->wrapItemText;
934}
935
946{
947 Q_D(QTreeView);
948 d->treePosition = index;
949 d->viewport->update();
950}
951
962{
963 Q_D(const QTreeView);
964 return d->treePosition;
965}
966
971{
972 Q_D(QTreeView);
973 if (!d->model->rowCount(d->root) || !d->model->columnCount(d->root))
974 return;
975
976 // Do a relayout nows, so that we can utilize viewItems
977 d->executePostedLayout();
978 if (d->viewItems.isEmpty())
979 return;
980
982 if (currentIndex().isValid())
984 else
985 start = d->viewItems.at(0).index;
986
987 bool skipRow = false;
988 bool keyboardTimeWasValid = d->keyboardInputTime.isValid();
989 qint64 keyboardInputTimeElapsed;
990 if (keyboardTimeWasValid)
991 keyboardInputTimeElapsed = d->keyboardInputTime.restart();
992 else
993 d->keyboardInputTime.start();
994 if (search.isEmpty() || !keyboardTimeWasValid
995 || keyboardInputTimeElapsed > QApplication::keyboardInputInterval()) {
996 d->keyboardInput = search;
997 skipRow = currentIndex().isValid(); //if it is not valid we should really start at QModelIndex(0,0)
998 } else {
999 d->keyboardInput += search;
1000 }
1001
1002 // special case for searches with same key like 'aaaaa'
1003 bool sameKey = false;
1004 if (d->keyboardInput.size() > 1) {
1005 int c = d->keyboardInput.count(d->keyboardInput.at(d->keyboardInput.size() - 1));
1006 sameKey = (c == d->keyboardInput.size());
1007 if (sameKey)
1008 skipRow = true;
1009 }
1010
1011 // skip if we are searching for the same key or a new search started
1012 if (skipRow) {
1013 if (indexBelow(start).isValid()) {
1015 } else {
1016 const int origCol = start.column();
1017 start = d->viewItems.at(0).index;
1018 if (origCol != start.column())
1019 start = start.sibling(start.row(), origCol);
1020 }
1021 }
1022
1023 int startIndex = d->viewIndex(start);
1024 if (startIndex <= -1)
1025 return;
1026
1027 int previousLevel = -1;
1028 int bestAbove = -1;
1029 int bestBelow = -1;
1030 QString searchString = sameKey ? QString(d->keyboardInput.at(0)) : d->keyboardInput;
1031 for (int i = 0; i < d->viewItems.size(); ++i) {
1032 if ((int)d->viewItems.at(i).level > previousLevel) {
1033 QModelIndex searchFrom = d->viewItems.at(i).index;
1034 if (start.column() > 0)
1035 searchFrom = searchFrom.sibling(searchFrom.row(), start.column());
1036 if (searchFrom.parent() == start.parent())
1037 searchFrom = start;
1038 QModelIndexList match = d->model->match(searchFrom, Qt::DisplayRole, searchString);
1039 if (match.size()) {
1040 int hitIndex = d->viewIndex(match.at(0));
1041 if (hitIndex >= 0 && hitIndex < startIndex)
1042 bestAbove = bestAbove == -1 ? hitIndex : qMin(hitIndex, bestAbove);
1043 else if (hitIndex >= startIndex)
1044 bestBelow = bestBelow == -1 ? hitIndex : qMin(hitIndex, bestBelow);
1045 }
1046 }
1047 previousLevel = d->viewItems.at(i).level;
1048 }
1049
1051 if (bestBelow > -1)
1052 index = d->viewItems.at(bestBelow).index;
1053 else if (bestAbove > -1)
1054 index = d->viewItems.at(bestAbove).index;
1055
1056 if (start.column() > 0)
1057 index = index.sibling(index.row(), start.column());
1058
1059 if (index.isValid())
1061}
1062
1068{
1069 Q_D(const QTreeView);
1070
1071 if (!d->isIndexValid(index) || isIndexHidden(index))
1072 return QRect();
1073
1074 d->executePostedLayout();
1075
1076 int vi = d->viewIndex(index);
1077 if (vi < 0)
1078 return QRect();
1079
1080 bool spanning = d->viewItems.at(vi).spanning;
1081
1082 // if we have a spanning item, make the selection stretch from left to right
1083 int x = (spanning ? 0 : columnViewportPosition(index.column()));
1084 int w = (spanning ? d->header->length() : columnWidth(index.column()));
1085 // handle indentation
1086 if (d->isTreePosition(index.column())) {
1087 int i = d->indentationForItem(vi);
1088 w -= i;
1089 if (!isRightToLeft())
1090 x += i;
1091 }
1092
1093 int y = d->coordinateForItem(vi);
1094 int h = d->itemHeight(vi);
1095
1096 return QRect(x, y, w, h);
1097}
1098
1108{
1109 Q_D(QTreeView);
1110
1111 if (!d->isIndexValid(index))
1112 return;
1113
1114 d->executePostedLayout();
1115 d->updateScrollBars();
1116
1117 // Expand all parents if the parent(s) of the node are not expanded.
1118 QModelIndex parent = index.parent();
1119 while (parent != d->root && parent.isValid() && state() == NoState && d->itemsExpandable) {
1120 if (!isExpanded(parent))
1121 expand(parent);
1122 parent = d->model->parent(parent);
1123 }
1124
1125 int item = d->viewIndex(index);
1126 if (item < 0)
1127 return;
1128
1129 QRect area = d->viewport->rect();
1130
1131 // vertical
1133 int top = verticalScrollBar()->value();
1134 int bottom = top + verticalScrollBar()->pageStep();
1135 if (hint == EnsureVisible && item >= top && item < bottom) {
1136 // nothing to do
1137 } else if (hint == PositionAtTop || (hint == EnsureVisible && item < top)) {
1138 verticalScrollBar()->setValue(item);
1139 } else { // PositionAtBottom or PositionAtCenter
1140 const int currentItemHeight = d->itemHeight(item);
1141 int y = (hint == PositionAtCenter
1142 //we center on the current item with a preference to the top item (ie. -1)
1143 ? area.height() / 2 + currentItemHeight - 1
1144 //otherwise we simply take the whole space
1145 : area.height());
1146 if (y > currentItemHeight) {
1147 while (item >= 0) {
1148 y -= d->itemHeight(item);
1149 if (y < 0) { //there is no more space left
1150 item++;
1151 break;
1152 }
1153 item--;
1154 }
1155 }
1156 verticalScrollBar()->setValue(item);
1157 }
1158 } else { // ScrollPerPixel
1160 d->coordinateForItem(item), // ### slow for items outside the view
1161 columnWidth(index.column()),
1162 d->itemHeight(item));
1163
1164 if (rect.isEmpty()) {
1165 // nothing to do
1166 } else if (hint == EnsureVisible && area.contains(rect)) {
1167 d->viewport->update(rect);
1168 // nothing to do
1169 } else {
1170 bool above = (hint == EnsureVisible
1171 && (rect.top() < area.top()
1172 || area.height() < rect.height()));
1173 bool below = (hint == EnsureVisible
1174 && rect.bottom() > area.bottom()
1175 && rect.height() < area.height());
1176
1177 int verticalValue = verticalScrollBar()->value();
1178 if (hint == PositionAtTop || above)
1179 verticalValue += rect.top();
1180 else if (hint == PositionAtBottom || below)
1181 verticalValue += rect.bottom() - area.height();
1182 else if (hint == PositionAtCenter)
1183 verticalValue += rect.top() - ((area.height() - rect.height()) / 2);
1184 verticalScrollBar()->setValue(verticalValue);
1185 }
1186 }
1187 // horizontal
1188 int viewportWidth = d->viewport->width();
1189 int horizontalOffset = d->header->offset();
1190 int horizontalPosition = d->header->sectionPosition(index.column());
1191 int cellWidth = d->header->sectionSize(index.column());
1192
1193 if (hint == PositionAtCenter) {
1194 horizontalScrollBar()->setValue(horizontalPosition - ((viewportWidth - cellWidth) / 2));
1195 } else {
1196 if (horizontalPosition - horizontalOffset < 0 || cellWidth > viewportWidth)
1197 horizontalScrollBar()->setValue(horizontalPosition);
1198 else if (horizontalPosition - horizontalOffset + cellWidth > viewportWidth)
1199 horizontalScrollBar()->setValue(horizontalPosition - viewportWidth + cellWidth);
1200 }
1201}
1202
1207{
1208 Q_D(QTreeView);
1209 if (event->type() == QEvent::StyleChange) {
1210 if (!d->customIndent) {
1211 // QAbstractItemView calls this method in case of a style change,
1212 // so update the indentation here if it wasn't set manually.
1213 d->updateIndentationFromStyle();
1214 }
1215 }
1216 QAbstractItemView::changeEvent(event);
1217}
1218
1223{
1224 Q_D(QTreeView);
1225 if (event->timerId() == d->columnResizeTimerID) {
1227 killTimer(d->columnResizeTimerID);
1228 d->columnResizeTimerID = 0;
1229 QRect rect;
1230 int viewportHeight = d->viewport->height();
1231 int viewportWidth = d->viewport->width();
1232 for (int i = d->columnsToUpdate.size() - 1; i >= 0; --i) {
1233 int column = d->columnsToUpdate.at(i);
1235 if (isRightToLeft())
1236 rect |= QRect(0, 0, x + columnWidth(column), viewportHeight);
1237 else
1238 rect |= QRect(x, 0, viewportWidth - x, viewportHeight);
1239 }
1240 d->viewport->update(rect.normalized());
1241 d->columnsToUpdate.clear();
1242 } else if (event->timerId() == d->openTimer.timerId()) {
1243 QPoint pos = d->viewport->mapFromGlobal(QCursor::pos());
1245 && d->viewport->rect().contains(pos)) {
1248 }
1249 d->openTimer.stop();
1250 }
1251
1253}
1254
1258#if QT_CONFIG(draganddrop)
1259void QTreeView::dragMoveEvent(QDragMoveEvent *event)
1260{
1261 Q_D(QTreeView);
1262 if (d->autoExpandDelay >= 0)
1263 d->openTimer.start(d->autoExpandDelay, this);
1264 QAbstractItemView::dragMoveEvent(event);
1265}
1266#endif
1267
1272{
1273 Q_D(QTreeView);
1274 switch (event->type()) {
1275 case QEvent::HoverEnter:
1276 case QEvent::HoverLeave:
1277 case QEvent::HoverMove: {
1278 QHoverEvent *he = static_cast<QHoverEvent*>(event);
1279 int oldBranch = d->hoverBranch;
1280 d->hoverBranch = d->itemDecorationAt(he->position().toPoint());
1281 QModelIndex newIndex = indexAt(he->position().toPoint());
1282 if (d->hover != newIndex || d->hoverBranch != oldBranch) {
1283 // Update the whole hovered over row. No need to update the old hovered
1284 // row, that is taken care in superclass hover handling.
1285 QRect rect = visualRect(newIndex);
1286 rect.setX(0);
1287 rect.setWidth(viewport()->width());
1288 viewport()->update(rect);
1289 }
1290 break; }
1291 default:
1292 break;
1293 }
1295}
1296
1301{
1302 Q_D(QTreeView);
1303 d->executePostedLayout();
1305#if QT_CONFIG(animation)
1306 if (d->isAnimating()) {
1307 drawTree(&painter, event->region() - d->animatedOperation.rect());
1308 d->drawAnimatedOperation(&painter);
1309 } else
1310#endif // animation
1311 {
1312 drawTree(&painter, event->region());
1313#if QT_CONFIG(draganddrop)
1314 d->paintDropIndicator(&painter);
1315#endif
1316 }
1317}
1318
1320{
1321 int index = treePosition;
1322 if (index < 0)
1324 return index;
1325}
1326
1327void QTreeViewPrivate::paintAlternatingRowColors(QPainter *painter, QStyleOptionViewItem *option, int y, int bottom) const
1328{
1329 Q_Q(const QTreeView);
1331 return;
1332 int rowHeight = defaultItemHeight;
1333 if (rowHeight <= 0) {
1334 rowHeight = itemDelegate->sizeHint(*option, QModelIndex()).height();
1335 if (rowHeight <= 0)
1336 return;
1337 }
1338 while (y <= bottom) {
1339 option->rect.setRect(0, y, viewport->width(), rowHeight);
1340 option->features.setFlag(QStyleOptionViewItem::Alternate, current & 1);
1341 ++current;
1342 q->style()->drawPrimitive(QStyle::PE_PanelItemViewRow, option, painter, q);
1343 y += rowHeight;
1344 }
1345}
1346
1348{
1349 Q_Q(QTreeView);
1350 // we want to handle mousePress in EditingState (persistent editors)
1353 || !viewport->rect().contains(pos))
1354 return true;
1355
1356 int i = itemDecorationAt(pos);
1357 if ((i != -1) && itemsExpandable && hasVisibleChildren(viewItems.at(i).index)) {
1358 if (viewItems.at(i).expanded)
1359 collapse(i, true);
1360 else
1361 expand(i, true);
1362 if (!isAnimating()) {
1363 q->updateGeometries();
1364 viewport->update();
1365 }
1366 return true;
1367 }
1368 return false;
1369}
1370
1372{
1373 //we need to clear the viewItems because it contains QModelIndexes to
1374 //the model currently being destroyed
1375 viewItems.clear();
1377}
1378
1379QRect QTreeViewPrivate::intersectedRect(const QRect rect, const QModelIndex &topLeft, const QModelIndex &bottomRight) const
1380{
1381 Q_Q(const QTreeView);
1382
1383 const auto parentIdx = topLeft.parent();
1385 QRect updateRect;
1386 for (int r = topLeft.row(); r <= bottomRight.row(); ++r) {
1387 if (isRowHidden(model->index(r, 0, parentIdx)))
1388 continue;
1389 for (int c = topLeft.column(); c <= bottomRight.column(); ++c) {
1390 const QModelIndex idx(model->index(r, c, parentIdx));
1391 updateRect |= q->visualRect(idx);
1392 }
1393 }
1394 return rect.intersected(updateRect);
1395}
1396
1403{
1404 Q_ASSERT(r);
1405 Q_Q(const QTreeView);
1409 for (const QModelIndex &idx : indexes) {
1410 if (idx.column() > 0 && q->isFirstColumnSpanned(idx.row(), idx.parent()))
1411 continue;
1412 list << idx;
1413 }
1415}
1416
1417void QTreeViewPrivate::adjustViewOptionsForIndex(QStyleOptionViewItem *option, const QModelIndex &current) const
1418{
1419 const int row = viewIndex(current); // get the index in viewItems[]
1423
1424 option->showDecorationSelected = (selectionBehavior & QTreeView::SelectRows)
1425 || option->showDecorationSelected;
1426
1428 logicalIndices; // index = visual index of visible columns only. data = logical index.
1430 viewItemPosList; // vector of left/middle/end for each logicalIndex, visible columns
1431 // only.
1432 const bool spanning = viewItems.at(row).spanning;
1433 const int left = (spanning ? header->visualIndex(0) : 0);
1434 const int right = (spanning ? header->visualIndex(0) : header->count() - 1 );
1435 calcLogicalIndices(&logicalIndices, &viewItemPosList, left, right);
1436
1437 const int visualIndex = logicalIndices.indexOf(current.column());
1438 option->viewItemPosition = viewItemPosList.at(visualIndex);
1439}
1440
1441
1449void QTreeView::drawTree(QPainter *painter, const QRegion &region) const
1450{
1451 Q_D(const QTreeView);
1452 // d->viewItems changes when posted layouts are executed in itemDecorationAt, so don't copy
1453 const QList<QTreeViewItem> &viewItems = d->viewItems;
1454
1455 QStyleOptionViewItem option;
1457 const QStyle::State state = option.state;
1458 d->current = 0;
1459
1460 if (viewItems.size() == 0 || d->header->count() == 0 || !d->itemDelegate) {
1461 d->paintAlternatingRowColors(painter, &option, 0, region.boundingRect().bottom()+1);
1462 return;
1463 }
1464
1465 int firstVisibleItemOffset = 0;
1466 const int firstVisibleItem = d->firstVisibleItem(&firstVisibleItemOffset);
1467 if (firstVisibleItem < 0) {
1468 d->paintAlternatingRowColors(painter, &option, 0, region.boundingRect().bottom()+1);
1469 return;
1470 }
1471
1472 const int viewportWidth = d->viewport->width();
1473
1474 QPoint hoverPos = d->viewport->mapFromGlobal(QCursor::pos());
1475 d->hoverBranch = d->itemDecorationAt(hoverPos);
1476
1477 QList<int> drawn;
1478 bool multipleRects = (region.rectCount() > 1);
1479 for (const QRect &a : region) {
1480 const QRect area = (multipleRects
1481 ? QRect(0, a.y(), viewportWidth, a.height())
1482 : a);
1483 d->leftAndRight = d->startAndEndColumns(area);
1484
1485 int i = firstVisibleItem; // the first item at the top of the viewport
1486 int y = firstVisibleItemOffset; // we may only see part of the first item
1487
1488 // start at the top of the viewport and iterate down to the update area
1489 for (; i < viewItems.size(); ++i) {
1490 const int itemHeight = d->itemHeight(i);
1491 if (y + itemHeight > area.top())
1492 break;
1493 y += itemHeight;
1494 }
1495
1496 // paint the visible rows
1497 for (; i < viewItems.size() && y <= area.bottom(); ++i) {
1498 const int itemHeight = d->itemHeight(i);
1499 option.rect.setRect(0, y, viewportWidth, itemHeight);
1500 option.state = state | (viewItems.at(i).expanded ? QStyle::State_Open : QStyle::State_None)
1501 | (viewItems.at(i).hasChildren ? QStyle::State_Children : QStyle::State_None)
1502 | (viewItems.at(i).hasMoreSiblings ? QStyle::State_Sibling : QStyle::State_None);
1503 d->current = i;
1504 d->spanning = viewItems.at(i).spanning;
1505 if (!multipleRects || !drawn.contains(i)) {
1506 drawRow(painter, option, viewItems.at(i).index);
1507 if (multipleRects) // even if the rect only intersects the item,
1508 drawn.append(i); // the entire item will be painted
1509 }
1510 y += itemHeight;
1511 }
1512
1513 if (y <= area.bottom()) {
1514 d->current = i;
1515 d->paintAlternatingRowColors(painter, &option, y, area.bottom());
1516 }
1517 }
1518}
1519
1521static inline bool ancestorOf(QObject *widget, QObject *other)
1522{
1523 for (QObject *parent = other; parent != nullptr; parent = parent->parent()) {
1524 if (parent == widget)
1525 return true;
1526 }
1527 return false;
1528}
1529
1531 QList<int> *logicalIndices, QList<QStyleOptionViewItem::ViewItemPosition> *itemPositions,
1532 int left, int right) const
1533{
1534 const int columnCount = header->count();
1535 /* 'left' and 'right' are the left-most and right-most visible visual indices.
1536 Compute the first visible logical indices before and after the left and right.
1537 We will use these values to determine the QStyleOptionViewItem::viewItemPosition. */
1538 int logicalIndexBeforeLeft = -1, logicalIndexAfterRight = -1;
1539 for (int visualIndex = left - 1; visualIndex >= 0; --visualIndex) {
1540 int logicalIndex = header->logicalIndex(visualIndex);
1541 if (!header->isSectionHidden(logicalIndex)) {
1542 logicalIndexBeforeLeft = logicalIndex;
1543 break;
1544 }
1545 }
1546
1547 for (int visualIndex = left; visualIndex < columnCount; ++visualIndex) {
1548 int logicalIndex = header->logicalIndex(visualIndex);
1549 if (!header->isSectionHidden(logicalIndex)) {
1550 if (visualIndex > right) {
1551 logicalIndexAfterRight = logicalIndex;
1552 break;
1553 }
1554 logicalIndices->append(logicalIndex);
1555 }
1556 }
1557
1558 itemPositions->resize(logicalIndices->size());
1559 for (int currentLogicalSection = 0; currentLogicalSection < logicalIndices->size(); ++currentLogicalSection) {
1560 const int headerSection = logicalIndices->at(currentLogicalSection);
1561 // determine the viewItemPosition depending on the position of column 0
1562 int nextLogicalSection = currentLogicalSection + 1 >= logicalIndices->size()
1563 ? logicalIndexAfterRight
1564 : logicalIndices->at(currentLogicalSection + 1);
1565 int prevLogicalSection = currentLogicalSection - 1 < 0
1566 ? logicalIndexBeforeLeft
1567 : logicalIndices->at(currentLogicalSection - 1);
1568 QStyleOptionViewItem::ViewItemPosition pos;
1569 if (columnCount == 1 || (nextLogicalSection == 0 && prevLogicalSection == -1)
1570 || (headerSection == 0 && nextLogicalSection == -1) || spanning)
1571 pos = QStyleOptionViewItem::OnlyOne;
1572 else if (isTreePosition(headerSection) || (nextLogicalSection != 0 && prevLogicalSection == -1))
1573 pos = QStyleOptionViewItem::Beginning;
1574 else if (nextLogicalSection == 0 || nextLogicalSection == -1)
1575 pos = QStyleOptionViewItem::End;
1576 else
1577 pos = QStyleOptionViewItem::Middle;
1578 (*itemPositions)[currentLogicalSection] = pos;
1579 }
1580}
1581
1586int QTreeViewPrivate::widthHintForIndex(const QModelIndex &index, int hint, const QStyleOptionViewItem &option, int i) const
1587{
1588 Q_Q(const QTreeView);
1590 if (editor && persistent.contains(editor)) {
1591 hint = qMax(hint, editor->sizeHint().width());
1592 int min = editor->minimumSize().width();
1593 int max = editor->maximumSize().width();
1594 hint = qBound(min, hint, max);
1595 }
1596 int xhint = q->itemDelegateForIndex(index)->sizeHint(option, index).width();
1597 hint = qMax(hint, xhint + (isTreePosition(index.column()) ? indentationForItem(i) : 0));
1598 return hint;
1599}
1600
1608void QTreeView::drawRow(QPainter *painter, const QStyleOptionViewItem &option,
1609 const QModelIndex &index) const
1610{
1611 Q_D(const QTreeView);
1612 QStyleOptionViewItem opt = option;
1613 const QPoint offset = d->scrollDelayOffset;
1614 const int y = option.rect.y() + offset.y();
1615 const QModelIndex parent = index.parent();
1616 const QHeaderView *header = d->header;
1617 const QModelIndex current = currentIndex();
1618 const QModelIndex hover = d->hover;
1619 const bool reverse = isRightToLeft();
1620 const QStyle::State state = opt.state;
1621 const bool spanning = d->spanning;
1622 const int left = (spanning ? header->visualIndex(0) : d->leftAndRight.first);
1623 const int right = (spanning ? header->visualIndex(0) : d->leftAndRight.second);
1624 const bool alternate = d->alternatingColors;
1625 const bool enabled = (state & QStyle::State_Enabled) != 0;
1626 const bool allColumnsShowFocus = d->allColumnsShowFocus;
1627
1628
1629 // when the row contains an index widget which has focus,
1630 // we want to paint the entire row as active
1631 bool indexWidgetHasFocus = false;
1632 if ((current.row() == index.row()) && !d->editorIndexHash.isEmpty()) {
1633 const int r = index.row();
1635 for (int c = 0; c < header->count(); ++c) {
1636 QModelIndex idx = d->model->index(r, c, parent);
1637 if (QWidget *editor = indexWidget(idx)) {
1638 if (ancestorOf(editor, fw)) {
1639 indexWidgetHasFocus = true;
1640 break;
1641 }
1642 }
1643 }
1644 }
1645
1646 const bool widgetHasFocus = hasFocus();
1647 bool currentRowHasFocus = false;
1648 if (allColumnsShowFocus && widgetHasFocus && current.isValid()) {
1649 // check if the focus index is before or after the visible columns
1650 const int r = index.row();
1651 for (int c = 0; c < left && !currentRowHasFocus; ++c) {
1652 QModelIndex idx = d->model->index(r, c, parent);
1653 currentRowHasFocus = (idx == current);
1654 }
1655 QModelIndex parent = d->model->parent(index);
1656 for (int c = right; c < header->count() && !currentRowHasFocus; ++c) {
1657 currentRowHasFocus = (d->model->index(r, c, parent) == current);
1658 }
1659 }
1660
1661 // ### special case: if we select entire rows, then we need to draw the
1662 // selection in the first column all the way to the second column, rather
1663 // than just around the item text. We abuse showDecorationSelected to
1664 // indicate this to the style. Below we will reset this value temporarily
1665 // to only respect the styleHint while we are rendering the decoration.
1666 opt.showDecorationSelected = (d->selectionBehavior & SelectRows)
1667 || option.showDecorationSelected;
1668
1669 int width, height = option.rect.height();
1670 int position;
1671 QModelIndex modelIndex;
1672 const bool hoverRow = selectionBehavior() == QAbstractItemView::SelectRows
1673 && index.parent() == hover.parent()
1674 && index.row() == hover.row();
1675
1676 QList<int> logicalIndices;
1678 viewItemPosList; // vector of left/middle/end for each logicalIndex
1679 d->calcLogicalIndices(&logicalIndices, &viewItemPosList, left, right);
1680
1681 for (int currentLogicalSection = 0; currentLogicalSection < logicalIndices.size(); ++currentLogicalSection) {
1682 int headerSection = logicalIndices.at(currentLogicalSection);
1683 position = columnViewportPosition(headerSection) + offset.x();
1684 width = header->sectionSize(headerSection);
1685
1686 if (spanning) {
1687 int lastSection = header->logicalIndex(header->count() - 1);
1688 if (!reverse) {
1689 width = columnViewportPosition(lastSection) + header->sectionSize(lastSection) - position;
1690 } else {
1691 width += position - columnViewportPosition(lastSection);
1692 position = columnViewportPosition(lastSection);
1693 }
1694 }
1695
1696 modelIndex = d->model->index(index.row(), headerSection, parent);
1697 if (!modelIndex.isValid())
1698 continue;
1699 opt.state = state;
1700
1701 opt.viewItemPosition = viewItemPosList.at(currentLogicalSection);
1702
1703 // fake activeness when row editor has focus
1704 if (indexWidgetHasFocus)
1706
1707 if (d->selectionModel->isSelected(modelIndex))
1709 if (widgetHasFocus && (current == modelIndex)) {
1711 currentRowHasFocus = true;
1712 else
1714 }
1716 (hoverRow || modelIndex == hover)
1717 && (option.showDecorationSelected || d->hoverBranch == -1));
1718
1719 if (enabled) {
1721 if ((d->model->flags(modelIndex) & Qt::ItemIsEnabled) == 0) {
1722 opt.state &= ~QStyle::State_Enabled;
1723 cg = QPalette::Disabled;
1724 } else if (opt.state & QStyle::State_Active) {
1725 cg = QPalette::Active;
1726 } else {
1727 cg = QPalette::Inactive;
1728 }
1730 }
1731
1732 if (alternate) {
1733 opt.features.setFlag(QStyleOptionViewItem::Alternate, d->current & 1);
1734 }
1735
1736 /* Prior to Qt 4.3, the background of the branch (in selected state and
1737 alternate row color was provided by the view. For backward compatibility,
1738 this is now delegated to the style using PE_PanelItemViewRow which
1739 does the appropriate fill */
1740 if (d->isTreePosition(headerSection)) {
1741 const int i = d->indentationForItem(d->current);
1742 QRect branches(reverse ? position + width - i : position, y, i, height);
1743 const bool setClipRect = branches.width() > width;
1744 if (setClipRect) {
1745 painter->save();
1747 }
1748 // draw background for the branch (selection + alternate row)
1749 opt.rect = branches;
1750
1751 // We use showDecorationSelected both to store the style hint, and to indicate
1752 // that the entire row has to be selected (see overrides of the value if
1753 // selectionBehavior == SelectRow).
1754 // While we are only painting the background we don't care for the
1755 // selectionBehavior factor, so respect only the style value, and reset later.
1756 const bool oldShowDecorationSelected = opt.showDecorationSelected;
1757 opt.showDecorationSelected = style()->styleHint(QStyle::SH_ItemView_ShowDecorationSelected,
1758 &opt, this);
1759 style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, this);
1760
1761 // draw background of the item (only alternate row). rest of the background
1762 // is provided by the delegate
1763 QStyle::State oldState = opt.state;
1764 opt.state &= ~QStyle::State_Selected;
1765 opt.rect.setRect(reverse ? position : i + position, y, width - i, height);
1766 style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, this);
1767 opt.state = oldState;
1768 opt.showDecorationSelected = oldShowDecorationSelected;
1769
1770 if (d->indent != 0)
1771 drawBranches(painter, branches, index);
1772 if (setClipRect)
1773 painter->restore();
1774 } else {
1775 QStyle::State oldState = opt.state;
1776 opt.state &= ~QStyle::State_Selected;
1778 style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, this);
1779 opt.state = oldState;
1780 }
1781
1782 itemDelegateForIndex(modelIndex)->paint(painter, opt, modelIndex);
1783 }
1784
1785 if (currentRowHasFocus) {
1787 o.QStyleOption::operator=(option);
1791 o.backgroundColor = option.palette.color(cg, d->selectionModel->isSelected(index)
1793 int x = 0;
1794 if (!option.showDecorationSelected)
1795 x = header->sectionPosition(0) + d->indentationForItem(d->current);
1796 QRect focusRect(x - header->offset(), y, header->length() - x, height);
1797 o.rect = style()->visualRect(layoutDirection(), d->viewport->rect(), focusRect);
1798 style()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter);
1799 // if we show focus on all columns and the first section is moved,
1800 // we have to split the focus rect into two rects
1801 if (allColumnsShowFocus && !option.showDecorationSelected
1802 && header->sectionsMoved() && (header->visualIndex(0) != 0)) {
1803 QRect sectionRect(0, y, header->sectionPosition(0), height);
1804 o.rect = style()->visualRect(layoutDirection(), d->viewport->rect(), sectionRect);
1805 style()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter);
1806 }
1807 }
1808}
1809
1816 const QModelIndex &index) const
1817{
1818 Q_D(const QTreeView);
1819 const bool reverse = isRightToLeft();
1820 const int indent = d->indent;
1821 const int outer = d->rootDecoration ? 0 : 1;
1822 const int item = d->current;
1823 const QTreeViewItem &viewItem = d->viewItems.at(item);
1824 int level = viewItem.level;
1825 QRect primitive(reverse ? rect.left() : rect.right() + 1, rect.top(), indent, rect.height());
1826
1827 QModelIndex parent = index.parent();
1828 QModelIndex current = parent;
1829 QModelIndex ancestor = current.parent();
1830
1831 QStyleOptionViewItem opt;
1833 QStyle::State extraFlags = QStyle::State_None;
1834 if (isEnabled())
1835 extraFlags |= QStyle::State_Enabled;
1836 if (hasFocus())
1837 extraFlags |= QStyle::State_Active;
1838 QPoint oldBO = painter->brushOrigin();
1841
1842 if (d->alternatingColors) {
1843 opt.features.setFlag(QStyleOptionViewItem::Alternate, d->current & 1);
1844 }
1845
1846 // When hovering over a row, pass State_Hover for painting the branch
1847 // indicators if it has the decoration (aka branch) selected.
1849 && opt.showDecorationSelected
1850 && index.parent() == d->hover.parent()
1851 && index.row() == d->hover.row();
1852
1853 if (d->selectionModel->isSelected(index))
1854 extraFlags |= QStyle::State_Selected;
1855
1856 if (level >= outer) {
1857 // start with the innermost branch
1858 primitive.moveLeft(reverse ? primitive.left() : primitive.left() - indent);
1859 opt.rect = primitive;
1860
1861 const bool expanded = viewItem.expanded;
1862 const bool children = viewItem.hasChildren;
1863 bool moreSiblings = viewItem.hasMoreSiblings;
1864
1865 opt.state = QStyle::State_Item | extraFlags
1866 | (moreSiblings ? QStyle::State_Sibling : QStyle::State_None)
1869 opt.state.setFlag(QStyle::State_MouseOver, hoverRow || item == d->hoverBranch);
1870
1871 style()->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, painter, this);
1872 }
1873 // then go out level by level
1874 for (--level; level >= outer; --level) { // we have already drawn the innermost branch
1875 primitive.moveLeft(reverse ? primitive.left() + indent : primitive.left() - indent);
1876 opt.rect = primitive;
1877 opt.state = extraFlags;
1878 bool moreSiblings = false;
1879 if (d->hiddenIndexes.isEmpty()) {
1880 moreSiblings = (d->model->rowCount(ancestor) - 1 > current.row());
1881 } else {
1882 int successor = item + viewItem.total + 1;
1883 while (successor < d->viewItems.size()
1884 && d->viewItems.at(successor).level >= uint(level)) {
1885 const QTreeViewItem &successorItem = d->viewItems.at(successor);
1886 if (successorItem.level == uint(level)) {
1887 moreSiblings = true;
1888 break;
1889 }
1890 successor += successorItem.total + 1;
1891 }
1892 }
1893 if (moreSiblings)
1895 opt.state.setFlag(QStyle::State_MouseOver, hoverRow || item == d->hoverBranch);
1896
1897 style()->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, painter, this);
1898 current = ancestor;
1899 ancestor = current.parent();
1900 }
1901 painter->setBrushOrigin(oldBO);
1902}
1903
1908{
1909 Q_D(QTreeView);
1910 bool handled = false;
1911 if (style()->styleHint(QStyle::SH_ListViewExpand_SelectMouseType, nullptr, this) == QEvent::MouseButtonPress)
1912 handled = d->expandOrCollapseItemAtPos(event->position().toPoint());
1913 if (!handled && d->itemDecorationAt(event->position().toPoint()) == -1)
1915 else
1916 d->pressedIndex = QModelIndex();
1917}
1918
1923{
1924 Q_D(QTreeView);
1925 if (d->itemDecorationAt(event->position().toPoint()) == -1) {
1927 } else {
1930 if (style()->styleHint(QStyle::SH_ListViewExpand_SelectMouseType, nullptr, this) == QEvent::MouseButtonRelease)
1931 d->expandOrCollapseItemAtPos(event->position().toPoint());
1932 }
1933}
1934
1939{
1940 Q_D(QTreeView);
1941 if (state() != NoState || !d->viewport->rect().contains(event->position().toPoint()))
1942 return;
1943
1944 int i = d->itemDecorationAt(event->position().toPoint());
1945 if (i == -1) {
1946 i = d->itemAtCoordinate(event->position().toPoint().y());
1947 if (i == -1)
1948 return; // user clicked outside the items
1949
1950 const QPersistentModelIndex firstColumnIndex = d->viewItems.at(i).index;
1951 const QPersistentModelIndex persistent = indexAt(event->position().toPoint());
1952
1953 if (d->pressedIndex != persistent) {
1955 return;
1956 }
1957
1958 // signal handlers may change the model
1959 emit doubleClicked(persistent);
1960
1961 if (!persistent.isValid())
1962 return;
1963
1964 if (edit(persistent, DoubleClicked, event) || state() != NoState)
1965 return; // the double click triggered editing
1966
1967 if (!style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, this))
1968 emit activated(persistent);
1969
1970 d->releaseFromDoubleClick = true;
1971 d->executePostedLayout(); // we need to make sure viewItems is updated
1972 if (d->itemsExpandable
1973 && d->expandsOnDoubleClick
1974 && d->hasVisibleChildren(persistent)) {
1975 if (!((i < d->viewItems.size()) && (d->viewItems.at(i).index == firstColumnIndex))) {
1976 // find the new index of the item
1977 for (i = 0; i < d->viewItems.size(); ++i) {
1978 if (d->viewItems.at(i).index == firstColumnIndex)
1979 break;
1980 }
1981 if (i == d->viewItems.size())
1982 return;
1983 }
1984 if (d->viewItems.at(i).expanded)
1985 d->collapse(i, true);
1986 else
1987 d->expand(i, true);
1989 viewport()->update();
1990 }
1991 }
1992}
1993
1998{
1999 Q_D(QTreeView);
2000 if (d->itemDecorationAt(event->position().toPoint()) == -1) // ### what about expanding/collapsing state ?
2002}
2003
2008{
2009 Q_D(QTreeView);
2010 QModelIndex current = currentIndex();
2011 //this is the management of the expansion
2012 if (d->isIndexValid(current) && d->model && d->itemsExpandable) {
2013 switch (event->key()) {
2014 case Qt::Key_Asterisk: {
2015 expandRecursively(current);
2016 break; }
2017 case Qt::Key_Plus:
2018 expand(current);
2019 break;
2020 case Qt::Key_Minus:
2021 collapse(current);
2022 break;
2023 }
2024 }
2025
2027}
2028
2033{
2034 Q_D(const QTreeView);
2035 d->executePostedLayout();
2036
2037 int visualIndex = d->itemAtCoordinate(point.y());
2038 QModelIndex idx = d->modelIndex(visualIndex);
2039 if (!idx.isValid())
2040 return QModelIndex();
2041
2042 if (d->viewItems.at(visualIndex).spanning)
2043 return idx;
2044
2045 int column = d->columnAt(point.x());
2046 if (column == idx.column())
2047 return idx;
2048 if (column < 0)
2049 return QModelIndex();
2050 return idx.sibling(idx.row(), column);
2051}
2052
2057{
2058 Q_D(const QTreeView);
2059 if (!d->isIndexValid(index))
2060 return QModelIndex();
2061 d->executePostedLayout();
2062 int i = d->viewIndex(index);
2063 if (--i < 0)
2064 return QModelIndex();
2065 const QModelIndex firstColumnIndex = d->viewItems.at(i).index;
2066 return firstColumnIndex.sibling(firstColumnIndex.row(), index.column());
2067}
2068
2073{
2074 Q_D(const QTreeView);
2075 if (!d->isIndexValid(index))
2076 return QModelIndex();
2077 d->executePostedLayout();
2078 int i = d->viewIndex(index);
2079 if (++i >= d->viewItems.size())
2080 return QModelIndex();
2081 const QModelIndex firstColumnIndex = d->viewItems.at(i).index;
2082 return firstColumnIndex.sibling(firstColumnIndex.row(), index.column());
2083}
2084
2091{
2092 Q_D(QTreeView);
2093 if (d->hasRemovedItems) {
2094 //clean the QSet that may contains old (and this invalid) indexes
2095 d->hasRemovedItems = false;
2096 QSet<QPersistentModelIndex>::iterator it = d->expandedIndexes.begin();
2097 while (it != d->expandedIndexes.end()) {
2098 if (!it->isValid())
2099 it = d->expandedIndexes.erase(it);
2100 else
2101 ++it;
2102 }
2103 it = d->hiddenIndexes.begin();
2104 while (it != d->hiddenIndexes.end()) {
2105 if (!it->isValid())
2106 it = d->hiddenIndexes.erase(it);
2107 else
2108 ++it;
2109 }
2110 }
2111 d->viewItems.clear(); // prepare for new layout
2112 QModelIndex parent = d->root;
2113 if (d->model->hasChildren(parent)) {
2114 d->layout(-1);
2115 }
2117 d->header->doItemsLayout();
2118}
2119
2124{
2125 Q_D(QTreeView);
2126 d->expandedIndexes.clear();
2127 d->hiddenIndexes.clear();
2128 d->spanningIndexes.clear();
2129 d->viewItems.clear();
2131}
2132
2142{
2143 Q_D(const QTreeView);
2144 return d->header->offset();
2145}
2146
2153{
2154 Q_D(const QTreeView);
2155 if (d->verticalScrollMode == QAbstractItemView::ScrollPerItem) {
2156 if (d->uniformRowHeights)
2157 return verticalScrollBar()->value() * d->defaultItemHeight;
2158 // If we are scrolling per item and have non-uniform row heights,
2159 // finding the vertical offset in pixels is going to be relatively slow.
2160 // ### find a faster way to do this
2161 d->executePostedLayout();
2162 int offset = 0;
2163 const int cnt = qMin(d->viewItems.size(), verticalScrollBar()->value());
2164 for (int i = 0; i < cnt; ++i)
2165 offset += d->itemHeight(i);
2166 return offset;
2167 }
2168 // scroll per pixel
2169 return verticalScrollBar()->value();
2170}
2171
2176QModelIndex QTreeView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
2177{
2178 Q_D(QTreeView);
2180
2181 d->executePostedLayout();
2182
2183 QModelIndex current = currentIndex();
2184 if (!current.isValid()) {
2185 int i = d->below(-1);
2186 int c = 0;
2187 while (c < d->header->count() && d->header->isSectionHidden(d->header->logicalIndex(c)))
2188 ++c;
2189 if (i < d->viewItems.size() && c < d->header->count()) {
2190 return d->modelIndex(i, d->header->logicalIndex(c));
2191 }
2192 return QModelIndex();
2193 }
2194
2195 const int vi = qMax(0, d->viewIndex(current));
2196
2197 if (isRightToLeft()) {
2198 if (cursorAction == MoveRight)
2199 cursorAction = MoveLeft;
2200 else if (cursorAction == MoveLeft)
2201 cursorAction = MoveRight;
2202 }
2203 switch (cursorAction) {
2204 case MoveNext:
2205 case MoveDown:
2206#ifdef QT_KEYPAD_NAVIGATION
2207 if (vi == d->viewItems.count()-1 && QApplicationPrivate::keypadNavigationEnabled())
2208 return d->model->index(0, current.column(), d->root);
2209#endif
2210 return d->modelIndex(d->below(vi), current.column());
2211 case MovePrevious:
2212 case MoveUp:
2213#ifdef QT_KEYPAD_NAVIGATION
2214 if (vi == 0 && QApplicationPrivate::keypadNavigationEnabled())
2215 return d->modelIndex(d->viewItems.count() - 1, current.column());
2216#endif
2217 return d->modelIndex(d->above(vi), current.column());
2218 case MoveLeft: {
2219 QScrollBar *sb = horizontalScrollBar();
2220 if (vi < d->viewItems.size() && d->viewItems.at(vi).expanded && d->itemsExpandable && sb->value() == sb->minimum()) {
2221 d->collapse(vi, true);
2222 d->moveCursorUpdatedView = true;
2223 } else {
2224 bool descend = style()->styleHint(QStyle::SH_ItemView_ArrowKeysNavigateIntoChildren, nullptr, this);
2225 if (descend) {
2226 QModelIndex par = current.parent();
2227 if (par.isValid() && par != rootIndex())
2228 return par;
2229 else
2230 descend = false;
2231 }
2232 if (!descend) {
2233 if (d->selectionBehavior == SelectItems || d->selectionBehavior == SelectColumns) {
2234 int visualColumn = d->header->visualIndex(current.column()) - 1;
2235 while (visualColumn >= 0 && isColumnHidden(d->header->logicalIndex(visualColumn)))
2236 visualColumn--;
2237 int newColumn = d->header->logicalIndex(visualColumn);
2238 QModelIndex next = current.sibling(current.row(), newColumn);
2239 if (next.isValid())
2240 return next;
2241 }
2242
2243 int oldValue = sb->value();
2244 sb->setValue(sb->value() - sb->singleStep());
2245 if (oldValue != sb->value())
2246 d->moveCursorUpdatedView = true;
2247 }
2248
2249 }
2251 viewport()->update();
2252 break;
2253 }
2254 case MoveRight:
2255 if (vi < d->viewItems.size() && !d->viewItems.at(vi).expanded && d->itemsExpandable
2256 && d->hasVisibleChildren(d->viewItems.at(vi).index)) {
2257 d->expand(vi, true);
2258 d->moveCursorUpdatedView = true;
2259 } else {
2260 bool descend = style()->styleHint(QStyle::SH_ItemView_ArrowKeysNavigateIntoChildren, nullptr, this);
2261 if (descend) {
2262 QModelIndex idx = d->modelIndex(d->below(vi));
2263 if (idx.parent() == current)
2264 return idx;
2265 else
2266 descend = false;
2267 }
2268 if (!descend) {
2269 if (d->selectionBehavior == SelectItems || d->selectionBehavior == SelectColumns) {
2270 int visualColumn = d->header->visualIndex(current.column()) + 1;
2271 while (visualColumn < d->model->columnCount(current.parent()) && isColumnHidden(d->header->logicalIndex(visualColumn)))
2272 visualColumn++;
2273 const int newColumn = d->header->logicalIndex(visualColumn);
2274 const QModelIndex next = current.sibling(current.row(), newColumn);
2275 if (next.isValid())
2276 return next;
2277 }
2278
2279 //last restort: we change the scrollbar value
2280 QScrollBar *sb = horizontalScrollBar();
2281 int oldValue = sb->value();
2282 sb->setValue(sb->value() + sb->singleStep());
2283 if (oldValue != sb->value())
2284 d->moveCursorUpdatedView = true;
2285 }
2286 }
2288 viewport()->update();
2289 break;
2290 case MovePageUp:
2291 return d->modelIndex(d->pageUp(vi), current.column());
2292 case MovePageDown:
2293 return d->modelIndex(d->pageDown(vi), current.column());
2294 case MoveHome:
2295 return d->modelIndex(d->itemForKeyHome(), current.column());
2296 case MoveEnd:
2297 return d->modelIndex(d->itemForKeyEnd(), current.column());
2298 }
2299 return current;
2300}
2301
2308void QTreeView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
2309{
2310 Q_D(QTreeView);
2311 if (!selectionModel() || rect.isNull())
2312 return;
2313
2314 d->executePostedLayout();
2315 QPoint tl(isRightToLeft() ? qMax(rect.left(), rect.right())
2316 : qMin(rect.left(), rect.right()), qMin(rect.top(), rect.bottom()));
2317 QPoint br(isRightToLeft() ? qMin(rect.left(), rect.right()) :
2318 qMax(rect.left(), rect.right()), qMax(rect.top(), rect.bottom()));
2319 QModelIndex topLeft = indexAt(tl);
2320 QModelIndex bottomRight = indexAt(br);
2321 if (!topLeft.isValid() && !bottomRight.isValid()) {
2322 if (command & QItemSelectionModel::Clear)
2323 selectionModel()->clear();
2324 return;
2325 }
2326 if (!topLeft.isValid() && !d->viewItems.isEmpty())
2327 topLeft = d->viewItems.constFirst().index;
2328 if (!bottomRight.isValid() && !d->viewItems.isEmpty()) {
2329 const int column = d->header->logicalIndex(d->header->count() - 1);
2330 const QModelIndex index = d->viewItems.constLast().index;
2331 bottomRight = index.sibling(index.row(), column);
2332 }
2333
2334 if (!d->isIndexEnabled(topLeft) || !d->isIndexEnabled(bottomRight))
2335 return;
2336
2337 d->select(topLeft, bottomRight, command);
2338}
2339
2348{
2349 Q_D(const QTreeView);
2350 if (selection.isEmpty())
2351 return QRegion();
2352
2353 QRegion selectionRegion;
2354 const QRect &viewportRect = d->viewport->rect();
2355 for (const auto &range : selection) {
2356 if (!range.isValid())
2357 continue;
2358 QModelIndex parent = range.parent();
2359 QModelIndex leftIndex = range.topLeft();
2360 int columnCount = d->model->columnCount(parent);
2361 while (leftIndex.isValid() && isIndexHidden(leftIndex)) {
2362 if (leftIndex.column() + 1 < columnCount)
2363 leftIndex = d->model->index(leftIndex.row(), leftIndex.column() + 1, parent);
2364 else
2365 leftIndex = QModelIndex();
2366 }
2367 if (!leftIndex.isValid())
2368 continue;
2369 const QRect leftRect = visualRect(leftIndex);
2370 int top = leftRect.top();
2371 QModelIndex rightIndex = range.bottomRight();
2372 while (rightIndex.isValid() && isIndexHidden(rightIndex)) {
2373 if (rightIndex.column() - 1 >= 0)
2374 rightIndex = d->model->index(rightIndex.row(), rightIndex.column() - 1, parent);
2375 else
2376 rightIndex = QModelIndex();
2377 }
2378 if (!rightIndex.isValid())
2379 continue;
2380 const QRect rightRect = visualRect(rightIndex);
2381 int bottom = rightRect.bottom();
2382 if (top > bottom)
2383 qSwap<int>(top, bottom);
2384 int height = bottom - top + 1;
2385 if (d->header->sectionsMoved()) {
2386 for (int c = range.left(); c <= range.right(); ++c) {
2387 const QRect rangeRect(columnViewportPosition(c), top, columnWidth(c), height);
2388 if (viewportRect.intersects(rangeRect))
2389 selectionRegion += rangeRect;
2390 }
2391 } else {
2392 QRect combined = leftRect|rightRect;
2393 combined.setX(columnViewportPosition(isRightToLeft() ? range.right() : range.left()));
2394 if (viewportRect.intersects(combined))
2395 selectionRegion += combined;
2396 }
2397 }
2398 return selectionRegion;
2399}
2400
2405{
2406 QModelIndexList viewSelected;
2407 QModelIndexList modelSelected;
2408 if (selectionModel())
2409 modelSelected = selectionModel()->selectedIndexes();
2410 for (int i = 0; i < modelSelected.size(); ++i) {
2411 // check that neither the parents nor the index is hidden before we add
2412 QModelIndex index = modelSelected.at(i);
2413 while (index.isValid() && !isIndexHidden(index))
2414 index = index.parent();
2415 if (index.isValid())
2416 continue;
2417 viewSelected.append(modelSelected.at(i));
2418 }
2419 return viewSelected;
2420}
2421
2426{
2427 Q_D(QTreeView);
2428
2429 d->delayedAutoScroll.stop(); // auto scroll was canceled by the user scrolling
2430
2431 dx = isRightToLeft() ? -dx : dx;
2432 if (dx) {
2433 int oldOffset = d->header->offset();
2434 d->header->d_func()->setScrollOffset(horizontalScrollBar(), horizontalScrollMode());
2436 int newOffset = d->header->offset();
2437 dx = isRightToLeft() ? newOffset - oldOffset : oldOffset - newOffset;
2438 }
2439 }
2440
2441 const int itemHeight = d->defaultItemHeight <= 0 ? sizeHintForRow(0) : d->defaultItemHeight;
2442 if (d->viewItems.isEmpty() || itemHeight == 0)
2443 return;
2444
2445 // guestimate the number of items in the viewport
2446 int viewCount = d->viewport->height() / itemHeight;
2447 int maxDeltaY = qMin(d->viewItems.size(), viewCount);
2448 // no need to do a lot of work if we are going to redraw the whole thing anyway
2449 if (qAbs(dy) > qAbs(maxDeltaY) && d->editorIndexHash.isEmpty()) {
2450 verticalScrollBar()->update();
2451 d->viewport->update();
2452 return;
2453 }
2454
2456 int currentScrollbarValue = verticalScrollBar()->value();
2457 int previousScrollbarValue = currentScrollbarValue + dy; // -(-dy)
2458 int currentViewIndex = currentScrollbarValue; // the first visible item
2459 int previousViewIndex = previousScrollbarValue;
2460 dy = 0;
2461 if (previousViewIndex < currentViewIndex) { // scrolling down
2462 for (int i = previousViewIndex; i < currentViewIndex; ++i) {
2463 if (i < d->viewItems.size())
2464 dy -= d->itemHeight(i);
2465 }
2466 } else if (previousViewIndex > currentViewIndex) { // scrolling up
2467 for (int i = previousViewIndex - 1; i >= currentViewIndex; --i) {
2468 if (i < d->viewItems.size())
2469 dy += d->itemHeight(i);
2470 }
2471 }
2472 }
2473
2474 d->scrollContentsBy(dx, dy);
2475}
2476
2481{
2482 Q_D(QTreeView);
2484 d->viewport->update();
2485}
2486
2491{
2492 // do nothing
2493}
2494
2500{
2501 Q_D(QTreeView);
2502 // if we are going to do a complete relayout anyway, there is no need to update
2503 if (d->delayedPendingLayout) {
2505 return;
2506 }
2507
2508 //don't add a hierarchy on a column != 0
2509 if (parent.column() != 0 && parent.isValid()) {
2511 return;
2512 }
2513
2514 const int parentRowCount = d->model->rowCount(parent);
2515 const int delta = end - start + 1;
2516 if (parent != d->root && !d->isIndexExpanded(parent) && parentRowCount > delta) {
2518 return;
2519 }
2520
2521 const int parentItem = d->viewIndex(parent);
2522 if (((parentItem != -1) && d->viewItems.at(parentItem).expanded)
2523 || (parent == d->root)) {
2524 d->doDelayedItemsLayout();
2525 } else if (parentItem != -1 && parentRowCount == delta) {
2526 // the parent just went from 0 children to more. update to re-paint the decoration
2527 d->viewItems[parentItem].hasChildren = true;
2528 viewport()->update();
2529 }
2531}
2532
2538{
2539 Q_D(QTreeView);
2541 d->viewItems.clear();
2542}
2543
2551{
2552 Q_D(QTreeView);
2553 d->viewItems.clear();
2554 d->doDelayedItemsLayout();
2555 d->hasRemovedItems = true;
2556 d->_q_rowsRemoved(parent, start, end);
2557}
2558
2563void QTreeView::columnCountChanged(int oldCount, int newCount)
2564{
2565 Q_D(QTreeView);
2566 if (oldCount == 0 && newCount > 0) {
2567 //if the first column has just been added we need to relayout.
2568 d->doDelayedItemsLayout();
2569 }
2570
2571 if (isVisible())
2573 viewport()->update();
2574}
2575
2582{
2583 Q_D(QTreeView);
2584 d->executePostedLayout();
2585 if (column < 0 || column >= d->header->count())
2586 return;
2588 int header = d->header->isHidden() ? 0 : d->header->sectionSizeHint(column);
2589 d->header->resizeSection(column, qMax(contents, header));
2590}
2591
2604{
2605 Q_D(QTreeView);
2606 if (column < -1)
2607 return;
2608 d->header->setSortIndicator(column, order);
2609 // If sorting is not enabled or has the same order as before, force to sort now
2610 // else sorting will be trigger through sortIndicatorChanged()
2611 if (!d->sortingEnabled ||
2612 (d->header->sortIndicatorSection() == column && d->header->sortIndicatorOrder() == order))
2613 d->model->sort(column, order);
2614}
2615
2620{
2621 Q_D(QTreeView);
2622 if (!selectionModel())
2623 return;
2624 SelectionMode mode = d->selectionMode;
2625 d->executePostedLayout(); //make sure we lay out the items
2626 if (mode != SingleSelection && mode != NoSelection && !d->viewItems.isEmpty()) {
2627 const QModelIndex &idx = d->viewItems.constLast().index;
2628 QModelIndex lastItemIndex = idx.sibling(idx.row(), d->model->columnCount(idx.parent()) - 1);
2629 d->select(d->viewItems.constFirst().index, lastItemIndex,
2632 }
2633}
2634
2639{
2640 Q_D(const QTreeView);
2641 d->executePostedLayout(); // Make sure that viewItems are up to date.
2642
2643 if (d->viewItems.size() == 0)
2645
2646 // Get rect for last item
2647 const QRect deepestRect = visualRect(d->viewItems.last().index);
2648
2649 if (!deepestRect.isValid())
2651
2652 QSize result = QSize(d->header->length(), deepestRect.bottom() + 1);
2653
2654 // add size for header
2655 result += QSize(0, d->header->isHidden() ? 0 : d->header->height());
2656
2657 return result;
2658}
2659
2673{
2674 Q_D(QTreeView);
2675 d->viewItems.clear();
2676 d->interruptDelayedItemsLayout();
2677 d->layout(-1, true);
2679 d->viewport->update();
2680}
2681
2698{
2699 Q_D(QTreeView);
2700
2701 if (depth < -1)
2702 return;
2703 // do layouting only once after expanding is done
2704 d->doDelayedItemsLayout();
2705 expand(index);
2706 if (depth == 0)
2707 return;
2709 parents.push({index, 0});
2710 while (!parents.isEmpty()) {
2711 const QPair<QModelIndex, int> elem = parents.pop();
2712 const QModelIndex &parent = elem.first;
2713 const int curDepth = elem.second;
2714 const int rowCount = d->model->rowCount(parent);
2715 for (int row = 0; row < rowCount; ++row) {
2716 const QModelIndex child = d->model->index(row, 0, parent);
2717 if (!d->isIndexValid(child))
2718 break;
2719 if (depth == -1 || curDepth + 1 < depth)
2720 parents.push({child, curDepth + 1});
2721 if (d->isIndexExpanded(child))
2722 continue;
2723 if (d->storeExpanded(child))
2725 }
2726 }
2727}
2728
2737{
2738 Q_D(QTreeView);
2739 QSet<QPersistentModelIndex> old_expandedIndexes;
2740 old_expandedIndexes = d->expandedIndexes;
2741 d->expandedIndexes.clear();
2742 if (!signalsBlocked() && isSignalConnected(QMetaMethod::fromSignal(&QTreeView::collapsed))) {
2744 for (; i != old_expandedIndexes.constEnd(); ++i) {
2745 const QPersistentModelIndex &mi = (*i);
2746 if (mi.isValid() && !(mi.flags() & Qt::ItemNeverHasChildren))
2747 emit collapsed(mi);
2748 }
2749 }
2750 doItemsLayout();
2751}
2752
2763{
2764 Q_D(QTreeView);
2765 d->viewItems.clear();
2766 QSet<QPersistentModelIndex> old_expandedIndexes;
2767 old_expandedIndexes = d->expandedIndexes;
2768 d->expandedIndexes.clear();
2769 d->interruptDelayedItemsLayout();
2770 d->layout(-1);
2771 for (int i = 0; i < d->viewItems.size(); ++i) {
2772 if (d->viewItems.at(i).level <= (uint)depth) {
2773 d->viewItems[i].expanded = true;
2774 d->layout(i);
2775 d->storeExpanded(d->viewItems.at(i).index);
2776 }
2777 }
2778
2779 bool someSignalEnabled = isSignalConnected(QMetaMethod::fromSignal(&QTreeView::collapsed));
2780 someSignalEnabled |= isSignalConnected(QMetaMethod::fromSignal(&QTreeView::expanded));
2781
2782 if (!signalsBlocked() && someSignalEnabled) {
2783 // emit signals
2784 QSet<QPersistentModelIndex> collapsedIndexes = old_expandedIndexes - d->expandedIndexes;
2786 for (; i != collapsedIndexes.constEnd(); ++i) {
2787 const QPersistentModelIndex &mi = (*i);
2788 if (mi.isValid() && !(mi.flags() & Qt::ItemNeverHasChildren))
2789 emit collapsed(mi);
2790 }
2791
2792 QSet<QPersistentModelIndex> expandedIndexs = d->expandedIndexes - old_expandedIndexes;
2793 i = expandedIndexs.constBegin();
2794 for (; i != expandedIndexs.constEnd(); ++i) {
2795 const QPersistentModelIndex &mi = (*i);
2796 if (mi.isValid() && !(mi.flags() & Qt::ItemNeverHasChildren))
2797 emit expanded(mi);
2798 }
2799 }
2800
2802 d->viewport->update();
2803}
2804
2812void QTreeView::columnResized(int column, int /* oldSize */, int /* newSize */)
2813{
2814 Q_D(QTreeView);
2815 d->columnsToUpdate.append(column);
2816 if (d->columnResizeTimerID == 0)
2817 d->columnResizeTimerID = startTimer(0);
2818}
2819
2824{
2825 Q_D(QTreeView);
2826 if (d->header) {
2827 if (d->geometryRecursionBlock)
2828 return;
2829 d->geometryRecursionBlock = true;
2830 int height = 0;
2831 if (!d->header->isHidden()) {
2832 height = qMax(d->header->minimumHeight(), d->header->sizeHint().height());
2833 height = qMin(height, d->header->maximumHeight());
2834 }
2835 setViewportMargins(0, height, 0, 0);
2836 QRect vg = d->viewport->geometry();
2837 QRect geometryRect(vg.left(), vg.top() - height, vg.width(), height);
2838 d->header->setGeometry(geometryRect);
2839 QMetaObject::invokeMethod(d->header, "updateGeometries");
2840 d->updateScrollBars();
2841 d->geometryRecursionBlock = false;
2842 }
2844}
2845
2861{
2862 Q_D(const QTreeView);
2863 d->executePostedLayout();
2864 if (d->viewItems.isEmpty())
2865 return -1;
2866 ensurePolished();
2867 int w = 0;
2868 QStyleOptionViewItem option;
2870 const QList<QTreeViewItem> viewItems = d->viewItems;
2871
2872 const int maximumProcessRows = d->header->resizeContentsPrecision(); // To avoid this to take forever.
2873
2874 int offset = 0;
2875 int start = d->firstVisibleItem(&offset);
2876 int end = d->lastVisibleItem(start, offset);
2877 if (start < 0 || end < 0 || end == viewItems.size() - 1) {
2878 end = viewItems.size() - 1;
2879 if (maximumProcessRows < 0) {
2880 start = 0;
2881 } else if (maximumProcessRows == 0) {
2882 start = qMax(0, end - 1);
2883 int remainingHeight = viewport()->height();
2884 while (start > 0 && remainingHeight > 0) {
2885 remainingHeight -= d->itemHeight(start);
2886 --start;
2887 }
2888 } else {
2889 start = qMax(0, end - maximumProcessRows);
2890 }
2891 }
2892
2893 int rowsProcessed = 0;
2894
2895 for (int i = start; i <= end; ++i) {
2896 if (viewItems.at(i).spanning)
2897 continue; // we have no good size hint
2898 QModelIndex index = viewItems.at(i).index;
2899 index = index.sibling(index.row(), column);
2900 w = d->widthHintForIndex(index, w, option, i);
2901 ++rowsProcessed;
2902 if (rowsProcessed == maximumProcessRows)
2903 break;
2904 }
2905
2906 --end;
2907 int actualBottom = viewItems.size() - 1;
2908
2909 if (maximumProcessRows == 0)
2910 rowsProcessed = 0; // skip the while loop
2911
2912 while (rowsProcessed != maximumProcessRows && (start > 0 || end < actualBottom)) {
2913 int idx = -1;
2914
2915 if ((rowsProcessed % 2 && start > 0) || end == actualBottom) {
2916 while (start > 0) {
2917 --start;
2918 if (viewItems.at(start).spanning)
2919 continue;
2920 idx = start;
2921 break;
2922 }
2923 } else {
2924 while (end < actualBottom) {
2925 ++end;
2926 if (viewItems.at(end).spanning)
2927 continue;
2928 idx = end;
2929 break;
2930 }
2931 }
2932 if (idx < 0)
2933 continue;
2934
2935 QModelIndex index = viewItems.at(idx).index;
2936 index = index.sibling(index.row(), column);
2937 w = d->widthHintForIndex(index, w, option, idx);
2938 ++rowsProcessed;
2939 }
2940 return w;
2941}
2942
2949{
2950 Q_D(const QTreeView);
2951 if (!d->isIndexValid(index) || !d->itemDelegate)
2952 return 0;
2953
2954 int start = -1;
2955 int end = -1;
2956 int indexRow = index.row();
2957 int count = d->header->count();
2958 bool emptyHeader = (count == 0);
2959 QModelIndex parent = index.parent();
2960
2961 if (count && isVisible()) {
2962 // If the sections have moved, we end up checking too many or too few
2963 start = d->header->visualIndexAt(0);
2964 } else {
2965 // If the header has not been laid out yet, we use the model directly
2966 count = d->model->columnCount(parent);
2967 }
2968
2969 if (isRightToLeft()) {
2970 start = (start == -1 ? count - 1 : start);
2971 end = 0;
2972 } else {
2973 start = (start == -1 ? 0 : start);
2974 end = count - 1;
2975 }
2976
2977 if (end < start)
2978 qSwap(end, start);
2979
2980 int height = -1;
2981 QStyleOptionViewItem option;
2983 // ### If we want word wrapping in the items,
2984 // ### we need to go through all the columns
2985 // ### and set the width of the column
2986
2987 // Hack to speed up the function
2988 option.rect.setWidth(-1);
2989
2990 for (int column = start; column <= end; ++column) {
2991 int logicalColumn = emptyHeader ? column : d->header->logicalIndex(column);
2992 if (d->header->isSectionHidden(logicalColumn))
2993 continue;
2994 QModelIndex idx = d->model->index(indexRow, logicalColumn, parent);
2995 if (idx.isValid()) {
2996 QWidget *editor = d->editorForIndex(idx).widget.data();
2997 if (editor && d->persistent.contains(editor)) {
2998 height = qMax(height, editor->sizeHint().height());
2999 int min = editor->minimumSize().height();
3000 int max = editor->maximumSize().height();
3001 height = qBound(min, height, max);
3002 }
3003 int hint = itemDelegateForIndex(idx)->sizeHint(option, idx).height();
3004 height = qMax(height, hint);
3005 }
3006 }
3007
3008 return height;
3009}
3010
3017{
3018 Q_D(const QTreeView);
3019 d->executePostedLayout();
3020 int i = d->viewIndex(index);
3021 if (i == -1)
3022 return 0;
3023 return d->itemHeight(i);
3024}
3025
3030{
3032}
3033
3038{
3039 return (isColumnHidden(index.column()) || isRowHidden(index.row(), index.parent()));
3040}
3041
3042/*
3043 private implementation
3044*/
3046{
3047 Q_Q(QTreeView);
3048
3050 updateStyledFrameWidths();
3051 q->setSelectionBehavior(QAbstractItemView::SelectRows);
3052 q->setSelectionMode(QAbstractItemView::SingleSelection);
3053 q->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
3054 q->setAttribute(Qt::WA_MacShowFocusRect);
3055
3060 q->setHeader(header);
3061#if QT_CONFIG(animation)
3062 animationsEnabled = q->style()->styleHint(QStyle::SH_Widget_Animation_Duration, nullptr, q) > 0;
3063 QObject::connect(&animatedOperation, SIGNAL(finished()), q, SLOT(_q_endAnimatedOperation()));
3064#endif // animation
3065}
3066
3067void QTreeViewPrivate::expand(int item, bool emitSignal)
3068{
3069 Q_Q(QTreeView);
3070
3071 if (item == -1 || viewItems.at(item).expanded)
3072 return;
3074 if (index.flags() & Qt::ItemNeverHasChildren)
3075 return;
3076
3077#if QT_CONFIG(animation)
3078 if (emitSignal && animationsEnabled)
3079 prepareAnimatedOperation(item, QVariantAnimation::Forward);
3080#endif // animation
3081 //if already animating, stateBeforeAnimation is set to the correct value
3086 viewItems[item].expanded = true;
3087 layout(item);
3088 q->setState(stateBeforeAnimation);
3089
3090 if (model->canFetchMore(index))
3092 if (emitSignal) {
3093 emit q->expanded(index);
3094#if QT_CONFIG(animation)
3096 beginAnimatedOperation();
3097#endif // animation
3098 }
3099}
3100
3102{
3103 viewItems.insert(pos, count, viewItem);
3105 for (int i = pos + count; i < viewItems.size(); i++)
3106 if (items[i].parentItem >= pos)
3107 items[i].parentItem += count;
3108}
3109
3111{
3114 for (int i = pos; i < viewItems.size(); i++)
3115 if (items[i].parentItem >= pos)
3116 items[i].parentItem -= count;
3117}
3118
3119#if 0
3120bool QTreeViewPrivate::checkViewItems() const
3121{
3122 for (int i = 0; i < viewItems.count(); ++i) {
3123 const QTreeViewItem &vi = viewItems.at(i);
3124 if (vi.parentItem == -1) {
3125 Q_ASSERT(!vi.index.parent().isValid() || vi.index.parent() == root);
3126 } else {
3128 }
3129 }
3130 return true;
3131}
3132#endif
3133
3134void QTreeViewPrivate::collapse(int item, bool emitSignal)
3135{
3136 Q_Q(QTreeView);
3137
3138 if (item == -1 || expandedIndexes.isEmpty())
3139 return;
3140
3141 //if the current item is now invisible, the autoscroll will expand the tree to see it, so disable the autoscroll
3143
3144 int total = viewItems.at(item).total;
3147 return; // if the index is not persistent, no chances it is expanded
3149 if (it == expandedIndexes.end() || viewItems.at(item).expanded == false)
3150 return; // nothing to do
3151
3152#if QT_CONFIG(animation)
3153 if (emitSignal && animationsEnabled)
3154 prepareAnimatedOperation(item, QVariantAnimation::Backward);
3155#endif // animation
3156
3157 //if already animating, stateBeforeAnimation is set to the correct value
3162 viewItems[item].expanded = false;
3163 int index = item;
3164 while (index > -1) {
3165 viewItems[index].total -= total;
3166 index = viewItems[index].parentItem;
3167 }
3168 removeViewItems(item + 1, total); // collapse
3169 q->setState(stateBeforeAnimation);
3170
3171 if (emitSignal) {
3172 emit q->collapsed(modelIndex);
3173#if QT_CONFIG(animation)
3175 beginAnimatedOperation();
3176#endif // animation
3177 }
3178}
3179
3180#if QT_CONFIG(animation)
3181void QTreeViewPrivate::prepareAnimatedOperation(int item, QVariantAnimation::Direction direction)
3182{
3183 animatedOperation.item = item;
3184 animatedOperation.viewport = viewport;
3185 animatedOperation.setDirection(direction);
3186
3188 QRect rect = viewport->rect();
3189 rect.setTop(top);
3191 const int limit = rect.height() * 2;
3192 int h = 0;
3193 int c = item + viewItems.at(item).total + 1;
3194 for (int i = item + 1; i < c && h < limit; ++i)
3195 h += itemHeight(i);
3196 rect.setHeight(h);
3197 animatedOperation.setEndValue(top + h);
3198 }
3199 animatedOperation.setStartValue(top);
3200 animatedOperation.before = renderTreeToPixmapForAnimation(rect);
3201}
3202
3203void QTreeViewPrivate::beginAnimatedOperation()
3204{
3205 Q_Q(QTreeView);
3206
3207 QRect rect = viewport->rect();
3208 rect.setTop(animatedOperation.top());
3209 if (animatedOperation.direction() == QVariantAnimation::Forward) {
3210 const int limit = rect.height() * 2;
3211 int h = 0;
3212 int c = animatedOperation.item + viewItems.at(animatedOperation.item).total + 1;
3213 for (int i = animatedOperation.item + 1; i < c && h < limit; ++i)
3214 h += itemHeight(i);
3215 rect.setHeight(h);
3216 animatedOperation.setEndValue(animatedOperation.top() + h);
3217 }
3218
3219 if (!rect.isEmpty()) {
3220 animatedOperation.after = renderTreeToPixmapForAnimation(rect);
3221
3223 animatedOperation.start(); //let's start the animation
3224 }
3225}
3226
3227void QTreeViewPrivate::drawAnimatedOperation(QPainter *painter) const
3228{
3229 const int start = animatedOperation.startValue().toInt(),
3230 end = animatedOperation.endValue().toInt(),
3231 current = animatedOperation.currentValue().toInt();
3232 bool collapsing = animatedOperation.direction() == QVariantAnimation::Backward;
3233 const QPixmap top = collapsing ? animatedOperation.before : animatedOperation.after;
3234 painter->drawPixmap(0, start, top, 0, end - current - 1, top.width(), top.height());
3235 const QPixmap bottom = collapsing ? animatedOperation.after : animatedOperation.before;
3237}
3238
3239QPixmap QTreeViewPrivate::renderTreeToPixmapForAnimation(const QRect &rect) const
3240{
3241 Q_Q(const QTreeView);
3242 QPixmap pixmap(rect.size() * q->devicePixelRatio());
3243 pixmap.setDevicePixelRatio(q->devicePixelRatio());
3244 if (rect.size().isEmpty())
3245 return pixmap;
3246 pixmap.fill(Qt::transparent); //the base might not be opaque, and we don't want uninitialized pixels.
3248 painter.fillRect(QRect(QPoint(0,0), rect.size()), q->palette().base());
3249 painter.translate(0, -rect.top());
3250 q->drawTree(&painter, QRegion(rect));
3251 painter.end();
3252
3253 //and now let's render the editors the editors
3254 QStyleOptionViewItem option;
3255 q->initViewItemOption(&option);
3257 QWidget *editor = it.key();
3258 const QModelIndex &index = it.value();
3259 option.rect = q->visualRect(index);
3260 if (option.rect.isValid()) {
3261
3262 if (QAbstractItemDelegate *delegate = q->itemDelegateForIndex(index))
3263 delegate->updateEditorGeometry(editor, option, index);
3264
3265 const QPoint pos = editor->pos();
3266 if (rect.contains(pos)) {
3267 editor->render(&pixmap, pos - rect.topLeft());
3268 //the animation uses pixmap to display the treeview's content
3269 //the editor is rendered on this pixmap and thus can (should) be hidden
3270 editor->hide();
3271 }
3272 }
3273 }
3274
3275
3276 return pixmap;
3277}
3278
3279void QTreeViewPrivate::_q_endAnimatedOperation()
3280{
3281 Q_Q(QTreeView);
3282 q->setState(stateBeforeAnimation);
3283 q->updateGeometries();
3284 viewport->update();
3285}
3286#endif // animation
3287
3289{
3290 viewItems.clear();
3291}
3292
3294{
3295 if (start <= 0 && 0 <= end)
3296 viewItems.clear();
3298}
3299
3301{
3302 if (start <= 0 && 0 <= end)
3305}
3306
3314void QTreeViewPrivate::layout(int i, bool recursiveExpanding, bool afterIsUninitialized)
3315{
3316 Q_Q(QTreeView);
3319
3320 if (i>=0 && !parent.isValid()) {
3321 //modelIndex() should never return something invalid for the real items.
3322 //This can happen if columncount has been set to 0.
3323 //To avoid infinite loop we stop here.
3324 return;
3325 }
3326
3327#if QT_CONFIG(accessibility)
3328 // QAccessibleTree's rowCount implementation uses viewItems.size(), so
3329 // we need to invalidate any cached accessibility data structures if
3330 // that value changes during the run of this function.
3331 const auto resetModelIfNeeded = qScopeGuard([oldViewItemsSize = viewItems.size(), this, q]{
3332 if (oldViewItemsSize != viewItems.size() && QAccessible::isActive()) {
3333 QAccessibleTableModelChangeEvent event(q, QAccessibleTableModelChangeEvent::ModelReset);
3334 QAccessible::updateAccessibility(&event);
3335 }
3336 });
3337#endif
3338
3339 int count = 0;
3340 if (model->hasChildren(parent)) {
3341 if (model->canFetchMore(parent)) {
3342 // fetchMore first, otherwise we might not yet have any data for sizeHintForRow
3344 // guestimate the number of items in the viewport, and fetch as many as might fit
3345 const int itemHeight = defaultItemHeight <= 0 ? q->sizeHintForRow(0) : defaultItemHeight;
3346 const int viewCount = itemHeight ? viewport->height() / itemHeight : 0;
3347 int lastCount = -1;
3348 while ((count = model->rowCount(parent)) < viewCount &&
3349 count != lastCount && model->canFetchMore(parent)) {
3351 lastCount = count;
3352 }
3353 } else {
3355 }
3356 }
3357
3358 bool expanding = true;
3359 if (i == -1) {
3360 if (uniformRowHeights) {
3362 defaultItemHeight = q->indexRowSizeHint(index);
3363 }
3364 viewItems.resize(count);
3365 afterIsUninitialized = true;
3366 } else if (viewItems[i].total != (uint)count) {
3367 if (!afterIsUninitialized)
3368 insertViewItems(i + 1, count, QTreeViewItem()); // expand
3369 else if (count > 0)
3370 viewItems.resize(viewItems.size() + count);
3371 } else {
3372 expanding = false;
3373 }
3374
3375 int first = i + 1;
3376 int level = (i >= 0 ? viewItems.at(i).level + 1 : 0);
3377 int hidden = 0;
3378 int last = 0;
3379 int children = 0;
3380 QTreeViewItem *item = nullptr;
3381 for (int j = first; j < first + count; ++j) {
3382 current = model->index(j - first, 0, parent);
3383 if (isRowHidden(current)) {
3384 ++hidden;
3385 last = j - hidden + children;
3386 } else {
3387 last = j - hidden + children;
3388 if (item)
3389 item->hasMoreSiblings = true;
3390 item = &viewItems[last];
3391 item->index = current;
3392 item->parentItem = i;
3393 item->level = level;
3394 item->height = 0;
3395 item->spanning = q->isFirstColumnSpanned(current.row(), parent);
3396 item->expanded = false;
3397 item->total = 0;
3398 item->hasMoreSiblings = false;
3399 if ((recursiveExpanding && !(current.flags() & Qt::ItemNeverHasChildren)) || isIndexExpanded(current)) {
3400 if (recursiveExpanding && storeExpanded(current) && !q->signalsBlocked())
3401 emit q->expanded(current);
3402 item->expanded = true;
3403 layout(last, recursiveExpanding, afterIsUninitialized);
3404 item = &viewItems[last];
3405 children += item->total;
3406 item->hasChildren = item->total > 0;
3407 last = j - hidden + children;
3408 } else {
3409 item->hasChildren = hasVisibleChildren(current);
3410 }
3411 }
3412 }
3413
3414 // remove hidden items
3415 if (hidden > 0) {
3416 if (!afterIsUninitialized)
3417 removeViewItems(last + 1, hidden);
3418 else
3419 viewItems.resize(viewItems.size() - hidden);
3420 }
3421
3422 if (!expanding)
3423 return; // nothing changed
3424
3425 while (i > -1) {
3426 viewItems[i].total += count - hidden;
3427 i = viewItems[i].parentItem;
3428 }
3429}
3430
3432{
3435 index--;
3436 if (index == -1)
3437 index = 0;
3439 index++;
3440 return index >= viewItems.size() ? 0 : index;
3441}
3442
3444{
3447 index++;
3448 if (index == -1 || index >= viewItems.size())
3449 index = viewItems.size() - 1;
3451 index--;
3452 return index == -1 ? viewItems.size() - 1 : index;
3453}
3454
3456{
3457 int index = 0;
3459 index++;
3460 return index >= viewItems.size() ? 0 : index;
3461}
3462
3464{
3465 int index = viewItems.size() - 1;
3467 index--;
3468 return index == -1 ? viewItems.size() - 1 : index;
3469}
3470
3472{
3473 if (item < 0 || item >= viewItems.size())
3474 return 0;
3475 int level = viewItems.at(item).level;
3476 if (rootDecoration)
3477 ++level;
3478 return level * indent;
3479}
3480
3482{
3485 return defaultItemHeight;
3486 if (viewItems.isEmpty())
3487 return 0;
3489 if (!index.isValid())
3490 return 0;
3491 int height = viewItems.at(item).height;
3492 if (height <= 0) {
3493 height = q_func()->indexRowSizeHint(index);
3494 viewItems[item].height = height;
3495 }
3496 return qMax(height, 0);
3497}
3498
3499
3505{
3508 return (item * defaultItemHeight) - vbar->value();
3509 // ### optimize (maybe do like QHeaderView by letting items have startposition)
3510 int y = 0;
3511 for (int i = 0; i < viewItems.size(); ++i) {
3512 if (i == item)
3513 return y - vbar->value();
3514 y += itemHeight(i);
3515 }
3516 } else { // ScrollPerItem
3517 int topViewItemIndex = vbar->value();
3519 return defaultItemHeight * (item - topViewItemIndex);
3520 if (item >= topViewItemIndex) {
3521 // search in the visible area first and continue down
3522 // ### slow if the item is not visible
3523 int viewItemCoordinate = 0;
3524 int viewItemIndex = topViewItemIndex;
3525 while (viewItemIndex < viewItems.size()) {
3526 if (viewItemIndex == item)
3527 return viewItemCoordinate;
3528 viewItemCoordinate += itemHeight(viewItemIndex);
3529 ++viewItemIndex;
3530 }
3531 // below the last item in the view
3532 Q_ASSERT(false);
3533 return viewItemCoordinate;
3534 } else {
3535 // search the area above the viewport (used for editor widgets)
3536 int viewItemCoordinate = 0;
3537 for (int viewItemIndex = topViewItemIndex; viewItemIndex > 0; --viewItemIndex) {
3538 if (viewItemIndex == item)
3539 return viewItemCoordinate;
3540 viewItemCoordinate -= itemHeight(viewItemIndex - 1);
3541 }
3542 return viewItemCoordinate;
3543 }
3544 }
3545 return 0;
3546}
3547
3555int QTreeViewPrivate::itemAtCoordinate(int coordinate) const
3556{
3557 const int itemCount = viewItems.size();
3558 if (itemCount == 0)
3559 return -1;
3561 return -1;
3563 if (uniformRowHeights) {
3564 const int viewItemIndex = (coordinate + vbar->value()) / defaultItemHeight;
3565 return ((viewItemIndex >= itemCount || viewItemIndex < 0) ? -1 : viewItemIndex);
3566 }
3567 // ### optimize
3568 int viewItemCoordinate = 0;
3569 const int contentsCoordinate = coordinate + vbar->value();
3570 for (int viewItemIndex = 0; viewItemIndex < viewItems.size(); ++viewItemIndex) {
3571 viewItemCoordinate += itemHeight(viewItemIndex);
3572 if (viewItemCoordinate > contentsCoordinate)
3573 return (viewItemIndex >= itemCount ? -1 : viewItemIndex);
3574 }
3575 } else { // ScrollPerItem
3576 int topViewItemIndex = vbar->value();
3577 if (uniformRowHeights) {
3578 if (coordinate < 0)
3579 coordinate -= defaultItemHeight - 1;
3580 const int viewItemIndex = topViewItemIndex + (coordinate / defaultItemHeight);
3581 return ((viewItemIndex >= itemCount || viewItemIndex < 0) ? -1 : viewItemIndex);
3582 }
3583 if (coordinate >= 0) {
3584 // the coordinate is in or below the viewport
3585 int viewItemCoordinate = 0;
3586 for (int viewItemIndex = topViewItemIndex; viewItemIndex < viewItems.size(); ++viewItemIndex) {
3587 viewItemCoordinate += itemHeight(viewItemIndex);
3588 if (viewItemCoordinate > coordinate)
3589 return (viewItemIndex >= itemCount ? -1 : viewItemIndex);
3590 }
3591 } else {
3592 // the coordinate is above the viewport
3593 int viewItemCoordinate = 0;
3594 for (int viewItemIndex = topViewItemIndex; viewItemIndex >= 0; --viewItemIndex) {
3595 if (viewItemCoordinate <= coordinate)
3596 return (viewItemIndex >= itemCount ? -1 : viewItemIndex);
3597 viewItemCoordinate -= itemHeight(viewItemIndex);
3598 }
3599 }
3600 }
3601 return -1;
3602}
3603
3605{
3606 if (!_index.isValid() || viewItems.isEmpty())
3607 return -1;
3608
3609 const int totalCount = viewItems.size();
3610 const QModelIndex index = _index.sibling(_index.row(), 0);
3611 const int row = index.row();
3612 const quintptr internalId = index.internalId();
3613
3614 // We start nearest to the lastViewedItem
3615 int localCount = qMin(lastViewedItem - 1, totalCount - lastViewedItem);
3616 for (int i = 0; i < localCount; ++i) {
3617 const QModelIndex &idx1 = viewItems.at(lastViewedItem + i).index;
3618 if (idx1.row() == row && idx1.internalId() == internalId) {
3620 return lastViewedItem;
3621 }
3622 const QModelIndex &idx2 = viewItems.at(lastViewedItem - i - 1).index;
3623 if (idx2.row() == row && idx2.internalId() == internalId) {
3625 return lastViewedItem;
3626 }
3627 }
3628
3629 for (int j = qMax(0, lastViewedItem + localCount); j < totalCount; ++j) {
3630 const QModelIndex &idx = viewItems.at(j).index;
3631 if (idx.row() == row && idx.internalId() == internalId) {
3632 lastViewedItem = j;
3633 return j;
3634 }
3635 }
3636 for (int j = qMin(totalCount, lastViewedItem - localCount) - 1; j >= 0; --j) {
3637 const QModelIndex &idx = viewItems.at(j).index;
3638 if (idx.row() == row && idx.internalId() == internalId) {
3639 lastViewedItem = j;
3640 return j;
3641 }
3642 }
3643
3644 // nothing found
3645 return -1;
3646}
3647
3649{
3650 if (i < 0 || i >= viewItems.size())
3651 return QModelIndex();
3652
3654 if (column)
3655 ret = ret.sibling(ret.row(), column);
3656 return ret;
3657}
3658
3660{
3661 const int value = vbar->value();
3663 if (offset)
3664 *offset = 0;
3665 return (value < 0 || value >= viewItems.size()) ? -1 : value;
3666 }
3667 // ScrollMode == ScrollPerPixel
3668 if (uniformRowHeights) {
3669 if (!defaultItemHeight)
3670 return -1;
3671
3672 if (offset)
3674 return value / defaultItemHeight;
3675 }
3676 int y = 0; // ### (maybe do like QHeaderView by letting items have startposition)
3677 for (int i = 0; i < viewItems.size(); ++i) {
3678 y += itemHeight(i); // the height value is cached
3679 if (y > value) {
3680 if (offset)
3681 *offset = y - value - itemHeight(i);
3682 return i;
3683 }
3684 }
3685 return -1;
3686}
3687
3688int QTreeViewPrivate::lastVisibleItem(int firstVisual, int offset) const
3689{
3690 if (firstVisual < 0 || offset < 0) {
3691 firstVisual = firstVisibleItem(&offset);
3692 if (firstVisual < 0)
3693 return -1;
3694 }
3695 int y = - offset;
3696 int value = viewport->height();
3697
3698 for (int i = firstVisual; i < viewItems.size(); ++i) {
3699 y += itemHeight(i); // the height value is cached
3700 if (y > value)
3701 return i;
3702 }
3703 return viewItems.size() - 1;
3704}
3705
3707{
3708 return header->logicalIndexAt(x);
3709}
3710
3712{
3713 Q_Q(QTreeView);
3714 QSize viewportSize = viewport->size();
3715 if (!viewportSize.isValid())
3716 viewportSize = QSize(0, 0);
3717
3719 if (viewItems.isEmpty()) {
3720 q->doItemsLayout();
3721 }
3722
3723 int itemsInViewport = 0;
3724 if (uniformRowHeights) {
3725 if (defaultItemHeight <= 0)
3726 itemsInViewport = viewItems.size();
3727 else
3728 itemsInViewport = viewportSize.height() / defaultItemHeight;
3729 } else {
3730 const int itemsCount = viewItems.size();
3731 const int viewportHeight = viewportSize.height();
3732 for (int height = 0, item = itemsCount - 1; item >= 0; --item) {
3734 if (height > viewportHeight)
3735 break;
3736 ++itemsInViewport;
3737 }
3738 }
3740 if (!viewItems.isEmpty())
3741 itemsInViewport = qMax(1, itemsInViewport);
3742 vbar->setRange(0, viewItems.size() - itemsInViewport);
3743 vbar->setPageStep(itemsInViewport);
3744 vbar->setSingleStep(1);
3745 } else { // scroll per pixel
3746 int contentsHeight = 0;
3747 if (uniformRowHeights) {
3748 contentsHeight = defaultItemHeight * viewItems.size();
3749 } else { // ### (maybe do like QHeaderView by letting items have startposition)
3750 for (int i = 0; i < viewItems.size(); ++i)
3751 contentsHeight += itemHeight(i);
3752 }
3753 vbar->setRange(0, contentsHeight - viewportSize.height());
3754 vbar->setPageStep(viewportSize.height());
3755 vbar->d_func()->itemviewChangeSingleStep(qMax(viewportSize.height() / (itemsInViewport + 1), 2));
3756 }
3757
3758 const int columnCount = header->count();
3759 const int viewportWidth = viewportSize.width();
3760 int columnsInViewport = 0;
3761 for (int width = 0, column = columnCount - 1; column >= 0; --column) {
3762 int logical = header->logicalIndex(column);
3763 width += header->sectionSize(logical);
3764 if (width > viewportWidth)
3765 break;
3766 ++columnsInViewport;
3767 }
3768 if (columnCount > 0)
3769 columnsInViewport = qMax(1, columnsInViewport);
3771 hbar->setRange(0, columnCount - columnsInViewport);
3772 hbar->setPageStep(columnsInViewport);
3773 hbar->setSingleStep(1);
3774 } else { // scroll per pixel
3775 const int horizontalLength = header->length();
3776 const QSize maxSize = q->maximumViewportSize();
3777 if (maxSize.width() >= horizontalLength && vbar->maximum() <= 0)
3778 viewportSize = maxSize;
3779 hbar->setPageStep(viewportSize.width());
3780 hbar->setRange(0, qMax(horizontalLength - viewportSize.width(), 0));
3781 hbar->d_func()->itemviewChangeSingleStep(qMax(viewportSize.width() / (columnsInViewport + 1), 2));
3782 }
3783}
3784
3786{
3787 Q_Q(const QTreeView);
3789 bool spanned = false;
3790 if (!spanningIndexes.isEmpty()) {
3791 const QModelIndex index = q->indexAt(pos);
3792 if (index.isValid())
3793 spanned = q->isFirstColumnSpanned(index.row(), index.parent());
3794 }
3795 const int column = spanned ? 0 : header->logicalIndexAt(pos.x());
3796 if (!isTreePosition(column))
3797 return -1; // no logical index at x
3798
3799 int viewItemIndex = itemAtCoordinate(pos.y());
3800 QRect returning = itemDecorationRect(modelIndex(viewItemIndex));
3801 if (!returning.contains(pos))
3802 return -1;
3803
3804 return viewItemIndex;
3805}
3806
3808{
3809 Q_Q(const QTreeView);
3810 if (!rootDecoration && index.parent() == root)
3811 return QRect(); // no decoration at root
3812
3813 int viewItemIndex = viewIndex(index);
3814 if (viewItemIndex < 0 || !hasVisibleChildren(viewItems.at(viewItemIndex).index))
3815 return QRect();
3816
3817 int itemIndentation = indentationForItem(viewItemIndex);
3820
3821 QRect rect;
3822 if (q->isRightToLeft())
3823 rect = QRect(position + size - itemIndentation, coordinateForItem(viewItemIndex),
3824 indent, itemHeight(viewItemIndex));
3825 else
3826 rect = QRect(position + itemIndentation - indent, coordinateForItem(viewItemIndex),
3827 indent, itemHeight(viewItemIndex));
3829 opt.initFrom(q);
3830 opt.rect = rect;
3831 return q->style()->subElementRect(QStyle::SE_TreeViewDisclosureItem, &opt, q);
3832}
3833
3835 const QModelIndex &bottomIndex) const
3836{
3837 const int topVisual = header->visualIndex(topIndex.column()),
3838 bottomVisual = header->visualIndex(bottomIndex.column());
3839
3840 const int start = qMin(topVisual, bottomVisual);
3841 const int end = qMax(topVisual, bottomVisual);
3842
3843 QList<int> logicalIndexes;
3844
3845 //we iterate over the visual indexes to get the logical indexes
3846 for (int c = start; c <= end; c++) {
3847 const int logical = header->logicalIndex(c);
3848 if (!header->isSectionHidden(logical)) {
3849 logicalIndexes << logical;
3850 }
3851 }
3852 //let's sort the list
3853 std::sort(logicalIndexes.begin(), logicalIndexes.end());
3854
3857 current.first = -2; // -1 is not enough because -1+1 = 0
3858 current.second = -2;
3859 for(int i = 0; i < logicalIndexes.size(); ++i) {
3860 const int logicalColumn = logicalIndexes.at(i);
3861 if (current.second + 1 != logicalColumn) {
3862 if (current.first != -2) {
3863 //let's save the current one
3864 ret += current;
3865 }
3866 //let's start a new one
3867 current.first = current.second = logicalColumn;
3868 } else {
3869 current.second++;
3870 }
3871 }
3872
3873 //let's get the last range
3874 if (current.first != -2) {
3875 ret += current;
3876 }
3877
3878 return ret;
3879}
3880
3881void QTreeViewPrivate::select(const QModelIndex &topIndex, const QModelIndex &bottomIndex,
3882 QItemSelectionModel::SelectionFlags command)
3883{
3884 Q_Q(QTreeView);
3886 const int top = viewIndex(topIndex),
3887 bottom = viewIndex(bottomIndex);
3888
3889 const QList<QPair<int, int>> colRanges = columnRanges(topIndex, bottomIndex);
3890 QList<QPair<int, int>>::const_iterator it;
3891 for (it = colRanges.begin(); it != colRanges.end(); ++it) {
3892 const int left = (*it).first,
3893 right = (*it).second;
3894
3895 QModelIndex previous;
3896 QItemSelectionRange currentRange;
3897 QStack<QItemSelectionRange> rangeStack;
3898 for (int i = top; i <= bottom; ++i) {
3900 QModelIndex parent = index.parent();
3901 QModelIndex previousParent = previous.parent();
3902 if (previous.isValid() && parent == previousParent) {
3903 // same parent
3904 if (qAbs(previous.row() - index.row()) > 1) {
3905 //a hole (hidden index inside a range) has been detected
3906 if (currentRange.isValid()) {
3907 selection.append(currentRange);
3908 }
3909 //let's start a new range
3910 currentRange = QItemSelectionRange(index.sibling(index.row(), left), index.sibling(index.row(), right));
3911 } else {
3912 QModelIndex tl = model->index(currentRange.top(), currentRange.left(),
3913 currentRange.parent());
3914 currentRange = QItemSelectionRange(tl, index.sibling(index.row(), right));
3915 }
3916 } else if (previous.isValid() && parent == model->index(previous.row(), 0, previousParent)) {
3917 // item is child of previous
3918 rangeStack.push(currentRange);
3919 currentRange = QItemSelectionRange(index.sibling(index.row(), left), index.sibling(index.row(), right));
3920 } else {
3921 if (currentRange.isValid())
3922 selection.append(currentRange);
3923 if (rangeStack.isEmpty()) {
3924 currentRange = QItemSelectionRange(index.sibling(index.row(), left), index.sibling(index.row(), right));
3925 } else {
3926 currentRange = rangeStack.pop();
3927 index = currentRange.bottomRight(); //let's resume the range
3928 --i; //we process again the current item
3929 }
3930 }
3931 previous = index;
3932 }
3933 if (currentRange.isValid())
3934 selection.append(currentRange);
3935 for (int i = 0; i < rangeStack.size(); ++i)
3936 selection.append(rangeStack.at(i));
3937 }
3938 q->selectionModel()->select(selection, command);
3939}
3940
3942{
3943 Q_Q(const QTreeView);
3944 int start = header->visualIndexAt(rect.left());
3945 int end = header->visualIndexAt(rect.right());
3946 if (q->isRightToLeft()) {
3947 start = (start == -1 ? header->count() - 1 : start);
3948 end = (end == -1 ? 0 : end);
3949 } else {
3950 start = (start == -1 ? 0 : start);
3951 end = (end == -1 ? header->count() - 1 : end);
3952 }
3953 return qMakePair(qMin(start, end), qMax(start, end));
3954}
3955
3957{
3958 Q_Q(const QTreeView);
3959 if (parent.flags() & Qt::ItemNeverHasChildren)
3960 return false;
3961 if (model->hasChildren(parent)) {
3962 if (hiddenIndexes.isEmpty())
3963 return true;
3964 if (q->isIndexHidden(parent))
3965 return false;
3966 int rowCount = model->rowCount(parent);
3967 for (int i = 0; i < rowCount; ++i) {
3968 if (!q->isRowHidden(i, parent))
3969 return true;
3970 }
3971 if (rowCount == 0)
3972 return true;
3973 }
3974 return false;
3975}
3976
3978{
3979 model->sort(column, order);
3980}
3981
3983{
3984 Q_Q(const QTreeView);
3985
3986 // Note that this will include the header, even if its hidden.
3987 return (q->visualIndex(index) + (q->header() ? 1 : 0)) * index.model()->columnCount() + index.column();
3988}
3989
3991{
3992 Q_Q(const QTreeView);
3993 indent = q->style()->pixelMetric(QStyle::PM_TreeViewIndentation, nullptr, q);
3994}
3995
3999void QTreeView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
4000{
4001 QAbstractItemView::currentChanged(current, previous);
4002
4003 if (allColumnsShowFocus()) {
4004 if (previous.isValid()) {
4005 QRect previousRect = visualRect(previous);
4006 previousRect.setX(0);
4007 previousRect.setWidth(viewport()->width());
4008 viewport()->update(previousRect);
4009 }
4010 if (current.isValid()) {
4011 QRect currentRect = visualRect(current);
4012 currentRect.setX(0);
4013 currentRect.setWidth(viewport()->width());
4014 viewport()->update(currentRect);
4015 }
4016 }
4017#if QT_CONFIG(accessibility)
4018 if (QAccessible::isActive() && current.isValid()) {
4019 Q_D(QTreeView);
4020
4021 QAccessibleEvent event(this, QAccessible::Focus);
4022 event.setChild(d->accessibleTree2Index(current));
4023 QAccessible::updateAccessibility(&event);
4024 }
4025#endif
4026}
4027
4032 const QItemSelection &deselected)
4033{
4034 QAbstractItemView::selectionChanged(selected, deselected);
4035#if QT_CONFIG(accessibility)
4036 if (QAccessible::isActive()) {
4037 Q_D(QTreeView);
4038
4039 // ### does not work properly for selection ranges.
4040 QModelIndex sel = selected.indexes().value(0);
4041 if (sel.isValid()) {
4042 int entry = d->accessibleTree2Index(sel);
4043 Q_ASSERT(entry >= 0);
4044 QAccessibleEvent event(this, QAccessible::SelectionAdd);
4045 event.setChild(entry);
4046 QAccessible::updateAccessibility(&event);
4047 }
4048 QModelIndex desel = deselected.indexes().value(0);
4049 if (desel.isValid()) {
4050 int entry = d->accessibleTree2Index(desel);
4051 Q_ASSERT(entry >= 0);
4052 QAccessibleEvent event(this, QAccessible::SelectionRemove);
4053 event.setChild(entry);
4054 QAccessible::updateAccessibility(&event);
4055 }
4056 }
4057#endif
4058}
4059
4060int QTreeView::visualIndex(const QModelIndex &index) const
4061{
4062 Q_D(const QTreeView);
4063 d->executePostedLayout();
4064 return d->viewIndex(index);
4065}
4066
4072{
4073 Q_D(QTreeView);
4074 if (!d->viewItems.isEmpty() && value == verticalScrollBar()->maximum()) {
4075 QModelIndex ret = d->viewItems.last().index;
4076 // Root index will be handled by base class implementation
4077 while (ret.isValid()) {
4078 if (isExpanded(ret) && d->model->canFetchMore(ret)) {
4079 d->model->fetchMore(ret);
4080 break;
4081 }
4082 ret = ret.parent();
4083 }
4084 }
4086}
4087
4089
4090#include "moc_qtreeview.cpp"
Direction
This enum describes the direction of the animation when in \l Running state.
The QAbstractItemDelegate class is used to display and edit data items from a model.
virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const =0
This pure abstract function must be reimplemented if you want to provide custom rendering.
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const =0
This pure abstract function must be reimplemented if you want to provide custom rendering.
static QAbstractItemModel * staticEmptyModel()
virtual Q_INVOKABLE bool hasChildren(const QModelIndex &parent=QModelIndex()) const
Returns {true} if parent has any children; otherwise returns {false}.
virtual Q_INVOKABLE int rowCount(const QModelIndex &parent=QModelIndex()) const =0
Returns the number of rows under the given parent.
virtual Q_INVOKABLE void fetchMore(const QModelIndex &parent)
Fetches any available data for the items with the parent specified by the parent index.
virtual Q_INVOKABLE bool canFetchMore(const QModelIndex &parent) const
Returns {true} if there is more data available for parent; otherwise returns {false}.
virtual Q_INVOKABLE int columnCount(const QModelIndex &parent=QModelIndex()) const =0
Returns the number of columns for the children of the given parent.
virtual Q_INVOKABLE QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const =0
Returns the index of the item in the model specified by the given row, column and parent index.
const QEditorInfo & editorForIndex(const QModelIndex &index) const
virtual QItemViewPaintPairs draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const
void doDelayedItemsLayout(int delay=0)
QPointer< QAbstractItemDelegate > itemDelegate
QAbstractItemView::ScrollMode horizontalScrollMode
virtual void _q_columnsRemoved(const QModelIndex &parent, int start, int end)
QAbstractItemView::State state
bool isPersistent(const QModelIndex &index) const
QAbstractItemView::State stateBeforeAnimation
QPersistentModelIndex root
virtual void _q_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
QWidget * editor(const QModelIndex &index, const QStyleOptionViewItem &options)
QAbstractItemView::SelectionBehavior selectionBehavior
QAbstractItemView::ScrollMode verticalScrollMode
The QAbstractItemView class provides the basic functionality for item view classes.
SelectionMode
This enum indicates how the view responds to user selections:
QWidget * indexWidget(const QModelIndex &index) const
void activated(const QModelIndex &index)
This signal is emitted when the item specified by index is activated by the user.
QAbstractItemModel * model() const
Returns the model that this view is presenting.
void setCurrentIndex(const QModelIndex &index)
Sets the current item to be the item at index.
void doubleClicked(const QModelIndex &index)
This signal is emitted when a mouse button is double-clicked.
virtual void setSelectionModel(QItemSelectionModel *selectionModel)
Sets the current selection model to the given selectionModel.
void timerEvent(QTimerEvent *event) override
This function is called with the given event when a timer event is sent to the widget.
ScrollMode verticalScrollMode
how the view scrolls its contents in the vertical direction
virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
This slot is called when the selection is changed.
State state() const
Returns the item view's state.
virtual void reset()
Reset the internal state of the view.
void mouseReleaseEvent(QMouseEvent *event) override
This function is called with the given event when a mouse button is released, after a mouse press eve...
virtual QAbstractItemDelegate * itemDelegateForIndex(const QModelIndex &index) const
virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles=QList< int >())
This slot is called when items with the given roles are changed in the model.
QModelIndex currentIndex() const
Returns the model index of the current item.
bool viewportEvent(QEvent *event) override
This function is used to handle tool tips, and What's This? mode, if the given event is a QEvent::Too...
virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
This slot is called when rows are about to be removed.
virtual void setModel(QAbstractItemModel *model)
Sets the model for the view to present.
ScrollMode horizontalScrollMode
how the view scrolls its contents in the horizontal direction
virtual void setRootIndex(const QModelIndex &index)
Sets the root item to the item at the given index.
virtual void initViewItemOption(QStyleOptionViewItem *option) const
virtual void doItemsLayout()
void keyPressEvent(QKeyEvent *event) override
This function is called with the given event when a key event is sent to the widget.
QModelIndex rootIndex() const
Returns the model index of the model's root item.
CursorAction
This enum describes the different ways to navigate between items,.
virtual void currentChanged(const QModelIndex &current, const QModelIndex &previous)
This slot is called when a new item becomes the current item.
ScrollHint
\value EnsureVisible Scroll to ensure that the item is visible.
void mousePressEvent(QMouseEvent *event) override
This function is called with the given event when a mouse button is pressed while the cursor is insid...
virtual void rowsInserted(const QModelIndex &parent, int start, int end)
This slot is called when rows are inserted.
virtual void updateEditorGeometries()
void edit(const QModelIndex &index)
Starts editing the item corresponding to the given index if it is editable.
void setState(State state)
Sets the item view's state to the given state.
void mouseMoveEvent(QMouseEvent *event) override
This function is called with the given event when a mouse move event is sent to the widget.
QItemSelectionModel * selectionModel() const
Returns the current selection model.
virtual void verticalScrollbarValueChanged(int value)
virtual void updateGeometries()
SelectionBehavior selectionBehavior
which selection behavior the view uses
QSize viewportSizeHint() const override
virtual void horizontalScrollbarAction(int action)
virtual int sizeHintForRow(int row) const
Returns the height size hint for the specified row or -1 if there is no model.
int value
the slider's current value
int singleStep
the single step.
int minimum
the sliders's minimum value
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
Returns the index of the data in row and column with parent.
static QWidget * focusWidget()
Returns the application widget that has the keyboard input focus, or \nullptr if no widget in this ap...
int keyboardInputInterval
the time limit in milliseconds that distinguishes a key press from two consecutive key presses
void stop()
Stops the timer.
static QPoint pos()
Returns the position of the cursor (hot spot) of the primary screen in global screen coordinates.
Definition qcursor.cpp:188
\inmodule QtCore
Definition qcoreevent.h:45
@ StyleChange
Definition qcoreevent.h:136
@ MouseButtonPress
Definition qcoreevent.h:60
@ HoverLeave
Definition qcoreevent.h:176
@ HoverEnter
Definition qcoreevent.h:175
@ HoverMove
Definition qcoreevent.h:177
@ MouseButtonRelease
Definition qcoreevent.h:61
QGraphicsItem * parentItem() const
Returns a pointer to this item's parent item.
const_iterator constEnd() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
Definition qhash.h:1209
const_iterator constBegin() const noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1205
The QHeaderView class provides a header row or header column for item views.
Definition qheaderview.h:18
void setSortIndicatorShown(bool show)
int sectionPosition(int logicalIndex) const
Returns the section position of the given logicalIndex, or -1 if the section is hidden.
void setSectionsClickable(bool clickable)
int sectionViewportPosition(int logicalIndex) const
Returns the section viewport position of the given logicalIndex.
bool isSectionHidden(int logicalIndex) const
Returns true if the section specified by logicalIndex is explicitly hidden from the user; otherwise r...
int sectionSize(int logicalIndex) const
Returns the width (or height for vertical headers) of the given logicalIndex.
bool sectionsMoved() const
Returns true if sections in the header has been moved; otherwise returns false;.
void setStretchLastSection(bool stretch)
int logicalIndexAt(int position) const
Returns the section that covers the given position in the viewport.
void setDefaultAlignment(Qt::Alignment alignment)
int logicalIndex(int visualIndex) const
Returns the logicalIndex for the section at the given visualIndex position, or -1 if visualIndex < 0 ...
int length() const
Returns the length along the orientation of the header.
int visualIndexAt(int position) const
Returns the visual index of the section that covers the given position in the viewport.
int visualIndex(int logicalIndex) const
Returns the visual index position of the section specified by the given logicalIndex,...
void setSectionsMovable(bool movable)
int offset() const
Returns the offset of the header: this is the header's left-most (or top-most for vertical headers) v...
int count() const
Returns the number of sections in the header.
\inmodule QtGui
Definition qevent.h:245
QModelIndexList selectedIndexes
virtual void clear()
Clears the selection model.
int top() const
Returns the row index corresponding to the uppermost selected row in the selection range.
const QPersistentModelIndex & bottomRight() const
Returns the index for the item located at the bottom-right corner of the selection range.
QModelIndex parent() const
Returns the parent model item index of the items in the selection range.
int left() const
Returns the column index corresponding to the leftmost selected column in the selection range.
bool isValid() const
Returns true if the selection range is valid; otherwise returns false.
\inmodule QtCore
Q_CORE_EXPORT QModelIndexList indexes() const
Returns a list of model indexes that correspond to the selected items.
The QKeyEvent class describes a key event.
Definition qevent.h:423
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
iterator insert(qsizetype i, parameter_type t)
Definition qlist.h:471
iterator end()
Definition qlist.h:609
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
T value(qsizetype i) const
Definition qlist.h:661
void remove(qsizetype i, qsizetype n=1)
Definition qlist.h:787
qsizetype count() const noexcept
Definition qlist.h:387
iterator begin()
Definition qlist.h:608
pointer data()
Definition qlist.h:414
void resize(qsizetype size)
Definition qlist.h:392
void append(parameter_type t)
Definition qlist.h:441
void clear()
Definition qlist.h:417
static QMetaMethod fromSignal(PointerToMemberFunction signal)
\inmodule QtCore
constexpr int row() const noexcept
Returns the row this model index refers to.
QModelIndex parent() const
Returns the parent of the model index, or QModelIndex() if it has no parent.
constexpr int column() const noexcept
Returns the column this model index refers to.
constexpr bool isValid() const noexcept
Returns {true} if this model index is valid; otherwise returns {false}.
QModelIndex sibling(int row, int column) const
Returns the sibling at row and column.
constexpr quintptr internalId() const noexcept
Returns a {quintptr} used by the model to associate the index with the internal data structure.
\inmodule QtGui
Definition qevent.h:195
\inmodule QtCore
Definition qobject.h:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
The QPaintEvent class contains event parameters for paint events.
Definition qevent.h:485
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
void setClipRect(const QRectF &, Qt::ClipOperation op=Qt::ReplaceClip)
Enables clipping, and sets the clip region to the given rectangle using the given clip operation.
void setBrushOrigin(int x, int y)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qpainter.h:698
void restore()
Restores the current painter state (pops a saved state off the stack).
void save()
Saves the current painter state (pushes the state onto a stack).
void drawPixmap(const QRectF &targetRect, const QPixmap &pixmap, const QRectF &sourceRect)
Draws the rectangular portion source of the given pixmap into the given target in the paint device.
QPoint brushOrigin() const
Returns the currently set brush origin.
bool end()
Ends painting.
void translate(const QPointF &offset)
Translates the coordinate system by the given offset; i.e.
void fillRect(const QRectF &, const QBrush &)
Fills the given rectangle with the brush specified.
void setCurrentColorGroup(ColorGroup cg)
Set the palette's current color group to cg.
Definition qpalette.h:64
ColorGroup
\value Disabled \value Active \value Inactive \value Normal synonym for Active
Definition qpalette.h:48
@ Inactive
Definition qpalette.h:48
@ Disabled
Definition qpalette.h:48
@ Highlight
Definition qpalette.h:52
bool isValid() const
Returns {true} if this persistent model index is valid; otherwise returns {false}.
Qt::ItemFlags flags() const
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
constexpr QPoint toPoint() const
Rounds the coordinates of this point to the nearest integer, and returns a QPoint object with the rou...
Definition qpoint.h:394
\inmodule QtCore\reentrant
Definition qpoint.h:23
constexpr int x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:127
constexpr int y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:132
T * data() const
Definition qpointer.h:56
\inmodule QtCore\reentrant
Definition qrect.h:30
bool intersects(const QRect &r) const noexcept
Returns true if this rectangle intersects with the given rectangle (i.e., there is at least one pixel...
Definition qrect.cpp:1065
constexpr bool isValid() const noexcept
Returns true if the rectangle is valid, otherwise returns false.
Definition qrect.h:169
constexpr int bottom() const noexcept
Returns the y-coordinate of the rectangle's bottom edge.
Definition qrect.h:181
constexpr int top() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:175
bool contains(const QRect &r, bool proper=false) const noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qrect.cpp:851
constexpr int left() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:172
constexpr void setRect(int x, int y, int w, int h) noexcept
Sets the coordinates of the rectangle's top-left corner to ({x}, {y}), and its size to the given widt...
Definition qrect.h:345
constexpr void setWidth(int w) noexcept
Sets the width of the rectangle to the given width.
Definition qrect.h:380
constexpr void setX(int x) noexcept
Sets the left edge of the rectangle to the given x coordinate.
Definition qrect.h:214
constexpr int width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:235
The QRegion class specifies a clip region for a painter.
Definition qregion.h:27
QRect boundingRect() const noexcept
Returns the bounding rectangle of this region.
int rectCount() const noexcept
The QScrollBar widget provides a vertical or horizontal scroll bar.
Definition qscrollbar.h:20
Definition qset.h:18
iterator end()
Definition qset.h:140
bool isEmpty() const
Definition qset.h:52
const_iterator constBegin() const noexcept
Definition qset.h:139
const_iterator constEnd() const noexcept
Definition qset.h:143
iterator erase(const_iterator i)
Definition qset.h:145
iterator find(const T &value)
Definition qset.h:159
bool contains(const T &value) const
Definition qset.h:71
QPointF position() const
Returns the position of the point in this event, relative to the widget or item that received the eve...
Definition qevent.h:118
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:132
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:129
constexpr bool isValid() const noexcept
Returns true if both the width and height is equal to or greater than 0; otherwise returns false.
Definition qsize.h:126
int rowCount(const QModelIndex &parent=QModelIndex()) const override
bool canFetchMore(const QModelIndex &parent=QModelIndex()) const override
void fetchMore(const QModelIndex &parent=QModelIndex()) override
\inmodule QtCore
Definition qstack.h:13
T pop()
Removes the top item from the stack and returns it.
Definition qstack.h:18
void push(const T &t)
Adds element t to the top of the stack.
Definition qstack.h:17
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
ButtonFeatures features
\variable QStyleOption::palette
The QStyleOption class stores the parameters used by QStyle functions.
QStyle::State state
QPalette palette
void initFrom(const QWidget *w)
The QStyle class is an abstract base class that encapsulates the look and feel of a GUI.
Definition qstyle.h:29
@ State_Sibling
Definition qstyle.h:88
@ State_MouseOver
Definition qstyle.h:80
@ State_Item
Definition qstyle.h:87
@ State_HasFocus
Definition qstyle.h:75
@ State_Active
Definition qstyle.h:83
@ State_Children
Definition qstyle.h:86
@ State_Open
Definition qstyle.h:85
@ State_KeyboardFocusChange
Definition qstyle.h:90
@ State_Enabled
Definition qstyle.h:67
@ State_Selected
Definition qstyle.h:82
@ State_None
Definition qstyle.h:66
@ SH_ItemView_PaintAlternatingRowColorsForEmptyArea
Definition qstyle.h:667
@ SH_ItemView_ArrowKeysNavigateIntoChildren
Definition qstyle.h:662
@ SH_Widget_Animation_Duration
Definition qstyle.h:698
@ SH_ListViewExpand_SelectMouseType
Definition qstyle.h:623
@ SH_ItemView_ActivateItemOnSingleClick
Definition qstyle.h:644
@ SH_ItemView_ShowDecorationSelected
Definition qstyle.h:643
@ PM_TreeViewIndentation
Definition qstyle.h:526
@ PE_PanelItemViewRow
Definition qstyle.h:154
@ PE_IndicatorBranch
Definition qstyle.h:128
@ PE_FrameFocusRect
Definition qstyle.h:106
@ SE_TreeViewDisclosureItem
Definition qstyle.h:279
\inmodule QtCore
Definition qcoreevent.h:359
int itemForKeyEnd() const
QHeaderView * header
int widthHintForIndex(const QModelIndex &index, int hint, const QStyleOptionViewItem &option, int i) const
int accessibleTree2Index(const QModelIndex &index) const
bool isRowHidden(const QModelIndex &idx) const
int logicalIndexForTree() const
bool isItemHiddenOrDisabled(int i) const
void _q_sortIndicatorChanged(int column, Qt::SortOrder order)
int itemHeight(int item) const
void collapse(int item, bool emitSignal)
void calcLogicalIndices(QList< int > *logicalIndices, QList< QStyleOptionViewItem::ViewItemPosition > *itemPositions, int left, int right) const
void layout(int item, bool recusiveExpanding=false, bool afterIsUninitialized=false)
QModelIndex modelIndex(int i, int column=0) const
bool storeExpanded(const QPersistentModelIndex &idx)
QList< QPair< int, int > > columnRanges(const QModelIndex &topIndex, const QModelIndex &bottomIndex) const
void removeViewItems(int pos, int count)
bool isTreePosition(int logicalIndex) const
Definition qtreeview_p.h:66
QRect intersectedRect(const QRect rect, const QModelIndex &topLeft, const QModelIndex &bottomRight) const override
int firstVisibleItem(int *offset=nullptr) const
QRect itemDecorationRect(const QModelIndex &index) const
void insertViewItems(int pos, int count, const QTreeViewItem &viewItem)
QItemViewPaintPairs draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const override
\reimp
void _q_columnsRemoved(const QModelIndex &, int, int) override
void select(const QModelIndex &start, const QModelIndex &stop, QItemSelectionModel::SelectionFlags command)
void paintAlternatingRowColors(QPainter *painter, QStyleOptionViewItem *option, int y, int bottom) const
void _q_modelDestroyed() override
QList< QTreeViewItem > viewItems
int pageUp(int item) const
int viewIndex(const QModelIndex &index) const
int itemDecorationAt(const QPoint &pos) const
int pageDown(int item) const
QSet< QPersistentModelIndex > hiddenIndexes
int lastVisibleItem(int firstVisual=-1, int offset=-1) const
int columnAt(int x) const
int itemAtCoordinate(int coordinate) const
QSet< QPersistentModelIndex > spanningIndexes
void _q_columnsAboutToBeRemoved(const QModelIndex &, int, int) override
int itemForKeyHome() const
int indentationForItem(int item) const
void updateIndentationFromStyle()
QSet< QPersistentModelIndex > expandedIndexes
bool expandOrCollapseItemAtPos(const QPoint &pos)
void adjustViewOptionsForIndex(QStyleOptionViewItem *option, const QModelIndex &current) const override
int coordinateForItem(int item) const
void _q_modelAboutToBeReset()
void expand(int item, bool emitSignal)
QPair< int, int > startAndEndColumns(const QRect &rect) const
bool hasVisibleChildren(const QModelIndex &parent) const
The QTreeView class provides a default model/view implementation of a tree view.
Definition qtreeview.h:20
void setItemsExpandable(bool enable)
bool isSortingEnabled() const
bool isIndexHidden(const QModelIndex &index) const override
\reimp
void expand(const QModelIndex &index)
Expands the model item specified by the index.
bool itemsExpandable
whether the items are expandable by the user.
Definition qtreeview.h:26
QRegion visualRegionForSelection(const QItemSelection &selection) const override
Returns the rectangle from the viewport of the items in the given selection.
void setWordWrap(bool on)
void setSelectionModel(QItemSelectionModel *selectionModel) override
\reimp
bool isHeaderHidden() const
virtual void drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const
Draws the branches in the tree view on the same row as the model item index, using the painter given.
void collapse(const QModelIndex &index)
Collapses the model item specified by the index.
void setModel(QAbstractItemModel *model) override
\reimp
QModelIndex indexBelow(const QModelIndex &index) const
Returns the model index of the item below index.
int indexRowSizeHint(const QModelIndex &index) const
Returns the size hint for the row indicated by index.
void reset() override
\reimp
void doItemsLayout() override
int rowHeight(const QModelIndex &index) const
bool isExpanded(const QModelIndex &index) const
Returns true if the model item index is expanded; otherwise returns false.
void changeEvent(QEvent *event) override
\reimp
QSize viewportSizeHint() const override
\reimp
int columnViewportPosition(int column) const
Returns the horizontal position of the column in the viewport.
void columnCountChanged(int oldCount, int newCount)
Informs the tree view that the number of columns in the tree view has changed from oldCount to newCou...
void setColumnHidden(int column, bool hide)
If hide is true the column is hidden, otherwise the column is shown.
QHeaderView * header() const
Returns the header for the tree view.
void setColumnWidth(int column, int width)
void keyPressEvent(QKeyEvent *event) override
\reimp
void collapseAll()
bool isAnimated() const
void setTreePosition(int logicalIndex)
void setExpandsOnDoubleClick(bool enable)
QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override
Move the cursor in the way described by cursorAction, using the information provided by the button mo...
void verticalScrollbarValueChanged(int value) override
bool wordWrap
the item text word-wrapping policy
Definition qtreeview.h:30
int columnAt(int x) const
Returns the column in the tree view whose header covers the x coordinate given.
virtual void drawRow(QPainter *painter, const QStyleOptionViewItem &options, const QModelIndex &index) const
Draws the row in the tree view that contains the model item index, using the painter given.
void selectAll() override
\reimp
void setExpanded(const QModelIndex &index, bool expand)
Sets the item referred to by index to either collapse or expanded, depending on the value of expanded...
void updateGeometries() override
\reimp
void mousePressEvent(QMouseEvent *event) override
\reimp
void timerEvent(QTimerEvent *event) override
\reimp
~QTreeView()
Destroys the tree view.
QModelIndex indexAt(const QPoint &p) const override
\reimp
int sizeHintForColumn(int column) const override
Returns the size hint for the column's width or -1 if there is no model.
void setHeaderHidden(bool hide)
void reexpand()
QModelIndex indexAbove(const QModelIndex &index) const
Returns the model index of the item above index.
bool viewportEvent(QEvent *event) override
\reimp
void drawTree(QPainter *painter, const QRegion &region) const
void columnMoved()
This slot is called whenever a column has been moved.
bool uniformRowHeights
whether all items in the treeview have the same height
Definition qtreeview.h:25
void expandToDepth(int depth)
void resetIndentation()
void setUniformRowHeights(bool uniform)
void showColumn(int column)
Shows the given column in the tree view.
void setHeader(QHeaderView *header)
Sets the header for the tree view, to the given header.
QTreeView(QWidget *parent=nullptr)
Constructs a tree view with a parent to represent a model's data.
void setRootIsDecorated(bool show)
void setIndentation(int i)
int indentation
indentation of the items in the tree view.
Definition qtreeview.h:23
void setAllColumnsShowFocus(bool enable)
void columnResized(int column, int oldSize, int newSize)
This function is called whenever {column}'s size is changed in the header.
void mouseReleaseEvent(QMouseEvent *event) override
\reimp
void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command) override
Applies the selection command to the items in or touched by the rectangle, rect.
bool allColumnsShowFocus
whether items should show keyboard focus using all columns
Definition qtreeview.h:29
void paintEvent(QPaintEvent *event) override
\reimp
void resizeColumnToContents(int column)
Resizes the column given to the size of its contents.
void setFirstColumnSpanned(int row, const QModelIndex &parent, bool span)
int autoExpandDelay
The delay time before items in a tree are opened during a drag and drop operation.
Definition qtreeview.h:22
void mouseDoubleClickEvent(QMouseEvent *event) override
\reimp
void setAutoExpandDelay(int delay)
QModelIndexList selectedIndexes() const override
\reimp
void keyboardSearch(const QString &search) override
\reimp
void collapsed(const QModelIndex &index)
This signal is emitted when the item specified by index is collapsed.
void mouseMoveEvent(QMouseEvent *event) override
\reimp
void sortByColumn(int column, Qt::SortOrder order)
bool isColumnHidden(int column) const
Returns true if the column is hidden; otherwise returns false.
void scrollTo(const QModelIndex &index, ScrollHint hint=EnsureVisible) override
Scroll the contents of the tree view until the given model item index is visible.
void rowsRemoved(const QModelIndex &parent, int first, int last)
bool rootIsDecorated
whether to show controls for expanding and collapsing top-level items
Definition qtreeview.h:24
bool expandsOnDoubleClick
whether the items can be expanded by double-clicking.
Definition qtreeview.h:32
void setRowHidden(int row, const QModelIndex &parent, bool hide)
If hide is true the row with the given parent is hidden, otherwise the row is shown.
bool isRowHidden(int row, const QModelIndex &parent) const
Returns true if the item in the given row of the parent is hidden; otherwise returns false.
QRect visualRect(const QModelIndex &index) const override
Returns the rectangle on the viewport occupied by the item at index.
int treePosition() const
int horizontalOffset() const override
Returns the horizontal offset of the items in the treeview.
void setSortingEnabled(bool enable)
int columnWidth(int column) const
Returns the width of the column.
void hideColumn(int column)
Hides the column given.
bool isFirstColumnSpanned(int row, const QModelIndex &parent) const
void setAnimated(bool enable)
void expanded(const QModelIndex &index)
This signal is emitted when the item specified by index is expanded.
void expandAll()
int verticalOffset() const override
Returns the vertical offset of the items in the tree view.
void expandRecursively(const QModelIndex &index, int depth=-1)
void scrollContentsBy(int dx, int dy) override
Scrolls the contents of the tree view by (dx, dy).
The QWidget class is the base class of all user interface objects.
Definition qwidget.h:99
QSize minimumSize
the widget's minimum size
Definition qwidget.h:120
QSize maximumSize
the widget's maximum size in pixels
Definition qwidget.h:121
QPoint pos
the position of the widget within its parent widget
Definition qwidget.h:111
void hide()
Hides the widget.
Definition qwidget.cpp:8209
QSize sizeHint
the recommended size for the widget
Definition qwidget.h:148
void render(QPaintDevice *target, const QPoint &targetOffset=QPoint(), const QRegion &sourceRegion=QRegion(), RenderFlags renderFlags=RenderFlags(DrawWindowBackground|DrawChildren))
Definition qwidget.cpp:5099
EGLImageKHR int int EGLuint64KHR * modifiers
QOpenGLWidget * widget
[1]
qSwap(pi, e)
QSet< QString >::iterator it
rect
[4]
direction
QStyleOptionButton opt
short next
Definition keywords.cpp:445
Combined button and popup list for selecting options.
@ AlignVCenter
Definition qnamespace.h:154
@ AlignLeft
Definition qnamespace.h:143
@ WA_MacShowFocusRect
Definition qnamespace.h:358
@ Horizontal
Definition qnamespace.h:98
@ transparent
Definition qnamespace.h:46
@ DisplayRole
@ Key_Plus
Definition qnamespace.h:524
@ Key_Minus
Definition qnamespace.h:526
@ Key_Asterisk
Definition qnamespace.h:523
SortOrder
Definition qnamespace.h:120
@ UniqueConnection
@ ItemNeverHasChildren
@ ItemIsEnabled
std::pair< T1, T2 > QPair
static QString header(const QString &name)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
static int area(const QSize &s)
Definition qicon.cpp:152
return ret
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
#define SLOT(a)
Definition qobjectdefs.h:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
GLint GLint GLint GLint GLint x
[0]
GLint GLenum GLsizei GLsizei GLsizei depth
GLenum mode
GLenum GLuint GLint level
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLdouble GLdouble GLdouble GLdouble top
GLenum GLenum GLsizei count
GLdouble GLdouble right
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLsizei range
GLint GLsizei width
GLint left
GLint GLint bottom
GLboolean enable
GLuint start
GLenum GLuint GLintptr offset
GLint first
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLenum GLenum GLsizei void GLsizei void * column
struct _cl_event * event
const GLubyte * c
GLuint entry
GLint limit
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLenum GLenum GLsizei void * row
GLenum GLenum GLsizei void GLsizei void void * span
GLuint64EXT * result
[6]
GLuint GLenum option
GLfixed GLfixed GLint GLint order
constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2) noexcept(noexcept(std::make_pair(std::forward< T1 >(value1), std::forward< T2 >(value2))))
Definition qpair.h:19
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
static QT_BEGIN_NAMESPACE QVariant hint(QPlatformIntegration::StyleHint h)
#define emit
#define Q_UNUSED(x)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
static bool ancestorOf(QObject *widget, QObject *other)
size_t quintptr
Definition qtypes.h:72
unsigned int uint
Definition qtypes.h:29
long long qint64
Definition qtypes.h:55
QSqlQueryModel * model
[16]
view show()
[18] //! [19]
QList< int > list
[14]
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
QVBoxLayout * layout
myObject disconnect()
[26]
QSharedPointer< T > other(t)
[5]
QGraphicsItem * item
view viewport() -> scroll(dx, dy, deviceRect)
edit hide()
edit isVisible()
QItemSelection * selection
[0]
QList< QTreeWidgetItem * > items
QLayoutItem * child
[0]
widget render & pixmap
QPainter painter(this)
[7]
QPointer< QWidget > widget
qsizetype indexOf(const AT &t, qsizetype from=0) const noexcept
Definition qlist.h:955
bool contains(const AT &t) const noexcept
Definition qlist.h:44
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...
QModelIndex index
Definition qtreeview_p.h:34
uint hasMoreSiblings
Definition qtreeview_p.h:39
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent