diff options
-rw-r--r-- | src/quick/items/qquicktableview.cpp | 33 | ||||
-rw-r--r-- | src/quick/items/qquicktableview_p.h | 3 | ||||
-rw-r--r-- | src/quick/items/qquicktableview_p_p.h | 3 | ||||
-rw-r--r-- | tests/auto/quick/qquicktableview/data/asyncloader.qml | 56 | ||||
-rw-r--r-- | tests/auto/quick/qquicktableview/data/asyncplain.qml | 96 | ||||
-rw-r--r-- | tests/auto/quick/qquicktableview/tst_qquicktableview.cpp | 63 |
6 files changed, 250 insertions, 4 deletions
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index 67a9a3a7c2..1e2ecf9047 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -1946,6 +1946,39 @@ void QQuickTableView::viewportMoved(Qt::Orientations orientation) d->updatePolish(); } +void QQuickTableViewPrivate::_q_componentFinalized() +{ + // Now that all bindings are evaluated, and we know + // our final geometery, we can build the table. + qCDebug(lcTableViewDelegateLifecycle); + updatePolish(); +} + +void QQuickTableViewPrivate::registerCallbackWhenBindingsAreEvaluated() +{ + // componentComplete() is called on us after all static values have been assigned, but + // before bindings to any anchestors has been evaluated. Especially this means that + // if our size is bound to the parents size, it will still be empty at that point. + // And we cannot build the table without knowing our own size. We could wait until we + // got the first updatePolish() callback, but at that time, any asynchronous loaders that we + // might be inside have already finished loading, which means that we would load all + // the delegate items synchronously instead of asynchronously. We therefore add a componentFinalized + // function that gets called after all the bindings we rely on has been evaluated. + // When receiving this call, we load the delegate items (and build the table). + Q_Q(QQuickTableView); + QQmlEnginePrivate *engPriv = QQmlEnginePrivate::get(qmlEngine(q)); + static int finalizedIdx = -1; + if (finalizedIdx < 0) + finalizedIdx = q->metaObject()->indexOfSlot("_q_componentFinalized()"); + engPriv->registerFinalizeCallback(q, finalizedIdx); +} + +void QQuickTableView::componentComplete() +{ + QQuickFlickable::componentComplete(); + d_func()->registerCallbackWhenBindingsAreEvaluated(); +} + #include "moc_qquicktableview_p.cpp" QT_END_NAMESPACE diff --git a/src/quick/items/qquicktableview_p.h b/src/quick/items/qquicktableview_p.h index 45e20027ba..9fcd4c6c17 100644 --- a/src/quick/items/qquicktableview_p.h +++ b/src/quick/items/qquicktableview_p.h @@ -128,10 +128,13 @@ Q_SIGNALS: protected: void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; void viewportMoved(Qt::Orientations orientation) override; + void componentComplete() override; private: Q_DISABLE_COPY(QQuickTableView) Q_DECLARE_PRIVATE(QQuickTableView) + + Q_PRIVATE_SLOT(d_func(), void _q_componentFinalized()) }; class Q_QUICK_PRIVATE_EXPORT QQuickTableViewAttached : public QObject diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h index 6bf3de76df..4f5c819887 100644 --- a/src/quick/items/qquicktableview_p_p.h +++ b/src/quick/items/qquicktableview_p_p.h @@ -353,6 +353,9 @@ public: void columnsRemovedCallback(const QModelIndex &parent, int begin, int end); void modelResetCallback(); + void _q_componentFinalized(); + void registerCallbackWhenBindingsAreEvaluated(); + inline QString tableLayoutToString() const; void dumpTable() const; }; diff --git a/tests/auto/quick/qquicktableview/data/asyncloader.qml b/tests/auto/quick/qquicktableview/data/asyncloader.qml new file mode 100644 index 0000000000..ba99430416 --- /dev/null +++ b/tests/auto/quick/qquicktableview/data/asyncloader.qml @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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 + +Item { + width: 640 + height: 450 + + property alias loader: loader + property TableView tableView: loader.item ? loader.item.tableView : null + property string loaderSource: "" + + Loader { + id: loader + anchors.fill: parent + source: Qt.resolvedUrl(loaderSource) + asynchronous: true + } +} diff --git a/tests/auto/quick/qquicktableview/data/asyncplain.qml b/tests/auto/quick/qquicktableview/data/asyncplain.qml new file mode 100644 index 0000000000..df09d3e276 --- /dev/null +++ b/tests/auto/quick/qquicktableview/data/asyncplain.qml @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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 +import TestModel 0.1 + +Item { + id: root + width: 640 + height: 450 + + property alias tableView: tableView + property Loader loader: parent + + property int statusWhenDelegate0_0Created: Loader.Null + property int statusWhenDelegate5_5Created: Loader.Null + + property real tableViewWidthWhileBuilding: -1 + property real tableViewHeightWhileBuilding: -1 + + TableView { + id: tableView + anchors.fill: parent + clip: true + delegate: tableViewDelegate + columnSpacing: 1 + rowSpacing: 1 + model: TestModel { + rowCount: 100 + columnCount: 100 + } + } + + Component { + id: tableViewDelegate + Rectangle { + implicitWidth: 100 + implicitHeight: 50 + color: "lightgray" + border.width: 1 + + Text { + anchors.centerIn: parent + text: modelData + } + + Component.onCompleted: { + if (row === 0 && column === 0) { + statusWhenDelegate0_0Created = loader.status + tableViewWidthWhileBuilding = tableView.width + tableViewHeightWhileBuilding = tableView.height + } + else if (row === 5 && column === 5) + statusWhenDelegate5_5Created = loader.status + } + } + } + +} diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp index a1951bdf90..039fb91da0 100644 --- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp +++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp @@ -31,6 +31,7 @@ #include <QtQuick/qquickview.h> #include <QtQuick/private/qquicktableview_p.h> #include <QtQuick/private/qquicktableview_p_p.h> +#include <QtQuick/private/qquickloader_p.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcontext.h> @@ -55,15 +56,28 @@ static const char *kModelDataBindingProp = "modelDataBinding"; Q_DECLARE_METATYPE(QMarginsF); -#define LOAD_TABLEVIEW(fileName) \ - view->setSource(testFileUrl(fileName)); \ - view->show(); \ - QVERIFY(QTest::qWaitForWindowActive(view)); \ +#define DECLARE_TABLEVIEW_VARIABLES \ auto tableView = view->rootObject()->property(kTableViewPropName).value<QQuickTableView *>(); \ QVERIFY(tableView); \ auto tableViewPrivate = QQuickTableViewPrivate::get(tableView); \ Q_UNUSED(tableViewPrivate) +#define LOAD_TABLEVIEW(fileName) \ + view->setSource(testFileUrl(fileName)); \ + view->show(); \ + QVERIFY(QTest::qWaitForWindowActive(view)); \ + DECLARE_TABLEVIEW_VARIABLES + +#define LOAD_TABLEVIEW_ASYNC(fileName) \ + view->setSource(testFileUrl("asyncloader.qml")); \ + view->show(); \ + QVERIFY(QTest::qWaitForWindowActive(view)); \ + auto loader = view->rootObject()->property("loader").value<QQuickLoader *>(); \ + loader->setSource(QUrl::fromLocalFile("data/" fileName)); \ + QTRY_VERIFY(loader->item()); \ + QCOMPARE(loader->status(), QQuickLoader::Status::Ready); \ + DECLARE_TABLEVIEW_VARIABLES + #define WAIT_UNTIL_POLISHED \ QVERIFY(tableViewPrivate->polishScheduled); \ QTRY_VERIFY(!tableViewPrivate->polishScheduled) @@ -139,6 +153,7 @@ private slots: void checkChangingModelFromDelegate(); void checkRebuildViewportOnly(); void useDelegateChooserWithoutDefault(); + void checkTableviewInsideAsyncLoader(); }; tst_QQuickTableView::tst_QQuickTableView() @@ -1815,6 +1830,46 @@ void tst_QQuickTableView::useDelegateChooserWithoutDefault() WAIT_UNTIL_POLISHED; }; +void tst_QQuickTableView::checkTableviewInsideAsyncLoader() +{ + // Check that you can put a TableView inside an async Loader, and + // that the delegate items are created before the loader is ready. + LOAD_TABLEVIEW_ASYNC("asyncplain.qml"); + + // At this point the Loader has finished + QCOMPARE(loader->status(), QQuickLoader::Ready); + + // Check that TableView has finished building + QCOMPARE(tableViewPrivate->rebuildScheduled, false); + QCOMPARE(tableViewPrivate->rebuildState, QQuickTableViewPrivate::RebuildState::Done); + + // Check that all expected delegate items have been loaded + const qreal delegateWidth = 100; + const qreal delegateHeight = 50; + int expectedColumns = qCeil(tableView->width() / delegateWidth); + int expectedRows = qCeil(tableView->height() / delegateHeight); + QCOMPARE(tableViewPrivate->loadedTable.width(), expectedColumns); + QCOMPARE(tableViewPrivate->loadedTable.height(), expectedRows); + + // Check that the loader was still in a loading state while TableView was creating + // delegate items. If we delayed creating delegate items until we got the first + // updatePolish() callback in QQuickTableView, this would not be the case. + auto statusWhenDelegate0_0Completed = qvariant_cast<QQuickLoader::Status>( + loader->item()->property("statusWhenDelegate0_0Created")); + auto statusWhenDelegate5_5Completed = qvariant_cast<QQuickLoader::Status>( + loader->item()->property("statusWhenDelegate5_5Created")); + QCOMPARE(statusWhenDelegate0_0Completed, QQuickLoader::Loading); + QCOMPARE(statusWhenDelegate5_5Completed, QQuickLoader::Loading); + + // Check that TableView had a valid geometry when we started to build. If the build + // was started too early (e.g upon QQuickTableView::componentComplete), width and + // height would still be 0 since the bindings would not have been evaluated yet. + qreal width = loader->item()->property("tableViewWidthWhileBuilding").toReal(); + qreal height = loader->item()->property("tableViewHeightWhileBuilding").toReal(); + QVERIFY(width > 0); + QVERIFY(height > 0); +}; + QTEST_MAIN(tst_QQuickTableView) #include "tst_qquicktableview.moc" |