summaryrefslogtreecommitdiffstats
path: root/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp')
-rw-r--r--tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp1783
1 files changed, 1474 insertions, 309 deletions
diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
index b43494ab54..58e20e159a 100644
--- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
+++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../../../shared/highdpi.h"
@@ -13,6 +13,7 @@
#include <qlineedit.h>
#include <qlistview.h>
#include <qmessagebox.h>
+#include <qmimedata.h>
#include <qpainter.h>
#include <qpoint.h>
#include <qpushbutton.h>
@@ -37,6 +38,7 @@
#include <QtGui/qbackingstore.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/qpa/qplatformwindow.h>
+#include <QtGui/qpa/qplatformdrag.h>
#include <QtGui/qscreen.h>
#include <qmenubar.h>
#include <qcompleter.h>
@@ -49,10 +51,7 @@
#include <QtGui/qwindow.h>
#include <qtimer.h>
#include <QtWidgets/QDoubleSpinBox>
-
-#if defined(Q_OS_MACOS)
-#include "tst_qwidget_mac_helpers.h" // Abstract the ObjC stuff out so not everyone must run an ObjC++ compile.
-#endif
+#include <QtWidgets/QComboBox>
#include <QtTest/QTest>
#include <QtTest/private/qtesthelpers_p.h>
@@ -123,6 +122,34 @@ static QByteArray msgComparisonFailed(T v1, const char *op, T v2)
return s.toLocal8Bit();
}
+template<class T> class EventSpy : public QObject
+{
+public:
+ EventSpy(T *widget, QEvent::Type event)
+ : m_widget(widget), eventToSpy(event)
+ {
+ if (m_widget)
+ m_widget->installEventFilter(this);
+ }
+
+ T *widget() const { return m_widget; }
+ int count() const { return m_count; }
+ void clear() { m_count = 0; }
+
+protected:
+ bool eventFilter(QObject *object, QEvent *event) override
+ {
+ if (event->type() == eventToSpy)
+ ++m_count;
+ return QObject::eventFilter(object, event);
+ }
+
+private:
+ T *m_widget;
+ const QEvent::Type eventToSpy;
+ int m_count = 0;
+};
+
Q_LOGGING_CATEGORY(lcTests, "qt.widgets.tests")
class tst_QWidget : public QObject
@@ -136,7 +163,9 @@ public:
public slots:
void initTestCase();
void cleanup();
+
private slots:
+ void nativeWindowAttribute();
void addActionOverloads();
void getSetCheck();
void fontPropagation();
@@ -162,11 +191,15 @@ private slots:
void mapFromAndTo();
void focusChainOnHide();
void focusChainOnReparent();
+ void focusAbstraction();
void defaultTabOrder();
void reverseTabOrder();
void tabOrderWithProxy();
void tabOrderWithProxyDisabled();
+ void tabOrderWithProxyOutOfOrder();
void tabOrderWithCompoundWidgets();
+ void tabOrderWithCompoundWidgetsInflection_data();
+ void tabOrderWithCompoundWidgetsInflection();
void tabOrderWithCompoundWidgetsNoFocusPolicy();
void tabOrderNoChange();
void tabOrderNoChange2();
@@ -174,6 +207,9 @@ private slots:
void appFocusWidgetWhenLosingFocusProxy();
void explicitTabOrderWithComplexWidget();
void explicitTabOrderWithSpinBox_QTBUG81097();
+ void tabOrderList();
+ void tabOrderComboBox_data();
+ void tabOrderComboBox();
#if defined(Q_OS_WIN)
void activation();
#endif
@@ -198,6 +234,8 @@ private slots:
void saveRestoreGeometry();
void restoreVersion1Geometry_data();
void restoreVersion1Geometry();
+ void restoreGeometryAfterScreenChange_data();
+ void restoreGeometryAfterScreenChange();
void widgetAt();
#ifdef Q_OS_MACOS
@@ -218,6 +256,7 @@ private slots:
void ensureCreated();
void createAndDestroy();
+ void eventsAndAttributesOnDestroy();
void winIdChangeEvent();
void persistentWinId();
void showNativeChild();
@@ -345,6 +384,7 @@ private slots:
void enterLeaveOnWindowShowHide_data();
void enterLeaveOnWindowShowHide();
void taskQTBUG_4055_sendSyntheticEnterLeave();
+ void hoverPosition();
void underMouse();
void taskQTBUG_27643_enterEvents();
#endif
@@ -368,7 +408,6 @@ private slots:
void openModal_taskQTBUG_5804();
void focusProxy();
- void focusProxyAndInputMethods();
void imEnabledNotImplemented();
#ifdef QT_BUILD_INTERNAL
@@ -428,6 +467,15 @@ private slots:
void showFullscreenAndroid();
#endif
+ void setVisibleDuringDestruction();
+
+ void explicitShowHide();
+
+ void dragEnterLeaveSymmetry();
+
+ void reparentWindowHandles_data();
+ void reparentWindowHandles();
+
private:
const QString m_platform;
QSize m_testWidgetSize;
@@ -436,6 +484,16 @@ private:
const bool m_windowsAnimationsEnabled;
QPointingDevice *m_touchScreen;
const int m_fuzz;
+ QPalette simplePalette();
+
+private:
+ enum class ScreenPosition {
+ OffAbove,
+ OffLeft,
+ OffBelow,
+ OffRight,
+ Contained
+ };
};
// Testing get/set functions
@@ -631,6 +689,10 @@ tst_QWidget::~tst_QWidget()
void tst_QWidget::initTestCase()
{
+#ifdef Q_OS_ANDROID
+ if (QNativeInterface::QAndroidApplication::sdkVersion() == 33)
+ QSKIP("Is flaky on Android 13 / RHEL 8.6 and 8.8 (QTQAINFRA-5606)");
+#endif
// Size of reference widget, 200 for < 2000, scale up for larger screens
// to avoid Windows warnings about minimum size for decorated windows.
int width = 200;
@@ -663,6 +725,24 @@ struct ImplicitlyConvertibleTo {
void testFunction0() {}
void testFunction1(bool) {}
+void tst_QWidget::nativeWindowAttribute()
+{
+ QWidget parent;
+ QWidget child(&parent);
+
+ QCOMPARE(parent.windowHandle(), nullptr);
+ QCOMPARE(child.windowHandle(), nullptr);
+
+ // Setting WA_NativeWindow should create window handle
+ parent.setAttribute(Qt::WA_NativeWindow);
+ QVERIFY(parent.windowHandle() != nullptr);
+ // But not its child's window handle
+ QCOMPARE(child.windowHandle(), nullptr);
+ // Until the child also gains WA_NativeWindow
+ child.setAttribute(Qt::WA_NativeWindow);
+ QVERIFY(child.windowHandle() != nullptr);
+}
+
void tst_QWidget::addActionOverloads()
{
// almost exhaustive check of addAction() overloads:
@@ -1857,8 +1937,6 @@ void tst_QWidget::focusChainOnHide()
QWidget::setTabOrder(child, parent.data());
parent->show();
- QApplication::setActiveWindow(parent->window());
- child->activateWindow();
child->setFocus();
QTRY_VERIFY(child->hasFocus());
@@ -1899,8 +1977,11 @@ public:
setObjectName(name);
lineEdit1 = new QLineEdit;
+ lineEdit1->setObjectName(name + "/lineEdit1");
lineEdit2 = new QLineEdit;
+ lineEdit2->setObjectName(name + "/lineEdit2");
lineEdit3 = new QLineEdit;
+ lineEdit3->setObjectName(name + "/lineEdit3");
lineEdit3->setEnabled(false);
QHBoxLayout* hbox = new QHBoxLayout(this);
@@ -1915,6 +1996,111 @@ public:
QLineEdit *lineEdit3;
};
+static QList<QWidget *> getFocusChain(QWidget *start, bool bForward)
+{
+ QList<QWidget *> ret;
+ QWidget *cur = start;
+ // detect infinite loop
+ int count = 100;
+ auto loopGuard = qScopeGuard([]{
+ QFAIL("Inifinite loop detected in focus chain");
+ });
+ do {
+ ret += cur;
+ cur = bForward ? cur->nextInFocusChain() : cur->previousInFocusChain();
+ if (!--count)
+ return ret;
+ } while (cur != start);
+ loopGuard.dismiss();
+ return ret;
+}
+
+void tst_QWidget::focusAbstraction()
+{
+ QWidget *widget1 = new QWidget;
+ widget1->setObjectName("Widget 1");
+ QWidget *widget2 = new QWidget;
+ widget2->setObjectName("Widget 2");
+ QWidget *widget3 = new QWidget;
+ widget3->setObjectName("Widget 3");
+ QWidgetPrivate *priv1 = QWidgetPrivate::get(widget1);
+ QWidgetPrivate *priv2 = QWidgetPrivate::get(widget2);
+ QWidgetPrivate *priv3 = QWidgetPrivate::get(widget3);
+
+ // Verify initialization
+ QVERIFY(!priv1->isInFocusChain());
+ QVERIFY(!priv2->isInFocusChain());
+ QVERIFY(!priv3->isInFocusChain());
+
+ // Verify, that parenting builds a focus chain.
+ QWidget parent;
+ parent.setObjectName("Parent");
+ widget1->setParent(&parent);
+ widget2->setParent(&parent);
+ widget3->setParent(&parent);
+ QVERIFY(priv1->isInFocusChain());
+ QVERIFY(priv2->isInFocusChain());
+ QVERIFY(priv3->isInFocusChain());
+ QWidgetList expected{widget1, widget2, widget3, &parent};
+ QCOMPARE(getFocusChain(widget1, true), expected);
+
+ // Verify, that reparented focus children end up behind parent.
+ widget1->setParent(widget2);
+ priv2->insertIntoFocusChainAfter(widget3);
+ priv2->reparentFocusChildren(QWidgetPrivate::FocusDirection::Next);
+ expected = {widget1, &parent, widget3, widget2};
+ QCOMPARE(getFocusChain(widget1, true), expected);
+ QVERIFY(priv1->isInFocusChain());
+ QVERIFY(priv2->isInFocusChain());
+ QVERIFY(priv3->isInFocusChain());
+
+ // Check removal
+ priv3->removeFromFocusChain(QWidgetPrivate::FocusChainRemovalRule::AssertConsistency);
+ expected.removeOne(widget3);
+ QCOMPARE(getFocusChain(widget1, true), expected);
+ QVERIFY(priv1->isInFocusChain());
+ QVERIFY(priv2->isInFocusChain());
+ QVERIFY(!priv3->isInFocusChain());
+
+ // Check insert
+ priv3->insertIntoFocusChain(QWidgetPrivate::FocusDirection::Previous, widget1);
+ expected = {widget3, widget1, &parent, widget2};
+ QCOMPARE(getFocusChain(widget3, true), expected);
+
+ // Verify, that take doesn't break
+ const QWidgetList taken = QWidgetPrivate::takeFromFocusChain(widget1, widget2);
+ QVERIFY(priv1->isFocusChainConsistent());
+ expected = {widget1, &parent, widget2};
+ QCOMPARE(taken, expected);
+ QVERIFY(priv1->isInFocusChain());
+ QVERIFY(priv2->isInFocusChain());
+ QVERIFY(!priv3->isInFocusChain());
+
+ // Verify insertion of multiple widgets
+ QWidgetPrivate::insertIntoFocusChain(taken, QWidgetPrivate::FocusDirection::Next, widget3);
+ expected = {widget3, widget1, &parent, widget2};
+ QCOMPARE(getFocusChain(widget3, true), expected);
+ QVERIFY(priv1->isInFocusChain());
+ QVERIFY(priv2->isInFocusChain());
+ QVERIFY(priv2->isInFocusChain());
+
+ // Verify broken chain identification
+ // d'tor asserts chain consistency => repair before going out of scope
+ auto guard = qScopeGuard([priv2, widget3]{ priv2->focus_next = widget3; });
+
+ // Nullptr is not allowed
+ priv2->focus_next = nullptr;
+ QVERIFY(!priv1->isFocusChainConsistent());
+
+ // Chain looping back in the middle
+ priv2->focus_next = widget1;
+ QVERIFY(!priv1->isFocusChainConsistent());
+
+ // "last" element pointing to itself
+ priv2->focus_next = widget2;
+ QVERIFY(!priv1->isFocusChainConsistent());
+}
+
void tst_QWidget::defaultTabOrder()
{
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
@@ -1938,7 +2124,6 @@ void tst_QWidget::defaultTabOrder()
container.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
container.show();
container.activateWindow();
- QApplication::setActiveWindow(&container);
QVERIFY(QTest::qWaitForWindowActive(&container));
QTRY_VERIFY(firstEdit->hasFocus());
@@ -1974,23 +2159,29 @@ void tst_QWidget::defaultTabOrder()
void tst_QWidget::reverseTabOrder()
{
- if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
+ if (QGuiApplication::platformName().startsWith(QLatin1StringView("wayland"), Qt::CaseInsensitive))
QSKIP("Wayland: This fails. Figure out why.");
const int compositeCount = 2;
Container container;
- container.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
+ container.setObjectName(QLatin1StringView("Container"));
+ container.setWindowTitle(QLatin1StringView(QTest::currentTestFunction()));
Composite* composite[compositeCount];
QLineEdit *firstEdit = new QLineEdit();
+ firstEdit->setObjectName(QLatin1StringView("FirstEdit"));
container.box->addWidget(firstEdit);
+ static constexpr QLatin1StringView comp("Composite-%1");
for (int i = 0; i < compositeCount; i++) {
- composite[i] = new Composite();
+ const QString name = QString(comp).arg(i);
+ composite[i] = new Composite(nullptr, name);
+ composite[i]->setObjectName(name);
container.box->addWidget(composite[i]);
}
QLineEdit *lastEdit = new QLineEdit();
+ lastEdit->setObjectName(QLatin1StringView("LastEdit"));
container.box->addWidget(lastEdit);
// Reverse tab order inside each composite
@@ -1999,7 +2190,6 @@ void tst_QWidget::reverseTabOrder()
container.show();
container.activateWindow();
- QApplication::setActiveWindow(&container);
QVERIFY(QTest::qWaitForWindowActive(&container));
QTRY_VERIFY(firstEdit->hasFocus());
@@ -2034,6 +2224,139 @@ void tst_QWidget::reverseTabOrder()
QVERIFY(firstEdit->hasFocus());
}
+void tst_QWidget::tabOrderList()
+{
+ Composite c;
+ QCOMPARE(getFocusChain(&c, true),
+ QList<QWidget *>({&c, c.lineEdit1, c.lineEdit2, c.lineEdit3}));
+ QWidget::setTabOrder({c.lineEdit3, c.lineEdit2, c.lineEdit1});
+ // not starting with 3 like one would maybe expect, but still 3, 2, 1
+ QCOMPARE(getFocusChain(&c, true),
+ QList<QWidget *>({&c, c.lineEdit1, c.lineEdit3, c.lineEdit2}));
+}
+
+void tst_QWidget::tabOrderComboBox_data()
+{
+ QTest::addColumn<const bool>("editableAtBeginning");
+ QTest::addColumn<const QList<int>>("firstTabOrder");
+ QTest::addColumn<const QList<int>>("secondTabOrder");
+
+ QTest::addRow("3 not editable") << false << QList<int>{2, 1, 0} << QList<int>{0, 1, 2};
+ QTest::addRow("4 editable") << true << QList<int>{2, 1, 0, 3} << QList<int>{3, 0, 2, 1};
+}
+
+QWidgetList expectedFocusChain(const QList<QComboBox *> &boxes, const QList<int> &sequence)
+{
+ Q_ASSERT(boxes.count() == sequence.count());
+ QWidgetList widgets;
+ for (int i : sequence) {
+ Q_ASSERT(i >= 0);
+ Q_ASSERT(i < boxes.count());
+ QComboBox *box = boxes.at(i);
+ widgets.append(box);
+ if (box->lineEdit())
+ widgets.append(box->lineEdit());
+ }
+
+ return widgets;
+}
+
+QWidgetList realFocusChain(const QList<QComboBox *> &boxes, const QList<int> &sequence)
+{
+ const QWidgetList all = getFocusChain(boxes.at(sequence.at(0)), true);
+ QWidgetList chain;
+ // Filter everything with NoFocus
+ for (auto *widget : all) {
+ if (widget->focusPolicy() != Qt::NoFocus)
+ chain << widget;
+ }
+ return chain;
+}
+
+void setTabOrder(const QList<QComboBox *> &boxes, const QList<int> &sequence)
+{
+ Q_ASSERT(boxes.count() == sequence.count());
+ QWidget *previous = nullptr;
+ for (int i : sequence) {
+ Q_ASSERT(i >= 0);
+ Q_ASSERT(i < boxes.count());
+ QWidget *box = boxes.at(i);
+ if (!previous) {
+ previous = box;
+ } else {
+ QWidget::setTabOrder(previous, box);
+ previous = box;
+ }
+ }
+}
+
+void tst_QWidget::tabOrderComboBox()
+{
+ QFETCH(const bool, editableAtBeginning);
+ QFETCH(const QList<int>, firstTabOrder);
+ QFETCH(const QList<int>, secondTabOrder);
+ const int count = firstTabOrder.count();
+ Q_ASSERT(count == secondTabOrder.count());
+ Q_ASSERT(count > 1);
+
+ QWidget w;
+ w.setObjectName("MainWidget");
+ QVBoxLayout* layout = new QVBoxLayout();
+ w.setLayout(layout);
+
+ QList<QComboBox *> boxes;
+ for (int i = 0; i < count; ++i) {
+ auto box = new QComboBox;
+ box->setObjectName("ComboBox " + QString::number(i));
+ if (editableAtBeginning) {
+ box->setEditable(true);
+ box->lineEdit()->setObjectName("LineEdit " + QString::number(i));
+ }
+ boxes.append(box);
+ layout->addWidget(box);
+ }
+ layout->addStretch();
+
+#define COMPARE(seq)\
+ setTabOrder(boxes, seq);\
+ QCOMPARE(realFocusChain(boxes, seq), expectedFocusChain(boxes, seq))
+
+ COMPARE(firstTabOrder);
+
+ if (!editableAtBeginning) {
+ for (auto *box : boxes)
+ box->setEditable(box);
+ }
+
+ COMPARE(secondTabOrder);
+
+ // Remove the focus proxy of the first combobox's line edit.
+ QComboBox *box = boxes.at(0);
+ QLineEdit *lineEdit = box->lineEdit();
+ const QWidget *prev = lineEdit->previousInFocusChain();
+ const QWidget *next = lineEdit->nextInFocusChain();
+ const QWidget *proxy = lineEdit->focusProxy();
+ QCOMPARE(proxy, box);
+ lineEdit->setFocusProxy(nullptr);
+ QCOMPARE(lineEdit->focusProxy(), nullptr);
+ QCOMPARE(lineEdit->previousInFocusChain(), prev);
+ QCOMPARE(lineEdit->nextInFocusChain(), next);
+
+ // Remove first item and check chain consistency
+ boxes.removeFirst();
+ delete box;
+
+ // Create new list with 0 removed and other indexes updated
+ QList<int> thirdTabOrder(secondTabOrder);
+ thirdTabOrder.removeIf([](int i){ return i == 0; });
+ for (int &i : thirdTabOrder)
+ --i;
+
+ COMPARE(thirdTabOrder);
+
+#undef COMPARE
+}
+
void tst_QWidget::tabOrderWithProxy()
{
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
@@ -2061,7 +2384,6 @@ void tst_QWidget::tabOrderWithProxy()
container.show();
container.activateWindow();
- QApplication::setActiveWindow(&container);
QVERIFY(QTest::qWaitForWindowActive(&container));
QTRY_VERIFY(firstEdit->hasFocus());
@@ -2122,7 +2444,6 @@ void tst_QWidget::tabOrderWithProxyDisabled()
container.show();
container.activateWindow();
- QApplication::setActiveWindow(&container);
if (!QTest::qWaitForWindowActive(&container))
QSKIP("Window failed to activate, skipping test");
@@ -2146,6 +2467,24 @@ void tst_QWidget::tabOrderWithProxyDisabled()
qPrintable(QApplication::focusWidget()->objectName()));
}
+//#define DEBUG_FOCUS_CHAIN
+static void dumpFocusChain(QWidget *start, bool bForward, const char *desc = nullptr)
+{
+#ifdef DEBUG_FOCUS_CHAIN
+ qDebug() << "Dump focus chain, start:" << start << "isForward:" << bForward << desc;
+ QWidget *cur = start;
+ do {
+ qDebug() << "-" << cur;
+ auto widgetPrivate = static_cast<QWidgetPrivate *>(qt_widget_private(cur));
+ cur = bForward ? widgetPrivate->focus_next : widgetPrivate->focus_prev;
+ } while (cur != start);
+#else
+ Q_UNUSED(start);
+ Q_UNUSED(bForward);
+ Q_UNUSED(desc);
+#endif
+}
+
void tst_QWidget::tabOrderWithCompoundWidgets()
{
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
@@ -2186,7 +2525,6 @@ void tst_QWidget::tabOrderWithCompoundWidgets()
container.show();
container.activateWindow();
- QApplication::setActiveWindow(&container);
QVERIFY(QTest::qWaitForWindowActive(&container));
lastEdit->setFocus();
@@ -2237,34 +2575,180 @@ void tst_QWidget::tabOrderWithCompoundWidgets()
QVERIFY(lastEdit->hasFocus());
}
-static QList<QWidget *> getFocusChain(QWidget *start, bool bForward)
+void tst_QWidget::tabOrderWithProxyOutOfOrder()
{
- QList<QWidget *> ret;
- QWidget *cur = start;
- do {
- ret += cur;
- auto widgetPrivate = static_cast<QWidgetPrivate *>(qt_widget_private(cur));
- cur = bForward ? widgetPrivate->focus_next : widgetPrivate->focus_prev;
- } while (cur != start);
- return ret;
+ Container container;
+ container.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
+ container.setObjectName(QLatin1StringView("Container"));
+
+ // important to create the widgets with parent so that they are
+ // added to the focus chain already now, and with the buttonBox
+ // before the outsideButton.
+ QWidget buttonBox(&container);
+ buttonBox.setObjectName(QLatin1StringView("buttonBox"));
+ QPushButton outsideButton(&container);
+ outsideButton.setObjectName(QLatin1StringView("outsideButton"));
+
+ container.box->addWidget(&outsideButton);
+ container.box->addWidget(&buttonBox);
+ QCOMPARE(getFocusChain(&container, true),
+ QList<QWidget*>({&container, &buttonBox, &outsideButton}));
+
+ // this now adds okButon and cancelButton to the focus chain,
+ // after the outsideButton - so the outsideButton is in between
+ // the buttonBox and the children of the buttonBox!
+ QPushButton okButton(&buttonBox);
+ okButton.setObjectName("okButton");
+ QPushButton cancelButton(&buttonBox);
+ cancelButton.setObjectName("cancelButton");
+ QCOMPARE(getFocusChain(&container, true),
+ QList<QWidget*>({&container, &buttonBox, &outsideButton, &okButton, &cancelButton}));
+
+ // by setting the okButton as the focusProxy, the outsideButton becomes
+ // unreachable when navigating the focus chain as the buttonBox is in front
+ // of, and proxies to the okButton behind the outsideButton. setFocusProxy
+ // must fix that by moving the buttonBox in front of the first sibling of
+ // the proxy.
+ buttonBox.setFocusProxy(&okButton);
+ QCOMPARE(getFocusChain(&container, true),
+ QList<QWidget*>({&container, &outsideButton, &buttonBox, &okButton, &cancelButton}));
+
+ container.show();
+ container.activateWindow();
+ if (!QTest::qWaitForWindowActive(&container))
+ QSKIP("Window failed to activate, skipping test");
+
+ QCOMPARE(QApplication::focusWidget(), &outsideButton);
+ container.tab();
+ QCOMPARE(QApplication::focusWidget(), &okButton);
+ container.tab();
+ QCOMPARE(QApplication::focusWidget(), &cancelButton);
+ container.tab();
+ QCOMPARE(QApplication::focusWidget(), &outsideButton);
+
+ container.backTab();
+ QCOMPARE(QApplication::focusWidget(), &cancelButton);
+ container.backTab();
+ QCOMPARE(QApplication::focusWidget(), &okButton);
+ container.backTab();
+ QCOMPARE(QApplication::focusWidget(), &outsideButton);
+ container.backTab();
+ QCOMPARE(QApplication::focusWidget(), &cancelButton);
}
-//#define DEBUG_FOCUS_CHAIN
-static void dumpFocusChain(QWidget *start, bool bForward, const char *desc = nullptr)
+static bool isFocusChainConsistent(QWidget *widget)
{
-#ifdef DEBUG_FOCUS_CHAIN
- qDebug() << "Dump focus chain, start:" << start << "isForward:" << bForward << desc;
- QWidget *cur = start;
- do {
- qDebug() << cur;
- auto widgetPrivate = static_cast<QWidgetPrivate *>(qt_widget_private(cur));
- cur = bForward ? widgetPrivate->focus_next : widgetPrivate->focus_prev;
- } while (cur != start);
-#else
- Q_UNUSED(start);
- Q_UNUSED(bForward);
- Q_UNUSED(desc);
-#endif
+ auto forward = getFocusChain(widget, true);
+ auto backward = getFocusChain(widget, false);
+ auto logger = qScopeGuard([=]{
+ qCritical("Focus chain is not consistent!");
+ qWarning() << forward.size() << "forwards: " << forward;
+ qWarning() << backward.size() << "backwards:" << backward;
+ });
+ // both lists start with the same, the widget
+ if (forward.takeFirst() != backward.takeFirst())
+ return false;
+ const qsizetype chainLength = forward.size();
+ if (backward.size() != chainLength)
+ return false;
+ for (qsizetype i = 0; i < chainLength; ++i) {
+ if (forward.at(i) != backward.at(chainLength - i - 1))
+ return false;
+ }
+ logger.dismiss();
+ return true;
+}
+
+/*
+ This tests that we end up with consistent and complete chains when we set
+ the tab order from a widget (the lineEdit) inside a compound (the tabWidget)
+ to the compound, or visa versa. In that case, QWidget::setTabOrder will walk
+ the focus chain to the focus child inside the compound to replace the compound
+ itself when manipulating the tab order. If that last focus child is then
+ however also the lineEdit, then we must not create an inconsistent or
+ incomplete loop.
+
+ The tabWidget is seen as a compound because QTabWidget sets the tab bar as
+ the focus proxy, and it has more widgets inside, like pages, toolbuttons etc.
+*/
+void tst_QWidget::tabOrderWithCompoundWidgetsInflection_data()
+{
+ QTest::addColumn<QByteArrayList>("tabOrder");
+
+ QTest::addRow("forward")
+ << QByteArrayList{"dialog", "tabWidget", "lineEdit", "compound", "okButton", "cancelButton"};
+ QTest::addRow("backward")
+ << QByteArrayList{"dialog", "cancelButton", "okButton", "compound", "lineEdit", "tabWidget"};
+}
+
+void tst_QWidget::tabOrderWithCompoundWidgetsInflection()
+{
+ QFETCH(const QByteArrayList, tabOrder);
+
+ QDialog dialog;
+ dialog.setObjectName("dialog");
+ QTabWidget *tabWidget = new QTabWidget;
+ tabWidget->setObjectName("tabWidget");
+ tabWidget->setFocusPolicy(Qt::TabFocus);
+ QWidget *page = new QWidget;
+ page->setObjectName("page");
+ QLineEdit *lineEdit = new QLineEdit;
+ lineEdit->setObjectName("lineEdit");
+ QWidget *compound = new QWidget;
+ compound->setObjectName("compound");
+ compound->setFocusPolicy(Qt::TabFocus);
+ QPushButton *okButton = new QPushButton("Ok");
+ okButton->setObjectName("okButton");
+ okButton->setFocusPolicy(Qt::TabFocus);
+ QPushButton *cancelButton = new QPushButton("Cancel");
+ cancelButton->setObjectName("cancelButton");
+ cancelButton->setFocusPolicy(Qt::TabFocus);
+
+ QVBoxLayout *pageLayout = new QVBoxLayout;
+ pageLayout->addWidget(lineEdit);
+ page->setLayout(pageLayout);
+ tabWidget->addTab(page, "Tab");
+
+ QHBoxLayout *compoundLayout = new QHBoxLayout;
+ compoundLayout->addStretch();
+ compoundLayout->addWidget(cancelButton);
+ compoundLayout->addWidget(okButton);
+ compound->setFocusProxy(okButton);
+ compound->setLayout(compoundLayout);
+
+ QVBoxLayout *dialogLayout = new QVBoxLayout;
+ dialogLayout->addWidget(tabWidget);
+ dialogLayout->addWidget(compound);
+ dialog.setLayout(dialogLayout);
+
+ QVERIFY(isFocusChainConsistent(&dialog));
+
+ QList<QWidget *> expectedFocusChain;
+ for (qsizetype i = 0; i < tabOrder.size() - 1; ++i) {
+ QWidget *first = dialog.findChild<QWidget *>(tabOrder.at(i));
+ if (!first && tabOrder.at(i) == dialog.objectName())
+ first = &dialog;
+ QVERIFY(first);
+ if (i == 0)
+ expectedFocusChain.append(first);
+ QWidget *second = dialog.findChild<QWidget *>(tabOrder.at(i + 1));
+ QVERIFY(second);
+ expectedFocusChain.append(second);
+ QWidget::setTabOrder(first, second);
+ QVERIFY(isFocusChainConsistent(&dialog));
+ }
+
+ const auto forwardChain = getFocusChain(&dialog, true);
+ auto logger = qScopeGuard([=]{
+ qCritical("Order of widgets in focus chain not matching:");
+ qCritical() << " Actual :" << forwardChain;
+ qCritical() << " Expected:" << expectedFocusChain;
+ });
+ for (qsizetype i = 0; i < expectedFocusChain.size() - 2; ++i) {
+ QCOMPARE_LT(forwardChain.indexOf(expectedFocusChain.at(i)),
+ forwardChain.indexOf(expectedFocusChain.at(i + 1)));
+ }
+ logger.dismiss();
}
void tst_QWidget::tabOrderWithCompoundWidgetsNoFocusPolicy()
@@ -2288,7 +2772,6 @@ void tst_QWidget::tabOrderWithCompoundWidgetsNoFocusPolicy()
container.show();
container.activateWindow();
- QApplication::setActiveWindow(&container);
if (!QTest::qWaitForWindowActive(&container))
QSKIP("Window failed to activate, skipping test");
@@ -2394,7 +2877,7 @@ void tst_QWidget::appFocusWidgetWithFocusProxyLater()
QLineEdit *lineEdit = new QLineEdit(&window);
lineEdit->setFocus();
window.show();
- QApplication::setActiveWindow(&window);
+ QApplicationPrivate::setActiveWindow(&window);
QVERIFY(QTest::qWaitForWindowActive(&window));
QCOMPARE(QApplication::focusWidget(), lineEdit);
@@ -2422,7 +2905,7 @@ void tst_QWidget::appFocusWidgetWhenLosingFocusProxy()
lineEdit->setFocusProxy(lineEditFocusProxy);
lineEdit->setFocus();
window.show();
- QApplication::setActiveWindow(&window);
+ QApplicationPrivate::setActiveWindow(&window);
QVERIFY(QTest::qWaitForWindowActive(&window));
QCOMPARE(QApplication::focusWidget(), lineEditFocusProxy);
QVERIFY(lineEdit->hasFocus());
@@ -2449,7 +2932,6 @@ void tst_QWidget::explicitTabOrderWithComplexWidget()
QWidget::setTabOrder(lineEditOne, lineEditTwo);
lineEditOne->setFocus();
window.show();
- QApplication::setActiveWindow(&window);
QVERIFY(QTest::qWaitForWindowActive(&window));
QTRY_COMPARE(QApplication::focusWidget(), lineEditOne);
@@ -2478,7 +2960,7 @@ void tst_QWidget::explicitTabOrderWithSpinBox_QTBUG81097()
QWidget::setTabOrder(spinBoxTwo, lineEdit);
spinBoxOne->setFocus();
window.show();
- QApplication::setActiveWindow(&window);
+ QApplicationPrivate::setActiveWindow(&window);
QVERIFY(QTest::qWaitForWindowActive(&window));
QTRY_COMPARE(QApplication::focusWidget(), spinBoxOne);
@@ -2691,18 +3173,15 @@ void tst_QWidget::resizePropagation()
QSKIP("resizePropagation test is designed for XCB only");
#endif
- // Platform specific notes:
- // Linux/XCB:
- // - Unless maximized, a widget and its corresponding window can have different sizes
- // - windowStateChanged can be fired multiple times (QTBUG-102478) when widget is maximized
- //
// Windows:
// When a widget is maximized after it has been resized, the widget retains its original size,
- // while the window shows maximum size
+ // while the window shows maximum size.
+ // windowStateChanged signal gets fired on a no-op change from/to WindowNoState
// Initialize widget and signal spy for window handle
QWidget widget;
widget.showMaximized();
+ QVERIFY(QTest::qWaitForWindowExposed(&widget));
QWindow *window = widget.windowHandle();
QTRY_VERIFY(window);
QSignalSpy spy(window, &QWindow::windowStateChanged);
@@ -2712,10 +3191,14 @@ void tst_QWidget::resizePropagation()
const QSize size1 = QSize(screenSize.width() * 0.5, screenSize.height() * 0.5);
const QSize size2 = QSize(screenSize.width() * 0.625, screenSize.height() * 0.833);
- auto verifyResize = [&](const QSize &size, Qt::WindowState windowState, bool checkCountIncrement, bool checkTargetSize)
+ enum CountIncrementCheck {Equal, Greater};
+ enum TargetSizeCheck {Fail, Warn};
+ auto verifyResize = [&](const QSize &size, Qt::WindowState windowState,
+ CountIncrementCheck checkCountIncrement,
+ TargetSizeCheck checkTargetSize)
{
// Capture count of latest async signals
- if (!checkCountIncrement)
+ if (checkCountIncrement == Equal)
count = spy.count();
// Resize if required
@@ -2726,13 +3209,24 @@ void tst_QWidget::resizePropagation()
QVERIFY(QTest::qWaitForWindowExposed(&widget));
// Check signal count and qDebug output for fail analysis
- if (checkCountIncrement) {
- QTRY_VERIFY(spy.count() > count);
- qDebug() << "spy count:" << spy.count() << "previous count:" << count;
- count = spy.count();
- } else {
- qDebug() << spy << widget.windowState() << window->windowState();
- QCOMPARE(spy.count(), count);
+ switch (checkCountIncrement) {
+ case Greater: {
+ auto logger = qScopeGuard([&](){
+ qDebug() << "spy count:" << spy.count() << "previous count:" << count;
+ });
+ QTRY_VERIFY(spy.count() > count);
+ logger.dismiss();
+ count = spy.count();
+ }
+ break;
+ case Equal: {
+ auto logger = qScopeGuard([&](){
+ qDebug() << spy << widget.windowState() << window->windowState();
+ });
+ QCOMPARE(spy.count(), count);
+ logger.dismiss();
+ }
+ break;
}
// QTRY necessary because state changes are propagated async
@@ -2740,32 +3234,37 @@ void tst_QWidget::resizePropagation()
QTRY_COMPARE(window->windowState(), windowState);
// Check target size with fail or warning
- if (checkTargetSize) {
+ switch (checkTargetSize) {
+ case Fail:
QCOMPARE(widget.size(), window->size());
- } else if (widget.size() != window->size()) {
- qWarning() << m_platform << "size mismtach tolerated. Widget:"
- << widget.size() << "Window:" << window->size();
+ break;
+ case Warn:
+ if (widget.size() != window->size()) {
+ qWarning() << m_platform << "size mismtach tolerated. Widget:"
+ << widget.size() << "Window:" << window->size();
+ }
+ break;
}
};
// test state and size consistency of maximized window
- verifyResize(QSize(), Qt::WindowMaximized, true, true);
+ verifyResize(QSize(), Qt::WindowMaximized, Equal, Fail);
if (QTest::currentTestFailed())
return;
// test state transition, state and size consistency after resize
- verifyResize(size1, Qt::WindowNoState, true, !xcb );
+ verifyResize(size1, Qt::WindowNoState, Greater, xcb ? Warn : Fail );
if (QTest::currentTestFailed())
return;
// test unchanged state, state and size consistency after resize
- verifyResize(size2, Qt::WindowNoState, false, !xcb);
+ verifyResize(size2, Qt::WindowNoState, Equal, xcb ? Warn : Fail);
if (QTest::currentTestFailed())
return;
// test state transition, state and size consistency after maximize
widget.showMaximized();
- verifyResize(QSize(), Qt::WindowMaximized, true, xcb);
+ verifyResize(QSize(), Qt::WindowMaximized, Greater, xcb ? Fail : Warn);
if (QTest::currentTestFailed())
return;
@@ -3065,7 +3564,7 @@ void tst_QWidget::showMinimizedKeepsFocus()
child1.setFocusPolicy(Qt::StrongFocus);
child2.setFocusPolicy(Qt::StrongFocus);
window.show();
- QApplication::setActiveWindow(&window);
+ QApplicationPrivate::setActiveWindow(&window);
QVERIFY(QTest::qWaitForWindowActive(&window));
child2.setFocus();
@@ -3089,7 +3588,7 @@ void tst_QWidget::showMinimizedKeepsFocus()
QWidget *child = new QWidget(&window);
child->setFocusPolicy(Qt::StrongFocus);
window.show();
- QApplication::setActiveWindow(&window);
+ QApplicationPrivate::setActiveWindow(&window);
QVERIFY(QTest::qWaitForWindowActive(&window));
child->setFocus();
QTRY_COMPARE(window.focusWidget(), child);
@@ -3108,7 +3607,7 @@ void tst_QWidget::showMinimizedKeepsFocus()
QWidget *child = new QWidget(&window);
child->setFocusPolicy(Qt::StrongFocus);
window.show();
- QApplication::setActiveWindow(&window);
+ QApplicationPrivate::setActiveWindow(&window);
QVERIFY(QTest::qWaitForWindowActive(&window));
child->setFocus();
QTRY_COMPARE(window.focusWidget(), child);
@@ -3128,7 +3627,7 @@ void tst_QWidget::showMinimizedKeepsFocus()
QWidget *child = new QWidget(&window);
child->setFocusPolicy(Qt::StrongFocus);
window.show();
- QApplication::setActiveWindow(&window);
+ QApplicationPrivate::setActiveWindow(&window);
QVERIFY(QTest::qWaitForWindowActive(&window));
child->setFocus();
QTRY_COMPARE(window.focusWidget(), child);
@@ -3149,7 +3648,7 @@ void tst_QWidget::showMinimizedKeepsFocus()
QWidget *child = new QWidget(&window);
child->setFocusPolicy(Qt::StrongFocus);
window.show();
- QApplication::setActiveWindow(&window);
+ QApplicationPrivate::setActiveWindow(&window);
QVERIFY(QTest::qWaitForWindowActive(&window));
child->setFocus();
QTRY_COMPARE(window.focusWidget(), child);
@@ -3166,7 +3665,7 @@ void tst_QWidget::showMinimizedKeepsFocus()
QTRY_COMPARE(QApplication::focusWidget(), nullptr);
window.showNormal();
- QApplication::setActiveWindow(&window);
+ QApplicationPrivate::setActiveWindow(&window);
QVERIFY(QTest::qWaitForWindowActive(&window));
#ifdef Q_OS_MACOS
if (!macHasAccessToWindowsServer())
@@ -3231,7 +3730,7 @@ void tst_QWidget::reparent()
void tst_QWidget::setScreen()
{
const auto screens = QApplication::screens();
- if (screens.count() < 2)
+ if (screens.size() < 2)
QSKIP("This test tests nothing on a machine with a single screen.");
QScreen *screen0 = screens.at(0);
@@ -3625,9 +4124,9 @@ void tst_QWidget::raise()
QObjectList list1{child1, child2, child3, child4};
QCOMPARE(parentPtr->children(), list1);
- QCOMPARE(allChildren.count(), list1.count());
+ QCOMPARE(allChildren.size(), list1.size());
- for (UpdateWidget *child : qAsConst(allChildren)) {
+ for (UpdateWidget *child : std::as_const(allChildren)) {
int expectedPaintEvents = child == child4 ? 1 : 0;
if (expectedPaintEvents == 0) {
QCOMPARE(child->numPaintEvents, 0);
@@ -3641,9 +4140,10 @@ void tst_QWidget::raise()
for (int i = 0; i < 5; ++i)
child2->raise();
- QTest::qWait(50);
+ QVERIFY(QTest::qWaitForWindowExposed(child2));
+ QApplication::processEvents(); // process events that could be triggered by raise();
- for (UpdateWidget *child : qAsConst(allChildren)) {
+ for (UpdateWidget *child : std::as_const(allChildren)) {
int expectedPaintEvents = child == child2 ? 1 : 0;
int expectedZOrderChangeEvents = child == child2 ? 1 : 0;
QTRY_COMPARE(child->numPaintEvents, expectedPaintEvents);
@@ -3670,15 +4170,17 @@ void tst_QWidget::raise()
onTop->show();
QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
QTRY_VERIFY(onTop->numPaintEvents > 0);
+ QApplication::processEvents(); // process remaining paint events if there's more than one
onTop->reset();
// Reset all the children.
- for (UpdateWidget *child : qAsConst(allChildren))
+ for (UpdateWidget *child : std::as_const(allChildren))
child->reset();
for (int i = 0; i < 5; ++i)
child3->raise();
- QTest::qWait(50);
+ QVERIFY(QTest::qWaitForWindowExposed(child3));
+ QApplication::processEvents(); // process events that could be triggered by raise();
QCOMPARE(onTop->numPaintEvents, 0);
QCOMPARE(onTop->numZOrderChangeEvents, 0);
@@ -3686,7 +4188,7 @@ void tst_QWidget::raise()
QObjectList list3{child1, child4, child2, child3};
QCOMPARE(parent->children(), list3);
- for (UpdateWidget *child : qAsConst(allChildren)) {
+ for (UpdateWidget *child : std::as_const(allChildren)) {
int expectedPaintEvents = 0;
int expectedZOrderChangeEvents = child == child3 ? 1 : 0;
QTRY_COMPARE(child->numPaintEvents, expectedPaintEvents);
@@ -3724,9 +4226,9 @@ void tst_QWidget::lower()
QObjectList list1{child1, child2, child3, child4};
QCOMPARE(parent->children(), list1);
- QCOMPARE(allChildren.count(), list1.count());
+ QCOMPARE(allChildren.size(), list1.size());
- for (UpdateWidget *child : qAsConst(allChildren)) {
+ for (UpdateWidget *child : std::as_const(allChildren)) {
int expectedPaintEvents = child == child4 ? 1 : 0;
if (expectedPaintEvents == 0) {
QCOMPARE(child->numPaintEvents, 0);
@@ -3743,7 +4245,7 @@ void tst_QWidget::lower()
QTest::qWait(100);
- for (UpdateWidget *child : qAsConst(allChildren)) {
+ for (UpdateWidget *child : std::as_const(allChildren)) {
int expectedPaintEvents = child == child3 ? 1 : 0;
int expectedZOrderChangeEvents = child == child4 ? 1 : 0;
QTRY_COMPARE(child->numZOrderChangeEvents, expectedZOrderChangeEvents);
@@ -3789,7 +4291,7 @@ void tst_QWidget::stackUnder()
QObjectList list1{child1, child2, child3, child4};
QCOMPARE(parent->children(), list1);
- for (UpdateWidget *child : qAsConst(allChildren)) {
+ for (UpdateWidget *child : std::as_const(allChildren)) {
int expectedPaintEvents = child == child4 ? 1 : 0;
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
if (expectedPaintEvents == 1 && child->numPaintEvents == 2)
@@ -3807,7 +4309,7 @@ void tst_QWidget::stackUnder()
QObjectList list2{child1, child4, child2, child3};
QCOMPARE(parent->children(), list2);
- for (UpdateWidget *child : qAsConst(allChildren)) {
+ for (UpdateWidget *child : std::as_const(allChildren)) {
int expectedPaintEvents = child == child3 ? 1 : 0;
int expectedZOrderChangeEvents = child == child4 ? 1 : 0;
QTRY_COMPARE(child->numPaintEvents, expectedPaintEvents);
@@ -3822,7 +4324,7 @@ void tst_QWidget::stackUnder()
QObjectList list3{child4, child2, child1, child3};
QCOMPARE(parent->children(), list3);
- for (UpdateWidget *child : qAsConst(allChildren)) {
+ for (UpdateWidget *child : std::as_const(allChildren)) {
int expectedZOrderChangeEvents = child == child1 ? 1 : 0;
if (child == child3) {
#ifndef Q_OS_MACOS
@@ -3944,6 +4446,13 @@ void tst_QWidget::saveRestoreGeometry()
QVERIFY(QTest::qWaitForWindowExposed(&widget));
QApplication::processEvents();
+
+ /* ---------------------------------------------------------------------
+ * This test function is likely to flake when debugged with Qt Creator.
+ * (29px offset making the following QTRY_VERIFY2 fail)
+ * ---------------------------------------------------------------------
+ */
+
QTRY_VERIFY2(HighDpi::fuzzyCompare(widget.pos(), position, m_fuzz),
qPrintable(HighDpi::msgPointMismatch(widget.pos(), position)));
QCOMPARE(widget.size(), size);
@@ -4099,8 +4608,7 @@ void tst_QWidget::restoreVersion1Geometry()
const Qt::WindowStates WindowStateMask = Qt::WindowFullScreen | Qt::WindowMaximized | Qt::WindowMinimized;
QFile f(fileName);
- QVERIFY(f.exists());
- f.open(QIODevice::ReadOnly);
+ QVERIFY(f.open(QIODevice::ReadOnly));
const QByteArray savedGeometry = f.readAll();
QCOMPARE(savedGeometry.size(), 46);
f.close();
@@ -4156,6 +4664,68 @@ void tst_QWidget::restoreVersion1Geometry()
#endif
}
+void tst_QWidget::restoreGeometryAfterScreenChange_data()
+{
+ QTest::addColumn<ScreenPosition>("screenPosition");
+ QTest::addColumn<int>("deltaWidth");
+ QTest::addColumn<int>("deltaHeight");
+ QTest::addColumn<int>("frameMargin");
+ QTest::addColumn<bool>("outside");
+
+ QTest::newRow("offAboveLarge") << ScreenPosition::OffAbove << 200 << 250 << 20 << true;
+ QTest::newRow("fitting") << ScreenPosition::Contained << 80 << 80 << 20 << false;
+ QTest::newRow("offRightWide") << ScreenPosition::OffRight << 150 << 80 << 20 << false;
+ QTest::newRow("offLeftFitting") << ScreenPosition::OffLeft << 70 << 70 << 20 << true;
+ QTest::newRow("offBelowHigh") << ScreenPosition::OffBelow << 80 << 200 << 20 << false;
+}
+
+void tst_QWidget::restoreGeometryAfterScreenChange()
+{
+ const QList<QScreen *> &screens = QApplication::screens();
+ QVERIFY2(!screens.isEmpty(), "No screens found.");
+ const QRect screenGeometry = screens.at(0)->geometry();
+
+ QFETCH(ScreenPosition, screenPosition);
+ QFETCH(int, deltaWidth);
+ QFETCH(int, deltaHeight);
+ QFETCH(int, frameMargin);
+ QFETCH(bool, outside);
+
+ QRect restoredGeometry = screenGeometry;
+ restoredGeometry.setHeight(screenGeometry.height() * deltaHeight / 100);
+ restoredGeometry.setWidth(screenGeometry.width() * deltaWidth / 100);
+ const float moveMargin = outside ? 1.2 : 0.75;
+
+ switch (screenPosition) {
+ case ScreenPosition::OffLeft:
+ restoredGeometry.setLeft(restoredGeometry.width() * (-moveMargin));
+ break;
+ case ScreenPosition::OffAbove:
+ restoredGeometry.setTop(restoredGeometry.height() * (-moveMargin));
+ break;
+ case ScreenPosition::OffRight:
+ restoredGeometry.setRight(restoredGeometry.width() * moveMargin);
+ break;
+ case ScreenPosition::OffBelow:
+ restoredGeometry.setBottom(restoredGeometry.height() * moveMargin);
+ break;
+ case ScreenPosition::Contained:
+ break;
+ }
+
+ // If restored geometry fits into screen and has not been moved,
+ // it is changed only by frame margin plus one pixel at each edge
+ const QRect originalGeometry = restoredGeometry.adjusted(1, frameMargin + 1, 1, frameMargin + 1);
+
+ QWidgetPrivate::checkRestoredGeometry(screenGeometry, &restoredGeometry, frameMargin);
+
+ if (deltaHeight < 100 && deltaWidth < 100 && screenPosition == ScreenPosition::Contained)
+ QCOMPARE(originalGeometry, restoredGeometry);
+
+ // new geometry has to fit on the screen
+ QVERIFY(screenGeometry.contains(restoredGeometry));
+}
+
void tst_QWidget::widgetAt()
{
#ifdef Q_OS_MACOS
@@ -4421,22 +4991,20 @@ class StaticWidget : public QWidget
Q_OBJECT
public:
bool partial = false;
- bool gotPaintEvent = false;
QRegion paintedRegion;
- explicit StaticWidget(QWidget *parent = nullptr) : QWidget(parent)
+ explicit StaticWidget(const QPalette &palette, QWidget *parent = nullptr) : QWidget(parent)
{
setAttribute(Qt::WA_StaticContents);
setAttribute(Qt::WA_OpaquePaintEvent);
- setPalette(Qt::red); // Make sure we have an opaque palette.
+ setPalette(palette);
setAutoFillBackground(true);
}
void paintEvent(QPaintEvent *e) override
{
paintedRegion += e->region();
- gotPaintEvent = true;
-// qDebug() << "paint" << e->region();
+ ++paintEvents;
// Look for a full update, set partial to false if found.
for (QRect r : e->region()) {
partial = (r != rect());
@@ -4444,107 +5012,114 @@ public:
break;
}
}
+
+ // Wait timeout ms until at least one paint event has been consumed
+ // and the counter is no longer increasing.
+ // => making sure to consume multiple paint events relating to one operation
+ // before returning true.
+ bool waitForPaintEvent(int timeout = 100)
+ {
+ QDeadlineTimer deadline(timeout);
+ int count = -1;
+ while (!deadline.hasExpired() && count != paintEvents) {
+ count = paintEvents;
+ QCoreApplication::processEvents();
+ if (count == paintEvents && count > 0) {
+ paintEvents = 0;
+ return true;
+ }
+ }
+ paintEvents = 0;
+ return false;
+ }
+private:
+ int paintEvents = 0;
};
/*
Test that widget resizes and moves can be done with minimal repaints when WA_StaticContents
- and WA_OpaquePaintEvent is set. Test is mac-only for now.
+ and WA_OpaquePaintEvent is set.
*/
void tst_QWidget::optimizedResizeMove()
{
- if (m_platform == QStringLiteral("wayland"))
- QSKIP("Wayland: This fails. Figure out why.");
+ const bool wayland = QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive);
+
QWidget parent;
- parent.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
+ parent.setPalette(simplePalette());
+ parent.setWindowTitle(QTest::currentTestFunction());
parent.resize(400, 400);
- StaticWidget staticWidget(&parent);
- staticWidget.gotPaintEvent = false;
+ StaticWidget staticWidget(simplePalette(), &parent);
staticWidget.move(150, 150);
staticWidget.resize(150, 150);
parent.show();
QVERIFY(QTest::qWaitForWindowExposed(&parent));
- QTRY_VERIFY(staticWidget.gotPaintEvent);
+ QVERIFY(staticWidget.waitForPaintEvent());
- staticWidget.gotPaintEvent = false;
staticWidget.move(staticWidget.pos() + QPoint(10, 10));
- QTest::qWait(20);
- QCOMPARE(staticWidget.gotPaintEvent, false);
+ if (!wayland) {
+ QVERIFY(!staticWidget.waitForPaintEvent());
+ } else {
+ if (staticWidget.waitForPaintEvent())
+ QSKIP("Wayland is not optimising paint events. Skipping test.");
+ }
- staticWidget.gotPaintEvent = false;
staticWidget.move(staticWidget.pos() + QPoint(-10, -10));
- QTest::qWait(20);
- QCOMPARE(staticWidget.gotPaintEvent, false);
+ QVERIFY(!staticWidget.waitForPaintEvent());
- staticWidget.gotPaintEvent = false;
staticWidget.move(staticWidget.pos() + QPoint(-10, 10));
- QTest::qWait(20);
- QCOMPARE(staticWidget.gotPaintEvent, false);
+ QVERIFY(!staticWidget.waitForPaintEvent());
- staticWidget.gotPaintEvent = false;
staticWidget.resize(staticWidget.size() + QSize(10, 10));
- QTRY_VERIFY(staticWidget.gotPaintEvent);
+ QVERIFY(staticWidget.waitForPaintEvent());
QCOMPARE(staticWidget.partial, true);
- staticWidget.gotPaintEvent = false;
staticWidget.resize(staticWidget.size() + QSize(-10, -10));
- QTest::qWait(20);
- QCOMPARE(staticWidget.gotPaintEvent, false);
+ QVERIFY(!staticWidget.waitForPaintEvent());
- staticWidget.gotPaintEvent = false;
staticWidget.resize(staticWidget.size() + QSize(10, -10));
- QTRY_VERIFY(staticWidget.gotPaintEvent);
+ QVERIFY(staticWidget.waitForPaintEvent());
QCOMPARE(staticWidget.partial, true);
- staticWidget.gotPaintEvent = false;
staticWidget.move(staticWidget.pos() + QPoint(10, 10));
staticWidget.resize(staticWidget.size() + QSize(-10, -10));
- QTest::qWait(20);
- QCOMPARE(staticWidget.gotPaintEvent, false);
+ QVERIFY(!staticWidget.waitForPaintEvent());
- staticWidget.gotPaintEvent = false;
staticWidget.move(staticWidget.pos() + QPoint(10, 10));
staticWidget.resize(staticWidget.size() + QSize(10, 10));
- QTRY_VERIFY(staticWidget.gotPaintEvent);
+ QVERIFY(staticWidget.waitForPaintEvent());
QCOMPARE(staticWidget.partial, true);
- staticWidget.gotPaintEvent = false;
staticWidget.move(staticWidget.pos() + QPoint(-10, -10));
staticWidget.resize(staticWidget.size() + QSize(-10, -10));
- QTest::qWait(20);
- QCOMPARE(staticWidget.gotPaintEvent, false);
+ QVERIFY(!staticWidget.waitForPaintEvent());
staticWidget.setAttribute(Qt::WA_StaticContents, false);
- staticWidget.gotPaintEvent = false;
staticWidget.move(staticWidget.pos() + QPoint(-10, -10));
staticWidget.resize(staticWidget.size() + QSize(-10, -10));
- QTRY_VERIFY(staticWidget.gotPaintEvent);
+ QVERIFY(staticWidget.waitForPaintEvent());
QCOMPARE(staticWidget.partial, false);
staticWidget.setAttribute(Qt::WA_StaticContents, true);
staticWidget.setAttribute(Qt::WA_StaticContents, false);
- staticWidget.gotPaintEvent = false;
staticWidget.move(staticWidget.pos() + QPoint(10, 10));
- QTest::qWait(20);
- QCOMPARE(staticWidget.gotPaintEvent, false);
+ QVERIFY(!staticWidget.waitForPaintEvent());
staticWidget.setAttribute(Qt::WA_StaticContents, true);
}
void tst_QWidget::optimizedResize_topLevel()
{
- if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
- QSKIP("Wayland: This fails. Figure out why.");
+ const bool wayland = QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive);
if (QHighDpiScaling::isActive())
QSKIP("Skip due to rounding errors in the regions.");
- StaticWidget topLevel;
+ StaticWidget topLevel(simplePalette());
+ topLevel.setPalette(simplePalette());
topLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
- topLevel.gotPaintEvent = false;
topLevel.show();
QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
- QTRY_VERIFY(topLevel.gotPaintEvent);
+ QVERIFY(topLevel.waitForPaintEvent());
- topLevel.gotPaintEvent = false;
topLevel.partial = false;
topLevel.paintedRegion = QRegion();
@@ -4569,10 +5144,15 @@ void tst_QWidget::optimizedResize_topLevel()
QRegion expectedUpdateRegion(topLevel.rect());
expectedUpdateRegion -= QRect(QPoint(), topLevel.size() - QSize(10, 10));
- QTRY_VERIFY(topLevel.gotPaintEvent);
+ QVERIFY(topLevel.waitForPaintEvent());
if (m_platform == QStringLiteral("xcb") || m_platform == QStringLiteral("offscreen"))
QSKIP("QTBUG-26424");
- QCOMPARE(topLevel.partial, true);
+ if (!wayland) {
+ QCOMPARE(topLevel.partial, true);
+ } else {
+ if (!topLevel.partial)
+ QSKIP("Wayland does repaint partially. Skipping test.");
+ }
QCOMPARE(topLevel.paintedRegion, expectedUpdateRegion);
}
@@ -4742,7 +5322,7 @@ protected:
}
public:
QList<WId> m_winIdList;
- int winIdChangeEventCount() const { return m_winIdList.count(); }
+ int winIdChangeEventCount() const { return m_winIdList.size(); }
};
class CreateDestroyWidget : public WinIdChangeWidget
@@ -4797,6 +5377,84 @@ void tst_QWidget::createAndDestroy()
QVERIFY(widget.internalWinId());
}
+void tst_QWidget::eventsAndAttributesOnDestroy()
+{
+ // The events and attributes when destroying a widget should
+ // include those of hiding the widget.
+
+ CreateDestroyWidget widget;
+ EventSpy<QWidget> showEventSpy(&widget, QEvent::Show);
+ EventSpy<QWidget> hideEventSpy(&widget, QEvent::Hide);
+
+ QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), false);
+ QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), false);
+ QCOMPARE(widget.testAttribute(Qt::WA_Mapped), false);
+
+ widget.show();
+ QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), true);
+ QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), true);
+ QTRY_COMPARE(widget.testAttribute(Qt::WA_Mapped), true);
+ QCOMPARE(showEventSpy.count(), 1);
+ QCOMPARE(hideEventSpy.count(), 0);
+
+ widget.hide();
+ QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), true);
+ QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), false);
+ QCOMPARE(widget.testAttribute(Qt::WA_Mapped), false);
+ QCOMPARE(showEventSpy.count(), 1);
+ QCOMPARE(hideEventSpy.count(), 1);
+
+ widget.show();
+ QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), true);
+ QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), true);
+ QTRY_COMPARE(widget.testAttribute(Qt::WA_Mapped), true);
+ QCOMPARE(showEventSpy.count(), 2);
+ QCOMPARE(hideEventSpy.count(), 1);
+
+ widget.destroy();
+ QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), false);
+ QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), false);
+ QCOMPARE(widget.testAttribute(Qt::WA_Mapped), false);
+ QCOMPARE(showEventSpy.count(), 2);
+ QCOMPARE(hideEventSpy.count(), 2);
+
+ const int hideEventsAfterDestroy = hideEventSpy.count();
+
+ widget.create();
+ QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), true);
+ QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), false);
+ QCOMPARE(widget.testAttribute(Qt::WA_Mapped), false);
+ QCOMPARE(showEventSpy.count(), 2);
+ QCOMPARE(hideEventSpy.count(), hideEventsAfterDestroy);
+
+ QWidgetPrivate::get(&widget)->setVisible(true);
+ QCOMPARE(widget.testAttribute(Qt::WA_WState_Created), true);
+ QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), true);
+ QTRY_COMPARE(widget.testAttribute(Qt::WA_Mapped), true);
+ QCOMPARE(showEventSpy.count(), 3);
+ QCOMPARE(hideEventSpy.count(), hideEventsAfterDestroy);
+
+ // Make sure the destroy that happens when a top level
+ // is moved to being a child does not prevent the child
+ // being shown again.
+
+ QWidget parent;
+ QWidget child;
+ parent.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&parent));
+ child.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&child));
+
+ child.setParent(&parent);
+ QCOMPARE(child.testAttribute(Qt::WA_WState_Created), false);
+ QCOMPARE(child.testAttribute(Qt::WA_WState_Visible), false);
+
+ child.show();
+ QCOMPARE(child.testAttribute(Qt::WA_WState_Created), true);
+ QCOMPARE(child.testAttribute(Qt::WA_WState_Visible), true);
+ QVERIFY(QTest::qWaitForWindowExposed(&child));
+}
+
void tst_QWidget::winIdChangeEvent()
{
{
@@ -4964,24 +5622,29 @@ void tst_QWidget::closeAndShowWithNativeChild()
QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
QWidget topLevel;
+ topLevel.setObjectName("TopLevel");
QWidget *nativeChild = new QWidget;
+ nativeChild->setObjectName("NativeChild");
nativeChild->setFixedSize(200, 200);
- QWidget *nativeHiddenChild = new QWidget;
- nativeHiddenChild->setFixedSize(200, 200);
QWidget *normalChild = new QWidget;
+ normalChild->setObjectName("NormalChild");
normalChild->setFixedSize(200, 200);
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(nativeChild);
- layout->addWidget(nativeHiddenChild);
layout->addWidget(normalChild);
topLevel.setLayout(layout);
- nativeHiddenChild->hide();
+ nativeChild->setAttribute(Qt::WA_NativeWindow);
+
+ QCOMPARE(normalChild->testAttribute(Qt::WA_WState_Hidden), false);
+ QCOMPARE(normalChild->testAttribute(Qt::WA_WState_ExplicitShowHide), false);
+
+ QCOMPARE(nativeChild->testAttribute(Qt::WA_WState_Hidden), false);
+ QCOMPARE(nativeChild->testAttribute(Qt::WA_WState_ExplicitShowHide), false);
topLevel.show();
QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
- nativeChild->winId();
const QSize originalSize = topLevel.size();
topLevel.close();
@@ -5149,6 +5812,7 @@ void tst_QWidget::update()
Q_CHECK_PAINTEVENTS
UpdateWidget w;
+ w.setPalette(simplePalette());
w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
w.resize(100, 100);
centerOnScreen(&w);
@@ -5162,6 +5826,7 @@ void tst_QWidget::update()
w.reset();
UpdateWidget child(&w);
+ child.setPalette(simplePalette());
child.setGeometry(10, 10, 80, 80);
child.show();
@@ -5233,6 +5898,7 @@ void tst_QWidget::update()
// overlapping sibling
UpdateWidget sibling(&w);
+ sibling.setPalette(simplePalette());
child.setGeometry(10, 10, 20, 20);
sibling.setGeometry(15, 15, 20, 20);
sibling.show();
@@ -5312,9 +5978,11 @@ void tst_QWidget::isOpaque()
{
#ifndef Q_OS_MACOS
QWidget w;
+ w.setPalette(simplePalette());
QVERIFY(::isOpaque(&w));
QWidget child(&w);
+ child.setPalette(simplePalette());
QVERIFY(!::isOpaque(&child));
child.setAutoFillBackground(true);
@@ -5396,11 +6064,11 @@ void tst_QWidget::scroll()
const int h = qMin(500, screen->availableGeometry().height() / 2);
UpdateWidget updateWidget;
+ updateWidget.setPalette(simplePalette());
updateWidget.resize(w, h);
updateWidget.reset();
updateWidget.move(m_availableTopLeft);
updateWidget.showNormal();
- QApplication::setActiveWindow(&updateWidget);
QVERIFY(QTest::qWaitForWindowActive(&updateWidget));
QVERIFY(updateWidget.numPaintEvents > 0);
@@ -5514,7 +6182,7 @@ public:
}
QObjectCastChecker(QObjectCastChecker &&other) noexcept
- : m_target(qExchange(other.m_target, nullptr))
+ : m_target(std::exchange(other.m_target, nullptr))
{}
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QObjectCastChecker)
@@ -5588,7 +6256,7 @@ void tst_QWidget::setWindowGeometry_data()
const Qt::WindowFlags windowFlags[] = {Qt::WindowFlags(), Qt::FramelessWindowHint};
const bool skipEmptyRects = (m_platform == QStringLiteral("windows"));
- for (Rects l : qAsConst(rects)) {
+ for (Rects l : std::as_const(rects)) {
if (skipEmptyRects)
l.removeIf([] (const QRect &r) { return r.isEmpty(); });
const QRect &rect = l.constFirst();
@@ -5607,8 +6275,8 @@ void tst_QWidget::setWindowGeometry_data()
void tst_QWidget::setWindowGeometry()
{
- if (m_platform == QStringLiteral("xcb"))
- QSKIP("X11: Skip this test due to Window manager positioning issues.");
+ if (m_platform == QStringLiteral("xcb") || m_platform.startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
+ QSKIP("X11/Wayland: Skip this test due to Window manager positioning issues.");
QFETCH(Rects, rects);
QFETCH(int, windowFlags);
@@ -5625,7 +6293,7 @@ void tst_QWidget::setWindowGeometry()
QCOMPARE(widget.geometry(), rect);
// setGeometry() without showing
- for (const QRect &r : qAsConst(rects)) {
+ for (const QRect &r : std::as_const(rects)) {
widget.setGeometry(r);
QTest::qWait(100);
QCOMPARE(widget.geometry(), r);
@@ -5651,7 +6319,7 @@ void tst_QWidget::setWindowGeometry()
QTRY_COMPARE(widget.geometry(), rect);
// setGeometry() while shown
- for (const QRect &r : qAsConst(rects)) {
+ for (const QRect &r : std::as_const(rects)) {
widget.setGeometry(r);
QTest::qWait(10);
QTRY_COMPARE(widget.geometry(), r);
@@ -5666,7 +6334,7 @@ void tst_QWidget::setWindowGeometry()
QTRY_COMPARE(widget.geometry(), rect);
// setGeometry() after hide()
- for (const QRect &r : qAsConst(rects)) {
+ for (const QRect &r : std::as_const(rects)) {
widget.setGeometry(r);
QTest::qWait(10);
QTRY_COMPARE(widget.geometry(), r);
@@ -5702,7 +6370,7 @@ void tst_QWidget::setWindowGeometry()
QTRY_COMPARE(widget.geometry(), rect);
// setGeometry() while shown
- for (const QRect &r : qAsConst(rects)) {
+ for (const QRect &r : std::as_const(rects)) {
widget.setGeometry(r);
QTest::qWait(10);
QTRY_COMPARE(widget.geometry(), r);
@@ -5717,7 +6385,7 @@ void tst_QWidget::setWindowGeometry()
QTRY_COMPARE(widget.geometry(), rect);
// setGeometry() after hide()
- for (const QRect &r : qAsConst(rects)) {
+ for (const QRect &r : std::as_const(rects)) {
widget.setGeometry(r);
QTest::qWait(10);
QTRY_COMPARE(widget.geometry(), r);
@@ -5796,7 +6464,7 @@ void tst_QWidget::windowMoveResize()
QTRY_COMPARE(widget.size(), rect.size());
// move() without showing
- for (const QRect &r : qAsConst(rects)) {
+ for (const QRect &r : std::as_const(rects)) {
widget.move(r.topLeft());
widget.resize(r.size());
QApplication::processEvents();
@@ -5826,7 +6494,7 @@ void tst_QWidget::windowMoveResize()
QTRY_COMPARE(widget.size(), rect.size());
// move() while shown
- for (const QRect &r : qAsConst(rects)) {
+ for (const QRect &r : std::as_const(rects)) {
// XCB: First resize after show of zero-sized gets wrong win_gravity.
const bool expectMoveFail = !windowFlags
&& ((widget.width() == 0 || widget.height() == 0) && r.width() != 0 && r.height() != 0)
@@ -5855,7 +6523,7 @@ void tst_QWidget::windowMoveResize()
QTRY_COMPARE(widget.size(), rect.size());
// move() after hide()
- for (const QRect &r : qAsConst(rects)) {
+ for (const QRect &r : std::as_const(rects)) {
widget.move(r.topLeft());
widget.resize(r.size());
QApplication::processEvents();
@@ -5906,7 +6574,7 @@ void tst_QWidget::windowMoveResize()
QTRY_COMPARE(widget.size(), rect.size());
// move() while shown
- for (const QRect &r : qAsConst(rects)) {
+ for (const QRect &r : std::as_const(rects)) {
widget.move(r.topLeft());
widget.resize(r.size());
QApplication::processEvents();
@@ -5926,7 +6594,7 @@ void tst_QWidget::windowMoveResize()
QTRY_COMPARE(widget.size(), rect.size());
// move() after hide()
- for (const QRect &r : qAsConst(rects)) {
+ for (const QRect &r : std::as_const(rects)) {
widget.move(r.topLeft());
widget.resize(r.size());
QApplication::processEvents();
@@ -6133,6 +6801,9 @@ void tst_QWidget::moveChild()
void tst_QWidget::showAndMoveChild()
{
+#ifdef ANDROID
+ QSKIP("Fails on Android due to removed grabWindow(): QTBUG-118849");
+#endif
if (m_platform == QStringLiteral("wayland"))
QSKIP("Wayland: This fails. Figure out why.");
QWidget parent(nullptr, Qt::Window | Qt::WindowStaysOnTopHint);
@@ -6150,7 +6821,7 @@ void tst_QWidget::showAndMoveChild()
parent.setGeometry(desktopDimensions);
parent.setPalette(Qt::red);
parent.show();
- QApplication::setActiveWindow(&parent);
+ QApplicationPrivate::setActiveWindow(&parent);
QVERIFY(QTest::qWaitForWindowActive(&parent));
QWidget child(&parent);
@@ -6269,17 +6940,17 @@ void tst_QWidget::multipleToplevelFocusCheck()
w2.show();
QVERIFY(QTest::qWaitForWindowExposed(&w2));
- QApplication::setActiveWindow(&w1);
w1.activateWindow();
+ QApplicationPrivate::setActiveWindow(&w1);
QVERIFY(QTest::qWaitForWindowActive(&w1));
- QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w1));
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w1));
QTest::mouseDClick(&w1, Qt::LeftButton);
QTRY_COMPARE(QApplication::focusWidget(), static_cast<QWidget *>(w1.edit));
w2.activateWindow();
- QApplication::setActiveWindow(&w2);
+ QApplicationPrivate::setActiveWindow(&w2);
QVERIFY(QTest::qWaitForWindowActive(&w2));
- QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w2));
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w2));
QTest::mouseClick(&w2, Qt::LeftButton);
QTRY_COMPARE(QApplication::focusWidget(), nullptr);
@@ -6287,16 +6958,16 @@ void tst_QWidget::multipleToplevelFocusCheck()
QTRY_COMPARE(QApplication::focusWidget(), static_cast<QWidget *>(w2.edit));
w1.activateWindow();
- QApplication::setActiveWindow(&w1);
+ QApplicationPrivate::setActiveWindow(&w1);
QVERIFY(QTest::qWaitForWindowActive(&w1));
- QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w1));
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w1));
QTest::mouseDClick(&w1, Qt::LeftButton);
QTRY_COMPARE(QApplication::focusWidget(), static_cast<QWidget *>(w1.edit));
w2.activateWindow();
- QApplication::setActiveWindow(&w2);
+ QApplicationPrivate::setActiveWindow(&w2);
QVERIFY(QTest::qWaitForWindowActive(&w2));
- QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w2));
+ QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&w2));
QTest::mouseClick(&w2, Qt::LeftButton);
QTRY_COMPARE(QApplication::focusWidget(), nullptr);
}
@@ -6359,7 +7030,7 @@ void tst_QWidget::setFocus()
{
// move focus to another window
testWidget->activateWindow();
- QApplication::setActiveWindow(testWidget.data());
+ QApplicationPrivate::setActiveWindow(testWidget.data());
if (testWidget->focusWidget())
testWidget->focusWidget()->clearFocus();
else
@@ -6405,7 +7076,7 @@ void tst_QWidget::setFocus()
// note: window may be active, but we don't want it to be
testWidget->activateWindow();
- QApplication::setActiveWindow(testWidget.data());
+ QApplicationPrivate::setActiveWindow(testWidget.data());
if (testWidget->focusWidget())
testWidget->focusWidget()->clearFocus();
else
@@ -6581,34 +7252,6 @@ void tst_QWidget::setFocus()
}
}
-template<class T> class EventSpy : public QObject
-{
-public:
- EventSpy(T *widget, QEvent::Type event)
- : m_widget(widget), eventToSpy(event)
- {
- if (m_widget)
- m_widget->installEventFilter(this);
- }
-
- T *widget() const { return m_widget; }
- int count() const { return m_count; }
- void clear() { m_count = 0; }
-
-protected:
- bool eventFilter(QObject *object, QEvent *event) override
- {
- if (event->type() == eventToSpy)
- ++m_count;
- return QObject::eventFilter(object, event);
- }
-
-private:
- T *m_widget;
- const QEvent::Type eventToSpy;
- int m_count = 0;
-};
-
#ifndef QT_NO_CURSOR
void tst_QWidget::setCursor()
{
@@ -6812,7 +7455,7 @@ void tst_QWidget::testWindowIconChangeEventPropagation()
QWidgetList widgets;
widgets << &topLevelWidget << &topLevelChild
<< &dialog << &dialogChild;
- QCOMPARE(widgets.count(), 4);
+ QCOMPARE(widgets.size(), 4);
topLevelWidget.show();
dialog.show();
@@ -6826,13 +7469,13 @@ void tst_QWidget::testWindowIconChangeEventPropagation()
// Create spy lists.
QList <EventSpyPtr> applicationEventSpies;
QList <EventSpyPtr> widgetEventSpies;
- for (QWidget *widget : qAsConst(widgets)) {
+ for (QWidget *widget : std::as_const(widgets)) {
applicationEventSpies.append(EventSpyPtr::create(widget, QEvent::ApplicationWindowIconChange));
widgetEventSpies.append(EventSpyPtr::create(widget, QEvent::WindowIconChange));
}
QList <WindowEventSpyPtr> appWindowEventSpies;
QList <WindowEventSpyPtr> windowEventSpies;
- for (QWindow *window : qAsConst(windows)) {
+ for (QWindow *window : std::as_const(windows)) {
appWindowEventSpies.append(WindowEventSpyPtr::create(window, QEvent::ApplicationWindowIconChange));
windowEventSpies.append(WindowEventSpyPtr::create(window, QEvent::WindowIconChange));
}
@@ -6841,7 +7484,7 @@ void tst_QWidget::testWindowIconChangeEventPropagation()
const QIcon windowIcon = qApp->style()->standardIcon(QStyle::SP_TitleBarMenuButton);
qApp->setWindowIcon(windowIcon);
- for (int i = 0; i < widgets.count(); ++i) {
+ for (int i = 0; i < widgets.size(); ++i) {
// Check QEvent::ApplicationWindowIconChange
EventSpyPtr spy = applicationEventSpies.at(i);
QWidget *widget = spy->widget();
@@ -6858,7 +7501,7 @@ void tst_QWidget::testWindowIconChangeEventPropagation()
QCOMPARE(spy->count(), 1);
spy->clear();
}
- for (int i = 0; i < windows.count(); ++i) {
+ for (int i = 0; i < windows.size(); ++i) {
// Check QEvent::ApplicationWindowIconChange (sent to QWindow)
// QWidgetWindows don't get this event, since the widget takes care of changing the icon
WindowEventSpyPtr spy = appWindowEventSpies.at(i);
@@ -6876,7 +7519,7 @@ void tst_QWidget::testWindowIconChangeEventPropagation()
// Set icon on a top-level widget.
topLevelWidget.setWindowIcon(QIcon());
- for (int i = 0; i < widgets.count(); ++i) {
+ for (int i = 0; i < widgets.size(); ++i) {
// Check QEvent::ApplicationWindowIconChange
EventSpyPtr spy = applicationEventSpies.at(i);
QCOMPARE(spy->count(), 0);
@@ -7033,7 +7676,7 @@ void tst_QWidget::clean_qt_x11_enforce_cursor()
child->setAttribute(Qt::WA_SetCursor, true);
window.show();
- QApplication::setActiveWindow(&window);
+ QApplicationPrivate::setActiveWindow(&window);
QVERIFY(QTest::qWaitForWindowActive(&window));
QTest::qWait(100);
QCursor::setPos(window.geometry().center());
@@ -7410,6 +8053,9 @@ private:
void tst_QWidget::render()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("QTBUG-118984: crashes on Android.");
+#endif
QCalendarWidget source;
source.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
// disable anti-aliasing to eliminate potential differences when subpixel antialiasing
@@ -7466,7 +8112,8 @@ void tst_QWidget::renderChildFillsBackground()
#ifndef Q_OS_ANDROID
// On Android all widgets are shown maximized, so the pixmaps
// will be similar
- QEXPECT_FAIL("", "This test fails on all platforms", Continue);
+ if (!m_platform.startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
+ QEXPECT_FAIL("", "This test fails on all platforms", Continue);
#endif
QCOMPARE(childPixmap, windowPixmap);
}
@@ -7501,15 +8148,12 @@ void tst_QWidget::renderTargetOffset()
QCOMPARE(image.pixel(120, 120), QColor(Qt::blue).rgb());
}
-// On Windows the active palette is used instead of the inactive palette even
-// though the widget is invisible. This is probably related to task 178507/168682,
-// but for the renderInvisible test it doesn't matter, we're mostly interested
-// in testing the geometry so just workaround the palette issue for now.
+// On some platforms the active palette is used instead of the inactive palette even
+// though the widget is invisible, but for the renderInvisible test it doesn't matter,
+// as we're mostly interested in testing the geometry, so just workaround the palette
+// issue for now.
static void workaroundPaletteIssue(QWidget *widget)
{
-#ifndef Q_OS_WIN
- return;
-#endif
if (!widget)
return;
@@ -7530,6 +8174,9 @@ void tst_QWidget::renderInvisible()
if (m_platform == QStringLiteral("xcb"))
QSKIP("QTBUG-26424");
+ if (m_platform.startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
+ QSKIP("Wayland: Skip this test, see also QTBUG-107157");
+
QScopedPointer<QCalendarWidget> calendar(new QCalendarWidget);
calendar->move(m_availableTopLeft + QPoint(100, 100));
calendar->setWindowTitle(QLatin1String(QTest::currentTestFunction()));
@@ -7550,7 +8197,7 @@ void tst_QWidget::renderInvisible()
// Create normal reference image.
const QSize calendarSize = calendar->size();
- QImage referenceImage(calendarSize, QImage::Format_ARGB32);
+ QImage referenceImage(calendarSize, QImage::Format_ARGB32_Premultiplied);
calendar->render(&referenceImage);
#ifdef RENDER_DEBUG
referenceImage.save("referenceImage.png");
@@ -7561,7 +8208,7 @@ void tst_QWidget::renderInvisible()
const QSize calendarSizeResized = calendar->size() + QSize(50, 50);
calendar->resize(calendarSizeResized);
QTest::qWait(30);
- QImage referenceImageResized(calendarSizeResized, QImage::Format_ARGB32);
+ QImage referenceImageResized(calendarSizeResized, QImage::Format_ARGB32_Premultiplied);
calendar->render(&referenceImageResized);
#ifdef RENDER_DEBUG
referenceImageResized.save("referenceImageResized.png");
@@ -7574,7 +8221,7 @@ void tst_QWidget::renderInvisible()
workaroundPaletteIssue(calendar.data());
{ // Make sure we get the same image when the calendar is explicitly hidden.
- QImage testImage(calendarSizeResized, QImage::Format_ARGB32);
+ QImage testImage(calendarSizeResized, QImage::Format_ARGB32_Premultiplied);
calendar->render(&testImage);
#ifdef RENDER_DEBUG
testImage.save("explicitlyHiddenCalendarResized.png");
@@ -7590,7 +8237,7 @@ void tst_QWidget::renderInvisible()
workaroundPaletteIssue(calendar.data());
{ // Never been visible, created or laid out.
- QImage testImage(calendarSize, QImage::Format_ARGB32);
+ QImage testImage(calendarSize, QImage::Format_ARGB32_Premultiplied);
calendar->render(&testImage);
#ifdef RENDER_DEBUG
testImage.save("neverBeenVisibleCreatedOrLaidOut.png");
@@ -7602,7 +8249,7 @@ void tst_QWidget::renderInvisible()
QTest::qWait(30);
{ // Calendar explicitly hidden.
- QImage testImage(calendarSize, QImage::Format_ARGB32);
+ QImage testImage(calendarSize, QImage::Format_ARGB32_Premultiplied);
calendar->render(&testImage);
#ifdef RENDER_DEBUG
testImage.save("explicitlyHiddenCalendar.png");
@@ -7616,7 +8263,7 @@ void tst_QWidget::renderInvisible()
navigationBar->hide();
{ // Check that the navigation bar isn't drawn when rendering the entire calendar.
- QImage testImage(calendarSize, QImage::Format_ARGB32);
+ QImage testImage(calendarSize, QImage::Format_ARGB32_Premultiplied);
calendar->render(&testImage);
#ifdef RENDER_DEBUG
testImage.save("calendarWithoutNavigationBar.png");
@@ -7625,7 +8272,7 @@ void tst_QWidget::renderInvisible()
}
{ // Make sure the navigation bar renders correctly even though it's hidden.
- QImage testImage(navigationBar->size(), QImage::Format_ARGB32);
+ QImage testImage(navigationBar->size(), QImage::Format_ARGB32_Premultiplied);
navigationBar->render(&testImage);
#ifdef RENDER_DEBUG
testImage.save("explicitlyHiddenNavigationBar.png");
@@ -7639,7 +8286,7 @@ void tst_QWidget::renderInvisible()
{ // Render next month button.
// Fill test image with correct background color.
- QImage testImage(nextMonthButton->size(), QImage::Format_ARGB32);
+ QImage testImage(nextMonthButton->size(), QImage::Format_ARGB32_Premultiplied);
navigationBar->render(&testImage, QPoint(), QRegion(), QWidget::RenderFlags());
#ifdef RENDER_DEBUG
testImage.save("nextMonthButtonBackground.png");
@@ -7677,7 +8324,7 @@ void tst_QWidget::renderInvisible()
QCoreApplication::processEvents();
{ // Make sure we get an image equal to the resized reference image.
- QImage testImage(calendarSizeResized, QImage::Format_ARGB32);
+ QImage testImage(calendarSizeResized, QImage::Format_ARGB32_Premultiplied);
calendar->render(&testImage);
#ifdef RENDER_DEBUG
testImage.save("calendarResized.png");
@@ -7689,7 +8336,7 @@ void tst_QWidget::renderInvisible()
QCalendarWidget calendar;
const QSize calendarSize = calendar.sizeHint();
- QImage image(2 * calendarSize, QImage::Format_ARGB32);
+ QImage image(2 * calendarSize, QImage::Format_ARGB32_Premultiplied);
image.fill(QColor(Qt::red).rgb());
calendar.render(&image);
@@ -8493,6 +9140,7 @@ void tst_QWidget::updateWhileMinimized()
QSKIP("Platform does not support showMinimized()");
#endif
UpdateWidget widget;
+ widget.setPalette(simplePalette());
widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
// Filter out activation change and focus events to avoid update() calls in QWidget.
widget.updateOnActivationChangeAndFocusIn = false;
@@ -9066,6 +9714,7 @@ void tst_QWidget::doubleRepaint()
QSKIP("Not having window server access causes the wrong number of repaints to be issues");
#endif
UpdateWidget widget;
+ widget.setPalette(simplePalette());
widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
centerOnScreen(&widget);
widget.setFocusPolicy(Qt::StrongFocus);
@@ -9097,9 +9746,10 @@ void tst_QWidget::resizeInPaintEvent()
QWidget window;
window.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
UpdateWidget widget(&window);
+ widget.setPalette(simplePalette());
window.resize(200, 200);
window.show();
- QApplication::setActiveWindow(&window);
+ QApplicationPrivate::setActiveWindow(&window);
QVERIFY(QTest::qWaitForWindowExposed(&window));
QTRY_VERIFY(widget.numPaintEvents > 0);
@@ -9155,6 +9805,9 @@ void tst_QWidget::opaqueChildren()
void tst_QWidget::dumpObjectTree()
{
+ if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
+ QSKIP("QWindow::requestActivate() is not supported.");
+
QWidget w;
w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
Q_SET_OBJECT_NAME(w);
@@ -9177,7 +9830,7 @@ void tst_QWidget::dumpObjectTree()
}
QTestPrivate::androidCompatibleShow(&w);
- QApplication::setActiveWindow(&w);
+ QApplicationPrivate::setActiveWindow(&w);
QVERIFY(QTest::qWaitForWindowActive(&w));
{
@@ -9225,6 +9878,7 @@ public slots:
void tst_QWidget::setMaskInResizeEvent()
{
UpdateWidget w;
+ w.setPalette(simplePalette());
w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
w.reset();
w.resize(200, 200);
@@ -9304,6 +9958,7 @@ void tst_QWidget::immediateRepaintAfterInvalidateBackingStore()
QSKIP("We don't support immediate repaint right after show on other platforms.");
QScopedPointer<UpdateWidget> widget(new UpdateWidget);
+ widget->setPalette(simplePalette());
widget->setWindowTitle(QLatin1String(QTest::currentTestFunction()));
centerOnScreen(widget.data());
widget->show();
@@ -9755,11 +10410,12 @@ void tst_QWidget::setClearAndResizeMask()
QSKIP("Wayland: This fails. Figure out why.");
UpdateWidget topLevel;
+ topLevel.setPalette(simplePalette());
topLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
topLevel.resize(160, 160);
centerOnScreen(&topLevel);
topLevel.show();
- QApplication::setActiveWindow(&topLevel);
+ QApplicationPrivate::setActiveWindow(&topLevel);
QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
QTRY_VERIFY(topLevel.numPaintEvents > 0);
topLevel.reset();
@@ -9789,6 +10445,7 @@ void tst_QWidget::setClearAndResizeMask()
}
UpdateWidget child(&topLevel);
+ child.setPalette(simplePalette());
child.setAutoFillBackground(true); // NB! Opaque child.
child.setPalette(Qt::red);
child.resize(100, 100);
@@ -10182,6 +10839,9 @@ void tst_QWidget::enterLeaveOnWindowShowHide_data()
*/
void tst_QWidget::enterLeaveOnWindowShowHide()
{
+ if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
+ QSKIP("QWindow::requestActivate() is not supported.");
+
QFETCH(Qt::WindowType, windowType);
class Widget : public QWidget
{
@@ -10251,7 +10911,7 @@ void tst_QWidget::enterLeaveOnWindowShowHide()
if (!QTest::qWaitFor([&]{ return widget.geometry().contains(QCursor::pos()); }))
QSKIP("We can't move the cursor");
widget.show();
- QApplication::setActiveWindow(&widget);
+ QApplicationPrivate::setActiveWindow(&widget);
QVERIFY(QTest::qWaitForWindowActive(&widget));
++expectedEnter;
@@ -10381,6 +11041,106 @@ void tst_QWidget::taskQTBUG_4055_sendSyntheticEnterLeave()
QTRY_COMPARE(child.numEnterEvents, 1);
QCOMPARE(child.numMouseMoveEvents, 0);
}
+
+void tst_QWidget::hoverPosition()
+{
+ if (m_platform == QStringLiteral("wayland"))
+ QSKIP("Wayland: Clients can't set cursor position on wayland.");
+
+ class HoverWidget : public QWidget
+ {
+ public:
+ HoverWidget(QWidget *parent = nullptr) : QWidget(parent) {
+ setMouseTracking(true);
+ setAttribute(Qt::WA_Hover);
+ }
+ bool event(QEvent *ev) override {
+ switch (ev->type()) {
+ case QEvent::HoverMove:
+ // The docs say that WA_Hover will cause a paint event on enter and leave, but not on move.
+ update();
+ Q_FALLTHROUGH();
+ case QEvent::HoverEnter:
+ case QEvent::HoverLeave: {
+ qCDebug(lcTests) << ev;
+ lastHoverType = ev->type();
+ ++hoverEventCount;
+ QHoverEvent *hov = static_cast<QHoverEvent *>(ev);
+ mousePos = hov->position().toPoint();
+ mouseScenePos = hov->scenePosition().toPoint();
+ if (ev->type() == QEvent::HoverEnter)
+ mouseEnterScenePos = hov->scenePosition().toPoint();
+ break;
+ }
+ default:
+ break;
+ }
+ return QWidget::event(ev);
+ }
+ void paintEvent(QPaintEvent *) override {
+ ++paintEventCount;
+ QPainter painter(this);
+ if (mousePos.x() > 0)
+ painter.setPen(Qt::red);
+ painter.drawRect(0, 0, width(), height());
+ painter.setPen(Qt::darkGreen);
+ painter.drawLine(mousePos - QPoint(crossHalfWidth, 0), mousePos + QPoint(crossHalfWidth, 0));
+ painter.drawLine(mousePos - QPoint(0, crossHalfWidth), mousePos + QPoint(0, crossHalfWidth));
+ }
+
+ QEvent::Type lastHoverType = QEvent::None;
+ int hoverEventCount = 0;
+ int paintEventCount = 0;
+ QPoint mousePos;
+ QPoint mouseScenePos;
+ QPoint mouseEnterScenePos;
+
+ private:
+ const int crossHalfWidth = 5;
+ };
+
+ QCursor::setPos(m_safeCursorPos);
+ if (!QTest::qWaitFor([this]{ return QCursor::pos() == m_safeCursorPos; }))
+ QSKIP("Can't move cursor");
+
+ QWidget root;
+ root.resize(300, 300);
+ HoverWidget h(&root);
+ h.setGeometry(100, 100, 100, 100);
+ root.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&root));
+
+ const QPoint middle(50, 50);
+ QPoint curpos = h.mapToGlobal(middle);
+ QCursor::setPos(curpos);
+ if (!QTest::qWaitFor([curpos]{ return QCursor::pos() == curpos; }))
+ QSKIP("Can't move cursor");
+ QTRY_COMPARE_GE(h.hoverEventCount, 1); // HoverEnter and then probably HoverMove, so usually 2
+ QTRY_COMPARE_GE(h.paintEventCount, 2);
+ const int enterHoverEventCount = h.hoverEventCount;
+ qCDebug(lcTests) << "hover enter events:" << enterHoverEventCount << "last was" << h.lastHoverType
+ << "; paint events:" << h.paintEventCount;
+ QCOMPARE(h.mousePos, middle);
+ QCOMPARE(h.mouseEnterScenePos, h.mapToParent(middle));
+ QCOMPARE(h.mouseScenePos, h.mapToParent(middle));
+ QCOMPARE(h.lastHoverType, enterHoverEventCount == 1 ? QEvent::HoverEnter : QEvent::HoverMove);
+
+ curpos += {10, 10};
+ QCursor::setPos(curpos);
+ if (!QTest::qWaitFor([curpos]{ return QCursor::pos() == curpos; }))
+ QSKIP("Can't move cursor");
+ QTRY_COMPARE(h.hoverEventCount, enterHoverEventCount + 1);
+ QCOMPARE(h.lastHoverType, QEvent::HoverMove);
+ QTRY_COMPARE_GE(h.paintEventCount, 3);
+
+ curpos += {50, 50}; // in the outer widget, but leaving the inner widget
+ QCursor::setPos(curpos);
+ if (!QTest::qWaitFor([curpos]{ return QCursor::pos() == curpos; }))
+ QSKIP("Can't move cursor");
+ QTRY_COMPARE(h.lastHoverType, QEvent::HoverLeave);
+ QCOMPARE_GE(h.hoverEventCount, enterHoverEventCount + 2);
+ QTRY_COMPARE_GE(h.paintEventCount, 4);
+}
#endif
void tst_QWidget::windowFlags()
@@ -10486,6 +11246,7 @@ void tst_QWidget::focusWidget_task254563()
void tst_QWidget::destroyBackingStore()
{
UpdateWidget w;
+ w.setPalette(simplePalette());
w.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
centerOnScreen(&w);
w.reset();
@@ -10956,36 +11717,11 @@ void tst_QWidget::focusProxy()
QCOMPARE(container2->focusOutCount, 1);
}
-void tst_QWidget::focusProxyAndInputMethods()
+void tst_QWidget::imEnabledNotImplemented()
{
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
- QSKIP("Window activation is not supported.");
- QScopedPointer<QWidget> toplevel(new QWidget(nullptr, Qt::X11BypassWindowManagerHint));
- toplevel->setWindowTitle(QLatin1String(QTest::currentTestFunction()));
- toplevel->resize(200, 200);
- toplevel->setAttribute(Qt::WA_InputMethodEnabled, true);
-
- QWidget *child = new QWidget(toplevel.data());
- child->setFocusProxy(toplevel.data());
- child->setAttribute(Qt::WA_InputMethodEnabled, true);
-
- toplevel->setFocusPolicy(Qt::WheelFocus);
- child->setFocusPolicy(Qt::WheelFocus);
-
- QVERIFY(!child->hasFocus());
- QVERIFY(!toplevel->hasFocus());
-
- toplevel->show();
- QVERIFY(QTest::qWaitForWindowExposed(toplevel.data()));
- QApplication::setActiveWindow(toplevel.data());
- QVERIFY(QTest::qWaitForWindowActive(toplevel.data()));
- QVERIFY(toplevel->hasFocus());
- QVERIFY(child->hasFocus());
- QCOMPARE(qApp->focusObject(), toplevel.data());
-}
+ QSKIP("QWindow::requestActivate() is not supported.");
-void tst_QWidget::imEnabledNotImplemented()
-{
// Check that a plain widget doesn't report that it supports IM. Only
// widgets that implements either Qt::ImEnabled, or the Qt4 backup
// solution, Qt::ImSurroundingText, should do so.
@@ -10995,7 +11731,7 @@ void tst_QWidget::imEnabledNotImplemented()
topLevel.show();
QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
- QApplication::setActiveWindow(&topLevel);
+ QApplicationPrivate::setActiveWindow(&topLevel);
QVERIFY(QTest::qWaitForWindowActive(&topLevel));
// A plain widget should return false for ImEnabled
@@ -11012,11 +11748,12 @@ void tst_QWidget::imEnabledNotImplemented()
QVERIFY(imEnabled.isValid());
QVERIFY(imEnabled.toBool());
- // ...even if it's read-only
+ // ImEnabled should be false when a lineedit is read-only since
+ // ImEnabled indicates the widget accepts input method _input_.
edit.setReadOnly(true);
imEnabled = QApplication::inputMethod()->queryFocusObject(Qt::ImEnabled, QVariant());
QVERIFY(imEnabled.isValid());
- QVERIFY(imEnabled.toBool());
+ QVERIFY(!imEnabled.toBool());
}
#ifdef QT_BUILD_INTERNAL
@@ -11375,7 +12112,7 @@ void tst_QWidget::grabMouse()
layout->addWidget(grabber);
centerOnScreen(&w);
w.show();
- QApplication::setActiveWindow(&w);
+ QApplicationPrivate::setActiveWindow(&w);
QVERIFY(QTest::qWaitForWindowActive(&w));
QStringList expectedLog;
@@ -11412,7 +12149,7 @@ void tst_QWidget::grabKeyboard()
layout->addWidget(nonGrabber);
centerOnScreen(&w);
w.show();
- QApplication::setActiveWindow(&w);
+ QApplicationPrivate::setActiveWindow(&w);
QVERIFY(QTest::qWaitForWindowActive(&w));
nonGrabber->setFocus();
grabber->grabKeyboard();
@@ -11506,6 +12243,9 @@ public:
void tst_QWidget::touchEventSynthesizedMouseEvent()
{
+ if (m_platform.startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
+ QSKIP("This test failed on Wayland. See also QTBUG-107157.");
+
{
// Simple case, we ignore the touch events, we get mouse events instead
TouchMouseWidget widget;
@@ -12214,6 +12954,7 @@ void tst_QWidget::resizeStaticContentsChildWidget_QTBUG35282()
widget.resize(200,200);
UpdateWidget childWidget(&widget);
+ childWidget.setPalette(simplePalette());
childWidget.setAttribute(Qt::WA_StaticContents);
childWidget.setAttribute(Qt::WA_OpaquePaintEvent);
childWidget.setGeometry(250, 250, 500, 500);
@@ -12312,60 +13053,54 @@ void tst_QWidget::testForOutsideWSRangeFlag()
}
}
-class TabletWidget : public QWidget
+void tst_QWidget::tabletTracking()
{
-public:
- TabletWidget(QWidget *parent) : QWidget(parent) { }
+ class TabletWidget : public QWidget
+ {
+ public:
+ using QWidget::QWidget;
- int tabletEventCount = 0;
- int pressEventCount = 0;
- int moveEventCount = 0;
- int releaseEventCount = 0;
- int trackingChangeEventCount = 0;
- qint64 uid = -1;
+ int tabletEventCount = 0;
+ int pressEventCount = 0;
+ int moveEventCount = 0;
+ int releaseEventCount = 0;
+ int trackingChangeEventCount = 0;
+ qint64 uid = -1;
-protected:
- void tabletEvent(QTabletEvent *event) override {
- ++tabletEventCount;
- uid = event->pointingDevice()->uniqueId().numericId();
- switch (event->type()) {
- case QEvent::TabletMove:
- ++moveEventCount;
- break;
- case QEvent::TabletPress:
- ++pressEventCount;
- break;
- case QEvent::TabletRelease:
- ++releaseEventCount;
- break;
- default:
- break;
+ protected:
+ void tabletEvent(QTabletEvent *event) override {
+ ++tabletEventCount;
+ uid = event->pointingDevice()->uniqueId().numericId();
+ switch (event->type()) {
+ case QEvent::TabletMove:
+ ++moveEventCount;
+ break;
+ case QEvent::TabletPress:
+ ++pressEventCount;
+ break;
+ case QEvent::TabletRelease:
+ ++releaseEventCount;
+ break;
+ default:
+ break;
+ }
}
- }
-
- bool event(QEvent *ev) override {
- if (ev->type() == QEvent::TabletTrackingChange)
- ++trackingChangeEventCount;
- return QWidget::event(ev);
- }
-};
-void tst_QWidget::tabletTracking()
-{
- QWidget parent;
- parent.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
- parent.resize(200,200);
- // QWidgetWindow::handleTabletEvent doesn't deliver tablet events to the window's widget, only to a child.
- // So it doesn't do any good to show a TabletWidget directly: it needs a parent.
- TabletWidget widget(&parent);
+ bool event(QEvent *ev) override {
+ if (ev->type() == QEvent::TabletTrackingChange)
+ ++trackingChangeEventCount;
+ return QWidget::event(ev);
+ }
+ } widget;
+ widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
widget.resize(200,200);
- parent.showNormal();
- QVERIFY(QTest::qWaitForWindowExposed(&parent));
+ widget.showNormal();
+ QVERIFY(QTest::qWaitForWindowExposed(&widget));
widget.setAttribute(Qt::WA_TabletTracking);
QTRY_COMPARE(widget.trackingChangeEventCount, 1);
QVERIFY(widget.hasTabletTracking());
- QWindow *window = parent.windowHandle();
+ QWindow *window = widget.windowHandle();
QPointF local(10, 10);
QPointF global = window->mapToGlobal(local.toPoint());
QPointF deviceLocal = QHighDpi::toNativeLocalPosition(local, window);
@@ -12687,7 +13422,7 @@ void tst_QWidget::deleteWindowInCloseEvent()
QApplication::exec();
// It should still result in a single lastWindowClosed emit
- QCOMPARE(quitSpy.count(), 1);
+ QCOMPARE(quitSpy.size(), 1);
}
/*!
@@ -12708,7 +13443,7 @@ void tst_QWidget::quitOnClose()
widget->close();
});
QApplication::exec();
- QCOMPARE(quitSpy.count(), 1);
+ QCOMPARE(quitSpy.size(), 1);
widget->show();
QVERIFY(QTest::qWaitForWindowExposed(widget.get()));
@@ -12716,7 +13451,7 @@ void tst_QWidget::quitOnClose()
widget.reset();
});
QApplication::exec();
- QCOMPARE(quitSpy.count(), 2);
+ QCOMPARE(quitSpy.size(), 2);
}
void tst_QWidget::setParentChangesFocus_data()
@@ -12776,20 +13511,24 @@ void tst_QWidget::setParentChangesFocus()
QCOMPARE(secondary->focusWidget()->objectName(), focusWidget);
}
secondary->show();
- QApplication::setActiveWindow(secondary.get());
+ QApplicationPrivate::setActiveWindow(secondary.get());
QVERIFY(QTest::qWaitForWindowActive(secondary.get()));
if (!reparentBeforeShow) {
secondary->setParent(targetParent ? &window : nullptr, targetType);
secondary->show(); // reparenting hides, so show again
- QApplication::setActiveWindow(secondary.get());
+ QApplicationPrivate::setActiveWindow(secondary.get());
QVERIFY(QTest::qWaitForWindowActive(secondary.get()));
}
+ QVERIFY(QTest::qWaitFor([]{ return QApplication::focusWidget(); }));
QCOMPARE(QApplication::focusWidget()->objectName(), focusWidget);
}
void tst_QWidget::activateWhileModalHidden()
{
+ if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))
+ QSKIP("QWindow::requestActivate() is not supported.");
+
QDialog dialog;
dialog.setWindowModality(Qt::ApplicationModal);
dialog.show();
@@ -12807,6 +13546,35 @@ void tst_QWidget::activateWhileModalHidden()
QCOMPARE(QApplication::activeWindow(), &window);
}
+// Create a simple palette to prevent multiple paint events
+QPalette tst_QWidget::simplePalette()
+{
+ static QPalette simplePalette = []{
+ const QColor windowText = Qt::black;
+ const QColor backGround = QColor(239, 239, 239);
+ const QColor light = backGround.lighter(150);
+ const QColor mid = (backGround.darker(130));
+ const QColor midLight = mid.lighter(110);
+ const QColor base = Qt::white;
+ const QColor dark = backGround.darker(150);
+ const QColor text = Qt::black;
+ const QColor highlight = QColor(48, 140, 198);
+ const QColor hightlightedText = Qt::white;
+ const QColor button = backGround;
+ const QColor shadow = dark.darker(135);
+
+ QPalette defaultPalette(windowText, backGround, light, dark, mid, text, base);
+ defaultPalette.setBrush(QPalette::Midlight, midLight);
+ defaultPalette.setBrush(QPalette::Button, button);
+ defaultPalette.setBrush(QPalette::Shadow, shadow);
+ defaultPalette.setBrush(QPalette::HighlightedText, hightlightedText);
+ defaultPalette.setBrush(QPalette::Active, QPalette::Highlight, highlight);
+ return defaultPalette;
+ }();
+
+ return simplePalette;
+}
+
#ifdef Q_OS_ANDROID
void tst_QWidget::showFullscreenAndroid()
{
@@ -12840,5 +13608,402 @@ void tst_QWidget::showFullscreenAndroid()
}
#endif // Q_OS_ANDROID
+void tst_QWidget::setVisibleDuringDestruction()
+{
+ CreateDestroyWidget widget;
+ widget.create();
+ QVERIFY(widget.windowHandle());
+
+ QSignalSpy signalSpy(widget.windowHandle(), &QWindow::visibleChanged);
+ EventSpy<QWindow> showEventSpy(widget.windowHandle(), QEvent::Show);
+ widget.show();
+ QTRY_COMPARE(showEventSpy.count(), 1);
+ QTRY_COMPARE(signalSpy.count(), 1);
+
+ EventSpy<QWindow> hideEventSpy(widget.windowHandle(), QEvent::Hide);
+ widget.hide();
+ QTRY_COMPARE(hideEventSpy.count(), 1);
+ QTRY_COMPARE(signalSpy.count(), 2);
+
+ widget.show();
+ QTRY_COMPARE(showEventSpy.count(), 2);
+ QTRY_COMPARE(signalSpy.count(), 3);
+
+ widget.destroy();
+ QTRY_COMPARE(hideEventSpy.count(), 2);
+ QTRY_COMPARE(signalSpy.count(), 4);
+}
+
+void tst_QWidget::explicitShowHide()
+{
+ {
+ QWidget parent;
+ parent.setObjectName("Parent");
+ QWidget child(&parent);
+ child.setObjectName("Child");
+
+ QCOMPARE(parent.testAttribute(Qt::WA_WState_ExplicitShowHide), false);
+ QCOMPARE(parent.testAttribute(Qt::WA_WState_Hidden), true);
+ QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false);
+ QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false);
+
+ parent.show();
+ QCOMPARE(parent.testAttribute(Qt::WA_WState_ExplicitShowHide), true);
+ QCOMPARE(parent.testAttribute(Qt::WA_WState_Hidden), false);
+ QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false);
+ QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false);
+
+ // Fix up earlier expected failure
+ child.setAttribute(Qt::WA_WState_ExplicitShowHide, false);
+
+ parent.hide();
+ QCOMPARE(parent.testAttribute(Qt::WA_WState_ExplicitShowHide), true);
+ QCOMPARE(parent.testAttribute(Qt::WA_WState_Hidden), true);
+ QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false);
+ QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false);
+ }
+
+ {
+ // Test what happens when a child is reparented after showing it
+
+ QWidget parent;
+ parent.setObjectName("Parent");
+ QWidget child;
+ child.setObjectName("Child");
+
+ child.show();
+ QCOMPARE(child.isVisible(), true);
+ QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), true);
+ QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false);
+
+ child.setParent(&parent);
+ // As documented, a widget becomes invisible as part of changing
+ // its parent, even if it was previously visible. The user must call
+ // show() to make the widget visible again.
+ QCOMPARE(child.isVisible(), false);
+
+ // However, the widget does not end up with Qt::WA_WState_Hidden,
+ // as QWidget::setParent treats it as a child, which normally will
+ // not get Qt::WA_WState_Hidden out of the box.
+ QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false);
+
+ // For some reason we reset WA_WState_ExplicitShowHide, and it's
+ // not clear whether this is correct or not See QWidget::setParent()
+ // for a comment with more details.
+ QEXPECT_FAIL("", "We reset WA_WState_ExplicitShowHide on widget re-parent", Continue);
+ QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), true);
+
+ // The fact that the child doesn't have Qt::WA_WState_Hidden means
+ // it's sufficient to show the parent widget. We don't need to
+ // explicitly show the child.
+ parent.show();
+ QCOMPARE(child.isVisible(), true);
+ QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false);
+ }
+
+ {
+ QWidget parent;
+ parent.setObjectName("Parent");
+ QWidget child(&parent);
+ child.setObjectName("Child");
+
+ parent.show();
+
+ // If a non-native child ends up being closed, we will hide the
+ // widget, but do so via QWidget::hide(), which marks the widget
+ // as explicitly hidden.
+
+ child.setAttribute(Qt::WA_WState_ExplicitShowHide, false);
+ QCOMPARE(child.close(), true);
+ QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), true);
+ QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), true);
+
+ child.show();
+ child.setAttribute(Qt::WA_NativeWindow);
+ child.setAttribute(Qt::WA_WState_ExplicitShowHide, false);
+ QCOMPARE(child.close(), true);
+ QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), true);
+ QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), true);
+
+ child.show();
+ child.setAttribute(Qt::WA_WState_ExplicitShowHide, false);
+ QCOMPARE(child.windowHandle()->close(), false); // Can't close non-top level QWindows
+ QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false);
+ QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false);
+
+ // If we end up in QWidgetPrivate::handleClose via QWidgetWindow::closeEvent,
+ // either through QWindow::close(), or via QWSI::handleCloseEvent, we'll still
+ // do the explicit hide.
+
+ child.show();
+ child.setAttribute(Qt::WA_WState_ExplicitShowHide, false);
+ QCOMPARE(QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(
+ child.windowHandle()), true);
+ QEXPECT_FAIL("", "Closing a native child via QWSI is treated as an explicit hide", Continue);
+ QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false);
+ QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), true);
+ }
+
+ {
+ QWidget widget;
+ widget.show();
+ widget.hide();
+
+ QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), false);
+ QCOMPARE(widget.testAttribute(Qt::WA_WState_ExplicitShowHide), true);
+ QCOMPARE(widget.testAttribute(Qt::WA_WState_Hidden), true);
+
+ // The widget is now explicitly hidden. Showing it again, via QWindow,
+ // should make the widget visible, and it should not stay hidden, as
+ // that's an invalid state for a widget.
+
+ widget.windowHandle()->setVisible(true);
+ QCOMPARE(widget.testAttribute(Qt::WA_WState_Visible), true);
+ QCOMPARE(widget.testAttribute(Qt::WA_WState_ExplicitShowHide), true);
+ QCOMPARE(widget.testAttribute(Qt::WA_WState_Hidden), false);
+ }
+}
+
+/*!
+ Verify that we deliver DragEnter/Leave events symmetrically, even if the
+ widget entered didn't accept the DragEnter event.
+*/
+void tst_QWidget::dragEnterLeaveSymmetry()
+{
+ QWidget widget;
+ widget.setAcceptDrops(true);
+ QLineEdit lineEdit;
+ QLabel label("Hello world");
+ label.setAcceptDrops(true);
+
+ struct EventFilter : QObject
+ {
+ bool eventFilter(QObject *receiver, QEvent *event) override
+ {
+ switch (event->type()) {
+ case QEvent::DragEnter:
+ case QEvent::DragLeave:
+ receivers[event->type()] << receiver;
+ break;
+
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+ QMap<QEvent::Type, QList<QObject *>> receivers;
+
+ void clear() { receivers.clear(); }
+ bool hasEntered(QWidget *widget) const
+ {
+ return receivers.value(QEvent::DragEnter).contains(widget);
+ }
+ bool hasLeft(QWidget *widget) const
+ {
+ return receivers.value(QEvent::DragLeave).contains(widget);
+ }
+ } filter;
+
+ widget.installEventFilter(&filter);
+ lineEdit.installEventFilter(&filter);
+ label.installEventFilter(&filter);
+
+ QVBoxLayout vbox;
+ vbox.setContentsMargins(10, 10, 10, 10);
+ vbox.addWidget(&lineEdit);
+ vbox.addWidget(&label);
+ widget.setLayout(&vbox);
+
+ widget.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&widget));
+
+ QMimeData data;
+ data.setColorData(QVariant::fromValue(Qt::red));
+ QWindowSystemInterface::handleDrag(widget.windowHandle(), &data, QPoint(1, 1),
+ Qt::ActionMask, Qt::LeftButton, {});
+ QVERIFY(filter.hasEntered(&widget));
+ QVERIFY(!filter.hasEntered(&lineEdit));
+ QVERIFY(!filter.hasEntered(&label));
+ QVERIFY(widget.underMouse());
+ QVERIFY(!lineEdit.underMouse());
+ filter.clear();
+
+ QWindowSystemInterface::handleDrag(widget.windowHandle(), &data, lineEdit.geometry().center(),
+ Qt::ActionMask, Qt::LeftButton, {});
+ // DragEnter propagates as the lineEdit doesn't want it, so the widget
+ // sees both a Leave and an Enter event
+ QVERIFY(filter.hasLeft(&widget));
+ QVERIFY(filter.hasEntered(&widget));
+ QVERIFY(filter.hasEntered(&widget));
+ // both have the UnderMouse attribute set
+ QVERIFY(lineEdit.underMouse());
+ QVERIFY(widget.underMouse());
+
+ // The lineEdit didn't accept the DragEnter, but it should still has to
+ // get the DragLeave so that UnderMouse is cleared; the widget gets both
+ // Leave and Enter through propagation.
+ QWindowSystemInterface::handleDrag(widget.windowHandle(), &data, label.geometry().center(),
+ Qt::ActionMask, Qt::LeftButton, {});
+ QVERIFY(filter.hasLeft(&lineEdit));
+ QVERIFY(filter.hasLeft(&widget));
+ QVERIFY(filter.hasEntered(&label));
+ QVERIFY(filter.hasEntered(&widget));
+
+ QVERIFY(!lineEdit.underMouse());
+ QVERIFY(label.underMouse());
+ QVERIFY(widget.underMouse());
+}
+
+void tst_QWidget::reparentWindowHandles_data()
+{
+ QTest::addColumn<int>("stage");
+ QTest::addRow("reparent child") << 1;
+ QTest::addRow("top level to child") << 2;
+ QTest::addRow("transient parent") << 3;
+ QTest::addRow("window container") << 4;
+}
+
+void tst_QWidget::reparentWindowHandles()
+{
+ const bool nativeSiblingsOriginal = qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
+ qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true);
+ auto nativeSiblingGuard = qScopeGuard([&]{
+ qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, nativeSiblingsOriginal);
+ });
+
+ QFETCH(int, stage);
+
+ switch (stage) {
+ case 1: {
+ // Reparent child widget
+
+ QWidget topLevel;
+ topLevel.setAttribute(Qt::WA_NativeWindow);
+ QVERIFY(topLevel.windowHandle());
+ QPointer<QWidget> child = new QWidget(&topLevel);
+ child->setAttribute(Qt::WA_DontCreateNativeAncestors);
+ child->setAttribute(Qt::WA_NativeWindow);
+ QVERIFY(child->windowHandle());
+
+ QWidget anotherTopLevel;
+ anotherTopLevel.setAttribute(Qt::WA_NativeWindow);
+ QVERIFY(anotherTopLevel.windowHandle());
+ QPointer<QWidget> intermediate = new QWidget(&anotherTopLevel);
+ QPointer<QWidget> leaf = new QWidget(intermediate);
+ leaf->setAttribute(Qt::WA_DontCreateNativeAncestors);
+ leaf->setAttribute(Qt::WA_NativeWindow);
+ QVERIFY(leaf->windowHandle());
+ QVERIFY(!intermediate->windowHandle());
+
+ // Reparenting a native widget should reparent the QWindow
+ child->setParent(leaf);
+ QCOMPARE(child->windowHandle()->parent(), leaf->windowHandle());
+ QCOMPARE(child->windowHandle()->transientParent(), nullptr);
+ QVERIFY(!intermediate->windowHandle());
+
+ // So should reparenting a non-native widget with native children
+ intermediate->setParent(&topLevel);
+ QVERIFY(!intermediate->windowHandle());
+ QCOMPARE(leaf->windowHandle()->parent(), topLevel.windowHandle());
+ QCOMPARE(leaf->windowHandle()->transientParent(), nullptr);
+ QCOMPARE(child->windowHandle()->parent(), leaf->windowHandle());
+ QCOMPARE(child->windowHandle()->transientParent(), nullptr);
+ }
+ break;
+ case 2: {
+ // Top level to child
+
+ QWidget topLevel;
+ topLevel.setAttribute(Qt::WA_NativeWindow);
+
+ // A regular top level loses its nativeness
+ QPointer<QWidget> regularToplevel = new QWidget;
+ regularToplevel->show();
+ QVERIFY(QTest::qWaitForWindowExposed(regularToplevel));
+ QVERIFY(regularToplevel->windowHandle());
+ regularToplevel->setParent(&topLevel);
+ QVERIFY(!regularToplevel->windowHandle());
+
+ // A regular top level loses its nativeness
+ QPointer<QWidget> regularToplevelWithNativeChildren = new QWidget;
+ QPointer<QWidget> nativeChild = new QWidget(regularToplevelWithNativeChildren);
+ nativeChild->setAttribute(Qt::WA_DontCreateNativeAncestors);
+ nativeChild->setAttribute(Qt::WA_NativeWindow);
+ QVERIFY(nativeChild->windowHandle());
+ regularToplevelWithNativeChildren->show();
+ QVERIFY(QTest::qWaitForWindowExposed(regularToplevelWithNativeChildren));
+ QVERIFY(regularToplevelWithNativeChildren->windowHandle());
+ regularToplevelWithNativeChildren->setParent(&topLevel);
+ QVERIFY(!regularToplevelWithNativeChildren->windowHandle());
+ // But the native child does not
+ QVERIFY(nativeChild->windowHandle());
+ QCOMPARE(nativeChild->windowHandle()->parent(), topLevel.windowHandle());
+
+ // An explicitly native top level keeps its nativeness, and the window handle moves
+ QPointer<QWidget> nativeTopLevel = new QWidget;
+ nativeTopLevel->setAttribute(Qt::WA_NativeWindow);
+ QVERIFY(nativeTopLevel->windowHandle());
+ nativeTopLevel->setParent(&topLevel);
+ QVERIFY(nativeTopLevel->windowHandle());
+ QCOMPARE(nativeTopLevel->windowHandle()->parent(), topLevel.windowHandle());
+ }
+ break;
+ case 3: {
+ // Transient parent
+
+ QWidget topLevel;
+ topLevel.setAttribute(Qt::WA_NativeWindow);
+ QVERIFY(topLevel.windowHandle());
+ QPointer<QWidget> child = new QWidget(&topLevel);
+ child->setAttribute(Qt::WA_NativeWindow);
+ QVERIFY(child->windowHandle());
+
+ QWidget anotherTopLevel;
+ anotherTopLevel.setAttribute(Qt::WA_NativeWindow);
+ QVERIFY(anotherTopLevel.windowHandle());
+
+ // Make transient child of top level
+ anotherTopLevel.setParent(&topLevel, Qt::Window);
+ QCOMPARE(anotherTopLevel.windowHandle()->parent(), nullptr);
+ QCOMPARE(anotherTopLevel.windowHandle()->transientParent(), topLevel.windowHandle());
+
+ // Make transient child of child
+ anotherTopLevel.setParent(child, Qt::Window);
+ QCOMPARE(anotherTopLevel.windowHandle()->parent(), nullptr);
+ QCOMPARE(anotherTopLevel.windowHandle()->transientParent(), topLevel.windowHandle());
+ }
+ break;
+ case 4: {
+ // Window container
+
+ QWidget topLevel;
+ topLevel.setAttribute(Qt::WA_NativeWindow);
+ QVERIFY(topLevel.windowHandle());
+
+ QPointer<QWidget> child = new QWidget(&topLevel);
+ QVERIFY(!child->windowHandle());
+
+ QWindow *window = new QWindow;
+ QWidget *container = QWidget::createWindowContainer(window);
+ container->setParent(child);
+ topLevel.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
+ QCOMPARE(window->parent(), topLevel.windowHandle());
+
+ QWidget anotherTopLevel;
+ anotherTopLevel.setAttribute(Qt::WA_NativeWindow);
+ QVERIFY(anotherTopLevel.windowHandle());
+
+ child->setParent(&anotherTopLevel);
+ QCOMPARE(window->parent(), anotherTopLevel.windowHandle());
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+}
+
QTEST_MAIN(tst_QWidget)
#include "tst_qwidget.moc"