QQuickTableView: add support for hiding rows and columns
This patch will add support for hiding rows and columns to TableView. You can now hide a column by returning 0 width for it from the columnWidthProvider. The same can be done to hide a row (by using the rowHeightProvider). If you return NaN or negative number, TableView will fall back to calculate the size of the column/row by looking at the delegate items, like before. This to make it possible to hide some rows/columns, without having to calculate and return the heights and widths of the other rows and columns. [ChangeLog][QtQuick][TableView] Added support for hiding rows and columns by setting their size to 0 from the columnsWidthProvider/rowHeightProvider. Change-Id: If9e1a8db91e257d36cb2787bab4856e6201456ac Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
This commit is contained in:
parent
e04e5db13d
commit
0068575602
File diff suppressed because it is too large
Load Diff
|
@ -88,54 +88,52 @@ public:
|
||||||
public:
|
public:
|
||||||
void begin(const QPoint &cell, const QPointF &pos, QQmlIncubator::IncubationMode incubationMode)
|
void begin(const QPoint &cell, const QPointF &pos, QQmlIncubator::IncubationMode incubationMode)
|
||||||
{
|
{
|
||||||
Q_ASSERT(!active);
|
Q_ASSERT(!m_active);
|
||||||
active = true;
|
m_active = true;
|
||||||
tableEdge = Qt::Edge(0);
|
m_edge = Qt::Edge(0);
|
||||||
tableCells = QLine(cell, cell);
|
m_mode = incubationMode;
|
||||||
mode = incubationMode;
|
m_edgeIndex = cell.x();
|
||||||
cellCount = 1;
|
m_visibleCellsInEdge.clear();
|
||||||
currentIndex = 0;
|
m_visibleCellsInEdge.append(cell.y());
|
||||||
startPos = pos;
|
m_currentIndex = 0;
|
||||||
|
m_startPos = pos;
|
||||||
qCDebug(lcTableViewDelegateLifecycle()) << "begin top-left:" << toString();
|
qCDebug(lcTableViewDelegateLifecycle()) << "begin top-left:" << toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void begin(const QLine cellsToLoad, Qt::Edge edgeToLoad, QQmlIncubator::IncubationMode incubationMode)
|
void begin(Qt::Edge edgeToLoad, int edgeIndex, const QList<int> visibleCellsInEdge, QQmlIncubator::IncubationMode incubationMode)
|
||||||
{
|
{
|
||||||
Q_ASSERT(!active);
|
Q_ASSERT(!m_active);
|
||||||
active = true;
|
m_active = true;
|
||||||
tableEdge = edgeToLoad;
|
m_edge = edgeToLoad;
|
||||||
tableCells = cellsToLoad;
|
m_edgeIndex = edgeIndex;
|
||||||
mode = incubationMode;
|
m_visibleCellsInEdge = visibleCellsInEdge;
|
||||||
cellCount = tableCells.x2() - tableCells.x1() + tableCells.y2() - tableCells.y1() + 1;
|
m_mode = incubationMode;
|
||||||
currentIndex = 0;
|
m_currentIndex = 0;
|
||||||
qCDebug(lcTableViewDelegateLifecycle()) << "begin:" << toString();
|
qCDebug(lcTableViewDelegateLifecycle()) << "begin:" << toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void markAsDone() { active = false; }
|
inline void markAsDone() { m_active = false; }
|
||||||
inline bool isActive() { return active; }
|
inline bool isActive() { return m_active; }
|
||||||
|
|
||||||
inline QPoint firstCell() { return tableCells.p1(); }
|
inline QPoint currentCell() { return cellAt(m_currentIndex); }
|
||||||
inline QPoint lastCell() { return tableCells.p2(); }
|
inline bool hasCurrentCell() { return m_currentIndex < m_visibleCellsInEdge.count(); }
|
||||||
inline QPoint currentCell() { return cellAt(currentIndex); }
|
inline void moveToNextCell() { ++m_currentIndex; }
|
||||||
inline QPoint previousCell() { return cellAt(currentIndex - 1); }
|
|
||||||
|
|
||||||
inline bool atBeginning() { return currentIndex == 0; }
|
inline Qt::Edge edge() { return m_edge; }
|
||||||
inline bool hasCurrentCell() { return currentIndex < cellCount; }
|
inline int row() { return cellAt(0).y(); }
|
||||||
inline void moveToNextCell() { ++currentIndex; }
|
inline int column() { return cellAt(0).x(); }
|
||||||
|
inline QQmlIncubator::IncubationMode incubationMode() { return m_mode; }
|
||||||
|
|
||||||
inline Qt::Edge edge() { return tableEdge; }
|
inline QPointF startPosition() { return m_startPos; }
|
||||||
inline QQmlIncubator::IncubationMode incubationMode() { return mode; }
|
|
||||||
|
|
||||||
inline QPointF startPosition() { return startPos; }
|
|
||||||
|
|
||||||
QString toString()
|
QString toString()
|
||||||
{
|
{
|
||||||
QString str;
|
QString str;
|
||||||
QDebug dbg(&str);
|
QDebug dbg(&str);
|
||||||
dbg.nospace() << "TableSectionLoadRequest(" << "edge:"
|
dbg.nospace() << "TableSectionLoadRequest(" << "edge:"
|
||||||
<< tableEdge << " cells:" << tableCells << " incubation:";
|
<< m_edge << ", edgeIndex:" << m_edgeIndex << ", incubation:";
|
||||||
|
|
||||||
switch (mode) {
|
switch (m_mode) {
|
||||||
case QQmlIncubator::Asynchronous:
|
case QQmlIncubator::Asynchronous:
|
||||||
dbg << "Asynchronous";
|
dbg << "Asynchronous";
|
||||||
break;
|
break;
|
||||||
|
@ -151,22 +149,31 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Qt::Edge tableEdge = Qt::Edge(0);
|
Qt::Edge m_edge = Qt::Edge(0);
|
||||||
QLine tableCells;
|
QList<int> m_visibleCellsInEdge;
|
||||||
int currentIndex = 0;
|
int m_edgeIndex = 0;
|
||||||
int cellCount = 0;
|
int m_currentIndex = 0;
|
||||||
bool active = false;
|
bool m_active = false;
|
||||||
QQmlIncubator::IncubationMode mode = QQmlIncubator::AsynchronousIfNested;
|
QQmlIncubator::IncubationMode m_mode = QQmlIncubator::AsynchronousIfNested;
|
||||||
QPointF startPos;
|
QPointF m_startPos;
|
||||||
|
|
||||||
QPoint cellAt(int index)
|
inline QPoint cellAt(int index) {
|
||||||
{
|
return !m_edge || (m_edge & (Qt::LeftEdge | Qt::RightEdge))
|
||||||
int x = tableCells.p1().x() + (tableCells.dx() ? index : 0);
|
? QPoint(m_edgeIndex, m_visibleCellsInEdge[index])
|
||||||
int y = tableCells.p1().y() + (tableCells.dy() ? index : 0);
|
: QPoint(m_visibleCellsInEdge[index], m_edgeIndex);
|
||||||
return QPoint(x, y);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class EdgeRange {
|
||||||
|
public:
|
||||||
|
EdgeRange();
|
||||||
|
bool containsIndex(Qt::Edge edge, int index);
|
||||||
|
|
||||||
|
int startIndex;
|
||||||
|
int endIndex;
|
||||||
|
qreal size;
|
||||||
|
};
|
||||||
|
|
||||||
enum class RebuildState {
|
enum class RebuildState {
|
||||||
Begin = 0,
|
Begin = 0,
|
||||||
LoadInitalTable,
|
LoadInitalTable,
|
||||||
|
@ -234,7 +241,6 @@ public:
|
||||||
|
|
||||||
TableEdgeLoadRequest loadRequest;
|
TableEdgeLoadRequest loadRequest;
|
||||||
|
|
||||||
QPoint contentSizeBenchMarkPoint = QPoint(-1, -1);
|
|
||||||
QSizeF cellSpacing = QSizeF(0, 0);
|
QSizeF cellSpacing = QSizeF(0, 0);
|
||||||
|
|
||||||
QQmlTableInstanceModel::ReusableFlag reusableFlag = QQmlTableInstanceModel::Reusable;
|
QQmlTableInstanceModel::ReusableFlag reusableFlag = QQmlTableInstanceModel::Reusable;
|
||||||
|
@ -248,6 +254,10 @@ public:
|
||||||
QJSValue rowHeightProvider;
|
QJSValue rowHeightProvider;
|
||||||
QJSValue columnWidthProvider;
|
QJSValue columnWidthProvider;
|
||||||
|
|
||||||
|
EdgeRange cachedNextVisibleEdgeIndex[4];
|
||||||
|
EdgeRange cachedColumnWidth;
|
||||||
|
EdgeRange cachedRowHeight;
|
||||||
|
|
||||||
// TableView uses contentWidth/height to report the size of the table (this
|
// TableView uses contentWidth/height to report the size of the table (this
|
||||||
// will e.g make scrollbars written for Flickable work out of the box). This
|
// will e.g make scrollbars written for Flickable work out of the box). This
|
||||||
// value is continuously calculated, and will change/improve as more columns
|
// value is continuously calculated, and will change/improve as more columns
|
||||||
|
@ -281,8 +291,13 @@ public:
|
||||||
qreal sizeHintForRow(int row);
|
qreal sizeHintForRow(int row);
|
||||||
void calculateTableSize();
|
void calculateTableSize();
|
||||||
|
|
||||||
qreal resolveColumnWidth(int column);
|
inline bool isColumnHidden(int column);
|
||||||
qreal resolveRowHeight(int row);
|
inline bool isRowHidden(int row);
|
||||||
|
|
||||||
|
qreal getColumnLayoutWidth(int column);
|
||||||
|
qreal getRowLayoutHeight(int row);
|
||||||
|
qreal getColumnWidth(int column);
|
||||||
|
qreal getRowHeight(int row);
|
||||||
|
|
||||||
inline int topRow() const { return loadedRows.firstKey(); }
|
inline int topRow() const { return loadedRows.firstKey(); }
|
||||||
inline int bottomRow() const { return loadedRows.lastKey(); }
|
inline int bottomRow() const { return loadedRows.lastKey(); }
|
||||||
|
@ -300,12 +315,16 @@ public:
|
||||||
void updateContentWidth();
|
void updateContentWidth();
|
||||||
void updateContentHeight();
|
void updateContentHeight();
|
||||||
void updateAverageEdgeSize();
|
void updateAverageEdgeSize();
|
||||||
|
void forceLayout();
|
||||||
|
|
||||||
void enforceTableAtOrigin();
|
void enforceTableAtOrigin();
|
||||||
void syncLoadedTableRectFromLoadedTable();
|
void syncLoadedTableRectFromLoadedTable();
|
||||||
void syncLoadedTableFromLoadRequest();
|
void syncLoadedTableFromLoadRequest();
|
||||||
|
|
||||||
|
int nextVisibleEdgeIndex(Qt::Edge edge, int startIndex);
|
||||||
int nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge);
|
int nextVisibleEdgeIndexAroundLoadedTable(Qt::Edge edge);
|
||||||
|
inline int edgeToArrayIndex(Qt::Edge edge);
|
||||||
|
void clearEdgeSizeCache();
|
||||||
|
|
||||||
bool canLoadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const;
|
bool canLoadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const;
|
||||||
bool canUnloadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const;
|
bool canUnloadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const;
|
||||||
|
@ -316,7 +335,6 @@ public:
|
||||||
qreal cellHeight(const QPoint &cell);
|
qreal cellHeight(const QPoint &cell);
|
||||||
|
|
||||||
FxTableItem *loadedTableItem(const QPoint &cell) const;
|
FxTableItem *loadedTableItem(const QPoint &cell) const;
|
||||||
FxTableItem *itemNextTo(const FxTableItem *fxTableItem, const QPoint &direction) const;
|
|
||||||
FxTableItem *createFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode);
|
FxTableItem *createFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode);
|
||||||
FxTableItem *loadFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode);
|
FxTableItem *loadFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode);
|
||||||
|
|
||||||
|
@ -324,18 +342,17 @@ public:
|
||||||
void releaseLoadedItems(QQmlTableInstanceModel::ReusableFlag reusableFlag);
|
void releaseLoadedItems(QQmlTableInstanceModel::ReusableFlag reusableFlag);
|
||||||
|
|
||||||
void unloadItem(const QPoint &cell);
|
void unloadItem(const QPoint &cell);
|
||||||
void unloadItems(const QLine &items);
|
|
||||||
|
|
||||||
void loadInitialTopLeftItem(const QPoint &cell, const QPointF &pos);
|
void loadInitialTopLeftItem(const QPoint &cell, const QPointF &pos);
|
||||||
void loadEdge(Qt::Edge edge, QQmlIncubator::IncubationMode incubationMode);
|
void loadEdge(Qt::Edge edge, QQmlIncubator::IncubationMode incubationMode);
|
||||||
void unloadEdge(Qt::Edge edge);
|
void unloadEdge(Qt::Edge edge);
|
||||||
void loadAndUnloadVisibleEdges();
|
void loadAndUnloadVisibleEdges();
|
||||||
void drainReusePoolAfterLoadRequest();
|
void drainReusePoolAfterLoadRequest();
|
||||||
void cancelLoadRequest();
|
|
||||||
void processLoadRequest();
|
void processLoadRequest();
|
||||||
|
|
||||||
void processRebuildTable();
|
void processRebuildTable();
|
||||||
bool moveToNextRebuildState();
|
bool moveToNextRebuildState();
|
||||||
|
QPoint calculateNewTopLeft();
|
||||||
|
void calculateTopLeft(QPoint &topLeft, QPointF &topLeftPos);
|
||||||
void beginRebuildTable();
|
void beginRebuildTable();
|
||||||
void layoutAfterLoadingInitialTable();
|
void layoutAfterLoadingInitialTable();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2019 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of the QtQuick module of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:LGPL$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 3 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 2.0 or (at your option) the GNU General
|
||||||
|
** Public license version 3 or any later version approved by the KDE Free
|
||||||
|
** Qt Foundation. The licenses are as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||||
|
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Window 2.3
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 640
|
||||||
|
height: 450
|
||||||
|
|
||||||
|
property alias tableView: tableView
|
||||||
|
property var rowsToHide
|
||||||
|
property var columnsToHide
|
||||||
|
|
||||||
|
TableView {
|
||||||
|
id: tableView
|
||||||
|
width: 600
|
||||||
|
height: 400
|
||||||
|
anchors.margins: 1
|
||||||
|
clip: true
|
||||||
|
delegate: tableViewDelegate
|
||||||
|
columnSpacing: 1
|
||||||
|
rowSpacing: 1
|
||||||
|
columnWidthProvider: function(column) {
|
||||||
|
if (columnsToHide.includes(column))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
rowHeightProvider: function(row) {
|
||||||
|
if (rowsToHide.includes(row))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: tableViewDelegate
|
||||||
|
Rectangle {
|
||||||
|
objectName: "tableViewDelegate"
|
||||||
|
color: "lightgray"
|
||||||
|
border.width: 1
|
||||||
|
implicitWidth: 50
|
||||||
|
implicitHeight: 50
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: column + "," + row
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -56,16 +56,14 @@ Item {
|
||||||
delegate: tableViewDelegate
|
delegate: tableViewDelegate
|
||||||
columnSpacing: 1
|
columnSpacing: 1
|
||||||
rowSpacing: 1
|
rowSpacing: 1
|
||||||
columnWidthProvider: function(column) { }
|
columnWidthProvider: function(column) { return "notAValidValue" }
|
||||||
rowHeightProvider: function(row) { return 0 }
|
rowHeightProvider: function(row) { return "notAValidValue" }
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: tableViewDelegate
|
id: tableViewDelegate
|
||||||
Rectangle {
|
Rectangle {
|
||||||
objectName: "tableViewDelegate"
|
objectName: "tableViewDelegate"
|
||||||
implicitWidth: 20
|
|
||||||
implicitHeight: 20
|
|
||||||
color: "lightgray"
|
color: "lightgray"
|
||||||
border.width: 1
|
border.width: 1
|
||||||
Text {
|
Text {
|
||||||
|
|
|
@ -46,6 +46,8 @@ Item {
|
||||||
|
|
||||||
property alias tableView: tableView
|
property alias tableView: tableView
|
||||||
property Component delegate: tableViewDelegate
|
property Component delegate: tableViewDelegate
|
||||||
|
property bool returnNegativeColumnWidth: false
|
||||||
|
property bool returnNegativeRowHeight: false
|
||||||
|
|
||||||
TableView {
|
TableView {
|
||||||
id: tableView
|
id: tableView
|
||||||
|
@ -56,8 +58,16 @@ Item {
|
||||||
delegate: tableViewDelegate
|
delegate: tableViewDelegate
|
||||||
columnSpacing: 1
|
columnSpacing: 1
|
||||||
rowSpacing: 1
|
rowSpacing: 1
|
||||||
columnWidthProvider: function(column) { return column + 10 }
|
columnWidthProvider: function(column) {
|
||||||
rowHeightProvider: function(row) { return row + 10 }
|
if (returnNegativeColumnWidth)
|
||||||
|
return -1
|
||||||
|
return column + 10
|
||||||
|
}
|
||||||
|
rowHeightProvider: function(row) {
|
||||||
|
if (returnNegativeRowHeight)
|
||||||
|
return -1
|
||||||
|
return row + 10
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
|
|
|
@ -111,10 +111,12 @@ private slots:
|
||||||
void checkDelegateWithAnchors();
|
void checkDelegateWithAnchors();
|
||||||
void checkColumnWidthProvider();
|
void checkColumnWidthProvider();
|
||||||
void checkColumnWidthProviderInvalidReturnValues();
|
void checkColumnWidthProviderInvalidReturnValues();
|
||||||
|
void checkColumnWidthProviderNegativeReturnValue();
|
||||||
void checkColumnWidthProviderNotCallable();
|
void checkColumnWidthProviderNotCallable();
|
||||||
void checkRowHeightWithoutProvider();
|
void checkRowHeightWithoutProvider();
|
||||||
void checkRowHeightProvider();
|
void checkRowHeightProvider();
|
||||||
void checkRowHeightProviderInvalidReturnValues();
|
void checkRowHeightProviderInvalidReturnValues();
|
||||||
|
void checkRowHeightProviderNegativeReturnValue();
|
||||||
void checkRowHeightProviderNotCallable();
|
void checkRowHeightProviderNotCallable();
|
||||||
void checkForceLayoutFunction();
|
void checkForceLayoutFunction();
|
||||||
void checkContentWidthAndHeight();
|
void checkContentWidthAndHeight();
|
||||||
|
@ -157,6 +159,8 @@ private slots:
|
||||||
void checkRebuildViewportOnly();
|
void checkRebuildViewportOnly();
|
||||||
void useDelegateChooserWithoutDefault();
|
void useDelegateChooserWithoutDefault();
|
||||||
void checkTableviewInsideAsyncLoader();
|
void checkTableviewInsideAsyncLoader();
|
||||||
|
void hideRowsAndColumns_data();
|
||||||
|
void hideRowsAndColumns();
|
||||||
};
|
};
|
||||||
|
|
||||||
tst_QQuickTableView::tst_QQuickTableView()
|
tst_QQuickTableView::tst_QQuickTableView()
|
||||||
|
@ -373,7 +377,7 @@ void tst_QQuickTableView::checkColumnWidthProviderInvalidReturnValues()
|
||||||
|
|
||||||
tableView->setModel(model);
|
tableView->setModel(model);
|
||||||
|
|
||||||
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*Provider.*valid"));
|
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*implicitHeight.*zero"));
|
||||||
|
|
||||||
WAIT_UNTIL_POLISHED;
|
WAIT_UNTIL_POLISHED;
|
||||||
|
|
||||||
|
@ -381,6 +385,23 @@ void tst_QQuickTableView::checkColumnWidthProviderInvalidReturnValues()
|
||||||
QCOMPARE(fxItem->item->width(), kDefaultColumnWidth);
|
QCOMPARE(fxItem->item->width(), kDefaultColumnWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QQuickTableView::checkColumnWidthProviderNegativeReturnValue()
|
||||||
|
{
|
||||||
|
// Check that we fall back to use the implicit width of the delegate
|
||||||
|
// items if the columnWidthProvider return a negative number.
|
||||||
|
LOAD_TABLEVIEW("userowcolumnprovider.qml");
|
||||||
|
|
||||||
|
auto model = TestModelAsVariant(10, 10);
|
||||||
|
view->rootObject()->setProperty("returnNegativeColumnWidth", true);
|
||||||
|
|
||||||
|
tableView->setModel(model);
|
||||||
|
|
||||||
|
WAIT_UNTIL_POLISHED;
|
||||||
|
|
||||||
|
for (auto fxItem : tableViewPrivate->loadedItems)
|
||||||
|
QCOMPARE(fxItem->item->width(), 20);
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QQuickTableView::checkColumnWidthProviderNotCallable()
|
void tst_QQuickTableView::checkColumnWidthProviderNotCallable()
|
||||||
{
|
{
|
||||||
// Check that we fall back to use default columns widths, if you
|
// Check that we fall back to use default columns widths, if you
|
||||||
|
@ -453,7 +474,7 @@ void tst_QQuickTableView::checkRowHeightProviderInvalidReturnValues()
|
||||||
|
|
||||||
tableView->setModel(model);
|
tableView->setModel(model);
|
||||||
|
|
||||||
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*Provider.*valid"));
|
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*implicitHeight.*zero"));
|
||||||
|
|
||||||
WAIT_UNTIL_POLISHED;
|
WAIT_UNTIL_POLISHED;
|
||||||
|
|
||||||
|
@ -461,6 +482,23 @@ void tst_QQuickTableView::checkRowHeightProviderInvalidReturnValues()
|
||||||
QCOMPARE(fxItem->item->height(), kDefaultRowHeight);
|
QCOMPARE(fxItem->item->height(), kDefaultRowHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QQuickTableView::checkRowHeightProviderNegativeReturnValue()
|
||||||
|
{
|
||||||
|
// Check that we fall back to use the implicit height of the delegate
|
||||||
|
// items if the rowHeightProvider return a negative number.
|
||||||
|
LOAD_TABLEVIEW("userowcolumnprovider.qml");
|
||||||
|
|
||||||
|
auto model = TestModelAsVariant(10, 10);
|
||||||
|
view->rootObject()->setProperty("returnNegativeRowHeight", true);
|
||||||
|
|
||||||
|
tableView->setModel(model);
|
||||||
|
|
||||||
|
WAIT_UNTIL_POLISHED;
|
||||||
|
|
||||||
|
for (auto fxItem : tableViewPrivate->loadedItems)
|
||||||
|
QCOMPARE(fxItem->item->height(), 20);
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QQuickTableView::checkRowHeightProviderNotCallable()
|
void tst_QQuickTableView::checkRowHeightProviderNotCallable()
|
||||||
{
|
{
|
||||||
// Check that we fall back to use default row heights, if you
|
// Check that we fall back to use default row heights, if you
|
||||||
|
@ -538,6 +576,8 @@ void tst_QQuickTableView::checkContentWidthAndHeight()
|
||||||
const qreal expectedSizeInit = (tableSize * cellSizeSmall) + ((tableSize - 1) * spacing);
|
const qreal expectedSizeInit = (tableSize * cellSizeSmall) + ((tableSize - 1) * spacing);
|
||||||
QCOMPARE(tableView->contentWidth(), expectedSizeInit);
|
QCOMPARE(tableView->contentWidth(), expectedSizeInit);
|
||||||
QCOMPARE(tableView->contentHeight(), expectedSizeInit);
|
QCOMPARE(tableView->contentHeight(), expectedSizeInit);
|
||||||
|
QCOMPARE(tableViewPrivate->averageEdgeSize.width(), cellSizeSmall);
|
||||||
|
QCOMPARE(tableViewPrivate->averageEdgeSize.height(), cellSizeSmall);
|
||||||
|
|
||||||
// Flick in 5 more rows and columns, but not so far that we start loading in
|
// Flick in 5 more rows and columns, but not so far that we start loading in
|
||||||
// the ones that are bigger. Loading in more rows and columns of the same
|
// the ones that are bigger. Loading in more rows and columns of the same
|
||||||
|
@ -548,6 +588,8 @@ void tst_QQuickTableView::checkContentWidthAndHeight()
|
||||||
|
|
||||||
QCOMPARE(tableView->contentWidth(), expectedSizeInit);
|
QCOMPARE(tableView->contentWidth(), expectedSizeInit);
|
||||||
QCOMPARE(tableView->contentHeight(), expectedSizeInit);
|
QCOMPARE(tableView->contentHeight(), expectedSizeInit);
|
||||||
|
QCOMPARE(tableViewPrivate->averageEdgeSize.width(), cellSizeSmall);
|
||||||
|
QCOMPARE(tableViewPrivate->averageEdgeSize.height(), cellSizeSmall);
|
||||||
|
|
||||||
// Flick to row and column 20 (smallCellCount), since there the row and
|
// Flick to row and column 20 (smallCellCount), since there the row and
|
||||||
// column sizes increases with 100. Check that TableView then adjusts
|
// column sizes increases with 100. Check that TableView then adjusts
|
||||||
|
@ -562,6 +604,11 @@ void tst_QQuickTableView::checkContentWidthAndHeight()
|
||||||
QVERIFY(tableViewPrivate->rebuildScheduled);
|
QVERIFY(tableViewPrivate->rebuildScheduled);
|
||||||
WAIT_UNTIL_POLISHED;
|
WAIT_UNTIL_POLISHED;
|
||||||
|
|
||||||
|
// Check that the average cell size is now matching the
|
||||||
|
// large cells since they fill up the whole view.
|
||||||
|
QCOMPARE(tableViewPrivate->averageEdgeSize.width(), cellSizeLarge);
|
||||||
|
QCOMPARE(tableViewPrivate->averageEdgeSize.height(), cellSizeLarge);
|
||||||
|
|
||||||
const int largeSizeCellCountInView = qCeil(tableView->width() / cellSizeLarge);
|
const int largeSizeCellCountInView = qCeil(tableView->width() / cellSizeLarge);
|
||||||
const int columnCount = smallCellCount + largeSizeCellCountInView;
|
const int columnCount = smallCellCount + largeSizeCellCountInView;
|
||||||
QCOMPARE(tableViewPrivate->leftColumn(), smallCellCount);
|
QCOMPARE(tableViewPrivate->leftColumn(), smallCellCount);
|
||||||
|
@ -571,41 +618,47 @@ void tst_QQuickTableView::checkContentWidthAndHeight()
|
||||||
const qreal secondHalfOneScreenLength = largeSizeCellCountInView * cellSizeLarge;
|
const qreal secondHalfOneScreenLength = largeSizeCellCountInView * cellSizeLarge;
|
||||||
const qreal lengthAfterFlick = firstHalfLength + secondHalfOneScreenLength;
|
const qreal lengthAfterFlick = firstHalfLength + secondHalfOneScreenLength;
|
||||||
|
|
||||||
const qreal averageCellSize = lengthAfterFlick / columnCount;
|
// Check that loadedTableOuterRect has been calculated correct thus far
|
||||||
const qreal expectedSizeHalf = (tableSize * averageCellSize) + accumulatedSpacing;
|
const qreal spacingAfterFlick = (smallCellCount + largeSizeCellCountInView - 1) * spacing;
|
||||||
|
QCOMPARE(tableViewPrivate->loadedTableOuterRect.left(), flickTo + spacing);
|
||||||
|
QCOMPARE(tableViewPrivate->loadedTableOuterRect.right(), lengthAfterFlick + spacingAfterFlick);
|
||||||
|
QCOMPARE(tableViewPrivate->loadedTableOuterRect.top(), flickTo + spacing);
|
||||||
|
QCOMPARE(tableViewPrivate->loadedTableOuterRect.bottom(), lengthAfterFlick + spacingAfterFlick);
|
||||||
|
|
||||||
QCOMPARE(tableView->contentWidth(), expectedSizeHalf);
|
// At this point, we should have the exact content width/height set, because
|
||||||
QCOMPARE(tableView->contentHeight(), expectedSizeHalf);
|
// TableView knows where the large cells start in the viewport, and how many
|
||||||
|
// columns that remain in the model. It will assume that the rest of the the
|
||||||
|
// columns have the same average size as the ones currently inside the viewport.
|
||||||
|
const qreal expectedContentSize = (smallCellCount * cellSizeSmall) + (largeCellCount * cellSizeLarge) + accumulatedSpacing;
|
||||||
|
QCOMPARE(tableView->contentWidth(), expectedContentSize);
|
||||||
|
QCOMPARE(tableView->contentHeight(), expectedContentSize);
|
||||||
|
|
||||||
// Flick to the end (row/column 100, and overshoot a bit), and
|
// Flick to the end (row/column 100, and overshoot a bit), and
|
||||||
// check that we then end up with the exact content width/height.
|
// check that we then end up with the exact content width/height.
|
||||||
const qreal secondHalfLength = largeCellCount * cellSizeLarge;
|
const qreal secondHalfLength = largeCellCount * cellSizeLarge;
|
||||||
const qreal expectedFullSize = (firstHalfLength + secondHalfLength) + accumulatedSpacing;
|
const qreal expectedFullSize = (firstHalfLength + secondHalfLength) + accumulatedSpacing;
|
||||||
|
const qreal overshoot = 100;
|
||||||
// If we flick more than one page at a time, tableview will jump to the new
|
const qreal endPosX = expectedFullSize - tableView->width() + overshoot;
|
||||||
// position and rebuild the table without loading the edges in-between. Which
|
const qreal endPosY = expectedFullSize - tableView->height() + overshoot;
|
||||||
// row and column that ends up as new top-left is then based on a prediction, and
|
tableView->setContentX(endPosX);
|
||||||
// therefore unreliable. To avoid this to happen (which will also affect the
|
tableView->setContentY(endPosY);
|
||||||
// reported size of the table), we flick to the end position in smaller chuncks.
|
|
||||||
QVERIFY(!tableViewPrivate->polishScheduled);
|
|
||||||
QVERIFY(!tableViewPrivate->rebuildScheduled);
|
|
||||||
int pages = qCeil((expectedFullSize - tableView->contentX()) / tableView->width());
|
|
||||||
for (int i = 0; i < pages; i++) {
|
|
||||||
tableView->setContentX(tableView->contentX() + tableView->width() - 1);
|
|
||||||
tableView->setContentY(tableView->contentY() + tableView->height() - 1);
|
|
||||||
QVERIFY(!tableViewPrivate->rebuildScheduled);
|
|
||||||
}
|
|
||||||
|
|
||||||
QCOMPARE(tableView->contentWidth(), expectedFullSize);
|
QCOMPARE(tableView->contentWidth(), expectedFullSize);
|
||||||
QCOMPARE(tableView->contentHeight(), expectedFullSize);
|
QCOMPARE(tableView->contentHeight(), expectedFullSize);
|
||||||
|
|
||||||
// Flick back to start. Since we know the actual table
|
// Flick back to start
|
||||||
// size, contentWidth/Height shouldn't change.
|
|
||||||
tableView->setContentX(0);
|
tableView->setContentX(0);
|
||||||
tableView->setContentY(0);
|
tableView->setContentY(0);
|
||||||
|
|
||||||
QCOMPARE(tableView->contentWidth(), expectedFullSize);
|
// Since we move the viewport more than a page, tableview
|
||||||
QCOMPARE(tableView->contentHeight(), expectedFullSize);
|
// will jump to the new position and do a rebuild.
|
||||||
|
QVERIFY(tableViewPrivate->polishScheduled);
|
||||||
|
QVERIFY(tableViewPrivate->rebuildScheduled);
|
||||||
|
WAIT_UNTIL_POLISHED;
|
||||||
|
|
||||||
|
// We should now have the same content width/height as when we started
|
||||||
|
QCOMPARE(tableView->contentWidth(), expectedSizeInit);
|
||||||
|
QCOMPARE(tableView->contentHeight(), expectedSizeInit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QQuickTableView::checkPageFlicking()
|
void tst_QQuickTableView::checkPageFlicking()
|
||||||
|
@ -2001,6 +2054,92 @@ void tst_QQuickTableView::checkTableviewInsideAsyncLoader()
|
||||||
QVERIFY(height > 0);
|
QVERIFY(height > 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define INT_LIST(indices) QVariant::fromValue(QList<int>() << indices)
|
||||||
|
|
||||||
|
void tst_QQuickTableView::hideRowsAndColumns_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QVariant>("rowsToHide");
|
||||||
|
QTest::addColumn<QVariant>("columnsToHide");
|
||||||
|
|
||||||
|
const auto emptyList = QVariant::fromValue(QList<int>());
|
||||||
|
|
||||||
|
// Hide rows
|
||||||
|
QTest::newRow("first") << INT_LIST(0) << emptyList;
|
||||||
|
QTest::newRow("middle 1") << INT_LIST(1) << emptyList;
|
||||||
|
QTest::newRow("middle 3") << INT_LIST(3) << emptyList;
|
||||||
|
QTest::newRow("last") << INT_LIST(4) << emptyList;
|
||||||
|
|
||||||
|
QTest::newRow("subsequent 0,1") << INT_LIST(0 << 1) << emptyList;
|
||||||
|
QTest::newRow("subsequent 1,2") << INT_LIST(1 << 2) << emptyList;
|
||||||
|
QTest::newRow("subsequent 3,4") << INT_LIST(3 << 4) << emptyList;
|
||||||
|
|
||||||
|
QTest::newRow("all but first") << INT_LIST(1 << 2 << 3 << 4) << emptyList;
|
||||||
|
QTest::newRow("all but last") << INT_LIST(0 << 1 << 2 << 3) << emptyList;
|
||||||
|
QTest::newRow("all but middle") << INT_LIST(0 << 1 << 3 << 4) << emptyList;
|
||||||
|
|
||||||
|
// Hide columns
|
||||||
|
QTest::newRow("first") << emptyList << INT_LIST(0);
|
||||||
|
QTest::newRow("middle 1") << emptyList << INT_LIST(1);
|
||||||
|
QTest::newRow("middle 3") << emptyList << INT_LIST(3);
|
||||||
|
QTest::newRow("last") << emptyList << INT_LIST(4);
|
||||||
|
|
||||||
|
QTest::newRow("subsequent 0,1") << emptyList << INT_LIST(0 << 1);
|
||||||
|
QTest::newRow("subsequent 1,2") << emptyList << INT_LIST(1 << 2);
|
||||||
|
QTest::newRow("subsequent 3,4") << emptyList << INT_LIST(3 << 4);
|
||||||
|
|
||||||
|
QTest::newRow("all but first") << emptyList << INT_LIST(1 << 2 << 3 << 4);
|
||||||
|
QTest::newRow("all but last") << emptyList << INT_LIST(0 << 1 << 2 << 3);
|
||||||
|
QTest::newRow("all but middle") << emptyList << INT_LIST(0 << 1 << 3 << 4);
|
||||||
|
|
||||||
|
// Hide both rows and columns at the same time
|
||||||
|
QTest::newRow("first") << INT_LIST(0) << INT_LIST(0);
|
||||||
|
QTest::newRow("middle 1") << INT_LIST(1) << INT_LIST(1);
|
||||||
|
QTest::newRow("middle 3") << INT_LIST(3) << INT_LIST(3);
|
||||||
|
QTest::newRow("last") << INT_LIST(4) << INT_LIST(4);
|
||||||
|
|
||||||
|
QTest::newRow("subsequent 0,1") << INT_LIST(0 << 1) << INT_LIST(0 << 1);
|
||||||
|
QTest::newRow("subsequent 1,2") << INT_LIST(1 << 2) << INT_LIST(1 << 2);
|
||||||
|
QTest::newRow("subsequent 3,4") << INT_LIST(3 << 4) << INT_LIST(3 << 4);
|
||||||
|
|
||||||
|
QTest::newRow("all but first") << INT_LIST(1 << 2 << 3 << 4) << INT_LIST(1 << 2 << 3 << 4);
|
||||||
|
QTest::newRow("all but last") << INT_LIST(0 << 1 << 2 << 3) << INT_LIST(0 << 1 << 2 << 3);
|
||||||
|
QTest::newRow("all but middle") << INT_LIST(0 << 1 << 3 << 4) << INT_LIST(0 << 1 << 3 << 4);
|
||||||
|
|
||||||
|
// Hide all rows and columns
|
||||||
|
QTest::newRow("all") << INT_LIST(0 << 1 << 2 << 3 << 4) << INT_LIST(0 << 1 << 2 << 3 << 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QQuickTableView::hideRowsAndColumns()
|
||||||
|
{
|
||||||
|
// Check that you can hide the first row (corner case)
|
||||||
|
// and that we load the other columns as expected.
|
||||||
|
QFETCH(QVariant, rowsToHide);
|
||||||
|
QFETCH(QVariant, columnsToHide);
|
||||||
|
LOAD_TABLEVIEW("hiderowsandcolumns.qml");
|
||||||
|
|
||||||
|
const QList<int> rowsToHideList = qvariant_cast<QList<int>>(rowsToHide);
|
||||||
|
const QList<int> columnsToHideList = qvariant_cast<QList<int>>(columnsToHide);
|
||||||
|
const int modelSize = 5;
|
||||||
|
auto model = TestModelAsVariant(modelSize, modelSize);
|
||||||
|
view->rootObject()->setProperty("rowsToHide", rowsToHide);
|
||||||
|
view->rootObject()->setProperty("columnsToHide", columnsToHide);
|
||||||
|
|
||||||
|
tableView->setModel(model);
|
||||||
|
|
||||||
|
WAIT_UNTIL_POLISHED;
|
||||||
|
|
||||||
|
const int expectedRowCount = modelSize - rowsToHideList.count();
|
||||||
|
const int expectedColumnCount = modelSize - columnsToHideList.count();
|
||||||
|
QCOMPARE(tableViewPrivate->loadedRows.count(), expectedRowCount);
|
||||||
|
QCOMPARE(tableViewPrivate->loadedColumns.count(), expectedColumnCount);
|
||||||
|
|
||||||
|
for (const int row : tableViewPrivate->loadedRows.keys())
|
||||||
|
QVERIFY(!rowsToHideList.contains(row));
|
||||||
|
|
||||||
|
for (const int column : tableViewPrivate->loadedColumns.keys())
|
||||||
|
QVERIFY(!columnsToHideList.contains(column));
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_QQuickTableView)
|
QTEST_MAIN(tst_QQuickTableView)
|
||||||
|
|
||||||
#include "tst_qquicktableview.moc"
|
#include "tst_qquicktableview.moc"
|
||||||
|
|
Loading…
Reference in New Issue