diff options
-rw-r--r-- | src/quick/items/qquicktableview.cpp | 321 | ||||
-rw-r--r-- | src/quick/items/qquicktableview_p.h | 4 | ||||
-rw-r--r-- | src/quick/items/qquicktableview_p_p.h | 34 | ||||
-rw-r--r-- | tests/auto/quick/qquicktableview/tst_qquicktableview.cpp | 186 |
4 files changed, 524 insertions, 21 deletions
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index cb600c7b47..28a5dee681 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -350,6 +350,81 @@ */ /*! + \qmlmethod QtQuick::TableView::positionViewAtCell(point cell, Qt.Alignment alignment, point offset) + + Positions \l contentX and \l contentY such that \a cell is at the position specified by + \a alignment. \a alignment can be an or-ed combination of the following: + + \list + \li Qt.AlignLeft - position the cell at the left of the view. + \li Qt.AlignHCenter - position the cell at the horizontal center of the view. + \li Qt.AlignRight - position the cell at the right of the view. + \li Qt.AlignTop - position the cell at the top of the view. + \li Qt.AlignVCenter - position the cell at the vertical center of the view. + \li Qt.AlignBottom - position the cell at the bottom of the view. + \li Qt.AlignCenter - the same as (Qt.AlignHCenter | Qt.AlignVCenter) + \endlist + + Optionally, you can specify \a offset to move \l contentX and \l contentY an extra number of + pixels beyond the target alignment. E.g if you want to position the view so + that cell [10, 10] ends up at the top-left corner with a 5px margin, you could do: + + \code + positionViewAtCell(Qt.point(10, 10), Qt.AlignLeft | Qt.AlignTop, Qt.point(-5, -5)) + \endcode +*/ + +/*! + \qmlmethod QtQuick::TableView::positionViewAtCell(int column, int row, Qt.Alignment alignment, point offset) + + Convenience for calling \code positionViewAtCell(Qt.point(column, row), alignment, offset) +*/ + +/*! + \qmlmethod QtQuick::TableView::positionViewAtRow(int row, Qt.Alignment alignment, real offset) + + Positions \l contentY such that \a row is at the position specified by + \a alignment. \a alignment can be one of the following: + + \list + \li Qt.AlignTop - position the row at the top of the view. + \li Qt.AlignVCenter - position the cell at the vertical center of the view. + \li Qt.AlignCenter - the same as Qt.AlignVCenter. + \li Qt.AlignBottom - position the cell at the bottom of the view. + \endlist + + Optionally, you can specify \a offset to move \l contentY an extra number of + pixels beyond the target alignment. E.g if you want to position the view so + that row 10 ends up at the bottom with a 5px margin, you could do: + + \code + positionViewAtRow(10, Qt.AlignBottom, 5) + \endcode +*/ + +/*! + \qmlmethod QtQuick::TableView::positionViewAtColumn(int column, Qt.Alignment alignment, real offset) + + Positions \l contentX such that \a column is at the position specified by + \a alignment. \a alignment can be one of the following: + + \list + \li Qt.AlignLeft - position the column at the left of the view. + \li Qt.AlignHCenter - position the column at the horizontal center of the view. + \li Qt.AlignCenter - the same as Qt.AlignVCenter. + \li Qt.AlignRight - position the column at the right of the view. + \endlist + + Optionally, you can specify \a offset to move \l contentX an extra number of + pixels to the side of the target alignment. E.g if you want to position the view so + that column 10 ends up at the left side with a 5px margin, you could do: + + \code + positionViewAtColumn(10, Qt.AlignLeft, -5) + \endcode +*/ + +/*! \qmlattachedproperty TableView QtQuick::TableView::view This attached property holds the view that manages the delegate instance. @@ -906,6 +981,18 @@ void QQuickTableViewPrivate::syncLoadedTableRectFromLoadedTable() loadedTableInnerRect = QRectF(topLeftRect.bottomRight(), bottomRightRect.topLeft()); } +void QQuickTableViewPrivate::shiftLoadedTableRect(const QPointF newPosition) +{ + // Move the tracked table rects to the new position. For this to + // take visual effect (move the delegate items to be inside the table + // rect), it needs to be followed by a relayoutTableItems(). + // Also note that the position of the viewport needs to be adjusted + // separately for it to overlap the loaded table. + const QPointF innerDiff = loadedTableOuterRect.topLeft() - loadedTableInnerRect.topLeft(); + loadedTableOuterRect.moveTopLeft(newPosition); + loadedTableInnerRect.moveTopLeft(newPosition + innerDiff); +} + QQuickTableViewPrivate::RebuildOptions QQuickTableViewPrivate::checkForVisibilityChanges() { // Go through all columns from first to last, find the columns that used @@ -1291,6 +1378,20 @@ qreal QQuickTableViewPrivate::getColumnLayoutWidth(int column) return columnWidth; } +qreal QQuickTableViewPrivate::getEffectiveRowHeight(int row) const +{ + // Return row height after layout + Q_TABLEVIEW_ASSERT(loadedRows.contains(row), row); + return loadedTableItem(QPoint(leftColumn(), row))->geometry().height(); +} + +qreal QQuickTableViewPrivate::getEffectiveColumnWidth(int column) const +{ + // Return column width after layout + Q_TABLEVIEW_ASSERT(loadedColumns.contains(column), column); + return loadedTableItem(QPoint(column, topRow()))->geometry().width(); +} + qreal QQuickTableViewPrivate::getRowLayoutHeight(int row) { // Return the row height specified by the application, or go @@ -1611,7 +1712,9 @@ void QQuickTableViewPrivate::processLoadRequest() loadRequest.markAsDone(); - qCDebug(lcTableViewDelegateLifecycle()) << "request completed! Table:" << tableLayoutToString(); + qCDebug(lcTableViewDelegateLifecycle()) << "current table:" << tableLayoutToString(); + qCDebug(lcTableViewDelegateLifecycle()) << "Load request completed!"; + qCDebug(lcTableViewDelegateLifecycle()) << "****************************************"; } void QQuickTableViewPrivate::processRebuildTable() @@ -1635,7 +1738,7 @@ void QQuickTableViewPrivate::processRebuildTable() moveToNextRebuildState(); if (rebuildState == RebuildState::LoadInitalTable) { - beginRebuildTable(); + loadInitialTable(); if (!moveToNextRebuildState()) return; } @@ -1688,7 +1791,10 @@ void QQuickTableViewPrivate::processRebuildTable() } Q_TABLEVIEW_ASSERT(rebuildState == RebuildState::Done, int(rebuildState)); - qCDebug(lcTableViewDelegateLifecycle()) << "rebuild complete:" << q; + qCDebug(lcTableViewDelegateLifecycle()) << "current table:" << tableLayoutToString(); + qCDebug(lcTableViewDelegateLifecycle()) << "rebuild completed!"; + qCDebug(lcTableViewDelegateLifecycle()) << "################################################"; + qCDebug(lcTableViewDelegateLifecycle()); } bool QQuickTableViewPrivate::moveToNextRebuildState() @@ -1781,10 +1887,16 @@ void QQuickTableViewPrivate::calculateTopLeft(QPoint &topLeftCell, QPointF &topL const int newColumn = int(viewportRect.x() / (averageEdgeSize.width() + cellSpacing.width())); topLeftCell.rx() = qBound(0, newColumn, tableSize.width() - 1); topLeftPos.rx() = topLeftCell.x() * (averageEdgeSize.width() + cellSpacing.width()); + } else if (rebuildOptions & RebuildOption::PositionViewAtColumn) { + topLeftCell.rx() = qBound(0, positionViewAtColumn, tableSize.width() - 1); + topLeftPos.rx() = qFloor(topLeftCell.x()) * (averageEdgeSize.width() + cellSpacing.width()); } else { // Keep the current top left, unless it's outside model topLeftCell.rx() = qBound(0, leftColumn(), tableSize.width() - 1); - topLeftPos.rx() = loadedTableOuterRect.topLeft().x(); + // We begin by loading the columns where the viewport is at + // now. But will move the whole table and the viewport + // later, when we do a layoutAfterLoadingInitialTable(). + topLeftPos.rx() = loadedTableOuterRect.x(); } } @@ -1801,21 +1913,25 @@ void QQuickTableViewPrivate::calculateTopLeft(QPoint &topLeftCell, QPointF &topL const int newRow = int(viewportRect.y() / (averageEdgeSize.height() + cellSpacing.height())); topLeftCell.ry() = qBound(0, newRow, tableSize.height() - 1); topLeftPos.ry() = topLeftCell.y() * (averageEdgeSize.height() + cellSpacing.height()); + } else if (rebuildOptions & RebuildOption::PositionViewAtRow) { + topLeftCell.ry() = qBound(0, positionViewAtRow, tableSize.height() - 1); + topLeftPos.ry() = qFloor(topLeftCell.y()) * (averageEdgeSize.height() + cellSpacing.height()); } else { - // Keep the current top left, unless it's outside model topLeftCell.ry() = qBound(0, topRow(), tableSize.height() - 1); - topLeftPos.ry() = loadedTableOuterRect.topLeft().y(); + topLeftPos.ry() = loadedTableOuterRect.y(); } } } -void QQuickTableViewPrivate::beginRebuildTable() +void QQuickTableViewPrivate::loadInitialTable() { updateTableSize(); QPoint topLeft; QPointF topLeftPos; calculateTopLeft(topLeft, topLeftPos); + qCDebug(lcTableViewDelegateLifecycle()) << "initial viewport rect:" << viewportRect; + qCDebug(lcTableViewDelegateLifecycle()) << "initial top left cell:" << topLeft << ", pos:" << topLeftPos; if (!loadedItems.isEmpty()) { if (rebuildOptions & RebuildOption::All) @@ -1838,15 +1954,17 @@ void QQuickTableViewPrivate::beginRebuildTable() loadedTableInnerRect = QRect(); clearEdgeSizeCache(); - if (syncHorizontally) { + if (syncHorizontally) setLocalViewportX(syncView->contentX()); - viewportRect.moveLeft(syncView->d_func()->viewportRect.left()); - } - if (syncVertically) { + if (syncVertically) setLocalViewportY(syncView->contentY()); - viewportRect.moveTop(syncView->d_func()->viewportRect.top()); - } + + if (!syncHorizontally && rebuildOptions & RebuildOption::PositionViewAtColumn) + setLocalViewportX(topLeftPos.x()); + + if (!syncVertically && rebuildOptions & RebuildOption::PositionViewAtRow) + setLocalViewportY(topLeftPos.y()); if (!model) { qCDebug(lcTableViewDelegateLifecycle()) << "no model found, leaving table empty"; @@ -1904,6 +2022,74 @@ void QQuickTableViewPrivate::layoutAfterLoadingInitialTable() } updateExtents(); + adjustViewportXAccordingToAlignment(); + adjustViewportYAccordingToAlignment(); +} + +void QQuickTableViewPrivate::adjustViewportXAccordingToAlignment() +{ + // Check if we are supposed to position the viewport at a certain column + if (!rebuildOptions.testFlag(RebuildOption::PositionViewAtColumn)) + return; + // The requested column might have been hidden or is outside model bounds + if (positionViewAtColumn != leftColumn()) + return; + + const float columnWidth = getEffectiveColumnWidth(positionViewAtColumn); + + switch (positionViewAtColumnAlignment) { + case Qt::AlignLeft: + setLocalViewportX(loadedTableOuterRect.left() + positionViewAtColumnOffset); + break; + case Qt::AlignHCenter: + setLocalViewportX(loadedTableOuterRect.left() + - (viewportRect.width() / 2) + + (columnWidth / 2) + + positionViewAtColumnOffset); + break; + case Qt::AlignRight: + setLocalViewportX(loadedTableOuterRect.left() + - viewportRect.width() + + columnWidth + + positionViewAtColumnOffset); + break; + default: + Q_TABLEVIEW_UNREACHABLE("options are checked in setter"); + break; + } +} + +void QQuickTableViewPrivate::adjustViewportYAccordingToAlignment() +{ + // Check if we are supposed to position the viewport at a certain row + if (!rebuildOptions.testFlag(RebuildOption::PositionViewAtRow)) + return; + // The requested row might have been hidden or is outside model bounds + if (positionViewAtRow != topRow()) + return; + + const float rowHeight = getEffectiveRowHeight(positionViewAtRow); + + switch (positionViewAtRowAlignment) { + case Qt::AlignTop: + setLocalViewportY(loadedTableOuterRect.top() + positionViewAtRowOffset); + break; + case Qt::AlignVCenter: + setLocalViewportY(loadedTableOuterRect.top() + - (viewportRect.height() / 2) + + (rowHeight / 2) + + positionViewAtRowOffset); + break; + case Qt::AlignBottom: + setLocalViewportY(loadedTableOuterRect.top() + - viewportRect.height() + + rowHeight + + positionViewAtRowOffset); + break; + default: + Q_TABLEVIEW_UNREACHABLE("options are checked in setter"); + break; + } } void QQuickTableViewPrivate::unloadEdge(Qt::Edge edge) @@ -2077,7 +2263,12 @@ bool QQuickTableViewPrivate::updateTableRecursive() const auto children = syncChildren; for (auto syncChild : children) { auto syncChild_d = syncChild->d_func(); - syncChild_d->scheduledRebuildOptions |= rebuildOptions; + const int mask = + RebuildOption::PositionViewAtRow | + RebuildOption::PositionViewAtColumn | + RebuildOption::CalculateNewTopLeftRow | + RebuildOption::CalculateNewTopLeftColumn; + syncChild_d->scheduledRebuildOptions |= rebuildOptions & ~mask; const bool descendantUpdateComplete = syncChild_d->updateTableRecursive(); if (!descendantUpdateComplete) @@ -2230,6 +2421,7 @@ void QQuickTableViewPrivate::syncWithPendingChanges() syncModel(); syncDelegate(); syncSyncView(); + syncPositionView(); syncRebuildOptions(); } @@ -2253,6 +2445,12 @@ void QQuickTableViewPrivate::syncRebuildOptions() } else if (rebuildOptions.testFlag(RebuildOption::ViewportOnly)) { rebuildOptions.setFlag(RebuildOption::LayoutOnly, false); } + + if (rebuildOptions.testFlag(RebuildOption::PositionViewAtRow)) + rebuildOptions.setFlag(RebuildOption::CalculateNewTopLeftRow, false); + + if (rebuildOptions.testFlag(RebuildOption::PositionViewAtColumn)) + rebuildOptions.setFlag(RebuildOption::CalculateNewTopLeftColumn, false); } void QQuickTableViewPrivate::syncDelegate() @@ -2369,6 +2567,16 @@ void QQuickTableViewPrivate::syncSyncView() } } +void QQuickTableViewPrivate::syncPositionView() +{ + // Only positionViewAtRow/positionViewAtColumn are critical + // to sync before a rebuild to avoid them being overwritten + // by the setters while building. The other position properties + // can change without it causing trouble. + positionViewAtRow = assignedPositionViewAtRow; + positionViewAtColumn = assignedPositionViewAtColumn; +} + void QQuickTableViewPrivate::connectToModel() { Q_Q(QQuickTableView); @@ -2538,6 +2746,10 @@ void QQuickTableViewPrivate::setLocalViewportX(qreal contentX) return; q->setContentX(contentX); + + // Keep our own viewportRect in sync + viewportRect.moveLeft(contentX); + qCDebug(lcTableViewDelegateLifecycle) << "viewportRect adjusted to:" << viewportRect; } void QQuickTableViewPrivate::setLocalViewportY(qreal contentY) @@ -2552,6 +2764,10 @@ void QQuickTableViewPrivate::setLocalViewportY(qreal contentY) return; q->setContentY(contentY); + + // Keep our own viewportRect in sync + viewportRect.moveTop(contentY); + qCDebug(lcTableViewDelegateLifecycle) << "viewportRect adjusted to:" << viewportRect; } void QQuickTableViewPrivate::syncViewportPosRecursive() @@ -2829,6 +3045,83 @@ void QQuickTableView::setSyncDirection(Qt::Orientations direction) emit syncDirectionChanged(); } +void QQuickTableView::positionViewAtCell(const QPoint &cell, Qt::Alignment alignment, const QPointF &offset) +{ + positionViewAtRow(cell.y(), alignment & Qt::AlignVertical_Mask, offset.y()); + positionViewAtColumn(cell.x(), alignment & Qt::AlignHorizontal_Mask, offset.x()); +} + +void QQuickTableView::positionViewAtCell(int column, int row, Qt::Alignment alignment, const QPointF &offset) +{ + positionViewAtCell(QPoint(column, row), alignment, offset); +} + +void QQuickTableView::positionViewAtRow(int row, Qt::Alignment alignment, qreal offset) +{ + Q_D(QQuickTableView); + + if (d->syncVertically) { + d->syncView->positionViewAtRow(row, alignment, offset); + return; + } + + // Clean up flags, in case it has + // Qt::AlignCenter set (which we allow) + Qt::Alignment adjustedAlignment = alignment; + adjustedAlignment.setFlag(Qt::AlignHCenter, false); + if (!int(adjustedAlignment)) + adjustedAlignment.setFlag(Qt::AlignTop); + + switch (adjustedAlignment) { + case Qt::AlignTop: + case Qt::AlignVCenter: + case Qt::AlignBottom: + break; + default: + qmlWarning(this) << "Unsupported alignment. Use AlignTop, AlignVCenter, or AlignBottom"; + return; + } + + d->assignedPositionViewAtRow = row; + d->positionViewAtRowAlignment = adjustedAlignment; + d->positionViewAtRowOffset = offset; + d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::ViewportOnly | + QQuickTableViewPrivate::RebuildOption::PositionViewAtRow); +} + +void QQuickTableView::positionViewAtColumn(int column, Qt::Alignment alignment, qreal offset) +{ + Q_D(QQuickTableView); + + if (d->syncHorizontally) { + d->syncView->positionViewAtColumn(column, alignment, offset); + return; + } + + // Clean up flags, in case it has + // Qt::AlignCenter set (which we allow) + Qt::Alignment adjustedAlignment = alignment; + adjustedAlignment.setFlag(Qt::AlignVCenter, false); + if (!int(adjustedAlignment)) + adjustedAlignment.setFlag(Qt::AlignLeft); + + switch (adjustedAlignment) { + case Qt::AlignLeft: + case Qt::AlignHCenter: + case Qt::AlignRight: + break; + default: + qmlWarning(this) << "Unsupported alignment. Use AlignLeft, AlignHCenter, or AlignRight"; + return; + } + + d->assignedPositionViewAtColumn = column; + d->positionViewAtColumnAlignment = adjustedAlignment; + d->positionViewAtColumnOffset = offset; + d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::ViewportOnly | + QQuickTableViewPrivate::RebuildOption::PositionViewAtColumn); +} + void QQuickTableView::forceLayout() { d_func()->forceLayout(); diff --git a/src/quick/items/qquicktableview_p.h b/src/quick/items/qquicktableview_p.h index c71e74bfdc..387cc5d51d 100644 --- a/src/quick/items/qquicktableview_p.h +++ b/src/quick/items/qquicktableview_p.h @@ -123,6 +123,10 @@ public: void setSyncDirection(Qt::Orientations direction); Q_INVOKABLE void forceLayout(); + Q_INVOKABLE void positionViewAtCell(const QPoint &cell, Qt::Alignment alignment, const QPointF &offset = QPointF()); + Q_INVOKABLE void positionViewAtCell(int column, int row, Qt::Alignment alignment, const QPointF &offset = QPointF()); + Q_INVOKABLE void positionViewAtRow(int row, Qt::Alignment alignment, qreal offset = 0); + Q_INVOKABLE void positionViewAtColumn(int column, Qt::Alignment alignment, qreal offset = 0); static QQuickTableViewAttached *qmlAttachedProperties(QObject *); diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h index 93a8d839e0..ab53a07dd0 100644 --- a/src/quick/items/qquicktableview_p_p.h +++ b/src/quick/items/qquicktableview_p_p.h @@ -208,11 +208,13 @@ public: enum class RebuildOption { None = 0, - LayoutOnly = 0x1, - ViewportOnly = 0x2, - CalculateNewTopLeftRow = 0x4, - CalculateNewTopLeftColumn = 0x8, - All = 0x10, + All = 0x1, + LayoutOnly = 0x2, + ViewportOnly = 0x4, + CalculateNewTopLeftRow = 0x8, + CalculateNewTopLeftColumn = 0x10, + PositionViewAtRow = 0x20, + PositionViewAtColumn = 0x40, }; Q_DECLARE_FLAGS(RebuildOptions, RebuildOption) @@ -310,6 +312,15 @@ public: QList<QPointer<QQuickTableView> > syncChildren; Qt::Orientations assignedSyncDirection = Qt::Horizontal | Qt::Vertical; + int assignedPositionViewAtRow = 0; + int assignedPositionViewAtColumn = 0; + int positionViewAtRow = 0; + int positionViewAtColumn = 0; + qreal positionViewAtRowOffset = 0; + qreal positionViewAtColumnOffset = 0; + Qt::Alignment positionViewAtRowAlignment = Qt::AlignTop; + Qt::Alignment positionViewAtColumnAlignment = Qt::AlignLeft; + const static QPoint kLeft; const static QPoint kRight; const static QPoint kUp; @@ -337,6 +348,8 @@ public: qreal getRowLayoutHeight(int row); qreal getColumnWidth(int column); qreal getRowHeight(int row); + qreal getEffectiveRowHeight(int row) const; + qreal getEffectiveColumnWidth(int column) const; inline int topRow() const { return loadedRows.cbegin().key(); } inline int bottomRow() const { return (--loadedRows.cend()).key(); } @@ -363,6 +376,7 @@ public: void updateExtents(); void syncLoadedTableRectFromLoadedTable(); void syncLoadedTableFromLoadRequest(); + void shiftLoadedTableRect(const QPointF newPosition); int nextVisibleEdgeIndex(Qt::Edge edge, int startIndex); int nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge); @@ -394,8 +408,11 @@ public: void processRebuildTable(); bool moveToNextRebuildState(); void calculateTopLeft(QPoint &topLeft, QPointF &topLeftPos); - void beginRebuildTable(); + void loadInitialTable(); + void layoutAfterLoadingInitialTable(); + void adjustViewportXAccordingToAlignment(); + void adjustViewportYAccordingToAlignment(); void scheduleRebuildTable(QQuickTableViewPrivate::RebuildOptions options); @@ -413,8 +430,9 @@ public: virtual QVariant modelImpl() const; virtual void setModelImpl(const QVariant &newModel); virtual void syncModel(); - inline void syncRebuildOptions(); virtual void syncSyncView(); + virtual void syncPositionView(); + inline void syncRebuildOptions(); void connectToModel(); void disconnectFromModel(); @@ -459,6 +477,8 @@ public: QPoint cell; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickTableViewPrivate::RebuildOptions) + QT_END_NAMESPACE #endif diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp index ef39a91a65..e0917cbea9 100644 --- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp +++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp @@ -178,6 +178,10 @@ private slots: void delegateWithRequiredProperties(); void checkThatFetchMoreIsCalledWhenScrolledToTheEndOfTable(); void replaceModel(); + void positionViewAtRow_data(); + void positionViewAtRow(); + void positionViewAtColumn_data(); + void positionViewAtColumn(); }; tst_QQuickTableView::tst_QQuickTableView() @@ -2768,6 +2772,188 @@ void tst_QQuickTableView::replaceModel() QCOMPARE(tableView->contentHeight(), 0); } +void tst_QQuickTableView::positionViewAtRow_data() +{ + QTest::addColumn<int>("row"); + QTest::addColumn<Qt::AlignmentFlag>("alignment"); + QTest::addColumn<qreal>("offset"); + QTest::addColumn<qreal>("contentYStartPos"); + + QTest::newRow("AlignTop 0") << 0 << Qt::AlignTop << 0. << 0.; + QTest::newRow("AlignTop 1") << 1 << Qt::AlignTop << 0. << 0.; + QTest::newRow("AlignTop 1") << 1 << Qt::AlignTop << 0. << 50.; + QTest::newRow("AlignTop 50") << 50 << Qt::AlignTop << 0. << -1.; + QTest::newRow("AlignTop 0") << 0 << Qt::AlignTop << 0. << -1.; + QTest::newRow("AlignTop 99") << 99 << Qt::AlignTop << 0. << -1.; + + QTest::newRow("AlignTop 0") << 0 << Qt::AlignTop << -10. << 0.; + QTest::newRow("AlignTop 1") << 1 << Qt::AlignTop << -10. << 0.; + QTest::newRow("AlignTop 1") << 1 << Qt::AlignTop << -10. << 50.; + QTest::newRow("AlignTop 50") << 50 << Qt::AlignTop << -10. << -1.; + QTest::newRow("AlignTop 0") << 0 << Qt::AlignTop << -10. << -1.; + QTest::newRow("AlignTop 99") << 99 << Qt::AlignTop << -10. << -1.; + + QTest::newRow("AlignBottom 0") << 0 << Qt::AlignBottom << 0. << 0.; + QTest::newRow("AlignBottom 1") << 1 << Qt::AlignBottom << 0. << 0.; + QTest::newRow("AlignBottom 1") << 1 << Qt::AlignBottom << 0. << 50.; + QTest::newRow("AlignBottom 50") << 50 << Qt::AlignBottom << 0. << -1.; + QTest::newRow("AlignBottom 0") << 0 << Qt::AlignBottom << 0. << -1.; + QTest::newRow("AlignBottom 99") << 99 << Qt::AlignBottom << 0. << -1.; + + QTest::newRow("AlignBottom 0") << 0 << Qt::AlignBottom << 10. << 0.; + QTest::newRow("AlignBottom 1") << 1 << Qt::AlignBottom << 10. << 0.; + QTest::newRow("AlignBottom 1") << 1 << Qt::AlignBottom << 10. << 50.; + QTest::newRow("AlignBottom 50") << 50 << Qt::AlignBottom << 10. << -1.; + QTest::newRow("AlignBottom 0") << 0 << Qt::AlignBottom << 10. << -1.; + QTest::newRow("AlignBottom 99") << 99 << Qt::AlignBottom << 10. << -1.; + + QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << 0. << 0.; + QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << 0. << 0.; + QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << 0. << 50.; + QTest::newRow("AlignCenter 50") << 50 << Qt::AlignCenter << 0. << -1.; + QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << 0. << -1.; + QTest::newRow("AlignCenter 99") << 99 << Qt::AlignCenter << 0. << -1.; + + QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << -10. << 0.; + QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << -10. << 0.; + QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << -10. << 50.; + QTest::newRow("AlignCenter 50") << 50 << Qt::AlignCenter << -10. << -1.; + QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << -10. << -1.; + QTest::newRow("AlignCenter 99") << 99 << Qt::AlignCenter << -10. << -1.; +} + +void tst_QQuickTableView::positionViewAtRow() +{ + // Check that positionViewAtRow actually flicks the view + // to the right position so that the row becomes visible + QFETCH(int, row); + QFETCH(Qt::AlignmentFlag, alignment); + QFETCH(qreal, offset); + QFETCH(qreal, contentYStartPos); + + LOAD_TABLEVIEW("plaintableview.qml"); + auto model = TestModelAsVariant(100, 100); + tableView->setModel(model); + if (contentYStartPos >= 0) + tableView->setContentY(contentYStartPos); + + WAIT_UNTIL_POLISHED; + + tableView->positionViewAtRow(row, alignment, offset); + + WAIT_UNTIL_POLISHED; + + const QPoint cell(0, row); + const int modelIndex = tableViewPrivate->modelIndexAtCell(cell); + QVERIFY(tableViewPrivate->loadedItems.contains(modelIndex)); + const auto geometry = tableViewPrivate->loadedTableItem(cell)->geometry(); + + switch (alignment) { + case Qt::AlignTop: + QCOMPARE(geometry.y(), tableView->contentY() - offset); + break; + case Qt::AlignBottom: + QCOMPARE(geometry.bottom(), tableView->contentY() + tableView->height() - offset); + break; + case Qt::AlignCenter: + QCOMPARE(geometry.y(), tableView->contentY() + (tableView->height() / 2) - (geometry.height() / 2) - offset); + break; + default: + Q_UNREACHABLE(); + } +} + +void tst_QQuickTableView::positionViewAtColumn_data() +{ + QTest::addColumn<int>("column"); + QTest::addColumn<Qt::AlignmentFlag>("alignment"); + QTest::addColumn<qreal>("offset"); + QTest::addColumn<qreal>("contentXStartPos"); + + QTest::newRow("AlignLeft 0") << 0 << Qt::AlignLeft << 0. << 0.; + QTest::newRow("AlignLeft 1") << 1 << Qt::AlignLeft << 0. << 0.; + QTest::newRow("AlignLeft 1") << 1 << Qt::AlignLeft << 0. << 50.; + QTest::newRow("AlignLeft 50") << 50 << Qt::AlignLeft << 0. << -1.; + QTest::newRow("AlignLeft 0") << 0 << Qt::AlignLeft << 0. << -1.; + QTest::newRow("AlignLeft 99") << 99 << Qt::AlignLeft << 0. << -1.; + + QTest::newRow("AlignLeft 0") << 0 << Qt::AlignLeft << -10. << 0.; + QTest::newRow("AlignLeft 1") << 1 << Qt::AlignLeft << -10. << 0.; + QTest::newRow("AlignLeft 1") << 1 << Qt::AlignLeft << -10. << 50.; + QTest::newRow("AlignLeft 50") << 50 << Qt::AlignLeft << -10. << -1.; + QTest::newRow("AlignLeft 0") << 0 << Qt::AlignLeft << -10. << -1.; + QTest::newRow("AlignLeft 99") << 99 << Qt::AlignLeft << -10. << -1.; + + QTest::newRow("AlignRight 0") << 0 << Qt::AlignRight << 0. << 0.; + QTest::newRow("AlignRight 1") << 1 << Qt::AlignRight << 0. << 0.; + QTest::newRow("AlignRight 1") << 1 << Qt::AlignRight << 0. << 50.; + QTest::newRow("AlignRight 50") << 50 << Qt::AlignRight << 0. << -1.; + QTest::newRow("AlignRight 0") << 0 << Qt::AlignRight << 0. << -1.; + QTest::newRow("AlignRight 99") << 99 << Qt::AlignRight << 0. << -1.; + + QTest::newRow("AlignRight 0") << 0 << Qt::AlignRight << 10. << 0.; + QTest::newRow("AlignRight 1") << 1 << Qt::AlignRight << 10. << 0.; + QTest::newRow("AlignRight 1") << 1 << Qt::AlignRight << 10. << 50.; + QTest::newRow("AlignRight 50") << 50 << Qt::AlignRight << 10. << -1.; + QTest::newRow("AlignRight 0") << 0 << Qt::AlignRight << 10. << -1.; + QTest::newRow("AlignRight 99") << 99 << Qt::AlignRight << 10. << -1.; + + QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << 0. << 0.; + QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << 0. << 0.; + QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << 0. << 50.; + QTest::newRow("AlignCenter 50") << 50 << Qt::AlignCenter << 0. << -1.; + QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << 0. << -1.; + QTest::newRow("AlignCenter 99") << 99 << Qt::AlignCenter << 0. << -1.; + + QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << -10. << 0.; + QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << -10. << 0.; + QTest::newRow("AlignCenter 1") << 1 << Qt::AlignCenter << -10. << 50.; + QTest::newRow("AlignCenter 50") << 50 << Qt::AlignCenter << -10. << -1.; + QTest::newRow("AlignCenter 0") << 0 << Qt::AlignCenter << -10. << -1.; + QTest::newRow("AlignCenter 99") << 99 << Qt::AlignCenter << -10. << -1.; +} + +void tst_QQuickTableView::positionViewAtColumn() +{ + // Check that positionViewAtColumn actually flicks the view + // to the right position so that the row becomes visible + QFETCH(int, column); + QFETCH(Qt::AlignmentFlag, alignment); + QFETCH(qreal, offset); + QFETCH(qreal, contentXStartPos); + + LOAD_TABLEVIEW("plaintableview.qml"); + auto model = TestModelAsVariant(100, 100); + tableView->setModel(model); + if (contentXStartPos >= 0) + tableView->setContentX(contentXStartPos); + + WAIT_UNTIL_POLISHED; + + tableView->positionViewAtColumn(column, alignment, offset); + + WAIT_UNTIL_POLISHED; + + const QPoint cell(column, 0); + const int modelIndex = tableViewPrivate->modelIndexAtCell(cell); + QVERIFY(tableViewPrivate->loadedItems.contains(modelIndex)); + const auto geometry = tableViewPrivate->loadedTableItem(cell)->geometry(); + + switch (alignment) { + case Qt::AlignLeft: + QCOMPARE(geometry.x(), tableView->contentX() - offset); + break; + case Qt::AlignRight: + QCOMPARE(geometry.right(), tableView->contentX() + tableView->width() - offset); + break; + case Qt::AlignCenter: + QCOMPARE(geometry.x(), tableView->contentX() + (tableView->width() / 2) - (geometry.width() / 2) - offset); + break; + default: + Q_UNREACHABLE(); + } +} + QTEST_MAIN(tst_QQuickTableView) #include "tst_qquicktableview.moc" |