summaryrefslogtreecommitdiffstats
path: root/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp')
-rw-r--r--tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp203
1 files changed, 167 insertions, 36 deletions
diff --git a/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp b/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp
index 2031c68708..9059a9262e 100644
--- a/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp
+++ b/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** 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 General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** 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-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
@@ -36,7 +11,9 @@
#include <private/qhighdpiscaling_p.h>
#include <private/qwidget_p.h>
#include <private/qwidgetrepaintmanager_p.h>
+#include <qpa/qplatformintegration.h>
#include <qpa/qplatformbackingstore.h>
+#include <private/qguiapplication_p.h>
//#define MANUAL_DEBUG
@@ -58,14 +35,33 @@ public:
void initialShow()
{
show();
- if (isWindow())
+ if (isWindow()) {
QVERIFY(QTest::qWaitForWindowExposed(this));
+ QVERIFY(waitForPainted());
+ }
paintedRegions = {};
}
bool waitForPainted(int timeout = 5000)
{
- return QTest::qWaitFor([this]{ return !paintedRegions.isEmpty(); }, timeout);
+ int remaining = timeout;
+ QDeadlineTimer deadline(remaining, Qt::PreciseTimer);
+ if (!QTest::qWaitFor([this]{ return !paintedRegions.isEmpty(); }, timeout))
+ return false;
+
+ // In case of multiple paint events:
+ // Process events and wait until all have been consumed,
+ // i.e. paintedRegions no longer changes.
+ QRegion reg;
+ while (remaining > 0 && reg != paintedRegions) {
+ reg = paintedRegions;
+ QCoreApplication::processEvents(QEventLoop::AllEvents, remaining);
+ if (reg == paintedRegions)
+ return true;
+
+ remaining = int(deadline.remainingTime());
+ }
+ return false;
}
QRegion takePaintedRegions()
@@ -76,6 +72,17 @@ public:
}
QRegion paintedRegions;
+ bool event(QEvent *event) override
+ {
+ const auto type = event->type();
+ if (type == QEvent::WindowActivate || type == QEvent::WindowDeactivate)
+ return true;
+ if (type == QEvent::UpdateRequest)
+ ++updateRequests;
+ return QWidget::event(event);
+ }
+ int updateRequests = 0;
+
protected:
void paintEvent(QPaintEvent *event) override
{
@@ -96,6 +103,14 @@ public:
setAttribute(Qt::WA_OpaquePaintEvent);
}
+ bool event(QEvent *event) override
+ {
+ const auto type = event->type();
+ if (type == QEvent::WindowActivate || type == QEvent::WindowDeactivate)
+ return true;
+ return QWidget::event(event);
+ }
+
protected:
void paintEvent(QPaintEvent *e) override
{
@@ -211,6 +226,14 @@ public:
QSize sizeHint() const override { return QSize(400, 400); }
+ bool event(QEvent *event) override
+ {
+ const auto type = event->type();
+ if (type == QEvent::WindowActivate || type == QEvent::WindowDeactivate)
+ return true;
+ return QWidget::event(event);
+ }
+
protected:
void resizeEvent(QResizeEvent *) override
{
@@ -236,6 +259,8 @@ private slots:
void opaqueChildren();
void staticContents();
void scroll();
+ void paintOnScreenUpdates();
+
#if defined(QT_BUILD_INTERNAL)
void scrollWithOverlap();
void overlappedRegion();
@@ -251,13 +276,14 @@ protected:
*/
bool compareWidget(QWidget *w)
{
+ QBackingStore *backingStore = w->window()->backingStore();
+ Q_ASSERT(backingStore && backingStore->handle());
+ QPlatformBackingStore *platformBackingStore = backingStore->handle();
+
if (!waitForFlush(w)) {
qWarning() << "Widget" << w << "failed to flush";
return false;
}
- QBackingStore *backingStore = w->window()->backingStore();
- Q_ASSERT(backingStore && backingStore->handle());
- QPlatformBackingStore *platformBackingStore = backingStore->handle();
QImage backingstoreContent = platformBackingStore->toImage();
if (!w->isWindow()) {
@@ -284,7 +310,14 @@ protected:
}
bool waitForFlush(QWidget *widget) const
{
+ if (!widget)
+ return true;
+
auto *repaintManager = QWidgetPrivate::get(widget->window())->maybeRepaintManager();
+
+ if (!repaintManager)
+ return true;
+
return QTest::qWaitFor([repaintManager]{ return !repaintManager->isDirty(); } );
};
#endif // QT_BUILD_INTERNAL
@@ -307,7 +340,7 @@ void tst_QWidgetRepaintManager::initTestCase()
QVERIFY(QTest::qWaitForWindowExposed(&widget));
m_implementsScroll = widget.backingStore()->handle()->scroll(QRegion(widget.rect()), 1, 1);
- qDebug() << QGuiApplication::platformName() << "QPA backend implements scroll:" << m_implementsScroll;
+ qInfo() << QGuiApplication::platformName() << "QPA backend implements scroll:" << m_implementsScroll;
}
void tst_QWidgetRepaintManager::cleanup()
@@ -346,6 +379,7 @@ void tst_QWidgetRepaintManager::children()
TestWidget *child1 = new TestWidget(&widget);
child1->move(20, 20);
child1->show();
+ QVERIFY(QTest::qWaitForWindowExposed(child1));
QVERIFY(child1->waitForPainted());
QCOMPARE(widget.takePaintedRegions(), QRegion(child1->geometry()));
QCOMPARE(child1->takePaintedRegions(), QRegion(child1->rect()));
@@ -402,16 +436,26 @@ void tst_QWidgetRepaintManager::opaqueChildren()
*/
void tst_QWidgetRepaintManager::staticContents()
{
+ const auto *integration = QGuiApplicationPrivate::platformIntegration();
+ if (!integration->hasCapability(QPlatformIntegration::BackingStoreStaticContents))
+ QSKIP("Platform does not support static backingstore content");
+
TestWidget widget;
widget.setAttribute(Qt::WA_StaticContents);
widget.initialShow();
- const QSize oldSize = widget.size();
+ // Trigger resize via QWindow (similar to QWSI code path)
+ QVERIFY(widget.windowHandle());
+ QSize oldSize = widget.size();
+ widget.windowHandle()->resize(widget.width(), widget.height() + 10);
+ QVERIFY(widget.waitForPainted());
+ QCOMPARE(widget.takePaintedRegions(), QRegion(0, oldSize.width(), widget.width(), 10));
+ // Trigger resize via QWidget
+ oldSize = widget.size();
widget.resize(widget.width() + 10, widget.height());
-
QVERIFY(widget.waitForPainted());
- QEXPECT_FAIL("", "This should just repaint the newly exposed region", Continue);
+ QEXPECT_FAIL("", "QWidgetPrivate::setGeometry_sys wrongly triggers full update", Continue);
QCOMPARE(widget.takePaintedRegions(), QRegion(oldSize.width(), 0, 10, widget.height()));
}
@@ -452,6 +496,90 @@ void tst_QWidgetRepaintManager::scroll()
QCOMPARE(widget.takePaintedRegions(), QRegion());
}
+class PaintOnScreenWidget : public TestWidget
+{
+public:
+ using TestWidget::TestWidget;
+
+ // Explicit override to prevent noPaintOnScreen on Windows
+ QPaintEngine *paintEngine() const override
+ {
+ return nullptr;
+ }
+};
+
+void tst_QWidgetRepaintManager::paintOnScreenUpdates()
+{
+ {
+ TestWidget topLevel;
+ topLevel.setObjectName("TopLevel");
+ topLevel.resize(500, 500);
+ TestWidget renderToTextureWidget(&topLevel);
+ renderToTextureWidget.setObjectName("RenderToTexture");
+ renderToTextureWidget.setGeometry(0, 0, 200, 200);
+ QWidgetPrivate::get(&renderToTextureWidget)->setRenderToTexture();
+
+ PaintOnScreenWidget paintOnScreenWidget(&topLevel);
+ paintOnScreenWidget.setObjectName("PaintOnScreen");
+ paintOnScreenWidget.setGeometry(200, 200, 300, 300);
+
+ topLevel.initialShow();
+
+ // Updating before toggling WA_PaintOnScreen should work fine
+ paintOnScreenWidget.update();
+ paintOnScreenWidget.waitForPainted();
+ QVERIFY(paintOnScreenWidget.waitForPainted());
+
+#ifdef Q_OS_ANDROID
+ QEXPECT_FAIL("", "This test fails on Android", Abort);
+#endif
+ QCOMPARE(paintOnScreenWidget.takePaintedRegions(), paintOnScreenWidget.rect());
+
+ renderToTextureWidget.update();
+ QVERIFY(renderToTextureWidget.waitForPainted());
+ QCOMPARE(renderToTextureWidget.takePaintedRegions(), renderToTextureWidget.rect());
+
+ // Then toggle WA_PaintOnScreen
+ paintOnScreenWidget.setAttribute(Qt::WA_PaintOnScreen);
+
+ // The render-to-texture widget updates fine
+ renderToTextureWidget.update();
+ QVERIFY(renderToTextureWidget.waitForPainted());
+ QCOMPARE(renderToTextureWidget.takePaintedRegions(), renderToTextureWidget.rect());
+
+ // Updating the paint-on-screen texture widget will not result
+ // in a paint event, but should result in an update request.
+ paintOnScreenWidget.updateRequests = 0;
+ paintOnScreenWidget.update();
+ QVERIFY(QTest::qWaitFor([&]{ return paintOnScreenWidget.updateRequests > 0; }));
+
+ // And should not prevent the render-to-texture widget from receiving updates
+ renderToTextureWidget.update();
+ QVERIFY(renderToTextureWidget.waitForPainted());
+ QCOMPARE(renderToTextureWidget.takePaintedRegions(), renderToTextureWidget.rect());
+ }
+
+ {
+ TestWidget paintOnScreenTopLevel;
+ paintOnScreenTopLevel.setObjectName("PaintOnScreenTopLevel");
+ paintOnScreenTopLevel.setAttribute(Qt::WA_PaintOnScreen);
+
+ paintOnScreenTopLevel.initialShow();
+
+ paintOnScreenTopLevel.updateRequests = 0;
+ paintOnScreenTopLevel.update();
+ QVERIFY(QTest::qWaitFor([&]{ return paintOnScreenTopLevel.updateRequests > 0; }));
+
+ // Turn off paint on screen and make it a render-to-texture widget.
+ // This will lead us into a QWidgetRepaintManager::markDirty() code
+ // path that checks updateRequestSent, which is still set from the
+ // update above since paint-on-screen handling doesn't reset it.
+ paintOnScreenTopLevel.setAttribute(Qt::WA_PaintOnScreen, false);
+ QWidgetPrivate::get(&paintOnScreenTopLevel)->setRenderToTexture();
+ paintOnScreenTopLevel.update();
+ QVERIFY(QTest::qWaitFor([&]{ return paintOnScreenTopLevel.updateRequests > 1; }));
+ }
+}
#if defined(QT_BUILD_INTERNAL)
@@ -472,6 +600,8 @@ void tst_QWidgetRepaintManager::scrollWithOverlap()
: QWidget(parent, Qt::WindowStaysOnTopHint)
{
m_scrollArea = new QScrollArea(this);
+ m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
QWidget *w = new QWidget;
w->setPalette(QPalette(Qt::gray));
w->setAutoFillBackground(true);
@@ -619,6 +749,7 @@ void tst_QWidgetRepaintManager::fastMove()
QCOMPARE(dirtyRegion(scene.yellowChild), QRect(0, 0, 100, 100));
}
QCOMPARE(dirtyRegion(&scene), QRect(0, 0, 25, 100));
+ QTRY_VERIFY(dirtyRegion(&scene).isEmpty());
QVERIFY(compareWidget(&scene));
}