diff options
Diffstat (limited to 'tests/auto/widgets')
395 files changed, 19652 insertions, 8856 deletions
diff --git a/tests/auto/widgets/CMakeLists.txt b/tests/auto/widgets/CMakeLists.txt index 5870f28db0..5ffacc9f18 100644 --- a/tests/auto/widgets/CMakeLists.txt +++ b/tests/auto/widgets/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from widgets.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause add_subdirectory(dialogs) add_subdirectory(effects) diff --git a/tests/auto/widgets/dialogs/CMakeLists.txt b/tests/auto/widgets/dialogs/CMakeLists.txt index c21c65dbe3..f46950b6bf 100644 --- a/tests/auto/widgets/dialogs/CMakeLists.txt +++ b/tests/auto/widgets/dialogs/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from dialogs.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause add_subdirectory(qcolordialog) add_subdirectory(qdialog) @@ -8,9 +9,9 @@ add_subdirectory(qfontdialog) add_subdirectory(qinputdialog) add_subdirectory(qprogressdialog) add_subdirectory(qwizard) -# QTBUG-87671 # special case +add_subdirectory(qfiledialog) +# QTBUG-101217, qmessagebox hangs on Android if(NOT ANDROID) - add_subdirectory(qfiledialog) add_subdirectory(qmessagebox) endif() if(QT_FEATURE_private_tests) diff --git a/tests/auto/widgets/dialogs/qcolordialog/CMakeLists.txt b/tests/auto/widgets/dialogs/qcolordialog/CMakeLists.txt index a25c02ad92..2cba18c0f0 100644 --- a/tests/auto/widgets/dialogs/qcolordialog/CMakeLists.txt +++ b/tests/auto/widgets/dialogs/qcolordialog/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qcolordialog.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qcolordialog Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcolordialog LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qcolordialog SOURCES tst_qcolordialog.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/dialogs/qcolordialog/tst_qcolordialog.cpp b/tests/auto/widgets/dialogs/qcolordialog/tst_qcolordialog.cpp index b3aeaa81f8..5ae8eaf30d 100644 --- a/tests/auto/widgets/dialogs/qcolordialog/tst_qcolordialog.cpp +++ b/tests/auto/widgets/dialogs/qcolordialog/tst_qcolordialog.cpp @@ -1,35 +1,12 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QtGui/QtGui> #include <QtWidgets/QColorDialog> +#include <QtWidgets/QLineEdit> +#include <QSignalSpy> QT_FORWARD_DECLARE_CLASS(QtTestEventThread) @@ -50,6 +27,10 @@ private slots: void native_activeModalWidget(); void task247349_alpha(); void QTBUG_43548_initialColor(); + void hexColor_data(); + void hexColor(); + + void hideNativeByDestruction(); }; class TestNativeDialog : public QColorDialog @@ -102,7 +83,7 @@ void tst_QColorDialog::native_activeModalWidget() void tst_QColorDialog::postKeyReturn() { QWidgetList list = QApplication::topLevelWidgets(); - for (int i=0; i<list.count(); ++i) { + for (int i=0; i<list.size(); ++i) { QColorDialog *dialog = qobject_cast<QColorDialog *>(list[i]); if (dialog) { QTest::keyClick( list[i], Qt::Key_Return, Qt::NoModifier ); @@ -151,5 +132,81 @@ void tst_QColorDialog::QTBUG_43548_initialColor() QCOMPARE(a, dialog.currentColor()); } +void tst_QColorDialog::hexColor_data() +{ + QTest::addColumn<const QString>("colorString"); + QTest::addColumn<const QString>("expectedHexColor"); + QTest::addColumn<const int>("expectedSignalCount"); + + QTest::newRow("White-#") << "#FFFFFE" << "#FFFFFE" << 1; + QTest::newRow("White") << "FFFFFD" << "#FFFFFD" << 1; + QTest::newRow("Blue-#") << "#77fffb" << "#77fffb" << 2; + QTest::newRow("Blue") << "77fffa" << "#77fffa" << 2; +} + +void tst_QColorDialog::hexColor() +{ + QColorDialog dialog; + dialog.setOption(QColorDialog::DontUseNativeDialog); + dialog.show(); + QVERIFY(QTest::qWaitForWindowExposed(&dialog)); + + QLineEdit *lineEdit = dialog.findChild<QLineEdit *>("qt_colorname_lineedit", + Qt::FindChildrenRecursively); + QVERIFY2(lineEdit, "QLineEdit for color not found. Adapt this test."); + QVERIFY(lineEdit); // eliminate compiler warning + + QFETCH(const QString, colorString); + QFETCH(const QString, expectedHexColor); + QFETCH(const int, expectedSignalCount); + + QSignalSpy spy(&dialog, &QColorDialog::currentColorChanged); + + // Delete existing color + lineEdit->activateWindow(); + QVERIFY(QTest::qWaitForWindowActive(lineEdit)); + for (int i = 0; i < 8; ++i) + QTest::keyEvent(QTest::KeyAction::Click, lineEdit, Qt::Key_Backspace); + QVERIFY(lineEdit->text().isEmpty()); + + // Enter new color + for (const QChar &key : colorString) + QTest::keyEvent(QTest::KeyAction::Click, lineEdit, key.toLatin1()); + QCOMPARE(lineEdit->text().toLower(), expectedHexColor.toLower()); + + // Consume all color change signals + QTRY_COMPARE(spy.count(), expectedSignalCount); + + const QColor color = qvariant_cast<QColor>(spy.last().at(0)); + QCOMPARE(color.name(QColor::HexRgb), expectedHexColor.toLower()); +} + +void tst_QColorDialog::hideNativeByDestruction() +{ + QWidget window; + QWidget *child = new QWidget(&window); + QPointer<QColorDialog> dialog = new QColorDialog(child); + // Make it application modal so that we don't end up with a sheet on macOS + dialog->setWindowModality(Qt::ApplicationModal); + window.show(); + QVERIFY(QTest::qWaitForWindowActive(&window)); + dialog->open(); + + // We test that the dialog opens and closes by watching the activation of the + // transient parent window. If it doesn't deactivate, then we have to skip. + const auto windowActive = [&window]{ return window.isActiveWindow(); }; + const auto windowInactive = [&window]{ return !window.isActiveWindow(); }; + if (!QTest::qWaitFor(windowInactive, 2000)) + QSKIP("Dialog didn't activate"); + + // This should destroy the dialog and close the native window + child->deleteLater(); + QTRY_VERIFY(!dialog); + // If the native window is still open, then the transient parent can't become + // active + window.activateWindow(); + QVERIFY(QTest::qWaitFor(windowActive)); +} + QTEST_MAIN(tst_QColorDialog) #include "tst_qcolordialog.moc" diff --git a/tests/auto/widgets/dialogs/qdialog/BLACKLIST b/tests/auto/widgets/dialogs/qdialog/BLACKLIST index 02eb64cb8b..ad86386895 100644 --- a/tests/auto/widgets/dialogs/qdialog/BLACKLIST +++ b/tests/auto/widgets/dialogs/qdialog/BLACKLIST @@ -2,6 +2,3 @@ macos [showFullScreen] macos ci -# QTBUG-87389 -[snapToDefaultButton] -android diff --git a/tests/auto/widgets/dialogs/qdialog/CMakeLists.txt b/tests/auto/widgets/dialogs/qdialog/CMakeLists.txt index 08c4222535..d69310541d 100644 --- a/tests/auto/widgets/dialogs/qdialog/CMakeLists.txt +++ b/tests/auto/widgets/dialogs/qdialog/CMakeLists.txt @@ -1,15 +1,23 @@ -# Generated from qdialog.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qdialog Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qdialog LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qdialog SOURCES tst_qdialog.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp b/tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp index 53c33255bf..13f971f5f0 100644 --- a/tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp +++ b/tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "../../../shared/highdpi.h" @@ -37,7 +12,9 @@ #include <qpushbutton.h> #include <qstyle.h> #include <QVBoxLayout> +#include <QSignalSpy> #include <QSizeGrip> +#include <QTimer> #include <QGraphicsProxyWidget> #include <QGraphicsView> #include <QWindow> @@ -45,6 +22,8 @@ #include <qpa/qplatformtheme.h> #include <qpa/qplatformtheme_p.h> +#include <QtWidgets/private/qapplication_p.h> + QT_FORWARD_DECLARE_CLASS(QDialog) // work around function being protected @@ -68,6 +47,8 @@ private slots: void showMinimized(); void showFullScreen(); void showAsTool(); + void showWithoutActivating_data(); + void showWithoutActivating(); void toolDialogPosition(); void deleteMainDefault(); void deleteInExec(); @@ -81,6 +62,10 @@ private slots: void transientParent(); void dialogInGraphicsView(); void keepPositionOnClose(); + void virtualsOnClose(); + void deleteOnDone(); + void quitOnDone(); + void focusWidgetAfterOpen(); }; // Testing get/set functions @@ -150,7 +135,7 @@ void tst_QDialog::defaultButtons() pushThree->setAutoDefault(false); testWidget.show(); - QApplication::setActiveWindow(&testWidget); + QApplicationPrivate::setActiveWindow(&testWidget); QVERIFY(QTest::qWaitForWindowExposed(&testWidget)); push->setDefault(true); @@ -299,9 +284,12 @@ void tst_QDialog::showFullScreen() void tst_QDialog::showAsTool() { -#if defined(Q_OS_UNIX) - QSKIP("Qt/X11: Skipped since activeWindow() is not respected by all window managers"); -#endif + if (QStringList{"xcb", "offscreen"}.contains(QGuiApplication::platformName())) + QSKIP("activeWindow() is not respected by all Xcb window managers and the offscreen plugin"); + + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + DummyDialog testWidget; testWidget.resize(200, 200); testWidget.setWindowTitle(QTest::currentTestFunction()); @@ -317,6 +305,33 @@ void tst_QDialog::showAsTool() } } +void tst_QDialog::showWithoutActivating_data() +{ + QTest::addColumn<bool>("showWithoutActivating"); + QTest::addColumn<int>("focusInCount"); + + QTest::addRow("showWithoutActivating") << true << 0; + QTest::addRow("showWithActivating") << false << 1; +} + +void tst_QDialog::showWithoutActivating() +{ + QFETCH(bool, showWithoutActivating); + QFETCH(int, focusInCount); + + struct Dialog : public QDialog + { + int focusInCount = 0; + protected: + void focusInEvent(QFocusEvent *) override { ++focusInCount; } + } dialog; + dialog.setAttribute(Qt::WA_ShowWithoutActivating, showWithoutActivating); + + dialog.show(); + QVERIFY(QTest::qWaitForWindowExposed(&dialog)); + QCOMPARE(dialog.focusInCount, focusInCount); +} + // Verify that pos() returns the same before and after show() // for a dialog with the Tool window type. void tst_QDialog::toolDialogPosition() @@ -469,6 +484,9 @@ void tst_QDialog::snapToDefaultButton() #else if (!QGuiApplication::platformName().compare(QLatin1String("wayland"), Qt::CaseInsensitive)) QSKIP("This platform does not support setting the cursor position."); +#ifdef Q_OS_ANDROID + QSKIP("Android does not support cursor"); +#endif const QRect dialogGeometry(QGuiApplication::primaryScreen()->availableGeometry().topLeft() + QPoint(100, 100), QSize(200, 200)); @@ -552,10 +570,11 @@ void tst_QDialog::keepPositionOnClose() dialog.setWindowTitle(QTest::currentTestFunction()); const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry(); dialog.resize(availableGeometry.size() / 4); - const QPoint pos = availableGeometry.topLeft() + QPoint(100, 100); + QPoint pos = availableGeometry.topLeft() + QPoint(100, 100); dialog.move(pos); dialog.show(); QVERIFY(QTest::qWaitForWindowExposed(&dialog)); + pos = dialog.pos(); dialog.close(); dialog.windowHandle()->destroy(); // Emulate a click on close by destroying the window. QTest::qWait(50); @@ -565,5 +584,189 @@ void tst_QDialog::keepPositionOnClose() QCOMPARE(dialog.pos(), pos); } +/*! + Verify that the virtual functions related to closing a dialog are + called exactly once, no matter how the dialog gets closed. +*/ +void tst_QDialog::virtualsOnClose() +{ + class Dialog : public QDialog + { + public: + using QDialog::QDialog; + int closeEventCount = 0; + int acceptCount = 0; + int rejectCount = 0; + int doneCount = 0; + + void accept() override + { + ++acceptCount; + QDialog::accept(); + } + void reject() override + { + ++rejectCount; + QDialog::reject(); + } + void done(int result) override + { + ++doneCount; + QDialog::done(result); + } + + protected: + void closeEvent(QCloseEvent *e) override + { + ++closeEventCount; + QDialog::closeEvent(e); + } + }; + + { + Dialog dialog; + dialog.show(); + QVERIFY(QTest::qWaitForWindowExposed(&dialog)); + dialog.accept(); + // we used to only hide the dialog, and we still don't want a + // closeEvent call for application-triggered calls to QDialog::done + QCOMPARE(dialog.closeEventCount, 0); + QCOMPARE(dialog.acceptCount, 1); + QCOMPARE(dialog.rejectCount, 0); + QCOMPARE(dialog.doneCount, 1); + } + + { + Dialog dialog; + dialog.show(); + QVERIFY(QTest::qWaitForWindowExposed(&dialog)); + dialog.reject(); + QCOMPARE(dialog.closeEventCount, 0); + QCOMPARE(dialog.acceptCount, 0); + QCOMPARE(dialog.rejectCount, 1); + QCOMPARE(dialog.doneCount, 1); + } + + { + Dialog dialog; + dialog.show(); + QVERIFY(QTest::qWaitForWindowExposed(&dialog)); + dialog.close(); + QCOMPARE(dialog.closeEventCount, 1); + QCOMPARE(dialog.acceptCount, 0); + QCOMPARE(dialog.rejectCount, 1); + QCOMPARE(dialog.doneCount, 1); + } + + { + // user clicks close button in title bar + Dialog dialog; + dialog.show(); + QVERIFY(QTest::qWaitForWindowExposed(&dialog)); + QWindowSystemInterface::handleCloseEvent(dialog.windowHandle()); + QApplication::processEvents(); + QCOMPARE(dialog.closeEventCount, 1); + QCOMPARE(dialog.acceptCount, 0); + QCOMPARE(dialog.rejectCount, 1); + QCOMPARE(dialog.doneCount, 1); + } + + { + struct EventFilter : QObject { + EventFilter(Dialog *dialog) + { dialog->installEventFilter(this); } + int closeEventCount = 0; + bool eventFilter(QObject *r, QEvent *e) override + { + if (e->type() == QEvent::Close) { + ++closeEventCount; + } + return QObject::eventFilter(r, e); + } + }; + // dialog gets destroyed while shown + Dialog *dialog = new Dialog; + QSignalSpy rejectedSpy(dialog, &QDialog::rejected); + EventFilter filter(dialog); + + dialog->show(); + QVERIFY(QTest::qWaitForWindowExposed(dialog)); + delete dialog; + // Qt doesn't deliver events to QWidgets closed during destruction + QCOMPARE(filter.closeEventCount, 0); + // QDialog doesn't emit signals when closed by destruction + QCOMPARE(rejectedSpy.size(), 0); + } +} + +/*! + QDialog::done is documented to respect Qt::WA_DeleteOnClose. +*/ +void tst_QDialog::deleteOnDone() +{ + { + std::unique_ptr<QDialog> dialog(new QDialog); + QPointer<QDialog> watcher(dialog.get()); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->show(); + QVERIFY(QTest::qWaitForWindowExposed(dialog.get())); + + dialog->accept(); + QTRY_COMPARE(watcher.isNull(), true); + dialog.release(); // if we get here, the dialog is destroyed + } + + // it is still safe to delete the dialog explicitly as long as events + // have not yet been processed + { + std::unique_ptr<QDialog> dialog(new QDialog); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->show(); + QVERIFY(QTest::qWaitForWindowExposed(dialog.get())); + + dialog->accept(); + dialog.reset(); + QApplication::processEvents(); + } +} + +/*! + QDialog::done is documented to make QApplication emit lastWindowClosed if + the dialog was the last window. +*/ +void tst_QDialog::quitOnDone() +{ + QSignalSpy quitSpy(qApp, &QGuiApplication::lastWindowClosed); + + QDialog dialog; + dialog.show(); + QVERIFY(QTest::qWaitForWindowExposed(&dialog)); + + // QGuiApplication::lastWindowClosed is documented to only be emitted + // when we are in exec() + QTimer::singleShot(0, &dialog, &QDialog::accept); + // also quit with a timer in case the test fails + QTimer::singleShot(1000, QApplication::instance(), &QApplication::quit); + QApplication::exec(); + QCOMPARE(quitSpy.size(), 1); +} + +void tst_QDialog::focusWidgetAfterOpen() +{ + QDialog dialog; + dialog.setLayout(new QVBoxLayout); + + QPushButton *pb1 = new QPushButton; + QPushButton *pb2 = new QPushButton; + dialog.layout()->addWidget(pb1); + dialog.layout()->addWidget(pb2); + + pb2->setFocus(); + QCOMPARE(dialog.focusWidget(), static_cast<QWidget *>(pb2)); + + dialog.open(); + QCOMPARE(dialog.focusWidget(), static_cast<QWidget *>(pb2)); +} + QTEST_MAIN(tst_QDialog) #include "tst_qdialog.moc" diff --git a/tests/auto/widgets/dialogs/qerrormessage/CMakeLists.txt b/tests/auto/widgets/dialogs/qerrormessage/CMakeLists.txt index 2ea2f01cdd..a26401c417 100644 --- a/tests/auto/widgets/dialogs/qerrormessage/CMakeLists.txt +++ b/tests/auto/widgets/dialogs/qerrormessage/CMakeLists.txt @@ -1,16 +1,21 @@ -# Generated from qerrormessage.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qerrormessage Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qerrormessage LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qerrormessage SOURCES tst_qerrormessage.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui + Qt::GuiPrivate Qt::Widgets ) - -#### Keys ignored in scope 1:.:.:qerrormessage.pro:<TRUE>: -# TEMPLATE = "app" diff --git a/tests/auto/widgets/dialogs/qerrormessage/tst_qerrormessage.cpp b/tests/auto/widgets/dialogs/qerrormessage/tst_qerrormessage.cpp index 711bb6fe35..53ade3cbc6 100644 --- a/tests/auto/widgets/dialogs/qerrormessage/tst_qerrormessage.cpp +++ b/tests/auto/widgets/dialogs/qerrormessage/tst_qerrormessage.cpp @@ -1,45 +1,43 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QErrorMessage> #include <QDebug> #include <QCheckBox> +#include <qpa/qplatformtheme.h> +#include <private/qguiapplication_p.h> + class tst_QErrorMessage : public QObject { Q_OBJECT private slots: + void initTestCase_data(); + void init(); + void dontShowAgain(); void dontShowCategoryAgain(); + void baseClassSetVisible(); }; +void tst_QErrorMessage::initTestCase_data() +{ + QTest::addColumn<bool>("useNativeDialog"); + QTest::newRow("widget") << false; + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) { + if (theme->usePlatformNativeDialog(QPlatformTheme::MessageDialog)) + QTest::newRow("native") << true; + } +} + +void tst_QErrorMessage::init() +{ + QFETCH_GLOBAL(bool, useNativeDialog); + qApp->setAttribute(Qt::AA_DontUseNativeDialogs, !useNativeDialog); +} + void tst_QErrorMessage::dontShowAgain() { QString plainString = QLatin1String("foo"); @@ -72,8 +70,7 @@ void tst_QErrorMessage::dontShowAgain() QVERIFY(errorMessageDialog.isVisible()); checkBox = errorMessageDialog.findChild<QCheckBox*>(); QVERIFY(checkBox); - QVERIFY(!checkBox->isChecked()); - checkBox->setChecked(true); + QVERIFY(checkBox->isChecked()); errorMessageDialog.close(); errorMessageDialog.showMessage(htmlString); @@ -141,5 +138,13 @@ void tst_QErrorMessage::dontShowCategoryAgain() QVERIFY(errorMessageDialog.isVisible()); } +void tst_QErrorMessage::baseClassSetVisible() +{ + QErrorMessage errorMessage; + errorMessage.QDialog::setVisible(true); + QCOMPARE(errorMessage.isVisible(), true); + errorMessage.close(); +} + QTEST_MAIN(tst_QErrorMessage) #include "tst_qerrormessage.moc" diff --git a/tests/auto/widgets/dialogs/qfiledialog/BLACKLIST b/tests/auto/widgets/dialogs/qfiledialog/BLACKLIST new file mode 100644 index 0000000000..38aa416115 --- /dev/null +++ b/tests/auto/widgets/dialogs/qfiledialog/BLACKLIST @@ -0,0 +1,10 @@ +[filesSelectedSignal] +android # QTBUG-101194 +[historyForward] +android # QTBUG-101194 +[historyBack] +android # QTBUG-101194 +[completer_data] +android # QTBUG-108329 +[completer] +android # QTBUG-101194 diff --git a/tests/auto/widgets/dialogs/qfiledialog/CMakeLists.txt b/tests/auto/widgets/dialogs/qfiledialog/CMakeLists.txt index 6515645c05..000d99cdcf 100644 --- a/tests/auto/widgets/dialogs/qfiledialog/CMakeLists.txt +++ b/tests/auto/widgets/dialogs/qfiledialog/CMakeLists.txt @@ -1,15 +1,20 @@ -# Generated from qfiledialog.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qfiledialog Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qfiledialog LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qfiledialog SOURCES tst_qfiledialog.cpp - DEFINES - SRCDIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/\\\" - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/dialogs/qfiledialog/tst_qfiledialog.cpp b/tests/auto/widgets/dialogs/qfiledialog/tst_qfiledialog.cpp index 2fe9f9b00d..6ebf255f31 100644 --- a/tests/auto/widgets/dialogs/qfiledialog/tst_qfiledialog.cpp +++ b/tests/auto/widgets/dialogs/qfiledialog/tst_qfiledialog.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -38,7 +13,7 @@ #include <qsharedpointer.h> #include <qfiledialog.h> #include <qabstractitemdelegate.h> -#include <qitemdelegate.h> +#include <qstyleditemdelegate.h> #include <qlistview.h> #include <qcombobox.h> #include <qpushbutton.h> @@ -63,6 +38,8 @@ #include <QFileDialog> #include <QFileSystemModel> +#include <QtWidgets/private/qapplication_p.h> + #if defined(Q_OS_UNIX) #include <unistd.h> // for pathconf() on OS X #ifdef QT_BUILD_INTERNAL @@ -139,6 +116,7 @@ private slots: void clearLineEdit(); void enableChooseButton(); void selectedFilesWithoutWidgets(); + void selectedFileWithDefaultSuffix(); void trailingDotsAndSpaces(); #ifdef Q_OS_UNIX #ifdef QT_BUILD_INTERNAL @@ -150,9 +128,10 @@ private slots: void QTBUG49600_nativeIconProviderCrash(); void focusObjectDuringDestruction(); - // NOTE: Please keep widgetlessNativeDialog() as the LAST test! + // NOTE: Please keep widgetlessNativeDialog() and + // hideNativeByDestruction() as the LAST tests! // - // widgetlessNativeDialog() is the only test function that creates + // widgetlessNativeDialog() are the only test functions that create // a native file dialog instance. GTK+ versions prior 3.15.5 have // a nasty bug (https://bugzilla.gnome.org/show_bug.cgi?id=725164) // in GtkFileChooserWidget, which makes it leak its folder change @@ -163,6 +142,7 @@ private slots: // The crash has been fixed in GTK+ 3.15.5, but the RHEL 7.2 CI has // GTK+ 3.14.13 installed (QTBUG-55276). void widgetlessNativeDialog(); + void hideNativeByDestruction(); private: void cleanupSettingsFile(); @@ -230,7 +210,7 @@ void tst_QFiledialog::currentChangedSignal() QVERIFY(listView->model()->hasChildren(folder)); listView->setCurrentIndex(folder); - QCOMPARE(spyCurrentChanged.count(), 1); + QCOMPARE(spyCurrentChanged.size(), 1); } // only emitted from the views, sidebar, or lookin combo @@ -252,7 +232,7 @@ void tst_QFiledialog::directoryEnteredSignal() QVERIFY(secondItem.isValid()); sidebar->setCurrentIndex(secondItem); QTest::keyPress(sidebar->viewport(), Qt::Key_Return); - QCOMPARE(spyDirectoryEntered.count(), 1); + QCOMPARE(spyDirectoryEntered.size(), 1); spyDirectoryEntered.clear(); // lookInCombo @@ -261,7 +241,7 @@ void tst_QFiledialog::directoryEnteredSignal() QVERIFY(comboBox->view()->model()->index(1, 0).isValid()); comboBox->view()->setCurrentIndex(comboBox->view()->model()->index(1, 0)); QTest::keyPress(comboBox->view()->viewport(), Qt::Key_Return); - QCOMPARE(spyDirectoryEntered.count(), 1); + QCOMPARE(spyDirectoryEntered.size(), 1); spyDirectoryEntered.clear(); // view @@ -303,7 +283,7 @@ void tst_QFiledialog::filesSelectedSignal() { QFileDialog fd; fd.setViewMode(QFileDialog::List); - QDir testDir(SRCDIR); + QDir testDir(QT_TESTCASE_SOURCEDIR); fd.setDirectory(testDir); QFETCH(QFileDialog::FileMode, fileMode); fd.setFileMode(fileMode); @@ -338,7 +318,7 @@ void tst_QFiledialog::filesSelectedSignal() QVERIFY(button->isEnabled()); button->animateClick(); QTRY_COMPARE(fd.isVisible(), false); - QCOMPARE(spyFilesSelected.count(), 1); + QCOMPARE(spyFilesSelected.size(), 1); } // only emitted when the combo box is activated @@ -363,7 +343,7 @@ void tst_QFiledialog::filterSelectedSignal() QTest::keyPress(filters, Qt::Key_Down); - QCOMPARE(spyFilterSelected.count(), 1); + QCOMPARE(spyFilterSelected.size(), 1); } void tst_QFiledialog::args() @@ -404,14 +384,14 @@ void tst_QFiledialog::directory() #ifndef Q_OS_WIN QCOMPARE(tempPath, fd.directory().absolutePath()); #endif - QCOMPARE(spyCurrentChanged.count(), 0); - QCOMPARE(spyDirectoryEntered.count(), 0); - QCOMPARE(spyFilesSelected.count(), 0); - QCOMPARE(spyFilterSelected.count(), 0); + QCOMPARE(spyCurrentChanged.size(), 0); + QCOMPARE(spyDirectoryEntered.size(), 0); + QCOMPARE(spyFilesSelected.size(), 0); + QCOMPARE(spyFilterSelected.size(), 0); // Check my way QList<QListView*> list = fd.findChildren<QListView*>("listView"); - QVERIFY(list.count() > 0); + QVERIFY(list.size() > 0); #ifdef Q_OS_WIN QCOMPARE(list.at(0)->rootIndex().data().toString().toLower(), temp.dirName().toLower()); #else @@ -442,7 +422,14 @@ void tst_QFiledialog::completer_data() QTest::newRow("goto root") << QString() << rootPath << -1; QTest::newRow("start at root") << rootPath << QString() << -1; - QFileInfoList list = QDir::root().entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + QDir dir = QDir::root(); +#ifdef Q_OS_ANDROID + const auto homePaths = QStandardPaths::standardLocations(QStandardPaths::HomeLocation); + QVERIFY(!homePaths.isEmpty()); + dir = QDir(homePaths.first()); +#endif + + QFileInfoList list = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); QVERIFY(!list.isEmpty()); const QString folder = list.first().absoluteFilePath(); QTest::newRow("start at one below root r") << folder << "r" << -1; @@ -529,10 +516,9 @@ void tst_QFiledialog::completer() } // press 'keys' for the input - for (int i = 0; i < input.count(); ++i) + for (int i = 0; i < input.size(); ++i) QTest::keyPress(lineEdit, input[i].toLatin1()); - QStringList expectedFiles; if (expected == -1) { QString fullPath = startPath; if (!fullPath.endsWith(QLatin1Char('/'))) @@ -545,11 +531,11 @@ void tst_QFiledialog::completer() QFileInfo fi(fullPath); QDir x(fi.absolutePath()); - expectedFiles = x.entryList(model->filter()); + const QStringList expectedFiles = x.entryList(model->filter()); expected = 0; if (input.startsWith("..")) input.clear(); - foreach (const QString &expectedFile, expectedFiles) { + for (const QString &expectedFile : expectedFiles) { if (expectedFile.startsWith(input, caseSensitivity)) ++expected; } @@ -585,16 +571,16 @@ void tst_QFiledialog::completer_up() fd.show(); QLineEdit *lineEdit = fd.findChild<QLineEdit*>("fileNameEdit"); QVERIFY(lineEdit); - QCOMPARE(spyFilesSelected.count(), 0); - int depth = QDir::currentPath().split('/').count(); + QCOMPARE(spyFilesSelected.size(), 0); + int depth = QDir::currentPath().split('/').size(); for (int i = 0; i <= depth * 3 + 1; ++i) { lineEdit->insert("../"); qApp->processEvents(); } - QCOMPARE(spyCurrentChanged.count(), 0); - QCOMPARE(spyDirectoryEntered.count(), 0); - QCOMPARE(spyFilesSelected.count(), 0); - QCOMPARE(spyFilterSelected.count(), 0); + QCOMPARE(spyCurrentChanged.size(), 0); + QCOMPARE(spyDirectoryEntered.size(), 0); + QCOMPARE(spyFilesSelected.size(), 0); + QCOMPARE(spyFilterSelected.size(), 0); } void tst_QFiledialog::acceptMode() @@ -674,7 +660,7 @@ void tst_QFiledialog::filters() // effects QList<QComboBox*> views = fd.findChildren<QComboBox*>("fileTypeCombo"); - QCOMPARE(views.count(), 1); + QCOMPARE(views.size(), 1); QCOMPARE(views.at(0)->isVisible(), false); QStringList filters; @@ -689,15 +675,15 @@ void tst_QFiledialog::filters() QCOMPARE(fd.nameFilters(), filters); fd.setNameFilter("Image files (*.png *.xpm *.jpg);;Text files (*.txt);;Any files (*.*)"); QCOMPARE(fd.nameFilters(), filters); - QCOMPARE(spyCurrentChanged.count(), 0); - QCOMPARE(spyDirectoryEntered.count(), 0); - QCOMPARE(spyFilesSelected.count(), 0); - QCOMPARE(spyFilterSelected.count(), 0); + QCOMPARE(spyCurrentChanged.size(), 0); + QCOMPARE(spyDirectoryEntered.size(), 0); + QCOMPARE(spyFilesSelected.size(), 0); + QCOMPARE(spyFilterSelected.size(), 0); // setting shouldn't emit any signals for (int i = views.at(0)->currentIndex(); i < views.at(0)->count(); ++i) views.at(0)->setCurrentIndex(i); - QCOMPARE(spyFilterSelected.count(), 0); + QCOMPARE(spyFilterSelected.size(), 0); //Let check if filters with whitespaces QFileDialog fd2; @@ -736,7 +722,7 @@ void tst_QFiledialog::selectFilter() QCOMPARE(fd.selectedNameFilter(), filters.at(2)); fd.selectNameFilter(""); QCOMPARE(fd.selectedNameFilter(), filters.at(2)); - QCOMPARE(spyFilterSelected.count(), 0); + QCOMPARE(spyFilterSelected.size(), 0); } void tst_QFiledialog::history() @@ -775,10 +761,10 @@ void tst_QFiledialog::history() badHistory << QDir::toNativeSeparators(QDir::current().absolutePath()); QCOMPARE(fd.history(), badHistory); - QCOMPARE(spyCurrentChanged.count(), 0); - QCOMPARE(spyDirectoryEntered.count(), 0); - QCOMPARE(spyFilesSelected.count(), 0); - QCOMPARE(spyFilterSelected.count(), 0); + QCOMPARE(spyCurrentChanged.size(), 0); + QCOMPARE(spyDirectoryEntered.size(), 0); + QCOMPARE(spyFilesSelected.size(), 0); + QCOMPARE(spyFilterSelected.size(), 0); } void tst_QFiledialog::iconProvider() @@ -820,7 +806,7 @@ void tst_QFiledialog::itemDelegate() { QFileDialog fd; QVERIFY(fd.itemDelegate() != 0); - QItemDelegate *id = new QItemDelegate(&fd); + QStyledItemDelegate *id = new QStyledItemDelegate(&fd); fd.setItemDelegate(id); QCOMPARE(fd.itemDelegate(), (QAbstractItemDelegate *)id); } @@ -881,7 +867,7 @@ void tst_QFiledialog::selectFile() QVERIFY(model); fd->setDirectory(QDir::currentPath()); // default value - QCOMPARE(fd->selectedFiles().count(), 1); + QCOMPARE(fd->selectedFiles().size(), 1); QScopedPointer<QTemporaryFile> tempFile; if (file == QLatin1String("temp")) { @@ -891,7 +877,7 @@ void tst_QFiledialog::selectFile() } fd->selectFile(file); - QCOMPARE(fd->selectedFiles().count(), count); + QCOMPARE(fd->selectedFiles().size(), count); if (tempFile.isNull()) { QCOMPARE(model->index(fd->directory().path()), model->index(QDir::currentPath())); } else { @@ -939,7 +925,7 @@ void tst_QFiledialog::selectFiles() QString filesPath = fd.directory().absolutePath(); for (int i=0; i < 5; ++i) { QFile file(filesPath + QLatin1String("/qfiledialog_auto_test_not_pres_") + QString::number(i)); - file.open(QIODevice::WriteOnly); + QVERIFY(file.open(QIODevice::WriteOnly)); file.resize(1024); file.flush(); file.close(); @@ -948,29 +934,29 @@ void tst_QFiledialog::selectFiles() // Get a list of files in the view and then get the corresponding index's QStringList list = fd.directory().entryList(QDir::Files); QModelIndexList toSelect; - QVERIFY(list.count() > 1); + QVERIFY(list.size() > 1); QListView* listView = fd.findChild<QListView*>("listView"); QVERIFY(listView); - for (int i = 0; i < list.count(); ++i) { + for (int i = 0; i < list.size(); ++i) { fd.selectFile(fd.directory().path() + QLatin1Char('/') + list.at(i)); QTRY_VERIFY(!listView->selectionModel()->selectedRows().isEmpty()); toSelect.append(listView->selectionModel()->selectedRows().last()); } - QCOMPARE(spyFilesSelected.count(), 0); + QCOMPARE(spyFilesSelected.size(), 0); listView->selectionModel()->clear(); - QCOMPARE(spyFilesSelected.count(), 0); + QCOMPARE(spyFilesSelected.size(), 0); // select the indexes - for (int i = 0; i < toSelect.count(); ++i) { + for (int i = 0; i < toSelect.size(); ++i) { listView->selectionModel()->select(toSelect.at(i), QItemSelectionModel::Select | QItemSelectionModel::Rows); } - QCOMPARE(fd.selectedFiles().count(), toSelect.count()); - QCOMPARE(spyCurrentChanged.count(), 0); - QCOMPARE(spyDirectoryEntered.count(), 0); - QCOMPARE(spyFilesSelected.count(), 0); - QCOMPARE(spyFilterSelected.count(), 0); + QCOMPARE(fd.selectedFiles().size(), toSelect.size()); + QCOMPARE(spyCurrentChanged.size(), 0); + QCOMPARE(spyDirectoryEntered.size(), 0); + QCOMPARE(spyFilesSelected.size(), 0); + QCOMPARE(spyFilterSelected.size(), 0); } @@ -996,13 +982,13 @@ void tst_QFiledialog::viewMode() // find widgets QList<QTreeView*> treeView = fd.findChildren<QTreeView*>("treeView"); - QCOMPARE(treeView.count(), 1); + QCOMPARE(treeView.size(), 1); QList<QListView*> listView = fd.findChildren<QListView*>("listView"); - QCOMPARE(listView.count(), 1); + QCOMPARE(listView.size(), 1); QList<QToolButton*> listButton = fd.findChildren<QToolButton*>("listModeButton"); - QCOMPARE(listButton.count(), 1); + QCOMPARE(listButton.size(), 1); QList<QToolButton*> treeButton = fd.findChildren<QToolButton*>("detailModeButton"); - QCOMPARE(treeButton.count(), 1); + QCOMPARE(treeButton.size(), 1); // default value QCOMPARE(fd.viewMode(), QFileDialog::List); @@ -1131,7 +1117,6 @@ void tst_QFiledialog::focus() QFileDialog fd; fd.setDirectory(QDir::currentPath()); fd.show(); - QApplication::setActiveWindow(&fd); QVERIFY(QTest::qWaitForWindowActive(&fd)); QCOMPARE(fd.isVisible(), true); QCOMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&fd)); @@ -1141,7 +1126,7 @@ void tst_QFiledialog::focus() QCursor::setPos(fd.geometry().center()); QList<QWidget*> treeView = fd.findChildren<QWidget*>("fileNameEdit"); - QCOMPARE(treeView.count(), 1); + QCOMPARE(treeView.size(), 1); QVERIFY(treeView.at(0)); QTRY_COMPARE(treeView.at(0)->hasFocus(), true); QCOMPARE(treeView.at(0)->hasFocus(), true); @@ -1171,13 +1156,13 @@ void tst_QFiledialog::historyBack() QCOMPARE(backButton->isEnabled(), true); QCOMPARE(forwardButton->isEnabled(), false); fd.setDirectory(desktop); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); backButton->click(); qApp->processEvents(); QCOMPARE(backButton->isEnabled(), true); QCOMPARE(forwardButton->isEnabled(), true); - QCOMPARE(spy.count(), 3); + QCOMPARE(spy.size(), 3); QString currentPath = qvariant_cast<QString>(spy.last().first()); QCOMPARE(model->index(currentPath), model->index(temp)); @@ -1186,11 +1171,11 @@ void tst_QFiledialog::historyBack() QCOMPARE(currentPath, home); QCOMPARE(backButton->isEnabled(), false); QCOMPARE(forwardButton->isEnabled(), true); - QCOMPARE(spy.count(), 4); + QCOMPARE(spy.size(), 4); // nothing should change at this point backButton->click(); - QCOMPARE(spy.count(), 4); + QCOMPARE(spy.size(), 4); QCOMPARE(backButton->isEnabled(), false); QCOMPARE(forwardButton->isEnabled(), true); } @@ -1224,7 +1209,7 @@ void tst_QFiledialog::historyForward() QCOMPARE(model->index(qvariant_cast<QString>(spy.last().first())), model->index(desktop)); QCOMPARE(backButton->isEnabled(), true); QCOMPARE(forwardButton->isEnabled(), false); - QCOMPARE(spy.count(), 4); + QCOMPARE(spy.size(), 4); backButton->click(); QCOMPARE(model->index(qvariant_cast<QString>(spy.last().first())), model->index(temp)); @@ -1234,13 +1219,13 @@ void tst_QFiledialog::historyForward() QCOMPARE(model->index(qvariant_cast<QString>(spy.last().first())), model->index(home)); QCOMPARE(backButton->isEnabled(), false); QCOMPARE(forwardButton->isEnabled(), true); - QCOMPARE(spy.count(), 6); + QCOMPARE(spy.size(), 6); forwardButton->click(); QCOMPARE(model->index(qvariant_cast<QString>(spy.last().first())), model->index(temp)); backButton->click(); QCOMPARE(model->index(qvariant_cast<QString>(spy.last().first())), model->index(home)); - QCOMPARE(spy.count(), 8); + QCOMPARE(spy.size(), 8); forwardButton->click(); QCOMPARE(model->index(qvariant_cast<QString>(spy.last().first())), model->index(temp)); @@ -1451,6 +1436,10 @@ void tst_QFiledialog::widgetlessNativeDialog() { if (!QGuiApplicationPrivate::platformTheme()->usePlatformNativeDialog(QPlatformTheme::FileDialog)) QSKIP("This platform always uses widgets to realize its QFileDialog, instead of the native file dialog."); +#ifdef Q_OS_ANDROID + // QTBUG-101194 + QSKIP("Android: This keeps the window open. Figure out why."); +#endif QApplication::setAttribute(Qt::AA_DontUseNativeDialogs, false); QFileDialog fd; fd.setWindowModality(Qt::ApplicationModal); @@ -1463,6 +1452,46 @@ void tst_QFiledialog::widgetlessNativeDialog() QApplication::setAttribute(Qt::AA_DontUseNativeDialogs, true); } +void tst_QFiledialog::hideNativeByDestruction() +{ + if (!QGuiApplicationPrivate::platformTheme()->usePlatformNativeDialog(QPlatformTheme::FileDialog)) + QSKIP("This platform always uses widgets to realize its QFileDialog, instead of the native file dialog."); + +#ifdef Q_OS_ANDROID + // QTBUG-101194 + QSKIP("Android: This keeps the native window open. Figure out why."); +#endif + + QApplication::setAttribute(Qt::AA_DontUseNativeDialogs, false); + auto resetAttribute = qScopeGuard([]{ + QApplication::setAttribute(Qt::AA_DontUseNativeDialogs, true); + }); + + QWidget window; + QWidget *child = new QWidget(&window); + QPointer<QFileDialog> dialog = new QFileDialog(child); + // Make it application modal so that we don't end up with a sheet on macOS + dialog->setWindowModality(Qt::ApplicationModal); + window.show(); + QVERIFY(QTest::qWaitForWindowActive(&window)); + dialog->open(); + + // We test that the dialog opens and closes by watching the activation of the + // transient parent window. If it doesn't deactivate, then we have to skip. + const auto windowActive = [&window]{ return window.isActiveWindow(); }; + const auto windowInactive = [&window]{ return !window.isActiveWindow(); }; + if (!QTest::qWaitFor(windowInactive, 2000)) + QSKIP("Dialog didn't activate"); + + // This should destroy the dialog and close the native window + child->deleteLater(); + QTRY_VERIFY(!dialog); + // If the native window is still open, then the transient parent can't become + // active + window.activateWindow(); + QVERIFY(QTest::qWaitFor(windowActive, 2000)); +} + void tst_QFiledialog::selectedFilesWithoutWidgets() { // Test for a crash when widgets are not instantiated yet. @@ -1471,6 +1500,21 @@ void tst_QFiledialog::selectedFilesWithoutWidgets() QVERIFY(fd.selectedFiles().size() >= 0); } +void tst_QFiledialog::selectedFileWithDefaultSuffix() +{ + // QTBUG-59401: dot in file path should not prevent default suffix from being added + QTemporaryDir tempDir(QDir::tempPath() + "/abcXXXXXX.def"); + QVERIFY2(tempDir.isValid(), qPrintable(tempDir.errorString())); + + QFileDialog fd; + fd.setDirectory(tempDir.path()); + fd.setDefaultSuffix(".txt"); + fd.selectFile("xxx"); + const auto selectedFiles = fd.selectedFiles(); + QCOMPARE(selectedFiles.size(), 1); + QVERIFY(selectedFiles.first().endsWith(".txt")); +} + void tst_QFiledialog::trailingDotsAndSpaces() { #ifndef Q_OS_WIN @@ -1552,6 +1596,10 @@ void tst_QFiledialog::rejectModalDialogs() { if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) QSKIP("Wayland: This freezes. Figure out why."); +#ifdef Q_OS_ANDROID + // QTBUG-101194 + QSKIP("Android: This freezes. Figure out why."); +#endif // QTBUG-38672 , static functions should return empty Urls DialogRejecter dr; @@ -1583,6 +1631,12 @@ void tst_QFiledialog::QTBUG49600_nativeIconProviderCrash() { if (!QGuiApplicationPrivate::platformTheme()->usePlatformNativeDialog(QPlatformTheme::FileDialog)) QSKIP("This platform always uses widgets to realize its QFileDialog, instead of the native file dialog."); + +#ifdef Q_OS_ANDROID + // QTBUG-101194 + QSKIP("Android: This hangs. Figure out why."); +#endif + QFileDialog fd; fd.iconProvider(); } @@ -1614,6 +1668,10 @@ void tst_QFiledialog::focusObjectDuringDestruction() { if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) QSKIP("Wayland: This freezes. Figure out why."); +#ifdef Q_OS_ANDROID + // QTBUG-101194 + QSKIP("Android: This freezes. Figure out why."); +#endif QTRY_VERIFY(QGuiApplication::topLevelWindows().isEmpty()); diff --git a/tests/auto/widgets/dialogs/qfiledialog2/CMakeLists.txt b/tests/auto/widgets/dialogs/qfiledialog2/CMakeLists.txt index 4fded21556..7db2168a20 100644 --- a/tests/auto/widgets/dialogs/qfiledialog2/CMakeLists.txt +++ b/tests/auto/widgets/dialogs/qfiledialog2/CMakeLists.txt @@ -1,15 +1,20 @@ -# Generated from qfiledialog2.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qfiledialog2 Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qfiledialog2 LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qfiledialog2 SOURCES tst_qfiledialog2.cpp - DEFINES - SRCDIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/\\\" - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/dialogs/qfiledialog2/tst_qfiledialog2.cpp b/tests/auto/widgets/dialogs/qfiledialog2/tst_qfiledialog2.cpp index a52ff0ee85..c34c8559da 100644 --- a/tests/auto/widgets/dialogs/qfiledialog2/tst_qfiledialog2.cpp +++ b/tests/auto/widgets/dialogs/qfiledialog2/tst_qfiledialog2.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -36,7 +11,6 @@ #include <qdebug.h> #include <qfiledialog.h> #include <qabstractitemdelegate.h> -#include <qitemdelegate.h> #include <qlistview.h> #include <qcombobox.h> #include <qpushbutton.h> @@ -60,9 +34,9 @@ #include <qpa/qplatformdialoghelper.h> #include <qpa/qplatformintegration.h> -#if defined(Q_OS_WIN) -#include "../../../network-settings.h" -#endif +#include "../../../../shared/filesystem.h" + +#include <QtWidgets/private/qapplication_p.h> #if defined QT_BUILD_INTERNAL QT_BEGIN_NAMESPACE @@ -133,6 +107,10 @@ private slots: void dontShowCompleterOnRoot(); void nameFilterParsing_data(); void nameFilterParsing(); +#if QT_CONFIG(settings) + void settingsCompatibility_data(); + void settingsCompatibility(); +#endif private: void cleanupSettingsFile(); @@ -281,7 +259,7 @@ void tst_QFileDialog2::unc() { #if defined(Q_OS_WIN) // Only test UNC on Windows./ - QString dir("\\\\" + QtNetworkSettings::winServerName() + "\\testsharewritable"); + QString dir("\\\\" + QTest::uncServerName() + "\\testsharewritable"); #else QString dir(QDir::currentPath()); #endif @@ -333,7 +311,7 @@ static bool openContextMenu(QFileDialog &fd) MenuCloser closer(&fd); QObject::connect(&timer, &QTimer::timeout, &closer, &MenuCloser::close); timer.start(); - QContextMenuEvent cme(QContextMenuEvent::Mouse, QPoint(10, 10)); + QContextMenuEvent cme(QContextMenuEvent::Mouse, QPoint(10, 10), list->viewport()->mapToGlobal(QPoint(10, 10))); qApp->sendEvent(list->viewport(), &cme); // blocks until menu is closed again. return true; } @@ -473,6 +451,44 @@ void tst_QFileDialog2::task180459_lastDirectory() delete dlg; } +#if QT_CONFIG(settings) +void tst_QFileDialog2::settingsCompatibility_data() +{ + QTest::addColumn<QString>("qtVersion"); + QTest::addColumn<QDataStream::Version>("dsVersion"); + QTest::newRow("6.2.3") << "6.2.3" << QDataStream::Qt_6_0; + QTest::newRow("6.5") << "6.5" << QDataStream::Qt_5_0; + QTest::newRow("15.5.2") << "5.15.2" << QDataStream::Qt_5_15; + QTest::newRow("15.5.9") << "5.15.9" << QDataStream::Qt_5_15; +} + +void tst_QFileDialog2::settingsCompatibility() +{ + static const QByteArray ba32 = QByteArrayLiteral("\x00\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xF7\x00\x00\x00\x04\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00""d\xFF\xFF\xFF\xFF\x00\x00\x00\x81\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x01\t\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00>\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00""B\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00n\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\xE8\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00"); + static const QByteArray ba64 = QByteArrayLiteral("\x00\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xF7\x00\x00\x00\x04\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00""d\xFF\xFF\xFF\xFF\x00\x00\x00\x81\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x01\t\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00>\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00""B\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00n\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\xE8\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00"); + QFETCH(QString, qtVersion); + QFETCH(QDataStream::Version, dsVersion); + // Create a header view, convert template to target format and store it in settings + { + QSettings settings(QSettings::UserScope, "QtProject"); + settings.beginGroup("FileDialog"); + settings.setValue("sidebarWidth", 93); // random value + settings.setValue("shortcuts", QStringList({settings.fileName(), "/tmp"})); + settings.setValue("qtVersion", qtVersion); + settings.setValue("treeViewHeader", dsVersion < QDataStream::Qt_6_0 ? ba32 : ba64); + settings.endGroup(); + } + // Create a file dialog, read settings write them back + { + QFileDialog fd; + } + // Read back settings and compare byte array + QSettings settings(QSettings::UserScope, "QtProject"); + settings.beginGroup("FileDialog"); + const QByteArray savedState = settings.value("treeViewHeader").toByteArray(); + QCOMPARE(savedState, ba32); +} +#endif class FilterDirModel : public QSortFilterProxyModel @@ -652,7 +668,7 @@ void tst_QFileDialog2::task226366_lowerCaseHardDriveWindows() //i clear my previous selection in the completer QTest::keyClick(edit->completer()->popup(), Qt::Key_Down); edit->clear(); - QTest::keyClick(edit, (char)(Qt::Key_C | Qt::SHIFT)); + QTest::keyClick(edit, Qt::Key_C, Qt::ShiftModifier); QTest::qWait(200); QTest::keyClick(edit->completer()->popup(), Qt::Key_Down); QCOMPARE(edit->text(), QString("C:/")); @@ -665,13 +681,13 @@ void tst_QFileDialog2::completionOnLevelAfterRoot() #if defined(Q_OS_WIN) fd.setDirectory("C:/"); QDir current = fd.directory(); - QStringList entryList = current.entryList(QStringList(), QDir::Dirs); + const QStringList entryList = current.entryList(QStringList(), QDir::Dirs); // Find a suitable test dir under c:-root: // - At least 6 characters long // - Ascii, letters only // - No another dir with same start QString testDir; - foreach (const QString &entry, entryList) { + for (const QString &entry : entryList) { if (entry.size() > 5 && QString(entry.toLatin1()).compare(entry) == 0) { bool invalid = false; for (int i = 0; i < 5; i++) { @@ -681,7 +697,7 @@ void tst_QFileDialog2::completionOnLevelAfterRoot() } } if (!invalid) { - foreach (const QString &check, entryList) { + for (const QString &check : entryList) { if (check.startsWith(entry.left(5), Qt::CaseInsensitive) && check != entry) { invalid = true; break; @@ -696,6 +712,17 @@ void tst_QFileDialog2::completionOnLevelAfterRoot() } if (testDir.isEmpty()) QSKIP("This test requires to have a unique directory of at least six ascii characters under c:/"); +#elif defined(Q_OS_ANDROID) + // Android 11 and above doesn't allow accessing root filesystem as before, + // so let's opt int for the app's home. + const auto homePaths = QStandardPaths::standardLocations(QStandardPaths::HomeLocation); + QVERIFY(!homePaths.isEmpty()); + fd.setFilter(QDir::Hidden | QDir::AllDirs | QDir::Files | QDir::System); + fd.setDirectory(homePaths.first()); + QDir(homePaths.first()).mkdir("etc"); + auto cleanup = qScopeGuard([&]() { + QDir(homePaths.first()).rmdir("etc"); + }); #else fd.setFilter(QDir::Hidden | QDir::AllDirs | QDir::Files | QDir::System); fd.setDirectory("/"); @@ -777,8 +804,8 @@ void tst_QFileDialog2::task235069_hideOnEscape() child->setFocus(); QTest::keyClick(child, Qt::Key_Escape); QCOMPARE(fd.isVisible(), false); - QCOMPARE(spyFinished.count(), 1); // QTBUG-7690 - QCOMPARE(spyRejected.count(), 1); // reject(), don't hide() + QCOMPARE(spyFinished.size(), 1); // QTBUG-7690 + QCOMPARE(spyRejected.size(), 1); // reject(), don't hide() } #ifdef QT_BUILD_INTERNAL @@ -825,7 +852,7 @@ void tst_QFileDialog2::task203703_returnProperSeparator() QVERIFY(button); QTest::keyClick(button, Qt::Key_Return); QString result = fd.selectedFiles().first(); - QVERIFY(result.at(result.count() - 1) != '/'); + QVERIFY(result.at(result.size() - 1) != '/'); QVERIFY(!result.contains('\\')); current.rmdir("aaaaaaaaaaaaaaaaaa"); } @@ -920,9 +947,9 @@ void tst_QFileDialog2::task239706_editableFilterCombo() d.show(); QVERIFY(QTest::qWaitForWindowExposed(&d)); - QList<QComboBox *> comboList = d.findChildren<QComboBox *>(); + const QList<QComboBox *> comboList = d.findChildren<QComboBox *>(); QComboBox *filterCombo = nullptr; - foreach (QComboBox *combo, comboList) { + for (QComboBox *combo : comboList) { if (combo->objectName() == QString("fileTypeCombo")) { filterCombo = combo; break; @@ -1000,10 +1027,10 @@ public : void removeSelection() { QList<QModelIndex> idxs = selectionModel()->selectedIndexes(); QList<QPersistentModelIndex> indexes; - for (int i = 0; i < idxs.count(); i++) + for (int i = 0; i < idxs.size(); i++) indexes.append(idxs.at(i)); - for (int i = 0; i < indexes.count(); ++i) + for (int i = 0; i < indexes.size(); ++i) if (!indexes.at(i).data(Qt::UserRole + 1).toUrl().path().isEmpty()) model()->removeRow(indexes.at(i).row()); } @@ -1083,7 +1110,6 @@ void tst_QFileDialog2::task254490_selectFileMultipleTimes() QTemporaryFile *t; t = new QTemporaryFile; QVERIFY2(t->open(), qPrintable(t->errorString())); - t->open(); QFileDialog fd(0, "TestFileDialog"); fd.setDirectory(tempPath); @@ -1105,7 +1131,7 @@ void tst_QFileDialog2::task254490_selectFileMultipleTimes() QCOMPARE(lineEdit->text(),QLatin1String("new_file.txt")); QListView *list = fd.findChild<QListView*>("listView"); QVERIFY(list); - QCOMPARE(list->selectionModel()->selectedRows(0).count(), 0); + QCOMPARE(list->selectionModel()->selectedRows(0).size(), 0); t->deleteLater(); } @@ -1121,7 +1147,7 @@ void tst_QFileDialog2::task257579_sideBarWithNonCleanUrls() QFileDialog fd; fd.setSidebarUrls(QList<QUrl>() << QUrl::fromLocalFile(url)); QSidebar *sidebar = fd.findChild<QSidebar*>("sidebar"); - QCOMPARE(sidebar->urls().count(), 1); + QCOMPARE(sidebar->urls().size(), 1); QVERIFY(sidebar->urls().first().toLocalFile() != url); QCOMPARE(sidebar->urls().first().toLocalFile(), QDir::cleanPath(url)); @@ -1226,7 +1252,7 @@ void tst_QFileDialog2::QTBUG6558_showDirsOnly() //Create a file QFile tempFile(dirPath + "/plop.txt"); - tempFile.open(QIODevice::WriteOnly | QIODevice::Text); + QVERIFY(tempFile.open(QIODevice::WriteOnly | QIODevice::Text)); QTextStream out(&tempFile); out << "The magic number is: " << 49 << "\n"; tempFile.close(); @@ -1239,7 +1265,6 @@ void tst_QFileDialog2::QTBUG6558_showDirsOnly() fd.setOption(QFileDialog::ShowDirsOnly, true); fd.show(); - QApplication::setActiveWindow(&fd); QVERIFY(QTest::qWaitForWindowActive(&fd)); QCOMPARE(fd.isVisible(), true); QCOMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&fd)); @@ -1283,7 +1308,6 @@ void tst_QFileDialog2::QTBUG4842_selectFilterWithHideNameFilterDetails() fd.selectNameFilter(chosenFilterString); fd.show(); - QApplication::setActiveWindow(&fd); QVERIFY(QTest::qWaitForWindowActive(&fd)); QCOMPARE(fd.isVisible(), true); QCOMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&fd)); @@ -1299,7 +1323,6 @@ void tst_QFileDialog2::QTBUG4842_selectFilterWithHideNameFilterDetails() fd2.selectNameFilter(chosenFilterString); fd2.show(); - QApplication::setActiveWindow(&fd2); QVERIFY(QTest::qWaitForWindowActive(&fd2)); QCOMPARE(fd2.isVisible(), true); QCOMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&fd2)); @@ -1319,7 +1342,6 @@ void tst_QFileDialog2::dontShowCompleterOnRoot() fd.setAcceptMode(QFileDialog::AcceptSave); fd.show(); - QApplication::setActiveWindow(&fd); QVERIFY(QTest::qWaitForWindowActive(&fd)); QCOMPARE(fd.isVisible(), true); QCOMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&fd)); diff --git a/tests/auto/widgets/dialogs/qfontdialog/CMakeLists.txt b/tests/auto/widgets/dialogs/qfontdialog/CMakeLists.txt index 4e231cc6cc..b1bbf62cc7 100644 --- a/tests/auto/widgets/dialogs/qfontdialog/CMakeLists.txt +++ b/tests/auto/widgets/dialogs/qfontdialog/CMakeLists.txt @@ -1,19 +1,15 @@ -# Generated from qfontdialog.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qfontdialog Test: ##################################################################### -qt_internal_add_test(tst_qfontdialog - SOURCES - tst_qfontdialog.cpp - PUBLIC_LIBRARIES - Qt::CorePrivate - Qt::Gui - Qt::GuiPrivate - Qt::Widgets - Qt::WidgetsPrivate -) +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qfontdialog LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() # Resources: set_source_files_properties("../../../shared/resources/test.ttf" @@ -27,13 +23,18 @@ set(testfonts_resource_files "../../../shared/resources/testfont.ttf" ) -qt_internal_add_resource(tst_qfontdialog "testfonts" - PREFIX - "/" - FILES - ${testfonts_resource_files} +qt_internal_add_test(tst_qfontdialog + SOURCES + tst_qfontdialog.cpp + LIBRARIES + Qt::CorePrivate + Qt::Gui + Qt::GuiPrivate + Qt::Widgets + Qt::WidgetsPrivate + TESTDATA ${testfonts_resource_files} + BUILTIN_TESTDATA ) - ## Scopes: ##################################################################### diff --git a/tests/auto/widgets/dialogs/qfontdialog/testfonts.qrc b/tests/auto/widgets/dialogs/qfontdialog/testfonts.qrc deleted file mode 100644 index cdfa287b39..0000000000 --- a/tests/auto/widgets/dialogs/qfontdialog/testfonts.qrc +++ /dev/null @@ -1,6 +0,0 @@ -<RCC> - <qresource prefix="/"> - <file alias="test.ttf">../../../shared/resources/test.ttf</file> - <file alias="testfont.ttf">../../../shared/resources/testfont.ttf</file> - </qresource> -</RCC> diff --git a/tests/auto/widgets/dialogs/qfontdialog/tst_qfontdialog.cpp b/tests/auto/widgets/dialogs/qfontdialog/tst_qfontdialog.cpp index 6032acf00c..01f3e7ec95 100644 --- a/tests/auto/widgets/dialogs/qfontdialog/tst_qfontdialog.cpp +++ b/tests/auto/widgets/dialogs/qfontdialog/tst_qfontdialog.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -70,6 +45,7 @@ private slots: void qtbug_41513_stylesheetStyle(); #endif + void hideNativeByDestruction(); private: void runSlotWithFailsafeTimer(const char *member); @@ -101,7 +77,7 @@ void tst_QFontDialog::cleanup() void tst_QFontDialog::postKeyReturn() { QWidgetList list = QApplication::topLevelWidgets(); - for (int i=0; i<list.count(); ++i) { + for (int i=0; i<list.size(); ++i) { QFontDialog *dialog = qobject_cast<QFontDialog*>(list[i]); if (dialog) { QTest::keyClick( list[i], Qt::Key_Return, Qt::NoModifier ); @@ -240,7 +216,7 @@ void tst_QFontDialog::testNonStandardFontSize() QList<int> standardSizesList = QFontDatabase::standardSizes(); int nonStandardFontSize; if (!standardSizesList.isEmpty()) { - nonStandardFontSize = standardSizesList.at(standardSizesList.count()-1); // get the maximum standard size. + nonStandardFontSize = standardSizesList.at(standardSizesList.size()-1); // get the maximum standard size. nonStandardFontSize += 1; // the increment of 1 to mock a non-standard font size. } else { QSKIP("QFontDatabase::standardSizes() is empty."); @@ -260,7 +236,34 @@ void tst_QFontDialog::testNonStandardFontSize() if (accepted) QCOMPARE(testFont.pointSize(), resultFont.pointSize()); else - QWARN("Fail using a non-standard font size."); + qWarning("Fail using a non-standard font size."); +} + +void tst_QFontDialog::hideNativeByDestruction() +{ + QWidget window; + QWidget *child = new QWidget(&window); + QPointer<QFontDialog> dialog = new QFontDialog(child); + // Make it application modal so that we don't end up with a sheet on macOS + dialog->setWindowModality(Qt::ApplicationModal); + window.show(); + QVERIFY(QTest::qWaitForWindowActive(&window)); + dialog->open(); + + // We test that the dialog opens and closes by watching the activation of the + // transient parent window. If it doesn't deactivate, then we have to skip. + const auto windowActive = [&window]{ return window.isActiveWindow(); }; + const auto windowInactive = [&window]{ return !window.isActiveWindow(); }; + if (!QTest::qWaitFor(windowInactive, 2000)) + QSKIP("Dialog didn't activate"); + + // This should destroy the dialog and close the native window + child->deleteLater(); + QTRY_VERIFY(!dialog); + // If the native window is still open, then the transient parent can't become + // active + window.activateWindow(); + QVERIFY(QTest::qWaitFor(windowActive)); } QTEST_MAIN(tst_QFontDialog) diff --git a/tests/auto/widgets/dialogs/qfontdialog/tst_qfontdialog_mac_helpers.mm b/tests/auto/widgets/dialogs/qfontdialog/tst_qfontdialog_mac_helpers.mm deleted file mode 100644 index 00cc6a879f..0000000000 --- a/tests/auto/widgets/dialogs/qfontdialog/tst_qfontdialog_mac_helpers.mm +++ /dev/null @@ -1,53 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <AppKit/AppKit.h> - -void click_cocoa_button() -{ - QMacAutoReleasePool pool; - NSArray *windows = [NSApp windows]; - for (NSWindow *window in windows) { - // This is NOT how one should do RTTI, but since I don't want to leak the class too much... - if ([[window delegate] respondsToSelector:@selector(qtFont)]) { - NSArray *subviews = [[window contentView] subviews]; - for (NSView *view in subviews) { - if ([view isKindOfClass:[NSButton class]] - && [[static_cast<NSButton *>(view) title] isEqualTo:@"OK"]) { - [static_cast<NSButton *>(view) performClick:view]; - [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint - modifierFlags:0 timestamp:0. windowNumber:0 context:0 - subtype:SHRT_MAX data1:0 data2:0] atStart:NO]; - - break; - } - } - break; - } - } -} diff --git a/tests/auto/widgets/dialogs/qinputdialog/CMakeLists.txt b/tests/auto/widgets/dialogs/qinputdialog/CMakeLists.txt index cdb311a3c2..528493a66b 100644 --- a/tests/auto/widgets/dialogs/qinputdialog/CMakeLists.txt +++ b/tests/auto/widgets/dialogs/qinputdialog/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qinputdialog.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qinputdialog Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qinputdialog LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qinputdialog SOURCES tst_qinputdialog.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/dialogs/qinputdialog/tst_qinputdialog.cpp b/tests/auto/widgets/dialogs/qinputdialog/tst_qinputdialog.cpp index db44597401..136c789abd 100644 --- a/tests/auto/widgets/dialogs/qinputdialog/tst_qinputdialog.cpp +++ b/tests/auto/widgets/dialogs/qinputdialog/tst_qinputdialog.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/dialogs/qmessagebox/BLACKLIST b/tests/auto/widgets/dialogs/qmessagebox/BLACKLIST deleted file mode 100644 index 8d8a7c2105..0000000000 --- a/tests/auto/widgets/dialogs/qmessagebox/BLACKLIST +++ /dev/null @@ -1,4 +0,0 @@ -[defaultButton] -opensuse-leap -rhel-7.6 -ubuntu diff --git a/tests/auto/widgets/dialogs/qmessagebox/CMakeLists.txt b/tests/auto/widgets/dialogs/qmessagebox/CMakeLists.txt index 8b6b864cf7..53fd8a61c6 100644 --- a/tests/auto/widgets/dialogs/qmessagebox/CMakeLists.txt +++ b/tests/auto/widgets/dialogs/qmessagebox/CMakeLists.txt @@ -1,18 +1,22 @@ -# Generated from qmessagebox.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qmessagebox Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmessagebox LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qmessagebox SOURCES tst_qmessagebox.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate Qt::Widgets ) - -#### Keys ignored in scope 1:.:.:qmessagebox.pro:<TRUE>: -# TEMPLATE = "app" diff --git a/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp b/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp index ce84459973..94afff6e40 100644 --- a/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp +++ b/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp @@ -1,36 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QMessageBox> #include <QDebug> -#include <QPair> +#include <QSet> #include <QList> #include <QPointer> #include <QTimer> @@ -47,9 +22,14 @@ class tst_QMessageBox : public QObject Q_OBJECT private slots: + void initTestCase_data(); + void init(); + void sanityTest(); + void baseClassSetVisible(); void defaultButton(); void escapeButton(); + void clickedButton(); void button(); void statics(); void about(); @@ -57,6 +37,9 @@ private slots: void detailsButtonText(); void expandDetailsWithoutMoving(); + void optionsEmptyByDefault(); + void changeNativeOption(); + #ifndef Q_OS_MAC void shortcut(); #endif @@ -74,6 +57,13 @@ private slots: void acceptedRejectedSignals(); void acceptedRejectedSignals_data(); + void overrideDone_data(); + void overrideDone(); + + void hideNativeByDestruction(); + + void explicitDoneAfterButtonClicked(); + void cleanup(); }; @@ -126,11 +116,20 @@ void ExecCloseHelper::timerEvent(QTimerEvent *te) return; QWidget *modalWidget = QApplication::activeModalWidget(); - if (!m_testCandidate && modalWidget) m_testCandidate = modalWidget; - if (m_testCandidate && m_testCandidate == modalWidget) { + QWidget *activeWindow = QApplication::activeWindow(); + if (!m_testCandidate && activeWindow) + m_testCandidate = activeWindow; + + if (!m_testCandidate) + return; + + bool shouldHelp = (m_testCandidate->isModal() && m_testCandidate == modalWidget) + || (!m_testCandidate->isModal() && m_testCandidate == activeWindow); + + if (shouldHelp) { if (m_key == CloseWindow) { m_testCandidate->close(); } else { @@ -143,6 +142,60 @@ void ExecCloseHelper::timerEvent(QTimerEvent *te) } } +void tst_QMessageBox::initTestCase_data() +{ + QTest::addColumn<bool>("useNativeDialog"); + QTest::newRow("widget") << false; + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) { + if (theme->usePlatformNativeDialog(QPlatformTheme::MessageDialog)) + QTest::newRow("native") << true; + } +} + +void tst_QMessageBox::init() +{ + QFETCH_GLOBAL(bool, useNativeDialog); + qApp->setAttribute(Qt::AA_DontUseNativeDialogs, !useNativeDialog); +} + +class OverridingMessageBox : public QMessageBox +{ +public: + void done(int result) override { + doneResult = result; + QMessageBox::done(result); + } + std::optional<int> doneResult; +}; + +void tst_QMessageBox::overrideDone_data() +{ + QTest::addColumn<QMessageBox::StandardButton>("button"); + QTest::addColumn<int>("closeAction"); + QTest::addColumn<int>("result"); + + QTest::newRow("close") << QMessageBox::Help << int(ExecCloseHelper::CloseWindow) << 0; + QTest::newRow("yes") << QMessageBox::Yes << int(Qt::Key_Enter) << int(QMessageBox::Yes); + QTest::newRow("no") << QMessageBox::No << int(Qt::Key_Enter) << int(QMessageBox::No); +} + +void tst_QMessageBox::overrideDone() +{ + QFETCH(QMessageBox::StandardButton, button); + QFETCH(int, closeAction); + QFETCH(int, result); + + OverridingMessageBox messageBox; + messageBox.addButton(button); + messageBox.setDefaultButton(button); + ExecCloseHelper closeHelper; + closeHelper.start(closeAction, &messageBox); + messageBox.exec(); + QVERIFY(messageBox.doneResult.has_value()); + QCOMPARE(*messageBox.doneResult, result); + +} + void tst_QMessageBox::cleanup() { QTRY_VERIFY(QApplication::topLevelWidgets().isEmpty()); // OS X requires TRY @@ -169,6 +222,15 @@ void tst_QMessageBox::sanityTest() msgBox.exec(); } +void tst_QMessageBox::baseClassSetVisible() +{ + QMessageBox msgBox; + msgBox.setText("Hello World"); + msgBox.QDialog::setVisible(true); + QCOMPARE(msgBox.isVisible(), true); + msgBox.close(); +} + void tst_QMessageBox::button() { QMessageBox msgBox; @@ -302,6 +364,28 @@ void tst_QMessageBox::escapeButton() QVERIFY(msgBox3.clickedButton() == msgBox3.button(QMessageBox::Ok)); // auto detected } +void tst_QMessageBox::clickedButton() +{ + QMessageBox msgBox; + msgBox.addButton(QMessageBox::Yes); + msgBox.addButton(QMessageBox::No); + msgBox.addButton(QMessageBox::Retry); + + QVERIFY(!msgBox.clickedButton()); + + for (int i = 0; i < 2; ++i) { + QAbstractButton *clickedButtonAfterExex = nullptr; + QTimer::singleShot(100, [&] { + clickedButtonAfterExex = msgBox.clickedButton(); + msgBox.close(); + }); + msgBox.exec(); + + QVERIFY(!clickedButtonAfterExex); + QVERIFY(msgBox.clickedButton()); + } +} + void tst_QMessageBox::statics() { QMessageBox::StandardButton (*statics[4])(QWidget *, const QString &, @@ -355,7 +439,7 @@ void tst_QMessageBox::shortcut() msgBox.addButton("&Maybe", QMessageBox::YesRole); ExecCloseHelper closeHelper; closeHelper.start(Qt::Key_M, &msgBox); - QCOMPARE(msgBox.exec(), 2); + QCOMPARE(msgBox.exec(), 4); } #endif @@ -388,69 +472,63 @@ void tst_QMessageBox::staticSourceCompat() int ret; // source compat tests for < 4.2 +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED +#define COMPARE(real, exp) do {\ + const auto pressed = static_cast<QMessageBox::StandardButton>(real);\ + const auto expected = static_cast<QMessageBox::StandardButton>(exp);\ + if (!QTest::qCompare(pressed, expected, #real, #exp, __FILE__, __LINE__)) \ + return; } while (false) + ExecCloseHelper closeHelper; closeHelper.start(Qt::Key_Enter); ret = QMessageBox::information(nullptr, "title", "text", QMessageBox::Yes, QMessageBox::No); - int expectedButton = int(QMessageBox::Yes); - if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) { - const int dialogButtonBoxLayout = theme->themeHint(QPlatformTheme::DialogButtonBoxLayout).toInt(); - if (dialogButtonBoxLayout == QDialogButtonBox::MacLayout - || dialogButtonBoxLayout == QDialogButtonBox::GnomeLayout) - expectedButton = int(QMessageBox::No); - } - QCOMPARE(ret, expectedButton); + COMPARE(ret, QMessageBox::No); QVERIFY(closeHelper.done()); closeHelper.start(Qt::Key_Enter); ret = QMessageBox::information(nullptr, "title", "text", QMessageBox::Yes | QMessageBox::Default, QMessageBox::No); - QCOMPARE(ret, int(QMessageBox::Yes)); + COMPARE(ret, int(QMessageBox::Yes)); QVERIFY(closeHelper.done()); +#if QT_DEPRECATED_SINCE(6, 2) + // The overloads below are valid only before 6.2 closeHelper.start(Qt::Key_Enter); ret = QMessageBox::information(nullptr, "title", "text", QMessageBox::Yes, QMessageBox::No | QMessageBox::Default); - QCOMPARE(ret, int(QMessageBox::No)); + COMPARE(ret, int(QMessageBox::No)); QVERIFY(closeHelper.done()); closeHelper.start(Qt::Key_Enter); ret = QMessageBox::information(nullptr, "title", "text", QMessageBox::Yes | QMessageBox::Default, QMessageBox::No | QMessageBox::Escape); - QCOMPARE(ret, int(QMessageBox::Yes)); + COMPARE(ret, int(QMessageBox::Yes)); QVERIFY(closeHelper.done()); closeHelper.start(Qt::Key_Enter); ret = QMessageBox::information(nullptr, "title", "text", QMessageBox::Yes | QMessageBox::Escape, QMessageBox::No | QMessageBox::Default); - QCOMPARE(ret, int(QMessageBox::No)); + COMPARE(ret, int(QMessageBox::No)); QVERIFY(closeHelper.done()); // the button text versions closeHelper.start(Qt::Key_Enter); ret = QMessageBox::information(nullptr, "title", "text", "Yes", "No", QString(), 1); - QCOMPARE(ret, 1); + COMPARE(ret, 3); // Custom button opaque result QVERIFY(closeHelper.done()); - - if (0) { // don't run these tests since the dialog won't close! - closeHelper.start(Qt::Key_Escape); - ret = QMessageBox::information(nullptr, "title", "text", "Yes", "No", QString(), 1); - QCOMPARE(ret, -1); - QVERIFY(closeHelper.done()); - - closeHelper.start(Qt::Key_Escape); - ret = QMessageBox::information(nullptr, "title", "text", "Yes", "No", QString(), 0, 1); - QCOMPARE(ret, 1); - QVERIFY(closeHelper.done()); - } +#endif // QT_DEPRECATED_SINCE(6, 2) +#undef COMPARE +QT_WARNING_POP } void tst_QMessageBox::instanceSourceCompat() { - QMessageBox mb("Application name here", - "Saving the file will overwrite the original file on the disk.\n" - "Do you really want to save?", - QMessageBox::Information, - QMessageBox::Yes | QMessageBox::Default, - QMessageBox::No, - QMessageBox::Cancel | QMessageBox::Escape); - mb.setButtonText(QMessageBox::Yes, "Save"); - mb.setButtonText(QMessageBox::No, "Discard"); + QMessageBox mb(QMessageBox::Information, + "Application name here", + "Saving the file will overwrite the original file on the disk.\n" + "Do you really want to save?", + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); + mb.setDefaultButton(QMessageBox::Yes); + mb.setEscapeButton(QMessageBox::Cancel); + mb.button(QMessageBox::Yes)->setText("Save"); + mb.button(QMessageBox::No)->setText("Discard"); mb.addButton("&Revert", QMessageBox::RejectRole); mb.addButton("&Zoo", QMessageBox::ActionRole); @@ -462,14 +540,18 @@ void tst_QMessageBox::instanceSourceCompat() #ifndef Q_OS_MAC // mnemonics are not used on OS X closeHelper.start(QKeyCombination(Qt::ALT | Qt::Key_R).toCombined(), &mb); - QCOMPARE(mb.exec(), 0); + QCOMPARE(mb.exec(), 2); closeHelper.start(QKeyCombination(Qt::ALT | Qt::Key_Z).toCombined(), &mb); - QCOMPARE(mb.exec(), 1); + QCOMPARE(mb.exec(), 3); #endif } void tst_QMessageBox::detailsText() { + QFETCH_GLOBAL(bool, useNativeDialog); + if (useNativeDialog) + QSKIP("Native dialogs do not propagate expose events"); + QMessageBox box; QString text("This is the details text."); box.setDetailedText(text); @@ -483,6 +565,10 @@ void tst_QMessageBox::detailsText() void tst_QMessageBox::detailsButtonText() { + QFETCH_GLOBAL(bool, useNativeDialog); + if (useNativeDialog) + QSKIP("Native dialogs do not propagate expose events"); + QMessageBox box; box.setDetailedText("bla"); box.open(); @@ -504,6 +590,10 @@ void tst_QMessageBox::detailsButtonText() void tst_QMessageBox::expandDetailsWithoutMoving() // QTBUG-32473 { + QFETCH_GLOBAL(bool, useNativeDialog); + if (useNativeDialog) + QSKIP("Native dialogs do not propagate expose events"); + tst_ResizingMessageBox box; box.setDetailedText("bla"); box.show(); @@ -529,6 +619,20 @@ void tst_QMessageBox::expandDetailsWithoutMoving() // QTBUG-32473 QCOMPARE(box.geometry().topLeft(), geom.topLeft()); } +void tst_QMessageBox::optionsEmptyByDefault() +{ + QMessageBox b; + QCOMPARE(b.options(), QMessageBox::Options()); + QVERIFY(!b.testOption(QMessageBox::Option::DontUseNativeDialog)); +} + +void tst_QMessageBox::changeNativeOption() +{ + QMessageBox b; + b.setOption(QMessageBox::Option::DontUseNativeDialog); + QCOMPARE(b.options(), QMessageBox::Options(QMessageBox::Option::DontUseNativeDialog)); +} + void tst_QMessageBox::incorrectDefaultButton() { ExecCloseHelper closeHelper; @@ -543,12 +647,17 @@ void tst_QMessageBox::incorrectDefaultButton() QMessageBox::question(nullptr, "", "I've been hit!",QFlag(QMessageBox::Ok | QMessageBox::Cancel),QMessageBox::Save); QVERIFY(closeHelper.done()); +#if QT_DEPRECATED_SINCE(6, 2) closeHelper.start(Qt::Key_Escape); QTest::ignoreMessage(QtWarningMsg, "QDialogButtonBox::createButton: Invalid ButtonRole, button not added"); QTest::ignoreMessage(QtWarningMsg, "QDialogButtonBox::createButton: Invalid ButtonRole, button not added"); +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED //do not crash here -> call old function of QMessageBox in this case QMessageBox::question(nullptr, "", "I've been hit!",QMessageBox::Ok | QMessageBox::Cancel,QMessageBox::Save | QMessageBox::Cancel,QMessageBox::Ok); +QT_WARNING_POP QVERIFY(closeHelper.done()); +#endif } void tst_QMessageBox::updateSize() @@ -598,6 +707,10 @@ Q_DECLARE_METATYPE(RoleSet); void tst_QMessageBox::acceptedRejectedSignals() { + QFETCH_GLOBAL(bool, useNativeDialog); + if (useNativeDialog) + QSKIP("Native dialogs do not propagate expose events"); + QMessageBox messageBox(QMessageBox::Information, "Test window", "Test text"); QFETCH(ButtonsCreator, buttonsCreator); @@ -619,7 +732,7 @@ void tst_QMessageBox::acceptedRejectedSignals() button->click(); if (roles.contains(messageBox.buttonRole(button))) - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); else QVERIFY(spy.isEmpty()); } @@ -677,5 +790,78 @@ void tst_QMessageBox::acceptedRejectedSignals_data() addCustomButtonsData(); } +void tst_QMessageBox::hideNativeByDestruction() +{ + QWidget window; + QWidget *child = new QWidget(&window); + QPointer<QMessageBox> dialog = new QMessageBox(child); + // Make it application modal so that we don't end up with a sheet on macOS + dialog->setWindowModality(Qt::ApplicationModal); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + dialog->open(); + + // We test that the dialog opens and closes by watching the activation of the + // transient parent window. If it doesn't deactivate, then we have to skip. + const auto windowActive = [&window]{ return window.isActiveWindow(); }; + const auto windowInactive = [&window]{ return !window.isActiveWindow(); }; + if (!QTest::qWaitFor(windowInactive, 2000)) + QSKIP("Dialog didn't activate"); + + // This should destroy the dialog and close the native window + child->deleteLater(); + QTRY_VERIFY(!dialog); + // If the native window is still open, then the transient parent can't become + // active + window.activateWindow(); + QVERIFY(QTest::qWaitFor(windowActive)); +} + +void tst_QMessageBox::explicitDoneAfterButtonClicked() +{ + QMessageBox msgBox; + auto *standardButton = msgBox.addButton(QMessageBox::Ok); + auto *customButton = msgBox.addButton("Custom", QMessageBox::RejectRole); + + QSignalSpy acceptedSpy(&msgBox, &QDialog::accepted); + QSignalSpy rejectedSpy(&msgBox, &QDialog::rejected); + + msgBox.setDefaultButton(standardButton); + ExecCloseHelper closeHelper; + closeHelper.start(Qt::Key_Enter, &msgBox); + msgBox.exec(); + QCOMPARE(msgBox.clickedButton(), standardButton); + QCOMPARE(msgBox.result(), QMessageBox::Ok); + QCOMPARE(acceptedSpy.size(), 1); + QCOMPARE(rejectedSpy.size(), 0); + + msgBox.accept(); + QCOMPARE(msgBox.result(), QDialog::Accepted); + QCOMPARE(acceptedSpy.size(), 2); + QCOMPARE(rejectedSpy.size(), 0); + msgBox.reject(); + QCOMPARE(msgBox.result(), QDialog::Rejected); + QCOMPARE(acceptedSpy.size(), 2); + QCOMPARE(rejectedSpy.size(), 1); + + msgBox.setDefaultButton(customButton); + closeHelper.start(Qt::Key_Enter, &msgBox); + msgBox.exec(); + QCOMPARE(msgBox.clickedButton(), customButton); + QVERIFY(msgBox.result() != QDialog::Accepted); + QVERIFY(msgBox.result() != QDialog::Rejected); + QCOMPARE(acceptedSpy.size(), 2); + QCOMPARE(rejectedSpy.size(), 2); + + msgBox.accept(); + QCOMPARE(msgBox.result(), QDialog::Accepted); + QCOMPARE(acceptedSpy.size(), 3); + QCOMPARE(rejectedSpy.size(), 2); + msgBox.reject(); + QCOMPARE(msgBox.result(), QDialog::Rejected); + QCOMPARE(acceptedSpy.size(), 3); + QCOMPARE(rejectedSpy.size(), 3); +} + QTEST_MAIN(tst_QMessageBox) #include "tst_qmessagebox.moc" diff --git a/tests/auto/widgets/dialogs/qprogressdialog/CMakeLists.txt b/tests/auto/widgets/dialogs/qprogressdialog/CMakeLists.txt index 5651cf8d26..4861f3af25 100644 --- a/tests/auto/widgets/dialogs/qprogressdialog/CMakeLists.txt +++ b/tests/auto/widgets/dialogs/qprogressdialog/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qprogressdialog.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qprogressdialog Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qprogressdialog LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qprogressdialog SOURCES tst_qprogressdialog.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/dialogs/qprogressdialog/tst_qprogressdialog.cpp b/tests/auto/widgets/dialogs/qprogressdialog/tst_qprogressdialog.cpp index b847315abd..baf727d766 100644 --- a/tests/auto/widgets/dialogs/qprogressdialog/tst_qprogressdialog.cpp +++ b/tests/auto/widgets/dialogs/qprogressdialog/tst_qprogressdialog.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -51,6 +26,7 @@ private Q_SLOTS: void getSetCheck(); void task198202(); void QTBUG_31046(); + void QTBUG_19983(); void settingCustomWidgets(); void i18n(); void setValueReentrancyGuard(); @@ -108,7 +84,7 @@ void tst_QProgressDialog::autoShow() // in order to test for the setValue() behavior instead // See autoShowCtor() for the ctor timer check dlg.setValue(value); - QThread::msleep(delay); + QThread::sleep(std::chrono::milliseconds{delay}); dlg.setValue(min+1); QCOMPARE(dlg.isVisible(), expectedAutoShow); } @@ -117,7 +93,7 @@ void tst_QProgressDialog::autoShowCtor() { QProgressDialog dlg; QVERIFY(!dlg.isVisible()); - QThread::msleep(dlg.minimumDuration()); + QThread::sleep(std::chrono::milliseconds{dlg.minimumDuration()}); QTRY_VERIFY(dlg.isVisible()); } @@ -205,11 +181,34 @@ void tst_QProgressDialog::QTBUG_31046() { QProgressDialog dlg("", "", 50, 60); dlg.setValue(0); - QThread::msleep(200); + QThread::sleep(std::chrono::milliseconds{200}); dlg.setValue(50); QCOMPARE(50, dlg.value()); } +void tst_QProgressDialog::QTBUG_19983() +{ + QProgressDialog tempDlg; + tempDlg.setRange(0, 0); + tempDlg.setLabelText("This is a test."); + + QPushButton *btnOne = new QPushButton("Cancel", &tempDlg); + tempDlg.setCancelButton(btnOne); + tempDlg.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tempDlg)); + const auto btnOneGeometry = btnOne->geometry(); + QVERIFY(QPoint(0,0) != btnOneGeometry.topLeft()); + + tempDlg.cancel(); + QVERIFY(!tempDlg.isVisible()); + + QPushButton *btnTwo = new QPushButton("Cancel", &tempDlg); + tempDlg.setCancelButton(btnTwo); + tempDlg.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tempDlg)); + QCOMPARE(btnOneGeometry, btnTwo->geometry()); +} + void tst_QProgressDialog::settingCustomWidgets() { QPointer<QLabel> l = new QLabel; diff --git a/tests/auto/widgets/dialogs/qsidebar/CMakeLists.txt b/tests/auto/widgets/dialogs/qsidebar/CMakeLists.txt index 8d2fda708d..bf9513bb69 100644 --- a/tests/auto/widgets/dialogs/qsidebar/CMakeLists.txt +++ b/tests/auto/widgets/dialogs/qsidebar/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qsidebar.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qsidebar Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsidebar LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qsidebar SOURCES tst_qsidebar.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::Widgets diff --git a/tests/auto/widgets/dialogs/qsidebar/tst_qsidebar.cpp b/tests/auto/widgets/dialogs/qsidebar/tst_qsidebar.cpp index 5847bcd694..cdbf2011a4 100644 --- a/tests/auto/widgets/dialogs/qsidebar/tst_qsidebar.cpp +++ b/tests/auto/widgets/dialogs/qsidebar/tst_qsidebar.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -63,9 +38,9 @@ void tst_QSidebar::setUrls() QCOMPARE(model->rowCount(), 0); qsidebar.setUrls(urls); QCOMPARE(qsidebar.urls(), urls); - QCOMPARE(model->rowCount(), urls.count()); + QCOMPARE(model->rowCount(), urls.size()); qsidebar.setUrls(urls); - QCOMPARE(model->rowCount(), urls.count()); + QCOMPARE(model->rowCount(), urls.size()); } void tst_QSidebar::selectUrls() @@ -80,7 +55,7 @@ void tst_QSidebar::selectUrls() QSignalSpy spy(&qsidebar, SIGNAL(goToUrl(QUrl))); qsidebar.selectUrl(urls.at(0)); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } void tst_QSidebar::addUrls() @@ -93,7 +68,7 @@ void tst_QSidebar::addUrls() QAbstractItemModel *model = qsidebar.model(); QDir testDir = QDir::home(); -#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) +#ifdef Q_OS_ANDROID // temp and home is the same directory on Android testDir.mkdir(QStringLiteral("test")); QVERIFY(testDir.cd(QStringLiteral("test"))); @@ -196,7 +171,7 @@ void tst_QSidebar::goToUrl() QSignalSpy spy(&qsidebar, SIGNAL(goToUrl(QUrl))); QTest::mousePress(qsidebar.viewport(), Qt::LeftButton, {}, qsidebar.visualRect(qsidebar.model()->index(0, 0)).center()); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE((spy.value(0)).at(0).toUrl(), urls.first()); } diff --git a/tests/auto/widgets/dialogs/qwizard/CMakeLists.txt b/tests/auto/widgets/dialogs/qwizard/CMakeLists.txt index 81dc9e85b2..d863126560 100644 --- a/tests/auto/widgets/dialogs/qwizard/CMakeLists.txt +++ b/tests/auto/widgets/dialogs/qwizard/CMakeLists.txt @@ -1,17 +1,15 @@ -# Generated from qwizard.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qwizard Test: ##################################################################### -qt_internal_add_test(tst_qwizard - SOURCES - tst_qwizard.cpp - tst_qwizard_2.cpp - PUBLIC_LIBRARIES - Qt::Gui - Qt::Widgets -) +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qwizard LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() # Resources: set(qwizard_resource_files @@ -21,10 +19,15 @@ set(qwizard_resource_files "images/watermark.png" ) -qt_internal_add_resource(tst_qwizard "qwizard" - PREFIX - "/" - FILES - ${qwizard_resource_files} +qt_internal_add_test(tst_qwizard + SOURCES + tst_qwizard.cpp + tst_qwizard_2.cpp + LIBRARIES + Qt::Gui + Qt::Widgets + Qt::WidgetsPrivate + TESTDATA ${qwizard_resource_files} + BUILTIN_TESTDATA ) diff --git a/tests/auto/widgets/dialogs/qwizard/qwizard.qrc b/tests/auto/widgets/dialogs/qwizard/qwizard.qrc deleted file mode 100644 index 471da9def6..0000000000 --- a/tests/auto/widgets/dialogs/qwizard/qwizard.qrc +++ /dev/null @@ -1,8 +0,0 @@ -<!DOCTYPE RCC><RCC version="1.0"> -<qresource> - <file>images/background.png</file> - <file>images/banner.png</file> - <file>images/logo.png</file> - <file>images/watermark.png</file> -</qresource> -</RCC> diff --git a/tests/auto/widgets/dialogs/qwizard/tst_qwizard.cpp b/tests/auto/widgets/dialogs/qwizard/tst_qwizard.cpp index 8f655490e1..c0afed6919 100644 --- a/tests/auto/widgets/dialogs/qwizard/tst_qwizard.cpp +++ b/tests/auto/widgets/dialogs/qwizard/tst_qwizard.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QFont> @@ -42,6 +17,10 @@ #include <QSignalSpy> #include <QOperatingSystemVersion> +#include <QtWidgets/private/qapplication_p.h> + +#include <memory> + Q_DECLARE_METATYPE(QWizard::WizardButton); static QImage grabWidget(QWidget *window) @@ -89,6 +68,7 @@ private slots: void sideWidget(); void objectNames_data(); void objectNames(); + void changePages(); // task-specific tests below me: void task177716_disableCommitButton(); @@ -420,7 +400,7 @@ void tst_QWizard::setPixmap() QVERIFY(wizard.pixmap(QWizard::BannerPixmap).isNull()); QVERIFY(wizard.pixmap(QWizard::LogoPixmap).isNull()); QVERIFY(wizard.pixmap(QWizard::WatermarkPixmap).isNull()); - if (QOperatingSystemVersion::current() <= QOperatingSystemVersion::MacOSHighSierra) + if (QOperatingSystemVersion::currentType() == QOperatingSystemVersion::MacOS) QVERIFY(!wizard.pixmap(QWizard::BackgroundPixmap).isNull()); else QVERIFY(wizard.pixmap(QWizard::BackgroundPixmap).isNull()); @@ -428,7 +408,7 @@ void tst_QWizard::setPixmap() QVERIFY(page->pixmap(QWizard::BannerPixmap).isNull()); QVERIFY(page->pixmap(QWizard::LogoPixmap).isNull()); QVERIFY(page->pixmap(QWizard::WatermarkPixmap).isNull()); - if (QOperatingSystemVersion::current() <= QOperatingSystemVersion::MacOSHighSierra) + if (QOperatingSystemVersion::currentType() == QOperatingSystemVersion::MacOS) QVERIFY(!wizard.pixmap(QWizard::BackgroundPixmap).isNull()); else QVERIFY(page->pixmap(QWizard::BackgroundPixmap).isNull()); @@ -546,7 +526,7 @@ void tst_QWizard::addPage() QCOMPARE(wizard.addPage(pages[i]), i); QCOMPARE(pages[i]->window(), (QWidget *)&wizard); QCOMPARE(wizard.startId(), 0); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QList<QVariant> arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), i); } @@ -559,37 +539,37 @@ void tst_QWizard::addPage() QVERIFY(!wizard.page(N + 1)); wizard.setPage(N + 50, new QWizardPage); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QList<QVariant> arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), N + 50); wizard.setPage(-3000, new QWizardPage); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), -3000); QWizardPage *pageX = new QWizardPage; QCOMPARE(wizard.addPage(pageX), N + 51); QCOMPARE(wizard.page(N + 51), pageX); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), N + 51); QCOMPARE(wizard.addPage(new QWizardPage), N + 52); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), N + 52); QTest::ignoreMessage(QtWarningMsg,"QWizard::setPage: Cannot insert null page"); wizard.addPage(0); // generates a warning - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); delete parent; } #define CHECK_VISITED(wizard, list) \ do { \ - QList<int> myList = list; \ + const QList<int> myList = list; \ QCOMPARE((wizard).visitedIds(), myList); \ - Q_FOREACH(int id, myList) \ + for (int id : myList) \ QVERIFY((wizard).hasVisitedPage(id)); \ } while (0) @@ -608,7 +588,7 @@ void tst_QWizard::setPage() page = new QWizardPage(parent); QTest::ignoreMessage(QtWarningMsg,"QWizard::setPage: Cannot insert page with ID -1"); wizard.setPage(-1, page); // gives a warning and does nothing - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QVERIFY(!wizard.page(-2)); QVERIFY(!wizard.page(-1)); QVERIFY(!wizard.page(0)); @@ -620,7 +600,7 @@ void tst_QWizard::setPage() page = new QWizardPage(parent); wizard.setPage(0, page); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QList<QVariant> arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), 0); QCOMPARE(page->window(), (QWidget *)&wizard); @@ -633,7 +613,7 @@ void tst_QWizard::setPage() page = new QWizardPage(parent); wizard.setPage(-2, page); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), -2); QCOMPARE(page->window(), (QWidget *)&wizard); @@ -654,7 +634,7 @@ void tst_QWizard::setPage() page = new QWizardPage(parent); wizard.setPage(2, page); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), 2); QCOMPARE(wizard.page(2), page); @@ -673,7 +653,7 @@ void tst_QWizard::setPage() page = new QWizardPage(parent); wizard.setPage(-3, page); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), -3); QCOMPARE(wizard.page(-3), page); @@ -744,7 +724,7 @@ void tst_QWizard::setPage() QCOMPARE(wizard.nextId(), -2); CHECK_VISITED(wizard, QList<int>() << -3); } - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); delete parent; } @@ -1002,7 +982,7 @@ void tst_QWizard::setOption_IgnoreSubTitles() // Check that subtitles are shown when they should (i.e., // they're set and IgnoreSubTitles is off). - qApp->setActiveWindow(0); // ensure no focus rectangle around cancel button + QApplicationPrivate::setActiveWindow(0); // ensure no focus rectangle around cancel button QImage i11 = grabWidget(&wizard1); QImage i21 = grabWidget(&wizard2); QVERIFY(i11 != i21); @@ -1603,9 +1583,9 @@ class SetPage : public Operation QString describe() const override { return QLatin1String("set page ") + QString::number(page); } int page; public: - static QSharedPointer<SetPage> create(int page) + static std::shared_ptr<SetPage> create(int page) { - QSharedPointer<SetPage> o = QSharedPointer<SetPage>::create(); + std::shared_ptr<SetPage> o = std::make_shared<SetPage>(); o->page = page; return o; } @@ -1617,9 +1597,9 @@ class SetStyle : public Operation QString describe() const override { return QLatin1String("set style ") + QString::number(style); } QWizard::WizardStyle style; public: - static QSharedPointer<SetStyle> create(QWizard::WizardStyle style) + static std::shared_ptr<SetStyle> create(QWizard::WizardStyle style) { - QSharedPointer<SetStyle> o = QSharedPointer<SetStyle>::create(); + std::shared_ptr<SetStyle> o = std::make_shared<SetStyle>(); o->style = style; return o; } @@ -1632,9 +1612,9 @@ class SetOption : public Operation QWizard::WizardOption option; bool on; public: - static QSharedPointer<SetOption> create(QWizard::WizardOption option, bool on) + static std::shared_ptr<SetOption> create(QWizard::WizardOption option, bool on) { - QSharedPointer<SetOption> o = QSharedPointer<SetOption>::create(); + std::shared_ptr<SetOption> o = std::make_shared<SetOption>(); o->option = option; o->on = on; return o; @@ -1663,8 +1643,8 @@ class OptionInfo tags[QWizard::HaveCustomButton3] = "15/CB3"; for (int i = 0; i < 2; ++i) { - QMap<QWizard::WizardOption, QSharedPointer<Operation> > operations_; - foreach (QWizard::WizardOption option, tags.keys()) + QMap<QWizard::WizardOption, std::shared_ptr<Operation> > operations_; + for (const auto &[option, _] : std::as_const(tags).asKeyValueRange()) operations_[option] = SetOption::create(option, i == 1); operations << operations_; } @@ -1672,7 +1652,7 @@ class OptionInfo OptionInfo(OptionInfo const&); OptionInfo& operator=(OptionInfo const&); QMap<QWizard::WizardOption, QString> tags; - QList<QMap<QWizard::WizardOption, QSharedPointer<Operation> > > operations; + QList<QMap<QWizard::WizardOption, std::shared_ptr<Operation> > > operations; public: static OptionInfo &instance() { @@ -1681,7 +1661,7 @@ public: } QString tag(QWizard::WizardOption option) const { return tags.value(option); } - QSharedPointer<Operation> operation(QWizard::WizardOption option, bool on) const + std::shared_ptr<Operation> operation(QWizard::WizardOption option, bool on) const { return operations.at(on).value(option); } QList<QWizard::WizardOption> options() const { return tags.keys(); } }; @@ -1692,7 +1672,7 @@ QString SetOption::describe() const + QLatin1Char(on ? '1' : '0'); } -Q_DECLARE_METATYPE(QList<QSharedPointer<Operation>>) +Q_DECLARE_METATYPE(QList<std::shared_ptr<Operation>>) class TestGroup { @@ -1709,7 +1689,7 @@ public: combinations.clear(); } - QList<QSharedPointer<Operation>> &add() + QList<std::shared_ptr<Operation>> &add() { combinations.resize(combinations.size() + 1); return combinations.last(); @@ -1717,7 +1697,7 @@ public: void createTestRows() { - for (int i = 0; i < combinations.count(); ++i) { + for (int i = 0; i < combinations.size(); ++i) { QTest::newRow((name.toLatin1() + ", row " + QByteArray::number(i)).constData()) << (i == 0) << (type == Equality) << combinations.at(i); ++nRows_; @@ -1730,7 +1710,7 @@ private: QString name; Type type; int nRows_; - QList<QList<QSharedPointer<Operation>>> combinations; + QList<QList<std::shared_ptr<Operation>>> combinations; }; class IntroPage : public QWizardPage @@ -1807,16 +1787,16 @@ public: ~TestWizard() { - foreach (int id, pageIds) { + for (int id : std::as_const(pageIds)) { QWizardPage *page_to_delete = page(id); removePage(id); delete page_to_delete; } } - void applyOperations(const QList<QSharedPointer<Operation>> &operations) + void applyOperations(const QList<std::shared_ptr<Operation>> &operations) { - foreach (const QSharedPointer<Operation> &op, operations) { + for (const std::shared_ptr<Operation> &op : operations) { if (op) { op->apply(this); opsDescr += QLatin1Char('(') + op->describe() + QLatin1String(") "); @@ -1836,25 +1816,31 @@ public: class CombinationsTestData { TestGroup testGroup; - QList<QSharedPointer<Operation>> pageOps; - QList<QSharedPointer<Operation>> styleOps; - QMap<bool, QList<QSharedPointer<Operation>>> setAllOptions; + const std::shared_ptr<Operation> pageOps[3] = { + SetPage::create(0), + SetPage::create(1), + SetPage::create(2), + }; + const std::shared_ptr<Operation> styleOps[3] = { + SetStyle::create(QWizard::ClassicStyle), + SetStyle::create(QWizard::ModernStyle), + SetStyle::create(QWizard::MacStyle), + }; + QMap<bool, QList<std::shared_ptr<Operation>>> setAllOptions; public: CombinationsTestData() { QTest::addColumn<bool>("ref"); QTest::addColumn<bool>("testEquality"); - QTest::addColumn<QList<QSharedPointer<Operation>>>("operations"); - pageOps << SetPage::create(0) << SetPage::create(1) << SetPage::create(2); - styleOps << SetStyle::create(QWizard::ClassicStyle) << SetStyle::create(QWizard::ModernStyle) - << SetStyle::create(QWizard::MacStyle); -#define SETPAGE(page) pageOps.at(page) -#define SETSTYLE(style) styleOps.at(style) + QTest::addColumn<QList<std::shared_ptr<Operation>>>("operations"); +#define SETPAGE(page) pageOps[page] +#define SETSTYLE(style) styleOps[style] #define OPT(option, on) OptionInfo::instance().operation(option, on) #define CLROPT(option) OPT(option, false) #define SETOPT(option) OPT(option, true) - foreach (QWizard::WizardOption option, OptionInfo::instance().options()) { + const auto options = OptionInfo::instance().options(); + for (QWizard::WizardOption option : options) { setAllOptions[false] << CLROPT(option); setAllOptions[true] << SETOPT(option); } @@ -1911,7 +1897,7 @@ public: testGroup.createTestRows(); for (int i = 0; i < 2; ++i) { - QList<QSharedPointer<Operation>> setOptions = setAllOptions.value(i == 1); + QList<std::shared_ptr<Operation>> setOptions = setAllOptions.value(i == 1); testGroup.reset("testAll 3.1"); testGroup.add() << setOptions; @@ -1928,21 +1914,22 @@ public: testGroup.createTestRows(); } - foreach (const QSharedPointer<Operation> &pageOp, pageOps) { + for (const std::shared_ptr<Operation> &pageOp : pageOps) { testGroup.reset("testAll 4.1"); testGroup.add() << pageOp; testGroup.add() << pageOp << pageOp; testGroup.createTestRows(); for (int i = 0; i < 2; ++i) { - QList<QSharedPointer<Operation>> optionOps = setAllOptions.value(i == 1); + QList<std::shared_ptr<Operation>> optionOps = setAllOptions.value(i == 1); testGroup.reset("testAll 4.2"); testGroup.add() << optionOps << pageOp; testGroup.add() << pageOp << optionOps; testGroup.createTestRows(); - foreach (QWizard::WizardOption option, OptionInfo::instance().options()) { - QSharedPointer<Operation> optionOp = OPT(option, i == 1); + const auto options = OptionInfo::instance().options(); + for (QWizard::WizardOption option : options) { + std::shared_ptr<Operation> optionOp = OPT(option, i == 1); testGroup.reset("testAll 4.3"); testGroup.add() << optionOp << pageOp; testGroup.add() << pageOp << optionOp; @@ -1951,21 +1938,22 @@ public: } } - foreach (const QSharedPointer<Operation> &styleOp, styleOps) { + for (const std::shared_ptr<Operation> &styleOp : styleOps) { testGroup.reset("testAll 5.1"); testGroup.add() << styleOp; testGroup.add() << styleOp << styleOp; testGroup.createTestRows(); for (int i = 0; i < 2; ++i) { - QList<QSharedPointer<Operation>> optionOps = setAllOptions.value(i == 1); + QList<std::shared_ptr<Operation>> optionOps = setAllOptions.value(i == 1); testGroup.reset("testAll 5.2"); testGroup.add() << optionOps << styleOp; testGroup.add() << styleOp << optionOps; testGroup.createTestRows(); - foreach (QWizard::WizardOption option, OptionInfo::instance().options()) { - QSharedPointer<Operation> optionOp = OPT(option, i == 1); + const auto options = OptionInfo::instance().options(); + for (QWizard::WizardOption option : options) { + std::shared_ptr<Operation> optionOp = OPT(option, i == 1); testGroup.reset("testAll 5.3"); testGroup.add() << optionOp << styleOp; testGroup.add() << styleOp << optionOp; @@ -1974,8 +1962,8 @@ public: } } - foreach (const QSharedPointer<Operation> &pageOp, pageOps) { - foreach (const QSharedPointer<Operation> &styleOp, styleOps) { + for (const std::shared_ptr<Operation> &pageOp : pageOps) { + for (const std::shared_ptr<Operation> &styleOp : styleOps) { testGroup.reset("testAll 6.1"); testGroup.add() << pageOp; @@ -1993,7 +1981,7 @@ public: testGroup.createTestRows(); for (int i = 0; i < 2; ++i) { - QList<QSharedPointer<Operation>> optionOps = setAllOptions.value(i == 1); + QList<std::shared_ptr<Operation>> optionOps = setAllOptions.value(i == 1); testGroup.reset("testAll 6.4"); testGroup.add() << optionOps << pageOp << styleOp; testGroup.add() << pageOp << optionOps << styleOp; @@ -2003,8 +1991,9 @@ public: testGroup.add() << styleOp << pageOp << optionOps; testGroup.createTestRows(); - foreach (QWizard::WizardOption option, OptionInfo::instance().options()) { - QSharedPointer<Operation> optionOp = OPT(option, i == 1); + const auto options = OptionInfo::instance().options(); + for (QWizard::WizardOption option : options) { + std::shared_ptr<Operation> optionOp = OPT(option, i == 1); testGroup.reset("testAll 6.5"); testGroup.add() << optionOp << pageOp << styleOp; testGroup.add() << pageOp << optionOp << styleOp; @@ -2066,7 +2055,7 @@ void tst_QWizard::combinations() { QFETCH(bool, ref); QFETCH(bool, testEquality); - QFETCH(QList<QSharedPointer<Operation>>, operations); + QFETCH(const QList<std::shared_ptr<Operation>>, operations); TestWizard wizard; #if !defined(QT_NO_STYLE_WINDOWSVISTA) @@ -2135,7 +2124,7 @@ public: QList<WizardPage *> shown() const { QList<WizardPage *> result; - foreach (WizardPage *page, pages) + for (WizardPage *page : pages) if (page->shown()) result << page; return result; @@ -2153,19 +2142,19 @@ void tst_QWizard::showCurrentPageOnly() wizard.show(); - QCOMPARE(pages.shown().count(), 1); + QCOMPARE(pages.shown().size(), 1); QCOMPARE(pages.shown().first(), pages.all().first()); const int steps = 2; for (int i = 0; i < steps; ++i) wizard.next(); - QCOMPARE(pages.shown().count(), 1); + QCOMPARE(pages.shown().size(), 1); QCOMPARE(pages.shown().first(), pages.all().at(steps)); wizard.restart(); - QCOMPARE(pages.shown().count(), 1); + QCOMPARE(pages.shown().size(), 1); QCOMPARE(pages.shown().first(), pages.all().first()); } @@ -2297,36 +2286,36 @@ void tst_QWizard::removePage() wizard.restart(); QCOMPARE(wizard.pageIds().size(), 4); QCOMPARE(wizard.visitedIds().size(), 1); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); // Removing a non-existent page wizard.removePage(4); QCOMPARE(wizard.pageIds().size(), 4); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); // Removing and then reinserting a page QCOMPARE(wizard.pageIds().size(), 4); QVERIFY(wizard.pageIds().contains(2)); wizard.removePage(2); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QList<QVariant> arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), 2); QCOMPARE(wizard.pageIds().size(), 3); QVERIFY(!wizard.pageIds().contains(2)); wizard.setPage(2, page2); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QCOMPARE(wizard.pageIds().size(), 4); QVERIFY(wizard.pageIds().contains(2)); // Removing the same page twice wizard.removePage(2); // restore - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), 2); QCOMPARE(wizard.pageIds().size(), 3); QVERIFY(!wizard.pageIds().contains(2)); wizard.removePage(2); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QCOMPARE(wizard.pageIds().size(), 3); QVERIFY(!wizard.pageIds().contains(2)); @@ -2336,9 +2325,9 @@ void tst_QWizard::removePage() wizard.next(); QCOMPARE(wizard.visitedIds().size(), 2); QCOMPARE(wizard.currentPage(), page1); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); wizard.removePage(2); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), 2); QCOMPARE(wizard.visitedIds().size(), 2); @@ -2349,11 +2338,11 @@ void tst_QWizard::removePage() wizard.setPage(2, page2); // restore wizard.restart(); wizard.next(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QCOMPARE(wizard.visitedIds().size(), 2); QCOMPARE(wizard.currentPage(), page1); wizard.removePage(0); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), 0); QCOMPARE(wizard.visitedIds().size(), 1); @@ -2365,11 +2354,11 @@ void tst_QWizard::removePage() wizard.setPage(0, page0); // restore wizard.restart(); wizard.next(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QCOMPARE(wizard.visitedIds().size(), 2); QCOMPARE(wizard.currentPage(), page1); wizard.removePage(1); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), 1); QCOMPARE(wizard.visitedIds().size(), 1); @@ -2379,7 +2368,7 @@ void tst_QWizard::removePage() // Remove the current page which is the first (and only) one in the history wizard.removePage(0); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), 0); QCOMPARE(wizard.visitedIds().size(), 1); @@ -2389,7 +2378,7 @@ void tst_QWizard::removePage() QCOMPARE(wizard.currentPage(), page2); // wizard.removePage(2); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), 2); QCOMPARE(wizard.visitedIds().size(), 1); @@ -2399,7 +2388,7 @@ void tst_QWizard::removePage() QCOMPARE(wizard.currentPage(), page3); // wizard.removePage(3); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), 3); QVERIFY(wizard.visitedIds().empty()); @@ -2587,7 +2576,8 @@ void tst_QWizard::task161658_alignments() wizard.show(); QVERIFY(QTest::qWaitForWindowExposed(&wizard)); - foreach (QLabel *subtitleLabel, wizard.findChildren<QLabel *>()) { + const auto subtitleLabels = wizard.findChildren<QLabel *>(); + for (QLabel *subtitleLabel : subtitleLabels) { if (subtitleLabel->text().startsWith("SUBTITLE#")) { QCOMPARE(lineEdit1.mapToGlobal(lineEdit1.contentsRect().bottomRight()).x(), subtitleLabel->mapToGlobal(subtitleLabel->contentsRect().bottomRight()).x()); @@ -2720,6 +2710,45 @@ void tst_QWizard::taskQTBUG_46894_nextButtonShortcut() } } +/* setCurrentId(int) method was added in QTBUG99488 */ +void tst_QWizard::changePages() +{ + QWizard wizard; + + QList<QWizardPage*> pages; + for (int i = 0; i < 4; ++i) { + QWizardPage *page = new QWizardPage; + wizard.addPage(page); + pages.append(page); + } + + wizard.show(); + QVERIFY(QTest::qWaitForWindowExposed(&wizard)); + + // Verify default page + QCOMPARE(wizard.currentPage(), pages.at(0)); + + wizard.next(); + QVERIFY(wizard.currentId() == 1); + wizard.back(); + QVERIFY(wizard.currentId() == 0); + + // Test illegal page + QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QWizard::setCurrentId: No such page: 5"); + wizard.setCurrentId(5); + QCOMPARE(wizard.currentId(), 0); + + for (int i = 0; i < 4; ++i) { + wizard.setCurrentId(i); + QCOMPARE(wizard.currentPage(), pages.at(i)); + } + + for (int i = 3; i >= 0; --i) { + wizard.setCurrentId(i); + QCOMPARE(wizard.currentPage(), pages.at(i)); + } +} + #endif // QT_CONFIG(shortcut) QTEST_MAIN(tst_QWizard) diff --git a/tests/auto/widgets/dialogs/qwizard/tst_qwizard_2.cpp b/tests/auto/widgets/dialogs/qwizard/tst_qwizard_2.cpp index 1b8ef9ad0f..8eef99ff38 100644 --- a/tests/auto/widgets/dialogs/qwizard/tst_qwizard_2.cpp +++ b/tests/auto/widgets/dialogs/qwizard/tst_qwizard_2.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QComboBox> diff --git a/tests/auto/widgets/effects/CMakeLists.txt b/tests/auto/widgets/effects/CMakeLists.txt index 3c0af4537a..fdc82023fb 100644 --- a/tests/auto/widgets/effects/CMakeLists.txt +++ b/tests/auto/widgets/effects/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from effects.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause add_subdirectory(qgraphicseffect) add_subdirectory(qpixmapfilter) diff --git a/tests/auto/widgets/effects/qgraphicseffect/BLACKLIST b/tests/auto/widgets/effects/qgraphicseffect/BLACKLIST index 64a92d18aa..69367f06fd 100644 --- a/tests/auto/widgets/effects/qgraphicseffect/BLACKLIST +++ b/tests/auto/widgets/effects/qgraphicseffect/BLACKLIST @@ -1,2 +1,2 @@ -[prepareGeometryChangeInvalidateCache] -opensuse-42.3 +[draw] +wayland diff --git a/tests/auto/widgets/effects/qgraphicseffect/CMakeLists.txt b/tests/auto/widgets/effects/qgraphicseffect/CMakeLists.txt index a88859681b..e9d32b1a8d 100644 --- a/tests/auto/widgets/effects/qgraphicseffect/CMakeLists.txt +++ b/tests/auto/widgets/effects/qgraphicseffect/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qgraphicseffect.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgraphicseffect Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgraphicseffect LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgraphicseffect SOURCES tst_qgraphicseffect.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/effects/qgraphicseffect/tst_qgraphicseffect.cpp b/tests/auto/widgets/effects/qgraphicseffect/tst_qgraphicseffect.cpp index 16c4ba6058..3d1988e5da 100644 --- a/tests/auto/widgets/effects/qgraphicseffect/tst_qgraphicseffect.cpp +++ b/tests/auto/widgets/effects/qgraphicseffect/tst_qgraphicseffect.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTestWidgets> @@ -379,7 +354,9 @@ void tst_QGraphicsEffect::draw() // Effect is already enabled; nothing should happen. effect->setEnabled(true); - QTest::qWait(50); + // Send only posted events, not window system events, + // so that we don't get any spontaneous paint events. + QCoreApplication::sendPostedEvents(); QCOMPARE(effect->numRepaints, 0); QCOMPARE(item->numRepaints, 0); @@ -694,6 +671,7 @@ void tst_QGraphicsEffect::prepareGeometryChangeInvalidateCache() QGraphicsView view(&scene); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); + QTRY_VERIFY(view.windowHandle()->isActive()); QTRY_VERIFY(item->nbPaint >= 1); item->nbPaint = 0; diff --git a/tests/auto/widgets/effects/qpixmapfilter/CMakeLists.txt b/tests/auto/widgets/effects/qpixmapfilter/CMakeLists.txt index 733a45b334..c19e6c7cfd 100644 --- a/tests/auto/widgets/effects/qpixmapfilter/CMakeLists.txt +++ b/tests/auto/widgets/effects/qpixmapfilter/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qpixmapfilter.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qpixmapfilter Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qpixmapfilter LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qpixmapfilter SOURCES tst_qpixmapfilter.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::GuiPrivate Qt::Widgets diff --git a/tests/auto/widgets/effects/qpixmapfilter/tst_qpixmapfilter.cpp b/tests/auto/widgets/effects/qpixmapfilter/tst_qpixmapfilter.cpp index 10e6ec63e6..39087aeaaf 100644 --- a/tests/auto/widgets/effects/qpixmapfilter/tst_qpixmapfilter.cpp +++ b/tests/auto/widgets/effects/qpixmapfilter/tst_qpixmapfilter.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/graphicsview/CMakeLists.txt b/tests/auto/widgets/graphicsview/CMakeLists.txt index 5e6fddccce..b0ea73b65e 100644 --- a/tests/auto/widgets/graphicsview/CMakeLists.txt +++ b/tests/auto/widgets/graphicsview/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from graphicsview.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause if(QT_FEATURE_private_tests) add_subdirectory(qgraphicsanchorlayout) @@ -17,20 +18,6 @@ add_subdirectory(qgraphicsobject) add_subdirectory(qgraphicspixmapitem) add_subdirectory(qgraphicspolygonitem) add_subdirectory(qgraphicstransform) -if(QT_FEATURE_style_fusion) - # special case begin - # These tests are never run in coin with qmake since the statement - # !qtConfig(style-fusion): always passes causing the subdirs to be removed. - # For these tests to be generated the .pro file needs to be patched and - # the above line changed to !qtConfig(style-fusion). That being said, - # one of the test fails and needs to be corrected by someone with know how in - # that area. Until then we will disable them to mimic the behavior seen in - # coin. - #add_subdirectory(qgraphicsproxywidget) - #add_subdirectory(qgraphicswidget) - # special case end -endif() -# QTBUG-87671 # special case -if(NOT ANDROID) - add_subdirectory(qgraphicsview) -endif() +add_subdirectory(qgraphicsproxywidget) +add_subdirectory(qgraphicswidget) +add_subdirectory(qgraphicsview) diff --git a/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/BLACKLIST b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/BLACKLIST index ce0e42b3c7..fde971443d 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/BLACKLIST +++ b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/BLACKLIST @@ -1,2 +1,2 @@ [layoutDirection] -ubuntu-20.04 +ubuntu-22.04 diff --git a/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/CMakeLists.txt b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/CMakeLists.txt index 1d1e576299..2b530d39f3 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/CMakeLists.txt +++ b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qgraphicsanchorlayout.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgraphicsanchorlayout Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgraphicsanchorlayout LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgraphicsanchorlayout SOURCES tst_qgraphicsanchorlayout.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index 92cffa0226..cf37aa5639 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QtWidgets/qgraphicsanchorlayout.h> diff --git a/tests/auto/widgets/graphicsview/qgraphicsanchorlayout1/CMakeLists.txt b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout1/CMakeLists.txt index 11d2767c9e..62235c8d1b 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsanchorlayout1/CMakeLists.txt +++ b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout1/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qgraphicsanchorlayout1.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgraphicsanchorlayout1 Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgraphicsanchorlayout1 LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgraphicsanchorlayout1 SOURCES tst_qgraphicsanchorlayout1.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/graphicsview/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp index 9e0293d875..9a047b876d 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtGui> #include <QTest> @@ -1688,7 +1663,7 @@ void tst_QGraphicsAnchorLayout1::testBasicLayout() // Determine amount of widgets to add. int widgetCount = -1; - for (int i = 0; i < data.count(); ++i) { + for (int i = 0; i < data.size(); ++i) { const BasicLayoutTestData item = data[i]; widgetCount = qMax(widgetCount, item.firstIndex); widgetCount = qMax(widgetCount, item.secondIndex); @@ -1703,7 +1678,7 @@ void tst_QGraphicsAnchorLayout1::testBasicLayout() // Setup anchor layout TheAnchorLayout *layout = new TheAnchorLayout; - for (int i = 0; i < data.count(); ++i) { + for (int i = 0; i < data.size(); ++i) { const BasicLayoutTestData item = data[i]; layout->setAnchor( getItem(item.firstIndex, widgets, layout), @@ -1720,7 +1695,7 @@ void tst_QGraphicsAnchorLayout1::testBasicLayout() QCOMPARE(widget->size(), size); // Validate - for (int i = 0; i < result.count(); ++i) { + for (int i = 0; i < result.size(); ++i) { const BasicLayoutTestResult item = result[i]; QRectF expected = item.rect; QRectF actual = widgets[item.index]->geometry(); @@ -1732,7 +1707,7 @@ void tst_QGraphicsAnchorLayout1::testBasicLayout() widget->setLayoutDirection(Qt::RightToLeft); layout->activate(); // Validate - for (int j = 0; j < result.count(); ++j) { + for (int j = 0; j < result.size(); ++j) { const BasicLayoutTestResult item = result[j]; QRectF mirroredRect(item.rect); // only valid cases are mirrored @@ -2193,7 +2168,7 @@ void tst_QGraphicsAnchorLayout1::testRemoveCenterAnchor() // Determine amount of widgets to add. int widgetCount = -1; - for (int i = 0; i < data.count(); ++i) { + for (int i = 0; i < data.size(); ++i) { const BasicLayoutTestData item = data[i]; widgetCount = qMax(widgetCount, item.firstIndex); widgetCount = qMax(widgetCount, item.secondIndex); @@ -2210,7 +2185,7 @@ void tst_QGraphicsAnchorLayout1::testRemoveCenterAnchor() // Setup anchor layout TheAnchorLayout *layout = new TheAnchorLayout; - for (int i = 0; i < data.count(); ++i) { + for (int i = 0; i < data.size(); ++i) { const BasicLayoutTestData item = data[i]; layout->setAnchor( getItem(item.firstIndex, widgets, layout), @@ -2220,7 +2195,7 @@ void tst_QGraphicsAnchorLayout1::testRemoveCenterAnchor() item.spacing ); } - for (int i = 0; i < removeData.count(); ++i) { + for (int i = 0; i < removeData.size(); ++i) { const BasicLayoutTestData item = removeData[i]; layout->removeAnchor( getItem(item.firstIndex, widgets, layout), @@ -2236,7 +2211,7 @@ void tst_QGraphicsAnchorLayout1::testRemoveCenterAnchor() QCOMPARE(widget->size(), size); // Validate - for (int i = 0; i < result.count(); ++i) { + for (int i = 0; i < result.size(); ++i) { const BasicLayoutTestResult item = result[i]; QCOMPARE(widgets[item.index]->geometry(), item.rect); @@ -3024,7 +2999,7 @@ void tst_QGraphicsAnchorLayout1::testComplexCases() // Determine amount of widgets to add. int widgetCount = -1; - for (int i = 0; i < data.count(); ++i) { + for (int i = 0; i < data.size(); ++i) { const BasicLayoutTestData item = data[i]; widgetCount = qMax(widgetCount, item.firstIndex); widgetCount = qMax(widgetCount, item.secondIndex); @@ -3050,7 +3025,7 @@ void tst_QGraphicsAnchorLayout1::testComplexCases() // Setup anchor layout TheAnchorLayout *layout = new TheAnchorLayout; - for (int i = 0; i < data.count(); ++i) { + for (int i = 0; i < data.size(); ++i) { const BasicLayoutTestData item = data[i]; layout->setAnchor( getItem(item.firstIndex, widgets, layout), @@ -3067,7 +3042,7 @@ void tst_QGraphicsAnchorLayout1::testComplexCases() QCOMPARE(widget->size(), size); // Validate - for (int i = 0; i < result.count(); ++i) { + for (int i = 0; i < result.size(); ++i) { const BasicLayoutTestResult item = result[i]; QCOMPARE(widgets[item.index]->geometry(), item.rect); } @@ -3076,7 +3051,7 @@ void tst_QGraphicsAnchorLayout1::testComplexCases() widget->setLayoutDirection(Qt::RightToLeft); layout->activate(); // Validate - for (int j = 0; j < result.count(); ++j) { + for (int j = 0; j < result.size(); ++j) { const BasicLayoutTestResult item = result[j]; QRectF mirroredRect(item.rect); // only valid cases are mirrored diff --git a/tests/auto/widgets/graphicsview/qgraphicseffectsource/CMakeLists.txt b/tests/auto/widgets/graphicsview/qgraphicseffectsource/CMakeLists.txt index 5a4fda8234..1e198d7baa 100644 --- a/tests/auto/widgets/graphicsview/qgraphicseffectsource/CMakeLists.txt +++ b/tests/auto/widgets/graphicsview/qgraphicseffectsource/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qgraphicseffectsource.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgraphicseffectsource Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgraphicseffectsource LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgraphicseffectsource SOURCES tst_qgraphicseffectsource.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/graphicsview/qgraphicseffectsource/tst_qgraphicseffectsource.cpp b/tests/auto/widgets/graphicsview/qgraphicseffectsource/tst_qgraphicseffectsource.cpp index ba8a309674..28c439dbcd 100644 --- a/tests/auto/widgets/graphicsview/qgraphicseffectsource/tst_qgraphicseffectsource.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicseffectsource/tst_qgraphicseffectsource.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QtWidgets/qgraphicseffect.h> @@ -179,11 +154,11 @@ void tst_QGraphicsEffectSource::init() QVERIFY(effect); QVERIFY(item); QVERIFY(effect->source()); - effect->reset(); effect->storeDeviceDependentStuff = false; effect->doNothingInDraw = false; - item->reset(); QCoreApplication::processEvents(); // Process all queued paint events + effect->reset(); + item->reset(); } void tst_QGraphicsEffectSource::graphicsItem() diff --git a/tests/auto/widgets/graphicsview/qgraphicsgridlayout/CMakeLists.txt b/tests/auto/widgets/graphicsview/qgraphicsgridlayout/CMakeLists.txt index 0554d62807..c4ffe216f5 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsgridlayout/CMakeLists.txt +++ b/tests/auto/widgets/graphicsview/qgraphicsgridlayout/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qgraphicsgridlayout.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgraphicsgridlayout Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgraphicsgridlayout LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgraphicsgridlayout SOURCES tst_qgraphicsgridlayout.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/graphicsview/qgraphicsgridlayout/tst_qgraphicsgridlayout.cpp b/tests/auto/widgets/graphicsview/qgraphicsgridlayout/tst_qgraphicsgridlayout.cpp index effd8a28c1..c91a0803ee 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsgridlayout/tst_qgraphicsgridlayout.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsgridlayout/tst_qgraphicsgridlayout.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -1683,7 +1658,7 @@ void tst_QGraphicsGridLayout::sizeHint() widget->setContentsMargins(0, 0, 0, 0); int i; - for (i = 0; i < itemDescriptions.count(); ++i) { + for (i = 0; i < itemDescriptions.size(); ++i) { ItemDesc desc = itemDescriptions.at(i); RectWidget *item = new RectWidget(widget); desc.apply(layout, item); @@ -2184,7 +2159,7 @@ void tst_QGraphicsGridLayout::defaultStretchFactors() widget->setContentsMargins(0, 0, 0, 0); int i; - for (i = 0; i < itemDescriptions.count(); ++i) { + for (i = 0; i < itemDescriptions.size(); ++i) { ItemDesc desc = itemDescriptions.at(i); RectWidget *item = new RectWidget(widget); desc.apply(layout, item); @@ -2199,7 +2174,7 @@ void tst_QGraphicsGridLayout::defaultStretchFactors() widget->resize(newSize); QApplication::sendPostedEvents(0, 0); - for (i = 0; i < expectedSizes.count(); ++i) { + for (i = 0; i < expectedSizes.size(); ++i) { QSizeF itemSize = layout->itemAt(i)->geometry().size(); QCOMPARE(itemSize, expectedSizes.at(i)); } @@ -2343,7 +2318,7 @@ void tst_QGraphicsGridLayout::alignment2() widget->setContentsMargins(0, 0, 0, 0); int i; - for (i = 0; i < itemDescriptions.count(); ++i) { + for (i = 0; i < itemDescriptions.size(); ++i) { ItemDesc desc = itemDescriptions.at(i); RectWidget *item = new RectWidget(widget); desc.apply(layout, item); @@ -2358,7 +2333,7 @@ void tst_QGraphicsGridLayout::alignment2() widget->resize(newSize); QApplication::sendPostedEvents(0, 0); - for (i = 0; i < expectedGeometries.count(); ++i) { + for (i = 0; i < expectedGeometries.size(); ++i) { QRectF itemRect = layout->itemAt(i)->geometry(); QCOMPARE(itemRect, expectedGeometries.at(i)); } @@ -2921,7 +2896,7 @@ void tst_QGraphicsGridLayout::geometries() widget->setContentsMargins(0, 0, 0, 0); int i; - for (i = 0; i < itemDescriptions.count(); ++i) { + for (i = 0; i < itemDescriptions.size(); ++i) { ItemDesc desc = itemDescriptions.at(i); RectWidget *item = new RectWidget(widget); desc.apply(layout, item); @@ -2936,7 +2911,7 @@ void tst_QGraphicsGridLayout::geometries() widget->resize(newSize); QApplication::processEvents(); - for (i = 0; i < expectedGeometries.count(); ++i) { + for (i = 0; i < expectedGeometries.size(); ++i) { QRectF itemRect = layout->itemAt(i)->geometry(); QCOMPARE(itemRect, expectedGeometries.at(i)); } @@ -3181,7 +3156,6 @@ void tst_QGraphicsGridLayout::heightForWidthWithSpanning() QCOMPARE(layout->effectiveSizeHint(Qt::MaximumSize, QSizeF(200, -1)), QSizeF(200, 100)); } -Q_DECLARE_METATYPE(QSizePolicy::Policy) void tst_QGraphicsGridLayout::spanningItem2x2_data() { QTest::addColumn<QSizePolicy::Policy>("sizePolicy"); diff --git a/tests/auto/widgets/graphicsview/qgraphicsitem/BLACKLIST b/tests/auto/widgets/graphicsview/qgraphicsitem/BLACKLIST index 31dbb38a5d..c3de568a24 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsitem/BLACKLIST +++ b/tests/auto/widgets/graphicsview/qgraphicsitem/BLACKLIST @@ -1,4 +1,6 @@ # QTBUG-74760 [sorting] -opensuse-42.3 osx +# QTBUG-115293 +[itemUsesExtendedStyleOption] +wayland diff --git a/tests/auto/widgets/graphicsview/qgraphicsitem/CMakeLists.txt b/tests/auto/widgets/graphicsview/qgraphicsitem/CMakeLists.txt index 02a667eb9c..3ee507cd46 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsitem/CMakeLists.txt +++ b/tests/auto/widgets/graphicsview/qgraphicsitem/CMakeLists.txt @@ -1,15 +1,22 @@ -# Generated from qgraphicsitem.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgraphicsitem Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgraphicsitem LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgraphicsitem SOURCES tst_qgraphicsitem.cpp DEFINES QT_NO_CAST_TO_ASCII - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate @@ -22,6 +29,6 @@ qt_internal_add_test(tst_qgraphicsitem ##################################################################### qt_internal_extend_target(tst_qgraphicsitem CONDITION WIN32 - PUBLIC_LIBRARIES + LIBRARIES user32 ) diff --git a/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp index 81e95cc3df..35356adcfc 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -63,6 +38,10 @@ #include <QSignalSpy> #include <QTimer> +#include <QtGui/private/qeventpoint_p.h> + +#include <QtWidgets/private/qapplication_p.h> + using AbstractGraphicsShapeItemPtr = QSharedPointer<QAbstractGraphicsShapeItem>; using GraphicsItems = QList<QGraphicsItem *>; using GraphicsItemsList = QList<QGraphicsItem *>; @@ -74,7 +53,7 @@ Q_DECLARE_METATYPE(QSizeF) Q_DECLARE_METATYPE(QTransform) #if defined(Q_OS_WIN) -#include <windows.h> +#include <qt_windows.h> #define Q_CHECK_PAINTEVENTS \ if (::SwitchDesktop(::GetThreadDesktop(::GetCurrentThreadId())) == 0) \ QSKIP("The Graphics View doesn't get the paint events"); @@ -1014,7 +993,6 @@ void tst_QGraphicsItem::inputMethodHints() scene.addItem(item); scene.addItem(item2); QGraphicsView view(&scene); - QApplication::setActiveWindow(&view); view.setWindowTitle(QLatin1String(QTest::currentTestFunction())); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); @@ -1071,7 +1049,6 @@ void tst_QGraphicsItem::toolTip() view.setWindowTitle(QLatin1String(QTest::currentTestFunction())); view.setFixedSize(200, 200); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); { @@ -2229,7 +2206,7 @@ void tst_QGraphicsItem::setTransform() scene.update(scene.sceneRect()); QCoreApplication::processEvents(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); item.setTransform(QTransform().rotate(qreal(12.34))); QRectF rotatedRect = scene.sceneRect(); @@ -2237,14 +2214,14 @@ void tst_QGraphicsItem::setTransform() scene.update(scene.sceneRect()); QCoreApplication::processEvents(); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); item.setTransform(QTransform()); scene.update(scene.sceneRect()); QCoreApplication::processEvents(); - QCOMPARE(spy.count(), 3); + QCOMPARE(spy.size(), 3); QList<QRectF> rlist = qvariant_cast<QList<QRectF> >(spy.last().at(0)); QCOMPARE(rlist.size(), 2); @@ -3397,7 +3374,7 @@ void tst_QGraphicsItem::childrenBoundingRect() childChild->setPos(500, 500); child->setTransform(QTransform().rotate(90), true); - scene.addPolygon(parent->mapToScene(parent->boundingRect() | parent->childrenBoundingRect()))->setPen(QPen(Qt::red));; + scene.addPolygon(parent->mapToScene(parent->boundingRect() | parent->childrenBoundingRect()))->setPen(QPen(Qt::red)); QGraphicsView view(&scene); view.setWindowTitle(QLatin1String(QTest::currentTestFunction())); @@ -3624,7 +3601,7 @@ void tst_QGraphicsItem::group() view.fitInView(scene.itemsBoundingRect()); - for (QGraphicsItem *item : qAsConst(newItems)) { + for (QGraphicsItem *item : std::as_const(newItems)) { group->addToGroup(item); QCOMPARE(item->group(), group); } @@ -4967,7 +4944,6 @@ void tst_QGraphicsItem::sceneEventFilter() QGraphicsView view(&scene); view.setWindowTitle(QLatin1String(QTest::currentTestFunction())); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); @@ -5360,8 +5336,9 @@ void tst_QGraphicsItem::deleteItemInEventHandlers() #ifndef QT_NO_CONTEXTMENU if (!HarakiriItem::dead) { - QContextMenuEvent event(QContextMenuEvent::Other, - view.mapFromScene(item->scenePos())); + auto viewPos = view.mapFromScene(item->scenePos()); + QContextMenuEvent event(QContextMenuEvent::Other, viewPos, + view.mapToGlobal(viewPos)); QCoreApplication::sendEvent(view.viewport(), &event); } #endif // QT_NO_CONTEXTMENU @@ -5587,7 +5564,6 @@ void tst_QGraphicsItem::itemClipsChildrenToShape4() scene.addEllipse( 100, 100, 100, 50 ); // <-- this is important to trigger the right codepath* //now the label is shown outerWidget->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false ); - QApplication::setActiveWindow(&view); view.setWindowTitle(QLatin1String(QTest::currentTestFunction())); view.show(); QTRY_COMPARE(QApplication::activeWindow(), &view); @@ -5703,7 +5679,7 @@ void tst_QGraphicsItem::itemClipsChildrenToShape5() } const QList<QGraphicsItem *> children = parent->childItems(); - const int childrenCount = children.count(); + const int childrenCount = children.size(); for (int i = 0; i < 5; ++i) { QString clipString; @@ -6931,8 +6907,10 @@ void tst_QGraphicsItem::opacityZeroUpdates() view.setWindowTitle(QLatin1String(QTest::currentTestFunction())); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); - if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) { + view.window()->activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&view)); + } QCoreApplication::processEvents(); // Process all queued paint events QTRY_VERIFY(view.repaints > 0); @@ -7288,6 +7266,7 @@ void tst_QGraphicsItem::tabChangesFocus() widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); widget.setLayout(layout); widget.show(); + view->window()->activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&widget)); QTRY_VERIFY(scene.isActive()); @@ -7322,7 +7301,7 @@ void tst_QGraphicsItem::cacheMode() view.setWindowTitle(QLatin1String(QTest::currentTestFunction())); view.resize(150, 150); view.show(); - QApplication::setActiveWindow(&view); + view.window()->activateWindow(); QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QCoreApplication::processEvents(); // Process all queued paint events @@ -7506,7 +7485,7 @@ void tst_QGraphicsItem::cacheMode2() view.setWindowTitle(QLatin1String(QTest::currentTestFunction())); view.resize(150, 150); view.show(); - QApplication::setActiveWindow(&view); + view.window()->activateWindow(); QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QCoreApplication::processEvents(); // Process all queued paint events @@ -8167,9 +8146,11 @@ void tst_QGraphicsItem::moveLineItem() MyGraphicsView view(&scene); view.setWindowTitle(QLatin1String(QTest::currentTestFunction())); view.show(); - QVERIFY(QTest::qWaitForWindowExposed(&view)); - if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) { + view.window()->activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&view)); + } + QVERIFY(QTest::qWaitForWindowExposed(&view)); QCoreApplication::processEvents(); // Process all queued paint events view.reset(); @@ -8239,11 +8220,11 @@ void tst_QGraphicsItem::sorting() view.setFrameStyle(0); view.show(); if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) { - qApp->setActiveWindow(&view); + view.window()->activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&view)); } QVERIFY(QTest::qWaitForWindowExposed(&view)); - QTRY_VERIFY(_paintedItems.count() > 0); + QTRY_VERIFY(_paintedItems.size() > 0); _paintedItems.clear(); @@ -8273,7 +8254,7 @@ void tst_QGraphicsItem::itemHasNoContents() view.setWindowTitle(QLatin1String(QTest::currentTestFunction())); view.show(); if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) { - qApp->setActiveWindow(&view); + view.window()->activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&view)); } QVERIFY(QTest::qWaitForWindowExposed(&view)); @@ -9293,7 +9274,7 @@ void tst_QGraphicsItem::ensureDirtySceneTransform() view.setWindowTitle(QLatin1String(QTest::currentTestFunction())); view.show(); if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) { - QApplication::setActiveWindow(&view); + view.window()->activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view)); } @@ -9691,7 +9672,7 @@ void tst_QGraphicsItem::QTBUG_4233_updateCachedWithSceneRect() QGraphicsView view(&scene); view.setWindowTitle(QLatin1String(QTest::currentTestFunction())); view.show(); - QApplication::setActiveWindow(&view); + view.window()->activateWindow(); QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), &view); @@ -10771,6 +10752,10 @@ void tst_QGraphicsItem::textItem_shortcuts() item->setFlag(QGraphicsItem::ItemIsFocusable); item->setTextInteractionFlags(Qt::TextEditorInteraction); w.show(); + if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) { + view.window()->activateWindow(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + } QVERIFY(QTest::qWaitForWindowExposed(&w)); item->setFocus(); @@ -10825,8 +10810,10 @@ void tst_QGraphicsItem::scroll() view.setFrameStyle(0); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); - if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) { + view.window()->activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&view)); + } QTRY_VERIFY(view.repaints > 0); view.reset(); @@ -10940,7 +10927,6 @@ void tst_QGraphicsItem::focusHandling() view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); - QApplication::setActiveWindow(&view); QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view)); QVERIFY(itemWithFocus->hasFocus()); @@ -11017,13 +11003,13 @@ static QList<QEventPoint> QEventPoint::State state = QEventPoint::State::Pressed) { const QPointF screenPos = view.viewport()->mapToGlobal(view.mapFromScene(scenePos)); - QMutableEventPoint tp(0, state, scenePos, screenPos); - tp.setState(state); - tp.setScenePosition(scenePos); - tp.setGlobalPosition(screenPos); - tp.setGlobalPressPosition(screenPos); - tp.setGlobalLastPosition(screenPos); - tp.setEllipseDiameters(ellipseDiameters); + QEventPoint tp(0, state, scenePos, screenPos); + QMutableEventPoint::setState(tp, state); + QMutableEventPoint::setScenePosition(tp, scenePos); + QMutableEventPoint::setGlobalPosition(tp, screenPos); + QMutableEventPoint::setGlobalPressPosition(tp, screenPos); + QMutableEventPoint::setGlobalLastPosition(tp, screenPos); + QMutableEventPoint::setEllipseDiameters(tp, ellipseDiameters); return QList<QEventPoint>() << tp; } @@ -11345,7 +11331,7 @@ void tst_QGraphicsItem::QTBUG_6738_missingUpdateWithSetParent() view.setWindowTitle(QLatin1String(QTest::currentTestFunction())); view.show(); if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) { - qApp->setActiveWindow(&view); + view.window()->activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&view)); } QVERIFY(QTest::qWaitForWindowExposed(&view)); @@ -11399,8 +11385,10 @@ void tst_QGraphicsItem::QT_2653_fullUpdateDiscardingOpacityUpdate() view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); - if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) { + view.window()->activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&view)); + } QCoreApplication::processEvents(); // Process all queued paint events view.reset(); @@ -11435,8 +11423,10 @@ void tst_QGraphicsItem::QTBUG_7714_fullUpdateDiscardingOpacityUpdate2() scene.addItem(parentGreen); origView.show(); - if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) { + view.window()->activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&origView)); + } QVERIFY(QTest::qWaitForWindowExposed(&origView)); QCoreApplication::processEvents(); // Process all queued paint events @@ -11452,7 +11442,7 @@ void tst_QGraphicsItem::QTBUG_7714_fullUpdateDiscardingOpacityUpdate2() view.show(); if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) { - qApp->setActiveWindow(&view); + QApplicationPrivate::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); } QVERIFY(QTest::qWaitForWindowExposed(&view)); @@ -11593,6 +11583,7 @@ void tst_QGraphicsItem::doNotMarkFullUpdateIfNotInScene() item2->setParentItem(item); scene.addItem(item); view.show(); + view.window()->activateWindow(); QVERIFY(QTest::qWaitForWindowExposed(view.windowHandle())); QVERIFY(QTest::qWaitForWindowActive(view.windowHandle())); QCoreApplication::processEvents(); // Process all queued paint events @@ -11627,7 +11618,7 @@ void tst_QGraphicsItem::itemDiesDuringDraggingOperation() scene.addItem(item); view.show(); if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) { - QApplication::setActiveWindow(&view); + view.window()->activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), &view); } @@ -11658,7 +11649,7 @@ void tst_QGraphicsItem::QTBUG_12112_focusItem() view.show(); if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) { - QApplication::setActiveWindow(&view); + view.window()->activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), &view); } @@ -11708,7 +11699,7 @@ public: QLatin1String wiseWords("AZ BUKI VEDI"); QString sentence(wiseWords); QStringList words = sentence.split(QLatin1Char(' '), Qt::SkipEmptyParts); - for (int i = 0; i < words.count(); ++i) { + for (int i = 0; i < words.size(); ++i) { QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(this); QLabel *label = new QLabel(words.at(i)); proxy->setWidget(label); diff --git a/tests/auto/widgets/graphicsview/qgraphicsitemanimation/CMakeLists.txt b/tests/auto/widgets/graphicsview/qgraphicsitemanimation/CMakeLists.txt index 48bb84d1bb..04bfb3f54a 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsitemanimation/CMakeLists.txt +++ b/tests/auto/widgets/graphicsview/qgraphicsitemanimation/CMakeLists.txt @@ -1,15 +1,22 @@ -# Generated from qgraphicsitemanimation.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgraphicsitemanimation Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgraphicsitemanimation LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgraphicsitemanimation SOURCES tst_qgraphicsitemanimation.cpp DEFINES QT_NO_CAST_TO_ASCII - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/graphicsview/qgraphicsitemanimation/tst_qgraphicsitemanimation.cpp b/tests/auto/widgets/graphicsview/qgraphicsitemanimation/tst_qgraphicsitemanimation.cpp index 46e834afdb..9cf74c23fe 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsitemanimation/tst_qgraphicsitemanimation.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsitemanimation/tst_qgraphicsitemanimation.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/graphicsview/qgraphicslayout/CMakeLists.txt b/tests/auto/widgets/graphicsview/qgraphicslayout/CMakeLists.txt index 768898549e..4951558423 100644 --- a/tests/auto/widgets/graphicsview/qgraphicslayout/CMakeLists.txt +++ b/tests/auto/widgets/graphicsview/qgraphicslayout/CMakeLists.txt @@ -1,15 +1,22 @@ -# Generated from qgraphicslayout.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgraphicslayout Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgraphicslayout LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgraphicslayout SOURCES tst_qgraphicslayout.cpp DEFINES QT_USE_USING_NAMESPACE - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/graphicsview/qgraphicslayout/tst_qgraphicslayout.cpp b/tests/auto/widgets/graphicsview/qgraphicslayout/tst_qgraphicslayout.cpp index 167f418c3d..268195d7d3 100644 --- a/tests/auto/widgets/graphicsview/qgraphicslayout/tst_qgraphicslayout.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicslayout/tst_qgraphicslayout.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -695,7 +670,7 @@ public: private slots: void valueChanged(qreal value) { - for (int i = 0; i < fromGeoms.count(); ++i) { + for (int i = 0; i < fromGeoms.size(); ++i) { QGraphicsLayoutItem *li = itemAt(i); QRectF from = fromGeoms.at(i); QRectF to = toGeoms.at(i); @@ -850,7 +825,7 @@ CustomLayout(QGraphicsLayoutItem *parent) int count() const override { - return items.count(); + return items.size(); } QGraphicsLayoutItem* itemAt(int index) const override @@ -866,12 +841,12 @@ void removeAt(int index) override void addItem(QGraphicsLayoutItem *item) { - insertItem(items.count(), item); + insertItem(items.size(), item); } void insertItem(int index, QGraphicsLayoutItem *item) { - index = qBound(0, index, items.count()); + index = qBound(0, index, items.size()); item->setParentLayoutItem(this); @@ -879,7 +854,7 @@ void insertItem(int index, QGraphicsLayoutItem *item) updateParentWidget(widget); - if (index == items.count()) { + if (index == items.size()) { items.append(item); } else { items.insert(index, item); @@ -947,7 +922,7 @@ void tst_QGraphicsLayout::ownership() destructedSet.clear(); window->setLayout(0); - QCOMPARE(destructedSet.count(), 0); + QCOMPARE(destructedSet.size(), 0); delete window; } diff --git a/tests/auto/widgets/graphicsview/qgraphicslayoutitem/CMakeLists.txt b/tests/auto/widgets/graphicsview/qgraphicslayoutitem/CMakeLists.txt index d927ec59aa..7bd25cecf2 100644 --- a/tests/auto/widgets/graphicsview/qgraphicslayoutitem/CMakeLists.txt +++ b/tests/auto/widgets/graphicsview/qgraphicslayoutitem/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qgraphicslayoutitem.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgraphicslayoutitem Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgraphicslayoutitem LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgraphicslayoutitem SOURCES tst_qgraphicslayoutitem.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/graphicsview/qgraphicslayoutitem/tst_qgraphicslayoutitem.cpp b/tests/auto/widgets/graphicsview/qgraphicslayoutitem/tst_qgraphicslayoutitem.cpp index 7e0a8b6033..87b703a286 100644 --- a/tests/auto/widgets/graphicsview/qgraphicslayoutitem/tst_qgraphicslayoutitem.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicslayoutitem/tst_qgraphicslayoutitem.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/graphicsview/qgraphicslinearlayout/CMakeLists.txt b/tests/auto/widgets/graphicsview/qgraphicslinearlayout/CMakeLists.txt index 6c2e734348..b63bbb6f33 100644 --- a/tests/auto/widgets/graphicsview/qgraphicslinearlayout/CMakeLists.txt +++ b/tests/auto/widgets/graphicsview/qgraphicslinearlayout/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qgraphicslinearlayout.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgraphicslinearlayout Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgraphicslinearlayout LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgraphicslinearlayout SOURCES tst_qgraphicslinearlayout.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/graphicsview/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp b/tests/auto/widgets/graphicsview/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp index eef554ad87..65212e94cc 100644 --- a/tests/auto/widgets/graphicsview/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -748,7 +723,7 @@ void tst_QGraphicsLinearLayout::orientation() // important to resize to preferredsize when orientation is switched widget->resize(widget->effectiveSizeHint(Qt::PreferredSize)); qApp->processEvents(); - for (i = 0; i < positions.count(); ++i) { + for (i = 0; i < positions.size(); ++i) { QGraphicsWidget *item = static_cast<QGraphicsWidget*>(layout.itemAt(i)); if (initialOrientation == Qt::Horizontal) QCOMPARE(item->pos().y(), positions.at(i)); @@ -1146,7 +1121,7 @@ void tst_QGraphicsLinearLayout::setStretchFactor() int i; - for (i = 0; i < stretches.count(); ++i) { + for (i = 0; i < stretches.size(); ++i) { QGraphicsWidget *item = new RectWidget(widget); item->setMinimumSize(5,5); item->setPreferredSize(10,5); @@ -1164,7 +1139,7 @@ void tst_QGraphicsLinearLayout::setStretchFactor() qreal firstStretch = -1; qreal firstExtent = -1.; qreal sumExtent = 0; - for (i = 0; i < stretches.count(); ++i) { + for (i = 0; i < stretches.size(); ++i) { QGraphicsWidget *item = static_cast<QGraphicsWidget*>(layout.itemAt(i)); qreal extent = item->size().width(); qreal stretch = (qreal)stretches.at(i); @@ -1322,7 +1297,7 @@ void tst_QGraphicsLinearLayout::defaultStretchFactors() QSizeF itemSize = layout->itemAt(i)->geometry().size(); if (orientation == Qt::Vertical) itemSize.transpose(); - if (i < expectedSizes.count()) + if (i < expectedSizes.size()) QCOMPARE(itemSize.width(), qreal(expectedSizes.at(i))); } diff --git a/tests/auto/widgets/graphicsview/qgraphicsobject/CMakeLists.txt b/tests/auto/widgets/graphicsview/qgraphicsobject/CMakeLists.txt index 9b7bf4c63c..0c21d9febc 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsobject/CMakeLists.txt +++ b/tests/auto/widgets/graphicsview/qgraphicsobject/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qgraphicsobject.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgraphicsobject Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgraphicsobject LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgraphicsobject SOURCES tst_qgraphicsobject.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::Widgets diff --git a/tests/auto/widgets/graphicsview/qgraphicsobject/tst_qgraphicsobject.cpp b/tests/auto/widgets/graphicsview/qgraphicsobject/tst_qgraphicsobject.cpp index 58c965a7d0..674a7f73d5 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsobject/tst_qgraphicsobject.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsobject/tst_qgraphicsobject.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -66,28 +41,28 @@ void tst_QGraphicsObject::pos() QSignalSpy ySpy(&object, SIGNAL(yChanged())); QVERIFY(object.pos() == QPointF(0, 0)); object.setPos(10, 10); - QCOMPARE(xSpy.count(), 1); - QCOMPARE(ySpy.count(), 1); + QCOMPARE(xSpy.size(), 1); + QCOMPARE(ySpy.size(), 1); QCOMPARE(object.pos(), QPointF(10,10)); object.setPos(10, 10); - QCOMPARE(xSpy.count(), 1); - QCOMPARE(ySpy.count(), 1); + QCOMPARE(xSpy.size(), 1); + QCOMPARE(ySpy.size(), 1); object.setProperty("pos", QPointF(0, 0)); - QCOMPARE(xSpy.count(), 2); - QCOMPARE(ySpy.count(), 2); + QCOMPARE(xSpy.size(), 2); + QCOMPARE(ySpy.size(), 2); QCOMPARE(object.property("pos").toPointF(), QPointF(0,0)); object.setProperty("pos", QPointF(10, 0)); - QCOMPARE(xSpy.count(), 3); - QCOMPARE(ySpy.count(), 2); + QCOMPARE(xSpy.size(), 3); + QCOMPARE(ySpy.size(), 2); QCOMPARE(object.property("pos").toPointF(), QPointF(10,0)); object.setProperty("pos", QPointF(10, 10)); - QCOMPARE(xSpy.count(), 3); - QCOMPARE(ySpy.count(), 3); + QCOMPARE(xSpy.size(), 3); + QCOMPARE(ySpy.size(), 3); QVERIFY(object.property("pos") == QPointF(10, 10)); } @@ -98,19 +73,19 @@ void tst_QGraphicsObject::x() QSignalSpy ySpy(&object, SIGNAL(yChanged())); QVERIFY(object.pos() == QPointF(0, 0)); object.setX(10); - QCOMPARE(xSpy.count(), 1); - QCOMPARE(ySpy.count(), 0); + QCOMPARE(xSpy.size(), 1); + QCOMPARE(ySpy.size(), 0); QVERIFY(object.pos() == QPointF(10, 0)); QCOMPARE(object.x(), qreal(10)); object.setX(10); - QCOMPARE(xSpy.count(), 1); - QCOMPARE(ySpy.count(), 0); + QCOMPARE(xSpy.size(), 1); + QCOMPARE(ySpy.size(), 0); object.setProperty("x", 0); - QCOMPARE(xSpy.count(), 2); - QCOMPARE(ySpy.count(), 0); + QCOMPARE(xSpy.size(), 2); + QCOMPARE(ySpy.size(), 0); QCOMPARE(object.property("x").toDouble(), double(0)); } @@ -121,19 +96,19 @@ void tst_QGraphicsObject::y() QSignalSpy ySpy(&object, SIGNAL(yChanged())); QVERIFY(object.pos() == QPointF(0, 0)); object.setY(10); - QCOMPARE(xSpy.count(), 0); - QCOMPARE(ySpy.count(), 1); + QCOMPARE(xSpy.size(), 0); + QCOMPARE(ySpy.size(), 1); QVERIFY(object.pos() == QPointF(0, 10)); QCOMPARE(object.y(), qreal(10)); object.setY(10); - QCOMPARE(xSpy.count(), 0); - QCOMPARE(ySpy.count(), 1); + QCOMPARE(xSpy.size(), 0); + QCOMPARE(ySpy.size(), 1); object.setProperty("y", 0); - QCOMPARE(xSpy.count(), 0); - QCOMPARE(ySpy.count(), 2); + QCOMPARE(xSpy.size(), 0); + QCOMPARE(ySpy.size(), 2); QCOMPARE(object.property("y").toDouble(), qreal(0)); } @@ -143,15 +118,15 @@ void tst_QGraphicsObject::z() QSignalSpy zSpy(&object, SIGNAL(zChanged())); QCOMPARE(object.zValue(), qreal(0)); object.setZValue(10); - QCOMPARE(zSpy.count(), 1); + QCOMPARE(zSpy.size(), 1); QCOMPARE(object.zValue(), qreal(10)); object.setZValue(10); - QCOMPARE(zSpy.count(), 1); + QCOMPARE(zSpy.size(), 1); object.setProperty("z", 0); - QCOMPARE(zSpy.count(), 2); + QCOMPARE(zSpy.size(), 2); QCOMPARE(object.property("z").toDouble(), double(0)); } @@ -161,15 +136,15 @@ void tst_QGraphicsObject::opacity() QSignalSpy spy(&object, SIGNAL(opacityChanged())); QCOMPARE(object.opacity(), 1.); object.setOpacity(0); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(object.opacity(), 0.); object.setOpacity(0); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); object.setProperty("opacity", .5); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); QCOMPARE(object.property("opacity").toDouble(), .5); } @@ -179,15 +154,15 @@ void tst_QGraphicsObject::enabled() QSignalSpy spy(&object, SIGNAL(enabledChanged())); QVERIFY(object.isEnabled()); object.setEnabled(false); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QVERIFY(!object.isEnabled()); object.setEnabled(false); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); object.setProperty("enabled", true); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); QVERIFY(object.property("enabled").toBool()); } @@ -197,15 +172,15 @@ void tst_QGraphicsObject::visible() QSignalSpy spy(&object, SIGNAL(visibleChanged())); QVERIFY(object.isVisible()); object.setVisible(false); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QVERIFY(!object.isVisible()); object.setVisible(false); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); object.setProperty("visible", true); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); QVERIFY(object.property("visible").toBool()); } diff --git a/tests/auto/widgets/graphicsview/qgraphicspixmapitem/CMakeLists.txt b/tests/auto/widgets/graphicsview/qgraphicspixmapitem/CMakeLists.txt index a848a8dd77..b60958f4f1 100644 --- a/tests/auto/widgets/graphicsview/qgraphicspixmapitem/CMakeLists.txt +++ b/tests/auto/widgets/graphicsview/qgraphicspixmapitem/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qgraphicspixmapitem.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgraphicspixmapitem Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgraphicspixmapitem LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgraphicspixmapitem SOURCES tst_qgraphicspixmapitem.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/graphicsview/qgraphicspixmapitem/tst_qgraphicspixmapitem.cpp b/tests/auto/widgets/graphicsview/qgraphicspixmapitem/tst_qgraphicspixmapitem.cpp index 22b81773b9..fd8f8bd37d 100644 --- a/tests/auto/widgets/graphicsview/qgraphicspixmapitem/tst_qgraphicspixmapitem.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicspixmapitem/tst_qgraphicspixmapitem.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/graphicsview/qgraphicspolygonitem/CMakeLists.txt b/tests/auto/widgets/graphicsview/qgraphicspolygonitem/CMakeLists.txt index 8a3c88a8a2..116378decb 100644 --- a/tests/auto/widgets/graphicsview/qgraphicspolygonitem/CMakeLists.txt +++ b/tests/auto/widgets/graphicsview/qgraphicspolygonitem/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qgraphicspolygonitem.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgraphicspolygonitem Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgraphicspolygonitem LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgraphicspolygonitem SOURCES tst_qgraphicspolygonitem.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/graphicsview/qgraphicspolygonitem/tst_qgraphicspolygonitem.cpp b/tests/auto/widgets/graphicsview/qgraphicspolygonitem/tst_qgraphicspolygonitem.cpp index 90d69ca853..9412fab0ee 100644 --- a/tests/auto/widgets/graphicsview/qgraphicspolygonitem/tst_qgraphicspolygonitem.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicspolygonitem/tst_qgraphicspolygonitem.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/BLACKLIST b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/BLACKLIST new file mode 100644 index 0000000000..731b3ae253 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/BLACKLIST @@ -0,0 +1,11 @@ +[scrollUpdate] +android +b2qt +[windowOpacity] +macos ci +[wheelEventPropagation] +android +[forwardTouchEvent] +android +[touchEventPropagation] +android diff --git a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/CMakeLists.txt b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/CMakeLists.txt index a1bf1270c2..f28ff74cfa 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/CMakeLists.txt +++ b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/CMakeLists.txt @@ -1,15 +1,22 @@ -# Generated from qgraphicsproxywidget.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgraphicsproxywidget Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgraphicsproxywidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgraphicsproxywidget SOURCES tst_qgraphicsproxywidget.cpp DEFINES QTEST_QPA_MOUSE_HANDLING - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp index e9ba24f4fc..f14b7299b5 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp @@ -1,38 +1,18 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QtGui> #include <QtWidgets> +#include <QSignalSpy> #include <private/qgraphicsproxywidget_p.h> #include <private/qlayoutengine_p.h> // qSmartMin functions... +#include <QtWidgets/private/qapplication_p.h> + +Q_LOGGING_CATEGORY(lcTests, "qt.widgets.tests") + /* Notes: @@ -60,7 +40,7 @@ public: QMap<QEvent::Type, int> counts; protected: - bool eventFilter(QObject *, QEvent *event) + bool eventFilter(QObject *, QEvent *event) override { ++counts[event->type()]; return false; @@ -74,7 +54,6 @@ class tst_QGraphicsProxyWidget : public QObject private slots: void initTestCase(); void cleanup(); - void qgraphicsproxywidget_data(); void qgraphicsproxywidget(); void paint(); void paint_2(); @@ -92,13 +71,10 @@ private slots: void focusProxy_QTBUG_51856(); void hoverEnterLeaveEvent_data(); void hoverEnterLeaveEvent(); - void hoverMoveEvent_data(); - void hoverMoveEvent(); void keyPressEvent_data(); void keyPressEvent(); void keyReleaseEvent_data(); void keyReleaseEvent(); - void mouseDoubleClickEvent_data(); void mouseDoubleClickEvent(); void mousePressReleaseEvent_data(); void mousePressReleaseEvent(); @@ -108,8 +84,6 @@ private slots: #if QT_CONFIG(wheelevent) void wheelEvent(); #endif - void sizeHint_data(); - void sizeHint(); void sizePolicy(); void minimumSize(); void maximumSize(); @@ -162,88 +136,39 @@ private slots: void mapToGlobal(); void mapToGlobalWithoutScene(); void QTBUG_43780_visibility(); +#if QT_CONFIG(wheelevent) + void wheelEventPropagation(); +#endif void forwardTouchEvent(); + void touchEventPropagation(); }; // Subclass that exposes the protected functions. class SubQGraphicsProxyWidget : public QGraphicsProxyWidget { - + friend tst_QGraphicsProxyWidget; public: - SubQGraphicsProxyWidget(QGraphicsItem *parent = nullptr) : QGraphicsProxyWidget(parent), - paintCount(0), keyPress(0), focusOut(0) - {} - - bool call_eventFilter(QObject* object, QEvent* event) - { return SubQGraphicsProxyWidget::eventFilter(object, event); } - - void call_focusInEvent(QFocusEvent* event) - { return SubQGraphicsProxyWidget::focusInEvent(event); } - - bool call_focusNextPrevChild(bool next) - { return SubQGraphicsProxyWidget::focusNextPrevChild(next); } - - void call_focusOutEvent(QFocusEvent* event) - { return SubQGraphicsProxyWidget::focusOutEvent(event); } - - void call_hideEvent(QHideEvent* event) - { return SubQGraphicsProxyWidget::hideEvent(event); } - - void call_hoverEnterEvent(QGraphicsSceneHoverEvent* event) - { return SubQGraphicsProxyWidget::hoverEnterEvent(event); } - - void call_hoverLeaveEvent(QGraphicsSceneHoverEvent* event) - { return SubQGraphicsProxyWidget::hoverLeaveEvent(event); } - - void call_hoverMoveEvent(QGraphicsSceneHoverEvent* event) - { return SubQGraphicsProxyWidget::hoverMoveEvent(event); } - - void call_keyPressEvent(QKeyEvent* event) - { return SubQGraphicsProxyWidget::keyPressEvent(event); } - - void call_keyReleaseEvent(QKeyEvent* event) - { return SubQGraphicsProxyWidget::keyReleaseEvent(event); } - - void call_mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) - { return SubQGraphicsProxyWidget::mouseDoubleClickEvent(event); } - - void call_mouseMoveEvent(QGraphicsSceneMouseEvent* event) - { return SubQGraphicsProxyWidget::mouseMoveEvent(event); } + using QGraphicsProxyWidget::QGraphicsProxyWidget; - void call_mousePressEvent(QGraphicsSceneMouseEvent* event) - { return SubQGraphicsProxyWidget::mousePressEvent(event); } - - void call_mouseReleaseEvent(QGraphicsSceneMouseEvent* event) - { return SubQGraphicsProxyWidget::mouseReleaseEvent(event); } - - void call_resizeEvent(QGraphicsSceneResizeEvent* event) - { return SubQGraphicsProxyWidget::resizeEvent(event); } - - QSizeF call_sizeHint(Qt::SizeHint which, QSizeF const& constraint = QSizeF()) const - { return SubQGraphicsProxyWidget::sizeHint(which, constraint); } - - void call_showEvent(QShowEvent* event) - { return SubQGraphicsProxyWidget::showEvent(event); } - - void paint (QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) { + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override { paintCount++; QGraphicsProxyWidget::paint(painter, option, widget); } - void focusOutEvent(QFocusEvent *event) + void focusOutEvent(QFocusEvent *event) override { focusOut++; QGraphicsProxyWidget::focusOutEvent(event); } - bool eventFilter(QObject *object, QEvent *event) { + bool eventFilter(QObject *object, QEvent *event) override { if (event->type() == QEvent::KeyPress && object == widget()) keyPress++; return QGraphicsProxyWidget::eventFilter(object, event); } - int paintCount; - int keyPress; - int focusOut; + int paintCount = 0; + int keyPress = 0; + int focusOut = 0; }; #if QT_CONFIG(wheelevent) @@ -252,7 +177,7 @@ class WheelWidget : public QWidget public: WheelWidget() : wheelEventCalled(false) { setFocusPolicy(Qt::WheelFocus); } - virtual void wheelEvent(QWheelEvent *event) { event->accept(); wheelEventCalled = true; } + virtual void wheelEvent(QWheelEvent *event) override { event->accept(); wheelEventCalled = true; } bool wheelEventCalled; }; @@ -276,10 +201,6 @@ void tst_QGraphicsProxyWidget::cleanup() QTRY_VERIFY(QApplication::topLevelWidgets().isEmpty()); } -void tst_QGraphicsProxyWidget::qgraphicsproxywidget_data() -{ -} - void tst_QGraphicsProxyWidget::qgraphicsproxywidget() { SubQGraphicsProxyWidget proxy; @@ -288,46 +209,47 @@ void tst_QGraphicsProxyWidget::qgraphicsproxywidget() QCOMPARE(proxy.type(), int(QGraphicsProxyWidget::Type)); QVERIFY(!proxy.widget()); QEvent event(QEvent::None); - proxy.call_eventFilter(0, &event); + proxy.eventFilter(0, &event); QFocusEvent focusEvent(QEvent::FocusIn); focusEvent.ignore(); - proxy.call_focusInEvent(&focusEvent); + proxy.focusInEvent(&focusEvent); QCOMPARE(focusEvent.isAccepted(), false); - QCOMPARE(proxy.call_focusNextPrevChild(false), false); - QCOMPARE(proxy.call_focusNextPrevChild(true), false); - proxy.call_focusOutEvent(&focusEvent); + QCOMPARE(proxy.focusNextPrevChild(false), false); + QCOMPARE(proxy.focusNextPrevChild(true), false); + proxy.focusOutEvent(&focusEvent); QHideEvent hideEvent; - proxy.call_hideEvent(&hideEvent); + proxy.hideEvent(&hideEvent); QGraphicsSceneHoverEvent hoverEvent; - proxy.call_hoverEnterEvent(&hoverEvent); - proxy.call_hoverLeaveEvent(&hoverEvent); - proxy.call_hoverMoveEvent(&hoverEvent); + proxy.hoverEnterEvent(&hoverEvent); + proxy.hoverLeaveEvent(&hoverEvent); + proxy.hoverMoveEvent(&hoverEvent); QKeyEvent keyEvent(QEvent::KeyPress, 0, Qt::NoModifier); - proxy.call_keyPressEvent(&keyEvent); - proxy.call_keyReleaseEvent(&keyEvent); + proxy.keyPressEvent(&keyEvent); + proxy.keyReleaseEvent(&keyEvent); QGraphicsSceneMouseEvent mouseEvent; - proxy.call_mouseDoubleClickEvent(&mouseEvent); - proxy.call_mouseMoveEvent(&mouseEvent); - proxy.call_mousePressEvent(&mouseEvent); - proxy.call_mouseReleaseEvent(&mouseEvent); + proxy.mouseDoubleClickEvent(&mouseEvent); + proxy.mouseMoveEvent(&mouseEvent); + proxy.mousePressEvent(&mouseEvent); + proxy.mouseReleaseEvent(&mouseEvent); QGraphicsSceneResizeEvent resizeEvent; - proxy.call_resizeEvent(&resizeEvent); + proxy.resizeEvent(&resizeEvent); QShowEvent showEvent; - proxy.call_showEvent(&showEvent); - proxy.call_sizeHint(Qt::PreferredSize, QSizeF()); + proxy.showEvent(&showEvent); + proxy.sizeHint(Qt::PreferredSize, QSizeF()); } // public void paint(QPainter* painter, QStyleOptionGraphicsItem const* option, QWidget* widget) void tst_QGraphicsProxyWidget::paint() { SubQGraphicsProxyWidget proxy; + proxy.paint(0, 0, 0); } class MyProxyWidget : public QGraphicsProxyWidget { public: - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override { // Make sure QGraphicsProxyWidget::paint does not modify the render hints set on the painter. painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform @@ -386,6 +308,8 @@ void tst_QGraphicsProxyWidget::setWidget() QGraphicsView view(&scene); view.show(); QScopedPointer<QStyle> style(QStyleFactory::create(QLatin1String("Fusion"))); + if (style.isNull()) + QSKIP("This test requires the Fusion style"); QVERIFY(QTest::qWaitForWindowExposed(&view)); QPointer<SubQGraphicsProxyWidget> proxy = new SubQGraphicsProxyWidget; SubQGraphicsProxyWidget parentProxy; @@ -411,7 +335,7 @@ void tst_QGraphicsProxyWidget::setWidget() widget->setStyle(style.data()); widget->setFont(QFont("Times")); widget->setVisible(true); - QApplication::setActiveWindow(widget); + QApplicationPrivate::setActiveWindow(widget); widget->activateWindow(); widget->setEnabled(true); widget->resize(325, 241); @@ -433,6 +357,10 @@ void tst_QGraphicsProxyWidget::setWidget() if (shouldBeInsertable) subWidget->setAttribute(Qt::WA_QuitOnClose, true); + if (hasParent) { + QTest::ignoreMessage(QtWarningMsg, QRegularExpression( + "QGraphicsProxyWidget::setWidget: cannot embed widget .* which is not a toplevel widget, and is not a child of an embedded widget")); + } proxy->setWidget(subWidget); if (shouldBeInsertable) { @@ -548,7 +476,7 @@ void tst_QGraphicsProxyWidget::testEventFilter() switch (eventType) { case QEvent::None: { QEvent event(QEvent::None); - proxy->call_eventFilter(widget, &event); + proxy->eventFilter(widget, &event); break; } case QEvent::Resize: { @@ -580,7 +508,7 @@ void tst_QGraphicsProxyWidget::testEventFilter() widget->hide(); } else { QHideEvent event; - proxy->call_eventFilter(widget, &event); + proxy->eventFilter(widget, &event); } QCOMPARE(proxy->isVisible(), false); break; @@ -592,7 +520,7 @@ void tst_QGraphicsProxyWidget::testEventFilter() widget->show(); } else { QShowEvent event; - proxy->call_eventFilter(widget, &event); + proxy->eventFilter(widget, &event); } QCOMPARE(proxy->isVisible(), true); break; @@ -607,7 +535,7 @@ void tst_QGraphicsProxyWidget::testEventFilter() QCOMPARE(proxy->isEnabled(), false); } else { QEvent event(QEvent::EnabledChange); - proxy->call_eventFilter(widget, &event); + proxy->eventFilter(widget, &event); // match the widget not the event QCOMPARE(proxy->isEnabled(), false); } @@ -635,7 +563,7 @@ void tst_QGraphicsProxyWidget::testEventFilter() QTest::keyPress(widget, Qt::Key_A, Qt::NoModifier); } else { QKeyEvent event(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier); - proxy->call_eventFilter(widget, &event); + proxy->eventFilter(widget, &event); } QCOMPARE(proxy->keyPress, 1); break; @@ -695,7 +623,7 @@ void tst_QGraphicsProxyWidget::focusInEvent() QFocusEvent event(QEvent::FocusIn, Qt::TabFocusReason); event.ignore(); - proxy->call_focusInEvent(&event); + proxy->focusInEvent(&event); QTRY_COMPARE(widget->hasFocus(), widgetCanHaveFocus); } @@ -712,7 +640,7 @@ void tst_QGraphicsProxyWidget::focusInEventNoWidget() QFocusEvent event(QEvent::FocusIn, Qt::TabFocusReason); event.ignore(); //should not crash - proxy->call_focusInEvent(&event); + proxy->focusInEvent(&event); } void tst_QGraphicsProxyWidget::focusNextPrevChild_data() @@ -750,23 +678,40 @@ void tst_QGraphicsProxyWidget::focusNextPrevChild() // otherwise respect the scene // Respect the widget over the scene! - SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; - - QLabel *widget = new QLabel; - // I can't believe designer adds this much junk! - widget->setText("<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\"> p, li { white-space: pre-wrap; } </style></head><body style=\" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;\"> <p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><a href=\"http://www.slashdot.org\"><span style=\" text-decoration: underline; color:#0000ff;\">old</span></a> foo <a href=\"http://www.reddit.org\"><span style=\" text-decoration: underline; color:#0000ff;\">new</span></a></p></body></html>"); - widget->setTextInteractionFlags(Qt::TextBrowserInteraction); + std::unique_ptr<SubQGraphicsProxyWidget> proxyGuard(new SubQGraphicsProxyWidget); + auto *proxy = proxyGuard.get(); - if (hasWidget) + if (hasWidget) { + QLabel *widget = new QLabel; + widget->setText(R"( + <html> + <head> + <meta name=\"qrichtext\" content=\"1\" /> + <style type=\"text/css\"> + p, li { white-space: pre-wrap; } + </style> + </head> + <body> + <p> + <a href=\"http://www.slashdot.org\"> + <span style=\" text-decoration: underline; color:#0000ff;\">old</span> + </a> foo + <a href=\"http://www.reddit.org\"> + <span style=\" text-decoration: underline; color:#0000ff;\">new</span> + </a> + </p> + </body> + </html>)"); + widget->setTextInteractionFlags(Qt::TextBrowserInteraction); proxy->setWidget(widget); + } QGraphicsScene scene; QGraphicsView view(&scene); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); if (hasScene) { - scene.addItem(proxy); + scene.addItem(proxyGuard.release()); proxy->show(); // widget should take precedence over scene so make scene.focusNextPrevChild return false @@ -781,12 +726,7 @@ void tst_QGraphicsProxyWidget::focusNextPrevChild() QVERIFY(proxy->hasFocus()); } - QCOMPARE(proxy->call_focusNextPrevChild(next), focusNextPrevChild); - - if (!hasScene) - delete proxy; - if (!hasWidget) - delete widget; + QCOMPARE(proxy->focusNextPrevChild(next), focusNextPrevChild); } void tst_QGraphicsProxyWidget::focusOutEvent_data() @@ -810,18 +750,17 @@ void tst_QGraphicsProxyWidget::focusOutEvent() SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; scene.addItem(proxy); view.show(); - QApplication::setActiveWindow(&view); view.activateWindow(); view.setFocus(); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_VERIFY(view.isVisible()); QTRY_COMPARE(QApplication::activeWindow(), (QWidget*)&view); - QScopedPointer<QWidget> widgetGuard(new QWidget); - QWidget *widget = widgetGuard.data(); - widget->setFocusPolicy(Qt::WheelFocus); + std::unique_ptr<QWidget> widgetGuard(new QWidget); + QWidget *widget = widgetGuard.get(); + widgetGuard->setFocusPolicy(Qt::WheelFocus); if (hasWidget) - proxy->setWidget(widgetGuard.take()); + proxy->setWidget(widgetGuard.release()); proxy->show(); proxy->setFocus(); QVERIFY(proxy->hasFocus()); @@ -832,41 +771,20 @@ void tst_QGraphicsProxyWidget::focusOutEvent() if (!call) { QWidget *other = new QLineEdit(&view); other->show(); - QApplication::processEvents(); QTRY_VERIFY(other->isVisible()); other->setFocus(); QTRY_VERIFY(other->hasFocus()); - qApp->processEvents(); QTRY_COMPARE(proxy->hasFocus(), false); QVERIFY(proxy->focusOut); QCOMPARE(widget->hasFocus(), false); - } else { - { - /* - ### Test doesn't make sense - QFocusEvent focusEvent(QEvent::FocusOut); - proxy->call_focusOutEvent(&focusEvent); - QCOMPARE(focusEvent.isAccepted(), hasWidget); - qApp->processEvents(); - QCOMPARE(proxy->paintCount, hasWidget ? 1 : 0); - */ - } - { - /* - ### Test doesn't make sense - proxy->setFlag(QGraphicsItem::ItemIsFocusable, false); - QFocusEvent focusEvent(QEvent::FocusOut); - proxy->call_focusOutEvent(&focusEvent); - QCOMPARE(focusEvent.isAccepted(), hasWidget); - qApp->processEvents(); - QCOMPARE(proxy->paintCount, 0); - */ - } } } void tst_QGraphicsProxyWidget::focusProxy_QTBUG_51856() { +#ifdef ANDROID + QSKIP("This test leads to failures on subsequent test cases, QTBUG-119574"); +#endif // QSpinBox has an internal QLineEdit; this QLineEdit has the spinbox // as its focus proxy. struct FocusedSpinBox : QSpinBox @@ -889,6 +807,9 @@ void tst_QGraphicsProxyWidget::focusProxy_QTBUG_51856() } }; + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + QGraphicsScene scene; QGraphicsView view(&scene); SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; @@ -943,19 +864,19 @@ public: installEventFilter(this); } - void enterEvent(QEnterEvent *event) + void enterEvent(QEnterEvent *event) override { enterCount++; QWidget::enterEvent(event); } - void leaveEvent(QEvent *event ) + void leaveEvent(QEvent *event ) override { leaveCount++; QWidget::leaveEvent(event); } - void mouseMoveEvent(QMouseEvent *event) + void mouseMoveEvent(QMouseEvent *event) override { event->setAccepted(true); moveCount++; @@ -970,7 +891,7 @@ public: int hoverLeave; int hoverMove; protected: - bool eventFilter(QObject *object, QEvent *event) + bool eventFilter(QObject *object, QEvent *event) override { switch (event->type()) { case QEvent::HoverEnter: @@ -1010,22 +931,21 @@ void tst_QGraphicsProxyWidget::hoverEnterLeaveEvent() QGraphicsScene scene; QGraphicsView view(&scene); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; - QScopedPointer<EventLogger> widgetGuard(new EventLogger); - EventLogger *widget = widgetGuard.data(); + std::unique_ptr<EventLogger> widgetGuard(new EventLogger); + EventLogger *widget = widgetGuard.get(); widget->resize(50, 50); widget->setAttribute(Qt::WA_Hover, hoverEnabled); widget->setMouseTracking(true); view.resize(100, 100); if (hasWidget) - proxy->setWidget(widgetGuard.take()); + proxy->setWidget(widgetGuard.release()); proxy->setPos(50, 0); QSignalSpy sceneChangedSpy(&scene, &QGraphicsScene::changed); scene.addItem(proxy); - QTRY_VERIFY(sceneChangedSpy.count() > 0); + QTRY_VERIFY(sceneChangedSpy.size() > 0); // outside graphics item QTest::mouseMove(&view, QPoint(10, 10)); @@ -1048,75 +968,6 @@ void tst_QGraphicsProxyWidget::hoverEnterLeaveEvent() QTRY_COMPARE(widget->hoverLeave, (hasWidget && hoverEnabled) ? 1 : 0); } -void tst_QGraphicsProxyWidget::hoverMoveEvent_data() -{ - QTest::addColumn<bool>("hasWidget"); - QTest::addColumn<bool>("hoverEnabled"); - QTest::addColumn<bool>("mouseTracking"); - QTest::addColumn<bool>("mouseDown"); - for (int i = 0; i < 2; ++i) { - for (int j = 0; j < 2; ++j) { - for (int k = 0; k < 2; ++k) { - for (int l = 0; l < 2; ++l) { - bool hasWidget = (i == 0); - bool hoverEnabled = (j == 0); - bool mouseTracking = (k == 0); - bool mouseDown = (l == 0); - QByteArray name = QByteArrayLiteral("hasWidget:") + (hasWidget ? '1' : '0') + ", hover:" - + (hoverEnabled ? '1' : '0') + ", mouseTracking:" - + (mouseTracking ? '1' : '0') + ", mouseDown: " + (mouseDown ? '1' : '0'); - QTest::newRow(name.constData()) << hasWidget << hoverEnabled << mouseTracking << mouseDown; - } - } - } - } -} - -// protected void hoverMoveEvent(QGraphicsSceneHoverEvent* event) -void tst_QGraphicsProxyWidget::hoverMoveEvent() -{ - QFETCH(bool, hasWidget); - QFETCH(bool, hoverEnabled); - QFETCH(bool, mouseTracking); - QFETCH(bool, mouseDown); - - QSKIP("Ambiguous test..."); - - // proxy should translate the move events to what the widget would expect - - QGraphicsScene scene; - QGraphicsView view(&scene); - view.show(); - - SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; - proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!! - EventLogger *widget = new EventLogger; - widget->resize(50, 50); - widget->setAttribute(Qt::WA_Hover, hoverEnabled); - if (mouseTracking) - widget->setMouseTracking(true); - view.resize(100, 100); - if (hasWidget) - proxy->setWidget(widget); - proxy->setPos(50, 0); - scene.addItem(proxy); - - // in - QTest::mouseMove(&view, QPoint(50, 50)); - QTest::qWait(12); - - if (mouseDown) - QTest::mousePress(view.viewport(), Qt::LeftButton); - - // move a little bit - QTest::mouseMove(&view, QPoint(60, 60)); - QTRY_COMPARE(widget->hoverEnter, (hasWidget && hoverEnabled) ? 1 : 0); - QCOMPARE(widget->moveCount, (hasWidget && mouseTracking) || (hasWidget && mouseDown) ? 1 : 0); - - if (!hasWidget) - delete widget; -} - void tst_QGraphicsProxyWidget::keyPressEvent_data() { QTest::addColumn<bool>("hasWidget"); @@ -1133,30 +984,27 @@ void tst_QGraphicsProxyWidget::keyPressEvent() QGraphicsView view(&scene); view.show(); view.viewport()->setFocus(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), (QWidget*)&view); SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!! - QLineEdit *widget = new QLineEdit; + std::unique_ptr<QLineEdit> widgetGuard(new QLineEdit); + QLineEdit *widget = widgetGuard.get(); widget->resize(50, 50); view.resize(100, 100); - if (hasWidget) { - proxy->setWidget(widget); - proxy->show(); - } - proxy->setPos(50, 0); + if (hasWidget) + proxy->setWidget(widgetGuard.release()); + scene.addItem(proxy); + proxy->show(); + proxy->setPos(50, 0); proxy->setFocus(); QTest::keyPress(view.viewport(), 'x'); QTRY_COMPARE(widget->text(), hasWidget ? QString("x") : QString()); - - if (!hasWidget) - delete widget; } void tst_QGraphicsProxyWidget::keyReleaseEvent_data() @@ -1174,19 +1022,18 @@ void tst_QGraphicsProxyWidget::keyReleaseEvent() QGraphicsScene scene; QGraphicsView view(&scene); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), (QWidget*)&view); SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!! - QPushButton *widget = new QPushButton; - QSignalSpy spy(widget, SIGNAL(clicked())); - widget->resize(50, 50); + std::unique_ptr<QPushButton> widgetGuard(new QPushButton); + QSignalSpy spy(widgetGuard.get(), SIGNAL(clicked())); + widgetGuard->resize(50, 50); view.resize(100, 100); if (hasWidget) { - proxy->setWidget(widget); + proxy->setWidget(widgetGuard.release()); proxy->show(); } proxy->setPos(50, 0); @@ -1194,59 +1041,43 @@ void tst_QGraphicsProxyWidget::keyReleaseEvent() proxy->setFocus(); QTest::keyPress(view.viewport(), Qt::Key_Space); - QTRY_COMPARE(spy.count(), 0); + QTRY_COMPARE(spy.size(), 0); QTest::keyRelease(view.viewport(), Qt::Key_Space); - QTRY_COMPARE(spy.count(), (hasWidget) ? 1 : 0); - - if (!hasWidget) - delete widget; -} - -void tst_QGraphicsProxyWidget::mouseDoubleClickEvent_data() -{ - QTest::addColumn<bool>("hasWidget"); - QTest::newRow("widget") << true; - QTest::newRow("no widget") << false; + QTRY_COMPARE(spy.size(), hasWidget ? 1 : 0); } // protected void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) void tst_QGraphicsProxyWidget::mouseDoubleClickEvent() { - QFETCH(bool, hasWidget); - QGraphicsScene scene; QGraphicsView view(&scene); - view.show(); - - QApplication::setActiveWindow(&view); - QVERIFY(QTest::qWaitForWindowActive(&view)); - QCOMPARE(QApplication::activeWindow(), (QWidget*)&view); SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!! QLineEdit *widget = new QLineEdit; widget->setText("foo"); widget->resize(50, 50); - view.resize(100, 100); - if (hasWidget) - proxy->setWidget(widget); + proxy->setWidget(widget); + proxy->setPos(50, 0); QSignalSpy sceneChangedSpy(&scene, &QGraphicsScene::changed); scene.addItem(proxy); proxy->setFocus(); + view.resize(100, 100); + view.show(); + + QVERIFY(QTest::qWaitForWindowActive(&view)); + QCOMPARE(QApplication::activeWindow(), (QWidget*)&view); // wait for scene to be updated before doing any coordinate mappings on it - QTRY_VERIFY(sceneChangedSpy.count() > 0); + QTRY_VERIFY(sceneChangedSpy.size() > 0); QPoint pointInLineEdit = view.mapFromScene(proxy->mapToScene(15, proxy->boundingRect().center().y())); - QTest::mousePress(view.viewport(), Qt::LeftButton, 0, pointInLineEdit); - QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, pointInLineEdit); - QTest::mouseDClick(view.viewport(), Qt::LeftButton, 0, pointInLineEdit); + QTest::mousePress(view.viewport(), Qt::LeftButton, {}, pointInLineEdit); + QTest::mouseRelease(view.viewport(), Qt::LeftButton, {}, pointInLineEdit); + QTest::mouseDClick(view.viewport(), Qt::LeftButton, {}, pointInLineEdit); - QTRY_COMPARE(widget->selectedText(), hasWidget ? QString("foo") : QString()); - - if (!hasWidget) - delete widget; + QTRY_COMPARE(widget->selectedText(), QString("foo")); } void tst_QGraphicsProxyWidget::mousePressReleaseEvent_data() @@ -1269,27 +1100,24 @@ void tst_QGraphicsProxyWidget::mousePressReleaseEvent() SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); // ### remove me!!! - QPushButton *widget = new QPushButton; - QSignalSpy spy(widget, SIGNAL(clicked())); - widget->resize(50, 50); + std::unique_ptr<QPushButton> widgetGuard(new QPushButton); + QSignalSpy spy(widgetGuard.get(), SIGNAL(clicked())); + widgetGuard->resize(50, 50); if (hasWidget) - proxy->setWidget(widget); + proxy->setWidget(widgetGuard.release()); proxy->setPos(50, 0); QSignalSpy sceneChangedSpy(&scene, &QGraphicsScene::changed); scene.addItem(proxy); proxy->setFocus(); // wait for scene to be updated before doing any coordinate mappings on it - QTRY_VERIFY(sceneChangedSpy.count() > 0); + QTRY_VERIFY(sceneChangedSpy.size() > 0); QPoint buttonCenter = view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center())); - QTest::mousePress(view.viewport(), Qt::LeftButton, 0, buttonCenter); - QTRY_COMPARE(spy.count(), 0); - QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, buttonCenter); - QTRY_COMPARE(spy.count(), (hasWidget) ? 1 : 0); - - if (!hasWidget) - delete widget; + QTest::mousePress(view.viewport(), Qt::LeftButton, {}, buttonCenter); + QTRY_COMPARE(spy.size(), 0); + QTest::mouseRelease(view.viewport(), Qt::LeftButton, {}, buttonCenter); + QTRY_COMPARE(spy.size(), hasWidget ? 1 : 0); } void tst_QGraphicsProxyWidget::resizeEvent_data() @@ -1305,19 +1133,17 @@ void tst_QGraphicsProxyWidget::resizeEvent() QFETCH(bool, hasWidget); SubQGraphicsProxyWidget proxy; - QWidget *widget = new QWidget; + if (hasWidget) - proxy.setWidget(widget); + proxy.setWidget(new QWidget); QSize newSize(100, 100); QGraphicsSceneResizeEvent event; event.setOldSize(QSize(10, 10)); event.setNewSize(newSize); - proxy.call_resizeEvent(&event); + proxy.resizeEvent(&event); if (hasWidget) - QCOMPARE(widget->size(), newSize); - if (!hasWidget) - delete widget; + QCOMPARE(proxy.widget()->size(), newSize); } void tst_QGraphicsProxyWidget::paintEvent() @@ -1326,7 +1152,6 @@ void tst_QGraphicsProxyWidget::paintEvent() QGraphicsScene scene; QGraphicsView view(&scene); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QVERIFY(view.isActiveWindow()); @@ -1342,7 +1167,7 @@ void tst_QGraphicsProxyWidget::paintEvent() QSignalSpy sceneChangedSpy(&scene, &QGraphicsScene::changed); scene.addItem(&proxy); - QTRY_VERIFY(sceneChangedSpy.count() > 0); // make sure the scene is ready + QTRY_VERIFY(sceneChangedSpy.size() > 0); // make sure the scene is ready proxy.paintCount = 0; w->update(); @@ -1376,48 +1201,13 @@ void tst_QGraphicsProxyWidget::wheelEvent() } #endif // QT_CONFIG(wheelevent) -Q_DECLARE_METATYPE(Qt::SizeHint) -void tst_QGraphicsProxyWidget::sizeHint_data() -{ - QTest::addColumn<Qt::SizeHint>("which"); - QTest::addColumn<QSizeF>("constraint"); - QTest::addColumn<QSizeF>("sizeHint"); - QTest::addColumn<bool>("hasWidget"); - - for (int i = 0; i < 2; ++i) { - bool hasWidget = (i == 0); - // ### What should these do? - QTest::newRow("min") << Qt::MinimumSize << QSizeF() << QSizeF() << hasWidget; - QTest::newRow("pre") << Qt::PreferredSize << QSizeF() << QSizeF() << hasWidget; - QTest::newRow("max") << Qt::MaximumSize << QSizeF() << QSizeF() << hasWidget; - QTest::newRow("mindes") << Qt::MinimumDescent << QSizeF() << QSizeF() << hasWidget; - QTest::newRow("nsize") << Qt::NSizeHints << QSizeF() << QSizeF() << hasWidget; - } -} - -// protected QSizeF sizeHint(Qt::SizeHint which, QSizeF const& constraint = QSizeF()) const -void tst_QGraphicsProxyWidget::sizeHint() -{ - QFETCH(Qt::SizeHint, which); - QFETCH(QSizeF, constraint); - QFETCH(QSizeF, sizeHint); - QFETCH(bool, hasWidget); - QSKIP("Broken test"); - SubQGraphicsProxyWidget proxy; - QWidget *widget = new QWidget; - if (hasWidget) - proxy.setWidget(widget); - QCOMPARE(proxy.call_sizeHint(which, constraint), sizeHint); - if (!hasWidget) - delete widget; -} - void tst_QGraphicsProxyWidget::sizePolicy() { for (int p = 0; p < 2; ++p) { bool hasWidget = (p == 0); - SubQGraphicsProxyWidget proxy; - QWidget *widget = new QWidget; + QGraphicsProxyWidget proxy; + std::unique_ptr<QWidget> widgetGuard(new QWidget); + QWidget *widget = widgetGuard.get(); QSizePolicy proxyPol(QSizePolicy::Maximum, QSizePolicy::Expanding); proxy.setSizePolicy(proxyPol); QSizePolicy widgetPol(QSizePolicy::Fixed, QSizePolicy::Minimum); @@ -1426,7 +1216,7 @@ void tst_QGraphicsProxyWidget::sizePolicy() QCOMPARE(proxy.sizePolicy(), proxyPol); QCOMPARE(widget->sizePolicy(), widgetPol); if (hasWidget) { - proxy.setWidget(widget); + proxy.setWidget(widgetGuard.release()); QCOMPARE(proxy.sizePolicy(), widgetPol); } else { QCOMPARE(proxy.sizePolicy(), proxyPol); @@ -1439,42 +1229,39 @@ void tst_QGraphicsProxyWidget::sizePolicy() QCOMPARE(proxy.sizePolicy(), proxyPol); else QCOMPARE(proxy.sizePolicy(), widgetPol); - - if (!hasWidget) - delete widget; } } void tst_QGraphicsProxyWidget::minimumSize() { - SubQGraphicsProxyWidget proxy; - QWidget *widget = new QWidget; + QGraphicsProxyWidget proxy; + std::unique_ptr<QWidget> widgetGuard(new QWidget); QSize minSize(50, 50); - widget->setMinimumSize(minSize); + widgetGuard->setMinimumSize(minSize); proxy.resize(30, 30); - widget->resize(30,30); + widgetGuard->resize(30,30); QCOMPARE(proxy.size(), QSizeF(30, 30)); - proxy.setWidget(widget); + proxy.setWidget(widgetGuard.release()); QCOMPARE(proxy.size().toSize(), minSize); QCOMPARE(proxy.minimumSize().toSize(), minSize); - widget->setMinimumSize(70, 70); + proxy.widget()->setMinimumSize(70, 70); QCOMPARE(proxy.minimumSize(), QSizeF(70, 70)); QCOMPARE(proxy.size(), QSizeF(70, 70)); } void tst_QGraphicsProxyWidget::maximumSize() { - SubQGraphicsProxyWidget proxy; - QWidget *widget = new QWidget; + QGraphicsProxyWidget proxy; + std::unique_ptr<QWidget> widgetGuard(new QWidget); QSize maxSize(150, 150); - widget->setMaximumSize(maxSize); + widgetGuard->setMaximumSize(maxSize); proxy.resize(200, 200); - widget->resize(200,200); + widgetGuard->resize(200,200); QCOMPARE(proxy.size(), QSizeF(200, 200)); - proxy.setWidget(widget); + proxy.setWidget(widgetGuard.release()); QCOMPARE(proxy.size().toSize(), maxSize); QCOMPARE(proxy.maximumSize().toSize(), maxSize); - widget->setMaximumSize(70, 70); + proxy.widget()->setMaximumSize(70, 70); QCOMPARE(proxy.maximumSize(), QSizeF(70, 70)); QCOMPARE(proxy.size(), QSizeF(70, 70)); } @@ -1488,7 +1275,7 @@ public: QRegion paintEventRegion; int npaints; protected: - void paintEvent(QPaintEvent *event) + void paintEvent(QPaintEvent *event) override { ++npaints; paintEventRegion += event->region(); @@ -1515,7 +1302,7 @@ public slots: } protected: - void paintEvent(QPaintEvent *event) + void paintEvent(QPaintEvent *event) override { ++npaints; paintEventRegion += event->region(); @@ -1535,6 +1322,9 @@ static QList<QRect> rects(const QRegion ®ion) void tst_QGraphicsProxyWidget::scrollUpdate() { + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + ScrollWidget *widget = new ScrollWidget; QGraphicsScene scene; @@ -1544,7 +1334,7 @@ void tst_QGraphicsProxyWidget::scrollUpdate() view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_VERIFY(view.npaints >= 1); - QTest::qWait(20); + QTest::qWait(150); widget->paintEventRegion = QRegion(); widget->npaints = 0; view.paintEventRegion = QRegion(); @@ -1748,7 +1538,6 @@ void tst_QGraphicsProxyWidget::tabFocus_simpleWidget() window.setLayout(layout); window.show(); - QApplication::setActiveWindow(&window); window.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&window)); @@ -1832,7 +1621,6 @@ void tst_QGraphicsProxyWidget::tabFocus_simpleTwoWidgets() window.setLayout(layout); window.show(); - QApplication::setActiveWindow(&window); window.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&window)); @@ -1965,7 +1753,6 @@ void tst_QGraphicsProxyWidget::tabFocus_complexWidget() window.setLayout(layout); window.show(); - QApplication::setActiveWindow(&window); window.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&window)); @@ -2102,7 +1889,6 @@ void tst_QGraphicsProxyWidget::tabFocus_complexTwoWidgets() window.setLayout(layout); window.show(); - QApplication::setActiveWindow(&window); window.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&window)); @@ -2276,7 +2062,7 @@ void tst_QGraphicsProxyWidget::setFocus_simpleWidget() window.setLayout(layout); window.show(); - QApplication::setActiveWindow(&window); + QApplicationPrivate::setActiveWindow(&window); window.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&window)); QCOMPARE(QApplication::activeWindow(), &window); @@ -2348,7 +2134,6 @@ void tst_QGraphicsProxyWidget::setFocus_simpleTwoWidgets() window.setLayout(layout); window.show(); - QApplication::setActiveWindow(&window); window.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&window)); QCOMPARE(QApplication::activeWindow(), &window); @@ -2427,7 +2212,6 @@ void tst_QGraphicsProxyWidget::setFocus_complexTwoWidgets() window.setLayout(layout); window.show(); - QApplication::setActiveWindow(&window); window.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&window)); QCOMPARE(QApplication::activeWindow(), &window); @@ -2500,28 +2284,29 @@ void tst_QGraphicsProxyWidget::setFocus_complexTwoWidgets() void tst_QGraphicsProxyWidget::popup_basic() { - QScopedPointer<QComboBox> box(new QComboBox); + std::unique_ptr<QComboBox> boxGuard(new QComboBox); QStyleOptionComboBox opt; - opt.initFrom(box.data()); - opt.editable = box->isEditable(); - if (box->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt)) + opt.initFrom(boxGuard.get()); + opt.editable = boxGuard->isEditable(); + if (boxGuard->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt)) QSKIP("Does not work due to SH_Combobox_Popup"); // ProxyWidget should automatically create proxy's when the widget creates a child - QGraphicsScene *scene = new QGraphicsScene; - QGraphicsView view(scene); + QGraphicsScene scene; + QGraphicsView view(&scene); view.setAlignment(Qt::AlignLeft | Qt::AlignTop); view.setGeometry(0, 100, 480, 500); view.show(); - SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + QComboBox *box = boxGuard.get(); + QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget; box->setGeometry(0, 0, 320, 40); box->addItems(QStringList() << "monday" << "tuesday" << "wednesday" << "thursday" << "saturday" << "sunday"); - QCOMPARE(proxy->childItems().count(), 0); - proxy->setWidget(box.data()); + QCOMPARE(proxy->childItems().size(), 0); + proxy->setWidget(boxGuard.release()); proxy->show(); - scene->addItem(proxy); + scene.addItem(proxy); QCOMPARE(box->pos(), QPoint()); QCOMPARE(proxy->pos(), QPointF()); @@ -2530,16 +2315,16 @@ void tst_QGraphicsProxyWidget::popup_basic() QTest::qWait(125); QApplication::processEvents(); - QTest::mousePress(view.viewport(), Qt::LeftButton, 0, + QTest::mousePress(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center()))); QTRY_COMPARE(box->pos(), QPoint()); - QCOMPARE(proxy->childItems().count(), 1); + QCOMPARE(proxy->childItems().size(), 1); QGraphicsProxyWidget *child = (QGraphicsProxyWidget*)(proxy->childItems())[0]; QVERIFY(child->isWidget()); QVERIFY(child->widget()); - QCOMPARE(child->widget()->parent(), static_cast<QObject*>(box.data())); + QCOMPARE(child->widget()->parent(), box); QTRY_COMPARE(proxy->pos(), QPointF(box->pos())); QCOMPARE(child->x(), qreal(box->x())); @@ -2610,12 +2395,12 @@ void tst_QGraphicsProxyWidget::changingCursor_basic() view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); - SubQGraphicsProxyWidget *proxy = new SubQGraphicsProxyWidget; + QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget; QLineEdit *widget = new QLineEdit; proxy->setWidget(widget); QSignalSpy sceneChangedSpy(&scene, &QGraphicsScene::changed); scene.addItem(proxy); - QTRY_VERIFY(sceneChangedSpy.count() > 0); // make sure the scene is ready + QTRY_VERIFY(sceneChangedSpy.size() > 0); // make sure the scene is ready // in QTest::mouseMove(view.viewport(), view.mapFromScene(proxy->mapToScene(proxy->boundingRect().center()))); @@ -2671,7 +2456,7 @@ void tst_QGraphicsProxyWidget::tooltip_basic() QGraphicsView view(&scene); view.setFixedSize(200, 200); view.show(); - QApplication::setActiveWindow(&view); + QApplicationPrivate::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); { QHelpEvent helpEvent(QEvent::ToolTip, view.viewport()->rect().topLeft(), @@ -2681,7 +2466,8 @@ void tst_QGraphicsProxyWidget::tooltip_basic() bool foundView = false; bool foundTipLabel = false; - foreach (QWidget *widget, QApplication::topLevelWidgets()) { + const auto widgets = QApplication::topLevelWidgets(); + for (QWidget *widget : widgets) { if (widget == &view) foundView = true; if (widget->inherits("QTipLabel")) @@ -2733,56 +2519,46 @@ void tst_QGraphicsProxyWidget::childPos() QGraphicsScene scene; - QComboBox *box = new QComboBox; - box->addItem("Item 1"); - box->addItem("Item 2"); - box->addItem("Item 3"); - box->addItem("Item 4"); + QComboBox box; + box.addItem("Item 1"); + box.addItem("Item 2"); + box.addItem("Item 3"); + box.addItem("Item 4"); if (moveCombo) - box->move(comboPos); + box.move(comboPos); - QGraphicsProxyWidget *proxy = scene.addWidget(box); + QGraphicsProxyWidget *proxy = scene.addWidget(&box); proxy->show(); QVERIFY(proxy->isVisible()); - QVERIFY(box->isVisible()); + QVERIFY(box.isVisible()); if (!moveCombo) proxy->setPos(proxyPos); QCOMPARE(proxy->pos(), proxyPos); - QCOMPARE(box->pos(), comboPos); + QCOMPARE(box.pos(), comboPos); for (int i = 0; i < 2; ++i) { - box->showPopup(); - QApplication::processEvents(); - QApplication::processEvents(); - - QWidget *menu = nullptr; - foreach (QObject *child, box->children()) { - if ((menu = qobject_cast<QWidget *>(child))) - break; - } + box.showPopup(); + QWidget *menu = box.findChild<QWidget *>(); QVERIFY(menu); - QVERIFY(menu->isVisible()); + QTRY_VERIFY(menu->isVisible()); QVERIFY(menu->testAttribute(Qt::WA_DontShowOnScreen)); QCOMPARE(proxy->childItems().size(), 1); - QGraphicsProxyWidget *proxyChild = 0; - foreach (QGraphicsItem *child, proxy->childItems()) { - if (child->isWidget() && (proxyChild = qobject_cast<QGraphicsProxyWidget *>(static_cast<QGraphicsWidget *>(child)))) - break; - } + QGraphicsProxyWidget *proxyChild = qobject_cast<QGraphicsProxyWidget *>( + static_cast<QGraphicsWidget *>(proxy->childItems().first())); QVERIFY(proxyChild); QVERIFY(proxyChild->isVisible()); qreal expectedXPosition = 0.0; -#if defined(Q_OS_MAC) && !defined(QT_NO_STYLE_MAC) + // The Mac style wants the popup to show up at QPoint(4 - 11, 1). // See QMacStyle::subControlRect SC_ComboBoxListBoxPopup. if (QApplication::style()->inherits("QMacStyle")) expectedXPosition = qreal(4 - 11); -#endif - QCOMPARE(proxyChild->pos().x(), expectedXPosition); + + QTRY_COMPARE(proxyChild->pos().x(), expectedXPosition); menu->hide(); } } @@ -2818,7 +2594,7 @@ void tst_QGraphicsProxyWidget::windowOpacity() QGraphicsProxyWidget *proxy = scene.addWidget(widget); proxy->setCacheMode(QGraphicsItem::ItemCoordinateCache); - QApplication::setActiveWindow(&view); + QApplicationPrivate::setActiveWindow(&view); view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); QVERIFY(view.isActiveWindow()); @@ -2830,18 +2606,20 @@ void tst_QGraphicsProxyWidget::windowOpacity() QVERIFY(widget->isVisible()); widget->setWindowOpacity(0.5); - QApplication::processEvents(); // Make sure setWindowOpacity triggers an update on the scene, // and not on the widget or the proxy itself. The entire proxy needs an update // in case it has a window decoration. Update: QGraphicsItem::CacheMode is // disabled on platforms without alpha channel support in QPixmap (e.g., - // X11 without XRender). + // X11 without XRender). On macOS, we always get a paint event. int paints = 0; +#ifdef Q_OS_MACOS + paints = 1; +#endif QTRY_COMPARE(eventSpy.counts[QEvent::UpdateRequest], 0); QTRY_COMPARE(eventSpy.counts[QEvent::Paint], paints); - QCOMPARE(signalSpy.count(), 1); + QTRY_COMPARE(signalSpy.size(), 1); const QList<QVariant> arguments = signalSpy.takeFirst(); const QList<QRectF> updateRects = qvariant_cast<QList<QRectF> >(arguments.at(0)); QCOMPARE(updateRects.size(), 1); @@ -2922,6 +2700,8 @@ void tst_QGraphicsProxyWidget::palettePropagation() QCOMPARE(editSpy.counts[QEvent::PaletteChange], 3); QCOMPARE(proxySpy.counts[QEvent::PaletteChange], 1); QVERIFY(!edit->testAttribute(Qt::WA_SetPalette)); + if (edit->palette() != palette) + QEXPECT_FAIL("", "Test case fails unless run alone", Abort); QCOMPARE(edit->palette(), palette); QCOMPARE(edit->palette(), proxy.palette()); QCOMPARE(edit->palette().color(QPalette::Text), QColor(Qt::red)); @@ -3112,12 +2892,12 @@ void tst_QGraphicsProxyWidget::createProxyForChildWidget() QSignalSpy spy(checkbox, SIGNAL(clicked())); - QTest::mousePress(view.viewport(), Qt::LeftButton, 0, + QTest::mousePress(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(checkboxProxy->mapToScene(QPointF(8,8)))); - QTRY_COMPARE(spy.count(), 0); - QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, + QTRY_COMPARE(spy.size(), 0); + QTest::mouseRelease(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(checkboxProxy->mapToScene(QPointF(8,8)))); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); @@ -3145,7 +2925,7 @@ public: bool embeddedPopup; bool gotContextMenuEvent; protected: - bool event(QEvent *event) + bool event(QEvent *event) override { if (event->type() == QEvent::ContextMenu) { if (!m_timer) { @@ -3157,7 +2937,7 @@ protected: } return QWidget::event(event); } - void contextMenuEvent(QContextMenuEvent *) + void contextMenuEvent(QContextMenuEvent *) override { gotContextMenuEvent = true; } @@ -3212,7 +2992,7 @@ void tst_QGraphicsProxyWidget::actionsContextMenu() view.resize(200, 200); view.move(QGuiApplication::primaryScreen()->geometry().center() - QPoint(100, 100)); view.show(); - QApplication::setActiveWindow(&view); + QApplicationPrivate::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); view.setFocus(); QTRY_VERIFY(view.hasFocus()); @@ -3282,9 +3062,13 @@ void tst_QGraphicsProxyWidget::bypassGraphicsProxyWidget_data() void tst_QGraphicsProxyWidget::bypassGraphicsProxyWidget() { + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + QFETCH(bool, bypass); - QWidget *widget = new QWidget; + std::unique_ptr<QWidget> widgetGuard(new QWidget); + QWidget *widget = widgetGuard.get(); widget->resize(100, 100); QGraphicsScene scene; @@ -3292,7 +3076,7 @@ void tst_QGraphicsProxyWidget::bypassGraphicsProxyWidget() view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); - QGraphicsProxyWidget *proxy = scene.addWidget(widget); + QGraphicsProxyWidget *proxy = scene.addWidget(widgetGuard.release()); QCOMPARE(proxy->widget(), widget); QVERIFY(proxy->childItems().isEmpty()); @@ -3312,8 +3096,6 @@ void tst_QGraphicsProxyWidget::bypassGraphicsProxyWidget() dialog->hide(); QApplication::processEvents(); - delete dialog; - delete widget; } static void makeDndEvent(QGraphicsSceneDragDropEvent *event, QGraphicsView *view, const QPointF &pos) @@ -3321,7 +3103,7 @@ static void makeDndEvent(QGraphicsSceneDragDropEvent *event, QGraphicsView *view event->setScenePos(pos); event->setScreenPos(view->mapToGlobal(view->mapFromScene(pos))); event->setButtons(Qt::LeftButton); - event->setModifiers(0); + event->setModifiers({}); event->setPossibleActions(Qt::CopyAction); event->setProposedAction(Qt::CopyAction); event->setDropAction(Qt::CopyAction); @@ -3483,7 +3265,7 @@ void tst_QGraphicsProxyWidget::updateAndDelete() class InputMethod_LineEdit : public QLineEdit { - bool event(QEvent *e) + bool event(QEvent *e) override { if (e->type() == QEvent::InputMethod) ++inputMethodEvents; @@ -3495,6 +3277,9 @@ public: void tst_QGraphicsProxyWidget::inputMethod() { + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + QGraphicsScene scene; // check that the proxy is initialized with the correct input method sensitivity @@ -3554,7 +3339,6 @@ void tst_QGraphicsProxyWidget::clickFocus() view.setFrameStyle(0); view.resize(300, 300); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QVERIFY(!proxy->hasFocus()); @@ -3568,7 +3352,7 @@ void tst_QGraphicsProxyWidget::clickFocus() QPointF lineEditCenter = proxy->mapToScene(proxy->boundingRect().center()); // Spontaneous mouse click sets focus on a clickable widget. for (int retry = 0; retry < 50 && !proxy->hasFocus(); retry++) - QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(lineEditCenter)); + QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(lineEditCenter)); QVERIFY(proxy->hasFocus()); QVERIFY(proxy->widget()->hasFocus()); QCOMPARE(proxySpy.counts[QEvent::FocusIn], 1); @@ -3626,7 +3410,7 @@ void tst_QGraphicsProxyWidget::clickFocus() // Spontaneous click on non-clickable widget does not give focus. proxy->widget()->setFocusPolicy(Qt::NoFocus); - QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(lineEditCenter)); + QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(lineEditCenter)); QVERIFY(!proxy->hasFocus()); QVERIFY(!proxy->widget()->hasFocus()); @@ -3635,8 +3419,8 @@ void tst_QGraphicsProxyWidget::clickFocus() scene.setFocusItem(0); QVERIFY(!proxy->hasFocus()); QVERIFY(!proxy->widget()->hasFocus()); - QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(lineEditCenter)); - QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(lineEditCenter)); + QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(lineEditCenter)); + QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.mapFromScene(lineEditCenter)); QVERIFY(proxy->hasFocus()); QVERIFY(proxy->widget()->hasFocus()); QCOMPARE(widgetSpy.counts[QEvent::FocusIn], 1); @@ -3647,7 +3431,7 @@ void tst_QGraphicsProxyWidget::clickFocus() void tst_QGraphicsProxyWidget::windowFrameMargins() { // Make sure the top margin is non-zero when passing Qt::Window. - QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(0, Qt::Window); + QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(nullptr, Qt::Window); qreal left, top, right, bottom; proxy->getWindowFrameMargins(&left, &top, &right, &bottom); @@ -3667,67 +3451,50 @@ void tst_QGraphicsProxyWidget::windowFrameMargins() QVERIFY(top > 0); } -class HoverButton : public QPushButton +void tst_QGraphicsProxyWidget::QTBUG_6986_sendMouseEventToAlienWidget() { -public: - HoverButton(QWidget *parent = nullptr) : QPushButton(parent), hoverLeaveReceived(false) - {} - - bool hoverLeaveReceived; - - bool event(QEvent* e) + struct HoverButton : public QPushButton { - if(QEvent::HoverLeave == e->type()) - hoverLeaveReceived = true; - return QPushButton::event(e); - } -}; + using QPushButton::QPushButton; + bool hoverLeaveReceived = false; -class Scene : public QGraphicsScene -{ -Q_OBJECT -public: - Scene() { - QWidget *background = new QWidget; - background->setGeometry(0, 0, 500, 500); - hoverButton = new HoverButton; - hoverButton->setParent(background); - hoverButton->setText("Second button"); - hoverButton->setGeometry(10, 10, 200, 50); - addWidget(background); - - QPushButton *hideButton = new QPushButton("I'm a button with a very very long text"); - hideButton->setGeometry(10, 10, 400, 50); - topButton = addWidget(hideButton); - connect(hideButton, &QPushButton::clicked, this, [&]() { topButton->hide(); }); - topButton->setFocus(); - } - - QGraphicsProxyWidget *topButton; - HoverButton *hoverButton; -}; + bool event(QEvent* e) override + { + if (QEvent::HoverLeave == e->type()) + hoverLeaveReceived = true; + return QPushButton::event(e); + } + }; -void tst_QGraphicsProxyWidget::QTBUG_6986_sendMouseEventToAlienWidget() -{ - if (QGuiApplication::platformName() == QLatin1String("cocoa")) { - // The "Second button" does not receive QEvent::HoverLeave - QSKIP("This test fails only on Cocoa. Investigate why. See QTBUG-69219"); - } + QGraphicsScene scene; + QWidget *background = new QWidget; + background->setGeometry(0, 0, 500, 500); + HoverButton *hoverButton = new HoverButton(background); + hoverButton->setText("Second button"); + hoverButton->setGeometry(10, 10, 200, 50); + scene.addWidget(background); + + QPushButton *hideButton = new QPushButton("I'm a button with a very very long text"); + hideButton->setGeometry(10, 10, 400, 50); + QGraphicsProxyWidget *topButton = scene.addWidget(hideButton); + connect(hideButton, &QPushButton::clicked, &scene, [&]() { topButton->hide(); }); + topButton->setFocus(); - QGraphicsView view; - Scene scene; - view.setScene(&scene); + QGraphicsView view(&scene); view.resize(600, 600); - QApplication::setActiveWindow(&view); view.show(); QVERIFY(QTest::qWaitForWindowActive(&view)); - QPoint topButtonTopLeftCorner = view.mapFromScene(scene.topButton->scenePos()); - QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, topButtonTopLeftCorner); + QPoint topButtonTopLeftCorner = view.mapFromScene(topButton->scenePos()); + QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, topButtonTopLeftCorner); // move to the bottom right corner (buttons are placed in the top left corner) - QCOMPARE(scene.hoverButton->hoverLeaveReceived, false); + QCOMPARE(hoverButton->hoverLeaveReceived, false); QTest::mouseMove(view.viewport(), view.viewport()->rect().bottomRight()); - QTRY_COMPARE(scene.hoverButton->hoverLeaveReceived, true); + if (QGuiApplication::platformName() == QLatin1String("cocoa")) { + // The "Second button" does not receive QEvent::HoverLeave + QEXPECT_FAIL("", "This test fails only on Cocoa. Investigate why. See QTBUG-69219", Continue); + } + QTRY_COMPARE(hoverButton->hoverLeaveReceived, true); } static QByteArray msgPointMismatch(const QPoint &actual, const QPoint &expected) @@ -3758,7 +3525,6 @@ void tst_QGraphicsProxyWidget::mapToGlobal() // QTBUG-41135 childWidget->resize(embeddedWidget->size() / 2); childWidget->move(embeddedWidget->width() / 4, embeddedWidget->height() / 4); // center in embeddedWidget scene.addWidget(embeddedWidget); - QApplication::setActiveWindow(&view); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); const QPoint embeddedCenter = embeddedWidget->rect().center(); @@ -3766,8 +3532,9 @@ void tst_QGraphicsProxyWidget::mapToGlobal() // QTBUG-41135 QCOMPARE(embeddedWidget->mapFromGlobal(embeddedCenterGlobal), embeddedCenter); // This should be equivalent to the view center give or take rounding // errors due to odd window margins + const int Tolerance = qCeil(4 * view.devicePixelRatio()); const QPoint viewCenter = view.geometry().center(); - QVERIFY2((viewCenter - embeddedCenterGlobal).manhattanLength() <= 3, + QVERIFY2((viewCenter - embeddedCenterGlobal).manhattanLength() <= Tolerance, msgPointMismatch(embeddedCenterGlobal, viewCenter).constData()); // Same test with child centered on embeddedWidget. Also make sure @@ -3777,7 +3544,7 @@ void tst_QGraphicsProxyWidget::mapToGlobal() // QTBUG-41135 const QPoint childCenter = childWidget->rect().center(); const QPoint childCenterGlobal = childWidget->mapToGlobal(childCenter); QCOMPARE(childWidget->mapFromGlobal(childCenterGlobal), childCenter); - QVERIFY2((viewCenter - childCenterGlobal).manhattanLength() <= 4, + QVERIFY2((viewCenter - childCenterGlobal).manhattanLength() <= Tolerance, msgPointMismatch(childCenterGlobal, viewCenter).constData()); } @@ -3832,7 +3599,7 @@ class TouchWidget : public QWidget public: TouchWidget(QWidget *parent = nullptr) : QWidget(parent) {} - bool event(QEvent *event) + bool event(QEvent *event) override { switch (event->type()) { case QEvent::TouchBegin: @@ -3840,7 +3607,6 @@ public: case QEvent::TouchEnd: event->accept(); return true; - break; default: break; } @@ -3849,48 +3615,416 @@ public: } }; +#if QT_CONFIG(wheelevent) +/*! + QGraphicsProxyWidget receives wheel events from QGraphicsScene, and then + generates a new event that is sent spontaneously in order to enable event + propagation. This requires extra handling of the wheel grabbing we do for + high-precision wheel event streams. + + Test that this doesn't trigger infinite recursion, while still resulting in + event propagation within the embedded widget hierarchy, and back to the + QGraphicsView if the event is not accepted. + + See tst_QApplication::wheelEventPropagation for a similar test. +*/ +void tst_QGraphicsProxyWidget::wheelEventPropagation() +{ + QGraphicsScene scene(0, 0, 600, 600); + + QWidget *label = new QLabel("Direct"); + label->setFixedSize(300, 30); + QGraphicsProxyWidget *labelProxy = scene.addWidget(label); + labelProxy->setPos(0, 50); + labelProxy->show(); + + class NestedWidget : public QWidget + { + public: + NestedWidget(const QString &text) + { + setObjectName("Nested Label"); + QLabel *nested = new QLabel(text); + QHBoxLayout *hbox = new QHBoxLayout; + hbox->addWidget(nested); + setLayout(hbox); + } + + int wheelEventCount = 0; + protected: + void wheelEvent(QWheelEvent *) override + { + ++wheelEventCount; + } + }; + NestedWidget *nestedWidget = new NestedWidget("Nested"); + nestedWidget->setFixedSize(300, 60); + QGraphicsProxyWidget *nestedProxy = scene.addWidget(nestedWidget); + nestedProxy->setPos(0, 120); + nestedProxy->show(); + + QGraphicsView view(&scene); + view.setFixedHeight(200); + view.show(); + + QVERIFY(QTest::qWaitForWindowActive(&view)); + QVERIFY(view.verticalScrollBar()->isVisible()); + + view.verticalScrollBar()->setValue(0); + QSignalSpy scrollSpy(view.verticalScrollBar(), &QScrollBar::valueChanged); + + const QPoint wheelPosition(50, 25); + auto wheelUp = [&view, wheelPosition](Qt::ScrollPhase phase) { + const QPoint global = view.mapToGlobal(wheelPosition); + const QPoint pixelDelta(0, -25); + const QPoint angleDelta(0, -120); + QWindowSystemInterface::handleWheelEvent(view.windowHandle(), wheelPosition, global, + pixelDelta, angleDelta, Qt::NoModifier, + phase); + QCoreApplication::processEvents(); + }; + + int scrollCount = 0; + // test non-kinetic events; they are not grabbed, and should scroll the view unless + // accepted by the embedded widget + QCOMPARE(view.itemAt(wheelPosition), nullptr); + wheelUp(Qt::NoScrollPhase); + QCOMPARE(scrollSpy.size(), ++scrollCount); + + // wheeling on the label, which ignores the event, should scroll the view + QCOMPARE(view.itemAt(wheelPosition), labelProxy); + wheelUp(Qt::NoScrollPhase); + QCOMPARE(scrollSpy.size(), ++scrollCount); + QCOMPARE(view.itemAt(wheelPosition), labelProxy); + wheelUp(Qt::NoScrollPhase); + QCOMPARE(scrollSpy.size(), ++scrollCount); + + // left the widget + QCOMPARE(view.itemAt(wheelPosition), nullptr); + wheelUp(Qt::NoScrollPhase); + QCOMPARE(scrollSpy.size(), ++scrollCount); + + // reached the nested widget, which accepts the wheel event, so no more scrolling + QCOMPARE(view.itemAt(wheelPosition), nestedProxy); + // remember this position for later + const int scrollBarValueOnNestedProxy = view.verticalScrollBar()->value(); + wheelUp(Qt::NoScrollPhase); + QCOMPARE(scrollSpy.size(), scrollCount); + QCOMPARE(nestedWidget->wheelEventCount, 1); + + // reset, try with kinetic events + view.verticalScrollBar()->setValue(0); + ++scrollCount; + + // starting a scroll outside any widget and scrolling through the widgets should work, + // no matter if the widget accepts wheel events - the view has the grab + QCOMPARE(view.itemAt(wheelPosition), nullptr); + wheelUp(Qt::ScrollBegin); + QCOMPARE(scrollSpy.size(), ++scrollCount); + for (int i = 0; i < 5; ++i) { + wheelUp(Qt::ScrollUpdate); + QCOMPARE(scrollSpy.size(), ++scrollCount); + } + wheelUp(Qt::ScrollEnd); + QCOMPARE(scrollSpy.size(), ++scrollCount); + + // reset + view.verticalScrollBar()->setValue(0); + scrollCount = scrollSpy.size(); + + // starting a scroll on a widget that doesn't accept wheel events + // should also scroll the view, which still gets the grab + wheelUp(Qt::NoScrollPhase); + scrollCount = scrollSpy.size(); + + QCOMPARE(view.itemAt(wheelPosition), labelProxy); + wheelUp(Qt::ScrollBegin); + QCOMPARE(scrollSpy.size(), ++scrollCount); + for (int i = 0; i < 5; ++i) { + wheelUp(Qt::ScrollUpdate); + QCOMPARE(scrollSpy.size(), ++scrollCount); + } + wheelUp(Qt::ScrollEnd); + QCOMPARE(scrollSpy.size(), ++scrollCount); + + // starting a scroll on a widget that does accept wheel events + // should not scroll the view + view.verticalScrollBar()->setValue(scrollBarValueOnNestedProxy); + scrollCount = scrollSpy.size(); + + QCOMPARE(view.itemAt(wheelPosition), nestedProxy); + wheelUp(Qt::ScrollBegin); + QCOMPARE(scrollSpy.size(), scrollCount); +} +#endif // QT_CONFIG(wheelevent) + // QTBUG_45737 void tst_QGraphicsProxyWidget::forwardTouchEvent() { - QGraphicsScene *scene = new QGraphicsScene; + QGraphicsScene scene; TouchWidget *widget = new TouchWidget; - widget->setAttribute(Qt::WA_AcceptTouchEvents); - QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget; - + QGraphicsProxyWidget *proxy = scene.addWidget(widget); proxy->setAcceptTouchEvents(true); - proxy->setWidget(widget); - - scene->addItem(proxy); - QGraphicsView *view = new QGraphicsView(scene); - - view->show(); + QGraphicsView view(&scene); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); EventSpy eventSpy(widget); QPointingDevice *device = QTest::createTouchDevice(); + QVERIFY(device); QCOMPARE(eventSpy.counts[QEvent::TouchBegin], 0); QCOMPARE(eventSpy.counts[QEvent::TouchUpdate], 0); QCOMPARE(eventSpy.counts[QEvent::TouchEnd], 0); - QTest::touchEvent(view, device).press(0, QPoint(10, 10), view); - QTest::touchEvent(view, device).move(0, QPoint(15, 15), view); - QTest::touchEvent(view, device).move(0, QPoint(16, 16), view); - QTest::touchEvent(view, device).release(0, QPoint(15, 15), view); + QTest::touchEvent(&view, device).press(0, QPoint(10, 10), &view); + QTest::touchEvent(&view, device).move(0, QPoint(15, 15), &view); + QTest::touchEvent(&view, device).move(0, QPoint(16, 16), &view); + QTest::touchEvent(&view, device).release(0, QPoint(15, 15), &view); QApplication::processEvents(); QCOMPARE(eventSpy.counts[QEvent::TouchBegin], 1); QCOMPARE(eventSpy.counts[QEvent::TouchUpdate], 2); QCOMPARE(eventSpy.counts[QEvent::TouchEnd], 1); +} - delete view; - delete proxy; - delete scene; +void tst_QGraphicsProxyWidget::touchEventPropagation() +{ + QGraphicsScene scene(0, 0, 300, 200); + QWidget *simpleWidget = new QWidget; + simpleWidget->setObjectName("simpleWidget"); + simpleWidget->setAttribute(Qt::WA_AcceptTouchEvents, true); + QGraphicsProxyWidget *simpleProxy = scene.addWidget(simpleWidget); + simpleProxy->setAcceptTouchEvents(true); + simpleProxy->setGeometry(QRectF(0, 0, 30, 30)); + + QWidget *formWidget = new QWidget; + formWidget->setObjectName("formWidget"); + formWidget->setAttribute(Qt::WA_AcceptTouchEvents, true); + QPushButton *pushButton1 = new QPushButton("One"); + pushButton1->setObjectName("pushButton1"); + pushButton1->setAttribute(Qt::WA_AcceptTouchEvents, true); + QPushButton *pushButton2 = new QPushButton("Two"); + pushButton2->setObjectName("pushButton2"); + pushButton2->setAttribute(Qt::WA_AcceptTouchEvents, true); + TouchWidget *touchWidget1 = new TouchWidget; + touchWidget1->setObjectName("touchWidget1"); + touchWidget1->setAttribute(Qt::WA_AcceptTouchEvents, true); + touchWidget1->setFixedSize(pushButton1->sizeHint()); + TouchWidget *touchWidget2 = new TouchWidget; + touchWidget2->setObjectName("touchWidget2"); + touchWidget2->setAttribute(Qt::WA_AcceptTouchEvents, true); + touchWidget2->setFixedSize(pushButton2->sizeHint()); + QVBoxLayout *vbox = new QVBoxLayout; + vbox->addWidget(pushButton1); + vbox->addWidget(pushButton2); + vbox->addWidget(touchWidget1); + vbox->addWidget(touchWidget2); + formWidget->setLayout(vbox); + QGraphicsProxyWidget *formProxy = scene.addWidget(formWidget); + formProxy->setAcceptTouchEvents(true); + formProxy->setGeometry(QRectF(50, 50, 200, 160)); + + QGraphicsView view(&scene); + view.setFixedSize(scene.width(), scene.height()); + view.verticalScrollBar()->setValue(0); + view.horizontalScrollBar()->setValue(0); + view.viewport()->setObjectName("GraphicsView's Viewport"); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + class TouchEventSpy : public QObject + { + public: + using QObject::QObject; + + struct TouchRecord { + QObject *receiver; + QEvent::Type eventType; + QPointF position; + }; + QHash<int, QList<TouchRecord>> records; + QWidget *mousePressReceiver = nullptr; + + int count(int id = 0) const { return records.value(id).size(); } + TouchRecord at(int i, int id = 0) const { return records.value(id).at(i); } + void clear() + { + records.clear(); + mousePressReceiver = nullptr; + } + protected: + bool eventFilter(QObject *receiver, QEvent *event) override + { + switch (event->type()) { + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchCancel: + case QEvent::TouchEnd: { + QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event); + // instead of detaching each QEventPoint, just store the relative positions + for (const auto &touchPoint : touchEvent->points()) + records[touchPoint.id()] << TouchRecord{receiver, event->type(), touchPoint.position()}; + qCDebug(lcTests) << "Recording" << event << receiver; + break; + } + case QEvent::MouseButtonPress: + mousePressReceiver = qobject_cast<QWidget*>(receiver); + break; + default: + break; + } + return QObject::eventFilter(receiver, event); + } + } eventSpy; + qApp->installEventFilter(&eventSpy); + + auto touchDevice = QTest::createTouchDevice(); + const QPointF simpleCenter = simpleProxy->geometry().center(); + + // On systems without double conversion we might get different rounding behavior. + // One pixel off in any direction is acceptable for this test. + constexpr auto closeEnough = [](QPointF exp, QPointF act) -> bool { + const QRectF expArea(exp - QPointF(1., 1.), exp + QPointF(1., 1.)); + const bool contains = expArea.contains(act); + if (!contains) + qWarning() << act << "not in" << exp; + return contains; + }; + + // verify that the embedded widget gets the correctly translated event + QTest::touchEvent(&view, touchDevice).press(0, simpleCenter.toPoint()); + // window, viewport, scene, simpleProxy, simpleWidget + QCOMPARE(eventSpy.count(), 5); + QCOMPARE(eventSpy.at(0).receiver, view.windowHandle()); + QCOMPARE(eventSpy.at(1).receiver, view.viewport()); + QCOMPARE(eventSpy.at(2).receiver, &scene); + QCOMPARE(eventSpy.at(3).receiver, simpleProxy); + auto record = eventSpy.at(4); + QCOMPARE(record.receiver, simpleWidget); + QCOMPARE(record.eventType, QEvent::TouchBegin); + QVERIFY(closeEnough(record.position, simpleCenter)); + eventSpy.clear(); + + // verify that the layout of formWidget is how we expect it to be + QCOMPARE(formWidget->childAt(QPoint(5, 5)), nullptr); + const QPoint pb1Center = pushButton1->rect().center(); + QCOMPARE(formWidget->childAt(pushButton1->pos() + pb1Center), pushButton1); + const QPoint pb2Center = pushButton2->rect().center(); + QCOMPARE(formWidget->childAt(pushButton2->pos() + pb2Center), pushButton2); + const QPoint tw1Center = touchWidget1->rect().center(); + QCOMPARE(formWidget->childAt(touchWidget1->pos() + tw1Center), touchWidget1); + const QPoint tw2Center = touchWidget2->rect().center(); + QCOMPARE(formWidget->childAt(touchWidget2->pos() + tw2Center), touchWidget2); + + // touch events are sent to the view, in view coordinates + const QPoint formProxyPox = view.mapFromScene(formProxy->pos().toPoint()); + const QPoint pb1TouchPos = pushButton1->pos() + pb1Center + formProxyPox; + const QPoint pb2TouchPos = pushButton2->pos() + pb2Center + formProxyPox; + const QPoint tw1TouchPos = touchWidget1->pos() + tw1Center + formProxyPox; + const QPoint tw2TouchPos = touchWidget2->pos() + tw2Center + formProxyPox; + + QSignalSpy clickedSpy(pushButton1, &QPushButton::clicked); + // Single touch point to nested widget not accepting event. + // Event should bubble up and translate correctly, TouchUpdate and TouchEnd events + // stop at the window since nobody accepted the TouchBegin. A mouse event will be generated. + QTest::touchEvent(&view, touchDevice).press(0, pb1TouchPos); + QTest::touchEvent(&view, touchDevice).move(0, pb1TouchPos + QPoint(1, 1)); + QTest::touchEvent(&view, touchDevice).release(0, pb1TouchPos + QPoint(1, 1)); + // ..., formProxy, pushButton1, formWidget, window, window + QCOMPARE(eventSpy.count(), 8); + QCOMPARE(eventSpy.at(3).receiver, formProxy); // formProxy dispatches to the right subwidget + record = eventSpy.at(4); + QCOMPARE(record.receiver, pushButton1); + QVERIFY(closeEnough(record.position, pb1Center)); + QCOMPARE(record.eventType, QEvent::TouchBegin); + // pushButton doesn't accept the point, so the TouchBegin propagates to parent + record = eventSpy.at(5); + QCOMPARE(record.receiver, formWidget); + QVERIFY(closeEnough(record.position, pushButton1->pos() + pb1Center)); + QCOMPARE(record.eventType, QEvent::TouchBegin); + record = eventSpy.at(6); + QCOMPARE(record.receiver, view.windowHandle()); + QCOMPARE(record.eventType, QEvent::TouchUpdate); + record = eventSpy.at(7); + QCOMPARE(record.receiver, view.windowHandle()); + QCOMPARE(record.eventType, QEvent::TouchEnd); + QCOMPARE(eventSpy.mousePressReceiver, pushButton1); + QCOMPARE(clickedSpy.size(), 1); + eventSpy.clear(); + clickedSpy.clear(); + + // Single touch point to nested widget accepting event. + QTest::touchEvent(&view, touchDevice).press(0, tw1TouchPos); + QTest::touchEvent(&view, touchDevice).move(0, tw1TouchPos + QPoint(5, 5)); + QTest::touchEvent(&view, touchDevice).release(0, tw1TouchPos + QPoint(5, 5)); + // Press: ..., formProxy, touchWidget1 (5) + // Move: window, touchWidget1 (2) + // Release: window, touchWidget1 (2) + QCOMPARE(eventSpy.count(), 9); + QCOMPARE(eventSpy.at(3).receiver, formProxy); // form proxy dispatches TouchBegin to the right widget + record = eventSpy.at(4); + QCOMPARE(record.receiver, touchWidget1); + QVERIFY(closeEnough(record.position, tw1Center)); + QCOMPARE(record.eventType, QEvent::TouchBegin); + QCOMPARE(eventSpy.at(5).receiver, view.windowHandle()); // QWidgetWindow dispatches TouchUpdate + record = eventSpy.at(6); + QCOMPARE(record.receiver, touchWidget1); + QVERIFY(closeEnough(record.position, tw1Center + QPoint(5, 5))); + QCOMPARE(record.eventType, QEvent::TouchUpdate); + QCOMPARE(eventSpy.at(7).receiver, view.windowHandle()); // QWidgetWindow dispatches TouchEnd + record = eventSpy.at(8); + QCOMPARE(record.receiver, touchWidget1); + QVERIFY(closeEnough(record.position, tw1Center + QPoint(5, 5))); + QCOMPARE(record.eventType, QEvent::TouchEnd); + eventSpy.clear(); + + // to simplify the remaining test, install the event spy explicitly on the target widgets + qApp->removeEventFilter(&eventSpy); + formWidget->installEventFilter(&eventSpy); + pushButton1->installEventFilter(&eventSpy); + pushButton2->installEventFilter(&eventSpy); + touchWidget1->installEventFilter(&eventSpy); + touchWidget2->installEventFilter(&eventSpy); + + // multi-touch to different widgets, some do and some don't accept the event + QTest::touchEvent(&view, touchDevice) + .press(0, pb1TouchPos) + .press(1, tw1TouchPos) + .press(2, pb2TouchPos) + .press(3, tw2TouchPos); + QTest::touchEvent(&view, touchDevice) + .move(0, pb1TouchPos + QPoint(1, 1)) + .move(1, tw1TouchPos + QPoint(1, 1)) + .move(2, pb2TouchPos + QPoint(1, 1)) + .move(3, tw2TouchPos + QPoint(1, 1)); + QTest::touchEvent(&view, touchDevice) + .release(0, pb1TouchPos + QPoint(1, 1)) + .release(1, tw1TouchPos + QPoint(1, 1)) + .release(2, pb2TouchPos + QPoint(1, 1)) + .release(3, tw2TouchPos + QPoint(1, 1)); + + QCOMPARE(eventSpy.count(0), 2); // Begin never accepted, so move up and then stop + QCOMPARE(eventSpy.count(1), 3); // Begin accepted, so not propagated and update/end received + QCOMPARE(eventSpy.count(2), 2); // Begin never accepted + QCOMPARE(eventSpy.count(3), 3); // Begin accepted + QCOMPARE(eventSpy.at(0, 0).receiver, pushButton1); + QCOMPARE(eventSpy.at(1, 0).receiver, formWidget); + QCOMPARE(eventSpy.at(0, 1).receiver, touchWidget1); + QCOMPARE(eventSpy.at(1, 1).receiver, touchWidget1); + QCOMPARE(eventSpy.at(2, 1).receiver, touchWidget1); + QCOMPARE(eventSpy.at(0, 2).receiver, pushButton2); + QCOMPARE(eventSpy.at(1, 2).receiver, formWidget); + QCOMPARE(eventSpy.at(0, 3).receiver, touchWidget2); + QCOMPARE(eventSpy.at(1, 3).receiver, touchWidget2); + QCOMPARE(eventSpy.at(2, 3).receiver, touchWidget2); + QCOMPARE(clickedSpy.size(), 0); // multi-touch event does not synthesize a mouse event } QTEST_MAIN(tst_QGraphicsProxyWidget) diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/BLACKLIST b/tests/auto/widgets/graphicsview/qgraphicsscene/BLACKLIST deleted file mode 100644 index c3797c9d57..0000000000 --- a/tests/auto/widgets/graphicsview/qgraphicsscene/BLACKLIST +++ /dev/null @@ -1,5 +0,0 @@ -[isActive] -opensuse-42.3 ci -[tabFocus_sceneWithNestedFocusWidgets] -opensuse-42.3 - diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/CMakeLists.txt b/tests/auto/widgets/graphicsview/qgraphicsscene/CMakeLists.txt index cf71701669..c0dd8995b8 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsscene/CMakeLists.txt +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/CMakeLists.txt @@ -1,36 +1,20 @@ -# Generated from qgraphicsscene.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgraphicsscene Test: ##################################################################### -qt_internal_add_test(tst_qgraphicsscene - LOWDPI # special case - SOURCES - tst_qgraphicsscene.cpp - DEFINES - QT_NO_CAST_TO_ASCII - SRCDIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}\\\" - PUBLIC_LIBRARIES - Qt::CorePrivate - Qt::Gui - Qt::GuiPrivate - Qt::Widgets - Qt::WidgetsPrivate -) +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgraphicsscene LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() # Resources: -set(images_resource_files - "Ash_European.jpg" -) -qt_internal_add_resource(tst_qgraphicsscene "images" - PREFIX - "/" - FILES - ${images_resource_files} -) set(testdata_resource_files + "Ash_European.jpg" "testData/render/all-all-45-deg-left.png" "testData/render/all-all-45-deg-right.png" "testData/render/all-all-scale-2x.png" @@ -61,18 +45,26 @@ set(testdata_resource_files "testData/render/topright-topleft-untransformed.png" ) -qt_internal_add_resource(tst_qgraphicsscene "testdata" - PREFIX - "/" - FILES - ${testdata_resource_files} +qt_internal_add_test(tst_qgraphicsscene + LOWDPI + SOURCES + tst_qgraphicsscene.cpp + DEFINES + QT_NO_CAST_TO_ASCII + LIBRARIES + Qt::CorePrivate + Qt::Gui + Qt::GuiPrivate + Qt::Widgets + Qt::WidgetsPrivate + TESTDATA ${testdata_resource_files} + BUILTIN_TESTDATA ) - ## Scopes: ##################################################################### qt_internal_extend_target(tst_qgraphicsscene CONDITION WIN32 - PUBLIC_LIBRARIES + LIBRARIES user32 ) diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/testdata.qrc b/tests/auto/widgets/graphicsview/qgraphicsscene/testdata.qrc deleted file mode 100644 index 07a0579cdc..0000000000 --- a/tests/auto/widgets/graphicsview/qgraphicsscene/testdata.qrc +++ /dev/null @@ -1,32 +0,0 @@ -<RCC> - <qresource prefix="/"> - <file>testData/render/all-all-45-deg-left.png</file> - <file>testData/render/all-all-45-deg-right.png</file> - <file>testData/render/all-all-scale-2x.png</file> - <file>testData/render/all-all-translate-0-50.png</file> - <file>testData/render/all-all-translate-50-0.png</file> - <file>testData/render/all-all-untransformed-clip-ellipse.png</file> - <file>testData/render/all-all-untransformed-clip-rect.png</file> - <file>testData/render/all-all-untransformed.png</file> - <file>testData/render/all-bottomleft-untransformed.png</file> - <file>testData/render/all-bottomright-untransformed.png</file> - <file>testData/render/all-topleft-untransformed.png</file> - <file>testData/render/all-topright-untransformed.png</file> - <file>testData/render/bottom-bottomright-untransformed.png</file> - <file>testData/render/bottom-topleft-untransformed.png</file> - <file>testData/render/bottomleft-all-untransformed.png</file> - <file>testData/render/bottomleft-topleft-untransformed.png</file> - <file>testData/render/bottomright-all-untransformed.png</file> - <file>testData/render/bottomright-topleft-untransformed.png</file> - <file>testData/render/left-bottomright-untransformed.png</file> - <file>testData/render/left-topleft-untransformed.png</file> - <file>testData/render/right-bottomright-untransformed.png</file> - <file>testData/render/right-topleft-untransformed.png</file> - <file>testData/render/top-bottomright-untransformed.png</file> - <file>testData/render/top-topleft-untransformed.png</file> - <file>testData/render/topleft-all-untransformed.png</file> - <file>testData/render/topleft-topleft-untransformed.png</file> - <file>testData/render/topright-all-untransformed.png</file> - <file>testData/render/topright-topleft-untransformed.png</file> - </qresource> -</RCC> diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp b/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp index bfb746ed95..515ba80bbe 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtWidgets/QApplication> #include <QtWidgets/QDial> @@ -56,6 +31,8 @@ #include "../../../shared/platforminputcontext.h" #include <private/qinputmethod_p.h> +#include <QtWidgets/private/qapplication_p.h> + #if defined(Q_OS_WIN) #include <QtCore/qt_windows.h> #define Q_CHECK_PAINTEVENTS \ @@ -278,6 +255,7 @@ private slots: void focusItemChangedSignal(); void minimumRenderSize(); void focusOnTouch(); + void clearSelection(); // task specific tests below me void task139710_bspTreeCrash(); @@ -294,6 +272,8 @@ private slots: void taskQTBUG_42915_focusNextPrevChild(); void taskQTBUG_85088_previewTextfailWhenLostFocus(); + void deleteItemsOnChange(); + private: QRect m_availableGeometry = QGuiApplication::primaryScreen()->availableGeometry(); QSize m_testSize; @@ -341,27 +321,27 @@ void tst_QGraphicsScene::sceneRect() QGraphicsScene scene; QSignalSpy sceneRectChanged(&scene, &QGraphicsScene::sceneRectChanged); QCOMPARE(scene.sceneRect(), QRectF()); - QCOMPARE(sceneRectChanged.count(), 0); + QCOMPARE(sceneRectChanged.size(), 0); QGraphicsRectItem *item = scene.addRect(QRectF(0, 0, 10, 10)); item->setPen(QPen(Qt::black, 0)); item->setPos(-5, -5); - QCOMPARE(sceneRectChanged.count(), 0); + QCOMPARE(sceneRectChanged.size(), 0); QCOMPARE(itemAt(scene, 0, 0), item); QVERIFY(scene.items(QPointF(10, 10)).isEmpty()); - QCOMPARE(sceneRectChanged.count(), 0); + QCOMPARE(sceneRectChanged.size(), 0); QCOMPARE(scene.sceneRect(), QRectF(-5, -5, 10, 10)); - QCOMPARE(sceneRectChanged.count(), 1); + QCOMPARE(sceneRectChanged.size(), 1); QCOMPARE(sceneRectChanged.last().at(0).toRectF(), scene.sceneRect()); item->setPos(0, 0); QCOMPARE(scene.sceneRect(), QRectF(-5, -5, 15, 15)); - QCOMPARE(sceneRectChanged.count(), 2); + QCOMPARE(sceneRectChanged.size(), 2); QCOMPARE(sceneRectChanged.last().at(0).toRectF(), scene.sceneRect()); scene.setSceneRect(-100, -100, 10, 10); - QCOMPARE(sceneRectChanged.count(), 3); + QCOMPARE(sceneRectChanged.size(), 3); QCOMPARE(sceneRectChanged.last().at(0).toRectF(), scene.sceneRect()); QCOMPARE(itemAt(scene, 0, 0), item); @@ -369,16 +349,16 @@ void tst_QGraphicsScene::sceneRect() QCOMPARE(scene.sceneRect(), QRectF(-100, -100, 10, 10)); item->setPos(10, 10); QCOMPARE(scene.sceneRect(), QRectF(-100, -100, 10, 10)); - QCOMPARE(sceneRectChanged.count(), 3); + QCOMPARE(sceneRectChanged.size(), 3); QCOMPARE(sceneRectChanged.last().at(0).toRectF(), scene.sceneRect()); scene.setSceneRect(QRectF()); QCOMPARE(itemAt(scene, 10, 10), item); QVERIFY(scene.items(QPointF(20, 20)).isEmpty()); - QCOMPARE(sceneRectChanged.count(), 4); + QCOMPARE(sceneRectChanged.size(), 4); QCOMPARE(scene.sceneRect(), QRectF(-5, -5, 25, 25)); - QCOMPARE(sceneRectChanged.count(), 5); + QCOMPARE(sceneRectChanged.size(), 5); QCOMPARE(sceneRectChanged.last().at(0).toRectF(), scene.sceneRect()); } @@ -554,7 +534,7 @@ void tst_QGraphicsScene::itemsBoundingRect() QGraphicsScene scene; - for (const auto &rect : qAsConst(rects)) { + for (const auto &rect : std::as_const(rects)) { QPainterPath path; path.addRect(rect); QGraphicsPathItem *item = scene.addPath(path); @@ -619,7 +599,7 @@ void tst_QGraphicsScene::items_QPointF() int n = 0; QList<QGraphicsItem *> addedItems; - for (const auto &rect : qAsConst(items)) { + for (const auto &rect : std::as_const(items)) { QPainterPath path; path.addRect(0, 0, rect.width(), rect.height()); @@ -966,32 +946,32 @@ void tst_QGraphicsScene::selectionChanged() { QGraphicsScene scene(0, 0, 1000, 1000); QSignalSpy spy(&scene, &QGraphicsScene::selectionChanged); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QPainterPath path; path.addRect(scene.sceneRect()); QCOMPARE(scene.selectionArea(), QPainterPath()); scene.setSelectionArea(path); QCOMPARE(scene.selectionArea(), path); - QCOMPARE(spy.count(), 0); // selection didn't change + QCOMPARE(spy.size(), 0); // selection didn't change QVERIFY(scene.selectedItems().isEmpty()); QGraphicsItem *rect = scene.addRect(QRectF(0, 0, 100, 100)); - QCOMPARE(spy.count(), 0); // selection didn't change + QCOMPARE(spy.size(), 0); // selection didn't change rect->setSelected(true); QVERIFY(!rect->isSelected()); - QCOMPARE(spy.count(), 0); // selection didn't change, item isn't selectable + QCOMPARE(spy.size(), 0); // selection didn't change, item isn't selectable rect->setFlag(QGraphicsItem::ItemIsSelectable); rect->setSelected(true); QVERIFY(rect->isSelected()); - QCOMPARE(spy.count(), 1); // selection changed + QCOMPARE(spy.size(), 1); // selection changed QCOMPARE(scene.selectedItems(), {rect}); rect->setSelected(false); QVERIFY(!rect->isSelected()); - QCOMPARE(spy.count(), 2); // selection changed + QCOMPARE(spy.size(), 2); // selection changed QVERIFY(scene.selectedItems().isEmpty()); QGraphicsEllipseItem *parentItem = new QGraphicsEllipseItem(QRectF(0, 0, 100, 100)); @@ -1003,33 +983,33 @@ void tst_QGraphicsScene::selectionChanged() grandChildItem->setSelected(true); scene.addItem(parentItem); - QCOMPARE(spy.count(), 3); // the grandchild was added, so the selection changed once + QCOMPARE(spy.size(), 3); // the grandchild was added, so the selection changed once scene.removeItem(parentItem); - QCOMPARE(spy.count(), 4); // the grandchild was removed, so the selection changed + QCOMPARE(spy.size(), 4); // the grandchild was removed, so the selection changed rect->setSelected(true); - QCOMPARE(spy.count(), 5); // the rect was reselected, so the selection changed + QCOMPARE(spy.size(), 5); // the rect was reselected, so the selection changed scene.clearSelection(); - QCOMPARE(spy.count(), 6); // the scene selection was cleared + QCOMPARE(spy.size(), 6); // the scene selection was cleared rect->setSelected(true); - QCOMPARE(spy.count(), 7); // the rect was reselected, so the selection changed + QCOMPARE(spy.size(), 7); // the rect was reselected, so the selection changed rect->setFlag(QGraphicsItem::ItemIsSelectable, false); - QCOMPARE(spy.count(), 8); // the rect was unselected, so the selection changed + QCOMPARE(spy.size(), 8); // the rect was unselected, so the selection changed rect->setSelected(true); - QCOMPARE(spy.count(), 8); // the rect is not longer selectable, so the selection does not change + QCOMPARE(spy.size(), 8); // the rect is not longer selectable, so the selection does not change rect->setFlag(QGraphicsItem::ItemIsSelectable, true); rect->setSelected(true); - QCOMPARE(spy.count(), 9); // the rect is again selectable, so the selection changed + QCOMPARE(spy.size(), 9); // the rect is again selectable, so the selection changed delete rect; - QCOMPARE(spy.count(), 10); // a selected item was deleted; selection changed + QCOMPARE(spy.size(), 10); // a selected item was deleted; selection changed } void tst_QGraphicsScene::selectionChanged2() @@ -1042,7 +1022,7 @@ void tst_QGraphicsScene::selectionChanged2() item1->setFlag(QGraphicsItem::ItemIsSelectable); item2->setFlag(QGraphicsItem::ItemIsSelectable); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); { QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); event.setScenePos(QPointF(50, 50)); @@ -1057,7 +1037,7 @@ void tst_QGraphicsScene::selectionChanged2() } QVERIFY(item1->isSelected()); QVERIFY(!item2->isSelected()); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); { QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); event.setScenePos(QPointF(150, 150)); @@ -1072,7 +1052,7 @@ void tst_QGraphicsScene::selectionChanged2() } QVERIFY(!item1->isSelected()); QVERIFY(item2->isSelected()); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); { QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMousePress); event.setScenePos(QPointF(50, 50)); @@ -1082,7 +1062,7 @@ void tst_QGraphicsScene::selectionChanged2() } QVERIFY(!item1->isSelected()); QVERIFY(item2->isSelected()); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); { QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); event.setScenePos(QPointF(50, 50)); @@ -1091,7 +1071,7 @@ void tst_QGraphicsScene::selectionChanged2() } QVERIFY(item1->isSelected()); QVERIFY(!item2->isSelected()); - QCOMPARE(spy.count(), 3); + QCOMPARE(spy.size(), 3); } void tst_QGraphicsScene::addItem() @@ -1218,10 +1198,10 @@ void tst_QGraphicsScene::addPath() QVERIFY(scene.items(QPointF(10, 10)).isEmpty()); QVERIFY(scene.items(QPointF(-10, 20)).isEmpty()); QVERIFY(scene.items(QPointF(10, 20)).isEmpty()); -if (sizeof(qreal) != sizeof(double)) - QWARN("Skipping test because of rounding errors when qreal != double"); -else - QVERIFY(scene.items(QPointF(-10, 30)).isEmpty()); + if (sizeof(qreal) != sizeof(double)) + qWarning("Skipping test because of rounding errors when qreal != double"); + else + QVERIFY(scene.items(QPointF(-10, 30)).isEmpty()); QVERIFY(scene.items(QPointF(10.1, 30)).isEmpty()); } @@ -1314,7 +1294,6 @@ void tst_QGraphicsScene::removeItem() view.setWindowTitle(QTest::currentTestFunction()); view.setFixedSize(150, 150); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QTest::mouseMove(view.windowHandle(), view.mapFromScene(hoverItem->scenePos() + QPointF(20, 20))); QTRY_VERIFY(!hoverItem->isHovered); @@ -1622,7 +1601,6 @@ void tst_QGraphicsScene::hoverEvents_siblings() view.rotate(10); view.scale(1.7, 1.7); view.show(); - QApplication::setActiveWindow(&view); view.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&view)); @@ -1692,7 +1670,6 @@ void tst_QGraphicsScene::hoverEvents_parentChild() view.rotate(10); view.scale(1.7, 1.7); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove); @@ -1754,7 +1731,7 @@ void tst_QGraphicsScene::createItemGroup() // All items in children1 are children of parent1 QGraphicsItem *parent1 = scene.addRect(QRectF(-10, -10, 20, 20)); - for (QGraphicsItem *item : qAsConst(children1)) + for (QGraphicsItem *item : std::as_const(children1)) item->setParentItem(parent1); QGraphicsItemGroup *group = scene.createItemGroup(children1); @@ -1770,7 +1747,7 @@ void tst_QGraphicsScene::createItemGroup() // All items in children2 are children of parent2 QGraphicsItem *parent2 = scene.addRect(QRectF(-10, -10, 20, 20)); - for (QGraphicsItem *item : qAsConst(children2)) + for (QGraphicsItem *item : std::as_const(children2)) item->setParentItem(parent2); // Now make parent2 a child of parent1, so all children2 are also children @@ -1794,7 +1771,7 @@ void tst_QGraphicsScene::createItemGroup() QCOMPARE(children2.first()->parentItem(), parent1); // Fixup the parent-child chain - for (QGraphicsItem *item : qAsConst(children2)) + for (QGraphicsItem *item : std::as_const(children2)) item->setParentItem(parent2); // These share no common parent @@ -1804,7 +1781,7 @@ void tst_QGraphicsScene::createItemGroup() // Make children3 children of parent3 QGraphicsItem *parent3 = scene.addRect(QRectF(-10, -10, 20, 20)); - for (QGraphicsItem *item : qAsConst(children3)) + for (QGraphicsItem *item : std::as_const(children3)) item->setParentItem(parent3); // These should have parent3 as a parent @@ -2697,9 +2674,12 @@ void tst_QGraphicsScene::render() void tst_QGraphicsScene::renderItemsWithNegativeWidthOrHeight() { -#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) +#ifdef Q_OS_ANDROID QSKIP("Test only works on platforms with resizable windows"); #endif + if (QGuiApplication::platformName().startsWith(QLatin1String("eglfs"), Qt::CaseInsensitive)) + QSKIP("EGLFS does not allow resizing on top window"); + QGraphicsScene scene(0, 0, m_testSize.width(), m_testSize.height()); // Add item with negative width. @@ -2777,7 +2757,7 @@ protected: void tst_QGraphicsScene::contextMenuEvent_ItemIgnoresTransformations() { -#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) +#ifdef Q_OS_ANDROID QSKIP("Test fails on some Android devices (QTBUG-44430)"); #endif @@ -2854,7 +2834,7 @@ void tst_QGraphicsScene::update() QCoreApplication::processEvents(); // Check that the update region is correct - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QRectF region; const auto &rects = qvariant_cast<QList<QRectF> >(spy.at(0).at(0)); for (const auto &rectF : rects) @@ -2871,7 +2851,6 @@ void tst_QGraphicsScene::update2() view.resize(m_testSize); view.setScene(&scene); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_VERIFY(view.repaints >= 1); view.repaints = 0; @@ -3061,7 +3040,6 @@ void tst_QGraphicsScene::tabFocus_emptyScene() widget.setLayout(layout); widget.setWindowTitle(QTest::currentTestFunction()); widget.show(); - QApplication::setActiveWindow(&widget); widget.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&widget)); @@ -3113,7 +3091,6 @@ void tst_QGraphicsScene::tabFocus_sceneWithFocusableItems() widget.setWindowTitle(QTest::currentTestFunction()); widget.setLayout(layout); widget.show(); - QApplication::setActiveWindow(&widget); widget.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&widget)); @@ -3167,7 +3144,7 @@ void tst_QGraphicsScene::tabFocus_sceneWithFocusableItems() // Check that the correct item regains focus. widget.show(); - QApplication::setActiveWindow(&widget); + QApplicationPrivate::setActiveWindow(&widget); widget.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&widget)); QVERIFY(view->hasFocus()); @@ -3251,7 +3228,7 @@ void tst_QGraphicsScene::tabFocus_sceneWithFocusWidgets() widget.setWindowTitle(QTest::currentTestFunction()); widget.setLayout(layout); widget.show(); - QApplication::setActiveWindow(&widget); + QApplicationPrivate::setActiveWindow(&widget); widget.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&widget)); @@ -3290,7 +3267,7 @@ void tst_QGraphicsScene::tabFocus_sceneWithFocusWidgets() widget.hide(); QTest::qWait(15); widget.show(); - QApplication::setActiveWindow(&widget); + QApplicationPrivate::setActiveWindow(&widget); widget.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&widget)); QTRY_VERIFY(widget1->hasFocus()); @@ -3339,7 +3316,7 @@ void tst_QGraphicsScene::tabFocus_sceneWithNestedFocusWidgets() widget.setWindowTitle(QTest::currentTestFunction()); widget.setLayout(layout); widget.show(); - QApplication::setActiveWindow(&widget); + QApplicationPrivate::setActiveWindow(&widget); widget.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&widget)); @@ -3424,7 +3401,7 @@ void tst_QGraphicsScene::tabFocus_sceneWithNestedFocusWidgets() widget.hide(); QTest::qWait(12); widget.show(); - QApplication::setActiveWindow(&widget); + QApplicationPrivate::setActiveWindow(&widget); widget.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&widget)); QTRY_VERIFY(widget1->hasFocus()); @@ -3553,7 +3530,7 @@ void tst_QGraphicsScene::task160653_selectionChanged() QVERIFY(QTest::qWaitForWindowActive(&view)); QTest::mouseClick( view.viewport(), Qt::LeftButton, {}, view.mapFromScene(scene.items().first()->scenePos())); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } void tst_QGraphicsScene::task250680_childClip() @@ -3684,8 +3661,8 @@ void tst_QGraphicsScene::insertionOrder() { QList<QGraphicsItem*> itemList = scene.items(); - QCOMPARE(itemList.count(), numItems); - for (int i = 0; i < itemList.count(); ++i) { + QCOMPARE(itemList.size(), numItems); + for (int i = 0; i < itemList.size(); ++i) { QCOMPARE(numItems-1-i, itemList.at(i)->data(0).toInt()); } } @@ -3698,8 +3675,8 @@ void tst_QGraphicsScene::insertionOrder() { QList<QGraphicsItem*> itemList = scene.items(); - QCOMPARE(itemList.count(), numItems); - for (int i = 0; i < itemList.count(); ++i) { + QCOMPARE(itemList.size(), numItems); + for (int i = 0; i < itemList.size(); ++i) { QCOMPARE(numItems-1-i, itemList.at(i)->data(0).toInt()); } } @@ -3842,7 +3819,6 @@ void tst_QGraphicsScene::inputMethod() view.resize(m_testSize); view.show(); view.setWindowTitle(QTest::currentTestFunction()); - QApplication::setActiveWindow(&view); view.setFocus(); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), &view); @@ -4047,7 +4023,7 @@ void tst_QGraphicsScene::polishItems2() // added 10 new children. These should be polished in the next // event loop iteration. const QList<QGraphicsItem *> children = item->childItems(); - QCOMPARE(children.count(), 10); + QCOMPARE(children.size(), 10); for (QGraphicsItem *child : children) QVERIFY(!static_cast<PolishItem *>(child)->polished); @@ -4058,12 +4034,11 @@ void tst_QGraphicsScene::polishItems2() void tst_QGraphicsScene::isActive() { - if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) - QSKIP("Window activation is not supported"); - -#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) +#ifdef Q_OS_ANDROID QSKIP("Fails on Android (QTBUG-44430)"); #endif + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("Window activation is not supported"); QGraphicsScene scene1; QVERIFY(!scene1.isActive()); @@ -4087,7 +4062,6 @@ void tst_QGraphicsScene::isActive() view1->setVisible(false); toplevel1.show(); - QApplication::setActiveWindow(&toplevel1); QVERIFY(QTest::qWaitForWindowActive(&toplevel1)); QCOMPARE(QApplication::activeWindow(), &toplevel1); @@ -4116,7 +4090,7 @@ void tst_QGraphicsScene::isActive() QVERIFY(!scene2.hasFocus()); toplevel1.show(); - QApplication::setActiveWindow(&toplevel1); + QApplicationPrivate::setActiveWindow(&toplevel1); QVERIFY(QTest::qWaitForWindowActive(&toplevel1)); QCOMPARE(QApplication::activeWindow(), &toplevel1); @@ -4156,7 +4130,7 @@ void tst_QGraphicsScene::isActive() toplevel2.move(m_availableGeometry.topLeft() + QPoint(50, 50)); toplevel2.show(); - QApplication::setActiveWindow(&toplevel2); + QApplicationPrivate::setActiveWindow(&toplevel2); QVERIFY(QTest::qWaitForWindowActive(&toplevel2)); QCOMPARE(QApplication::activeWindow(), &toplevel2); @@ -4206,7 +4180,7 @@ void tst_QGraphicsScene::isActive() topLevelView.move(toplevel2.geometry().topRight() + QPoint(100, 50)); topLevelView.resize(m_testSize); topLevelView.show(); - QApplication::setActiveWindow(&topLevelView); + QApplicationPrivate::setActiveWindow(&topLevelView); topLevelView.setFocus(); QVERIFY(QTest::qWaitForWindowActive(&topLevelView)); QCOMPARE(QApplication::activeWindow(), &topLevelView); @@ -4234,7 +4208,7 @@ void tst_QGraphicsScene::isActive() QVERIFY(scene1.hasFocus()); QVERIFY(!scene2.hasFocus()); - QApplication::setActiveWindow(&toplevel2); + QApplicationPrivate::setActiveWindow(&toplevel2); QVERIFY(QTest::qWaitForWindowActive(&toplevel2)); QVERIFY(!scene1.isActive()); @@ -4264,7 +4238,6 @@ void tst_QGraphicsScene::isActive() toplevel3.show(); - QApplication::setActiveWindow(&toplevel3); QVERIFY(QTest::qWaitForWindowActive(&toplevel3)); QCOMPARE(QApplication::activeWindow(), &toplevel3); @@ -4377,7 +4350,6 @@ void tst_QGraphicsScene::removeFullyTransparentItem() view.resize(m_testSize); view.setScene(&scene); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QCoreApplication::processEvents(); // Process all queued paint events @@ -4627,13 +4599,13 @@ void tst_QGraphicsScene::zeroScale() rect1->setScale(0.00000001); QApplication::processEvents(); - QTRY_COMPARE(cl.changes.count(), 1); + QTRY_COMPARE(cl.changes.size(), 1); QGraphicsRectItem *rect2 = new QGraphicsRectItem(-0.0000001, -0.0000001, 0.0000001, 0.0000001); rect2->setScale(0.00000001); scene.addItem(rect2); rect1->setPos(20,20); QApplication::processEvents(); - QTRY_COMPARE(cl.changes.count(), 2); + QTRY_COMPARE(cl.changes.size(), 2); } void tst_QGraphicsScene::focusItemChangedSignal() @@ -4644,17 +4616,17 @@ void tst_QGraphicsScene::focusItemChangedSignal() QGraphicsScene scene; QSignalSpy spy(&scene, &QGraphicsScene::focusItemChanged); QVERIFY(spy.isValid()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); scene.setFocus(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QEvent activateEvent(QEvent::WindowActivate); QCoreApplication::sendEvent(&scene, &activateEvent); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QGraphicsRectItem *topLevelItem1 = new QGraphicsRectItem; topLevelItem1->setFlag(QGraphicsItem::ItemIsFocusable); scene.addItem(topLevelItem1); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QVERIFY(!topLevelItem1->hasFocus()); QGraphicsRectItem *topLevelItem2 = new QGraphicsRectItem; @@ -4662,7 +4634,7 @@ void tst_QGraphicsScene::focusItemChangedSignal() topLevelItem2->setFocus(); QVERIFY(!topLevelItem2->hasFocus()); scene.addItem(topLevelItem2); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QList<QVariant> arguments = spy.takeFirst(); QCOMPARE(arguments.size(), 3); QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(0)), topLevelItem2); @@ -4671,7 +4643,7 @@ void tst_QGraphicsScene::focusItemChangedSignal() QVERIFY(topLevelItem2->hasFocus()); scene.clearFocus(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.size(), 3); QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(0)), nullptr); @@ -4679,7 +4651,7 @@ void tst_QGraphicsScene::focusItemChangedSignal() QCOMPARE(qvariant_cast<Qt::FocusReason>(arguments.at(2)), Qt::OtherFocusReason); scene.setFocus(Qt::MenuBarFocusReason); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.size(), 3); QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(0)), topLevelItem2); @@ -4708,16 +4680,16 @@ void tst_QGraphicsScene::focusItemChangedSignal() QEvent deactivateEvent(QEvent::WindowDeactivate); QCoreApplication::sendEvent(&scene, &deactivateEvent); QEXPECT_FAIL("", "QTBUG-28346", Continue); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCoreApplication::sendEvent(&scene, &activateEvent); QEXPECT_FAIL("", "QTBUG-28346", Continue); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QGraphicsRectItem *panel1 = new QGraphicsRectItem; panel1->setFlags(QGraphicsItem::ItemIsPanel | QGraphicsItem::ItemIsFocusable); panel1->setFocus(); scene.addItem(panel1); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.size(), 3); QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(0)), panel1); @@ -4727,11 +4699,11 @@ void tst_QGraphicsScene::focusItemChangedSignal() QGraphicsRectItem *panel2 = new QGraphicsRectItem; panel2->setFlags(QGraphicsItem::ItemIsPanel | QGraphicsItem::ItemIsFocusable); scene.addItem(panel2); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); for (int i = 0; i < 3; ++i) { scene.setActivePanel(panel2); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.size(), 3); QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(0)), panel2); @@ -4739,7 +4711,7 @@ void tst_QGraphicsScene::focusItemChangedSignal() QCOMPARE(qvariant_cast<Qt::FocusReason>(arguments.at(2)), Qt::ActiveWindowFocusReason); scene.setActivePanel(panel1); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.size(), 3); QCOMPARE(qvariant_cast<QGraphicsItem *>(arguments.at(0)), panel1); @@ -4835,7 +4807,6 @@ void tst_QGraphicsScene::focusOnTouch() rect->setFlag(QGraphicsItem::ItemIsFocusable, true); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QVERIFY(!rect->hasFocus()); @@ -4858,6 +4829,53 @@ void tst_QGraphicsScene::focusOnTouch() QVERIFY(rect->hasFocus()); } +void tst_QGraphicsScene::clearSelection() +{ + class AlwaysSelectedItem : public QGraphicsRectItem + { + public: + using QGraphicsRectItem::QGraphicsRectItem; + protected: + QVariant itemChange(GraphicsItemChange change, const QVariant& value) override + { + if (change == ItemSelectedChange) + return true; + return QGraphicsRectItem::itemChange(change, value); + } + }; + QGraphicsScene scene; + QSignalSpy spy(&scene, &QGraphicsScene::selectionChanged); + + QGraphicsRectItem *regularRect = new QGraphicsRectItem; + regularRect->setFlag(QGraphicsItem::ItemIsSelectable); + regularRect->setRect(0, 0, 50, 50); + regularRect->setSelected(true); + AlwaysSelectedItem *selectedRect = new AlwaysSelectedItem; + selectedRect->setFlag(QGraphicsItem::ItemIsSelectable); + selectedRect->setRect(50, 50, 50, 50); + selectedRect->setSelected(true); + scene.addItem(regularRect); + scene.addItem(selectedRect); + + QCOMPARE(spy.size(), 2); + + QCOMPARE(scene.selectedItems().size(), 2); + scene.clearSelection(); + QVERIFY(!regularRect->isSelected()); + QVERIFY(selectedRect->isSelected()); + QCOMPARE(scene.selectedItems().size(), 1); + QCOMPARE(spy.size(), 3); + + delete regularRect; + QCOMPARE(spy.size(), 3); + + scene.clearSelection(); + QCOMPARE(spy.size(), 3); + + delete selectedRect; + QCOMPARE(spy.size(), 4); +} + void tst_QGraphicsScene::taskQTBUG_15977_renderWithDeviceCoordinateCache() { QGraphicsScene scene; @@ -4889,7 +4907,6 @@ void tst_QGraphicsScene::taskQTBUG_16401_focusItem() rect->setFlag(QGraphicsItem::ItemIsFocusable); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QVERIFY(!scene.focusItem()); @@ -4931,7 +4948,6 @@ void tst_QGraphicsScene::taskQTBUG_42915_focusNextPrevChild() widget2->setFocusPolicy(Qt::NoFocus); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QTest::keyEvent(QTest::Click, &view, Qt::Key_Tab); @@ -4961,6 +4977,7 @@ void tst_QGraphicsScene::taskQTBUG_85088_previewTextfailWhenLostFocus() // focusItem will lose focus QMouseEvent pressEvent(QEvent::MouseButtonPress, QPointF(0, 0), + view.viewport()->mapToGlobal(QPointF()), Qt::LeftButton, Qt::NoButton, Qt::NoModifier); QApplication::sendEvent(view.viewport(), &pressEvent); @@ -4970,5 +4987,72 @@ void tst_QGraphicsScene::taskQTBUG_85088_previewTextfailWhenLostFocus() QCOMPARE(simpleTextItem->toPlainText(), str + str); } +void tst_QGraphicsScene::deleteItemsOnChange() +{ + QGraphicsScene scene; + + class SelectionItem : public QGraphicsRectItem { + public: + QRectF boundingRect() const override { return QRectF(); } + }; + + class ChangeItem : public QGraphicsItem + { + public: + ChangeItem() + { + setFlag(QGraphicsItem::ItemIsSelectable, true); + setFlag(QGraphicsItem::ItemIsMovable, true); + } + QRectF boundingRect() const override + { + return QRectF(0,0,100,100); + } + + protected: + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override + { + painter->fillRect(boundingRect().toRect(), isSelected() ? Qt::yellow : Qt::cyan); + } + + QVariant itemChange(GraphicsItemChange change, const QVariant &value) override + { + if (change != QGraphicsItem::ItemSelectedHasChanged) + return QGraphicsItem::itemChange(change, value); + if (value.toBool()) { + selectionRect = new SelectionItem; + scene()->addItem(selectionRect); + } else { + // this recreates the selectedItems QSet inside of QGraphicsScene, + // invalidating iterators. See QTBUG-101651. + scene()->selectedItems(); + delete selectionRect; + selectionRect = nullptr; + } + return QGraphicsItem::itemChange(change, value); + } + private: + SelectionItem *selectionRect = nullptr; + }; + + ChangeItem item1; + item1.setPos(0, 0); + ChangeItem item2; + item1.setPos(50, 50); + + scene.addItem(&item1); + scene.addItem(&item2); + + QGraphicsView view; + view.setScene(&scene); + view.show(); + + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + // this should not crash - see QTBUG-101651 + QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, QPoint(120, 120)); + QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, QPoint(25, 25)); +} + QTEST_MAIN(tst_QGraphicsScene) #include "tst_qgraphicsscene.moc" diff --git a/tests/auto/widgets/graphicsview/qgraphicssceneindex/CMakeLists.txt b/tests/auto/widgets/graphicsview/qgraphicssceneindex/CMakeLists.txt index f95e618a16..867831d898 100644 --- a/tests/auto/widgets/graphicsview/qgraphicssceneindex/CMakeLists.txt +++ b/tests/auto/widgets/graphicsview/qgraphicssceneindex/CMakeLists.txt @@ -1,4 +1,11 @@ -# Generated from qgraphicssceneindex.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgraphicssceneindex LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() if(NOT QT_FEATURE_private_tests) return() @@ -11,13 +18,10 @@ endif() qt_internal_add_test(tst_qgraphicssceneindex SOURCES tst_qgraphicssceneindex.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate Qt::Widgets Qt::WidgetsPrivate ) - -#### Keys ignored in scope 1:.:.:qgraphicssceneindex.pro:<TRUE>: -# _REQUIREMENTS = "qtConfig(private_tests)" diff --git a/tests/auto/widgets/graphicsview/qgraphicssceneindex/tst_qgraphicssceneindex.cpp b/tests/auto/widgets/graphicsview/qgraphicssceneindex/tst_qgraphicssceneindex.cpp index 9645b9a7fb..b6d48b52d5 100644 --- a/tests/auto/widgets/graphicsview/qgraphicssceneindex/tst_qgraphicssceneindex.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicssceneindex/tst_qgraphicssceneindex.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -33,6 +8,7 @@ #include <private/qgraphicsscenebsptreeindex_p.h> #include <private/qgraphicssceneindex_p.h> #include <private/qgraphicsscenelinearindex_p.h> +#include <QtWidgets/private/qapplication_p.h> class tst_QGraphicsSceneIndex : public QObject { @@ -99,13 +75,13 @@ void tst_QGraphicsSceneIndex::scatteredItems() for (int i = 0; i < 10; ++i) scene.addRect(i*50, i*50, 40, 35); - QCOMPARE(scene.items(QPointF(5, 5)).count(), 1); - QCOMPARE(scene.items(QPointF(55, 55)).count(), 1); - QCOMPARE(scene.items(QPointF(-100, -100)).count(), 0); + QCOMPARE(scene.items(QPointF(5, 5)).size(), 1); + QCOMPARE(scene.items(QPointF(55, 55)).size(), 1); + QCOMPARE(scene.items(QPointF(-100, -100)).size(), 0); - QCOMPARE(scene.items(QRectF(0, 0, 10, 10)).count(), 1); - QCOMPARE(scene.items(QRectF(0, 0, 1000, 1000)).count(), 10); - QCOMPARE(scene.items(QRectF(-100, -1000, 0, 0)).count(), 0); + QCOMPARE(scene.items(QRectF(0, 0, 10, 10)).size(), 1); + QCOMPARE(scene.items(QRectF(0, 0, 1000, 1000)).size(), 10); + QCOMPARE(scene.items(QRectF(-100, -1000, 0, 0)).size(), 0); } void tst_QGraphicsSceneIndex::overlappedItems_data() @@ -124,17 +100,17 @@ void tst_QGraphicsSceneIndex::overlappedItems() for (int j = 0; j < 10; ++j) scene.addRect(i*50, j*50, 200, 200)->setPen(QPen(Qt::black, 0)); - QCOMPARE(scene.items(QPointF(5, 5)).count(), 1); - QCOMPARE(scene.items(QPointF(55, 55)).count(), 4); - QCOMPARE(scene.items(QPointF(105, 105)).count(), 9); - QCOMPARE(scene.items(QPointF(-100, -100)).count(), 0); - - QCOMPARE(scene.items(QRectF(0, 0, 1000, 1000)).count(), 100); - QCOMPARE(scene.items(QRectF(-100, -1000, 0, 0)).count(), 0); - QCOMPARE(scene.items(QRectF(0, 0, 200, 200)).count(), 16); - QCOMPARE(scene.items(QRectF(0, 0, 100, 100)).count(), 4); - QCOMPARE(scene.items(QRectF(0, 0, 1, 100)).count(), 2); - QCOMPARE(scene.items(QRectF(0, 0, 1, 1000)).count(), 10); + QCOMPARE(scene.items(QPointF(5, 5)).size(), 1); + QCOMPARE(scene.items(QPointF(55, 55)).size(), 4); + QCOMPARE(scene.items(QPointF(105, 105)).size(), 9); + QCOMPARE(scene.items(QPointF(-100, -100)).size(), 0); + + QCOMPARE(scene.items(QRectF(0, 0, 1000, 1000)).size(), 100); + QCOMPARE(scene.items(QRectF(-100, -1000, 0, 0)).size(), 0); + QCOMPARE(scene.items(QRectF(0, 0, 200, 200)).size(), 16); + QCOMPARE(scene.items(QRectF(0, 0, 100, 100)).size(), 4); + QCOMPARE(scene.items(QRectF(0, 0, 1, 100)).size(), 2); + QCOMPARE(scene.items(QRectF(0, 0, 1, 1000)).size(), 10); } void tst_QGraphicsSceneIndex::movingItems_data() @@ -153,20 +129,20 @@ void tst_QGraphicsSceneIndex::movingItems() scene.addRect(i*50, i*50, 40, 35); QGraphicsRectItem *box = scene.addRect(0, 0, 10, 10); - QCOMPARE(scene.items(QPointF(5, 5)).count(), 2); - QCOMPARE(scene.items(QPointF(-1, -1)).count(), 0); - QCOMPARE(scene.items(QRectF(0, 0, 5, 5)).count(), 2); + QCOMPARE(scene.items(QPointF(5, 5)).size(), 2); + QCOMPARE(scene.items(QPointF(-1, -1)).size(), 0); + QCOMPARE(scene.items(QRectF(0, 0, 5, 5)).size(), 2); box->setPos(10, 10); - QCOMPARE(scene.items(QPointF(9, 9)).count(), 1); - QCOMPARE(scene.items(QPointF(15, 15)).count(), 2); - QCOMPARE(scene.items(QRectF(0, 0, 1, 1)).count(), 1); + QCOMPARE(scene.items(QPointF(9, 9)).size(), 1); + QCOMPARE(scene.items(QPointF(15, 15)).size(), 2); + QCOMPARE(scene.items(QRectF(0, 0, 1, 1)).size(), 1); box->setPos(-5, -5); - QCOMPARE(scene.items(QPointF(-1, -1)).count(), 1); - QCOMPARE(scene.items(QRectF(0, 0, 1, 1)).count(), 2); + QCOMPARE(scene.items(QPointF(-1, -1)).size(), 1); + QCOMPARE(scene.items(QRectF(0, 0, 1, 1)).size(), 2); - QCOMPARE(scene.items(QRectF(0, 0, 1000, 1000)).count(), 11); + QCOMPARE(scene.items(QRectF(0, 0, 1000, 1000)).size(), 11); } void tst_QGraphicsSceneIndex::connectedToSceneRectChanged() @@ -315,7 +291,6 @@ void tst_QGraphicsSceneIndex::removeItems() QGraphicsView view(&scene); view.resize(600, 600); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); scene.removeItem(widgetChild1); @@ -347,7 +322,6 @@ void tst_QGraphicsSceneIndex::clear() QGraphicsView view(&scene); view.show(); - qApp->setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); scene.clear(); diff --git a/tests/auto/widgets/graphicsview/qgraphicstransform/CMakeLists.txt b/tests/auto/widgets/graphicsview/qgraphicstransform/CMakeLists.txt index 12ba7dd834..a11036b30f 100644 --- a/tests/auto/widgets/graphicsview/qgraphicstransform/CMakeLists.txt +++ b/tests/auto/widgets/graphicsview/qgraphicstransform/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qgraphicstransform.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgraphicstransform Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgraphicstransform LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgraphicstransform SOURCES tst_qgraphicstransform.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/graphicsview/qgraphicstransform/tst_qgraphicstransform.cpp b/tests/auto/widgets/graphicsview/qgraphicstransform/tst_qgraphicstransform.cpp index 5d5323a3a2..3ee63d288b 100644 --- a/tests/auto/widgets/graphicsview/qgraphicstransform/tst_qgraphicstransform.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicstransform/tst_qgraphicstransform.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/graphicsview/qgraphicsview/BLACKLIST b/tests/auto/widgets/graphicsview/qgraphicsview/BLACKLIST index fe3d929e9e..ae5cbf7d6a 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsview/BLACKLIST +++ b/tests/auto/widgets/graphicsview/qgraphicsview/BLACKLIST @@ -1,23 +1,7 @@ -[task255529_transformationAnchorMouseAndViewportMargins] -opensuse-leap -[cursor] -opensuse-leap [cursor2] -ubuntu-16.04 -[sendEvent] -ubuntu-16.04 -opensuse-42.3 -[resizeAnchor] -opensuse-leap -rhel-7.6 -ubuntu -[update2] -opensuse-42.3 -[itemsInRect_cosmeticAdjust] -# QTBUG-66815 -ubuntu-16.04 -[fitInView] osx +[resizeAnchor] +rhel # QTBUG-87397 [itemsAtPoint] android @@ -45,3 +29,9 @@ android android [update_ancestorClipsChildrenToShape2] android +[QTBUG_70255_scrollTo] +android # QTBUG-87397 +[embeddedViewsWithFocus] +android # QTBUG-87397 +[viewportUpdateMode2] +android # QTBUG-87397 diff --git a/tests/auto/widgets/graphicsview/qgraphicsview/CMakeLists.txt b/tests/auto/widgets/graphicsview/qgraphicsview/CMakeLists.txt index be9c4a3bc4..cf33de0b33 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsview/CMakeLists.txt +++ b/tests/auto/widgets/graphicsview/qgraphicsview/CMakeLists.txt @@ -1,16 +1,23 @@ -# Generated from qgraphicsview.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgraphicsview Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgraphicsview LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgraphicsview SOURCES tst_qgraphicsview.cpp tst_qgraphicsview.h tst_qgraphicsview_2.cpp DEFINES QT_NO_CAST_TO_ASCII - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate @@ -19,14 +26,11 @@ qt_internal_add_test(tst_qgraphicsview Qt::WidgetsPrivate ) -#### Keys ignored in scope 1:.:.:qgraphicsview.pro:<TRUE>: -# testcase.timeout = "500" - ## Scopes: ##################################################################### qt_internal_extend_target(tst_qgraphicsview CONDITION QT_FEATURE_opengl - PUBLIC_LIBRARIES + LIBRARIES Qt::OpenGL Qt::OpenGLWidgets ) diff --git a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp index e666ac24c9..7ed1f28b0a 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -50,6 +25,7 @@ #include <QtWidgets/QBoxLayout> #include <QtWidgets/QStyle> #include <QtWidgets/QPushButton> +#include <QtWidgets/QScroller> #if QT_CONFIG(opengl) #include <QtOpenGLWidgets/QOpenGLWidget> #endif @@ -64,6 +40,8 @@ #include <QtTest/private/qtesthelpers_p.h> +#include <QtWidgets/private/qapplication_p.h> + using namespace QTestPrivate; Q_DECLARE_METATYPE(ExpectedValueDescription) @@ -101,6 +79,12 @@ static void sendMouseRelease(QWidget *widget, const QPoint &point, Qt::MouseButt QApplication::sendEvent(widget, &event); } +static bool isPlatformEGLFS() +{ + static const bool isEGLFS = !QGuiApplication::platformName().compare(QLatin1String("eglfs"), Qt::CaseInsensitive); + return isEGLFS; +} + class EventSpy : public QObject { Q_OBJECT @@ -225,6 +209,7 @@ private slots: void replayMouseMove(); void itemsUnderMouse(); void embeddedViews(); + void embeddedViewsWithFocus(); void scrollAfterResize_data(); void scrollAfterResize(); void moveItemWhileScrolling_data(); @@ -265,9 +250,12 @@ private slots: void QTBUG_5859_exposedRect(); void hoverLeave(); void QTBUG_16063_microFocusRect(); + void QTBUG_70255_scrollTo(); #ifndef QT_NO_CURSOR void QTBUG_7438_cursor(); #endif + void resizeContentsOnItemDrag_data(); + void resizeContentsOnItemDrag(); public slots: void dummySlot() {} @@ -666,6 +654,8 @@ void tst_QGraphicsView::openGLViewport() { if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) QSKIP("QOpenGL is not supported on this platform."); + if (isPlatformEGLFS()) + QSKIP("", "Resizing does not work on EGLFS on top level window", Continue); QGraphicsScene scene; scene.setBackgroundBrush(Qt::white); @@ -687,16 +677,16 @@ void tst_QGraphicsView::openGLViewport() view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); - QTRY_VERIFY(spy1.count() > 0); - QTRY_VERIFY(spy2.count() >= spy1.count()); + QTRY_VERIFY(spy1.size() > 0); + QTRY_VERIFY(spy2.size() >= spy1.size()); spy1.clear(); spy2.clear(); // Now test for resize (QTBUG-52419). This is special when the viewport is // a QOpenGLWidget since the underlying FBO must also be maintained. view.resize(300, 300); - QTRY_VERIFY(spy1.count() > 0); - QTRY_VERIFY(spy2.count() >= spy1.count()); + QTRY_VERIFY(spy1.size() > 0); + QTRY_VERIFY(spy2.size() >= spy1.size()); // There is no sane way to check if the framebuffer contents got updated // (grabFramebuffer is no good for the viewport case as that does not go // through paintGL). So skip the actual verification. @@ -740,8 +730,9 @@ void tst_QGraphicsView::dragMode_scrollHand() int verticalScrollBarValue = view.verticalScrollBar()->value(); { // Press - QMouseEvent event(QEvent::MouseButtonPress, - view.viewport()->rect().center(), + auto pos = view.viewport()->rect().center(); + QMouseEvent event(QEvent::MouseButtonPress, pos, + view.viewport()->mapToGlobal(pos), Qt::LeftButton, Qt::LeftButton, {}); event.setAccepted(true); QApplication::sendEvent(view.viewport(), &event); @@ -757,8 +748,9 @@ void tst_QGraphicsView::dragMode_scrollHand() #endif { // Move - QMouseEvent event(QEvent::MouseMove, - view.viewport()->rect().center() + QPoint(10, 0), + auto pos = view.viewport()->rect().center() + QPoint(10, 0); + QMouseEvent event(QEvent::MouseMove, pos, + view.viewport()->mapToGlobal(pos), Qt::LeftButton, Qt::LeftButton, {}); event.setAccepted(true); QApplication::sendEvent(view.viewport(), &event); @@ -769,8 +761,9 @@ void tst_QGraphicsView::dragMode_scrollHand() QCOMPARE(view.verticalScrollBar()->value(), verticalScrollBarValue); { // Move - QMouseEvent event(QEvent::MouseMove, - view.viewport()->rect().center() + QPoint(10, 10), + auto pos = view.viewport()->rect().center() + QPoint(10, 10); + QMouseEvent event(QEvent::MouseMove, pos, + view.viewport()->mapToGlobal(pos), Qt::LeftButton, Qt::LeftButton, {}); event.setAccepted(true); QApplication::sendEvent(view.viewport(), &event); @@ -783,8 +776,9 @@ void tst_QGraphicsView::dragMode_scrollHand() { // Release - QMouseEvent event(QEvent::MouseButtonRelease, - view.viewport()->rect().center() + QPoint(10, 10), + auto pos = view.viewport()->rect().center() + QPoint(10, 10); + QMouseEvent event(QEvent::MouseButtonRelease, pos, + view.viewport()->mapToGlobal(pos), Qt::LeftButton, Qt::LeftButton, {}); event.setAccepted(true); QApplication::sendEvent(view.viewport(), &event); @@ -805,15 +799,17 @@ void tst_QGraphicsView::dragMode_scrollHand() // Check that a click will still unselect the item. { // Press - QMouseEvent event(QEvent::MouseButtonPress, - view.viewport()->rect().center() + QPoint(10, 10), + auto pos = view.viewport()->rect().center() + QPoint(10, 10); + QMouseEvent event(QEvent::MouseButtonPress, pos, + view.viewport()->mapToGlobal(pos), Qt::LeftButton, Qt::LeftButton, {}); QApplication::sendEvent(view.viewport(), &event); } { // Release - QMouseEvent event(QEvent::MouseButtonRelease, - view.viewport()->rect().center() + QPoint(10, 10), + auto pos = view.viewport()->rect().center() + QPoint(10, 10); + QMouseEvent event(QEvent::MouseButtonRelease, pos, + view.viewport()->mapToGlobal(pos), Qt::LeftButton, Qt::LeftButton, {}); QApplication::sendEvent(view.viewport(), &event); } @@ -862,8 +858,9 @@ void tst_QGraphicsView::dragMode_rubberBand() int verticalScrollBarValue = view.verticalScrollBar()->value(); { // Press - QMouseEvent event(QEvent::MouseButtonPress, - view.viewport()->rect().center(), + auto pos = view.viewport()->rect().center(); + QMouseEvent event(QEvent::MouseButtonPress, pos, + view.viewport()->mapToGlobal(pos), Qt::LeftButton, Qt::LeftButton, {}); event.setAccepted(true); QApplication::sendEvent(view.viewport(), &event); @@ -877,8 +874,9 @@ void tst_QGraphicsView::dragMode_rubberBand() { // Move - QMouseEvent event(QEvent::MouseMove, - view.viewport()->rect().center() + QPoint(100, 0), + auto pos = view.viewport()->rect().center() + QPoint(100, 0); + QMouseEvent event(QEvent::MouseMove, pos, + view.viewport()->mapToGlobal(pos), Qt::LeftButton, Qt::LeftButton, {}); event.setAccepted(true); QApplication::sendEvent(view.viewport(), &event); @@ -892,8 +890,9 @@ void tst_QGraphicsView::dragMode_rubberBand() { // Move - QMouseEvent event(QEvent::MouseMove, - view.viewport()->rect().center() + QPoint(100, 100), + auto pos = view.viewport()->rect().center() + QPoint(100, 100); + QMouseEvent event(QEvent::MouseMove, pos, + view.viewport()->mapToGlobal(pos), Qt::LeftButton, Qt::LeftButton, {}); event.setAccepted(true); QApplication::sendEvent(view.viewport(), &event); @@ -904,8 +903,9 @@ void tst_QGraphicsView::dragMode_rubberBand() { // Release - QMouseEvent event(QEvent::MouseButtonRelease, - view.viewport()->rect().center() + QPoint(100, 100), + auto pos = view.viewport()->rect().center() + QPoint(100, 100); + QMouseEvent event(QEvent::MouseButtonRelease, pos, + view.viewport()->mapToGlobal(pos), Qt::LeftButton, Qt::LeftButton, {}); event.setAccepted(true); QApplication::sendEvent(view.viewport(), &event); @@ -1056,10 +1056,10 @@ void tst_QGraphicsView::rotated_rubberBand() sendMousePress(view.viewport(), QPoint(midWidth - 2, 0), Qt::LeftButton); sendMouseMove(view.viewport(), QPoint(midWidth + 2, view.viewport()->height()), Qt::LeftButton, Qt::LeftButton); - QCOMPARE(scene.selectedItems().count(), dim); - foreach (const QGraphicsItem *item, scene.items()) { + QCOMPARE(scene.selectedItems().size(), dim); + const auto items = scene.items(); + for (const QGraphicsItem *item : items) QCOMPARE(item->isSelected(), item->data(0).toBool()); - } sendMouseRelease(view.viewport(), QPoint(), Qt::LeftButton); } @@ -1637,6 +1637,9 @@ void tst_QGraphicsView::itemsInRect_cosmeticAdjust_data() void tst_QGraphicsView::itemsInRect_cosmeticAdjust() { + if (isPlatformEGLFS()) + QSKIP("", "Resizing does not work on EGLFS on top level window", Continue); + QFETCH(QRect, updateRect); QFETCH(int, numPaints); QFETCH(bool, adjustForAntialiasing); @@ -1930,6 +1933,9 @@ void tst_QGraphicsView::mapToSceneRect_data() void tst_QGraphicsView::mapToSceneRect() { + if (isPlatformEGLFS()) + QSKIP("", "Resizing does not work on EGLFS on top level window", Continue); + QFETCH(QRect, viewRect); QFETCH(QPolygonF, scenePoly); QFETCH(qreal, rotation); @@ -2147,7 +2153,6 @@ void tst_QGraphicsView::sendEvent() QGraphicsView view(&scene); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view)); @@ -2215,7 +2220,6 @@ void tst_QGraphicsView::wheelEvent() // Assign a view. QGraphicsView view(&scene); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view)); @@ -2452,7 +2456,6 @@ void tst_QGraphicsView::viewportUpdateMode() // Show the view, and initialize our test. view.show(); - qApp->setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_VERIFY(!view.lastUpdateRegions.isEmpty()); @@ -2535,7 +2538,6 @@ void tst_QGraphicsView::viewportUpdateMode2() const QMargins margins = view.contentsMargins(); view.resize(200 + margins.left() + margins.right(), 200 + margins.top() + margins.bottom()); toplevel.show(); - qApp->setActiveWindow(&toplevel); QVERIFY(QTest::qWaitForWindowExposed(&toplevel)); QVERIFY(QTest::qWaitForWindowActive(&toplevel)); QTRY_VERIFY(view.painted); @@ -2747,7 +2749,8 @@ void tst_QGraphicsView::optimizationFlags_dontSavePainterState2() rectB->setTransform(QTransform::fromTranslate(200, 200)); rectB->setPen(QPen(Qt::black, 0)); - foreach (QGraphicsItem *item, scene.items()) + const auto items = scene.items(); + for (QGraphicsItem *item : items) item->setOpacity(0.6); CustomView view(&scene); @@ -2884,6 +2887,9 @@ public: void tst_QGraphicsView::scrollBarRanges() { + if (isPlatformEGLFS()) + QSKIP("", "Resizing does not work on EGLFS on top level window", Continue); + QFETCH(QByteArray, style); QFETCH(QSize, viewportSize); QFETCH(QRectF, sceneRect); @@ -3179,7 +3185,6 @@ void tst_QGraphicsView::task172231_untransformableItems() view.scale(2, 1); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view)); @@ -3241,7 +3246,6 @@ void tst_QGraphicsView::task187791_setSceneCausesUpdate() QGraphicsScene scene(0, 0, 200, 200); QGraphicsView view(&scene); view.show(); - qApp->setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowExposed(&view)); EventSpy updateSpy(view.viewport(), QEvent::Paint); @@ -3266,7 +3270,8 @@ protected: { ++mouseMoves; QGraphicsView::mouseMoveEvent(event); - foreach (QGraphicsItem *item, scene()->items()) { + const auto items = scene()->items(); + for (QGraphicsItem *item : items) { scene()->removeItem(item); delete item; } @@ -3292,7 +3297,8 @@ void tst_QGraphicsView::task186827_deleteReplayedItem() QCOMPARE(view.mouseMoves, 0); { - QMouseEvent event(QEvent::MouseMove, view.mapFromScene(25, 25), Qt::NoButton, {}, {}); + auto pos = view.mapFromScene(25, 25); + QMouseEvent event(QEvent::MouseMove, pos, view.viewport()->mapToGlobal(pos), Qt::NoButton, {}, {}); QApplication::sendEvent(view.viewport(), &event); } QCOMPARE(view.mouseMoves, 1); @@ -3300,7 +3306,8 @@ void tst_QGraphicsView::task186827_deleteReplayedItem() QTRY_COMPARE(view.mouseMoves, 1); QTest::qWait(25); { - QMouseEvent event(QEvent::MouseMove, view.mapFromScene(25, 25), Qt::NoButton, {}, {}); + auto pos = view.mapFromScene(25, 25); + QMouseEvent event(QEvent::MouseMove, pos, view.viewport()->mapToGlobal(pos), Qt::NoButton, {}, {}); QApplication::sendEvent(view.viewport(), &event); } QCOMPARE(view.mouseMoves, 2); @@ -3325,7 +3332,6 @@ void tst_QGraphicsView::task207546_focusCrash() widget.layout()->addWidget(gr2); widget.show(); widget.activateWindow(); - QApplication::setActiveWindow(&widget); QVERIFY(QTest::qWaitForWindowActive(&widget)); QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&widget)); widget.focusNextPrevChild(true); @@ -3346,8 +3352,10 @@ void tst_QGraphicsView::task210599_unsetDragWhileDragging() // Enable and do a drag { view.setDragMode(QGraphicsView::ScrollHandDrag); - QMouseEvent press(QEvent::MouseButtonPress, origPos, Qt::LeftButton, {}, {}); - QMouseEvent move(QEvent::MouseMove, step1Pos, Qt::LeftButton, {}, {}); + QMouseEvent press(QEvent::MouseButtonPress, origPos, + view.viewport()->mapToGlobal(origPos), Qt::LeftButton, {}, {}); + QMouseEvent move(QEvent::MouseMove, step1Pos, + view.viewport()->mapToGlobal(step1Pos), Qt::LeftButton, {}, {}); QApplication::sendEvent(view.viewport(), &press); QApplication::sendEvent(view.viewport(), &move); } @@ -3355,7 +3363,8 @@ void tst_QGraphicsView::task210599_unsetDragWhileDragging() // unset drag and release mouse, inverse order { view.setDragMode(QGraphicsView::NoDrag); - QMouseEvent release(QEvent::MouseButtonRelease, step1Pos, Qt::LeftButton, {}, {}); + QMouseEvent release(QEvent::MouseButtonRelease, step1Pos, + view.viewport()->mapToGlobal(step1Pos), Qt::LeftButton, {}, {}); QApplication::sendEvent(view.viewport(), &release); } @@ -3364,7 +3373,8 @@ void tst_QGraphicsView::task210599_unsetDragWhileDragging() // reset drag, and move mouse without holding button down. { view.setDragMode(QGraphicsView::ScrollHandDrag); - QMouseEvent move(QEvent::MouseMove, step2Pos, Qt::LeftButton, {}, {}); + QMouseEvent move(QEvent::MouseMove, step2Pos, + view.viewport()->mapToGlobal(step2Pos), Qt::LeftButton, {}, {}); QApplication::sendEvent(view.viewport(), &move); } @@ -3413,7 +3423,6 @@ void tst_QGraphicsView::task239729_noViewUpdate() QCOMPARE(spy.count(), 0); view->show(); - qApp->setActiveWindow(view); QVERIFY(QTest::qWaitForWindowActive(view)); QTRY_VERIFY(spy.count() >= 1); @@ -3524,6 +3533,63 @@ void tst_QGraphicsView::embeddedViews() delete v1; } +/*! + Verify that a nested graphics view and embedded widgets receive window + activation and focus correctly. + + See QTBUG-94091. +*/ +void tst_QGraphicsView::embeddedViewsWithFocus() +{ + class FocusWidget : public QWidget + { + public: + FocusWidget() { setFocusPolicy(Qt::StrongFocus); } + QSize sizeHint() const override { return QSize(100, 100); } + + int focusCount = 0; + protected: + void mousePressEvent(QMouseEvent *) override {} // accept event to avoid warning + void focusInEvent(QFocusEvent *) override { ++focusCount; } + void focusOutEvent(QFocusEvent *) override { --focusCount; } + }; + + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + + QGraphicsScene innerScene; + FocusWidget *innerWidget = new FocusWidget; + innerScene.addWidget(innerWidget); + QGraphicsView *innerView = new QGraphicsView(&innerScene); + + QGraphicsScene outerScene; + FocusWidget *outerWidget = new FocusWidget; + QGraphicsProxyWidget *outerProxy = outerScene.addWidget(outerWidget); + QGraphicsProxyWidget *nestedProxy = outerScene.addWidget(innerView); + outerProxy->setPos(0, 0); + nestedProxy->setPos(0, outerWidget->sizeHint().height()); + QGraphicsView outerView(&outerScene); + outerView.show(); + outerView.activateWindow(); + QVERIFY(QTest::qWaitForWindowActive(&outerView)); + const QPoint outerCenter(QPoint(innerWidget->sizeHint().width() / 2, + innerWidget->sizeHint().height() / 2)); + const QPoint innerCenter(outerCenter + QPoint(0, innerWidget->sizeHint().height())); + QCOMPARE(outerView.itemAt(outerCenter), outerProxy); + QCOMPARE(outerView.itemAt(innerCenter), nestedProxy); + QVERIFY(outerScene.isActive()); + QVERIFY(innerScene.isActive()); + + QCOMPARE(outerWidget->focusCount, 0); + QCOMPARE(innerWidget->focusCount, 0); + QTest::mouseClick(outerView.viewport(), Qt::LeftButton, {}, outerCenter); + QCOMPARE(outerWidget->focusCount, 1); + QCOMPARE(innerWidget->focusCount, 0); + QTest::mouseClick(outerView.viewport(), Qt::LeftButton, {}, innerCenter); + QCOMPARE(outerWidget->focusCount, 0); + QCOMPARE(innerWidget->focusCount, 1); +} + void tst_QGraphicsView::scrollAfterResize_data() { QTest::addColumn<bool>("reverse"); @@ -3592,6 +3658,9 @@ void tst_QGraphicsView::moveItemWhileScrolling_data() void tst_QGraphicsView::moveItemWhileScrolling() { + if (isPlatformEGLFS()) + QSKIP("", "Resizing does not work on EGLFS on top level window", Continue); + QFETCH(bool, adjustForAntialiasing); QFETCH(bool, changedConnected); @@ -3814,8 +3883,9 @@ void tst_QGraphicsView::mouseTracking2() EventSpy spy(&scene, QEvent::GraphicsSceneMouseMove); QCOMPARE(spy.count(), 0); - QMouseEvent event(QEvent::MouseMove,view.viewport()->rect().center(), Qt::NoButton, - Qt::MouseButtons(Qt::NoButton), {}); + auto pos = view.viewport()->rect().center(); + QMouseEvent event(QEvent::MouseMove, pos, view.viewport()->mapToGlobal(pos), + Qt::NoButton, Qt::MouseButtons(Qt::NoButton), {}); QApplication::sendEvent(view.viewport(), &event); QCOMPARE(spy.count(), 1); } @@ -3983,7 +4053,7 @@ void tst_QGraphicsView::exposeRegion() CustomView view; view.setScene(&scene); view.show(); - qApp->setActiveWindow(&view); + QApplicationPrivate::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); @@ -4057,7 +4127,6 @@ void tst_QGraphicsView::update() QVERIFY(QTest::qWaitForWindowExposed(&toplevel)); - QApplication::setActiveWindow(&toplevel); QApplication::processEvents(); QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&toplevel)); @@ -4114,6 +4183,9 @@ void tst_QGraphicsView::update2_data() void tst_QGraphicsView::update2() { + if (isPlatformEGLFS()) + QSKIP("", "Resizing does not work on EGLFS on top level window", Continue); + QFETCH(qreal, penWidth); QFETCH(bool, antialiasing); QFETCH(bool, changedConnected); @@ -4139,7 +4211,7 @@ void tst_QGraphicsView::update2() view.setFrameStyle(0); view.resize(200, 200); view.show(); - qApp->setActiveWindow(&view); + QApplicationPrivate::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_VERIFY(rect->numPaints > 0); @@ -4209,7 +4281,7 @@ void tst_QGraphicsView::update_ancestorClipsChildrenToShape() CustomView view(&scene); view.show(); - qApp->setActiveWindow(&view); + QApplicationPrivate::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_VERIFY(view.painted); @@ -4263,7 +4335,7 @@ void tst_QGraphicsView::update_ancestorClipsChildrenToShape2() CustomView view(&scene); view.show(); - qApp->setActiveWindow(&view); + QApplicationPrivate::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_VERIFY(view.painted); @@ -4324,7 +4396,6 @@ void tst_QGraphicsView::inputMethodSensitivity() QGraphicsScene scene; QGraphicsView view(&scene); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view)); @@ -4422,7 +4493,6 @@ void tst_QGraphicsView::inputContextReset() QVERIFY(view.testAttribute(Qt::WA_InputMethodEnabled)); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view)); @@ -4570,7 +4640,6 @@ void tst_QGraphicsView::task255529_transformationAnchorMouseAndViewportMargins() VpGraphicsView view(&scene); view.setWindowFlags(Qt::X11BypassWindowManagerHint); view.show(); - qApp->setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowExposed(&view)); const bool isActiveWindow = QTest::qWaitForWindowActive(&view); if (!isActiveWindow) @@ -4713,6 +4782,9 @@ void tst_QGraphicsView::QTBUG_4151_clipAndIgnore() void tst_QGraphicsView::QTBUG_5859_exposedRect() { + if (isPlatformEGLFS()) + QSKIP("", "Resizing does not work on EGLFS on top level window", Continue); + class CustomScene : public QGraphicsScene { public: @@ -4741,7 +4813,6 @@ void tst_QGraphicsView::QTBUG_5859_exposedRect() QGraphicsView view(&scene); view.scale(4.15, 4.15); view.showNormal(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); @@ -4813,7 +4884,6 @@ void tst_QGraphicsView::hoverLeave() scene.addItem(item); view.showNormal(); - qApp->setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowExposed(&view)); QWindow *viewWindow = view.window()->windowHandle(); @@ -4878,5 +4948,138 @@ void tst_QGraphicsView::QTBUG_16063_microFocusRect() QCOMPARE(mfv, IMItem::mf.translated(-view.mapToScene(view.sceneRect().toRect()).boundingRect().topLeft())); } +void tst_QGraphicsView::QTBUG_70255_scrollTo() +{ + QGraphicsView view; + QGraphicsScene scene; + view.setFixedSize(200, 200); + scene.setSceneRect(0, 0, 1000, 1000); + QGraphicsRectItem item; + item.setRect(-20, -20, 40, 40); + item.setFlag(QGraphicsItem::ItemIsMovable, true); + scene.addItem(&item); + view.setScene(&scene); + view.centerOn(0, 0); + + view.show(); + if (!QTest::qWaitForWindowExposed(&view) || !QTest::qWaitForWindowActive(&view)) + QSKIP("Failed to show and activate window"); + + QPoint point = view.mapFromScene(0, 0); + QCOMPARE(point, QPoint(0, 0)); + + QScroller::scroller(&view)->scrollTo(QPointF(0, 500), 100); + QTest::qWait(200); + + point = view.mapFromScene(0, 0); + QCOMPARE(point, QPoint(0, -500)); +} + +void tst_QGraphicsView::resizeContentsOnItemDrag_data() +{ + QTest::addColumn<Qt::Alignment>("alignment"); + QTest::addColumn<Qt::Orientation>("orientation"); + QTest::addRow("Center right") << Qt::Alignment(Qt::AlignCenter) << Qt::Horizontal; + QTest::addRow("Center down") << Qt::Alignment(Qt::AlignCenter) << Qt::Vertical; + QTest::addRow("BottomLeft right") << (Qt::AlignBottom | Qt::AlignLeft) << Qt::Horizontal; + QTest::addRow("TopRight down") << (Qt::AlignTop | Qt::AlignRight) << Qt::Vertical; +} + +void tst_QGraphicsView::resizeContentsOnItemDrag() +{ + QFETCH(Qt::Alignment, alignment); + QFETCH(Qt::Orientation, orientation); + + QGraphicsView view; + QGraphicsScene scene; + view.setFixedSize(200, 200); + view.setScene(&scene); + + view.setAlignment(alignment); + + class MovableItem : public QGraphicsEllipseItem + { + public: + using QGraphicsEllipseItem::QGraphicsEllipseItem; + + QList<QPointF> scenePositions; + + protected: + void mousePressEvent(QGraphicsSceneMouseEvent *event) override + { + scenePositions << event->scenePos(); + } + void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override + { + scenePositions << event->scenePos(); + QGraphicsEllipseItem::mouseMoveEvent(event); + } + }; + + MovableItem item(-10, -10, 20, 20); + item.setFlags(QGraphicsItem::ItemIsMovable); + scene.addItem(&item); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + // Position the item near the relevant edge of the view, with a few pixels + // to go until the scrollbars should be showing. + if (orientation == Qt::Horizontal) + item.setPos(view.width() - item.rect().width() - 5, 0); + else + item.setPos(0, view.height() - item.rect().height() - 5); + QApplication::processEvents(); // queued connection used to trigger recalculateContentSize + QPoint mousePos = view.mapFromScene(item.pos()); + + QTest::mousePress(view.viewport(), Qt::LeftButton, {}, mousePos); + QCOMPARE(item.scenePositions.count(), 1); + QCOMPARE(item.scenePositions.takeLast(), view.mapToScene(mousePos)); + + auto lastItemPos = item.pos(); + auto lastScenePos = view.mapToScene(mousePos); + int overshoot = 0; + const QScrollBar *scrollBar = orientation == Qt::Horizontal + ? view.horizontalScrollBar() + : view.verticalScrollBar(); + // Drag the item until the scroll bars become visible, and then for a few more pixels. + // Verify that the item doesn't jump when the scrollbar shows. + while (overshoot < 10) { + if (orientation == Qt::Horizontal) + mousePos.rx() += 1; + else + mousePos.ry() += 1; + QTest::mouseMove(view.viewport(), mousePos); + QApplication::processEvents(); // queued connection used to trigger recalculateContentSize + const bool scrollbarAvailable = scrollBar->maximum() > scrollBar->minimum(); + bool allowMoreEvents = false; + if (scrollbarAvailable) { + if (!overshoot) { + QTRY_VERIFY(scrollBar->isVisible()); + // scrollbar becoming visible triggers event replay, so we get more than one + allowMoreEvents = true; + } + ++overshoot; + } + if (allowMoreEvents) + QCOMPARE_GE(item.scenePositions.count(), 1); + else + QCOMPARE(item.scenePositions.count(), 1); + const auto scenePos = item.scenePositions.takeLast(); + item.scenePositions.clear(); + + const auto same = orientation == Qt::Horizontal ? &QPointF::y : &QPointF::x; + const auto moved = orientation == Qt::Horizontal ? &QPointF::x : &QPointF::y; + QCOMPARE((item.pos().*same)(), (lastItemPos.*same)()); + QCOMPARE_GE((item.pos().*moved)() - (lastItemPos.*moved)(), 1); + QCOMPARE_LE((item.pos().*moved)() - (lastItemPos.*moved)(), 2); + lastItemPos = item.pos(); + + QCOMPARE((scenePos.*same)(), (lastScenePos.*same)()); + QCOMPARE_GE((scenePos.*moved)() - (lastScenePos.*moved)(), 1); + QCOMPARE_LE((scenePos.*moved)() - (lastScenePos.*moved)(), 2); + lastScenePos = scenePos; + } +} + QTEST_MAIN(tst_QGraphicsView) #include "tst_qgraphicsview.moc" diff --git a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.h b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.h index 42e9e6e2a3..2fb5ed638c 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.h +++ b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TST_QGRAPHICSVIEW_H #define TST_QGRAPHICSVIEW_H diff --git a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview_2.cpp b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview_2.cpp index 72b5acc654..1380e007e7 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview_2.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview_2.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QtCore/QSize> #include <QtCore/QRectF> diff --git a/tests/auto/widgets/graphicsview/qgraphicswidget/BLACKLIST b/tests/auto/widgets/graphicsview/qgraphicswidget/BLACKLIST new file mode 100644 index 0000000000..cad381b7b7 --- /dev/null +++ b/tests/auto/widgets/graphicsview/qgraphicswidget/BLACKLIST @@ -0,0 +1,8 @@ +[initStyleOption] +macos ci +android +b2qt +# QTBUG-100930 +[updateFocusChainWhenChildDie] +b2qt +qnx diff --git a/tests/auto/widgets/graphicsview/qgraphicswidget/CMakeLists.txt b/tests/auto/widgets/graphicsview/qgraphicswidget/CMakeLists.txt index 8cf7f92cc3..42e30e4f5d 100644 --- a/tests/auto/widgets/graphicsview/qgraphicswidget/CMakeLists.txt +++ b/tests/auto/widgets/graphicsview/qgraphicswidget/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qgraphicswidget.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgraphicswidget Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgraphicswidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgraphicswidget SOURCES tst_qgraphicswidget.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/graphicsview/qgraphicswidget/tst_qgraphicswidget.cpp b/tests/auto/widgets/graphicsview/qgraphicswidget/tst_qgraphicswidget.cpp index c22195fada..9fd93ca91d 100644 --- a/tests/auto/widgets/graphicsview/qgraphicswidget/tst_qgraphicswidget.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicswidget/tst_qgraphicswidget.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -41,6 +16,9 @@ #include <qcommonstyle.h> #include <qstylefactory.h> #include <qscreen.h> +#include <qsignalspy.h> + +#include <QtWidgets/private/qapplication_p.h> typedef QList<QGraphicsItem *> QGraphicsItemList; @@ -57,7 +35,7 @@ public: int count() const { return _count; } protected: - bool eventFilter(QObject *watched, QEvent *event) + bool eventFilter(QObject *watched, QEvent *event) override { Q_UNUSED(watched); if (event->type() == spied) @@ -151,7 +129,6 @@ private slots: void polishEvent2(); void autoFillBackground(); void initialShow(); - void initialShow2(); void itemChangeEvents(); void itemSendGeometryPosChangesDeactivated(); void fontPropagatesResolveToChildren(); @@ -169,8 +146,16 @@ private slots: void QT_BUG_6544_tabFocusFirstUnsetWhenRemovingItems(); void QT_BUG_12056_tabFocusFirstUnsetWhenRemovingItems(); void QTBUG_45867_send_itemChildAddedChange_to_parent(); + +private: + static bool hasWindowActivation(); }; +bool tst_QGraphicsWidget::hasWindowActivation() +{ + return (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)); +} + // Subclass that exposes the protected functions. class SubQGraphicsWidget : public QGraphicsWidget { public: @@ -178,7 +163,7 @@ public: : QGraphicsWidget(parent, windowFlags), eventCount(0) { } - void initStyleOption(QStyleOption *option) const + void initStyleOption(QStyleOption *option) const override { QGraphicsWidget::initStyleOption(option); } void call_changeEvent(QEvent* event) @@ -232,7 +217,7 @@ public: int eventCount; Qt::LayoutDirection m_painterLayoutDirection; - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override { m_painterLayoutDirection = painter->layoutDirection(); QGraphicsWidget::paint(painter, option, widget); @@ -244,7 +229,7 @@ public: } protected: - bool event(QEvent *event) + bool event(QEvent *event) override { eventCount++; return QGraphicsWidget::event(event); @@ -271,7 +256,7 @@ public: } protected: - QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const override { Q_UNUSED(constraint); return m_sizes[which]; @@ -518,11 +503,14 @@ void tst_QGraphicsWidget::focusWidget2() class FocusWatchWidget : public QGraphicsWidget { public: - FocusWatchWidget(QGraphicsItem *parent = nullptr) : QGraphicsWidget(parent) { gotFocusInCount = 0; gotFocusOutCount = 0; } + FocusWatchWidget(QGraphicsItem *parent = nullptr) + : QGraphicsWidget(parent) { gotFocusInCount = 0; gotFocusOutCount = 0; } int gotFocusInCount, gotFocusOutCount; protected: - void focusInEvent(QFocusEvent *fe) { gotFocusInCount++; QGraphicsWidget::focusInEvent(fe); } - void focusOutEvent(QFocusEvent *fe) { gotFocusOutCount++; QGraphicsWidget::focusOutEvent(fe); } + void focusInEvent(QFocusEvent *fe) override + { gotFocusInCount++; QGraphicsWidget::focusInEvent(fe); } + void focusOutEvent(QFocusEvent *fe) override + { gotFocusOutCount++; QGraphicsWidget::focusOutEvent(fe); } }; void tst_QGraphicsWidget::focusWidget3() @@ -962,9 +950,9 @@ void tst_QGraphicsWidget::geometry() widget.setPos(pos); widget.resize(size); if (!size.isNull() && !pos.isNull()) - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); if (!size.isNull() && pos.isNull()) - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(widget.geometry(), QRectF(pos, size)); } @@ -975,10 +963,10 @@ void tst_QGraphicsWidget::geometryChanged() QCOMPARE(w.geometry(), QRectF(0, 0, 200, 200)); QSignalSpy spy(&w, SIGNAL(geometryChanged())); w.setGeometry(0, 0, 100, 100); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(w.geometry(), QRectF(0, 0, 100, 100)); w.setPos(10, 10); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); QCOMPARE(w.geometry(), QRectF(10, 10, 100, 100)); } @@ -990,10 +978,10 @@ void tst_QGraphicsWidget::width() QSignalSpy spy(&w, SIGNAL(widthChanged())); w.setProperty("width", qreal(50)); QCOMPARE(w.property("width").toReal(), qreal(50)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); //calling old school setGeometry should work too w.setGeometry(0, 0, 200, 200); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); } void tst_QGraphicsWidget::height() @@ -1003,10 +991,10 @@ void tst_QGraphicsWidget::height() QSignalSpy spy(&w, SIGNAL(heightChanged())); w.setProperty("height", qreal(50)); QCOMPARE(w.property("height").toReal(), qreal(50)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); //calling old school setGeometry should work too w.setGeometry(0, 0, 200, 200); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); } void tst_QGraphicsWidget::getContentsMargins_data() @@ -1071,7 +1059,10 @@ void tst_QGraphicsWidget::initStyleOption() QGraphicsView view(&scene); view.resize(300, 300); view.show(); - QVERIFY(QTest::qWaitForWindowActive(&view)); + if (hasWindowActivation()) + QVERIFY(QTest::qWaitForWindowActive(&view)); + else + QVERIFY(QTest::qWaitForWindowExposed(&view)); view.setAlignment(Qt::AlignTop | Qt::AlignLeft); SubQGraphicsWidget *widget = new SubQGraphicsWidget; @@ -1085,10 +1076,12 @@ void tst_QGraphicsWidget::initStyleOption() QFETCH(bool, enabled); widget->setEnabled(enabled); QFETCH(bool, focus); - if (focus) { - widget->setFlag(QGraphicsItem::ItemIsFocusable, true); - widget->setFocus(); - QVERIFY(widget->hasFocus()); + if (hasWindowActivation()) { + if (focus) { + widget->setFlag(QGraphicsItem::ItemIsFocusable, true); + widget->setFocus(); + QVERIFY(widget->hasFocus()); + } } QFETCH(bool, underMouse); if (underMouse) { @@ -1107,8 +1100,10 @@ void tst_QGraphicsWidget::initStyleOption() bool isEnabled = option.state & QStyle::State_Enabled; QCOMPARE(isEnabled, enabled); - bool hasFocus = option.state & QStyle::State_HasFocus; - QCOMPARE(hasFocus, focus); + if (hasWindowActivation()) { + bool hasFocus = option.state & QStyle::State_HasFocus; + QCOMPARE(hasFocus, focus); + } bool isUnderMouse = option.state & QStyle::State_MouseOver; QCOMPARE(isUnderMouse, underMouse); // if (layoutDirection != Qt::LeftToRight) @@ -1145,12 +1140,12 @@ void tst_QGraphicsWidget::layout() widget.setLayout(layout); QTRY_COMPARE(widget.layout(), static_cast<QGraphicsLayout*>(layout)); - for (int i = 0; i < children.count(); ++i) { + for (int i = 0; i < children.size(); ++i) { SubQGraphicsWidget *item = children[i]; QCOMPARE(item->parentWidget(), (QGraphicsWidget *)&widget); QVERIFY(item->geometry() != QRectF(0, 0, -1, -1)); } - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); // don't crash widget.setLayout(0); } @@ -1183,7 +1178,7 @@ void tst_QGraphicsWidget::layoutDirection() QCOMPARE(widget.testAttribute(Qt::WA_SetLayoutDirection), true); view->show(); QVERIFY(QTest::qWaitForWindowExposed(view.data())); - for (int i = 0; i < children.count(); ++i) { + for (int i = 0; i < children.size(); ++i) { QTRY_COMPARE(children[i]->layoutDirection(), layoutDirection); QTRY_COMPARE(children[i]->testAttribute(Qt::WA_SetLayoutDirection), false); view->update(); @@ -1216,8 +1211,6 @@ void tst_QGraphicsWidget::palettePropagation() // These colors are unlikely to be imposed on the default palette of // QWidget ;-). - QColor sysPalText(21, 22, 23); - QColor sysPalToolTipBase(12, 13, 14); QColor overridePalText(42, 43, 44); QColor overridePalToolTipBase(45, 46, 47); QColor sysPalButton(99, 98, 97); @@ -1375,6 +1368,8 @@ void tst_QGraphicsWidget::setStyle() // cleanup widget.setStyle(0); +#else + QSKIP("This test requires the Fusion style"); #endif } @@ -1393,7 +1388,6 @@ void tst_QGraphicsWidget::setTabOrder() QGraphicsScene scene; QGraphicsView view(&scene); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QGraphicsWidget *lastItem = nullptr; @@ -1421,7 +1415,7 @@ void tst_QGraphicsWidget::setTabOrder() QVERIFY(view.viewport()->hasFocus()); int currentItem = 0; - while (currentItem < children.count() - 1) { + while (currentItem < children.size() - 1) { QTest::keyPress(view.viewport(), Qt::Key_Tab); ++currentItem; QVERIFY(children[currentItem % children.size()]->hasFocus()); @@ -1465,7 +1459,6 @@ void tst_QGraphicsWidget::setTabOrderAndReparent() QGraphicsScene scene; QGraphicsView view(&scene); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), (QWidget*)&view); @@ -1570,7 +1563,7 @@ void tst_QGraphicsWidget::unsetLayoutDirection() widget.setLayoutDirection(layoutDirection); widget.unsetLayoutDirection(); QCOMPARE(widget.testAttribute(Qt::WA_SetLayoutDirection), false); - for (int i = 0; i < children.count(); ++i) { + for (int i = 0; i < children.size(); ++i) { QCOMPARE(children[i]->layoutDirection(), Qt::LeftToRight); } } @@ -1595,7 +1588,6 @@ void tst_QGraphicsWidget::verifyFocusChain() QGraphicsScene scene; QGraphicsView view(&scene); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); { @@ -1687,7 +1679,6 @@ void tst_QGraphicsWidget::verifyFocusChain() w1_2->setFocusPolicy(Qt::StrongFocus); scene.addItem(w1_2); window->show(); - QApplication::setActiveWindow(window.data()); QVERIFY(QTest::qWaitForWindowActive(window.data())); lineEdit->setFocus(); @@ -1735,13 +1726,16 @@ void tst_QGraphicsWidget::verifyFocusChain() void tst_QGraphicsWidget::updateFocusChainWhenChildDie() { + if (!hasWindowActivation()) + QSKIP("Window activation is not supported"); + const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry(); QGraphicsScene scene; QGraphicsView view(&scene); view.resize(200, 150); view.move(availableGeometry.topLeft() + QPoint(50, 50)); view.show(); - QApplication::setActiveWindow(&view); + view.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&view)); // delete item in focus chain with no focus and verify chain @@ -1770,13 +1764,8 @@ void tst_QGraphicsWidget::updateFocusChainWhenChildDie() w->setParentItem(parent); //We don't crash perfect QVERIFY(w); - const QPoint center(view.viewport()->width() / 2, view.viewport()->height() / 2); - QTest::mouseMove(view.viewport(), center); - QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, center); -#ifdef Q_OS_MAC - QEXPECT_FAIL("", "QTBUG-23699", Continue); -#endif - QTRY_COMPARE(qApp->activeWindow(), static_cast<QWidget *>(&view)); + view.activateWindow(); + QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_COMPARE(scene.focusItem(), static_cast<QGraphicsItem *>(w)); } @@ -1862,7 +1851,7 @@ enum WhichSize { MinimumSizeHint, PreferredSizeHint, MaximumSizeHint, - Size, + WidgetSize, None, }; @@ -1877,48 +1866,48 @@ void tst_QGraphicsWidget::setSizes_data() QTest::addColumn<QList<Inst>>("compareInstructions"); QTest::newRow("minSize1") << (QList<Inst>() - << Inst(Size, QSize(25, 25)) << Inst(MinimumSize, QSize(10, 10))) - << (QList<Inst>() << Inst(Size, QSize(25, 25))); - QTest::newRow("minSize2") << (QList<Inst>() << Inst(Size, QSizeF(20, 20)) + << Inst(WidgetSize, QSize(25, 25)) << Inst(MinimumSize, QSize(10, 10))) + << (QList<Inst>() << Inst(WidgetSize, QSize(25, 25))); + QTest::newRow("minSize2") << (QList<Inst>() << Inst(WidgetSize, QSizeF(20, 20)) << Inst(MinimumSize, QSizeF(25, 25))) - << (QList<Inst>() << Inst(Size, QSizeF(25, 25))); + << (QList<Inst>() << Inst(WidgetSize, QSizeF(25, 25))); QTest::newRow("minWidth1") << (QList<Inst>() - << Inst(Size, QSizeF(20, 20)) << Inst(MinimumWidth, 5.0)) - << (QList<Inst>() << Inst(Size, QSizeF(20, 20))); + << Inst(WidgetSize, QSizeF(20, 20)) << Inst(MinimumWidth, 5.0)) + << (QList<Inst>() << Inst(WidgetSize, QSizeF(20, 20))); QTest::newRow("minWidth2") << (QList<Inst>() - << Inst(Size, QSizeF(20, 20)) << Inst(MinimumWidth, 25.0)) - << (QList<Inst>() << Inst(Size, QSizeF(25, 20))); + << Inst(WidgetSize, QSizeF(20, 20)) << Inst(MinimumWidth, 25.0)) + << (QList<Inst>() << Inst(WidgetSize, QSizeF(25, 20))); QTest::newRow("minHeight1") << (QList<Inst>() - << Inst(Size, QSizeF(20, 20)) << Inst(MinimumHeight, 5.0)) - << (QList<Inst>() << Inst(Size, QSizeF(20, 20))); + << Inst(WidgetSize, QSizeF(20, 20)) << Inst(MinimumHeight, 5.0)) + << (QList<Inst>() << Inst(WidgetSize, QSizeF(20, 20))); QTest::newRow("minHeight2") << (QList<Inst>() - << Inst(Size, QSizeF(20, 20)) << Inst(MinimumHeight, 25.0)) - << (QList<Inst>() << Inst(Size, QSizeF(20, 25))); - QTest::newRow("maxSize1") << (QList<Inst>() << Inst(Size, QSizeF(40, 40)) + << Inst(WidgetSize, QSizeF(20, 20)) << Inst(MinimumHeight, 25.0)) + << (QList<Inst>() << Inst(WidgetSize, QSizeF(20, 25))); + QTest::newRow("maxSize1") << (QList<Inst>() << Inst(WidgetSize, QSizeF(40, 40)) << Inst(MaximumSize, QSizeF(30, 30))) - << (QList<Inst>() << Inst(Size, QSizeF(30, 30))); - QTest::newRow("maxSize2") << (QList<Inst>() << Inst(Size, QSizeF(40, 40)) + << (QList<Inst>() << Inst(WidgetSize, QSizeF(30, 30))); + QTest::newRow("maxSize2") << (QList<Inst>() << Inst(WidgetSize, QSizeF(40, 40)) << Inst(MaximumSize, QSizeF(30, -1))) - << (QList<Inst>() << Inst(Size, QSizeF(30, 40))); - QTest::newRow("maxSize3") << (QList<Inst>() << Inst(Size, QSizeF(40, 40)) + << (QList<Inst>() << Inst(WidgetSize, QSizeF(30, 40))); + QTest::newRow("maxSize3") << (QList<Inst>() << Inst(WidgetSize, QSizeF(40, 40)) << Inst(MaximumSize, QSizeF(-1, 30))) - << (QList<Inst>() << Inst(Size, QSizeF(40, 30))); + << (QList<Inst>() << Inst(WidgetSize, QSizeF(40, 30))); QTest::newRow("maxWidth1") << (QList<Inst>() - << Inst(Size, QSizeF(40, 40)) << Inst(MaximumWidth, 30)) - << (QList<Inst>() << Inst(Size, QSizeF(30, 40))); + << Inst(WidgetSize, QSizeF(40, 40)) << Inst(MaximumWidth, 30)) + << (QList<Inst>() << Inst(WidgetSize, QSizeF(30, 40))); QTest::newRow("maxHeight") << (QList<Inst>() - << Inst(Size, QSizeF(40, 40)) << Inst(MaximumHeight, 20)) - << (QList<Inst>() << Inst(Size, QSizeF(40, 20))); - QTest::newRow("unsetMinSize") << (QList<Inst>() << Inst(Size, QSizeF(40, 40)) + << Inst(WidgetSize, QSizeF(40, 40)) << Inst(MaximumHeight, 20)) + << (QList<Inst>() << Inst(WidgetSize, QSizeF(40, 20))); + QTest::newRow("unsetMinSize") << (QList<Inst>() << Inst(WidgetSize, QSizeF(40, 40)) << Inst(MinimumSize, QSizeF(-1, -1))) << (QList<Inst>() << Inst(MinimumSize, QSizeF(5, 5))); - QTest::newRow("unsetMaxSize") << (QList<Inst>() << Inst(Size, QSizeF(40, 40)) + QTest::newRow("unsetMaxSize") << (QList<Inst>() << Inst(WidgetSize, QSizeF(40, 40)) << Inst(MaximumSize, QSizeF(-1, -1))) << (QList<Inst>() << Inst(MaximumSize, QSizeF(500, 500))); QTest::newRow("unsetMinSize, expand size to minimumSizeHint") - << (QList<Inst>() << Inst(MinimumSize, QSize(0, 0)) << Inst(Size, QSize(1, 1)) + << (QList<Inst>() << Inst(MinimumSize, QSize(0, 0)) << Inst(WidgetSize, QSize(1, 1)) << Inst(MinimumSize, QSize(-1.0, -1.0))) - << (QList<Inst>() << Inst(Size, QSize(5, 5)) << Inst(MinimumSize, QSize(5, 5))); + << (QList<Inst>() << Inst(WidgetSize, QSize(5, 5)) << Inst(MinimumSize, QSize(5, 5))); } void tst_QGraphicsWidget::setSizes() @@ -1934,7 +1923,7 @@ void tst_QGraphicsWidget::setSizes() QSizeF max = QSizeF(50, 50); int i; - for (i = 0; i < inputInstructions.count(); ++i) { + for (i = 0; i < inputInstructions.size(); ++i) { Inst input = inputInstructions.at(i); // defaults @@ -1948,7 +1937,7 @@ void tst_QGraphicsWidget::setSizes() case MaximumSize: max = input.second.toSizeF(); break; - case Size: + case WidgetSize : widget->resize(input.second.toSizeF()); break; case MinimumWidth: @@ -1988,7 +1977,7 @@ void tst_QGraphicsWidget::setSizes() widget->setPreferredSize(pref); widget->setMaximumSize(max); - for (i = 0; i < compareInstructions.count(); ++i) { + for (i = 0; i < compareInstructions.size(); ++i) { Inst input = compareInstructions.at(i); switch (input.first) { case MinimumSize: @@ -2000,7 +1989,7 @@ void tst_QGraphicsWidget::setSizes() case MaximumSize: QTRY_COMPARE(widget->maximumSize(), input.second.toSizeF()); break; - case Size: + case WidgetSize: QTRY_COMPARE(widget->size(), input.second.toSizeF()); break; case MinimumWidth: @@ -2318,7 +2307,7 @@ public: int ngrab; int nungrab; protected: - bool sceneEvent(QEvent *event) + bool sceneEvent(QEvent *event) override { switch (event->type()) { case QEvent::GrabMouse: @@ -2333,17 +2322,17 @@ protected: return QGraphicsRectItem::sceneEvent(event); } - void mousePressEvent(QGraphicsSceneMouseEvent *) + void mousePressEvent(QGraphicsSceneMouseEvent *) override { grabMouse(); ++npress; } - void mouseReleaseEvent(QGraphicsSceneMouseEvent *) + void mouseReleaseEvent(QGraphicsSceneMouseEvent *) override { ungrabMouse(); ++nrelease; } - void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) override { ++ndoubleClick; } @@ -2369,7 +2358,7 @@ void tst_QGraphicsWidget::doubleClickAfterExplicitMouseGrab() { QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); event.setButton(Qt::LeftButton); - event.setButtons(0); + event.setButtons({}); event.ignore(); event.setScenePos(QPointF(50, 50)); qApp->sendEvent(&scene, &event); @@ -2391,7 +2380,7 @@ void tst_QGraphicsWidget::doubleClickAfterExplicitMouseGrab() { QGraphicsSceneMouseEvent event(QEvent::GraphicsSceneMouseRelease); event.setButton(Qt::LeftButton); - event.setButtons(0); + event.setButtons({}); event.ignore(); event.setScenePos(QPointF(50, 50)); qApp->sendEvent(&scene, &event); @@ -2538,7 +2527,7 @@ void tst_QGraphicsWidget::windowFlags() QCOMPARE(widget2.windowFlags(), Qt::WindowFlags(outputFlags)); // Reset flags - widget2.setWindowFlags(0); + widget2.setWindowFlags({}); QVERIFY(!widget2.windowFlags()); // Set flags back again @@ -2555,7 +2544,7 @@ void tst_QGraphicsWidget::windowFlags() QCOMPARE(widget4.windowFlags(), Qt::WindowFlags(inputFlags | Qt::FramelessWindowHint)); // Reset flags - widget4.setWindowFlags(0); + widget4.setWindowFlags({}); QVERIFY(!widget4.windowFlags()); // Set custom flags back again @@ -2565,7 +2554,7 @@ void tst_QGraphicsWidget::windowFlags() QGraphicsWidget *widget5 = new QGraphicsWidget; widget5->setWindowFlags(Qt::WindowFlags(inputFlags)); QCOMPARE(widget5->windowFlags(), Qt::WindowFlags(outputFlags)); - QGraphicsWidget window(0, Qt::Window); + QGraphicsWidget window(nullptr, Qt::Window); widget5->setParentItem(&window); QCOMPARE(widget5->windowFlags(), Qt::WindowFlags(outputFlags)); } @@ -2591,13 +2580,13 @@ public: : QGraphicsWidget(parent, wFlags) {} - void paintWindowFrame(QPainter * painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + void paintWindowFrame(QPainter * painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override { QCOMPARE(painter->opacity(), 1.0); painter->setOpacity(0.0); QGraphicsWidget::paintWindowFrame(painter, option, widget); } - void paint(QPainter * painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + void paint(QPainter * painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override { QCOMPARE(painter->opacity(), 1.0); painter->drawRect(0, 0, 100, 100); @@ -2624,7 +2613,7 @@ public: m_proxyStyle = proxyStyle; } - int pixelMetric(QStyle::PixelMetric metric, const QStyleOption *option = 0, const QWidget *widget = 0) const + int pixelMetric(QStyle::PixelMetric metric, const QStyleOption *option = 0, const QWidget *widget = 0) const override { return m_proxyStyle->pixelMetric(metric, option, widget); } @@ -2676,14 +2665,14 @@ public: int shortcutEvents; private: - bool event(QEvent *event) + bool event(QEvent *event) override { if (event->type() == QEvent::Shortcut) shortcutEvents++; return QGraphicsWidget::event(event); } - void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override { if (hasFocus()) { painter->setPen(QPen(Qt::black, 0, Qt::DashLine)); @@ -2701,7 +2690,6 @@ void tst_QGraphicsWidget::task250119_shortcutContext() QGraphicsView view; view.setScene(&scene); view.show(); - QApplication::setActiveWindow(&view); QTRY_COMPARE(QApplication::activeWindow(), (QWidget*)&view); @@ -2738,11 +2726,11 @@ void tst_QGraphicsWidget::task250119_shortcutContext() w_signal.setFocus(); QTest::keyPress(&view, Qt::Key_B); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); w_signal.clearFocus(); QTest::keyPress(&view, Qt::Key_B); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); scene.removeItem(&w_signal); } @@ -2753,7 +2741,7 @@ public: QList<QGraphicsItem *> drawnItems; protected: void drawItems(QPainter *painter, int numItems, QGraphicsItem *items[], - const QStyleOptionGraphicsItem options[], QWidget *widget = 0) + const QStyleOptionGraphicsItem options[], QWidget *widget = 0) override { drawnItems.clear(); for (int i = 0; i < numItems; ++i) @@ -2768,7 +2756,7 @@ public: RectWidget(Qt::GlobalColor color, QGraphicsItem *parent=0) : QGraphicsWidget(parent), mColor(color) {} - void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override { painter->setBrush(QBrush(mColor)); painter->drawRect(boundingRect()); @@ -2783,10 +2771,10 @@ public: RectItem(Qt::GlobalColor color, QGraphicsItem *parent=0) : QGraphicsItem(parent), mColor(color) {} - QRectF boundingRect() const + QRectF boundingRect() const override {return QRectF(10,10,50,50);} - void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override { painter->setBrush(QBrush(mColor)); painter->drawRect(boundingRect()); @@ -2844,7 +2832,7 @@ public: QList<QVariant> values; QList<QVariant> oldValues; protected: - QVariant itemChange(GraphicsItemChange change, const QVariant &value) + QVariant itemChange(GraphicsItemChange change, const QVariant &value) override { changes << change; values << value; @@ -2864,7 +2852,7 @@ protected: void tst_QGraphicsWidget::widgetSendsGeometryChanges() { ItemChangeTester widget; - widget.setFlags(0); + widget.setFlags({}); widget.clear(); QPointF pos(10, 10); @@ -2906,7 +2894,7 @@ public: setSizePolicy(sp); } - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override { Q_UNUSED(option); Q_UNUSED(widget); @@ -2918,7 +2906,7 @@ public: } protected: - QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const override { qreal w = constraint.width(); switch (which) { @@ -2941,9 +2929,6 @@ protected: void tst_QGraphicsWidget::respectHFW() { -#if defined(Q_OS_DARWIN) - QSKIP("This test is platform dependent, it fails on Apple platforms. Please fix."); -#else QGraphicsScene scene; HFWWidget *window = new HFWWidget; scene.addItem(window); @@ -2958,22 +2943,22 @@ void tst_QGraphicsWidget::respectHFW() { // here we go - simulate a interactive resize of the window QTest::mouseMove(view.data(), view->mapFromScene(71, 71)); // bottom right corner - QTest::mousePress(view->viewport(), Qt::LeftButton, 0, view->mapFromScene(71, 71), 200); + QTest::mousePress(view->viewport(), Qt::LeftButton, {}, view->mapFromScene(71, 71), 200); view->grabMouse(); // move both mouse cursor and set correct event in order to emulate resize QTest::mouseMove(view->viewport(), view->mapFromScene(60, 30), 200); - QMouseEvent e = QMouseEvent(QEvent::MouseMove, - view->mapFromScene(60, 20), - Qt::NoButton, - Qt::LeftButton, - Qt::NoModifier); + auto pos = view->mapFromScene(60, 20); + QMouseEvent e(QEvent::MouseMove, pos, view->mapToGlobal(pos), + Qt::NoButton, Qt::LeftButton, Qt::NoModifier); QApplication::sendEvent(view->viewport(), &e); view->releaseMouse(); } const QSizeF winSize = window->size(); qreal minHFW = window->effectiveSizeHint(Qt::MinimumSize, QSizeF(winSize.width(), -1)).height(); - QTRY_VERIFY(qAbs(minHFW - winSize.height()) < 1); +#ifdef Q_OS_DARWIN + QEXPECT_FAIL("", "This test is known to fail on Apple platforms.", Continue); #endif + QTRY_VERIFY(qAbs(minHFW - winSize.height()) < 1); } class PolishWidget : public QGraphicsWidget @@ -2985,13 +2970,13 @@ public: { } - void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) + void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override { painter->setBrush(QBrush(mColor)); painter->drawRect(boundingRect()); } - void polishEvent() + void polishEvent() override { if (!parentWidget()) { //We add a child in the polish event for the parent @@ -3031,9 +3016,9 @@ void tst_QGraphicsWidget::polishEvent() { class MyGraphicsWidget : public QGraphicsWidget { public: - void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) override { events << QEvent::Paint; } - void polishEvent() + void polishEvent() override { events << QEvent::Polish; } QList<QEvent::Type> events; }; @@ -3058,7 +3043,7 @@ void tst_QGraphicsWidget::polishEvent2() { class MyGraphicsWidget : public QGraphicsWidget { public: - void polishEvent() + void polishEvent() override { events << QEvent::Polish; } QList<QEvent::Type> events; }; @@ -3107,31 +3092,8 @@ void tst_QGraphicsWidget::initialShow() { public: MyGraphicsWidget() : repaints(0) {} int repaints; - void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget*) { ++repaints; } - void polishEvent() { update(); } - }; - - QGraphicsScene scene; - MyGraphicsWidget *widget = new MyGraphicsWidget; - - QGraphicsView view(&scene); - view.show(); - qApp->setActiveWindow(&view); - QVERIFY(QTest::qWaitForWindowActive(&view)); - - scene.addItem(widget); - - QTRY_COMPARE(widget->repaints, 1); -} - -void tst_QGraphicsWidget::initialShow2() -{ - class MyGraphicsWidget : public QGraphicsWidget - { public: - MyGraphicsWidget() : repaints(0) {} - int repaints; - void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget*) { ++repaints; } - void polishEvent() { update(); } + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget*) override { ++repaints; } + void polishEvent() override { update(); } }; // Don't let paint events triggered by the windowing system @@ -3146,8 +3108,7 @@ void tst_QGraphicsWidget::initialShow2() dummyView->setWindowFlags(Qt::X11BypassWindowManagerHint); EventSpy paintSpy(dummyView->viewport(), QEvent::Paint); dummyView->show(); - qApp->setActiveWindow(dummyView.data()); - QVERIFY(QTest::qWaitForWindowActive(dummyView.data())); + QVERIFY(QTest::qWaitForWindowExposed(dummyView.data())); const int expectedRepaintCount = paintSpy.count(); dummyView.reset(); @@ -3160,8 +3121,7 @@ void tst_QGraphicsWidget::initialShow2() QGraphicsView view(&scene); view.setWindowFlags(view.windowFlags()|Qt::X11BypassWindowManagerHint); view.show(); - qApp->setActiveWindow(&view); - QVERIFY(QTest::qWaitForWindowActive(&view)); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QTRY_COMPARE(widget->repaints, expectedRepaintCount); } @@ -3172,7 +3132,7 @@ void tst_QGraphicsWidget::itemChangeEvents() { public: TestGraphicsWidget() : QGraphicsWidget() {} QHash<QEvent::Type, QVariant> valueDuringEvents; - bool event(QEvent *event) { + bool event(QEvent *event) override { Q_UNUSED(event); switch (event->type()) { case QEvent::EnabledChange: { @@ -3203,11 +3163,11 @@ void tst_QGraphicsWidget::itemChangeEvents() } return true; } - void showEvent(QShowEvent *event) { + void showEvent(QShowEvent *event) override { Q_UNUSED(event); valueDuringEvents.insert(QEvent::Show, isVisible()); } - void hideEvent(QHideEvent *event) { + void hideEvent(QHideEvent *event) override { Q_UNUSED(event); valueDuringEvents.insert(QEvent::Hide, isVisible()); } @@ -3293,8 +3253,8 @@ public: void verifyTabFocus(QGraphicsScene *scene, const QList<QGraphicsWidget *> &chain, bool wrapsAround) { - QKeyEvent tabEvent(QEvent::KeyPress, Qt::Key_Tab, 0); - QKeyEvent backtabEvent(QEvent::KeyPress, Qt::Key_Backtab, 0); + QKeyEvent tabEvent(QEvent::KeyPress, Qt::Key_Tab, {}); + QKeyEvent backtabEvent(QEvent::KeyPress, Qt::Key_Backtab, {}); for (int i = 0; i < chain.size(); ++i) chain.at(i)->clearFocus(); diff --git a/tests/auto/widgets/itemviews/CMakeLists.txt b/tests/auto/widgets/itemviews/CMakeLists.txt index ec6e00ec7f..60e05fadb4 100644 --- a/tests/auto/widgets/itemviews/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from itemviews.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause add_subdirectory(qabstractitemview) add_subdirectory(qdatawidgetmapper) diff --git a/tests/auto/widgets/itemviews/qabstractitemview/BLACKLIST b/tests/auto/widgets/itemviews/qabstractitemview/BLACKLIST index 249c91c3c4..778a25b2e4 100644 --- a/tests/auto/widgets/itemviews/qabstractitemview/BLACKLIST +++ b/tests/auto/widgets/itemviews/qabstractitemview/BLACKLIST @@ -1,5 +1,4 @@ -[task200665_itemEntered] -macos # Can't move cursor (QTBUG-76312) -# QTBUG-87400 -[task200665_itemEntered] -android +[focusNextOnHide] +wayland +[selectionAutoScrolling] +wayland diff --git a/tests/auto/widgets/itemviews/qabstractitemview/CMakeLists.txt b/tests/auto/widgets/itemviews/qabstractitemview/CMakeLists.txt index e779b1a165..ca39501a95 100644 --- a/tests/auto/widgets/itemviews/qabstractitemview/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qabstractitemview/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qabstractitemview.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qabstractitemview Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qabstractitemview LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qabstractitemview SOURCES tst_qabstractitemview.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::GuiPrivate Qt::TestPrivate diff --git a/tests/auto/widgets/itemviews/qabstractitemview/qtlogo.png b/tests/auto/widgets/itemviews/qabstractitemview/qtlogo.png Binary files differnew file mode 100644 index 0000000000..4f68e162de --- /dev/null +++ b/tests/auto/widgets/itemviews/qabstractitemview/qtlogo.png diff --git a/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp b/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp index 10320c80d6..4f2495375d 100644 --- a/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp +++ b/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <private/qguiapplication_p.h> @@ -35,11 +10,13 @@ #include <QHeaderView> #include <QIdentityProxyModel> #include <QItemDelegate> +#include <QLabel> #include <QLineEdit> #include <QListWidget> #include <QProxyStyle> #include <QPushButton> #include <QScrollBar> +#include <QScroller> #include <QSignalSpy> #include <QSortFilterProxyModel> #include <QSpinBox> @@ -47,11 +24,13 @@ #include <QStringListModel> #include <QStyledItemDelegate> #include <QTableWidget> +#include <QTimer> #include <QTreeWidget> #include <QTest> #include <QVBoxLayout> #include <QtTest/private/qtesthelpers_p.h> #include <private/qabstractitemview_p.h> +#include <QtWidgets/private/qapplication_p.h> Q_DECLARE_METATYPE(Qt::ItemFlags); @@ -100,6 +79,7 @@ private slots: void selectAll(); void ctrlA(); void persistentEditorFocus(); + void pressClosesReleaseDoesntOpenEditor(); void setItemDelegate(); void setItemDelegate_data(); // The dragAndDrop() test doesn't work, and is thus disabled on Mac and Windows @@ -129,6 +109,7 @@ private slots: void QTBUG6407_extendedSelection(); void QTBUG6753_selectOnSelection(); void testDelegateDestroyEditor(); + void testDelegateDestroyEditorChild(); void testClickedSignal(); void testChangeEditorState(); void deselectInSingleSelection(); @@ -154,8 +135,24 @@ private slots: void checkFocusAfterActivationChanges_data(); void checkFocusAfterActivationChanges(); void dragSelectAfterNewPress(); + void dragWithSecondClick_data(); + void dragWithSecondClick(); + void clickAfterDoubleClick(); void selectionCommand_data(); void selectionCommand(); + void mouseSelection_data(); + void mouseSelection(); + void keepSingleSelectionOnEmptyAreaClick(); + void scrollerSmoothScroll(); + void inputMethodOpensEditor_data(); + void inputMethodOpensEditor(); + void selectionAutoScrolling_data(); + void selectionAutoScrolling(); + void testSpinBoxAsEditor_data(); + void testSpinBoxAsEditor(); + void removeIndexWhileEditing(); + void focusNextOnHide(); + private: static QAbstractItemView *viewFromString(const QByteArray &viewType, QWidget *parent = nullptr) { @@ -181,17 +178,19 @@ public: QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const override { openedEditor = new QWidget(parent); + virtualCtorCallCount++; return openedEditor; } void destroyEditor(QWidget *editor, const QModelIndex &) const override { - calledVirtualDtor = true; + virtualDtorCallCount++; editor->deleteLater(); } void changeSize() { size = QSize(50, 50); emit sizeHintChanged(QModelIndex()); } mutable QWidget *openedEditor = nullptr; QSize size; - mutable bool calledVirtualDtor = false; + mutable int virtualCtorCallCount = 0; + mutable int virtualDtorCallCount = 0; }; class DialogItemDelegate : public QStyledItemDelegate @@ -434,12 +433,12 @@ void tst_QAbstractItemView::basic_tests(QAbstractItemView *view) QVERIFY(spy.isValid()); view->setIconSize(QSize(32, 32)); QCOMPARE(view->iconSize(), QSize(32, 32)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).value<QSize>(), QSize(32, 32)); // Should this happen? view->setIconSize(QSize(-1, -1)); QCOMPARE(view->iconSize(), QSize(-1, -1)); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); QCOMPARE(view->currentIndex(), QModelIndex()); QCOMPARE(view->rootIndex(), QModelIndex()); @@ -673,9 +672,9 @@ void tst_QAbstractItemView::selectAll() GeometriesTestView view; view.setModel(&model); - QCOMPARE(view.selectedIndexes().count(), 0); + QCOMPARE(view.selectedIndexes().size(), 0); view.selectAll(); - QCOMPARE(view.selectedIndexes().count(), 4 * 4); + QCOMPARE(view.selectedIndexes().size(), 4 * 4); } void tst_QAbstractItemView::ctrlA() @@ -684,9 +683,9 @@ void tst_QAbstractItemView::ctrlA() GeometriesTestView view; view.setModel(&model); - QCOMPARE(view.selectedIndexes().count(), 0); + QCOMPARE(view.selectedIndexes().size(), 0); QTest::keyClick(&view, Qt::Key_A, Qt::ControlModifier); - QCOMPARE(view.selectedIndexes().count(), 4 * 4); + QCOMPARE(view.selectedIndexes().size(), 4 * 4); } void tst_QAbstractItemView::persistentEditorFocus() @@ -703,7 +702,7 @@ void tst_QAbstractItemView::persistentEditorFocus() //these are spinboxes because we put numbers inside const QList<QSpinBox*> list = view.viewport()->findChildren<QSpinBox*>(); - QCOMPARE(list.count(), 2); //these should be the 2 editors + QCOMPARE(list.size(), 2); //these should be the 2 editors view.setCurrentIndex(model.index(0, 0)); QCOMPARE(view.currentIndex(), model.index(0, 0)); @@ -715,8 +714,8 @@ void tst_QAbstractItemView::persistentEditorFocus() const QPoint p(5, 5); for (QSpinBox *sb : list) { QTRY_VERIFY(sb->isVisible()); - QMouseEvent mouseEvent(QEvent::MouseButtonPress, p, Qt::LeftButton, - Qt::LeftButton, Qt::NoModifier); + QMouseEvent mouseEvent(QEvent::MouseButtonPress, p, sb->mapToGlobal(p), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QCoreApplication::sendEvent(sb, &mouseEvent); if (!QApplication::focusWidget()) QSKIP("Some window managers don't handle focus that well"); @@ -724,6 +723,47 @@ void tst_QAbstractItemView::persistentEditorFocus() } } +/*! + A press into the selection area of an item being edited, but outside the editor, + closes the editor by transferring focus to the view. The corresponding release + should then not re-open the editor. + + QTBUG-20456. +*/ +void tst_QAbstractItemView::pressClosesReleaseDoesntOpenEditor() +{ + QStandardItemModel model(0, 1); + auto *parent = new QStandardItem("parent"); + for (const auto &childText : {"child1", "child2"}) { + auto *child = new QStandardItem(childText); + child->setFlags(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsEditable | Qt::ItemIsSelectable); + parent->appendRow(child); + } + model.appendRow(parent); + + QTreeView view; + view.setModel(&model); + view.setExpanded(model.indexFromItem(parent), true); + view.setSelectionMode(QAbstractItemView::SingleSelection); + view.setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::DoubleClicked); + + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + const QRect childRect = view.visualRect(model.indexFromItem(parent->child(0))); + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, childRect.center()); // select + QVERIFY(view.selectionModel()->selectedIndexes().contains(model.indexFromItem(parent->child(0)))); + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, childRect.center()); // edit + QTRY_COMPARE(view.state(), QAbstractItemView::EditingState); + QPoint inChildOutsideEditor = QPoint(view.indentation() / 2, childRect.center().y()); + QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, inChildOutsideEditor); // focus itemview, editor closes + QCOMPARE(view.state(), QAbstractItemView::NoState); + QTest::qWait(10); // process some events, let the internal timer time out + QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, inChildOutsideEditor); // should not reopen editor + QTest::qWait(QApplication::doubleClickInterval() * 2); + QCOMPARE(view.state(), QAbstractItemView::NoState); +} + #if !defined(Q_OS_MAC) && !defined(Q_OS_WIN) @@ -901,7 +941,7 @@ void tst_QAbstractItemView::dragAndDrop() if (successes < attempts) { QString msg = QString("# successes (%1) < # attempts (%2)").arg(successes).arg(attempts); - QWARN(msg.toLatin1()); + qWarning() << qPrintable(msg); } QVERIFY(successes > 0); // allow for some "event unstability" (i.e. unless // successes == 0, QAbstractItemView is probably ok!) @@ -1011,7 +1051,6 @@ void tst_QAbstractItemView::setItemDelegate() centerOnScreen(&v); moveCursorAway(&v); v.show(); - QApplication::setActiveWindow(&v); QVERIFY(QTest::qWaitForWindowActive(&v)); QModelIndex index = model.index(cellToEdit.y(), cellToEdit.x()); @@ -1220,7 +1259,6 @@ void tst_QAbstractItemView::task221955_selectedEditor() tree.show(); tree.setFocus(); tree.setCurrentIndex(tree.model()->index(1,0)); - QApplication::setActiveWindow(&tree); QVERIFY(QTest::qWaitForWindowActive(&tree)); QVERIFY(! tree.selectionModel()->selectedIndexes().contains(tree.model()->index(3,0))); @@ -1286,7 +1324,13 @@ void tst_QAbstractItemView::task200665_itemEntered() { if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) QSKIP("Wayland: This fails. Figure out why."); - + { + // skip if we can't move mouse + const QPoint cursorPos = QCursor::pos() + QPoint(10, 10); + QCursor::setPos(cursorPos); + if (!QTest::qWaitFor([cursorPos] { return QCursor::pos() == cursorPos; }, 500)) + QSKIP("Can't move mouse"); + } //we test that view will emit entered //when the scrollbar move but not the mouse itself QStandardItemModel model(1000, 1); @@ -1301,7 +1345,7 @@ void tst_QAbstractItemView::task200665_itemEntered() QSignalSpy spy(&view, &QAbstractItemView::entered); view.verticalScrollBar()->setValue(view.verticalScrollBar()->maximum()); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); } void tst_QAbstractItemView::task257481_emptyEditor() @@ -1324,17 +1368,17 @@ void tst_QAbstractItemView::task257481_emptyEditor() treeView.edit(model.index(0, 0)); QList<QLineEdit *> lineEditors = treeView.viewport()->findChildren<QLineEdit *>(); - QCOMPARE(lineEditors.count(), 1); + QCOMPARE(lineEditors.size(), 1); QVERIFY(!lineEditors.constFirst()->size().isEmpty()); treeView.edit(model.index(1, 0)); lineEditors = treeView.viewport()->findChildren<QLineEdit *>(); - QCOMPARE(lineEditors.count(), 1); + QCOMPARE(lineEditors.size(), 1); QVERIFY(!lineEditors.constFirst()->size().isEmpty()); treeView.edit(model.index(2, 0)); lineEditors = treeView.viewport()->findChildren<QLineEdit *>(); - QCOMPARE(lineEditors.count(), 1); + QCOMPARE(lineEditors.size(), 1); QVERIFY(!lineEditors.constFirst()->size().isEmpty()); } @@ -1368,7 +1412,7 @@ void tst_QAbstractItemView::shiftArrowSelectionAfterScrolling() QCOMPARE(view.currentIndex(), index1); QModelIndexList selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 2); + QCOMPARE(selected.size(), 2); QVERIFY(selected.contains(index0)); QVERIFY(selected.contains(index1)); } @@ -1408,7 +1452,8 @@ void tst_QAbstractItemView::shiftSelectionAfterRubberbandSelection() // The mouse move event has to be created manually because the QTest framework does not // contain a function for mouse moves with buttons pressed QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, pressPos); - QMouseEvent moveEvent(QEvent::MouseMove, releasePos, Qt::NoButton, Qt::LeftButton, Qt::NoModifier); + QMouseEvent moveEvent(QEvent::MouseMove, releasePos, view.viewport()->mapToGlobal(releasePos), + Qt::NoButton, Qt::LeftButton, Qt::NoModifier); bool moveEventReceived = qApp->notify(view.viewport(), &moveEvent); QVERIFY(moveEventReceived); QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, releasePos); @@ -1421,7 +1466,7 @@ void tst_QAbstractItemView::shiftSelectionAfterRubberbandSelection() // Verify that the selection worked OK QModelIndexList selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 2); + QCOMPARE(selected.size(), 2); QVERIFY(selected.contains(index1)); QVERIFY(selected.contains(index2)); @@ -1431,7 +1476,8 @@ void tst_QAbstractItemView::shiftSelectionAfterRubberbandSelection() // Repeat the same steps as above, but with a Shift-Arrow selection QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, pressPos); - QMouseEvent moveEvent2(QEvent::MouseMove, releasePos, Qt::NoButton, Qt::LeftButton, Qt::NoModifier); + QMouseEvent moveEvent2(QEvent::MouseMove, releasePos, view.viewport()->mapToGlobal(releasePos), + Qt::NoButton, Qt::LeftButton, Qt::NoModifier); moveEventReceived = qApp->notify(view.viewport(), &moveEvent2); QVERIFY(moveEventReceived); QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, releasePos); @@ -1443,7 +1489,7 @@ void tst_QAbstractItemView::shiftSelectionAfterRubberbandSelection() // Verify that the selection worked OK selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 2); + QCOMPARE(selected.size(), 2); QVERIFY(selected.contains(index1)); QVERIFY(selected.contains(index2)); } @@ -1472,7 +1518,7 @@ void tst_QAbstractItemView::ctrlRubberbandSelection() // Select item 1 view.setCurrentIndex(index1); QModelIndexList selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 1); + QCOMPARE(selected.size(), 1); QVERIFY(selected.contains(index1)); // Now press control and draw a rubberband around items 1 and 2. @@ -1481,14 +1527,15 @@ void tst_QAbstractItemView::ctrlRubberbandSelection() QPoint pressPos = view.visualRect(index1).topLeft() - QPoint(1, 1); QPoint releasePos = view.visualRect(index2).bottomRight() + QPoint(1, 1); QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::ControlModifier, pressPos); - QMouseEvent moveEvent(QEvent::MouseMove, releasePos, Qt::NoButton, Qt::LeftButton, Qt::ControlModifier); + QMouseEvent moveEvent(QEvent::MouseMove, releasePos, view.viewport()->mapToGlobal(releasePos), + Qt::NoButton, Qt::LeftButton, Qt::ControlModifier); bool moveEventReceived = qApp->notify(view.viewport(), &moveEvent); QVERIFY(moveEventReceived); QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::ControlModifier, releasePos); // Verify that item 2 is selected now selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 1); + QCOMPARE(selected.size(), 1); QVERIFY(selected.contains(index2)); } @@ -1510,7 +1557,6 @@ void tst_QAbstractItemView::QTBUG6407_extendedSelection() moveCursorAway(&view); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(&view, QApplication::activeWindow()); @@ -1521,21 +1567,21 @@ void tst_QAbstractItemView::QTBUG6407_extendedSelection() QVERIFY(view.viewport()->rect().contains(p)); QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, p); QCOMPARE(view.currentIndex(), index49); - QCOMPARE(view.selectedItems().count(), 1); + QCOMPARE(view.selectedItems().size(), 1); QModelIndex index47 = view.model()->index(47,0); p = view.visualRect(index47).center(); QVERIFY(view.viewport()->rect().contains(p)); QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, p); QCOMPARE(view.currentIndex(), index47); - QCOMPARE(view.selectedItems().count(), 3); //49, 48, 47; + QCOMPARE(view.selectedItems().size(), 3); //49, 48, 47; QModelIndex index44 = view.model()->index(44,0); p = view.visualRect(index44).center(); QVERIFY(view.viewport()->rect().contains(p)); QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, p); QCOMPARE(view.currentIndex(), index44); - QCOMPARE(view.selectedItems().count(), 6); //49 .. 44; + QCOMPARE(view.selectedItems().size(), 6); //49 .. 44; } @@ -1560,7 +1606,7 @@ void tst_QAbstractItemView::QTBUG6753_selectOnSelection() QTest::mouseMove(table.viewport(), itemRect.center()); QTest::mouseClick(table.viewport(), Qt::LeftButton, Qt::NoModifier, itemRect.center()); - QCOMPARE(table.selectedItems().count(), 1); + QCOMPARE(table.selectedItems().size(), 1); QCOMPARE(table.selectedItems().first(), table.item(item.row(), item.column())); } @@ -1571,9 +1617,31 @@ void tst_QAbstractItemView::testDelegateDestroyEditor() table.setItemDelegate(&delegate); table.edit(table.model()->index(1, 1)); QAbstractItemView *tv = &table; - QVERIFY(!delegate.calledVirtualDtor); + QCOMPARE(delegate.virtualDtorCallCount, 0); tv->closeEditor(delegate.openedEditor, QAbstractItemDelegate::NoHint); - QVERIFY(delegate.calledVirtualDtor); + QCOMPARE(delegate.virtualDtorCallCount, 1); +} + +void tst_QAbstractItemView::testDelegateDestroyEditorChild() +{ + QTreeWidget tree; + MyAbstractItemDelegate delegate; + tree.setItemDelegate(&delegate); + QTreeWidgetItem *topLevel = new QTreeWidgetItem; + QTreeWidgetItem *levelOne1 = new QTreeWidgetItem(topLevel); + QTreeWidgetItem *levelTwo1 = new QTreeWidgetItem(levelOne1); + QTreeWidgetItem *levelOne2 = new QTreeWidgetItem(topLevel); + QTreeWidgetItem *levelTwo2 = new QTreeWidgetItem(levelOne2); + tree.insertTopLevelItem(0, topLevel); + tree.openPersistentEditor(levelOne1); + tree.openPersistentEditor(levelTwo1); + tree.openPersistentEditor(levelOne2); + tree.openPersistentEditor(levelTwo2); + QCOMPARE(delegate.virtualCtorCallCount, 4); + levelOne1->removeChild(levelTwo1); + QCOMPARE(delegate.virtualDtorCallCount, 1); + topLevel->removeChild(levelOne2); + QCOMPARE(delegate.virtualDtorCallCount, 3); } void tst_QAbstractItemView::testClickedSignal() @@ -1586,7 +1654,6 @@ void tst_QAbstractItemView::testClickedSignal() centerOnScreen(&view); moveCursorAway(&view); view.showNormal(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(&view, QApplication::activeWindow()); @@ -1597,12 +1664,12 @@ void tst_QAbstractItemView::testClickedSignal() QSignalSpy clickedSpy(&view, &QTableWidget::clicked); QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, p); - QCOMPARE(clickedSpy.count(), 1); + QCOMPARE(clickedSpy.size(), 1); QTest::mouseClick(view.viewport(), Qt::RightButton, {}, p); // We expect that right-clicks do not cause the clicked() signal to // be emitted. - QCOMPARE(clickedSpy.count(), 1); + QCOMPARE(clickedSpy.size(), 1); } @@ -1637,7 +1704,6 @@ void tst_QAbstractItemView::testChangeEditorState() centerOnScreen(&view); moveCursorAway(&view); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(&view, QApplication::activeWindow()); @@ -1658,7 +1724,6 @@ void tst_QAbstractItemView::deselectInSingleSelection() QVERIFY(QTest::qWaitForWindowExposed(&view)); view.setSelectionMode(QAbstractItemView::SingleSelection); view.setEditTriggers(QAbstractItemView::NoEditTriggers); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowExposed(&view)); // mouse QModelIndex index22 = s.index(2, 2); @@ -1666,28 +1731,28 @@ void tst_QAbstractItemView::deselectInSingleSelection() QPoint clickpos = rect22.center(); QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, clickpos); QCOMPARE(view.currentIndex(), index22); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 1); QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, clickpos); QCOMPARE(view.currentIndex(), index22); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 0); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 0); // second click with modifier however does select QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, clickpos); QCOMPARE(view.currentIndex(), index22); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 1); // keyboard QTest::keyClick(&view, Qt::Key_Space, Qt::NoModifier); QCOMPARE(view.currentIndex(), index22); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 1); QTest::keyClick(&view, Qt::Key_Space, Qt::ControlModifier); QCOMPARE(view.currentIndex(), index22); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 0); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 0); // second keypress with modifier however does select QTest::keyClick(&view, Qt::Key_Space, Qt::ControlModifier); QCOMPARE(view.currentIndex(), index22); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 1); } void tst_QAbstractItemView::testNoActivateOnDisabledItem() @@ -1705,7 +1770,6 @@ void tst_QAbstractItemView::testNoActivateOnDisabledItem() moveCursorAway(&treeView); treeView.show(); - QApplication::setActiveWindow(&treeView); QVERIFY(QTest::qWaitForWindowActive(&treeView)); QSignalSpy activatedSpy(&treeView, &QAbstractItemView::activated); @@ -1715,7 +1779,7 @@ void tst_QAbstractItemView::testNoActivateOnDisabledItem() QPoint clickPos = treeView.visualRect(itemIndex).center(); QTest::mouseClick(treeView.viewport(), Qt::LeftButton, {}, clickPos); - QCOMPARE(activatedSpy.count(), 0); + QCOMPARE(activatedSpy.size(), 0); } void tst_QAbstractItemView::testFocusPolicy_data() @@ -1752,7 +1816,6 @@ void tst_QAbstractItemView::testFocusPolicy() moveCursorAway(&window); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); // itemview accepts focus => editor is closed => return focus to the itemview @@ -1790,7 +1853,6 @@ void tst_QAbstractItemView::QTBUG31411_noSelection() moveCursorAway(&window); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); qRegisterMetaType<QItemSelection>(); @@ -1809,7 +1871,7 @@ void tst_QAbstractItemView::QTBUG31411_noSelection() QVERIFY(editor2); QTest::keyClick(editor2, Qt::Key_Escape, Qt::NoModifier); - QCOMPARE(selectionChangeSpy.count(), 0); + QCOMPARE(selectionChangeSpy.size(), 0); } void tst_QAbstractItemView::QTBUG39324_settingSameInstanceOfIndexWidget() @@ -1851,7 +1913,7 @@ void tst_QAbstractItemView::shiftSelectionAfterChangingModelContents() // Click "C" QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(indexC).center()); QModelIndexList selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 1); + QCOMPARE(selected.size(), 1); QVERIFY(selected.contains(indexC)); // Insert new item "B1" @@ -1861,14 +1923,14 @@ void tst_QAbstractItemView::shiftSelectionAfterChangingModelContents() // Shift-click "D" -> we expect that "C" and "D" are selected QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, view.visualRect(indexD).center()); selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 2); + QCOMPARE(selected.size(), 2); QVERIFY(selected.contains(indexC)); QVERIFY(selected.contains(indexD)); // Click "D" QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(indexD).center()); selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 1); + QCOMPARE(selected.size(), 1); QVERIFY(selected.contains(indexD)); // Remove items "B" and "C" @@ -1880,7 +1942,7 @@ void tst_QAbstractItemView::shiftSelectionAfterChangingModelContents() // Shift-click "F" -> we expect that "D", "E", and "F" are selected QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, view.visualRect(indexF).center()); selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 3); + QCOMPARE(selected.size(), 3); QVERIFY(selected.contains(indexD)); QVERIFY(selected.contains(indexE)); QVERIFY(selected.contains(indexF)); @@ -1889,7 +1951,7 @@ void tst_QAbstractItemView::shiftSelectionAfterChangingModelContents() while (view.currentIndex() != indexA) QTest::keyClick(&view, Qt::Key_Up); selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 1); + QCOMPARE(selected.size(), 1); QVERIFY(selected.contains(indexA)); // Change the sort order @@ -1898,7 +1960,7 @@ void tst_QAbstractItemView::shiftSelectionAfterChangingModelContents() // Shift-click "F" -> All items should be selected QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, view.visualRect(indexF).center()); selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), model.rowCount()); + QCOMPARE(selected.size(), model.rowCount()); // Restore the old sort order proxyModel.sort(0, Qt::AscendingOrder); @@ -1906,7 +1968,7 @@ void tst_QAbstractItemView::shiftSelectionAfterChangingModelContents() // Click "D" QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(indexD).center()); selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 1); + QCOMPARE(selected.size(), 1); QVERIFY(selected.contains(indexD)); // Insert new item "B2" @@ -1916,7 +1978,7 @@ void tst_QAbstractItemView::shiftSelectionAfterChangingModelContents() // Press Shift+Down -> "D" and "E" should be selected. QTest::keyClick(&view, Qt::Key_Down, Qt::ShiftModifier); selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 2); + QCOMPARE(selected.size(), 2); QVERIFY(selected.contains(indexD)); QVERIFY(selected.contains(indexE)); @@ -1925,7 +1987,7 @@ void tst_QAbstractItemView::shiftSelectionAfterChangingModelContents() QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(indexA).center()); view.setCurrentIndex(indexD); selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 1); + QCOMPARE(selected.size(), 1); QVERIFY(selected.contains(indexD)); // Insert new item "B3" @@ -1935,7 +1997,7 @@ void tst_QAbstractItemView::shiftSelectionAfterChangingModelContents() // Press Shift+Down -> "D" and "E" should be selected. QTest::keyClick(&view, Qt::Key_Down, Qt::ShiftModifier); selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 2); + QCOMPARE(selected.size(), 2); QVERIFY(selected.contains(indexD)); QVERIFY(selected.contains(indexE)); } @@ -2213,14 +2275,14 @@ void tst_QAbstractItemView::testClickToSelect() // Click the center of the visualRect of item "A" QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, centerA); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.back().front().value<QRect>(), QRect(centerA, QSize(1, 1))); // Click a point slightly away from the center const QPoint nearCenterA = centerA + QPoint(1, 1); QVERIFY(visualRectA.contains(nearCenterA)); QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, nearCenterA); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); QCOMPARE(spy.back().front().value<QRect>(), QRect(nearCenterA, QSize(1, 1))); } @@ -2390,15 +2452,11 @@ void tst_QAbstractItemView::inputMethodEnabled() // Check focus by switching the activation of the window to force a focus in view->setCurrentIndex(model->index(1, 0)); - QApplication::setActiveWindow(nullptr); - QApplication::setActiveWindow(view.data()); QVERIFY(QTest::qWaitForWindowActive(view.data())); QCOMPARE(view->testAttribute(Qt::WA_InputMethodEnabled), result); view->setCurrentIndex(QModelIndex()); QVERIFY(!view->testAttribute(Qt::WA_InputMethodEnabled)); - QApplication::setActiveWindow(nullptr); - QApplication::setActiveWindow(view.data()); QVERIFY(QTest::qWaitForWindowActive(view.data())); QModelIndex index = model->index(1, 0); QPoint p = view->visualRect(index).center(); @@ -2408,8 +2466,6 @@ void tst_QAbstractItemView::inputMethodEnabled() QCOMPARE(view->testAttribute(Qt::WA_InputMethodEnabled), result); index = model->index(0, 0); - QApplication::setActiveWindow(nullptr); - QApplication::setActiveWindow(view.data()); QVERIFY(QTest::qWaitForWindowActive(view.data())); p = view->visualRect(index).center(); QTest::mouseClick(view->viewport(), Qt::LeftButton, Qt::NoModifier, p); @@ -2419,11 +2475,11 @@ void tst_QAbstractItemView::inputMethodEnabled() // There is a case when it goes to the first visible item so we // make the flags of the first item match the ones we are testing // to check the attribute correctly - QApplication::setActiveWindow(nullptr); + QApplicationPrivate::setActiveWindow(nullptr); view->setCurrentIndex(QModelIndex()); view->reset(); item->setFlags(Qt::ItemFlags(itemFlags)); - QApplication::setActiveWindow(view.data()); + QApplicationPrivate::setActiveWindow(view.data()); QVERIFY(QTest::qWaitForWindowActive(view.data())); QCOMPARE(view->testAttribute(Qt::WA_InputMethodEnabled), result); } @@ -2534,11 +2590,11 @@ void tst_QAbstractItemView::checkFocusAfterActivationChanges() QVERIFY(QTest::qWaitForWindowExposed(delegate.openedEditor)); QVERIFY(delegate.openedEditor->hasFocus()); - QApplication::setActiveWindow(&otherTopLevel); + QApplicationPrivate::setActiveWindow(&otherTopLevel); otherTopLevel.setFocus(); QTRY_VERIFY(!delegate.openedEditor); - QApplication::setActiveWindow(&w); + QApplicationPrivate::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QVERIFY(view->hasFocus()); } @@ -2578,7 +2634,7 @@ void tst_QAbstractItemView::dragSelectAfterNewPress() // Verify that the selection worked OK QModelIndexList selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 3); + QCOMPARE(selected.size(), 3); for (int i = 0; i < 2; ++i) QVERIFY(selected.contains(model.index(i, 0))); @@ -2586,8 +2642,8 @@ void tst_QAbstractItemView::dragSelectAfterNewPress() const QPoint releasePos = view.visualRect(index5).center(); // The mouse move event has to be created manually because the QTest framework does not // contain a function for mouse moves with buttons pressed - QMouseEvent moveEvent2(QEvent::MouseMove, releasePos, Qt::NoButton, Qt::LeftButton, - Qt::ShiftModifier); + QMouseEvent moveEvent2(QEvent::MouseMove, releasePos, view.viewport()->mapToGlobal(releasePos), + Qt::NoButton, Qt::LeftButton, Qt::ShiftModifier); const bool moveEventReceived = qApp->notify(view.viewport(), &moveEvent2); QVERIFY(moveEventReceived); QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, releasePos); @@ -2595,11 +2651,141 @@ void tst_QAbstractItemView::dragSelectAfterNewPress() // Verify that the selection worked OK selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 6); + QCOMPARE(selected.size(), 6); for (int i = 0; i < 5; ++i) QVERIFY(selected.contains(model.index(i, 0))); } +void tst_QAbstractItemView::dragWithSecondClick_data() +{ + QTest::addColumn<QByteArray>("viewClass"); + QTest::addColumn<bool>("doubleClick"); + const QList<QByteArray> widgets { "QListView", "QTreeView" }; + for (const QByteArray &widget : widgets) { + QTest::newRow(widget + ": DoubleClick") << widget << true; + QTest::newRow(widget + ": Two Single Clicks") << widget << false; + } +} + +// inject the ability to record which indexes get dragged into any QAbstractItemView class +struct DragRecorder +{ + virtual ~DragRecorder() = default; + bool dragStarted = false; + QModelIndexList draggedIndexes; + QAbstractItemView *view; +}; + +template<class ViewClass> +class DragRecorderView : public ViewClass, public DragRecorder +{ +public: + DragRecorderView() + { view = this; } +protected: + void startDrag(Qt::DropActions) override + { + draggedIndexes = ViewClass::selectedIndexes(); + dragStarted = true; + } +}; + +void tst_QAbstractItemView::dragWithSecondClick() +{ + QFETCH(QByteArray, viewClass); + QFETCH(bool, doubleClick); + + QStandardItemModel model; + QStandardItem *parentItem = model.invisibleRootItem(); + for (int i = 0; i < 10; ++i) { + QStandardItem *item = new QStandardItem(QString("item %0").arg(i)); + item->setDragEnabled(true); + item->setEditable(false); + parentItem->appendRow(item); + } + + std::unique_ptr<DragRecorder> dragRecorder; + if (viewClass == "QTreeView") + dragRecorder.reset(new DragRecorderView<QTreeView>); + else if (viewClass == "QListView") + dragRecorder.reset(new DragRecorderView<QListView>); + + QAbstractItemView *view = dragRecorder->view; + view->setModel(&model); + view->setFixedSize(160, 650); // Minimum width for windows with frame on Windows 8 + view->setSelectionMode(QAbstractItemView::MultiSelection); + view->setDragDropMode(QAbstractItemView::InternalMove); + centerOnScreen(view); + moveCursorAway(view); + view->show(); + QVERIFY(QTest::qWaitForWindowExposed(view)); + + QModelIndex index0 = model.index(0, 0); + QModelIndex index1 = model.index(1, 0); + // Select item 0 using a single click + QTest::mouseClick(view->viewport(), Qt::LeftButton, Qt::NoModifier, + view->visualRect(index0).center()); + QCOMPARE(view->currentIndex(), index0); + + if (doubleClick) { + // press on same item within the double click interval + QTest::mouseDClick(view->viewport(), Qt::LeftButton, Qt::NoModifier, + view->visualRect(index0).center()); + } else { + // or on different item with a slow second press + QTest::mousePress(view->viewport(), Qt::LeftButton, Qt::NoModifier, + view->visualRect(index1).center()); + } + // then drag far enough with left button held + const QPoint dragTo = view->visualRect(index1).center() + + QPoint(2 * QApplication::startDragDistance(), + 2 * QApplication::startDragDistance()); + QMouseEvent mouseMoveEvent(QEvent::MouseMove, dragTo, view->viewport()->mapToGlobal(dragTo), + Qt::NoButton, Qt::LeftButton, Qt::NoModifier); + QVERIFY(QApplication::sendEvent(view->viewport(), &mouseMoveEvent)); + // twice since the view will first enter dragging state, then start the drag + // (not necessary to actually move the mouse) + QVERIFY(QApplication::sendEvent(view->viewport(), &mouseMoveEvent)); + QVERIFY(dragRecorder->dragStarted); + QTest::mouseRelease(view->viewport(), Qt::LeftButton, Qt::NoModifier, dragTo); +} + +void tst_QAbstractItemView::clickAfterDoubleClick() +{ + QTableWidget view(5, 5); + view.horizontalHeader()->hide(); + view.verticalHeader()->hide(); + view.setEditTriggers(QAbstractItemView::NoEditTriggers); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + const QModelIndex index = view.model()->index(1, 1); + QVERIFY(index.isValid()); + const QPoint clickPoint = view.visualRect(index).center(); + + // must use the QWindow overloads so that modality is respected + QWindow *window = view.window()->windowHandle(); + int clickCount = 0; + + connect(&view, &QAbstractItemView::doubleClicked, [&]{ + QDialog dialog(&view); + dialog.setModal(true); + QTimer::singleShot(0, [&]{ dialog.close(); }); + dialog.exec(); + }); + connect(&view, &QAbstractItemView::clicked, [&]{ + ++clickCount; + }); + + QTest::mouseClick(window, Qt::LeftButton, {}, clickPoint); + QCOMPARE(clickCount, 1); + // generates a click followed by a double click; double click opens + // dialog that eats second release + QTest::mouseDClick(window, Qt::LeftButton, {}, clickPoint); + QCOMPARE(clickCount, 2); + QTest::mouseClick(window, Qt::LeftButton, {}, clickPoint); + QCOMPARE(clickCount, 3); +} + void tst_QAbstractItemView::selectionCommand_data() { QTest::addColumn<QAbstractItemView::SelectionMode>("selectionMode"); @@ -2644,5 +2830,736 @@ void tst_QAbstractItemView::selectionCommand() QCOMPARE(selectionFlag, view.selectionCommand(QModelIndex(), nullptr)); } +struct SelectionEvent +{ + enum MouseEvent + { Press, Release, Click, Move }; + constexpr SelectionEvent(MouseEvent type, int r = -1) noexcept + : eventType(type), row(r) {} + constexpr SelectionEvent(MouseEvent type, Qt::KeyboardModifiers mod, int r = -1) noexcept + : eventType(type), keyboardModifiers(mod), row(r) {} + MouseEvent eventType = Press; + Qt::KeyboardModifiers keyboardModifiers = Qt::NoModifier; + int row = -1; +}; + +void tst_QAbstractItemView::mouseSelection_data() +{ + QTest::addColumn<QAbstractItemView::SelectionMode>("selectionMode"); + QTest::addColumn<bool>("dragEnabled"); + QTest::addColumn<QAbstractItemView::EditTrigger>("editTrigger"); + QTest::addColumn<QList<SelectionEvent>>("selectionEvents"); + QTest::addColumn<QList<int>>("selectedRows"); + + // single selection mode - always one row selected, modifiers ignored + QTest::addRow("Single:Press") << QAbstractItemView::SingleSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 1)} + << QList{1}; + QTest::addRow("Single:Click") << QAbstractItemView::SingleSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent{SelectionEvent::Click, 1}} + << QList{1}; + QTest::addRow("Single:Press+Drag") << QAbstractItemView::SingleSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 1), + SelectionEvent{SelectionEvent::Move, 2}, + SelectionEvent{SelectionEvent::Release}} + << QList{2}; + QTest::addRow("Single:Shift+Click") << QAbstractItemView::SingleSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent{SelectionEvent::Click, Qt::ShiftModifier, 2}} + << QList{2}; + QTest::addRow("Single:Press;Ctrl+Press") << QAbstractItemView::SingleSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent{SelectionEvent::Press, 3}, + SelectionEvent{SelectionEvent::Press, Qt::ControlModifier, 3}} + << QList{3}; + QTest::addRow("Single:Ctrl+Click") << QAbstractItemView::SingleSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent{SelectionEvent::Click, Qt::ControlModifier, 3}} + << QList{3}; + QTest::addRow("Single:Click;Ctrl+Click") << QAbstractItemView::SingleSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent{SelectionEvent::Click, 3}, + SelectionEvent{SelectionEvent::Click, Qt::ControlModifier, 3}} + << QList<int>{}; + + // multi selection mode - selection toggles on press, selection can be drag-extended + // modifiers ignored + QTest::addRow("Multi:Press") << QAbstractItemView::MultiSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 1)} + << QList{1}; + QTest::addRow("Multi:Press twice") << QAbstractItemView::MultiSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 1), + SelectionEvent(SelectionEvent::Press, 1)} + << QList<int>{}; + QTest::addRow("Multi:Press twice with drag enabled") << QAbstractItemView::MultiSelection << true + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Click, 1), + SelectionEvent(SelectionEvent::Press, 1)} + << QList{1}; + QTest::addRow("Multi:Press and click with drag enabled") << QAbstractItemView::MultiSelection << true + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 1), + SelectionEvent(SelectionEvent::Click, 1)} + << QList<int>{}; + QTest::addRow("Multi:Press,Press") << QAbstractItemView::MultiSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 2), + SelectionEvent(SelectionEvent::Press, 3)} + << QList{2, 3}; + QTest::addRow("Multi:Press,Drag") << QAbstractItemView::MultiSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 1), + SelectionEvent(SelectionEvent::Move, 5), + SelectionEvent(SelectionEvent::Release)} + << QList{1, 2, 3, 4, 5}; + QTest::addRow("Multi:Press,Drag,Deselect") << QAbstractItemView::MultiSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 1), + SelectionEvent(SelectionEvent::Move, 5), + SelectionEvent(SelectionEvent::Release), + SelectionEvent(SelectionEvent::Press, 3)} + << QList{1, 2, 4, 5}; + // drag-select a few indices; then drag-select a larger area that includes the first + QTest::addRow("Multi:Press,Drag;Surround") << QAbstractItemView::MultiSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 3), + SelectionEvent(SelectionEvent::Move, 5), + SelectionEvent(SelectionEvent::Release), + SelectionEvent(SelectionEvent::Press, 1), + SelectionEvent(SelectionEvent::Move, 8), + SelectionEvent(SelectionEvent::Release)} + << QList{1, 2, 3, 4, 5, 6, 7, 8}; + // drag-select a few indices; then try to select more starting with the last -> not working + QTest::addRow("Multi:Press,Drag;Expand") << QAbstractItemView::MultiSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 3), + SelectionEvent(SelectionEvent::Move, 5), + SelectionEvent(SelectionEvent::Release), + SelectionEvent(SelectionEvent::Press, 5), // this will deselect #5 and not select 6/7/8 + SelectionEvent(SelectionEvent::Move, 8), + SelectionEvent(SelectionEvent::Release)} + << QList{3, 4}; + // Multi: Press-dragging a selection should not deselect #QTBUG-59888 + QTest::addRow("Multi:Press-Drag selection") << QAbstractItemView::MultiSelection << true + // with drag'n'drop enabled, we cannot drag a selection + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Click, 2), + SelectionEvent(SelectionEvent::Click, 3), + SelectionEvent(SelectionEvent::Click, 4), + SelectionEvent(SelectionEvent::Click, 5), + SelectionEvent(SelectionEvent::Press, 3), + // two moves needed because of distance and state logic in QAbstractItemView + SelectionEvent(SelectionEvent::Move, 5), + SelectionEvent(SelectionEvent::Move, 6)} + << QList{2, 3, 4, 5}; + + // Extended selection: Press selects a single item + QTest::addRow("Extended:Press") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 3)} + << QList{3}; + QTest::addRow("Extended:Press twice") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 3), + SelectionEvent(SelectionEvent::Press, 3)} + << QList{3}; + QTest::addRow("Extended:Press,Press") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 2), + SelectionEvent(SelectionEvent::Press, 3)} + << QList{3}; + // Extended selection: press with Ctrl toggles item + QTest::addRow("Extended:Press,Toggle") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 3), + SelectionEvent(SelectionEvent::Click, Qt::ControlModifier, 3)} + << QList<int>{}; + QTest::addRow("Extended:Press,Add") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 1), + SelectionEvent(SelectionEvent::Click, Qt::ControlModifier, 3)} + << QList{1, 3}; + // Extended selection: Shift creates a range between first and last pressed + QTest::addRow("Extended:Press,Range") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 1), + SelectionEvent(SelectionEvent::Press, Qt::ShiftModifier, 5)} + << QList{1, 2, 3, 4, 5}; + QTest::addRow("Extended:Press,Range,Fix Range") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 1), + SelectionEvent(SelectionEvent::Press, Qt::ShiftModifier, 5), + SelectionEvent(SelectionEvent::Press, Qt::ShiftModifier, 3)} + << QList{1, 2, 3}; + // Extended: dragging extends the selection + QTest::addRow("Extended:Press,Drag") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 2), + SelectionEvent(SelectionEvent::Move, 5)} + << QList{2, 3, 4, 5}; + // Extended: Ctrl+Press-dragging extends the selection + QTest::addRow("Extended:Press,Drag;Ctrl-Press,Drag") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 2), + SelectionEvent(SelectionEvent::Move, 5), + SelectionEvent(SelectionEvent::Release), + SelectionEvent(SelectionEvent::Press, Qt::ControlModifier, 6), + SelectionEvent(SelectionEvent::Move, Qt::ControlModifier, 8), + SelectionEvent(SelectionEvent::Release, Qt::ControlModifier, 8)} + << QList{2, 3, 4, 5, 6, 7, 8}; + // Extended: Ctrl+Press-dragging in a selection should not deselect #QTBUG-59888 + QTest::addRow("Extended:Ctrl-Drag selection,no deselect") << QAbstractItemView::ExtendedSelection << true + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Click, 2), + SelectionEvent(SelectionEvent::Click, Qt::ShiftModifier, 5), + SelectionEvent(SelectionEvent::Press, Qt::ControlModifier, 3), + SelectionEvent(SelectionEvent::Move, Qt::ControlModifier, 5), + // two moves needed because of distance and state logic in QAbstractItemView + SelectionEvent(SelectionEvent::Move, Qt::ControlModifier, 6)} + << QList{2, 3, 4, 5}; + // Extended: Ctrl+Press-dragging with a selection extends, then drags #QTBUG-59888 + QTest::addRow("Extended:Ctrl-Drag selection") << QAbstractItemView::ExtendedSelection << true + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Click, 2), + SelectionEvent(SelectionEvent::Click, Qt::ShiftModifier, 5), + SelectionEvent(SelectionEvent::Press, Qt::ControlModifier, 6), + SelectionEvent(SelectionEvent::Move, Qt::ControlModifier, 7), + // two moves needed because of distance and state logic in 7QAbstractItemView + SelectionEvent(SelectionEvent::Move, Qt::ControlModifier, 8)} + << QList{2, 3, 4, 5, 6}; + // Extended: when drag is enabled, click with Ctrl toggles item instead of editing # QTBUG-111131 + QTest::addRow("Extended:Click,Toggle,editable") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::SelectedClicked + << QList{SelectionEvent(SelectionEvent::Click, 3), + SelectionEvent(SelectionEvent::Click, Qt::ControlModifier, 3)} + << QList<int>{}; + QTest::addRow("Extended:Click,Toggle,dragable,editable") << QAbstractItemView::ExtendedSelection << true + << QAbstractItemView::SelectedClicked + << QList{SelectionEvent(SelectionEvent::Click, 3), + SelectionEvent(SelectionEvent::Click, Qt::ControlModifier, 3)} + << QList<int>{}; + // Extended: when drag is enabled, click on selected without Ctrl clears before editing + QTest::addRow("Extended:Range,Click,editable") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::SelectedClicked + << QList{SelectionEvent(SelectionEvent::Click, 1), + SelectionEvent(SelectionEvent::Click, Qt::ShiftModifier, 3), + SelectionEvent(SelectionEvent::Click, 2)} + << QList<int>{2}; + QTest::addRow("Extended:Range,Click,dragable,editable") << QAbstractItemView::ExtendedSelection << true + << QAbstractItemView::SelectedClicked + << QList{SelectionEvent(SelectionEvent::Click, 1), + SelectionEvent(SelectionEvent::Click, Qt::ShiftModifier, 3), + SelectionEvent(SelectionEvent::Click, 2)} + << QList<int>{2}; +} + +void tst_QAbstractItemView::mouseSelection() +{ + QFETCH(QAbstractItemView::SelectionMode, selectionMode); + QFETCH(bool, dragEnabled); + QFETCH(QAbstractItemView::EditTrigger, editTrigger); + QFETCH(QList<SelectionEvent>, selectionEvents); + QFETCH(QList<int>, selectedRows); + + QStandardItemModel model; + QStandardItem *parentItem = model.invisibleRootItem(); + for (int i = 0; i < 10; ++i) { + QStandardItem *item = new QStandardItem(QString("item %0").arg(i)); + item->setDragEnabled(dragEnabled); + item->setEditable(editTrigger != QAbstractItemView::NoEditTriggers); + parentItem->appendRow(item); + } + + std::unique_ptr<DragRecorder> dragRecorder(new DragRecorderView<QTreeView>); + QAbstractItemView *view = dragRecorder->view; + QVERIFY(view); + view->setModel(&model); + view->setDragEnabled(dragEnabled); + view->setSelectionMode(selectionMode); + view->setEditTriggers(editTrigger); + view->show(); + QVERIFY(QTest::qWaitForWindowActive(view)); + + Qt::MouseButton buttonDown = Qt::NoButton; + int targetRow = -1; + QModelIndex pressedIndex; + for (const auto &event : std::as_const(selectionEvents)) { + if (event.row != -1) + targetRow = event.row; + const QModelIndex targetIndex = model.index(targetRow, 0); + const QPoint targetPoint = view->visualRect(targetIndex).center(); + switch (event.eventType) { + case SelectionEvent::Press: + if (buttonDown != Qt::NoButton) { + QTest::mouseRelease(view->viewport(), buttonDown, event.keyboardModifiers, + view->visualRect(pressedIndex).center()); + } + buttonDown = Qt::LeftButton; + pressedIndex = model.index(targetRow, 0); + QTest::mousePress(view->viewport(), buttonDown, event.keyboardModifiers, targetPoint); + break; + case SelectionEvent::Release: + QTest::mouseRelease(view->viewport(), buttonDown, event.keyboardModifiers, targetPoint); + buttonDown = Qt::NoButton; + pressedIndex = QModelIndex(); + break; + case SelectionEvent::Click: + QTest::mouseClick(view->viewport(), Qt::LeftButton, event.keyboardModifiers, targetPoint); + buttonDown = Qt::NoButton; + break; + case SelectionEvent::Move: { + QMouseEvent mouseMoveEvent(QEvent::MouseMove, targetPoint, + view->viewport()->mapToGlobal(targetPoint), + Qt::NoButton, buttonDown, event.keyboardModifiers); + QApplication::sendEvent(view->viewport(), &mouseMoveEvent); + } + break; + } + } + + QList<int> actualSelected; + const auto selectedIndexes = dragRecorder->dragStarted + ? dragRecorder->draggedIndexes + : view->selectionModel()->selectedIndexes(); + for (auto index : selectedIndexes) + actualSelected << index.row(); + + QCOMPARE(actualSelected, selectedRows); +} + +/*! + Make sure that when clicking on empty space in the view, we don't + unselect the current row. + QTBUG-105870 +*/ +void tst_QAbstractItemView::keepSingleSelectionOnEmptyAreaClick() +{ + QListWidget view; + view.setSelectionMode(QAbstractItemView::SingleSelection); + QListWidgetItem *lastItem; + for (int i = 0; i < 5; i++) + lastItem = new QListWidgetItem("item " + QString::number(i), &view); + + // Make widget large enough so that there is empty area below the last item + view.setFixedSize(300, 500); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + // Select third row + view.setCurrentRow(2); + + // Click below the last row + QPoint targetPoint = view.visualItemRect(lastItem).bottomLeft(); + targetPoint += QPoint(10, 10); + + QTest::mouseClick(view.viewport(), Qt::MouseButton::LeftButton, Qt::NoModifier, targetPoint); + + QCOMPARE(view.currentRow(), 2); + QVERIFY(view.currentItem()->isSelected()); +} + +/*! + Verify that scrolling an autoScroll enabled itemview with a QScroller + produces a continuous, smooth scroll without any jumping around due to + the currentItem negotiation between QAbstractItemView and QScroller. + QTBUG-64543. +*/ +void tst_QAbstractItemView::scrollerSmoothScroll() +{ + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + + QListWidget view; + view.setAutoScroll(true); + view.setVerticalScrollMode(QListView::ScrollPerPixel); + + QScroller::grabGesture(view.viewport(), QScroller::TouchGesture); + QScroller::grabGesture(view.viewport(), QScroller::LeftMouseButtonGesture); + + for (int i = 0; i < 50; i++) { + QListWidgetItem* item = new QListWidgetItem("item " + QString::number(i), &view); + // gives items a touch friendly size so that only a few fit into the viewport + item->setSizeHint(QSize(100,50)); + } + + // make sure we have space for only a few items + view.setFixedSize(120, 200); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + // we flick up, so we should never scroll back + int lastScrollPosition = 0; + bool scrollBack = false; + connect(view.verticalScrollBar(), &QScrollBar::valueChanged, [&](int value){ + scrollBack |= (value < lastScrollPosition); + lastScrollPosition = value; + }); + + // start in the middle + view.scrollToItem(view.item(25)); + QCOMPARE(view.currentItem(), view.item(0)); + QListWidgetItem *pressItem = view.item(23); + QPoint dragPosition = view.visualRect(view.indexFromItem(pressItem)).center(); + // the mouse press changes the current item temporarily, but the press is delayed + // by the gesture machinery. this is not what we are testing here, so skip the test + // if this fails within a reasonable amount of time. + QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, dragPosition); + if (!(QTest::qWaitFor([&]{ return view.currentItem() == pressItem; }))) + QSKIP("Current item didn't change on press, skipping test"); + + // QAIV will reset the current item when the scroller changes state to Dragging + for (int y = 0; y < QApplication::startDragDistance() * 2; ++y) { + // gesture recognizer needs some throttling + QTest::qWait(10); + dragPosition -= QPoint(0, 10); + const QPoint globalPos = view.viewport()->mapToGlobal(dragPosition); + QMouseEvent mouseMoveEvent(QEvent::MouseMove, dragPosition, dragPosition, globalPos, + Qt::NoButton, Qt::LeftButton, Qt::NoModifier); + QApplication::sendEvent(view.viewport(), &mouseMoveEvent); + QVERIFY(!scrollBack); + } + + QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, dragPosition); +} + +/*! + Verify that starting the editing of an item with a key press while a composing + input method is active doesn't break the input method. See QTBUG-54848. +*/ +void tst_QAbstractItemView::inputMethodOpensEditor_data() +{ + QTest::addColumn<QPoint>("editItem"); + QTest::addColumn<QString>("preedit"); + QTest::addColumn<QString>("commit"); + + QTest::addRow("IM accepted") << QPoint(1, 1) << "chang" << QString("长"); + QTest::addRow("IM cancelled") << QPoint(25, 25) << "chang" << QString(); +} + +void tst_QAbstractItemView::inputMethodOpensEditor() +{ + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + + QTableWidget tableWidget(50, 50); + tableWidget.setEditTriggers(QAbstractItemView::AnyKeyPressed); + for (int r = 0; r < 50; ++r) { + for (int c = 0; c < 50; ++c ) + tableWidget.setItem(r, c, new QTableWidgetItem(QString("Item %1:%2").arg(r).arg(c))); + } + + tableWidget.show(); + QVERIFY(QTest::qWaitForWindowActive(&tableWidget)); + + const auto sendInputMethodEvent = [](const QString &preeditText, const QString &commitString = {}){ + QInputMethodEvent imEvent(preeditText, {}); + imEvent.setCommitString(commitString); + QApplication::sendEvent(QApplication::focusWidget(), &imEvent); + }; + + QCOMPARE(QApplication::focusWidget(), &tableWidget); + + QFETCH(QPoint, editItem); + QFETCH(QString, preedit); + QFETCH(QString, commit); + + tableWidget.setCurrentCell(editItem.y(), editItem.x()); + const QString orgText = tableWidget.currentItem()->text(); + const QModelIndex currentIndex = tableWidget.currentIndex(); + QCOMPARE(tableWidget.inputMethodQuery(Qt::ImCursorRectangle), tableWidget.visualRect(currentIndex)); + + // simulate the start of input via a composing input method + sendInputMethodEvent(preedit.left(1)); + QCOMPARE(tableWidget.state(), QAbstractItemView::EditingState); + QLineEdit *editor = tableWidget.findChild<QLineEdit*>(); + QVERIFY(editor); + QCOMPARE(editor->text(), QString()); + // the focus must remain with the tableWidget, as otherwise the compositing is interrupted + QCOMPARE(QApplication::focusWidget(), &tableWidget); + // the item view delegates input method queries to the editor + const QRect cursorRect = tableWidget.inputMethodQuery(Qt::ImCursorRectangle).toRect(); + QVERIFY(cursorRect.isValid()); + QVERIFY(tableWidget.visualRect(currentIndex).intersects(cursorRect)); + + // finish preediting, then commit or cancel the input + sendInputMethodEvent(preedit); + sendInputMethodEvent(QString(), commit); + // editing continues, the editor now has focus + QCOMPARE(tableWidget.state(), QAbstractItemView::EditingState); + QVERIFY(editor->hasFocus()); + // finish editing + QTest::keyClick(editor, Qt::Key_Return); + if (commit.isEmpty()) { + // if composition was cancelled, then the item's value is unchanged + QCOMPARE(tableWidget.currentItem()->text(), orgText); + } else { + // otherwise, the item's value is now the commit string + QTRY_COMPARE(tableWidget.currentItem()->text(), commit); + } +} + +void tst_QAbstractItemView::selectionAutoScrolling_data() +{ + QTest::addColumn<Qt::Orientation>("orientation"); + QTest::addColumn<int>("direction"); // negative or positive + + QTest::addRow("scroll up") << Qt::Vertical << -1; + QTest::addRow("scroll left") << Qt::Horizontal << -1; + QTest::addRow("scroll down") << Qt::Vertical << +1; + QTest::addRow("scroll right") << Qt::Horizontal << +1; +} + +void tst_QAbstractItemView::selectionAutoScrolling() +{ + QFETCH(Qt::Orientation, orientation); + QFETCH(int, direction); + + QListView listview; + listview.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + listview.setResizeMode(QListView::Fixed); + listview.setAutoScroll(true); + listview.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + listview.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + listview.setSpacing(10); + listview.setGeometry(0, 0, 500, 500); + listview.setFrameShape(QFrame::Shape::NoFrame); + listview.setEditTriggers(QListView::NoEditTriggers); + + QStandardItemModel *listModel = new QStandardItemModel(&listview); + listview.setModel(listModel); + listview.setViewMode(QListView::IconMode); + listview.setSelectionMode(QListView::ExtendedSelection); + + QPixmap pm(50, 50); + pm.fill(Qt::red); + for (int i = 0; i < 80; i++) { + QStandardItem *item = new QStandardItem(pm, QString::number(i)); + listModel->appendRow(item); + } + + listview.show(); + QVERIFY(QTest::qWaitForWindowExposed(&listview)); + + listview.resize(200, 200); + // scroll to the middle + listview.verticalScrollBar()->setValue(listview.verticalScrollBar()->maximum() / 2); + listview.horizontalScrollBar()->setValue(listview.horizontalScrollBar()->maximum() / 2); + + // remove all visible items so that we don't select any items at the edges, as that + // would scroll the view already + for (int x = 0; x < listview.viewport()->width(); x += 5) { + for (int y = 0; y < listview.viewport()->height(); y += 5) { + const QModelIndex index = listview.indexAt(QPoint(x, y)); + if (index.isValid()) + delete listModel->itemFromIndex(index); + } + } + // remove all items around the edges of the model + QRect topLeftRect = listview.visualRect(listModel->index(0, 0)); + const QPoint topLeftCenter(topLeftRect.center()); + QPoint bottomRightCenter; + for (int x = 0; x < listview.horizontalScrollBar()->maximum() + listview.viewport()->width(); x += 5) { + const QModelIndex index = listview.indexAt(topLeftCenter + QPoint(x, 0)); + if (index.isValid()) { + delete listModel->itemFromIndex(index); + bottomRightCenter.rx() = x; + } + } + for (int y = 0; y < listview.verticalScrollBar()->maximum() + listview.viewport()->height(); y += 5) { + const QModelIndex index = listview.indexAt(topLeftCenter + QPoint(0, y)); + if (index.isValid()) { + delete listModel->itemFromIndex(index); + bottomRightCenter.ry() = y; + } + } + for (int x = 0; x < bottomRightCenter.x(); x += 5) { + const QModelIndex index = listview.indexAt(topLeftCenter + QPoint(x, bottomRightCenter.y())); + if (index.isValid()) + delete listModel->itemFromIndex(index); + } + for (int y = 0; y < bottomRightCenter.y(); y += 5) { + const QModelIndex index = listview.indexAt(topLeftCenter + QPoint(bottomRightCenter.x(), y)); + if (index.isValid()) + delete listModel->itemFromIndex(index); + } + + + // Simulate multiple select behavior; start in the middle, drag to the edge + const QPoint pressPoint(listview.viewport()->width() / 2, listview.viewport()->height() / 2); + QPoint dragPoint = pressPoint; + if (orientation == Qt::Vertical) { + dragPoint.rx() += 50; + dragPoint.ry() = direction > 0 ? listview.viewport()->height() : 0; + } else { + dragPoint.rx() = direction > 0 ? listview.viewport()->width() : 0; + dragPoint.ry() += 50; + } + + QTest::mousePress(listview.viewport(), Qt::LeftButton, Qt::NoModifier, pressPoint); + QMouseEvent mmEvent(QEvent::MouseMove, dragPoint, listview.viewport()->mapToGlobal(dragPoint), + Qt::NoButton, Qt::LeftButton, Qt::NoModifier); + QApplication::sendEvent(listview.viewport(), &mmEvent); // QTest::mouseMove is useless + + // check that we scrolled to the end + QScrollBar *scrollBar = orientation == Qt::Vertical + ? listview.verticalScrollBar() + : listview.horizontalScrollBar(); + + if (direction < 0) + QTRY_COMPARE(scrollBar->value(), 0); + else + QTRY_COMPARE(scrollBar->value(), scrollBar->maximum()); + QVERIFY(listview.selectionModel()->selectedIndexes().size() > 0); + + QTest::mouseRelease(listview.viewport(), Qt::LeftButton, Qt::NoModifier, dragPoint); +} +class SpinBoxDelegate : public QStyledItemDelegate +{ +public: + using QStyledItemDelegate::QStyledItemDelegate; + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const override + { + QSpinBox *spinboxEditor = new QSpinBox(parent); + return spinboxEditor; + } + + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override + { + if (QSpinBox *spin = qobject_cast<QSpinBox *>(editor)) { + model->setData(index, spin->value()); + } + } +}; + +void tst_QAbstractItemView::testSpinBoxAsEditor_data() +{ + QTest::addColumn<bool>("keyboardTracking"); + QTest::newRow("true") << true; + QTest::newRow("false")<< false; +} + +void tst_QAbstractItemView::testSpinBoxAsEditor() +{ + QFETCH(bool, keyboardTracking); + + QStandardItemModel model(2, 2); + SpinBoxDelegate delegate; + + QTableView view; + view.setModel(&model); + view.setItemDelegate(&delegate); + + view.setCurrentIndex(model.index(0, 1)); + view.openPersistentEditor(model.index(0, 1)); + const QList<QSpinBox *> list = view.viewport()->findChildren<QSpinBox *>(); + QCOMPARE(list.size(), 1); + QSpinBox *sb = list.first(); + QVERIFY(sb); + + sb->setKeyboardTracking(keyboardTracking); + + centerOnScreen(&view); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + QTRY_COMPARE(QApplication::focusWidget(), sb); + + QTest::keyClick(sb, Qt::Key_1, Qt::NoModifier); + QPoint clickpos = view.visualRect(model.index(0, 0)).center(); + QTest::mouseDClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, clickpos); + + QCOMPARE(model.data(model.index(0, 1)).toInt(), 1); +} + +void tst_QAbstractItemView::removeIndexWhileEditing() +{ + QTreeView view; + QStandardItemModel treeModel; + auto editableItem1 = new QStandardItem("aa"); + auto editableItem2 = new QStandardItem("ab"); + auto editableItem3 = new QStandardItem("ac"); + auto item = new QStandardItem("a"); + item->appendRow(editableItem1); + item->appendRow(editableItem2); + item->appendRow(editableItem3); + treeModel.setItem(0, 0, item); + QSortFilterProxyModel filterModel; + filterModel.setSourceModel(&treeModel); + view.setModel(&filterModel); + view.show(); + + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + view.setExpanded(item->index(), true); + + filterModel.setFilterRegularExpression("a.*"); + + QTest::failOnWarning(QRegularExpression("QAbstractItemView::closeEditor called with an editor " + "that does not belong to this view")); + + // Verify that we shut editing down cleanly if the index we are editing is + // filtered out after committing + { + const QModelIndex filteredIndex = filterModel.mapFromSource(editableItem1->index()); + QVERIFY(filteredIndex.isValid()); + view.edit(filteredIndex); + QCOMPARE(view.state(), QAbstractItemView::EditingState); + QTRY_VERIFY(QApplication::focusWidget()); + QPointer<QLineEdit> lineEdit = qobject_cast<QLineEdit *>(QApplication::focusWidget()); + QVERIFY(lineEdit); + lineEdit->setText("c"); + QTest::keyClick(lineEdit, Qt::Key_Enter); + QTRY_VERIFY(!lineEdit); + QCOMPARE(editableItem1->data(Qt::DisplayRole), "c"); + QCOMPARE(view.state(), QAbstractItemView::NoState); + } + + // If we change the filter while we edit, then we should clean up state as well + { + const QModelIndex filteredIndex = filterModel.mapFromSource(editableItem2->index()); + QVERIFY(filteredIndex.isValid()); + view.edit(filteredIndex); + QCOMPARE(view.state(), QAbstractItemView::EditingState); + QTRY_VERIFY(QApplication::focusWidget()); + QPointer<QLineEdit> lineEdit = qobject_cast<QLineEdit *>(QApplication::focusWidget()); + QVERIFY(lineEdit); + filterModel.setFilterFixedString("c"); + QVERIFY(!filterModel.mapFromSource(editableItem2->index()).isValid()); + QTRY_VERIFY(!lineEdit); + QCOMPARE(view.state(), QAbstractItemView::NoState); + } +} + +void tst_QAbstractItemView::focusNextOnHide() +{ + QWidget widget; + QTableWidget table(10, 10); + table.setTabKeyNavigation(true); + QLineEdit lineEdit; + + QHBoxLayout layout; + layout.addWidget(&table); + layout.addWidget(&lineEdit); + widget.setLayout(&layout); + + widget.setTabOrder({&table, &lineEdit}); + + widget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&widget)); + + QVERIFY(table.hasFocus()); + QCOMPARE(table.currentIndex(), table.model()->index(0, 0)); + QTest::keyPress(&table, Qt::Key_Tab); + QCOMPARE(table.currentIndex(), table.model()->index(0, 1)); + + table.hide(); + QCOMPARE(table.currentIndex(), table.model()->index(0, 1)); + QVERIFY(lineEdit.hasFocus()); +} + QTEST_MAIN(tst_QAbstractItemView) #include "tst_qabstractitemview.moc" diff --git a/tests/auto/widgets/itemviews/qcolumnview/CMakeLists.txt b/tests/auto/widgets/itemviews/qcolumnview/CMakeLists.txt index 886971004b..8cf19c703e 100644 --- a/tests/auto/widgets/itemviews/qcolumnview/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qcolumnview/CMakeLists.txt @@ -1,14 +1,21 @@ -# Generated from qcolumnview.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qcolumnview Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcolumnview LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qcolumnview SOURCES ../../../../shared/fakedirmodel.h tst_qcolumnview.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/itemviews/qcolumnview/tst_qcolumnview.cpp b/tests/auto/widgets/itemviews/qcolumnview/tst_qcolumnview.cpp index 6dd39d5d2a..1ff9973e00 100644 --- a/tests/auto/widgets/itemviews/qcolumnview/tst_qcolumnview.cpp +++ b/tests/auto/widgets/itemviews/qcolumnview/tst_qcolumnview.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QColumnView> #include <QScrollBar> @@ -522,11 +497,11 @@ void tst_QColumnView::selectAll() view.setModel(&m_fakeDirModel); view.selectAll(); - QVERIFY(view.selectionModel()->selectedIndexes().count() >= 0); + QVERIFY(view.selectionModel()->selectedIndexes().size() >= 0); view.setCurrentIndex(m_fakeDirHomeIndex); view.selectAll(); - QVERIFY(view.selectionModel()->selectedIndexes().count() > 0); + QVERIFY(view.selectionModel()->selectedIndexes().size() > 0); QModelIndex file; for (int i = 0; i < m_fakeDirModel.rowCount(m_fakeDirHomeIndex); ++i) { @@ -537,10 +512,10 @@ void tst_QColumnView::selectAll() } view.setCurrentIndex(file); view.selectAll(); - QVERIFY(view.selectionModel()->selectedIndexes().count() > 0); + QVERIFY(view.selectionModel()->selectedIndexes().size() > 0); view.setCurrentIndex(QModelIndex()); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 0); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 0); } void tst_QColumnView::clicked() @@ -561,13 +536,13 @@ void tst_QColumnView::clicked() QPoint localPoint = view.visualRect(m_fakeDirHomeIndex).center(); QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, localPoint); - QCOMPARE(clickedSpy.count(), 1); + QCOMPARE(clickedSpy.size(), 1); QCoreApplication::processEvents(); if (sizeof(qreal) != sizeof(double)) QSKIP("Skipped due to rounding errors"); - for (int i = 0; i < view.createdColumns.count(); ++i) { + for (int i = 0; i < view.createdColumns.size(); ++i) { QAbstractItemView *column = view.createdColumns.at(i); if (column && column->selectionModel() && (column->rootIndex() == m_fakeDirHomeIndex)) QVERIFY(column->selectionModel()->selectedIndexes().isEmpty()); @@ -585,7 +560,7 @@ void tst_QColumnView::selectedColumns() QTest::qWait(ANIMATION_DELAY); - for (int i = 0; i < view.createdColumns.count(); ++i) { + for (int i = 0; i < view.createdColumns.size(); ++i) { QAbstractItemView *column = view.createdColumns.at(i); if (!column) continue; @@ -616,7 +591,7 @@ void tst_QColumnView::setSelectionModel() view.setSelectionModel(selectionModel); bool found = false; - for (int i = 0; i < view.createdColumns.count(); ++i) { + for (int i = 0; i < view.createdColumns.size(); ++i) { if (view.createdColumns.at(i)->selectionModel() == selectionModel) { found = true; break; @@ -656,7 +631,7 @@ void tst_QColumnView::moveGrip_basic() view.setMinimumWidth(200); grip->moveGrip(-800); QCOMPARE(view.width(), 200); - QCOMPARE(spy.count(), 5); + QCOMPARE(spy.size(), 5); } void tst_QColumnView::moveGrip_data() @@ -684,7 +659,7 @@ void tst_QColumnView::moveGrip() topLevel.show(); QVERIFY(QTest::qWaitForWindowActive(&topLevel)); - int columnNum = view.createdColumns.count() - 2; + int columnNum = view.createdColumns.size() - 2; QVERIFY(columnNum >= 0); const QObjectList list = view.createdColumns[columnNum]->children(); QColumnViewGrip *grip = nullptr; @@ -712,7 +687,7 @@ void tst_QColumnView::doubleClick() QCOMPARE(view.width(), 200); QTest::mouseDClick(grip, Qt::LeftButton); QCOMPARE(view.width(), view.sizeHint().width()); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } void tst_QColumnView::gripMoved() @@ -736,7 +711,7 @@ void tst_QColumnView::gripMoved() QCoreApplication::processEvents(); QTest::mouseRelease(grip, Qt::LeftButton); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(view.width(), oldWidth + 65); } @@ -801,7 +776,7 @@ void tst_QColumnView::setPreviewWidget() void tst_QColumnView::sizes() { QColumnView view; - QCOMPARE(view.columnWidths().count(), 0); + QCOMPARE(view.columnWidths().size(), 0); const QList<int> newSizes{ 10, 4, 50, 6 }; @@ -812,16 +787,16 @@ void tst_QColumnView::sizes() view.setModel(&m_fakeDirModel); view.setCurrentIndex(m_fakeDirHomeIndex); - QList<int> postSizes = view.columnWidths().mid(0, newSizes.count()); - QCOMPARE(postSizes, newSizes.mid(0, postSizes.count())); + QList<int> postSizes = view.columnWidths().mid(0, newSizes.size()); + QCOMPARE(postSizes, newSizes.mid(0, postSizes.size())); - QVERIFY(view.columnWidths().count() > 1); + QVERIFY(view.columnWidths().size() > 1); QList<int> smallerSizes{ 6 }; view.setColumnWidths(smallerSizes); QList<int> expectedSizes = newSizes; expectedSizes[0] = 6; - postSizes = view.columnWidths().mid(0, newSizes.count()); - QCOMPARE(postSizes, expectedSizes.mid(0, postSizes.count())); + postSizes = view.columnWidths().mid(0, newSizes.size()); + QCOMPARE(postSizes, expectedSizes.mid(0, postSizes.size())); } void tst_QColumnView::rowDelegate() @@ -831,7 +806,7 @@ void tst_QColumnView::rowDelegate() view.setItemDelegateForRow(3, d); view.setModel(&m_fakeDirModel); - for (int i = 0; i < view.createdColumns.count(); ++i) { + for (int i = 0; i < view.createdColumns.size(); ++i) { QAbstractItemView *column = view.createdColumns.at(i); QCOMPARE(column->itemDelegateForRow(3), d); } @@ -981,7 +956,7 @@ void tst_QColumnView::dynamicModelChanges() model.appendRow(item); QVERIFY(QTest::qWaitForWindowExposed(&view)); //let the time for painting to occur - QTRY_COMPARE(delegate.paintedIndexes.count(), 1); + QTRY_COMPARE(delegate.paintedIndexes.size(), 1); QCOMPARE(*delegate.paintedIndexes.begin(), model.index(0,0)); } diff --git a/tests/auto/widgets/itemviews/qdatawidgetmapper/CMakeLists.txt b/tests/auto/widgets/itemviews/qdatawidgetmapper/CMakeLists.txt index 325e8af9b0..e4b2c52fb2 100644 --- a/tests/auto/widgets/itemviews/qdatawidgetmapper/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qdatawidgetmapper/CMakeLists.txt @@ -1,13 +1,21 @@ -# Generated from qdatawidgetmapper.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qdatawidgetmapper Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qdatawidgetmapper LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qdatawidgetmapper SOURCES tst_qdatawidgetmapper.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/itemviews/qdatawidgetmapper/tst_qdatawidgetmapper.cpp b/tests/auto/widgets/itemviews/qdatawidgetmapper/tst_qdatawidgetmapper.cpp index 856672b957..c3527e4215 100644 --- a/tests/auto/widgets/itemviews/qdatawidgetmapper/tst_qdatawidgetmapper.cpp +++ b/tests/auto/widgets/itemviews/qdatawidgetmapper/tst_qdatawidgetmapper.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QComboBox> #include <QDataWidgetMapper> @@ -36,6 +11,8 @@ #include <QTextEdit> #include <QVBoxLayout> +#include <QtWidgets/private/qapplication_p.h> + class tst_QDataWidgetMapper: public QObject { Q_OBJECT @@ -277,22 +254,22 @@ void tst_QDataWidgetMapper::currentIndexChanged() QSignalSpy spy(&mapper, &QDataWidgetMapper::currentIndexChanged); mapper.toFirst(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().at(0).toInt(), 0); mapper.toNext(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().at(0).toInt(), 1); mapper.setCurrentIndex(7); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().at(0).toInt(), 7); mapper.setCurrentIndex(-1); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); mapper.setCurrentIndex(42); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } void tst_QDataWidgetMapper::changingValues() @@ -443,27 +420,26 @@ void tst_QDataWidgetMapper::textEditDoesntChangeFocusOnTab_qtbug3305() container.show(); - QApplication::setActiveWindow(&container); QVERIFY(QTest::qWaitForWindowActive(&container)); int closeEditorSpyCount = 0; const QString textEditContents = textEdit->toPlainText(); - QCOMPARE(closeEditorSpy.count(), closeEditorSpyCount); + QCOMPARE(closeEditorSpy.size(), closeEditorSpyCount); QVERIFY(lineEdit->hasFocus()); QVERIFY(!textEdit->hasFocus()); // this will generate a closeEditor for the tab key, and another for the focus out QTest::keyClick(QApplication::focusWidget(), Qt::Key_Tab); closeEditorSpyCount += 2; - QTRY_COMPARE(closeEditorSpy.count(), closeEditorSpyCount); + QTRY_COMPARE(closeEditorSpy.size(), closeEditorSpyCount); QTRY_VERIFY(textEdit->hasFocus()); QVERIFY(!lineEdit->hasFocus()); // now that the text edit is focused, a tab keypress will insert a tab, not change focus QTest::keyClick(QApplication::focusWidget(), Qt::Key_Tab); - QTRY_COMPARE(closeEditorSpy.count(), closeEditorSpyCount); + QTRY_COMPARE(closeEditorSpy.size(), closeEditorSpyCount); QVERIFY(!lineEdit->hasFocus()); QVERIFY(textEdit->hasFocus()); @@ -474,7 +450,7 @@ void tst_QDataWidgetMapper::textEditDoesntChangeFocusOnTab_qtbug3305() QTRY_VERIFY(lineEdit->hasFocus()); QVERIFY(!textEdit->hasFocus()); ++closeEditorSpyCount; - QCOMPARE(closeEditorSpy.count(), closeEditorSpyCount); + QCOMPARE(closeEditorSpy.size(), closeEditorSpyCount); } QTEST_MAIN(tst_QDataWidgetMapper) diff --git a/tests/auto/widgets/itemviews/qfileiconprovider/CMakeLists.txt b/tests/auto/widgets/itemviews/qfileiconprovider/CMakeLists.txt index 4f626e3b8c..88ded71aac 100644 --- a/tests/auto/widgets/itemviews/qfileiconprovider/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qfileiconprovider/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qfileiconprovider.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qfileiconprovider Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qfileiconprovider LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qfileiconprovider SOURCES tst_qfileiconprovider.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/itemviews/qfileiconprovider/tst_qfileiconprovider.cpp b/tests/auto/widgets/itemviews/qfileiconprovider/tst_qfileiconprovider.cpp index 39f6946b1d..3cba6dbf8b 100644 --- a/tests/auto/widgets/itemviews/qfileiconprovider/tst_qfileiconprovider.cpp +++ b/tests/auto/widgets/itemviews/qfileiconprovider/tst_qfileiconprovider.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QFileIconProvider> @@ -73,6 +48,7 @@ void tst_QFileIconProvider::iconType_data() // public QIcon icon(QFileIconProvider::IconType const& type) const void tst_QFileIconProvider::iconType() { + QGuiApplication::setDesktopSettingsAware(false); QFETCH(QFileIconProvider::IconType, type); QFileIconProvider provider; QVERIFY(!provider.icon(type).isNull()); diff --git a/tests/auto/widgets/itemviews/qheaderview/BLACKLIST b/tests/auto/widgets/itemviews/qheaderview/BLACKLIST index b8a4435fe1..7c9152a16c 100644 --- a/tests/auto/widgets/itemviews/qheaderview/BLACKLIST +++ b/tests/auto/widgets/itemviews/qheaderview/BLACKLIST @@ -1,5 +1,3 @@ -[stretchAndRestoreLastSection] -opensuse-leap # QTBUG-87406 [stretchAndRestoreLastSection] android diff --git a/tests/auto/widgets/itemviews/qheaderview/CMakeLists.txt b/tests/auto/widgets/itemviews/qheaderview/CMakeLists.txt index de83539f56..a13454085c 100644 --- a/tests/auto/widgets/itemviews/qheaderview/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qheaderview/CMakeLists.txt @@ -1,15 +1,20 @@ -# Generated from qheaderview.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qheaderview Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qheaderview LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qheaderview SOURCES tst_qheaderview.cpp - DEFINES - QT_DISABLE_DEPRECATED_BEFORE=0 - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp b/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp index 8856a8ef6a..57efa3e349 100644 --- a/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp +++ b/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp @@ -1,31 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2012 Thorbjørn Lund Martsum - tmartsum[at]gmail.com -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2012 Thorbjørn Lund Martsum - tmartsum[at]gmail.com +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QHeaderView> #include <QProxyStyle> @@ -37,6 +12,7 @@ #include <QTest> #include <QTreeWidget> #include <QtWidgets/private/qheaderview_p.h> +#include <QtWidgets/private/qapplication_p.h> using BoolList = QList<bool>; using IntList = QList<int>; @@ -171,7 +147,7 @@ private slots: void moveSectionAndReset(); void moveSectionAndRemove(); void saveRestore(); - void restoreQt4State(); + void QTBUG99487_saveRestoreQt5Compat(); void restoreToMoreColumns(); void restoreToMoreColumnsNoMovedColumns(); void restoreBeforeSetModel(); @@ -250,6 +226,8 @@ private slots: void testResetCachedSizeHint(); void statusTips(); void testRemovingColumnsViaLayoutChanged(); + void testModelMovingColumns(); + void testModelMovingRows(); protected: void setupTestData(bool use_reset_model = false); @@ -330,6 +308,12 @@ public: endRemoveRows(); } + void moveRow(int from, int to) + { + beginMoveRows(QModelIndex(), from, from, QModelIndex(), to); + endMoveRows(); + } + void removeOneColumn(int col) { beginRemoveColumns(QModelIndex(), col, col); @@ -351,6 +335,12 @@ public: endRemoveColumns(); } + void moveColumn(int from, int to) + { + beginMoveColumns(QModelIndex(), from, from, QModelIndex(), to); + endMoveColumns(); + } + void cleanup() { emit layoutAboutToBeChanged(); @@ -492,7 +482,7 @@ void tst_QHeaderView::init() QSignalSpy spy(view, &QHeaderView::sectionCountChanged); view->setModel(model); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); view->resize(200,200); } @@ -813,10 +803,10 @@ void tst_QHeaderView::visualIndexAt() for (int i : hidden) view->setSectionHidden(i, true); - for (int j = 0; j < from.count(); ++j) + for (int j = 0; j < from.size(); ++j) view->moveSection(from.at(j), to.at(j)); - for (int k = 0; k < coordinate.count(); ++k) + for (int k = 0; k < coordinate.size(); ++k) QTRY_COMPARE(view->visualIndexAt(coordinate.at(k)), visual.at(k)); } @@ -928,14 +918,14 @@ void tst_QHeaderView::swapSections() QCOMPARE(view->sectionsMoved(), true); for (int i = 0; i < view->count(); ++i) QCOMPARE(view->logicalIndex(i), logical.at(i)); - QCOMPARE(spy1.count(), 4); + QCOMPARE(spy1.size(), 4); logical = { 3, 1, 2, 0 }; view->swapSections(3, 0); QCOMPARE(view->sectionsMoved(), true); for (int j = 0; j < view->count(); ++j) QCOMPARE(view->logicalIndex(j), logical.at(j)); - QCOMPARE(spy1.count(), 6); + QCOMPARE(spy1.size(), 6); } void tst_QHeaderView::moveSection_data() @@ -981,9 +971,9 @@ void tst_QHeaderView::moveSection() QFETCH(const IntList, logical); QFETCH(int, count); - QCOMPARE(from.count(), to.count()); - QCOMPARE(from.count(), moved.count()); - QCOMPARE(view->count(), logical.count()); + QCOMPARE(from.size(), to.size()); + QCOMPARE(from.size(), moved.size()); + QCOMPARE(view->count(), logical.size()); QSignalSpy spy1(view, &QHeaderView::sectionMoved); QCOMPARE(view->sectionsMoved(), false); @@ -991,7 +981,7 @@ void tst_QHeaderView::moveSection() for (int h : hidden) view->setSectionHidden(h, true); - for (int i = 0; i < from.count(); ++i) { + for (int i = 0; i < from.size(); ++i) { view->moveSection(from.at(i), to.at(i)); QCOMPARE(view->sectionsMoved(), moved.at(i)); } @@ -999,7 +989,7 @@ void tst_QHeaderView::moveSection() for (int j = 0; j < view->count(); ++j) QCOMPARE(view->logicalIndex(j), logical.at(j)); - QCOMPARE(spy1.count(), count); + QCOMPARE(spy1.size(), count); } void tst_QHeaderView::resizeAndMoveSection_data() @@ -1184,14 +1174,14 @@ void tst_QHeaderView::resizeWithResizeModes() QFETCH(const IntList, expected); view->setStretchLastSection(false); - for (int i = 0; i < sections.count(); ++i) { + for (int i = 0; i < sections.size(); ++i) { view->resizeSection(i, sections.at(i)); view->setSectionResizeMode(i, modes.at(i)); } topLevel->show(); QVERIFY(QTest::qWaitForWindowExposed(topLevel)); view->resize(size, size); - for (int j = 0; j < expected.count(); ++j) + for (int j = 0; j < expected.size(); ++j) QCOMPARE(view->sectionSize(j), expected.at(j)); } @@ -1218,7 +1208,7 @@ void tst_QHeaderView::moveAndInsertSection() view->moveSection(from, to); model->insertRow(insert); - for (int i = 0; i < mapping.count(); ++i) + for (int i = 0; i < mapping.size(); ++i) QCOMPARE(view->logicalIndex(i), mapping.at(i)); } @@ -1289,21 +1279,21 @@ void tst_QHeaderView::resizeSection() view->setSectionsMovable(true); view->setStretchLastSection(false); - for (int i = 0; i < logical.count(); ++i) + for (int i = 0; i < logical.size(); ++i) if (logical.at(i) > -1 && logical.at(i) < view->count()) // for now view->setSectionResizeMode(logical.at(i), mode.at(i)); - for (int j = 0; j < logical.count(); ++j) + for (int j = 0; j < logical.size(); ++j) view->resizeSection(logical.at(j), initial); QSignalSpy spy(view, &QHeaderView::sectionResized); - for (int k = 0; k < logical.count(); ++k) + for (int k = 0; k < logical.size(); ++k) view->resizeSection(logical.at(k), size.at(k)); - QCOMPARE(spy.count(), resized); + QCOMPARE(spy.size(), resized); - for (int l = 0; l < logical.count(); ++l) + for (int l = 0; l < logical.size(); ++l) QCOMPARE(view->sectionSize(logical.at(l)), expected.at(l)); } @@ -1356,19 +1346,19 @@ void tst_QHeaderView::clearSectionSorting() QSignalSpy sectionClickedSpy(&h, &QHeaderView::sectionClicked); QVERIFY(sectionClickedSpy.isValid()); - QCOMPARE(sectionClickedSpy.count(), 0); + QCOMPARE(sectionClickedSpy.size(), 0); QSignalSpy sortIndicatorChangedSpy(&h, &QHeaderView::sortIndicatorChanged); QVERIFY(sortIndicatorChangedSpy.isValid()); - QCOMPARE(sortIndicatorChangedSpy.count(), 0); + QCOMPARE(sortIndicatorChangedSpy.size(), 0); enum { Count = 30 }; // normal behavior: clicking multiple times will just toggle the sort indicator for (int i = 0; i < Count; ++i) { QTest::mouseClick(h.viewport(), Qt::LeftButton, Qt::NoModifier, QPoint(5, 5)); - QCOMPARE(sectionClickedSpy.count(), i + 1); - QCOMPARE(sortIndicatorChangedSpy.count(), i + 1); + QCOMPARE(sectionClickedSpy.size(), i + 1); + QCOMPARE(sortIndicatorChangedSpy.size(), i + 1); QCOMPARE(h.sortIndicatorSection(), 0); const auto expectedOrder = (i % 2) == 0 ? Qt::AscendingOrder : Qt::DescendingOrder; QCOMPARE(h.sortIndicatorOrder(), expectedOrder); @@ -1385,8 +1375,8 @@ void tst_QHeaderView::clearSectionSorting() // clearing behavior: clicking multiple times will be tristate (asc, desc, nothing) for (int i = 0; i < Count; ++i) { QTest::mouseClick(h.viewport(), Qt::LeftButton, Qt::NoModifier, QPoint(5, 5)); - QCOMPARE(sectionClickedSpy.count(), i + 1); - QCOMPARE(sortIndicatorChangedSpy.count(), i + 1); + QCOMPARE(sectionClickedSpy.size(), i + 1); + QCOMPARE(sortIndicatorChangedSpy.size(), i + 1); switch (i % 3) { case 0: QCOMPARE(h.sortIndicatorSection(), 0); @@ -1511,11 +1501,11 @@ void tst_QHeaderView::testEvent() void protected_QHeaderView::testEvent() { // No crashy please - QHoverEvent enterEvent(QEvent::HoverEnter, QPoint(), QPoint()); + QHoverEvent enterEvent(QEvent::HoverEnter, QPoint(), QPoint(), QPoint()); event(&enterEvent); - QHoverEvent eventLeave(QEvent::HoverLeave, QPoint(), QPoint()); + QHoverEvent eventLeave(QEvent::HoverLeave, QPoint(), QPoint(), QPoint()); event(&eventLeave); - QHoverEvent eventMove(QEvent::HoverMove, QPoint(), QPoint()); + QHoverEvent eventMove(QEvent::HoverMove, QPoint(), QPoint(), QPoint()); event(&eventMove); } @@ -1630,7 +1620,6 @@ void tst_QHeaderView::focusPolicy() widget.show(); widget.setFocus(Qt::OtherFocusReason); - QApplication::setActiveWindow(&widget); widget.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&widget)); QVERIFY(widget.hasFocus()); @@ -1735,18 +1724,26 @@ static QByteArray savedState() return h1.saveState(); } -void tst_QHeaderView::saveRestore() +// As generated by savedState() +static const QByteArray qt5SavedSate = QByteArrayLiteral("\x00\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x02\x01\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04\b\x00\x00\x00\x01\x00\x00\x00\x03\x00\x00\x00""d\x00\x00\x00\xD2\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00""d\x00\x00\x00\x00\x00\x00\x00\x84\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00""d\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00""d\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\xE8\x00\x00\x00\x00\x00\x00\x00\x00\x00"); + +enum class SaveRestoreOption +{ + CheckGeneratedState, + DoNotCheckGeneratedState, +}; + +static void saveRestoreImpl(const QByteArray &state, SaveRestoreOption option) { QStandardItemModel m(4, 4); - const QByteArray s1 = savedState(); QHeaderView h2(Qt::Vertical); QSignalSpy spy(&h2, &QHeaderView::sortIndicatorChanged); h2.setModel(&m); - QVERIFY(h2.restoreState(s1)); + QVERIFY(h2.restoreState(state)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toInt(), 2); QCOMPARE(h2.logicalIndex(0), 2); @@ -1759,50 +1756,28 @@ void tst_QHeaderView::saveRestore() QVERIFY(h2.isSectionHidden(3)); QCOMPARE(h2.hiddenSectionCount(), 1); - QByteArray s2 = h2.saveState(); - QCOMPARE(s1, s2); + switch (option) { + case SaveRestoreOption::CheckGeneratedState: + { + QByteArray s2 = h2.saveState(); + QCOMPARE(state, s2); + break; + } + case SaveRestoreOption::DoNotCheckGeneratedState: + break; + }; QVERIFY(!h2.restoreState(QByteArrayLiteral("Garbage"))); } -void tst_QHeaderView::restoreQt4State() +void tst_QHeaderView::saveRestore() { -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - // QTBUG-40462 - // Setting from Qt4, where information about multiple sections were grouped together in one - // sectionItem object - QStandardItemModel m(4, 10); - QHeaderView h2(Qt::Vertical); - QByteArray settings_qt4 = - QByteArray::fromHex("000000ff00000000000000010000000100000000010000000000000000000000000000" - "0000000003e80000000a0101000100000000000000000000000064ffffffff00000081" - "0000000000000001000003e80000000a00000000"); - QVERIFY(h2.restoreState(settings_qt4)); - int sectionItemsLengthTotal = 0; - for (int i = 0; i < h2.count(); ++i) - sectionItemsLengthTotal += h2.sectionSize(i); - QCOMPARE(sectionItemsLengthTotal, h2.length()); - - // Buggy setting where sum(sectionItems) != length. Check false is returned and this corrupted - // state isn't restored - QByteArray settings_buggy_length = - QByteArray::fromHex("000000ff000000000000000100000000000000050100000000000000000000000a4000" - "000000010000000600000258000000fb0000000a010100010000000000000000000000" - "0064ffffffff00000081000000000000000a000000d30000000100000000000000c800" - "000001000000000000008000000001000000000000005c00000001000000000000003c" - "0000000100000000000002580000000100000000000000000000000100000000000002" - "580000000100000000000002580000000100000000000003c000000001000000000000" - "03e8"); - int old_length = h2.length(); - QByteArray old_state = h2.saveState(); - // Check setting is correctly recognized as corrupted - QVERIFY(!h2.restoreState(settings_buggy_length)); - // Check nothing has been actually restored - QCOMPARE(h2.length(), old_length); - QCOMPARE(h2.saveState(), old_state); -#else - QSKIP("Qt4 compatibility no longer needed in Qt6"); -#endif + saveRestoreImpl(savedState(), SaveRestoreOption::CheckGeneratedState); +} + +void tst_QHeaderView::QTBUG99487_saveRestoreQt5Compat() +{ + saveRestoreImpl(qt5SavedSate, SaveRestoreOption::DoNotCheckGeneratedState); } void tst_QHeaderView::restoreToMoreColumns() @@ -2072,9 +2047,9 @@ void tst_QHeaderView::sectionPressedSignal() QSignalSpy spy(&h, &QHeaderView::sectionPressed); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QTest::mousePress(h.viewport(), Qt::LeftButton, Qt::NoModifier, QPoint(5, 5)); - QCOMPARE(spy.count(), count); + QCOMPARE(spy.size(), count); } void tst_QHeaderView::sectionClickedSignal() @@ -2094,19 +2069,19 @@ void tst_QHeaderView::sectionClickedSignal() QSignalSpy spy(&h, &QHeaderView::sectionClicked); QSignalSpy spy2(&h, &QHeaderView::sortIndicatorChanged); - QCOMPARE(spy.count(), 0); - QCOMPARE(spy2.count(), 0); + QCOMPARE(spy.size(), 0); + QCOMPARE(spy2.size(), 0); QTest::mouseClick(h.viewport(), Qt::LeftButton, Qt::NoModifier, QPoint(5, 5)); - QCOMPARE(spy.count(), count); - QCOMPARE(spy2.count(), count); + QCOMPARE(spy.size(), count); + QCOMPARE(spy2.size(), count); //now let's try with the sort indicator hidden (the result should be the same spy.clear(); spy2.clear(); h.setSortIndicatorShown(false); QTest::mouseClick(h.viewport(), Qt::LeftButton, Qt::NoModifier, QPoint(5, 5)); - QCOMPARE(spy.count(), count); - QCOMPARE(spy2.count(), count); + QCOMPARE(spy.size(), count); + QCOMPARE(spy2.size(), count); } void tst_QHeaderView::defaultSectionSize_data() @@ -2222,6 +2197,9 @@ void tst_QHeaderView::preserveHiddenSectionWidth() void tst_QHeaderView::invisibleStretchLastSection() { + if (QGuiApplication::platformName().startsWith(QLatin1String("eglfs"), Qt::CaseInsensitive)) + QSKIP("EGLFS does not allow resizing on top window"); + int count = 6; QStandardItemModel model(1, count); QHeaderView view(Qt::Horizontal); @@ -2287,7 +2265,7 @@ void tst_QHeaderView::task236450_hidden() for (int i : hide1) view.hideSection(i); - QCOMPARE(view.hiddenSectionCount(), hide1.count()); + QCOMPARE(view.hiddenSectionCount(), hide1.size()); for (int i = 0; i < 6; i++) QCOMPARE(!view.isSectionHidden(i), !hide1.contains(i)); @@ -2295,13 +2273,13 @@ void tst_QHeaderView::task236450_hidden() view.scheduleDelayedItemsLayout(); view.executeDelayedItemsLayout(); //force to do a relayout - QCOMPARE(view.hiddenSectionCount(), hide1.count()); + QCOMPARE(view.hiddenSectionCount(), hide1.size()); for (int i = 0; i < 6; i++) { QCOMPARE(!view.isSectionHidden(i), !hide1.contains(i)); view.setSectionHidden(i, hide2.contains(i)); } - QCOMPARE(view.hiddenSectionCount(), hide2.count()); + QCOMPARE(view.hiddenSectionCount(), hide2.size()); for (int i = 0; i < 6; i++) QCOMPARE(!view.isSectionHidden(i), !hide2.contains(i)); } @@ -2333,10 +2311,10 @@ void tst_QHeaderView::task248050_hideRow() //returns 0 if everything is fine. static int checkHeaderViewOrder(const QHeaderView *view, const IntList &expected) { - if (view->count() != expected.count()) + if (view->count() != expected.size()) return 1; - for (int i = 0; i < expected.count(); i++) { + for (int i = 0; i < expected.size(); i++) { if (view->logicalIndex(i) != expected.at(i)) return i + 10; if (view->visualIndex(expected.at(i)) != i) @@ -2427,8 +2405,8 @@ void tst_QHeaderView::QTBUG7833_sectionClicked() QTest::mouseClick(tv.horizontalHeader()->viewport(), Qt::LeftButton, Qt::NoModifier, QPoint(tv.horizontalHeader()->sectionViewportPosition(11) + tv.horizontalHeader()->sectionSize(11) / 2, 5)); - QCOMPARE(clickedSpy.count(), 1); - QCOMPARE(pressedSpy.count(), 1); + QCOMPARE(clickedSpy.size(), 1); + QCOMPARE(pressedSpy.size(), 1); QCOMPARE(clickedSpy.at(0).at(0).toInt(), 11); QCOMPARE(pressedSpy.at(0).at(0).toInt(), 11); @@ -2436,8 +2414,8 @@ void tst_QHeaderView::QTBUG7833_sectionClicked() QPoint(tv.horizontalHeader()->sectionViewportPosition(8) + tv.horizontalHeader()->sectionSize(0) / 2, 5)); - QCOMPARE(clickedSpy.count(), 2); - QCOMPARE(pressedSpy.count(), 2); + QCOMPARE(clickedSpy.size(), 2); + QCOMPARE(pressedSpy.size(), 2); QCOMPARE(clickedSpy.at(1).at(0).toInt(), 8); QCOMPARE(pressedSpy.at(1).at(0).toInt(), 8); @@ -2445,8 +2423,8 @@ void tst_QHeaderView::QTBUG7833_sectionClicked() QPoint(tv.horizontalHeader()->sectionViewportPosition(0) + tv.horizontalHeader()->sectionSize(0) / 2, 5)); - QCOMPARE(clickedSpy.count(), 3); - QCOMPARE(pressedSpy.count(), 3); + QCOMPARE(clickedSpy.size(), 3); + QCOMPARE(pressedSpy.size(), 3); QCOMPARE(clickedSpy.at(2).at(0).toInt(), 0); QCOMPARE(pressedSpy.at(2).at(0).toInt(), 0); } @@ -2573,7 +2551,7 @@ public: void insertRowAtBeginning() { Q_EMIT layoutAboutToBeChanged(); - m_displayNames.insert(0, QStringLiteral("Item %1").arg(m_displayNames.count())); + m_displayNames.insert(0, QStringLiteral("Item %1").arg(m_displayNames.size())); // Rows are always inserted at the beginning, so move all others. const auto pl = persistentIndexList(); // The vertical header view will have a persistent index stored here on the second call to insertRowAtBeginning. @@ -2589,7 +2567,7 @@ public: QModelIndex index(int row, int column, const QModelIndex &) const override { return createIndex(row, column); } QModelIndex parent(const QModelIndex &) const override { return QModelIndex(); } - int rowCount(const QModelIndex &) const override { return m_displayNames.count(); } + int rowCount(const QModelIndex &) const override { return m_displayNames.size(); } int columnCount(const QModelIndex &) const override { return 1; } private: @@ -2873,10 +2851,6 @@ void tst_QHeaderView::calculateAndCheck(int cppline, const int precalced_compare int sum_visual = 0; int sum_logical = 0; - int sum_size = 0; - int sum_hidden_size = 0; - int sum_lookup_visual = 0; - int sum_lookup_logical = 0; int chk_visual = 1; int chk_logical = 1; @@ -2896,7 +2870,6 @@ void tst_QHeaderView::calculateAndCheck(int cppline, const int precalced_compare sum_visual += visual; sum_logical += logical; - sum_size += ssize; if (visual >= 0) { chk_visual %= p2; @@ -2916,7 +2889,6 @@ void tst_QHeaderView::calculateAndCheck(int cppline, const int precalced_compare if (view->isSectionHidden(i)) { view->showSection(i); int hiddensize = view->sectionSize(i); - sum_hidden_size += hiddensize; chk_hidden_size %= p2; chk_hidden_size += ( (hiddensize + 1) * (i + 1) * p1); // (hiddensize + 1) in the above to differ between hidden and size 0 @@ -2935,8 +2907,6 @@ void tst_QHeaderView::calculateAndCheck(int cppline, const int precalced_compare for (int u = 0; u < max_lookup_count; ++u) { int visu = view->visualIndexAt(u); int logi = view->logicalIndexAt(u); - sum_lookup_visual += logi; - sum_lookup_logical += visu; chk_lookup_visual %= p2; chk_lookup_visual *= ( (u + 1) * p1 * (visu + 2)); chk_lookup_logical %= p2; @@ -3412,7 +3382,7 @@ void tst_QHeaderView::stretchAndRestoreLastSection() const int someOtherSectionSize = 40; const int biggerSizeThanAnySection = 50; - QVERIFY(QTest::qWaitForWindowExposed(&tv)); + QVERIFY(QTest::qWaitForWindowActive(&tv)); QHeaderView &header = *tv.horizontalHeader(); // set minimum size before resizeSections() is called @@ -3641,7 +3611,6 @@ void tst_QHeaderView::statusTips() headerView.setGeometry(QRect(QPoint(QGuiApplication::primaryScreen()->geometry().center() - QPoint(250, 250)), QSize(500, 500))); headerView.show(); - QApplication::setActiveWindow(&headerView); QVERIFY(QTest::qWaitForWindowActive(&headerView)); // Ensure it is moved away first and then moved to the relevant section @@ -3676,5 +3645,41 @@ void tst_QHeaderView::testRemovingColumnsViaLayoutChanged() // The main point of this test is that the section-size restoring code didn't go out of bounds. } +void tst_QHeaderView::testModelMovingColumns() +{ + QtTestModel model(10, 10); + QHeaderView hv(Qt::Horizontal); + hv.setModel(&model); + hv.resizeSections(QHeaderView::ResizeToContents); + hv.show(); + hv.hideSection(3); + QVERIFY(!hv.isSectionHidden(1)); + QVERIFY(hv.isSectionHidden(3)); + + QPersistentModelIndex index3 = model.index(0, 3); + model.moveColumn(3, 1); + QCOMPARE(index3.column(), 1); + QVERIFY(hv.isSectionHidden(1)); + QVERIFY(!hv.isSectionHidden(3)); +} + +void tst_QHeaderView::testModelMovingRows() +{ + QtTestModel model(10, 10); + QHeaderView hv(Qt::Vertical); + hv.setModel(&model); + hv.resizeSections(QHeaderView::ResizeToContents); + hv.show(); + hv.hideSection(3); + QVERIFY(!hv.isSectionHidden(1)); + QVERIFY(hv.isSectionHidden(3)); + + QPersistentModelIndex index3 = model.index(3, 0); + model.moveRow(3, 1); + QCOMPARE(index3.row(), 1); + QVERIFY(hv.isSectionHidden(1)); + QVERIFY(!hv.isSectionHidden(3)); +} + QTEST_MAIN(tst_QHeaderView) #include "tst_qheaderview.moc" diff --git a/tests/auto/widgets/itemviews/qitemdelegate/BLACKLIST b/tests/auto/widgets/itemviews/qitemdelegate/BLACKLIST deleted file mode 100644 index 5eda888af6..0000000000 --- a/tests/auto/widgets/itemviews/qitemdelegate/BLACKLIST +++ /dev/null @@ -1,6 +0,0 @@ -[enterKey] -opensuse-42.3 ci -[testLineEditValidation] -opensuse-42.3 ci -[editorKeyPress] -ubuntu-20.04 diff --git a/tests/auto/widgets/itemviews/qitemdelegate/CMakeLists.txt b/tests/auto/widgets/itemviews/qitemdelegate/CMakeLists.txt index 50057bbd7c..58a00fdc9d 100644 --- a/tests/auto/widgets/itemviews/qitemdelegate/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qitemdelegate/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qitemdelegate.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qitemdelegate Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qitemdelegate LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qitemdelegate SOURCES tst_qitemdelegate.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets Qt::WidgetsPrivate @@ -17,6 +24,6 @@ qt_internal_add_test(tst_qitemdelegate ##################################################################### qt_internal_extend_target(tst_qitemdelegate CONDITION WIN32 - PUBLIC_LIBRARIES + LIBRARIES user32 ) diff --git a/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp b/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp index 25212373a4..769456951f 100644 --- a/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp +++ b/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp @@ -1,35 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "../../../shared/highdpi.h" #include <QTest> #include <QTimeZone> +#include <QDateTime> #include <QTimer> #include <QTestEventLoop> #include <QSignalSpy> @@ -59,11 +35,12 @@ #include <qscreen.h> #include <QtWidgets/private/qabstractitemdelegate_p.h> +#include <QtWidgets/private/qapplication_p.h> Q_DECLARE_METATYPE(QAbstractItemDelegate::EndEditHint) #if defined (Q_OS_WIN) -#include <windows.h> +#include <qt_windows.h> #define Q_CHECK_PAINTEVENTS \ if (::SwitchDesktop(::GetThreadDesktop(::GetCurrentThreadId())) == 0) \ QSKIP("The widgets don't get the paint events"); @@ -230,6 +207,8 @@ private slots: void dateTextForRole_data(); void dateTextForRole(); + void reuseEditor(); + private: #ifdef QT_BUILD_INTERNAL struct RoleDelegate : public QItemDelegate @@ -350,16 +329,15 @@ void tst_QItemDelegate::editorKeyPress() view.edit(index); QList<QLineEdit*> lineEditors = view.viewport()->findChildren<QLineEdit *>(); - QCOMPARE(lineEditors.count(), 1); + QCOMPARE(lineEditors.size(), 1); QLineEdit *editor = lineEditors.at(0); QCOMPARE(editor->selectedText(), initial); QTest::keyClicks(editor, expected); QTest::keyClick(editor, Qt::Key_Enter); - QApplication::processEvents(); - QCOMPARE(index.data().toString(), expected); + QTRY_COMPARE(index.data().toString(), expected); } void tst_QItemDelegate::doubleEditorNegativeInput() @@ -379,7 +357,7 @@ void tst_QItemDelegate::doubleEditorNegativeInput() view.edit(index); QList<QDoubleSpinBox*> editors = view.viewport()->findChildren<QDoubleSpinBox *>(); - QCOMPARE(editors.count(), 1); + QCOMPARE(editors.size(), 1); QDoubleSpinBox *editor = editors.at(0); QCOMPARE(editor->value(), double(10)); @@ -696,33 +674,33 @@ void tst_QItemDelegate::testEventFilter() //For each test we send a key event and check if signals were emitted. event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier); QVERIFY(delegate.eventFilter(&widget, event)); - QCOMPARE(closeEditorSpy.count(), 1); - QCOMPARE(commitDataSpy.count(), 1); + QCOMPARE(closeEditorSpy.size(), 1); + QCOMPARE(commitDataSpy.size(), 1); delete event; event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier); QVERIFY(delegate.eventFilter(&widget, event)); - QCOMPARE(closeEditorSpy.count(), 2); - QCOMPARE(commitDataSpy.count(), 2); + QCOMPARE(closeEditorSpy.size(), 2); + QCOMPARE(commitDataSpy.size(), 2); delete event; event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Escape, Qt::NoModifier); QVERIFY(delegate.eventFilter(&widget, event)); - QCOMPARE(closeEditorSpy.count(), 3); - QCOMPARE(commitDataSpy.count(), 2); + QCOMPARE(closeEditorSpy.size(), 3); + QCOMPARE(commitDataSpy.size(), 2); delete event; event = new QKeyEvent(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier); QVERIFY(!delegate.eventFilter(&widget, event)); - QCOMPARE(closeEditorSpy.count(), 3); - QCOMPARE(commitDataSpy.count(), 2); + QCOMPARE(closeEditorSpy.size(), 3); + QCOMPARE(commitDataSpy.size(), 2); delete event; //Subtest focusEvent event = new QFocusEvent(QEvent::FocusOut); QVERIFY(!delegate.eventFilter(&widget, event)); - QCOMPARE(closeEditorSpy.count(), 4); - QCOMPARE(commitDataSpy.count(), 3); + QCOMPARE(closeEditorSpy.size(), 4); + QCOMPARE(commitDataSpy.size(), 3); delete event; } @@ -769,7 +747,6 @@ void tst_QItemDelegate::dateTimeEditor() widget.setItem(0, 2, item3); widget.show(); QVERIFY(QTest::qWaitForWindowExposed(&widget)); - QApplication::setActiveWindow(&widget); widget.editItem(item1); @@ -785,7 +762,6 @@ void tst_QItemDelegate::dateTimeEditor() timeEditor->setTime(time.addSecs(60)); widget.clearFocus(); - qApp->setActiveWindow(&widget); widget.setFocus(); widget.editItem(item2); @@ -1049,7 +1025,6 @@ void tst_QItemDelegate::decoration() TestItemDelegate delegate; table.setItemDelegate(&delegate); table.show(); - QApplication::setActiveWindow(&table); QVERIFY(QTest::qWaitForWindowActive(&table)); QVariant value; @@ -1245,7 +1220,7 @@ void tst_QItemDelegate::editorEvent() QPoint pos = inCheck ? qApp->style()->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &option, 0).center() + QPoint(checkMargin, 0) : QPoint(200,200); QEvent *event = new QMouseEvent((QEvent::Type)type, - pos, + pos, pos, (Qt::MouseButton)button, (Qt::MouseButton)button, Qt::NoModifier); @@ -1304,7 +1279,6 @@ void tst_QItemDelegate::enterKey() QListView view; view.setModel(&model); view.show(); - QApplication::setActiveWindow(&view); view.setFocus(); QVERIFY(QTest::qWaitForWindowActive(&view)); @@ -1338,7 +1312,7 @@ void tst_QItemDelegate::enterKey() view.edit(index); QList<QWidget*> lineEditors = view.viewport()->findChildren<QWidget *>(QString::fromLatin1("TheEditor")); - QCOMPARE(lineEditors.count(), 1); + QCOMPARE(lineEditors.size(), 1); QPointer<QWidget> editor = lineEditors.at(0); QCOMPARE(editor->hasFocus(), true); @@ -1364,7 +1338,6 @@ void tst_QItemDelegate::task257859_finalizeEdit() QListView view; view.setModel(&model); view.show(); - QApplication::setActiveWindow(&view); view.setFocus(); QVERIFY(QTest::qWaitForWindowActive(&view)); @@ -1372,7 +1345,7 @@ void tst_QItemDelegate::task257859_finalizeEdit() view.edit(index); QList<QLineEdit *> lineEditors = view.viewport()->findChildren<QLineEdit *>(); - QCOMPARE(lineEditors.count(), 1); + QCOMPARE(lineEditors.size(), 1); QPointer<QWidget> editor = lineEditors.at(0); QCOMPARE(editor->hasFocus(), true); @@ -1394,6 +1367,7 @@ void tst_QItemDelegate::QTBUG4435_keepSelectionOnCheck() } QTableView view; view.setModel(&model); + view.setSelectionMode(QAbstractItemView::MultiSelection); view.setItemDelegate(new TestItemDelegate(&view)); view.show(); view.selectAll(); @@ -1404,11 +1378,16 @@ void tst_QItemDelegate::QTBUG4435_keepSelectionOnCheck() option.features = QStyleOptionViewItem::HasDisplay | QStyleOptionViewItem::HasCheckIndicator; option.checkState = Qt::CheckState(model.index(0, 0).data(Qt::CheckStateRole).toInt()); const int checkMargin = qApp->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, 0) + 1; - QPoint pos = qApp->style()->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &option, 0).center() - + QPoint(checkMargin, 0); - QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, pos); - QTRY_VERIFY(view.selectionModel()->isColumnSelected(0, QModelIndex())); + QRect checkRect = qApp->style()->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &option, 0); + checkRect.translate(checkMargin, 0); + // click into the check mark checks, but doesn't change selection + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, checkRect.center()); QCOMPARE(model.item(0)->checkState(), Qt::Checked); + QTRY_VERIFY(view.selectionModel()->isColumnSelected(0, QModelIndex())); + // click outside the check mark doesn't check, and changes selection + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, + checkRect.center() + QPoint(checkRect.width(), 0)); + QTRY_VERIFY(!view.selectionModel()->isColumnSelected(0, QModelIndex())); } void tst_QItemDelegate::comboBox() @@ -1420,7 +1399,7 @@ void tst_QItemDelegate::comboBox() widget.setItem(0, 0, item1); widget.show(); QVERIFY(QTest::qWaitForWindowExposed(&widget)); - QApplication::setActiveWindow(&widget); + QApplicationPrivate::setActiveWindow(&widget); widget.editItem(item1); @@ -1485,7 +1464,6 @@ void tst_QItemDelegate::testLineEditValidation() view.setItemDelegate(&delegate); view.show(); view.setFocus(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QPointer<QLineEdit> editor; @@ -1497,7 +1475,7 @@ void tst_QItemDelegate::testLineEditValidation() const auto findEditors = [&]() { return view.findChildren<QLineEdit *>(QStringLiteral("TheEditor")); }; - QCOMPARE(findEditors().count(), 1); + QCOMPARE(findEditors().size(), 1); editor = findEditors().at(0); editor->clear(); @@ -1517,7 +1495,7 @@ void tst_QItemDelegate::testLineEditValidation() view.setCurrentIndex(index); view.edit(index); - QTRY_COMPARE(findEditors().count(), 1); + QTRY_COMPARE(findEditors().size(), 1); editor = findEditors().at(0); editor->clear(); @@ -1539,13 +1517,13 @@ void tst_QItemDelegate::testLineEditValidation() // reset the view to forcibly close the editor view.reset(); - QTRY_COMPARE(findEditors().count(), 0); + QTRY_COMPARE(findEditors().size(), 0); // set a valid text again view.setCurrentIndex(index); view.edit(index); - QTRY_COMPARE(findEditors().count(), 1); + QTRY_COMPARE(findEditors().size(), 1); editor = findEditors().at(0); editor->clear(); @@ -1609,12 +1587,12 @@ void tst_QItemDelegate::dateTextForRole_data() QDate date(2013, 12, 11); QTime time(10, 9, 8, 765); // Ensure we exercise every time-spec variant: - QTest::newRow("local") << QDateTime(date, time, Qt::LocalTime); - QTest::newRow("UTC") << QDateTime(date, time, Qt::UTC); -#if QT_CONFIG(timezone) + QTest::newRow("local") << QDateTime(date, time); + QTest::newRow("UTC") << QDateTime(date, time, QTimeZone::UTC); +# if QT_CONFIG(timezone) QTest::newRow("zone") << QDateTime(date, time, QTimeZone("Europe/Dublin")); -#endif - QTest::newRow("offset") << QDateTime(date, time, Qt::OffsetFromUTC, 36000); +# endif + QTest::newRow("offset") << QDateTime(date, time, QTimeZone::fromSecondsAheadOfUtc(36000)); #endif } @@ -1637,6 +1615,75 @@ void tst_QItemDelegate::dateTextForRole() #endif } +void tst_QItemDelegate::reuseEditor() +{ + class ReusingDelegate: public QItemDelegate { + public: + using QItemDelegate::QItemDelegate; + ~ReusingDelegate() + { + if (cached) + cached->deleteLater(); + } + + QWidget* createEditor(QWidget* parent, + const QStyleOptionViewItem&, + const QModelIndex&) const override + { + auto *cb = new QComboBox(parent); + cb->addItem("One"); + cb->addItem("Two"); + cb->setEditable(true); + return cb; + } + + void setEditorData(QWidget* editor, const QModelIndex& index) + const override + { + auto *cb = qobject_cast<QComboBox*>(editor); + cb->setCurrentText(index.data(Qt::DisplayRole).toString()); + } + + void setModelData(QWidget* editor, + QAbstractItemModel* model, + const QModelIndex& index) const override + { + auto *cb = qobject_cast<QComboBox*>(editor); + model->setData(index, cb->currentText(), Qt::DisplayRole); + } + + void destroyEditor(QWidget* editor, const QModelIndex&) const override + { + auto *cb = qobject_cast<QComboBox*>(editor); + cb->setParent(nullptr); // How to completely detach the editor from treeview ? + cb->hide(); + cb->setEnabled(false); + cached = cb; + } + + private: + mutable QComboBox* cached = nullptr; + }; + + QStandardItemModel model; + model.appendRow(new QStandardItem("One")); + model.appendRow(new QStandardItem("Two")); + + ReusingDelegate delegate; + + QTreeView tree; + tree.setModel(&model); + tree.setItemDelegate(&delegate); + + tree.show(); + QVERIFY(QTest::qWaitForWindowActive(&tree)); + + tree.edit(model.index(0, 0)); + QTRY_VERIFY(qobject_cast<QComboBox *>(tree.focusWidget())); + + tree.close(); +} + // ### _not_ covered: // editing with a custom editor factory diff --git a/tests/auto/widgets/itemviews/qitemeditorfactory/CMakeLists.txt b/tests/auto/widgets/itemviews/qitemeditorfactory/CMakeLists.txt index e57ee0d01c..608c323b44 100644 --- a/tests/auto/widgets/itemviews/qitemeditorfactory/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qitemeditorfactory/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qitemeditorfactory.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qitemeditorfactory Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qitemeditorfactory LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qitemeditorfactory SOURCES tst_qitemeditorfactory.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/itemviews/qitemeditorfactory/tst_qitemeditorfactory.cpp b/tests/auto/widgets/itemviews/qitemeditorfactory/tst_qitemeditorfactory.cpp index 6f176c4ba5..dcd751c696 100644 --- a/tests/auto/widgets/itemviews/qitemeditorfactory/tst_qitemeditorfactory.cpp +++ b/tests/auto/widgets/itemviews/qitemeditorfactory/tst_qitemeditorfactory.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QDoubleSpinBox> diff --git a/tests/auto/widgets/itemviews/qitemview/CMakeLists.txt b/tests/auto/widgets/itemviews/qitemview/CMakeLists.txt index 93f10f0fbf..8ff1fd67c9 100644 --- a/tests/auto/widgets/itemviews/qitemview/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qitemview/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qitemview.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qitemview Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qitemview LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qitemview SOURCES tst_qitemview.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/itemviews/qitemview/tst_qitemview.cpp b/tests/auto/widgets/itemviews/qitemview/tst_qitemview.cpp index 244998eb1c..d5a6ddea6e 100644 --- a/tests/auto/widgets/itemviews/qitemview/tst_qitemview.cpp +++ b/tests/auto/widgets/itemviews/qitemview/tst_qitemview.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QRandomGenerator> #include <QStack> diff --git a/tests/auto/widgets/itemviews/qitemview/viewstotest.cpp b/tests/auto/widgets/itemviews/qitemview/viewstotest.cpp index 19f0b80cc6..0c7c17503e 100644 --- a/tests/auto/widgets/itemviews/qitemview/viewstotest.cpp +++ b/tests/auto/widgets/itemviews/qitemview/viewstotest.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QHeaderView> #include <QListView> diff --git a/tests/auto/widgets/itemviews/qlistview/CMakeLists.txt b/tests/auto/widgets/itemviews/qlistview/CMakeLists.txt index bd440ff203..f6c9f154de 100644 --- a/tests/auto/widgets/itemviews/qlistview/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qlistview/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qlistview.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qlistview Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qlistview LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qlistview SOURCES tst_qlistview.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate @@ -20,6 +27,6 @@ qt_internal_add_test(tst_qlistview ##################################################################### qt_internal_extend_target(tst_qlistview CONDITION WIN32 - PUBLIC_LIBRARIES + LIBRARIES user32 ) diff --git a/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp b/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp index 98dec7ded9..236cd6212f 100644 --- a/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp +++ b/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QListWidget> @@ -37,14 +12,18 @@ #include <QTest> #include <QTimer> #include <QtMath> +#include <QProxyStyle> +#include <QVBoxLayout> +#include <QDialog> #include <QtTest/private/qtesthelpers_p.h> #include <QtWidgets/private/qlistview_p.h> +#include <QtWidgets/private/qapplication_p.h> using namespace QTestPrivate; #if defined(Q_OS_WIN) -# include <windows.h> +# include <qt_windows.h> # include <QDialog> # include <QGuiApplication> # include <QVBoxLayout> @@ -89,8 +68,14 @@ public: { return QListView::visualRegionForSelection(selectionModel()->selection()); } + void moveEvent(QMoveEvent *e) override + { + QListView::moveEvent(e); + m_gotValidResizeEvent = !e->pos().isNull(); + } friend class tst_QListView; + bool m_gotValidResizeEvent = false; }; class tst_QListView : public QObject @@ -109,6 +94,8 @@ private slots: void moveCursor(); void moveCursor2(); void moveCursor3(); + void moveCursor4(); + void moveCursor5(); void indexAt(); void clicked(); void singleSelectionRemoveRow(); @@ -164,11 +151,18 @@ private slots: void taskQTBUG_39902_mutualScrollBars(); void horizontalScrollingByVerticalWheelEvents(); void taskQTBUG_7232_AllowUserToControlSingleStep(); + void taskQTBUG_58749_adjustToContent(); void taskQTBUG_51086_skippingIndexesInSelectedIndexes(); void taskQTBUG_47694_indexOutOfBoundBatchLayout(); + void moveLastRow(); void itemAlignment(); void internalDragDropMove_data(); void internalDragDropMove(); + void spacingWithWordWrap_data(); + void spacingWithWordWrap(); + void scrollOnRemove_data(); + void scrollOnRemove(); + void wordWrapNullIcon(); }; // Testing get/set functions @@ -434,7 +428,7 @@ void tst_QListView::cursorMove() } break; default: - QVERIFY(false); + QFAIL(qPrintable(QStringLiteral("Unexpected key: %1").arg(key))); } QCoreApplication::processEvents(); @@ -572,6 +566,76 @@ void tst_QListView::moveCursor3() QCOMPARE(view.selectionModel()->currentIndex(), model.index(0, 0)); } +void tst_QListView::moveCursor4() +{ + int indexCount = 100; + PublicListView listView; + QStandardItemModel model; + for (int i = 0; i < 100; i++) + { + QStandardItem* item = new QStandardItem(QString("item 0%0").arg(i)); + QFont font = item->font(); + font.setPixelSize(14); + item->setFont(font); + model.appendRow(item); + } + QFont font = model.item(0)->font(); + font.setPixelSize(50); + font.setBold(true); + model.item(0)->setFont(font); + listView.setModel(&model); + listView.setFixedSize(200, 200); + listView.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + listView.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + listView.show(); + listView.selectionModel()->setCurrentIndex(model.index(0, 0), QItemSelectionModel::SelectCurrent); + + QModelIndex idx = listView.moveCursor(PublicListView::MovePageDown, Qt::NoModifier); + + int actualIndex = 0; + int indexHeight = 0; + while (indexHeight <= listView.viewport()->height()) { + indexHeight += listView.visualRect(model.item(actualIndex)->index()).height(); + actualIndex++; + } + QTRY_COMPARE(idx, model.index(actualIndex - 2, 0)); + idx = listView.moveCursor(PublicListView::MoveUp, Qt::NoModifier); + QTRY_COMPARE(idx, model.index(0, 0)); + + listView.setCurrentIndex(model.index(indexCount - 2, 0)); + idx = listView.moveCursor(PublicListView::MovePageDown, Qt::NoModifier); + QTRY_COMPARE(idx, model.index(99, 0)); + + listView.setCurrentIndex(model.index(3, 0)); + actualIndex = 3; + indexHeight = 0; + while (indexHeight <= listView.viewport()->height()) { + indexHeight += listView.visualRect(model.item(actualIndex)->index()).height(); + actualIndex++; + } + idx = listView.moveCursor(PublicListView::MovePageDown, Qt::NoModifier); + QTRY_COMPARE(idx, model.index(actualIndex - 2, 0)); +} + +void tst_QListView::moveCursor5() +{ + PublicListView listView; + QStandardItemModel model; + QIcon icon(QPixmap(300,300)); + model.appendRow(new QStandardItem(icon,"11")); + model.appendRow(new QStandardItem(icon,"22")); + model.appendRow(new QStandardItem(icon,"33")); + listView.setModel(&model); + listView.setGeometry(10,10,200,200); + listView.setIconSize(QSize(300,300)); + listView.setViewMode(QListView::IconMode); + listView.setCurrentIndex(model.index(0, 0)); + + QModelIndex idx = listView.moveCursor(PublicListView::MovePageDown, Qt::NoModifier); + QTRY_COMPARE(idx, model.index(1, 0)); + idx = listView.moveCursor(PublicListView::MovePageUp, Qt::NoModifier); + QTRY_COMPARE(idx, model.index(0, 0)); +} class QListViewShowEventListener : public QListView { @@ -662,7 +726,7 @@ void tst_QListView::clicked() continue; QSignalSpy spy(&view, &QListView::clicked); QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } } @@ -1091,7 +1155,7 @@ void tst_QListView::selection() v.setSelection(selectionRect, QItemSelectionModel::ClearAndSelect); const QModelIndexList selected = v.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), expectedItems.count()); + QCOMPARE(selected.size(), expectedItems.size()); for (const auto &idx : selected) QVERIFY(expectedItems.contains(idx.row())); } @@ -1501,7 +1565,7 @@ void tst_QListView::task203585_selectAll() QVERIFY(view.selectionModel()->selectedIndexes().isEmpty()); view.setRowHidden(0, false); view.selectAll(); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 1); } void tst_QListView::task228566_infiniteRelayout() @@ -1586,7 +1650,7 @@ void tst_QListView::task196118_visualRegionForSelection() view.selectionModel()->select(top1.index(), QItemSelectionModel::Select); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 1); QVERIFY(view.getVisualRegionForSelection().isEmpty()); } @@ -1643,7 +1707,6 @@ void tst_QListView::keyboardSearch() QListView view; view.setModel(&model); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QTest::keyClick(&view, Qt::Key_K); @@ -1684,7 +1747,7 @@ void tst_QListView::shiftSelectionWithNonUniformItemSizes() QTRY_COMPARE(view.currentIndex(), model.index(1, 0)); QModelIndexList selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 3); + QCOMPARE(selected.size(), 3); QVERIFY(!selected.contains(model.index(0, 0))); } { // Second test: QListView::TopToBottom flow @@ -1711,7 +1774,7 @@ void tst_QListView::shiftSelectionWithNonUniformItemSizes() QTRY_COMPARE(view.currentIndex(), model.index(1, 0)); QModelIndexList selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 3); + QCOMPARE(selected.size(), 3); QVERIFY(!selected.contains(model.index(0, 0))); } } @@ -1745,7 +1808,6 @@ void tst_QListView::shiftSelectionWithItemAlignment() view.resize(300, view.sizeHintForRow(0) * items.size() / 2 + view.horizontalScrollBar()->height()); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(static_cast<QWidget *>(&view), QApplication::activeWindow()); @@ -1773,7 +1835,7 @@ void tst_QListView::clickOnViewportClearsSelection() view.selectAll(); QModelIndex index = model.index(0); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 1); QVERIFY(view.selectionModel()->isSelected(index)); //we try to click outside of the index @@ -1781,7 +1843,7 @@ void tst_QListView::clickOnViewportClearsSelection() QTest::mousePress(view.viewport(), Qt::LeftButton, {}, point); //at this point, the selection shouldn't have changed - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 1); QVERIFY(view.selectionModel()->isSelected(index)); QTest::mouseRelease(view.viewport(), Qt::LeftButton, {}, point); @@ -1804,7 +1866,6 @@ void tst_QListView::task262152_setModelColumnNavigate() view.setModelColumn(1); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(&view, QApplication::activeWindow()); QTest::keyClick(&view, Qt::Key_Down); @@ -1890,7 +1951,7 @@ void tst_QListView::taskQTBUG_435_deselectOnViewportClick() view.setModel(&model); view.setSelectionMode(QAbstractItemView::ExtendedSelection); view.selectAll(); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), model.rowCount()); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), model.rowCount()); const QRect itemRect = view.visualRect(model.index(model.rowCount() - 1)); @@ -1900,7 +1961,7 @@ void tst_QListView::taskQTBUG_435_deselectOnViewportClick() QVERIFY(!view.selectionModel()->hasSelection()); view.selectAll(); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), model.rowCount()); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), model.rowCount()); //and now the right button QTest::mouseClick(view.viewport(), Qt::RightButton, {}, p); @@ -2471,6 +2532,46 @@ void tst_QListView::taskQTBUG_7232_AllowUserToControlSingleStep() QCOMPARE(hStep1, lv.horizontalScrollBar()->singleStep()); } +void tst_QListView::taskQTBUG_58749_adjustToContent() +{ + QStandardItemModel model; + model.setRowCount(20); + model.setColumnCount(1); + const QString rowStr = QStringLiteral("Row number txt:"); + for (int u = 0; u < model.rowCount(); ++u) + model.setData(model.index(u, 0), rowStr + QString::number(u)); + + QDialog w; // It really should work for QWidget, too, but sometimes an event (like move) + // is needed to get the resize triggered. + QVBoxLayout *l = new QVBoxLayout(&w); + l->setSizeConstraint(QLayout::SetFixedSize); + auto *view = new QListView; + view->setModel(&model); + view->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); + l->addWidget(view); + l->setSizeConstraint(QLayout::SetFixedSize); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + + const QString longText = "Here we have a row text that is somewhat longer ..."; + + QFontMetrics fm(w.font(), &w); + QRect r = fm.boundingRect(model.data(model.index(0, 0)).toString()); + const int longTextWidth = fm.horizontalAdvance(longText); + QVERIFY(w.height() > r.height() * model.rowCount()); + // We have a width longer than the width for the given index data. + QVERIFY(w.width() > r.width()); + // but ... the width should not have a width matching the much longer text. + QVERIFY(w.width() < longTextWidth); + + // use the long text and make sure the width is adjusted. + model.setData(model.index(0, 0), longText); + QApplication::processEvents(); + const QRect itemRect = view->visualRect(model.index(0, 0)); + QVERIFY(w.width() > itemRect.width()); + QCOMPARE_GE(view->width(), itemRect.width()); +} + void tst_QListView::taskQTBUG_51086_skippingIndexesInSelectedIndexes() { QStandardItemModel data(10, 1); @@ -2504,6 +2605,221 @@ void tst_QListView::taskQTBUG_47694_indexOutOfBoundBatchLayout() view.scrollTo(model.index(batchSize - 1, 0)); } +class TstMoveItem +{ + friend class TstMoveModel; +public: + TstMoveItem(TstMoveItem *parent = nullptr) + : parentItem(parent) + { + if (parentItem) + parentItem->childItems.append(this); + } + + ~TstMoveItem() + { + QList<TstMoveItem *> delItms; + delItms.swap(childItems); + qDeleteAll(delItms); + + if (parentItem) + parentItem->childItems.removeAll(this); + } + + int row() + { + if (parentItem) + return parentItem->childItems.indexOf(this); + return -1; + } + +public: + TstMoveItem *parentItem = nullptr; + QList<TstMoveItem *> childItems; + QHash<int, QVariant> data; +}; + +/*! + Test that removing the last row in an IconView mode QListView + doesn't crash. The model is specifically crafted to provoke a + stale QBspTree by returning a 0 column count for indexes without + children, which changes the column count after moving the last row. + + See QTBUG_95463. +*/ +class TstMoveModel : public QAbstractItemModel +{ + Q_OBJECT +public: + TstMoveModel(QObject *parent = nullptr) + : QAbstractItemModel(parent) + { + rootItem = new TstMoveItem; + rootItem->data.insert(Qt::DisplayRole, "root"); + + TstMoveItem *itm = new TstMoveItem(rootItem); + itm->data.insert(Qt::DisplayRole, "parentItem1"); + + TstMoveItem *itmCh = new TstMoveItem(itm); + itmCh->data.insert(Qt::DisplayRole, "childItem"); + + itm = new TstMoveItem(rootItem); + itm->data.insert(Qt::DisplayRole, "parentItem2"); + } + + ~TstMoveModel() + { + delete rootItem; + } + + QModelIndex index(int row, int column, const QModelIndex &idxPar = QModelIndex()) const override + { + QModelIndex idx; + if (hasIndex(row, column, idxPar)) { + TstMoveItem *parentItem = nullptr; + if (idxPar.isValid()) + parentItem = static_cast<TstMoveItem *>(idxPar.internalPointer()); + else + parentItem = rootItem; + + Q_ASSERT(parentItem); + TstMoveItem *childItem = parentItem->childItems.at(row); + if (childItem) + idx = createIndex(row, column, childItem); + } + return idx; + } + + QModelIndex parent(const QModelIndex &index) const override + { + QModelIndex idxPar; + if (index.isValid()) { + TstMoveItem *childItem = static_cast<TstMoveItem *>(index.internalPointer()); + TstMoveItem *parentItem = childItem->parentItem; + if (parentItem != rootItem) + idxPar = createIndex(parentItem->row(), 0, parentItem); + } + return idxPar; + } + + int columnCount(const QModelIndex &idxPar = QModelIndex()) const override + { + int cnt = 0; + if (idxPar.isValid()) { + TstMoveItem *parentItem = static_cast<TstMoveItem *>(idxPar.internalPointer()); + Q_ASSERT(parentItem); + cnt = parentItem->childItems.isEmpty() ? 0 : 1; + } else { + cnt = rootItem->childItems.isEmpty() ? 0 : 1; + } + return cnt; + } + + int rowCount(const QModelIndex &idxPar = QModelIndex()) const override + { + int cnt = 0; + if (idxPar.isValid()) { + TstMoveItem *parentItem = static_cast<TstMoveItem *>(idxPar.internalPointer()); + Q_ASSERT(parentItem); + cnt = parentItem->childItems.size(); + } else { + cnt = rootItem->childItems.size(); + } + return cnt; + } + + Qt::ItemFlags flags(const QModelIndex &index) const override + { + Q_UNUSED(index) + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; + } + + bool hasChildren(const QModelIndex &parent = QModelIndex()) const override + { + bool ret = false; + if (parent.isValid()) { + TstMoveItem *parentItem = static_cast<TstMoveItem *>(parent.internalPointer()); + Q_ASSERT(parentItem); + ret = parentItem->childItems.size() > 0; + } else { + ret = rootItem->childItems.size() > 0; + } + return ret; + } + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override + { + QVariant dt; + if (index.isValid()) { + TstMoveItem *item = static_cast<TstMoveItem *>(index.internalPointer()); + if (item) + dt = item->data.value(role); + } + return dt; + } + + bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override + { + TstMoveItem *itmSrcParent = itemAt(sourceParent); + TstMoveItem *itmDestParent = itemAt(destinationParent); + + if (itmSrcParent && sourceRow >= 0 + && sourceRow + count <= itmSrcParent->childItems.size() + && itmDestParent && destinationChild <= itmDestParent->childItems.size()) { + beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, + destinationParent, destinationChild); + QList<TstMoveItem *> itemsToMove; + for (int i = 0; i < count; ++i) { + TstMoveItem *itm = itmSrcParent->childItems.at(sourceRow+i); + itemsToMove.append(itm); + } + for (int i = itemsToMove.size() -1; i >= 0; --i) { + TstMoveItem *itm = itemsToMove.at(i); + itm->parentItem->childItems.removeAll(itm); + itm->parentItem = itmDestParent; + itmDestParent->childItems.insert(destinationChild, itm); + } + endMoveRows(); + return true; + } + return false; + } + +private: + TstMoveItem *itemAt(const QModelIndex &index) const + { + TstMoveItem *item = nullptr; + if (index.isValid()) { + Q_ASSERT(index.model() == this); + item = static_cast<TstMoveItem *>(index.internalPointer()); + } else { + item = rootItem; + } + return item; + } + +private: + TstMoveItem *rootItem = nullptr; +}; + +void tst_QListView::moveLastRow() +{ + TstMoveModel model; + QListView view; + view.setModel(&model); + view.setRootIndex(model.index(0, 0, QModelIndex())); + view.setViewMode(QListView::IconMode); + view.show(); + + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QModelIndex sourceParent = model.index(0, 0); + QModelIndex destinationParent = model.index(1, 0); + // must not crash when paint event is processed + model.moveRow(sourceParent, 0, destinationParent, 0); + QTest::qWait(100); +} + void tst_QListView::itemAlignment() { auto item1 = new QStandardItem("111"); @@ -2542,6 +2858,7 @@ void tst_QListView::internalDragDropMove_data() | Qt::ItemIsEditable | Qt::ItemIsDragEnabled; + const QStringList unchanged = QStringList{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; const QStringList reordered = QStringList{"0", "2", "3", "4", "5", "6", "7", "8", "9", "1"}; const QStringList replaced = QStringList{"0", "2", "3", "4", "1", "6", "7", "8", "9"}; @@ -2556,7 +2873,8 @@ void tst_QListView::internalDragDropMove_data() << Qt::MoveAction << defaultFlags << modelMoves - << reordered; + // listview in IconMode doesn't change the model + << ((viewMode == QListView::IconMode && !modelMoves) ? unchanged : reordered); QTest::newRow((rowName + ", only move").constData()) << viewMode @@ -2565,7 +2883,8 @@ void tst_QListView::internalDragDropMove_data() << Qt::MoveAction << defaultFlags << modelMoves - << reordered; + // listview in IconMode doesn't change the model + << ((viewMode == QListView::IconMode && !modelMoves) ? unchanged : reordered); QTest::newRow((rowName + ", replace item").constData()) << viewMode @@ -2678,6 +2997,7 @@ void tst_QListView::internalDragDropMove() // The test relies on the global position of mouse events; make sure // the window is properly mapped on X11. QVERIFY(QTest::qWaitForWindowActive(&list)); + QVERIFY(QTest::qWaitFor([&]() { return list.m_gotValidResizeEvent; })); // execute as soon as the eventloop is running again // which is the case inside list.startDrag() QTimer::singleShot(0, [&]() @@ -2710,6 +3030,176 @@ void tst_QListView::internalDragDropMove() } } +/*! + Verify fix for QTBUG-92366 +*/ +void tst_QListView::spacingWithWordWrap_data() +{ + QTest::addColumn<bool>("scrollBarOverlap"); + + QTest::addRow("Without overlap") << false; + QTest::addRow("With overlap") << true; +} + +void tst_QListView::spacingWithWordWrap() +{ + QFETCH(bool, scrollBarOverlap); + + class MyStyle : public QProxyStyle + { + bool scrollBarOverlap; + public: + MyStyle(bool scrollBarOverlap) : scrollBarOverlap(scrollBarOverlap) {} + + int pixelMetric(PixelMetric metric, const QStyleOption *option = nullptr, + const QWidget *widget = nullptr) const override{ + switch (metric) { + case QStyle::PM_ScrollView_ScrollBarOverlap: return scrollBarOverlap; + default: + break; + } + return QProxyStyle::pixelMetric(metric, option, widget); + } + }; + + QStyle *oldStyle = QApplication::style(); + oldStyle->setParent(nullptr); + const auto resetStyle = qScopeGuard([oldStyle]{ + QApplication::setStyle(oldStyle); + }); + QApplication::setStyle(new MyStyle(scrollBarOverlap)); + + const int listViewResizeCount = 200; + QWidget window; + window.resize(300, 200); + QListView lv(&window); + + lv.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + lv.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + lv.setFlow(QListView::TopToBottom); + lv.setWordWrap(true); + lv.setSpacing(0); + lv.setGeometry(0, 0, 200, 150); + + QStandardItem *it1 = new QStandardItem("qqqqqqqqqqqqqqqqqqqqq-ttttttttttttttttt"); + QStandardItem *it2 = new QStandardItem("qqqqqqqqqqqqqqqq-tttttttttttt"); + QStandardItemModel model; + lv.setModel(&model); + model.appendRow(it1); + model.appendRow(it2); + + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + QVERIFY(!lv.verticalScrollBar()->isVisible()); + for (int i = 0; i < listViewResizeCount; ++i) { + lv.resize(lv.width() + 1, lv.height()); + QRect rectForRowOne = lv.visualRect(model.index(0, 0)); + QRect rectForRowTwo = lv.visualRect(model.index(1, 0)); + + QCOMPARE(rectForRowOne.y() + rectForRowOne.height(), rectForRowTwo.y()); + } + + lv.resize(200, 150); + const QStringList &stringList = generateList(QStringLiteral("Test_Abnormal_Spacing"), 30); + for (const QString &item_string : stringList) { + QStandardItem *item = new QStandardItem(item_string); + model.appendRow(item); + } + + // test whether the height of item is correct if the vbar is shown. + QTRY_VERIFY(lv.verticalScrollBar()->isVisible()); + for (int i = 0; i < listViewResizeCount; ++i) { + lv.resize(lv.width() + 1, lv.height()); + QRect rectForRowOne = lv.visualRect(model.index(0, 0)); + QRect rectForRowTwo = lv.visualRect(model.index(1, 0)); + + QCOMPARE(rectForRowOne.y() + rectForRowOne.height(), rectForRowTwo.y()); + } +} + +void tst_QListView::scrollOnRemove_data() +{ + QTest::addColumn<QListView::ViewMode>("viewMode"); + QTest::addColumn<QAbstractItemView::SelectionMode>("selectionMode"); + + const QMetaObject &mo = QListView::staticMetaObject; + const auto viewModeEnum = mo.enumerator(mo.indexOfEnumerator("ViewMode")); + const auto selectionModeEnum = mo.enumerator(mo.indexOfEnumerator("SelectionMode")); + for (auto viewMode : { QListView::ListMode, QListView::IconMode }) { + const char *viewModeName = viewModeEnum.valueToKey(viewMode); + for (int index = 0; index < selectionModeEnum.keyCount(); ++index) { + const auto selectionMode = QAbstractItemView::SelectionMode(selectionModeEnum.value(index)); + const char *selectionModeName = selectionModeEnum.valueToKey(selectionMode); + QTest::addRow("%s, %s", viewModeName, selectionModeName) << viewMode << selectionMode; + } + } +} + +void tst_QListView::scrollOnRemove() +{ + QFETCH(QListView::ViewMode, viewMode); + QFETCH(QAbstractItemView::SelectionMode, selectionMode); + + QPixmap pixmap; + if (viewMode == QListView::IconMode) { + pixmap = QPixmap(25, 25); + pixmap.fill(Qt::red); + } + + QStandardItemModel model; + for (int i = 0; i < 50; ++i) { + QStandardItem *item = new QStandardItem(QString::number(i)); + item->setIcon(pixmap); + model.appendRow(item); + } + + QWidget widget; + QListView view(&widget); + view.setFixedSize(100, 100); + view.setAutoScroll(true); + if (viewMode == QListView::IconMode) + view.setWrapping(true); + view.setModel(&model); + view.setSelectionMode(selectionMode); + view.setViewMode(viewMode); + + widget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&widget)); + + QCOMPARE(view.verticalScrollBar()->value(), 0); + const QModelIndex item25 = model.index(25, 0); + view.scrollTo(item25); + QTRY_VERIFY(view.verticalScrollBar()->value() > 0); // layout and scrolling are delayed + const int item25Position = view.verticalScrollBar()->value(); + // selecting a fully visible item shouldn't scroll + view.selectionModel()->setCurrentIndex(item25, QItemSelectionModel::SelectCurrent); + QTRY_COMPARE(view.verticalScrollBar()->value(), item25Position); + + // removing the selected item might scroll if another item is selected + model.removeRow(25); + + // if nothing is selected now, then the view should not have scrolled + if (!view.selectionModel()->selectedIndexes().size()) + QTRY_COMPARE(view.verticalScrollBar()->value(), item25Position); +} + +void tst_QListView::wordWrapNullIcon() +{ + QListView listView; + listView.setViewMode(QListView::IconMode); + listView.setWrapping(true); + listView.setWordWrap(true); + listView.setFixedSize(QSize(100, 500)); + + QStandardItemModel model; + QStandardItem *item = new QStandardItem(QIcon(), "This is a long text for word wrapping Item_"); + model.appendRow(item); + listView.setModel(&model); + + listView.indexAt(QPoint(0, 0)); +} + QTEST_MAIN(tst_QListView) #include "tst_qlistview.moc" diff --git a/tests/auto/widgets/itemviews/qlistwidget/CMakeLists.txt b/tests/auto/widgets/itemviews/qlistwidget/CMakeLists.txt index 2c02f57324..fea17e1d75 100644 --- a/tests/auto/widgets/itemviews/qlistwidget/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qlistwidget/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qlistwidget.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qlistwidget Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qlistwidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qlistwidget SOURCES tst_qlistwidget.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/itemviews/qlistwidget/tst_qlistwidget.cpp b/tests/auto/widgets/itemviews/qlistwidget/tst_qlistwidget.cpp index 48b0cb8d6b..14a6cee0d9 100644 --- a/tests/auto/widgets/itemviews/qlistwidget/tst_qlistwidget.cpp +++ b/tests/auto/widgets/itemviews/qlistwidget/tst_qlistwidget.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QCompleter> #include <QHBoxLayout> @@ -33,8 +8,11 @@ #include <QSignalSpy> #include <QStyledItemDelegate> #include <QTest> +#include <QLabel> #include <private/qlistwidget_p.h> +#include <QtWidgets/private/qapplication_p.h> + using IntList = QList<int>; class tst_QListWidget : public QObject @@ -97,6 +75,8 @@ private slots: void sortItems(); void sortHiddenItems(); void sortHiddenItems_data(); + void sortCheckStability_data(); + void sortCheckStability(); void closeEditor(); void setData_data(); void setData(); @@ -118,6 +98,8 @@ private slots: void QTBUG14363_completerWithAnyKeyPressedEditTriggers(); void mimeData(); void QTBUG50891_ensureSelectionModelSignalConnectionsAreSet(); + void createPersistentOnLayoutAboutToBeChanged(); + void createPersistentOnLayoutAboutToBeChangedAutoSort(); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) void clearItemData(); #endif @@ -126,6 +108,7 @@ private slots: void moveRows(); void moveRowsInvalid_data(); void moveRowsInvalid(); + void noopDragDrop(); protected slots: void rowsAboutToBeInserted(const QModelIndex &parent, int first, int last) @@ -367,7 +350,7 @@ void tst_QListWidget::addItems() QString::number(testWidget->count() + 3), label}; testWidget->addItems(stringList); - QCOMPARE(testWidget->count(), count + stringList.count()); + QCOMPARE(testWidget->count(), count + stringList.size()); QCOMPARE(testWidget->item(testWidget->count()-1)->text(), label); } @@ -379,30 +362,30 @@ void tst_QListWidget::openPersistentEditor() QListWidgetItem *item = new QListWidgetItem(QString::number(testWidget->count())); testWidget->openPersistentEditor(item); - int childCount = testWidget->viewport()->children().count(); + int childCount = testWidget->viewport()->children().size(); testWidget->addItem(item); testWidget->openPersistentEditor(item); - QCOMPARE(childCount + 1, testWidget->viewport()->children().count()); + QCOMPARE(childCount + 1, testWidget->viewport()->children().size()); } void tst_QListWidget::closePersistentEditor() { // Boundary checking - int childCount = testWidget->viewport()->children().count(); + int childCount = testWidget->viewport()->children().size(); testWidget->closePersistentEditor(nullptr); QListWidgetItem *item = new QListWidgetItem(QString::number(testWidget->count())); testWidget->closePersistentEditor(item); - QCOMPARE(childCount, testWidget->viewport()->children().count()); + QCOMPARE(childCount, testWidget->viewport()->children().size()); // Create something testWidget->addItem(item); testWidget->openPersistentEditor(item); // actual test - childCount = testWidget->viewport()->children().count(); + childCount = testWidget->viewport()->children().size(); testWidget->closePersistentEditor(item); QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCOMPARE(testWidget->viewport()->children().count(), childCount - 1); + QCOMPARE(testWidget->viewport()->children().size(), childCount - 1); } void tst_QListWidget::setItemHidden() @@ -552,7 +535,7 @@ void tst_QListWidget::editItem() item->setFlags(item->flags() | Qt::ItemIsEditable); testWidget->addItem(item); - int childCount = testWidget->viewport()->children().count(); + int childCount = testWidget->viewport()->children().size(); QWidget *existsAlready = testWidget->indexWidget(testWidget->model()->index(testWidget->row(item), 0)); testWidget->editItem(item); Qt::ItemFlags flags = item->flags(); @@ -560,7 +543,7 @@ void tst_QListWidget::editItem() // There doesn't seem to be a way to detect if the item has already been edited... if (!existsAlready && flags & Qt::ItemIsEditable && flags & Qt::ItemIsEnabled) { QList<QObject *> children = testWidget->viewport()->children(); - QVERIFY(children.count() > childCount); + QVERIFY(children.size() > childCount); bool found = false; for (int i = 0; i < children.size(); ++i) { if (children.at(i)->inherits("QExpandingLineEdit")) @@ -568,7 +551,7 @@ void tst_QListWidget::editItem() } QVERIFY(found); } else { - QCOMPARE(testWidget->viewport()->children().count(), childCount); + QCOMPARE(testWidget->viewport()->children().size(), childCount); } } @@ -577,12 +560,12 @@ void tst_QListWidget::findItems() // This really just tests that the items that are returned are converted from index's to items correctly. // Boundary checking - QCOMPARE(testWidget->findItems("GirlsCanWearJeansAndCutTheirHairShort", Qt::MatchExactly).count(), 0); + QCOMPARE(testWidget->findItems("GirlsCanWearJeansAndCutTheirHairShort", Qt::MatchExactly).size(), 0); populate(); for (int i = 0; i < testWidget->count(); ++i) - QCOMPARE(testWidget->findItems(testWidget->item(i)->text(), Qt::MatchExactly).count(), 1); + QCOMPARE(testWidget->findItems(testWidget->item(i)->text(), Qt::MatchExactly).size(), 1); } @@ -597,8 +580,8 @@ void tst_QListWidget::insertItem_data() QTest::newRow("Insert less then 0") << initialItems << -1 << "inserted" << 0; QTest::newRow("Insert at 0") << initialItems << 0 << "inserted" << 0; - QTest::newRow("Insert beyond count") << initialItems << initialItems.count()+1 << "inserted" << initialItems.count(); - QTest::newRow("Insert at count") << initialItems << initialItems.count() << "inserted" << initialItems.count(); + QTest::newRow("Insert beyond count") << initialItems << initialItems.size()+1 << "inserted" << initialItems.size(); + QTest::newRow("Insert at count") << initialItems << initialItems.size() << "inserted" << initialItems.size(); QTest::newRow("Insert in the middle") << initialItems << 1 << "inserted" << 1; } @@ -610,7 +593,7 @@ void tst_QListWidget::insertItem() QFETCH(int, expectedIndex); testWidget->insertItems(0, initialItems); - QCOMPARE(testWidget->count(), initialItems.count()); + QCOMPARE(testWidget->count(), initialItems.size()); testWidget->insertItem(insertIndex, itemLabel); @@ -619,7 +602,7 @@ void tst_QListWidget::insertItem() QCOMPARE(rcFirst[RowsInserted], expectedIndex); QCOMPARE(rcLast[RowsInserted], expectedIndex); - QCOMPARE(testWidget->count(), initialItems.count() + 1); + QCOMPARE(testWidget->count(), initialItems.size() + 1); QCOMPARE(testWidget->item(expectedIndex)->text(), itemLabel); } @@ -692,8 +675,8 @@ void tst_QListWidget::insertItems() for (int i = 0; i < testWidget->count(); ++i) QCOMPARE(testWidget->item(i)->listWidget(), testWidget); - QCOMPARE(itemChangedSpy.count(), 0); - QCOMPARE(dataChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); + QCOMPARE(dataChangedSpy.size(), 0); } void tst_QListWidget::itemAssignment() @@ -848,7 +831,7 @@ void tst_QListWidget::selectedItems() QListWidgetItem *item = testWidget->item(i); item->setSelected(true); QVERIFY(item->isSelected()); - QCOMPARE(testWidget->selectedItems().count(), 1); + QCOMPARE(testWidget->selectedItems().size(), 1); } //let's clear the selection testWidget->clearSelection(); @@ -866,7 +849,7 @@ void tst_QListWidget::selectedItems() // check that the correct number of items and the expected items are there QList<QListWidgetItem *> selectedItems = testWidget->selectedItems(); - QCOMPARE(selectedItems.count(), expectedRows.count()); + QCOMPARE(selectedItems.size(), expectedRows.size()); for (int row : expectedRows) QVERIFY(selectedItems.contains(testWidget->item(row))); @@ -962,20 +945,20 @@ void tst_QListWidget::moveItemsPriv() else QCOMPARE(testWidget->item(dstRow)->text(), QString::number(srcRow)); - QCOMPARE(beginMoveSpy.count(), 1); + QCOMPARE(beginMoveSpy.size(), 1); const QList<QVariant> &beginMoveArgs = beginMoveSpy.takeFirst(); QCOMPARE(beginMoveArgs.at(1).toInt(), srcRow); QCOMPARE(beginMoveArgs.at(2).toInt(), srcRow); QCOMPARE(beginMoveArgs.at(4).toInt(), dstRow); - QCOMPARE(movedSpy.count(), 1); + QCOMPARE(movedSpy.size(), 1); const QList<QVariant> &movedArgs = movedSpy.takeFirst(); QCOMPARE(movedArgs.at(1).toInt(), srcRow); QCOMPARE(movedArgs.at(2).toInt(), srcRow); QCOMPARE(movedArgs.at(4).toInt(), dstRow); } else { - QCOMPARE(beginMoveSpy.count(), 0); - QCOMPARE(movedSpy.count(), 0); + QCOMPARE(beginMoveSpy.size(), 0); + QCOMPARE(movedSpy.size(), 0); } } @@ -1068,7 +1051,7 @@ void tst_QListWidget::sortItems() testWidget->sortItems(order); - QCOMPARE(testWidget->count(), expectedList.count()); + QCOMPARE(testWidget->count(), expectedList.size()); for (int i = 0; i < testWidget->count(); ++i) QCOMPARE(testWidget->item(i)->text(), expectedList.at(i).toString()); @@ -1138,7 +1121,7 @@ void tst_QListWidget::sortHiddenItems() tw->setSortingEnabled(true); tw->sortItems(order); - QCOMPARE(tw->count(), expectedList.count()); + QCOMPARE(tw->count(), expectedList.size()); for (int i = 0; i < tw->count(); ++i) { QCOMPARE(tw->item(i)->text(), expectedList.at(i)); QCOMPARE(tw->item(i)->isHidden(), !expectedVisibility.at(i)); @@ -1150,6 +1133,64 @@ void tst_QListWidget::sortHiddenItems() delete tw; } +void tst_QListWidget::sortCheckStability_data() { + QTest::addColumn<Qt::SortOrder>("order"); + QTest::addColumn<QVariantList>("initialList"); + QTest::addColumn<QVariantList>("expectedList"); + + QTest::newRow("ascending strings") + << Qt::AscendingOrder + << QVariantList{ QString("a"), QString("b"), QString("b"), QString("a")} + << QVariantList{ QString("a"), QString("a"), QString("b"), QString("b")}; + + QTest::newRow("descending strings") + << Qt::DescendingOrder + << QVariantList{ QString("a"), QString("b"), QString("b"), QString("a")} + << QVariantList{ QString("b"), QString("b"), QString("a"), QString("a")}; + + QTest::newRow("ascending numbers") + << Qt::AscendingOrder + << QVariantList{ 1, 2, 2, 1} + << QVariantList{ 1, 1, 2, 2}; + + QTest::newRow("descending numbers") + << Qt::DescendingOrder + << QVariantList{ 1, 2, 2, 1} + << QVariantList{ 2, 2, 1, 1}; +} + +void tst_QListWidget::sortCheckStability() { + QFETCH(Qt::SortOrder, order); + QFETCH(const QVariantList, initialList); + QFETCH(const QVariantList, expectedList); + + for (const QVariant &data : initialList) { + QListWidgetItem *item = new QListWidgetItem(testWidget); + item->setData(Qt::DisplayRole, data); + } + + QAbstractItemModel *model = testWidget->model(); + QList<QPersistentModelIndex> persistent; + for (int j = 0; j < model->rowCount(QModelIndex()); ++j) + persistent << model->index(j, 0, QModelIndex()); + + testWidget->sortItems(order); + + QCOMPARE(testWidget->count(), expectedList.size()); + for (int i = 0; i < testWidget->count(); ++i) + QCOMPARE(testWidget->item(i)->text(), expectedList.at(i).toString()); + + QVector<QListWidgetItem*> itemOrder(testWidget->count()); + for (int i = 0; i < testWidget->count(); ++i) + itemOrder[i] = testWidget->item(i); + + qobject_cast<QListModel*>(testWidget->model())->ensureSorted(0, order, 1, 1); + testWidget->sortItems(order); + + for (int i = 0; i < testWidget->count(); ++i) + QCOMPARE(itemOrder[i],testWidget->item(i)); +} + class TestListWidget : public QListWidget { Q_OBJECT @@ -1235,17 +1276,17 @@ void tst_QListWidget::setData() QFETCH(QVariantList, values); QFETCH(int, expectedSignalCount); - QCOMPARE(roles.count(), values.count()); + QCOMPARE(roles.size(), values.size()); for (int manipulateModel = 0; manipulateModel < 2; ++manipulateModel) { testWidget->clear(); testWidget->insertItems(0, initialItems); - QCOMPARE(testWidget->count(), initialItems.count()); + QCOMPARE(testWidget->count(), initialItems.size()); QSignalSpy itemChanged(testWidget, &QListWidget::itemChanged); QSignalSpy dataChanged(testWidget->model(), &QAbstractItemModel::dataChanged); - for (int i = 0; i < roles.count(); ++i) { + for (int i = 0; i < roles.size(); ++i) { if (manipulateModel) testWidget->model()->setData( testWidget->model()->index(itemIndex, 0, testWidget->rootIndex()), @@ -1256,12 +1297,12 @@ void tst_QListWidget::setData() } // make sure the data is actually set - for (int i = 0; i < roles.count(); ++i) + for (int i = 0; i < roles.size(); ++i) QCOMPARE(testWidget->item(itemIndex)->data(roles.at(i)), values.at(i)); // make sure we get the right number of emits - QCOMPARE(itemChanged.count(), expectedSignalCount); - QCOMPARE(dataChanged.count(), expectedSignalCount); + QCOMPARE(itemChanged.size(), expectedSignalCount); + QCOMPARE(dataChanged.size(), expectedSignalCount); } } @@ -1403,11 +1444,11 @@ void tst_QListWidget::insertItemsWithSorting() w.addItem(str); break; } - QCOMPARE(w.count(), expectedItems.count()); + QCOMPARE(w.count(), expectedItems.size()); for (int i = 0; i < w.count(); ++i) QCOMPARE(w.item(i)->text(), expectedItems.at(i)); - for (int k = 0; k < persistent.count(); ++k) + for (int k = 0; k < persistent.size(); ++k) QCOMPARE(persistent.at(k).row(), expectedRows.at(k)); } } @@ -1503,7 +1544,7 @@ void tst_QListWidget::itemData() QCOMPARE(widget.currentRoles, QList<int> { Qt::UserRole + i }); } QMap<int, QVariant> flags = widget.model()->itemData(widget.model()->index(0, 0)); - QCOMPARE(flags.count(), 6); + QCOMPARE(flags.size(), 6); for (int i = 0; i < 4; ++i) QCOMPARE(flags[Qt::UserRole + i].toString(), QString::number(i + 1)); @@ -1547,19 +1588,19 @@ void tst_QListWidget::changeDataWithSorting() QListWidgetItem *item = w.item(itemIndex); item->setText(newValue); - for (int i = 0; i < expectedItems.count(); ++i) { + for (int i = 0; i < expectedItems.size(); ++i) { QCOMPARE(w.item(i)->text(), expectedItems.at(i)); - for (int j = 0; j < persistent.count(); ++j) { + for (int j = 0; j < persistent.size(); ++j) { if (persistent.at(j).row() == i) // the same toplevel row QCOMPARE(persistent.at(j).internalPointer(), static_cast<void *>(w.item(i))); } } - for (int k = 0; k < persistent.count(); ++k) + for (int k = 0; k < persistent.size(); ++k) QCOMPARE(persistent.at(k).row(), expectedRows.at(k)); - QCOMPARE(dataChangedSpy.count(), 1); - QCOMPARE(layoutChangedSpy.count(), reorderingExpected ? 1 : 0); + QCOMPARE(dataChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), reorderingExpected ? 1 : 0); } void tst_QListWidget::itemWidget() @@ -1638,7 +1679,7 @@ void tst_QListWidget::insertUnchanged() QListWidget w; QSignalSpy itemChangedSpy(&w, &QListWidget::itemChanged); QListWidgetItem item("foo", &w); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); } void tst_QListWidget::setSortingEnabled() @@ -1729,12 +1770,12 @@ void tst_QListWidget::QTBUG8086_currentItemChangedOnClick() QVERIFY(QTest::qWaitForWindowExposed(&win)); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QTest::mouseClick(list.viewport(), Qt::LeftButton, {}, list.visualItemRect(list.item(2)).center()); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } @@ -1769,7 +1810,6 @@ void tst_QListWidget::QTBUG14363_completerWithAnyKeyPressedEditTriggers() new QListWidgetItem(QLatin1String("completer"), &listWidget); listWidget.show(); listWidget.setCurrentItem(item); - QApplication::setActiveWindow(&listWidget); QVERIFY(QTest::qWaitForWindowActive(&listWidget)); listWidget.setFocus(); QCOMPARE(QApplication::focusWidget(), &listWidget); @@ -1833,15 +1873,133 @@ void tst_QListWidget::QTBUG50891_ensureSelectionModelSignalConnectionsAreSet() QSignalSpy currentItemChangedSpy(&list, &QListWidget::currentItemChanged); QSignalSpy itemSelectionChangedSpy(&list, &QListWidget::itemSelectionChanged); - QCOMPARE(currentItemChangedSpy.count(), 0); - QCOMPARE(itemSelectionChangedSpy.count(), 0); + QCOMPARE(currentItemChangedSpy.size(), 0); + QCOMPARE(itemSelectionChangedSpy.size(), 0); QTest::mouseClick(list.viewport(), Qt::LeftButton, {}, list.visualItemRect(list.item(2)).center()); - QCOMPARE(currentItemChangedSpy.count(), 1); - QCOMPARE(itemSelectionChangedSpy.count(), 1); + QCOMPARE(currentItemChangedSpy.size(), 1); + QCOMPARE(itemSelectionChangedSpy.size(), 1); + +} + +void tst_QListWidget::createPersistentOnLayoutAboutToBeChanged() // QTBUG-93466 +{ + QListWidget widget; + QCOMPARE(widget.model()->columnCount(), 1); + widget.model()->insertRows(0, 3); + for (int row = 0; row < 3; ++row) + widget.model()->setData(widget.model()->index(row, 0), row); + QList<QPersistentModelIndex> idxList; + QSignalSpy layoutAboutToBeChangedSpy(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged); + QSignalSpy layoutChangedSpy(widget.model(), &QAbstractItemModel::layoutChanged); + connect(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged, this, [&idxList, &widget](){ + idxList.clear(); + for (int row = 0; row < 3; ++row) + idxList << QPersistentModelIndex(widget.model()->index(row, 0)); + }); + connect(widget.model(), &QAbstractItemModel::layoutChanged, this, [&idxList](){ + QCOMPARE(idxList.size(), 3); + QCOMPARE(idxList.at(0).row(), 1); + QCOMPARE(idxList.at(0).column(), 0); + QCOMPARE(idxList.at(0).data().toInt(), 0); + QCOMPARE(idxList.at(1).row(), 0); + QCOMPARE(idxList.at(1).column(), 0); + QCOMPARE(idxList.at(1).data().toInt(), -1); + QCOMPARE(idxList.at(2).row(), 2); + QCOMPARE(idxList.at(2).column(), 0); + QCOMPARE(idxList.at(2).data().toInt(), 2); + }); + widget.model()->setData(widget.model()->index(1, 0), -1); + widget.model()->sort(0); + QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), 1); +} + +void tst_QListWidget::createPersistentOnLayoutAboutToBeChangedAutoSort() // QTBUG-93466 +{ + QListWidget widget; + QCOMPARE(widget.model()->columnCount(), 1); + widget.model()->insertRows(0, 3); + for (int row = 0; row < 3; ++row) + widget.model()->setData(widget.model()->index(row, 0), row); + widget.setSortingEnabled(true); + QList<QPersistentModelIndex> idxList; + QSignalSpy layoutAboutToBeChangedSpy(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged); + QSignalSpy layoutChangedSpy(widget.model(), &QAbstractItemModel::layoutChanged); + connect(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged, this, [&idxList, &widget](){ + idxList.clear(); + for (int row = 0; row < 3; ++row) + idxList << QPersistentModelIndex(widget.model()->index(row, 0)); + }); + connect(widget.model(), &QAbstractItemModel::layoutChanged, this, [&idxList](){ + QCOMPARE(idxList.size(), 3); + QCOMPARE(idxList.at(0).row(), 1); + QCOMPARE(idxList.at(0).column(), 0); + QCOMPARE(idxList.at(0).data().toInt(), 0); + QCOMPARE(idxList.at(1).row(), 0); + QCOMPARE(idxList.at(1).column(), 0); + QCOMPARE(idxList.at(1).data().toInt(), -1); + QCOMPARE(idxList.at(2).row(), 2); + QCOMPARE(idxList.at(2).column(), 0); + QCOMPARE(idxList.at(2).data().toInt(), 2); + }); + widget.model()->setData(widget.model()->index(1, 0), -1); + QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), 1); +} + +// Test that dropping an item on or beneath itself remains a no-op +void tst_QListWidget::noopDragDrop() // QTBUG-100128 +{ + QListWidget listWidget; + QList<QListWidgetItem *> items; + for (int i = 0; i < 5; ++i) { + const QString number = QString::number(i); + QListWidgetItem *item = new QListWidgetItem(&listWidget); + item->setData(Qt::UserRole, number); + QLabel *label = new QLabel(number); + listWidget.setItemWidget(item, label); + items.append(item); + } + + listWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&listWidget)); + + const QRect &lastItemRect = listWidget.visualItemRect(items.at(4)); + const QPoint &dragStart = lastItemRect.center(); + const QPoint &dropPointNirvana = lastItemRect.center() + QPoint(20, 2 * lastItemRect.height()); + + // Implement check as a macro (not a method) to safely determine the error location. + // The macro checks that item data and item widget remain unchanged when drag&drop are executed. + // In order to verify that the assets do *not* change, we can't use QTRY*: These macros would + // spin the event loop only once, while 3/4 mouse events need to get processed. + // That's why we spin the event loop 13 times, to make sure other unexpected or pending events + // get processed. +#define CHECK_ITEM {\ + const QString number = QString::number(4);\ + for (int i = 0; i < 13; ++i)\ + QApplication::processEvents();\ + QLabel *label = qobject_cast<QLabel *>(listWidget.itemWidget(items.at(4)));\ + QVERIFY(label);\ + QCOMPARE(label->text(), number);\ + const QString &data = items.at(4)->data(Qt::UserRole).toString();\ + QCOMPARE(data, number);\ + } + // Test dropping last item beneath itself + QTest::mousePress(&listWidget, Qt::LeftButton, Qt::KeyboardModifiers(), dragStart); + QTest::mouseMove(&listWidget, dropPointNirvana); + QTest::mouseRelease(&listWidget, Qt::LeftButton); + CHECK_ITEM; + + // Test dropping last item on itself + QTest::mousePress(&listWidget, Qt::LeftButton, Qt::KeyboardModifiers(), dragStart); + QTest::mouseMove(&listWidget, dropPointNirvana); + QTest::mouseMove(&listWidget, dragStart); + QTest::mouseRelease(&listWidget, Qt::LeftButton); + CHECK_ITEM; } #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) diff --git a/tests/auto/widgets/itemviews/qtableview/BLACKLIST b/tests/auto/widgets/itemviews/qtableview/BLACKLIST index c8fc59bc46..ff870915be 100644 --- a/tests/auto/widgets/itemviews/qtableview/BLACKLIST +++ b/tests/auto/widgets/itemviews/qtableview/BLACKLIST @@ -3,10 +3,3 @@ osx [mouseWheel:scroll down per pixel] macos -# QTBUG-87407 -[rowViewportPosition] -android -[columnViewportPosition] -android -[task191545_dragSelectRows] -android diff --git a/tests/auto/widgets/itemviews/qtableview/CMakeLists.txt b/tests/auto/widgets/itemviews/qtableview/CMakeLists.txt index 23e6a01f7d..af78c0f5d3 100644 --- a/tests/auto/widgets/itemviews/qtableview/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qtableview/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qtableview.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtableview Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtableview LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qtableview SOURCES tst_qtableview.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp b/tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp index 92bc30380a..681be2491d 100644 --- a/tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp +++ b/tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QIdentityProxyModel> #include <QLabel> @@ -77,6 +52,13 @@ public: : QAbstractTableModel(parent), row_count(rows), column_count(columns) {} + void insertRows(int rows) + { + beginInsertRows(QModelIndex(), row_count, row_count + rows - 1); + row_count += rows; + endInsertRows(); + } + int rowCount(const QModelIndex& = QModelIndex()) const override { return row_count; @@ -409,6 +391,7 @@ private slots: void resizeToContents(); void resizeToContentsSpans(); + void resizeToContentsEarly(); void tabFocus(); void bigModel(); @@ -437,6 +420,11 @@ private slots: void taskQTBUG_30653_doItemsLayout(); void taskQTBUG_50171_selectRowAfterSwapColumns(); void deselectRow(); + void selectRowsAndCells(); + void selectColumnsAndCells(); + void selectWithHeader_data(); + void selectWithHeader(); + void resetDefaultSectionSize(); #if QT_CONFIG(wheelevent) void mouseWheel_data(); @@ -450,6 +438,7 @@ private slots: void viewOptions(); void taskQTBUG_7232_AllowUserToControlSingleStep(); + void rowsInVerticalHeader(); #if QT_CONFIG(textmarkdownwriter) void markdownWriter(); @@ -524,7 +513,7 @@ void tst_QTableView::emptyModel() QSignalSpy spy(&model, &QtTestTableModel::invalidIndexEncountered); view.setModel(&model); view.show(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } void tst_QTableView::removeRows_data() @@ -549,10 +538,10 @@ void tst_QTableView::removeRows() view.show(); model.removeLastRow(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); model.removeAllRows(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } void tst_QTableView::removeColumns_data() @@ -577,10 +566,10 @@ void tst_QTableView::removeColumns() view.show(); model.removeLastColumn(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); model.removeAllColumns(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } void tst_QTableView::keyboardNavigation_data() @@ -621,7 +610,6 @@ void tst_QTableView::keyboardNavigation() view.setCurrentIndex(index); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); int row = rowCount - 1; @@ -1618,7 +1606,7 @@ void tst_QTableView::selection() view.setSelection(QRect(x, y, width, height), command); - QCOMPARE(view.selectedIndexes().count(), selectedCount); + QCOMPARE(view.selectedIndexes().size(), selectedCount); } void tst_QTableView::selectRow_data() @@ -1731,12 +1719,12 @@ void tst_QTableView::selectRow() view.setSelectionMode(mode); view.setSelectionBehavior(behavior); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 0); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 0); view.selectRow(row); //test we have 10 items selected - QCOMPARE(view.selectionModel()->selectedIndexes().count(), selectedItems); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), selectedItems); //test that all 10 items are in the same row for (int i = 0; selectedItems > 0 && i < rowCount; ++i) QCOMPARE(view.selectionModel()->selectedIndexes().at(i).row(), row); @@ -1852,11 +1840,11 @@ void tst_QTableView::selectColumn() view.setSelectionMode(mode); view.setSelectionBehavior(behavior); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 0); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 0); view.selectColumn(column); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), selectedItems); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), selectedItems); for (int i = 0; selectedItems > 0 && i < columnCount; ++i) QCOMPARE(view.selectionModel()->selectedIndexes().at(i).column(), column); } @@ -2001,22 +1989,22 @@ void tst_QTableView::selectall() // try slot first view.clearSelection(); - QCOMPARE(view.selectedIndexes().count(), 0); + QCOMPARE(view.selectedIndexes().size(), 0); view.selectAll(); - QCOMPARE(view.selectedIndexes().count(), selectedCount); + QCOMPARE(view.selectedIndexes().size(), selectedCount); // try by key sequence view.clearSelection(); - QCOMPARE(view.selectedIndexes().count(), 0); + QCOMPARE(view.selectedIndexes().size(), 0); QTest__keySequence(&view, QKeySequence(QKeySequence::SelectAll)); - QCOMPARE(view.selectedIndexes().count(), selectedCount); + QCOMPARE(view.selectedIndexes().size(), selectedCount); // check again with no selection mode view.clearSelection(); view.setSelectionMode(QAbstractItemView::NoSelection); - QCOMPARE(view.selectedIndexes().count(), 0); + QCOMPARE(view.selectedIndexes().size(), 0); QTest__keySequence(&view, QKeySequence(QKeySequence::SelectAll)); - QCOMPARE(view.selectedIndexes().count(), 0); + QCOMPARE(view.selectedIndexes().size(), 0); } #endif // QT_CONFIG(shortcut) @@ -2209,7 +2197,7 @@ void tst_QTableView::resizeRowsToContents() QSignalSpy resizedSpy(view.verticalHeader(), &QHeaderView::sectionResized); view.resizeRowsToContents(); - QCOMPARE(resizedSpy.count(), model.rowCount()); + QCOMPARE(resizedSpy.size(), model.rowCount()); for (int r = 0; r < model.rowCount(); ++r) QCOMPARE(view.rowHeight(r), rowHeight); } @@ -2255,7 +2243,7 @@ void tst_QTableView::resizeColumnsToContents() QSignalSpy resizedSpy(view.horizontalHeader(), &QHeaderView::sectionResized); view.resizeColumnsToContents(); - QCOMPARE(resizedSpy.count(), model.columnCount()); + QCOMPARE(resizedSpy.size(), model.columnCount()); for (int c = 0; c < model.columnCount(); ++c) QCOMPARE(view.columnWidth(c), columnWidth); } @@ -2270,46 +2258,46 @@ void tst_QTableView::rowViewportPosition_data() QTest::addColumn<int>("rowViewportPosition"); QTest::newRow("row 0, scroll per item 0") - << 10 << 40 << 0 << QAbstractItemView::ScrollPerItem << 0 << 0; + << 100 << 40 << 0 << QAbstractItemView::ScrollPerItem << 0 << 0; QTest::newRow("row 1, scroll per item, 0") - << 10 << 40 << 1 << QAbstractItemView::ScrollPerItem << 0 << 1 * 40; + << 100 << 40 << 1 << QAbstractItemView::ScrollPerItem << 0 << 1 * 40; QTest::newRow("row 1, scroll per item, 1") - << 10 << 40 << 1 << QAbstractItemView::ScrollPerItem << 1 << 0; + << 100 << 40 << 1 << QAbstractItemView::ScrollPerItem << 1 << 0; QTest::newRow("row 5, scroll per item, 0") - << 10 << 40 << 5 << QAbstractItemView::ScrollPerItem << 0 << 5 * 40; + << 100 << 40 << 5 << QAbstractItemView::ScrollPerItem << 0 << 5 * 40; QTest::newRow("row 5, scroll per item, 5") - << 10 << 40 << 5 << QAbstractItemView::ScrollPerItem << 5 << 0; + << 100 << 40 << 5 << QAbstractItemView::ScrollPerItem << 5 << 0; QTest::newRow("row 9, scroll per item, 0") - << 10 << 40 << 9 << QAbstractItemView::ScrollPerItem << 0 << 9 * 40; + << 100 << 40 << 9 << QAbstractItemView::ScrollPerItem << 0 << 9 * 40; QTest::newRow("row 9, scroll per item, 5") - << 10 << 40 << 9 << QAbstractItemView::ScrollPerItem << 5 << 4 * 40; + << 100 << 40 << 9 << QAbstractItemView::ScrollPerItem << 5 << 4 * 40; QTest::newRow("row 0, scroll per pixel 0") - << 10 << 40 << 0 << QAbstractItemView::ScrollPerPixel << 0 << 0; + << 100 << 40 << 0 << QAbstractItemView::ScrollPerPixel << 0 << 0; QTest::newRow("row 1, scroll per pixel, 0") - << 10 << 40 << 1 << QAbstractItemView::ScrollPerPixel << 0 << 1 * 40; + << 100 << 40 << 1 << QAbstractItemView::ScrollPerPixel << 0 << 1 * 40; QTest::newRow("row 1, scroll per pixel, 1") - << 10 << 40 << 1 << QAbstractItemView::ScrollPerPixel << 1 * 40 << 0; + << 100 << 40 << 1 << QAbstractItemView::ScrollPerPixel << 1 * 40 << 0; QTest::newRow("row 5, scroll per pixel, 0") - << 10 << 40 << 5 << QAbstractItemView::ScrollPerPixel << 0 << 5 * 40; + << 100 << 40 << 5 << QAbstractItemView::ScrollPerPixel << 0 << 5 * 40; QTest::newRow("row 5, scroll per pixel, 5") - << 10 << 40 << 5 << QAbstractItemView::ScrollPerPixel << 5 * 40 << 0; + << 100 << 40 << 5 << QAbstractItemView::ScrollPerPixel << 5 * 40 << 0; QTest::newRow("row 9, scroll per pixel, 0") - << 10 << 40 << 9 << QAbstractItemView::ScrollPerPixel << 0 << 9 * 40; + << 100 << 40 << 9 << QAbstractItemView::ScrollPerPixel << 0 << 9 * 40; QTest::newRow("row 9, scroll per pixel, 5") - << 10 << 40 << 9 << QAbstractItemView::ScrollPerPixel << 5 * 40 << 4 * 40; + << 100 << 40 << 9 << QAbstractItemView::ScrollPerPixel << 5 * 40 << 4 * 40; } void tst_QTableView::rowViewportPosition() @@ -2369,7 +2357,7 @@ void tst_QTableView::rowAt() for (int r = 0; r < rowCount; ++r) view.setRowHeight(r, rowHeight); - for (int i = 0; i < hiddenRows.count(); ++i) + for (int i = 0; i < hiddenRows.size(); ++i) view.hideRow(hiddenRows.at(i)); QCOMPARE(view.rowAt(coordinate), row); @@ -2433,46 +2421,46 @@ void tst_QTableView::columnViewportPosition_data() QTest::addColumn<int>("columnViewportPosition"); QTest::newRow("column 0, scroll per item 0") - << 10 << 40 << 0 << QAbstractItemView::ScrollPerItem << 0 << 0; + << 100 << 40 << 0 << QAbstractItemView::ScrollPerItem << 0 << 0; QTest::newRow("column 1, scroll per item, 0") - << 10 << 40 << 1 << QAbstractItemView::ScrollPerItem << 0 << 1 * 40; + << 100 << 40 << 1 << QAbstractItemView::ScrollPerItem << 0 << 1 * 40; QTest::newRow("column 1, scroll per item, 1") - << 10 << 40 << 1 << QAbstractItemView::ScrollPerItem << 1 << 0; + << 100 << 40 << 1 << QAbstractItemView::ScrollPerItem << 1 << 0; QTest::newRow("column 5, scroll per item, 0") - << 10 << 40 << 5 << QAbstractItemView::ScrollPerItem << 0 << 5 * 40; + << 100 << 40 << 5 << QAbstractItemView::ScrollPerItem << 0 << 5 * 40; QTest::newRow("column 5, scroll per item, 5") - << 10 << 40 << 5 << QAbstractItemView::ScrollPerItem << 5 << 0; + << 100 << 40 << 5 << QAbstractItemView::ScrollPerItem << 5 << 0; QTest::newRow("column 9, scroll per item, 0") - << 10 << 40 << 9 << QAbstractItemView::ScrollPerItem << 0 << 9 * 40; + << 100 << 40 << 9 << QAbstractItemView::ScrollPerItem << 0 << 9 * 40; QTest::newRow("column 9, scroll per item, 5") - << 10 << 40 << 9 << QAbstractItemView::ScrollPerItem << 5 << 4 * 40; + << 100 << 40 << 9 << QAbstractItemView::ScrollPerItem << 5 << 4 * 40; QTest::newRow("column 0, scroll per pixel 0") - << 10 << 40 << 0 << QAbstractItemView::ScrollPerPixel << 0 << 0; + << 100 << 40 << 0 << QAbstractItemView::ScrollPerPixel << 0 << 0; QTest::newRow("column 1, scroll per pixel 0") - << 10 << 40 << 1 << QAbstractItemView::ScrollPerPixel << 0 << 1 * 40; + << 100 << 40 << 1 << QAbstractItemView::ScrollPerPixel << 0 << 1 * 40; QTest::newRow("column 1, scroll per pixel 1") - << 10 << 40 << 1 << QAbstractItemView::ScrollPerPixel << 1 * 40 << 0; + << 100 << 40 << 1 << QAbstractItemView::ScrollPerPixel << 1 * 40 << 0; QTest::newRow("column 5, scroll per pixel 0") - << 10 << 40 << 5 << QAbstractItemView::ScrollPerPixel << 0 << 5 * 40; + << 100 << 40 << 5 << QAbstractItemView::ScrollPerPixel << 0 << 5 * 40; QTest::newRow("column 5, scroll per pixel 5") - << 10 << 40 << 5 << QAbstractItemView::ScrollPerPixel << 5 * 40 << 0; + << 100 << 40 << 5 << QAbstractItemView::ScrollPerPixel << 5 * 40 << 0; QTest::newRow("column 9, scroll per pixel 0") - << 10 << 40 << 9 << QAbstractItemView::ScrollPerPixel << 0 << 9 * 40; + << 100 << 40 << 9 << QAbstractItemView::ScrollPerPixel << 0 << 9 * 40; QTest::newRow("column 9, scroll per pixel 5") - << 10 << 40 << 9 << QAbstractItemView::ScrollPerPixel << 5 * 40 << 4 * 40; + << 100 << 40 << 9 << QAbstractItemView::ScrollPerPixel << 5 * 40 << 4 * 40; } void tst_QTableView::columnViewportPosition() @@ -2532,7 +2520,7 @@ void tst_QTableView::columnAt() for (int c = 0; c < columnCount; ++c) view.setColumnWidth(c, columnWidth); - for (int i = 0; i < hiddenColumns.count(); ++i) + for (int i = 0; i < hiddenColumns.size(); ++i) view.hideColumn(hiddenColumns.at(i)); QCOMPARE(view.columnAt(coordinate), column); @@ -3789,6 +3777,35 @@ void tst_QTableView::resizeToContentsSpans() QCOMPARE(view2.columnWidth(0), view3.columnWidth(0) - view2.columnWidth(1)); } +void tst_QTableView::resizeToContentsEarly() +{ + QStringListModel model; + QTableView view; + + // connect to the model before setting it on the view + connect(&model, &QStringListModel::modelReset, &model, [&view]{ + view.resizeColumnsToContents(); + }); + connect(&model, &QStringListModel::modelReset, &model, [&view]{ + view.resizeRowsToContents(); + }); + + // the view only connects now to the model's signals, so responds to the + // reset signal *after* the lambdas above + view.setModel(&model); + + QStringList data(200, QString("Hello World")); + model.setStringList(data); + + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + view.verticalScrollBar()->setValue(view.verticalScrollBar()->maximum()); + + data = data.sliced(data.size() / 2); + model.setStringList(data); +} + QT_BEGIN_NAMESPACE extern bool Q_WIDGETS_EXPORT qt_tab_all_widgets(); // qapplication.cpp QT_END_NAMESPACE @@ -3811,7 +3828,6 @@ void tst_QTableView::tabFocus() QLineEdit *edit = new QLineEdit(&window); window.show(); - QApplication::setActiveWindow(&window); window.setFocus(); window.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&window)); @@ -4351,15 +4367,18 @@ void tst_QTableView::task191545_dragSelectRows() QHeaderView *vHeader = table.verticalHeader(); QWidget *vHeaderVp = vHeader->viewport(); QPoint rowPos(cellRect.center()); - QMouseEvent rowPressEvent(QEvent::MouseButtonPress, rowPos, Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); + QMouseEvent rowPressEvent(QEvent::MouseButtonPress, rowPos, rowPos, vHeaderVp->mapToGlobal(rowPos), + Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); QCoreApplication::sendEvent(vHeaderVp, &rowPressEvent); for (int i = 0; i < 4; ++i) { rowPos.setY(rowPos.y() + cellRect.height()); - QMouseEvent moveEvent(QEvent::MouseMove, rowPos, Qt::NoButton, Qt::LeftButton, Qt::ControlModifier); + QMouseEvent moveEvent(QEvent::MouseMove, rowPos, rowPos, vHeaderVp->mapToGlobal(rowPos), + Qt::NoButton, Qt::LeftButton, Qt::ControlModifier); QCoreApplication::sendEvent(vHeaderVp, &moveEvent); } - QMouseEvent rowReleaseEvent(QEvent::MouseButtonRelease, rowPos, Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); + QMouseEvent rowReleaseEvent(QEvent::MouseButtonRelease, rowPos, vHeaderVp->mapToGlobal(rowPos), + Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); QCoreApplication::sendEvent(vHeaderVp, &rowReleaseEvent); for (int i = 0; i < 4; ++i) { @@ -4373,15 +4392,18 @@ void tst_QTableView::task191545_dragSelectRows() QHeaderView *hHeader = table.horizontalHeader(); QWidget *hHeaderVp = hHeader->viewport(); QPoint colPos((cellRect.left() + cellRect.right()) / 2, 5); - QMouseEvent colPressEvent(QEvent::MouseButtonPress, colPos, Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); + QMouseEvent colPressEvent(QEvent::MouseButtonPress, colPos, hHeaderVp->mapToGlobal(colPos), + Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); QCoreApplication::sendEvent(hHeaderVp, &colPressEvent); for (int i = 0; i < 4; ++i) { colPos.setX(colPos.x() + cellRect.width()); - QMouseEvent moveEvent(QEvent::MouseMove, colPos, Qt::NoButton, Qt::LeftButton, Qt::ControlModifier); + QMouseEvent moveEvent(QEvent::MouseMove, colPos, hHeaderVp->mapToGlobal(colPos), + Qt::NoButton, Qt::LeftButton, Qt::ControlModifier); QCoreApplication::sendEvent(hHeaderVp, &moveEvent); } - QMouseEvent colReleaseEvent(QEvent::MouseButtonRelease, colPos, Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); + QMouseEvent colReleaseEvent(QEvent::MouseButtonRelease, colPos, hHeaderVp->mapToGlobal(colPos), + Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); QCoreApplication::sendEvent(hHeaderVp, &colReleaseEvent); for (int i = 0; i < 4; ++i) { @@ -4394,16 +4416,19 @@ void tst_QTableView::task191545_dragSelectRows() QRect cellRect = table.visualRect(model.index(2, 2)); QWidget *tableVp = table.viewport(); QPoint cellPos = cellRect.center(); - QMouseEvent cellPressEvent(QEvent::MouseButtonPress, cellPos, Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); + QMouseEvent cellPressEvent(QEvent::MouseButtonPress, cellPos, tableVp->mapToGlobal(cellPos), + Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); QCoreApplication::sendEvent(tableVp, &cellPressEvent); for (int i = 0; i < 6; ++i) { cellPos.setX(cellPos.x() + cellRect.width()); cellPos.setY(cellPos.y() + cellRect.height()); - QMouseEvent moveEvent(QEvent::MouseMove, cellPos, Qt::NoButton, Qt::LeftButton, Qt::ControlModifier); + QMouseEvent moveEvent(QEvent::MouseMove, cellPos, tableVp->mapToGlobal(cellPos), + Qt::NoButton, Qt::LeftButton, Qt::ControlModifier); QCoreApplication::sendEvent(tableVp, &moveEvent); } - QMouseEvent cellReleaseEvent(QEvent::MouseButtonRelease, cellPos, Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); + QMouseEvent cellReleaseEvent(QEvent::MouseButtonRelease, cellPos, tableVp->mapToGlobal(cellPos), + Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); QCoreApplication::sendEvent(tableVp, &cellReleaseEvent); for (int i = 0; i < 6; ++i) { @@ -4418,16 +4443,21 @@ void tst_QTableView::task191545_dragSelectRows() QRect cellRect = table.visualRect(model.index(3, 3)); QWidget *tableVp = table.viewport(); QPoint cellPos = cellRect.center(); - QMouseEvent cellPressEvent(QEvent::MouseButtonPress, cellPos, Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); + QMouseEvent cellPressEvent(QEvent::MouseButtonPress, cellPos, tableVp->mapToGlobal(cellPos), + Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); QCoreApplication::sendEvent(tableVp, &cellPressEvent); for (int i = 0; i < 6; ++i) { + // cellPos might have been updated by scrolling, so refresh + cellPos = table.visualRect(model.index(3+i, 3+i)).center(); cellPos.setX(cellPos.x() + cellRect.width()); cellPos.setY(cellPos.y() + cellRect.height()); - QMouseEvent moveEvent(QEvent::MouseMove, cellPos, Qt::NoButton, Qt::LeftButton, Qt::ControlModifier); + QMouseEvent moveEvent(QEvent::MouseMove, cellPos, tableVp->mapToGlobal(cellPos), + Qt::NoButton, Qt::LeftButton, Qt::ControlModifier); QCoreApplication::sendEvent(tableVp, &moveEvent); } - QMouseEvent cellReleaseEvent(QEvent::MouseButtonRelease, cellPos, Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); + QMouseEvent cellReleaseEvent(QEvent::MouseButtonRelease, cellPos, tableVp->mapToGlobal(cellPos), + Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); QCoreApplication::sendEvent(tableVp, &cellReleaseEvent); QTest::qWait(200); @@ -4783,6 +4813,147 @@ void tst_QTableView::deselectRow() QVERIFY(!tw.selectionModel()->isRowSelected(1, QModelIndex())); } +class QTableViewSelectCells : public QTableView +{ +public: + QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex &index, + const QEvent *) const override + { + return QTableView::selectionCommand(index, shiftPressed ? &mouseEvent : nullptr); + } + QMouseEvent mouseEvent = QMouseEvent(QEvent::MouseButtonPress, QPointF(), QPointF(), + Qt::LeftButton, Qt::LeftButton, Qt::ShiftModifier); + bool shiftPressed = false; +}; + +void tst_QTableView::selectRowsAndCells() +{ + const auto checkRows = [](const QModelIndexList &mil) + { + QCOMPARE(mil.size(), 3); + for (const auto &mi : mil) + QVERIFY(mi.row() >= 1 && mi.row() <= 3); + }; + QTableViewSelectCells tw; + QtTestTableModel model(5, 1); + tw.setSelectionBehavior(QAbstractItemView::SelectRows); + tw.setSelectionMode(QAbstractItemView::ExtendedSelection); + tw.setModel(&model); + tw.show(); + + tw.selectRow(1); + tw.shiftPressed = true; + tw.selectRow(2); + tw.shiftPressed = false; + QTest::mouseClick(tw.viewport(), Qt::LeftButton, Qt::ShiftModifier, tw.visualRect(model.index(3, 0)).center()); + checkRows(tw.selectionModel()->selectedRows()); + + tw.clearSelection(); + QTest::mouseClick(tw.viewport(), Qt::LeftButton, Qt::NoModifier, tw.visualRect(model.index(3, 0)).center()); + tw.shiftPressed = true; + tw.selectRow(1); + checkRows(tw.selectionModel()->selectedRows()); +} + +void tst_QTableView::selectColumnsAndCells() +{ + const auto checkColumns = [](const QModelIndexList &mil) + { + QCOMPARE(mil.size(), 3); + for (const auto &mi : mil) + QVERIFY(mi.column() >= 1 && mi.column() <= 3); + }; + QTableViewSelectCells tw; + QtTestTableModel model(1, 5); + tw.setSelectionBehavior(QAbstractItemView::SelectColumns); + tw.setSelectionMode(QAbstractItemView::ExtendedSelection); + tw.setModel(&model); + tw.show(); + + tw.selectColumn(1); + tw.shiftPressed = true; + tw.selectColumn(2); + tw.shiftPressed = false; + QTest::mouseClick(tw.viewport(), Qt::LeftButton, Qt::ShiftModifier, tw.visualRect(model.index(0, 3)).center()); + checkColumns(tw.selectionModel()->selectedColumns()); + + tw.clearSelection(); + QTest::mouseClick(tw.viewport(), Qt::LeftButton, Qt::NoModifier, tw.visualRect(model.index(0, 3)).center()); + tw.shiftPressed = true; + tw.selectColumn(1); + checkColumns(tw.selectionModel()->selectedColumns()); +} + +void tst_QTableView::selectWithHeader_data() +{ + QTest::addColumn<Qt::Orientation>("orientation"); + + QTest::addRow("horizontal") << Qt::Horizontal; + QTest::addRow("vertical") << Qt::Vertical; +} + +void tst_QTableView::selectWithHeader() +{ + QFETCH(Qt::Orientation, orientation); + + QTableWidget view(10, 10); + view.resize(200, 100); + view.show(); + + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QHeaderView *header = nullptr; + QPoint clickPos; + QModelIndex lastIndex; + + switch (orientation) { + case Qt::Horizontal: + header = view.horizontalHeader(); + clickPos.rx() = header->sectionPosition(0) + header->sectionSize(0) / 2; + clickPos.ry() = header->height() / 2; + lastIndex = view.model()->index(9, 0); + break; + case Qt::Vertical: + header = view.verticalHeader(); + clickPos.rx() = header->width() / 2; + clickPos.ry() = header->sectionPosition(0) + header->sectionSize(0) / 2; + lastIndex = view.model()->index(0, 9); + break; + } + + const auto isSelected = [&]{ + return orientation == Qt::Horizontal + ? view.selectionModel()->isColumnSelected(0) + : view.selectionModel()->isRowSelected(0); + }; + + QTest::mouseClick(header->viewport(), Qt::LeftButton, {}, clickPos); + QVERIFY(isSelected()); + QTest::mouseClick(header->viewport(), Qt::LeftButton, Qt::ControlModifier, clickPos); + QVERIFY(!isSelected()); + QTest::mouseClick(header->viewport(), Qt::LeftButton, {}, clickPos); + QVERIFY(isSelected()); + view.scrollTo(lastIndex); + QTest::mouseClick(header->viewport(), Qt::LeftButton, Qt::ControlModifier, clickPos); + QVERIFY(!isSelected()); +} + +void tst_QTableView::resetDefaultSectionSize() +{ + // Create a table and change its default section size and then reset it. + // This should be a no op so clicking on row 1 should select row 1 and not row + // 0 as previously. QTBUG-116013 + QTableWidget view(10, 10); + view.resize(300, 300); + view.verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); + view.verticalHeader()->setDefaultSectionSize(120); + view.verticalHeader()->resetDefaultSectionSize(); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + QEXPECT_FAIL("", "Reverted fix for QTBUG-116013 due to QTBUG-122109", Continue); + QCOMPARE(view.verticalHeader()->logicalIndexAt(9, 45), 1); +} + // This has nothing to do with QTableView, but it's convenient to reuse the QtTestTableModel #if QT_CONFIG(textmarkdownwriter) @@ -4811,5 +4982,18 @@ void tst_QTableView::markdownWriter() } #endif +void tst_QTableView::rowsInVerticalHeader() +{ + QtTestTableModel model(0, 2); + QTableView view; + view.setModel(&model); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + auto *verticalHeader = view.verticalHeader(); + QCOMPARE(verticalHeader->count(), 0); + model.insertRows(2); + QCOMPARE(verticalHeader->count(), 2); +} + QTEST_MAIN(tst_QTableView) #include "tst_qtableview.moc" diff --git a/tests/auto/widgets/itemviews/qtablewidget/CMakeLists.txt b/tests/auto/widgets/itemviews/qtablewidget/CMakeLists.txt index 5409741dd9..306ba4bdff 100644 --- a/tests/auto/widgets/itemviews/qtablewidget/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qtablewidget/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qtablewidget.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtablewidget Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtablewidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qtablewidget SOURCES tst_qtablewidget.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/itemviews/qtablewidget/tst_qtablewidget.cpp b/tests/auto/widgets/itemviews/qtablewidget/tst_qtablewidget.cpp index 6ee923662e..34972bc3e8 100644 --- a/tests/auto/widgets/itemviews/qtablewidget/tst_qtablewidget.cpp +++ b/tests/auto/widgets/itemviews/qtablewidget/tst_qtablewidget.cpp @@ -1,34 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QHeaderView> #include <QLineEdit> #include <QMimeData> +#include <QScrollBar> #include <QSignalSpy> #include <QTableWidget> #include <QTest> @@ -49,6 +25,7 @@ private slots: void initTestCase(); void init(); void getSetCheck(); + void selectionRange(); void clear(); void clearContents(); void rowCount(); @@ -60,6 +37,8 @@ private slots: void takeItem(); void selectedItems_data(); void selectedItems(); + void selectedSpannedCells_data(); + void selectedSpannedCells(); void removeRow_data(); void removeRow(); void removeColumn_data(); @@ -85,12 +64,15 @@ private slots: void task219380_removeLastRow(); void task262056_sortDuplicate(); void itemWithHeaderItems(); + void checkHeaderItemFlagsConflict(); void mimeData(); void selectedRowAfterSorting(); void search(); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) void clearItemData(); #endif + void createPersistentOnLayoutAboutToBeChanged(); + void createPersistentOnLayoutAboutToBeChangedAutoSort(); private: std::unique_ptr<QTableWidget> testWidget; @@ -153,6 +135,19 @@ void tst_QTableWidget::getSetCheck() QCOMPARE(obj1.itemPrototype(), nullptr); } +void tst_QTableWidget::selectionRange() +{ + QTableWidgetSelectionRange defaultSelection; + QTableWidgetSelectionRange selection(1, 2, 3, 4); + + QTableWidgetSelectionRange copy(selection); + QCOMPARE(copy, selection); + QVERIFY(copy != defaultSelection); + + defaultSelection = copy; + QCOMPARE(defaultSelection, copy); +} + void tst_QTableWidget::initTestCase() { testWidget.reset(new QTableWidget); @@ -199,8 +194,8 @@ void tst_QTableWidget::clear() QVERIFY(bla.isNull()); QVERIFY(!testWidget->item(0,0)); - QVERIFY(!testWidget->selectedRanges().count()); - QVERIFY(!testWidget->selectedItems().count()); + QVERIFY(!testWidget->selectedRanges().size()); + QVERIFY(!testWidget->selectedItems().size()); } void tst_QTableWidget::rowCount() @@ -367,7 +362,7 @@ void tst_QTableWidget::takeItem() QCOMPARE(item->text(), QString::number(row * column + column)); delete item; - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); const QList<QVariant> arguments = spy.takeFirst(); QCOMPARE(arguments.size(), 2); QCOMPARE(arguments.at(0).toInt(), row); @@ -532,24 +527,24 @@ void tst_QTableWidget::selectedItems() testWidget->setColumnHidden(column, true); // make sure we don't have any previous selections hanging around - QVERIFY(!testWidget->selectedRanges().count()); - QVERIFY(!testWidget->selectedItems().count()); + QVERIFY(!testWidget->selectedRanges().size()); + QVERIFY(!testWidget->selectedItems().size()); // select range and check that it is set correctly testWidget->setRangeSelected(selectionRange, true); if (selectionRange.topRow() >= 0) { - QCOMPARE(testWidget->selectedRanges().count(), 1); + QCOMPARE(testWidget->selectedRanges().size(), 1); QCOMPARE(testWidget->selectedRanges().at(0).topRow(), selectionRange.topRow()); QCOMPARE(testWidget->selectedRanges().at(0).bottomRow(), selectionRange.bottomRow()); QCOMPARE(testWidget->selectedRanges().at(0).leftColumn(), selectionRange.leftColumn()); QCOMPARE(testWidget->selectedRanges().at(0).rightColumn(), selectionRange.rightColumn()); } else { - QCOMPARE(testWidget->selectedRanges().count(), 0); + QCOMPARE(testWidget->selectedRanges().size(), 0); } // check that the correct number of items and the expected items are there const QList<QTableWidgetItem *> selectedItems = testWidget->selectedItems(); - QCOMPARE(selectedItems.count(), expectedItems.count()); + QCOMPARE(selectedItems.size(), expectedItems.size()); for (const auto &intPair : expectedItems) QVERIFY(selectedItems.contains(testWidget->item(intPair.first, intPair.second))); @@ -569,6 +564,75 @@ void tst_QTableWidget::selectedItems() } } +void tst_QTableWidget::selectedSpannedCells_data() +{ + QTest::addColumn<QRect>("spannedCells"); // in cells, not pixels + QTest::addColumn<QPoint>("selectionStartCell"); + QTest::addColumn<QPoint>("selectionEndCell"); + QTest::addColumn<int>("expectedSelectionRangeCount"); + QTest::addColumn<QTableWidgetSelectionRange>("expectedFirstSelectionRange"); + + QTest::newRow("merge 2 cells in column, select adjacent left") + << QRect(1, 2, 1, 2) << QPoint(0, 1) << QPoint(0, 3) + << 1 << QTableWidgetSelectionRange(1, 0, 3, 0); + + QTest::newRow("merge 2 cells in column, select those and one more") + << QRect(1, 2, 1, 2) << QPoint(1, 1) << QPoint(1, 3) + << 3 << QTableWidgetSelectionRange(1, 1, 1, 1); + + QTest::newRow("merge 2 cells in column, select rows above") + << QRect(1, 2, 1, 2) << QPoint(0, 0) << QPoint(3, 1) + << 1 << QTableWidgetSelectionRange(0, 0, 1, 3); + + QTest::newRow("merge 4 cells in column, select adjacent right") + << QRect(1, 0, 1, 4) << QPoint(2, 0) << QPoint(3, 3) + << 1 << QTableWidgetSelectionRange(0, 2, 3, 3); + + QTest::newRow("merge 3 cells in row, select those and one more") + << QRect(0, 1, 3, 1) << QPoint(0, 1) << QPoint(3, 1) + << 4 << QTableWidgetSelectionRange(1, 0, 1, 0); + + QTest::newRow("merge 3 cells in row, select adjacent to right") + << QRect(0, 1, 3, 1) << QPoint(3, 0) << QPoint(3, 2) + << 1 << QTableWidgetSelectionRange(0, 3, 2, 3); + + QTest::newRow("merge 3 cells in row, select adjacent above") + << QRect(0, 2, 3, 2) << QPoint(0, 1) << QPoint(2, 1) + << 1 << QTableWidgetSelectionRange(1, 0, 1, 2); +} + +void tst_QTableWidget::selectedSpannedCells() // QTBUG-255 +{ + QFETCH(QRect, spannedCells); + QFETCH(QPoint, selectionStartCell); + QFETCH(QPoint, selectionEndCell); + QFETCH(int, expectedSelectionRangeCount); + QFETCH(const QTableWidgetSelectionRange, expectedFirstSelectionRange); + + QTableWidget testWidget(4, 4); + testWidget.resize(600, 200); + testWidget.show(); + + // create and set items + for (int c = 0; c < 4; ++c) { + for (int r = 0; r < 4; ++r) + testWidget.setItem(r, c, new QTableWidgetItem(QString("Item %1 %2").arg(c).arg(r))); + } + + // merge some cells + testWidget.setSpan(spannedCells.top(), spannedCells.left(), spannedCells.height(), spannedCells.width()); + + // click one cell and shift-click another, to select a range + QTest::mouseClick(testWidget.viewport(), Qt::LeftButton, Qt::NoModifier, + testWidget.visualRect(testWidget.model()->index(selectionStartCell.y(), selectionStartCell.x())).center()); + QTest::mouseClick(testWidget.viewport(), Qt::LeftButton, Qt::ShiftModifier, + testWidget.visualRect(testWidget.model()->index(selectionEndCell.y(), selectionEndCell.x())).center()); + + auto ranges = testWidget.selectedRanges(); + QCOMPARE(ranges.size(), expectedSelectionRangeCount); + QCOMPARE(ranges.first(), expectedFirstSelectionRange); +} + void tst_QTableWidget::removeRow_data() { QTest::addColumn<int>("rowCount"); @@ -1132,15 +1196,15 @@ void tst_QTableWidget::sortItems() persistent << model->index(r, sortColumn, QModelIndex()); } - for (int h = 0; h < initialHidden.count(); ++h) + for (int h = 0; h < initialHidden.size(); ++h) testWidget->hideRow(initialHidden.at(h)); - QCOMPARE(testWidget->verticalHeader()->hiddenSectionCount(), initialHidden.count()); + QCOMPARE(testWidget->verticalHeader()->hiddenSectionCount(), initialHidden.size()); testWidget->sortItems(sortColumn, sortOrder); int te = 0; - for (int i = 0; i < rows.count(); ++i) { + for (int i = 0; i < rows.size(); ++i) { for (int j = 0; j < columnCount; ++j) { QString value; QTableWidgetItem *itm = testWidget->item(i, j); @@ -1154,7 +1218,7 @@ void tst_QTableWidget::sortItems() // << "expected" << rows.at(i); } - for (int k = 0; k < expectedHidden.count(); ++k) + for (int k = 0; k < expectedHidden.size(); ++k) QVERIFY(testWidget->isRowHidden(expectedHidden.at(k))); } @@ -1335,18 +1399,18 @@ void tst_QTableWidget::setItemWithSorting() } } - for (int k = 0; k < persistent.count(); ++k) { + for (int k = 0; k < persistent.size(); ++k) { QCOMPARE(persistent.at(k).row(), expectedRows.at(k)); int i = (persistent.at(k).row() * columnCount) + sortColumn; QCOMPARE(persistent.at(k).data().toString(), expectedValues.at(i)); } if (i == 0) - QCOMPARE(dataChangedSpy.count(), reorderingExpected ? 0 : 1); + QCOMPARE(dataChangedSpy.size(), reorderingExpected ? 0 : 1); else - QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.size(), 1); - QCOMPARE(layoutChangedSpy.count(), reorderingExpected ? 1 : 0); + QCOMPARE(layoutChangedSpy.size(), reorderingExpected ? 1 : 0); } } @@ -1382,7 +1446,7 @@ void tst_QTableWidget::itemData() QCOMPARE(widget.currentRoles, QList<int> { Qt::UserRole + i }); } QMap<int, QVariant> flags = widget.model()->itemData(widget.model()->index(0, 0)); - QCOMPARE(flags.count(), 6); + QCOMPARE(flags.size(), 6); for (int i = 0; i < 4; ++i) QCOMPARE(flags[Qt::UserRole + i].toString(), QString::number(i + 1)); } @@ -1395,7 +1459,7 @@ void tst_QTableWidget::setItemData() QTableWidgetItem *item = new QTableWidgetItem; table.setItem(0, 0, item); - QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.size(), 1); QModelIndex idx = qvariant_cast<QModelIndex>(dataChangedSpy.takeFirst().at(0)); QMap<int, QVariant> data; @@ -1407,7 +1471,7 @@ void tst_QTableWidget::setItemData() QCOMPARE(table.model()->data(idx, Qt::DisplayRole).toString(), QLatin1String("Display")); QCOMPARE(table.model()->data(idx, Qt::EditRole).toString(), QLatin1String("Display")); QCOMPARE(table.model()->data(idx, Qt::ToolTipRole).toString(), QLatin1String("ToolTip")); - QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.size(), 1); QCOMPARE(idx, qvariant_cast<QModelIndex>(dataChangedSpy.first().at(0))); QCOMPARE(idx, qvariant_cast<QModelIndex>(dataChangedSpy.first().at(1))); const auto roles = qvariant_cast<QList<int>>(dataChangedSpy.first().at(2)); @@ -1418,13 +1482,13 @@ void tst_QTableWidget::setItemData() dataChangedSpy.clear(); table.model()->setItemData(idx, data); - QCOMPARE(dataChangedSpy.count(), 0); + QCOMPARE(dataChangedSpy.size(), 0); data.clear(); data.insert(Qt::DisplayRole, QLatin1String("dizplaye")); table.model()->setItemData(idx, data); QCOMPARE(table.model()->data(idx, Qt::DisplayRole).toString(), QLatin1String("dizplaye")); - QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.size(), 1); QCOMPARE(QList<int>({ Qt::DisplayRole, Qt::EditRole }), qvariant_cast<QList<int>>(dataChangedSpy.first().at(2))); @@ -1493,6 +1557,12 @@ void tst_QTableWidget::sizeHint() QFETCH(Qt::ScrollBarPolicy, scrollBarPolicy); QFETCH(QSize, viewSize); + const QString defaultStyle = QApplication::style()->name(); + QApplication::setStyle("windows"); + const auto resetStyle = qScopeGuard([defaultStyle]{ + QApplication::setStyle(defaultStyle); + }); + QTableWidget view(2, 2); view.setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); view.setVerticalScrollBarPolicy(scrollBarPolicy); @@ -1512,18 +1582,21 @@ void tst_QTableWidget::sizeHint() QTRY_COMPARE(view.size(), viewSize); } + QApplication::processEvents(); // execute delayed layouts auto sizeHint = view.sizeHint(); view.hide(); QCOMPARE(view.sizeHint(), sizeHint); view.horizontalHeader()->hide(); view.show(); + QApplication::processEvents(); // execute delayed layouts sizeHint = view.sizeHint(); view.hide(); QCOMPARE(view.sizeHint(), sizeHint); view.verticalHeader()->hide(); view.show(); + QApplication::processEvents(); // execute delayed layouts sizeHint = view.sizeHint(); view.hide(); QCOMPARE(view.sizeHint(), sizeHint); @@ -1587,7 +1660,7 @@ void tst_QTableWidget::task262056_sortDuplicate() QSignalSpy layoutChangedSpy(testWidget->model(), &QAbstractItemModel::layoutChanged); testWidget->item(3,0)->setBackground(Qt::red); - QCOMPARE(layoutChangedSpy.count(),0); + QCOMPARE(layoutChangedSpy.size(),0); } @@ -1607,6 +1680,25 @@ void tst_QTableWidget::itemWithHeaderItems() QCOMPARE(table.item(0, 1), nullptr); } +void tst_QTableWidget::checkHeaderItemFlagsConflict() +{ + // QTBUG-113209 + // Check that setting header item doesn't set Qt::ItemNeverHasChildren + // Chech that header items do not emit itemChanged. + QTableWidget table(1, 1); + QSignalSpy itemChangeSpy(&table, &QTableWidget::itemChanged); + QVERIFY(itemChangeSpy.isValid()); + + QTableWidgetItem *item = new QTableWidgetItem("Initial"); + table.setHorizontalHeaderItem(0, item); + + QVERIFY(!(item->flags() & Qt::ItemNeverHasChildren)); + + item->setData(Qt::DisplayRole, "updated"); + + QCOMPARE(itemChangeSpy.size(), 0); +} + class TestTableWidget : public QTableWidget { Q_OBJECT @@ -1684,7 +1776,7 @@ void tst_QTableWidget::selectedRowAfterSorting() table.setProperty("sortingEnabled",true); table.selectRow(1); table.item(1,1)->setText("9"); - QCOMPARE(table.selectedItems().count(),3); + QCOMPARE(table.selectedItems().size(),3); const auto selectedItems = table.selectedItems(); for (QTableWidgetItem *item : selectedItems) QCOMPARE(item->row(), 0); @@ -1756,5 +1848,74 @@ void tst_QTableWidget::clearItemData() } #endif +void tst_QTableWidget::createPersistentOnLayoutAboutToBeChanged() // QTBUG-93466 +{ + QTableWidget widget; + widget.model()->insertColumn(0); + QCOMPARE(widget.model()->columnCount(), 1); + widget.model()->insertRows(0, 3); + for (int row = 0; row < 3; ++row) + widget.model()->setData(widget.model()->index(row, 0), row); + QList<QPersistentModelIndex> idxList; + QSignalSpy layoutAboutToBeChangedSpy(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged); + QSignalSpy layoutChangedSpy(widget.model(), &QAbstractItemModel::layoutChanged); + connect(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged, this, [&idxList, &widget](){ + idxList.clear(); + for (int row = 0; row < 3; ++row) + idxList << QPersistentModelIndex(widget.model()->index(row, 0)); + }); + connect(widget.model(), &QAbstractItemModel::layoutChanged, this, [&idxList](){ + QCOMPARE(idxList.size(), 3); + QCOMPARE(idxList.at(0).row(), 1); + QCOMPARE(idxList.at(0).column(), 0); + QCOMPARE(idxList.at(0).data().toInt(), 0); + QCOMPARE(idxList.at(1).row(), 0); + QCOMPARE(idxList.at(1).column(), 0); + QCOMPARE(idxList.at(1).data().toInt(), -1); + QCOMPARE(idxList.at(2).row(), 2); + QCOMPARE(idxList.at(2).column(), 0); + QCOMPARE(idxList.at(2).data().toInt(), 2); + }); + widget.model()->setData(widget.model()->index(1, 0), -1); + widget.model()->sort(0); + QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), 1); +} + +void tst_QTableWidget::createPersistentOnLayoutAboutToBeChangedAutoSort() // QTBUG-93466 +{ + QTableWidget widget; + widget.model()->insertColumn(0); + QCOMPARE(widget.model()->columnCount(), 1); + widget.model()->insertRows(0, 3); + for (int row = 0; row < 3; ++row) + widget.model()->setData(widget.model()->index(row, 0), row); + widget.sortByColumn(0, Qt::AscendingOrder); + widget.setSortingEnabled(true); + QList<QPersistentModelIndex> idxList; + QSignalSpy layoutAboutToBeChangedSpy(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged); + QSignalSpy layoutChangedSpy(widget.model(), &QAbstractItemModel::layoutChanged); + connect(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged, this, [&idxList, &widget](){ + idxList.clear(); + for (int row = 0; row < 3; ++row) + idxList << QPersistentModelIndex(widget.model()->index(row, 0)); + }); + connect(widget.model(), &QAbstractItemModel::layoutChanged, this, [&idxList](){ + QCOMPARE(idxList.size(), 3); + QCOMPARE(idxList.at(0).row(), 1); + QCOMPARE(idxList.at(0).column(), 0); + QCOMPARE(idxList.at(0).data().toInt(), 0); + QCOMPARE(idxList.at(1).row(), 0); + QCOMPARE(idxList.at(1).column(), 0); + QCOMPARE(idxList.at(1).data().toInt(), -1); + QCOMPARE(idxList.at(2).row(), 2); + QCOMPARE(idxList.at(2).column(), 0); + QCOMPARE(idxList.at(2).data().toInt(), 2); + }); + widget.model()->setData(widget.model()->index(1, 0), -1); + QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), 1); +} + QTEST_MAIN(tst_QTableWidget) #include "tst_qtablewidget.moc" diff --git a/tests/auto/widgets/itemviews/qtreeview/BLACKLIST b/tests/auto/widgets/itemviews/qtreeview/BLACKLIST deleted file mode 100644 index 3aaf1e4e64..0000000000 --- a/tests/auto/widgets/itemviews/qtreeview/BLACKLIST +++ /dev/null @@ -1,5 +0,0 @@ -# QTBUG-87408 -[horizontalScrollMode] -android -[setSelection] -android diff --git a/tests/auto/widgets/itemviews/qtreeview/CMakeLists.txt b/tests/auto/widgets/itemviews/qtreeview/CMakeLists.txt index 57267e1694..deccd71f59 100644 --- a/tests/auto/widgets/itemviews/qtreeview/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qtreeview/CMakeLists.txt @@ -1,14 +1,21 @@ -# Generated from qtreeview.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtreeview Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtreeview LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qtreeview SOURCES ../../../../shared/fakedirmodel.h tst_qtreeview.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp index 3e22181c2f..9b02b0e80d 100644 --- a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp +++ b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "../../../../shared/fakedirmodel.h" @@ -49,6 +24,8 @@ #include <private/qtreeview_p.h> #include <private/qtesthelpers_p.h> +#include <QtWidgets/private/qapplication_p.h> + using namespace QTestPrivate; #if QT_CONFIG(draganddrop) @@ -250,6 +227,7 @@ private slots: void taskQTBUG_7232_AllowUserToControlSingleStep(); void taskQTBUG_8376(); void taskQTBUG_61476(); + void taskQTBUG_42469_crash(); void testInitialFocus(); void fetchUntilScreenFull(); void expandAfterTake(); @@ -889,8 +867,8 @@ void tst_QTreeView::horizontalScrollMode() QTreeView view; setFrameless(&view); view.setModel(&model); - view.setFixedSize(100, 100); - view.header()->resizeSection(0, 200); + view.setFixedSize(100, 1000); + view.header()->resizeSection(0, 2000); view.show(); QCOMPARE(view.horizontalScrollMode(), QAbstractItemView::ScrollPerPixel); @@ -1262,7 +1240,6 @@ void tst_QTreeView::keyboardSearchMultiColumn() view.setModel(&model); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); view.setCurrentIndex(model.index(0, 1)); @@ -1303,7 +1280,7 @@ void tst_QTreeView::setModel() QCOMPARE(view.header()->model(), model); QCOMPARE(view.selectionModel() != oldSelectionModel, (i == 0)); } - QTRY_COMPARE(modelDestroyedSpy.count(), 0); + QTRY_COMPARE(modelDestroyedSpy.size(), 0); view.setModel(nullptr); QCOMPARE(view.model(), nullptr); @@ -1358,7 +1335,7 @@ void tst_QTreeView::setHeader() Qt::Orientation orient = x ? Qt::Vertical : Qt::Horizontal; QHeaderView *head = new QHeaderView(orient); view.setHeader(head); - QCOMPARE(destroyedSpy.count(), 1); + QCOMPARE(destroyedSpy.size(), 1); QCOMPARE(head->parent(), &view); QCOMPARE(view.header(), head); view.setHeader(head); @@ -1541,10 +1518,10 @@ void tst_QTreeView::limitedExpand() QVERIFY(spy.isValid()); view.expand(model.index(0, 0)); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); view.expand(model.index(1, 0)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } { QStringListModel model(QStringList() << "one" << "two"); @@ -1555,9 +1532,9 @@ void tst_QTreeView::limitedExpand() QVERIFY(spy.isValid()); view.expand(model.index(0, 0)); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); view.expandAll(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } } @@ -1596,58 +1573,58 @@ void tst_QTreeView::expandAndCollapse() view.expand(QModelIndex()); QCOMPARE(view.isExpanded(QModelIndex()), false); view.collapse(QModelIndex()); - QCOMPARE(expandedSpy.count(), 0); - QCOMPARE(collapsedSpy.count(), 0); + QCOMPARE(expandedSpy.size(), 0); + QCOMPARE(collapsedSpy.size(), 0); // expand a first level item QVERIFY(!view.isExpanded(a)); view.expand(a); QVERIFY(view.isExpanded(a)); - QCOMPARE(expandedSpy.count(), 1); - QCOMPARE(collapsedSpy.count(), 0); + QCOMPARE(expandedSpy.size(), 1); + QCOMPARE(collapsedSpy.size(), 0); args = expandedSpy.takeFirst(); QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), a); view.expand(a); QVERIFY(view.isExpanded(a)); - QCOMPARE(expandedSpy.count(), 0); - QCOMPARE(collapsedSpy.count(), 0); + QCOMPARE(expandedSpy.size(), 0); + QCOMPARE(collapsedSpy.size(), 0); // expand a second level item QVERIFY(!view.isExpanded(b)); view.expand(b); QVERIFY(view.isExpanded(a)); QVERIFY(view.isExpanded(b)); - QCOMPARE(expandedSpy.count(), 1); - QCOMPARE(collapsedSpy.count(), 0); + QCOMPARE(expandedSpy.size(), 1); + QCOMPARE(collapsedSpy.size(), 0); args = expandedSpy.takeFirst(); QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), b); view.expand(b); QVERIFY(view.isExpanded(b)); - QCOMPARE(expandedSpy.count(), 0); - QCOMPARE(collapsedSpy.count(), 0); + QCOMPARE(expandedSpy.size(), 0); + QCOMPARE(collapsedSpy.size(), 0); // collapse the first level item view.collapse(a); QVERIFY(!view.isExpanded(a)); QVERIFY(view.isExpanded(b)); - QCOMPARE(expandedSpy.count(), 0); - QCOMPARE(collapsedSpy.count(), 1); + QCOMPARE(expandedSpy.size(), 0); + QCOMPARE(collapsedSpy.size(), 1); args = collapsedSpy.takeFirst(); QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), a); view.collapse(a); QVERIFY(!view.isExpanded(a)); - QCOMPARE(expandedSpy.count(), 0); - QCOMPARE(collapsedSpy.count(), 0); + QCOMPARE(expandedSpy.size(), 0); + QCOMPARE(collapsedSpy.size(), 0); // expand the first level item again view.expand(a); QVERIFY(view.isExpanded(a)); QVERIFY(view.isExpanded(b)); - QCOMPARE(expandedSpy.count(), 1); - QCOMPARE(collapsedSpy.count(), 0); + QCOMPARE(expandedSpy.size(), 1); + QCOMPARE(collapsedSpy.size(), 0); args = expandedSpy.takeFirst(); QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), a); @@ -1655,8 +1632,8 @@ void tst_QTreeView::expandAndCollapse() view.collapse(b); QVERIFY(view.isExpanded(a)); QVERIFY(!view.isExpanded(b)); - QCOMPARE(expandedSpy.count(), 0); - QCOMPARE(collapsedSpy.count(), 1); + QCOMPARE(expandedSpy.size(), 0); + QCOMPARE(collapsedSpy.size(), 1); args = collapsedSpy.takeFirst(); QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), b); @@ -1664,8 +1641,8 @@ void tst_QTreeView::expandAndCollapse() view.collapse(a); QVERIFY(!view.isExpanded(a)); QVERIFY(!view.isExpanded(b)); - QCOMPARE(expandedSpy.count(), 0); - QCOMPARE(collapsedSpy.count(), 1); + QCOMPARE(expandedSpy.size(), 0); + QCOMPARE(collapsedSpy.size(), 1); args = collapsedSpy.takeFirst(); QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), a); @@ -1677,8 +1654,8 @@ void tst_QTreeView::expandAndCollapse() QVERIFY(view.isExpanded(a)); QVERIFY(view.isExpanded(b)); QVERIFY(!view.isExpanded(c)); - QCOMPARE(expandedSpy.count(), 2); - QCOMPARE(collapsedSpy.count(), 0); + QCOMPARE(expandedSpy.size(), 2); + QCOMPARE(collapsedSpy.size(), 0); args = expandedSpy.takeFirst(); QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), a); args = expandedSpy.takeFirst(); @@ -1689,8 +1666,8 @@ void tst_QTreeView::expandAndCollapse() QVERIFY(!view.isExpanded(a)); QVERIFY(!view.isExpanded(b)); QVERIFY(!view.isExpanded(c)); - QCOMPARE(expandedSpy.count(), 0); - QCOMPARE(collapsedSpy.count(), 2); + QCOMPARE(expandedSpy.size(), 0); + QCOMPARE(collapsedSpy.size(), 2); args = collapsedSpy.takeFirst(); QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), a); args = collapsedSpy.takeFirst(); @@ -1752,22 +1729,22 @@ void tst_QTreeView::expandAndCollapseAll() view.expandAll(); checkExpandState(model, view, QModelIndex(), true, &count); - QCOMPARE(collapsedSpy.count(), 0); - QCOMPARE(expandedSpy.count(), 39); // == 3 (first) + 9 (second) + 27 (third level) + QCOMPARE(collapsedSpy.size(), 0); + QCOMPARE(expandedSpy.size(), 39); // == 3 (first) + 9 (second) + 27 (third level) QCOMPARE(count, 39); collapsedSpy.clear(); expandedSpy.clear(); view.collapseAll(); checkExpandState(model, view, QModelIndex(), false, &count); - QCOMPARE(collapsedSpy.count(), 39); - QCOMPARE(expandedSpy.count(), 0); + QCOMPARE(collapsedSpy.size(), 39); + QCOMPARE(expandedSpy.size(), 0); QCOMPARE(count, 39); collapsedSpy.clear(); expandedSpy.clear(); view.expandRecursively(model.index(0, 0)); - QCOMPARE(expandedSpy.count(), 13); // 1 + 3 + 9 + QCOMPARE(expandedSpy.size(), 13); // 1 + 3 + 9 checkExpandState(model, view, model.index(0, 0), true, &count); QCOMPARE(count, 13); @@ -1779,9 +1756,9 @@ void tst_QTreeView::expandAndCollapseAll() expandedSpy.clear(); view.collapseAll(); view.expandRecursively(model.index(0, 0), 1); - QCOMPARE(expandedSpy.count(), 4); // 1 + 3 + QCOMPARE(expandedSpy.size(), 4); // 1 + 3 view.expandRecursively(model.index(0, 0), 2); - QCOMPARE(expandedSpy.count(), 13); // (1 + 3) + 9 + QCOMPARE(expandedSpy.size(), 13); // (1 + 3) + 9 checkExpandState(model, view, model.index(0, 0), true, &count); QCOMPARE(count, 13); @@ -1877,7 +1854,7 @@ void tst_QTreeView::keyboardNavigation() } break; default: - QVERIFY(false); + QFAIL(qPrintable(QStringLiteral("Unexpected key: %1").arg(key))); } QCOMPARE(view.currentIndex().row(), row); @@ -1948,7 +1925,6 @@ void tst_QTreeView::moveCursor() view.setColumnHidden(0, true); QVERIFY(view.isColumnHidden(0)); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); //here the first visible index should be selected @@ -2049,7 +2025,7 @@ void tst_QTreeView::setSelection() QFETCH(PointList, expectedItems); QFETCH(int, verticalOffset); - QtTestModel model(10, 5); + QtTestModel model(100, 5); model.levels = 1; model.setDecorationsEnabled(true); QTreeView view; @@ -2067,7 +2043,7 @@ void tst_QTreeView::setSelection() QVERIFY(selectionModel); const QModelIndexList selectedIndexes = selectionModel->selectedIndexes(); - QCOMPARE(selectedIndexes.count(), expectedItems.count()); + QCOMPARE(selectedIndexes.size(), expectedItems.size()); for (const QModelIndex &idx : selectedIndexes) QVERIFY(expectedItems.contains(QPoint(idx.column(), idx.row()))); } @@ -2171,7 +2147,7 @@ void tst_QTreeView::clicked() continue; QSignalSpy spy(&view, &QTreeView::clicked); QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); } } @@ -2252,7 +2228,7 @@ void tst_QTreeView::rowsAboutToBeRemoved() model.removeRows(1,1); QCOMPARE((view.state()), 0); // Should not be 5 (or any other number for that sake :) - QCOMPARE(spy1.count(), 1); + QCOMPARE(spy1.size(), 1); } @@ -2564,28 +2540,28 @@ void tst_QTreeView::selectionWithHiddenItems() //child should not be selected as it is hidden (its parent is not expanded) view.selectAll(); - QCOMPARE(view.selectionModel()->selection().count(), 1); //one range - QCOMPARE(view.selectionModel()->selectedRows().count(), 4); + QCOMPARE(view.selectionModel()->selection().size(), 1); //one range + QCOMPARE(view.selectionModel()->selectedRows().size(), 4); view.expandAll(); QVERIFY(view.isExpanded(item1.index())); - QCOMPARE(view.selectionModel()->selection().count(), 1); - QCOMPARE(view.selectionModel()->selectedRows().count(), 4); + QCOMPARE(view.selectionModel()->selection().size(), 1); + QCOMPARE(view.selectionModel()->selectedRows().size(), 4); QVERIFY( !view.selectionModel()->isSelected(model.indexFromItem(&child))); view.clearSelection(); QVERIFY(view.isExpanded(item1.index())); //child should be selected as it is visible (its parent is expanded) view.selectAll(); - QCOMPARE(view.selectionModel()->selection().count(), 2); - QCOMPARE(view.selectionModel()->selectedRows().count(), 5); //everything is selected + QCOMPARE(view.selectionModel()->selection().size(), 2); + QCOMPARE(view.selectionModel()->selectedRows().size(), 5); //everything is selected view.clearSelection(); //we hide the node with a child (there should then be 3 items selected in 2 ranges) view.setRowHidden(1, QModelIndex(), true); QVERIFY(view.isExpanded(item1.index())); view.selectAll(); - QCOMPARE(view.selectionModel()->selection().count(), 2); - QCOMPARE(view.selectionModel()->selectedRows().count(), 3); + QCOMPARE(view.selectionModel()->selection().size(), 2); + QCOMPARE(view.selectionModel()->selectedRows().size(), 3); QVERIFY(!view.selectionModel()->isSelected(model.indexFromItem(&item1))); QVERIFY(!view.selectionModel()->isSelected(model.indexFromItem(&child))); @@ -2598,8 +2574,8 @@ void tst_QTreeView::selectionWithHiddenItems() QVERIFY(view.isExpanded(item1.index())); view.selectAll(); QVERIFY(view.isExpanded(item1.index())); - QCOMPARE(view.selectionModel()->selection().count(), 3); - QCOMPARE(view.selectionModel()->selectedRows().count(), 4); + QCOMPARE(view.selectionModel()->selection().size(), 3); + QCOMPARE(view.selectionModel()->selectedRows().size(), 4); QVERIFY( !view.selectionModel()->isSelected(model.indexFromItem(&item2))); view.setRowHidden(2, QModelIndex(), false); QVERIFY(view.isExpanded(item1.index())); @@ -2614,21 +2590,21 @@ void tst_QTreeView::selectAll() view2.setSelectionMode(QAbstractItemView::ExtendedSelection); view2.selectAll(); // Should work with an empty model //everything should be selected since we are in ExtendedSelection mode - QCOMPARE(view2.selectedIndexes().count(), model.rowCount() * model.columnCount()); + QCOMPARE(view2.selectedIndexes().size(), model.rowCount() * model.columnCount()); for (int i = 0; i < model.rowCount(); ++i) model.setData(model.index(i,0), QLatin1String("row ") + QString::number(i)); QTreeView view; view.setModel(&model); - int selectedCount = view.selectedIndexes().count(); + int selectedCount = view.selectedIndexes().size(); view.selectAll(); - QCOMPARE(view.selectedIndexes().count(), selectedCount); + QCOMPARE(view.selectedIndexes().size(), selectedCount); QTreeView view3; view3.setModel(&model); view3.setSelectionMode(QAbstractItemView::NoSelection); view3.selectAll(); - QCOMPARE(view3.selectedIndexes().count(), 0); + QCOMPARE(view3.selectedIndexes().size(), 0); } void tst_QTreeView::extendedSelection_data() @@ -2654,7 +2630,7 @@ void tst_QTreeView::extendedSelection() topLevel.show(); QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); QTest::mousePress(view.viewport(), Qt::LeftButton, {}, mousePressPos); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), selectedCount); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), selectedCount); } void tst_QTreeView::rowSizeHint() @@ -2894,7 +2870,7 @@ public: } void kill() { - for (int i = children.count() -1; i >= 0; --i) { + for (int i = children.size() -1; i >= 0; --i) { children.at(i)->kill(); if (parent == nullptr) { deadChildren.append(children.at(i)); @@ -2936,7 +2912,7 @@ public: emit layoutAboutToBeChanged(); QModelIndexList oldList = persistentIndexList(); QList<QStack<int>> oldListPath; - for (int i = 0; i < oldList.count(); ++i) { + for (int i = 0; i < oldList.size(); ++i) { QModelIndex idx = oldList.at(i); QStack<int> path; while (idx.isValid()) { @@ -2948,7 +2924,7 @@ public: root->kill(); QModelIndexList newList; - for (auto path : qAsConst(oldListPath)) { + for (auto path : std::as_const(oldListPath)) { QModelIndex idx; while (!path.isEmpty()) idx = index(path.pop(), 0, idx); @@ -2967,7 +2943,7 @@ public: if (parentNode->isDead) qFatal("%s: parentNode is dead!", Q_FUNC_INFO); } - return parentNode->children.count(); + return parentNode->children.size(); } int columnCount(const QModelIndex &parent = QModelIndex()) const override { @@ -3558,7 +3534,7 @@ void tst_QTreeView::task174627_moveLeftToRoot() QSignalSpy spy(&view, &task174627_TreeView::signalCurrentChanged); QTest::keyClick(&view, Qt::Key_Left); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } void tst_QTreeView::task171902_expandWith1stColHidden() @@ -3727,7 +3703,6 @@ void tst_QTreeView::task224091_appendColumns() treeView->setModel(model); topLevel->show(); treeView->resize(50, 50); - QApplication::setActiveWindow(topLevel); QVERIFY(QTest::qWaitForWindowActive(topLevel)); QVERIFY(!treeView->verticalScrollBar()->isVisible()); @@ -4064,7 +4039,7 @@ void tst_QTreeView::task248022_changeSelection() &view, &TreeView::handleSelectionChanged); QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.visualRect(model.index(1)).center()); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), list.count()); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), list.size()); } void tst_QTreeView::task245654_changeModelAndExpandAll() @@ -4103,7 +4078,6 @@ void tst_QTreeView::doubleClickedWithSpans() view.setModel(&model); view.setFirstColumnSpanned(0, QModelIndex(), true); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QVERIFY(view.isActiveWindow()); @@ -4113,7 +4087,7 @@ void tst_QTreeView::doubleClickedWithSpans() QTest::mousePress(view.viewport(), Qt::LeftButton, {}, p); QTest::mouseDClick(view.viewport(), Qt::LeftButton, {}, p); QTest::mouseRelease(view.viewport(), Qt::LeftButton, {}, p); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); //let's click on the 2nd column p.setX(p.x() + view.header()->sectionSize(0)); @@ -4124,7 +4098,7 @@ void tst_QTreeView::doubleClickedWithSpans() QTest::mousePress(view.viewport(), Qt::LeftButton, {}, p); QTest::mouseDClick(view.viewport(), Qt::LeftButton, {}, p); QTest::mouseRelease(view.viewport(), Qt::LeftButton, {}, p); - QTRY_COMPARE(spy.count(), 2); + QTRY_COMPARE(spy.size(), 2); } void tst_QTreeView::taskQTBUG_6450_selectAllWith1stColumnHidden() @@ -4205,7 +4179,6 @@ void tst_QTreeView::keyboardNavigationWithDisabled() view.resize(200, view.visualRect(model.index(0,0)).height()*10); topLevel.show(); - QApplication::setActiveWindow(&topLevel); QVERIFY(QTest::qWaitForWindowActive(&topLevel)); QVERIFY(topLevel.isActiveWindow()); @@ -4791,7 +4764,6 @@ void tst_QTreeView::statusTip() mw.setGeometry(QRect(QPoint(QGuiApplication::primaryScreen()->geometry().center() - QPoint(250, 250)), QSize(500, 500))); mw.show(); - QApplication::setActiveWindow(&mw); QVERIFY(QTest::qWaitForWindowActive(&mw)); // Ensure it is moved away first and then moved to the relevant section QTest::mouseMove(mw.windowHandle(), view->mapTo(&mw, view->rect().bottomLeft() + QPoint(20, 20))); @@ -4842,6 +4814,9 @@ void tst_QTreeView::fetchMoreOnScroll() if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) QSKIP("Wayland: This fails. Figure out why."); + if (QGuiApplication::platformName().startsWith(QLatin1String("eglfs"), Qt::CaseInsensitive)) + QSKIP("EGLFS does not allow resizing on top level window"); + QTreeView tw; FetchMoreModel im; tw.setModel(&im); @@ -4919,6 +4894,9 @@ void tst_QTreeView::checkIntersectedRect_data() void tst_QTreeView::checkIntersectedRect() { + if (QGuiApplication::platformName().startsWith(QLatin1String("eglfs"), Qt::CaseInsensitive)) + QSKIP("EGLFS does not allow resizing on top level window"); + QFETCH(QStandardItemModel *, model); QFETCH(const QList<QModelIndex>, changedIndexes); QFETCH(bool, isEmpty); @@ -5070,6 +5048,29 @@ void tst_QTreeView::taskQTBUG_61476() QCOMPARE(lastTopLevel->checkState(), Qt::Checked); } +void tst_QTreeView::taskQTBUG_42469_crash() +{ + QTreeWidget treeWidget; + QTreeWidgetItem *itemOne = new QTreeWidgetItem(QStringList("item1")); + QTreeWidgetItem *itemTwo = new QTreeWidgetItem(QStringList("item2")); + treeWidget.addTopLevelItem(itemOne); + treeWidget.addTopLevelItem(itemTwo); + treeWidget.topLevelItem(1)->addChild(new QTreeWidgetItem(QStringList("child1"))); + + treeWidget.setAnimated(true); + QObject::connect(&treeWidget, &QTreeWidget::itemExpanded, [&](QTreeWidgetItem* p_item) { + auto tempCount = treeWidget.topLevelItemCount(); + for (int j = 0; j < tempCount; ++j) + if (treeWidget.topLevelItem(j) != p_item) { + auto temp = treeWidget.topLevelItem(j); + temp->setHidden(true); + } + }); + + treeWidget.show(); + itemTwo->setExpanded(true); +} + void tst_QTreeView::fetchUntilScreenFull() { class TreeModel : public QAbstractItemModel @@ -5118,7 +5119,7 @@ void tst_QTreeView::fetchUntilScreenFull() TreeItem* parentItem = parent.isValid() ? static_cast<TreeItem*>(parent.internalPointer()) : m_root; - return parentItem->children.count(); + return parentItem->children.size(); } int columnCount(const QModelIndex&) const override { return 2; } @@ -5197,6 +5198,10 @@ void tst_QTreeView::fetchUntilScreenFull() TreeItem* m_root; }; + if (QGuiApplication::platformName().startsWith(QLatin1String("eglfs"), Qt::CaseInsensitive)) + QSKIP("EGLFS does not allow resizing on top level window"); + + QTreeView tv; TreeModel model; tv.setModel(&model); diff --git a/tests/auto/widgets/itemviews/qtreewidget/CMakeLists.txt b/tests/auto/widgets/itemviews/qtreewidget/CMakeLists.txt index 8a9dba604e..251dbb9b79 100644 --- a/tests/auto/widgets/itemviews/qtreewidget/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qtreewidget/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qtreewidget.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtreewidget Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtreewidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qtreewidget SOURCES tst_qtreewidget.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp b/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp index 2a02afb36b..f4423831ca 100644 --- a/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp +++ b/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp @@ -1,35 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QApplication> #include <QHeaderView> #include <QLineEdit> +#include <QScopeGuard> #include <QScrollBar> #include <QSignalSpy> #include <QStyledItemDelegate> @@ -151,6 +127,9 @@ private slots: void getMimeDataWithInvalidItem(); void testVisualItemRect(); void reparentHiddenItem(); + void persistentChildIndex(); + void createPersistentOnLayoutAboutToBeChanged(); + void createPersistentOnLayoutAboutToBeChangedAutoSort(); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) void clearItemData(); #endif @@ -302,8 +281,8 @@ void tst_QTreeWidget::addTopLevelItem() while (!tops.isEmpty()) { TreeItem *ti = tops.takeFirst(); delete ti; - QCOMPARE(tree.topLevelItemCount(), tops.count()); - for (int i = 0; i < tops.count(); ++i) + QCOMPARE(tree.topLevelItemCount(), tops.size()); + for (int i = 0; i < tops.size(); ++i) QCOMPARE(tree.topLevelItem(i), tops.at(i)); } @@ -344,6 +323,7 @@ void tst_QTreeWidget::addTopLevelItem() // invalid insert { tops.clear(); + const auto sg = qScopeGuard([&] { qDeleteAll(std::exchange(tops, {})); }); for (int i = 0; i < 10; ++i) tops << new TreeItem(); int count = tree.topLevelItemCount(); @@ -398,12 +378,12 @@ void tst_QTreeWidget::currentItem() tree.setCurrentItem(item); QCOMPARE(tree.currentItem(), item); - QCOMPARE(currentItemChangedSpy.count(), 1); + QCOMPARE(currentItemChangedSpy.size(), 1); QVariantList args = currentItemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(1)), previous); - QCOMPARE(itemSelectionChangedSpy.count(), 1); + QCOMPARE(itemSelectionChangedSpy.size(), 1); itemSelectionChangedSpy.clear(); previous = item; @@ -416,15 +396,15 @@ void tst_QTreeWidget::currentItem() if (!currentItemChangedSpy.isEmpty()) { // ### we get a currentItemChanged() when what really // changed was just currentColumn(). Should it be like this? - QCOMPARE(currentItemChangedSpy.count(), 1); + QCOMPARE(currentItemChangedSpy.size(), 1); QVariantList args = currentItemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(1)), item); if (tree.selectionBehavior() == QAbstractItemView::SelectItems) { - QCOMPARE(itemSelectionChangedSpy.count(), 1); + QCOMPARE(itemSelectionChangedSpy.size(), 1); itemSelectionChangedSpy.clear(); } else { - QCOMPARE(itemSelectionChangedSpy.count(), 0); + QCOMPARE(itemSelectionChangedSpy.size(), 0); } } } @@ -484,7 +464,7 @@ void tst_QTreeWidget::editItem() QTest::keyClick(editor, Qt::Key_A); QTest::keyClick(editor, Qt::Key_Enter); QCoreApplication::processEvents(); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); QVariantList args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem *>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), col); @@ -640,7 +620,7 @@ void tst_QTreeWidget::setItemHidden2() { // From Task 78587 const QStringList hl({"ID", "Desc"}); - testWidget->setColumnCount(hl.count()); + testWidget->setColumnCount(hl.size()); testWidget->setHeaderLabels(hl); testWidget->setSortingEnabled(true); @@ -841,7 +821,7 @@ void tst_QTreeWidget::selectedItems() // check selectedItems const auto sel = testWidget->selectedItems(); - QCOMPARE(sel.count(), expectedItems.count()); + QCOMPARE(sel.size(), expectedItems.size()); for (const auto &itemPath : expectedItems) { QTreeWidgetItem *item = nullptr; for (int index : itemPath) { @@ -877,7 +857,7 @@ void tst_QTreeWidget::selectedItems() } item->setSelected(false); } - QCOMPARE(testWidget->selectedItems().count(), 0); + QCOMPARE(testWidget->selectedItems().size(), 0); } void tst_QTreeWidget::itemAssignment() @@ -1110,9 +1090,9 @@ void tst_QTreeWidget::findItems() QList<QTreeWidgetItem*> result = testWidget->findItems(pattern, Qt::MatchExactly|Qt::MatchRecursive); - QCOMPARE(result.count(), resultCount); + QCOMPARE(result.size(), resultCount); - for (int k = 0; k < result.count() && k < resultText.count(); ++k) + for (int k = 0; k < result.size() && k < resultText.size(); ++k) QCOMPARE(result.at(k)->text(column), resultText.at(k)); } @@ -1130,7 +1110,7 @@ void tst_QTreeWidget::findItemsInColumn() // Recursively search column one for 400. QList<QTreeWidgetItem*> items = testWidget->findItems("400", Qt::MatchExactly|Qt::MatchRecursive, 1); - QCOMPARE(items.count(), 1); + QCOMPARE(items.size(), 1); } void tst_QTreeWidget::sortItems_data() @@ -1197,16 +1177,16 @@ void tst_QTreeWidget::sortItems() testWidget->sortItems(column, order); QCOMPARE(testWidget->sortColumn(), column); - for (int k = 0; k < topLevelResult.count(); ++k) { + for (int k = 0; k < topLevelResult.size(); ++k) { QTreeWidgetItem *item = testWidget->topLevelItem(k); QCOMPARE(item->text(column), topLevelResult.at(k)); - for (int l = 0; l < childResult.count(); ++l) + for (int l = 0; l < childResult.size(); ++l) QCOMPARE(item->child(l)->text(column), childResult.at(l)); } - for (int m = 0; m < tops.count(); ++m) + for (int m = 0; m < tops.size(); ++m) QCOMPARE(tops.at(m).row(), expectedTopRows.at(m)); - for (int n = 0; n < children.count(); ++n) + for (int n = 0; n < children.size(); ++n) QCOMPARE(children.at(n).row(), expectedChildRows.at(n)); } @@ -1366,17 +1346,17 @@ void tst_QTreeWidget::insertTopLevelItems_data() const QStringList insert{ "baz" }; QTest::newRow("Insert at count") << initial << insert - << initial.count() << initial.count() - << initial.count() << initial.count(); + << initial.size() << initial.size() + << initial.size() << initial.size(); QTest::newRow("Insert in the middle") << initial << insert - << (initial.count() / 2) << (initial.count() / 2) - << (initial.count() / 2) << (initial.count() / 2); + << (initial.size() / 2) << (initial.size() / 2) + << (initial.size() / 2) << (initial.size() / 2); QTest::newRow("Insert less than 0") << initial << insert << -1 << -1 << -1 << -1; QTest::newRow("Insert beyond count") << initial << insert - << initial.count() + 1 << -1 - << initial.count() + 1 << -1; + << initial.size() + 1 << -1 + << initial.size() + 1 << -1; } void tst_QTreeWidget::insertTopLevelItems() @@ -1391,26 +1371,26 @@ void tst_QTreeWidget::insertTopLevelItems() { // insert the initial items QCOMPARE(testWidget->topLevelItemCount(), 0); - for (int i = 0; i < initialText.count(); ++i) { + for (int i = 0; i < initialText.size(); ++i) { QTreeWidgetItem *top = new QTreeWidgetItem(QStringList(initialText.at(i))); testWidget->addTopLevelItem(top); QCOMPARE(testWidget->indexOfTopLevelItem(top), i); } - QCOMPARE(testWidget->topLevelItemCount(), initialText.count()); + QCOMPARE(testWidget->topLevelItemCount(), initialText.size()); } { // test adding children QTreeWidgetItem *topLevel = testWidget->topLevelItem(0); - for (int i = 0; i < initialText.count(); ++i) + for (int i = 0; i < initialText.size(); ++i) topLevel->addChild(new QTreeWidgetItem(QStringList(initialText.at(i)))); - QCOMPARE(topLevel->childCount(), initialText.count()); + QCOMPARE(topLevel->childCount(), initialText.size()); } { // test adding more top level items QTreeWidgetItem *topsy = new QTreeWidgetItem(QStringList(insertText.at(0))); testWidget->insertTopLevelItem(insertTopLevelIndex, topsy); if (expectedTopLevelIndex == -1) { - QCOMPARE(testWidget->topLevelItemCount(), initialText.count()); + QCOMPARE(testWidget->topLevelItemCount(), initialText.size()); delete topsy; } else { QTreeWidgetItem *item = testWidget->topLevelItem(expectedTopLevelIndex); @@ -1426,7 +1406,7 @@ void tst_QTreeWidget::insertTopLevelItems() QTreeWidgetItem *child = new QTreeWidgetItem(QStringList(insertText.at(0))); topLevel->insertChild(insertChildIndex, child); if (expectedChildIndex == -1) { - QCOMPARE(topLevel->childCount(), initialText.count()); + QCOMPARE(topLevel->childCount(), initialText.size()); delete child; } else { QTreeWidgetItem *item = topLevel->child(expectedChildIndex); @@ -1524,7 +1504,7 @@ void tst_QTreeWidget::keyboardNavigation() if (testWidget->currentItem() != item) { QCOMPARE(testWidget->currentItem(), item->parent()); item = testWidget->currentItem(); - row = item->parent() ? item->parent()->indexOfChild(item) : testWidget->indexOfTopLevelItem(item);; + row = item->parent() ? item->parent()->indexOfChild(item) : testWidget->indexOfTopLevelItem(item); } break; case Qt::Key_Right: @@ -1539,7 +1519,7 @@ void tst_QTreeWidget::keyboardNavigation() } break; default: - QVERIFY(false); + QFAIL(qPrintable(QStringLiteral("Unexpected key: %1").arg(key))); } QTreeWidgetItem *current = testWidget->currentItem(); @@ -1612,7 +1592,7 @@ void tst_QTreeWidget::scrollToItem() void tst_QTreeWidget::setSortingEnabled() { const QStringList hl{ "ID" }; - testWidget->setColumnCount(hl.count()); + testWidget->setColumnCount(hl.size()); testWidget->setHeaderLabels(hl); QTreeWidgetItem *item1 = new QTreeWidgetItem(testWidget); @@ -1681,7 +1661,7 @@ void tst_QTreeWidget::addChild() QList<QTreeWidgetItem*> taken = item->takeChildren(); QCOMPARE(taken, children); QCOMPARE(item->childCount(), 0); - for (int i = 0; i < taken.count(); ++i) { + for (int i = 0; i < taken.size(); ++i) { QCOMPARE(taken.at(i)->parent(), nullptr); QCOMPARE(taken.at(i)->treeWidget(), nullptr); item->addChild(taken.at(i)); // re-add @@ -1691,8 +1671,8 @@ void tst_QTreeWidget::addChild() while (!children.isEmpty()) { QTreeWidgetItem *ti = children.takeFirst(); delete ti; - QCOMPARE(item->childCount(), children.count()); - for (int i = 0; i < children.count(); ++i) + QCOMPARE(item->childCount(), children.size()); + for (int i = 0; i < children.size(); ++i) QCOMPARE(item->child(i), children.at(i)); } @@ -1739,9 +1719,9 @@ void tst_QTreeWidget::setData() QSignalSpy itemChangedSpy( testWidget, &QTreeWidget::itemChanged); headerItem->setText(0, "test"); - QCOMPARE(dataChangedSpy.count(), 0); - QCOMPARE(headerDataChangedSpy.count(), 1); - QCOMPARE(itemChangedSpy.count(), 0); // no itemChanged() signal for header item + QCOMPARE(dataChangedSpy.size(), 0); + QCOMPARE(headerDataChangedSpy.size(), 1); + QCOMPARE(itemChangedSpy.size(), 0); // no itemChanged() signal for header item headerItem->setData(-1, -1, QVariant()); } @@ -1759,24 +1739,24 @@ void tst_QTreeWidget::setData() const QString text = QLatin1String("text ") + iS; item->setText(j, text); QCOMPARE(item->text(j), text); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setText(j, text); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); QPixmap pixmap(32, 32); pixmap.fill((i == 1) ? Qt::red : Qt::green); QIcon icon(pixmap); item->setIcon(j, icon); QCOMPARE(item->icon(j), icon); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setIcon(j, icon); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); @@ -1784,94 +1764,94 @@ void tst_QTreeWidget::setData() const QString toolTip = QLatin1String("toolTip ") + iS; item->setToolTip(j, toolTip); QCOMPARE(item->toolTip(j), toolTip); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setToolTip(j, toolTip); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); const QString statusTip = QLatin1String("statusTip ") + iS; item->setStatusTip(j, statusTip); QCOMPARE(item->statusTip(j), statusTip); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setStatusTip(j, statusTip); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); const QString whatsThis = QLatin1String("whatsThis ") + iS; item->setWhatsThis(j, whatsThis); QCOMPARE(item->whatsThis(j), whatsThis); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setWhatsThis(j, whatsThis); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); QSize sizeHint(64*i, 48*i); item->setSizeHint(j, sizeHint); QCOMPARE(item->sizeHint(j), sizeHint); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setSizeHint(j, sizeHint); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); QFont font; item->setFont(j, font); QCOMPARE(item->font(j), font); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setFont(j, font); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); Qt::Alignment textAlignment((i == 1) ? Qt::AlignLeft|Qt::AlignVCenter : Qt::AlignRight); item->setTextAlignment(j, textAlignment); QCOMPARE(item->textAlignment(j), int(textAlignment)); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setTextAlignment(j, textAlignment); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); QColor backgroundColor((i == 1) ? Qt::blue : Qt::yellow); item->setBackground(j, backgroundColor); QCOMPARE(item->background(j).color(), backgroundColor); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setBackground(j, backgroundColor); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); const QColor foregroundColor((i == 1) ? Qt::green : Qt::cyan); item->setForeground(j, foregroundColor); QCOMPARE(item->foreground(j), foregroundColor); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setForeground(j, foregroundColor); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); Qt::CheckState checkState((i == 1) ? Qt::PartiallyChecked : Qt::Checked); item->setCheckState(j, checkState); QCOMPARE(item->checkState(j), checkState); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setCheckState(j, checkState); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); QCOMPARE(item->text(j), text); QCOMPARE(item->icon(j), icon); @@ -1904,7 +1884,7 @@ void tst_QTreeWidget::setData() QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setBackground(j, pixmap); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); item->setData(j, Qt::DisplayRole, QVariant()); item->setData(j, Qt::DecorationRole, QVariant()); @@ -1917,7 +1897,7 @@ void tst_QTreeWidget::setData() item->setData(j, Qt::BackgroundRole, QVariant()); item->setData(j, Qt::ForegroundRole, QVariant()); item->setData(j, Qt::CheckStateRole, QVariant()); - QCOMPARE(itemChangedSpy.count(), 11); + QCOMPARE(itemChangedSpy.size(), 11); itemChangedSpy.clear(); QCOMPARE(item->data(j, Qt::DisplayRole).toString(), QString()); @@ -1980,11 +1960,11 @@ void tst_QTreeWidget::itemData() QCOMPARE(widget.currentRoles, QList<int> { Qt::UserRole + i }); } QMap<int, QVariant> flags = widget.model()->itemData(widget.model()->index(0, 0)); - QCOMPARE(flags.count(), 6); + QCOMPARE(flags.size(), 6); for (int i = 0; i < 4; ++i) QCOMPARE(flags[Qt::UserRole + i].toString(), QString::number(i + 1)); flags = widget.model()->itemData(widget.model()->index(0, 1)); - QCOMPARE(flags.count(), 0); + QCOMPARE(flags.size(), 0); item.setBackground(0, QBrush(Qt::red)); item.setForeground(0, QBrush(Qt::green)); @@ -2053,7 +2033,7 @@ void tst_QTreeWidget::setHeaderLabels() { QStringList list = QString("a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z").split(QLatin1Char(',')); testWidget->setHeaderLabels(list); - QCOMPARE(testWidget->header()->count(), list.count()); + QCOMPARE(testWidget->header()->count(), list.size()); } void tst_QTreeWidget::setHeaderItem() @@ -2283,11 +2263,11 @@ void tst_QTreeWidget::insertItemsWithSorting() w.addTopLevelItem(new QTreeWidgetItem({ txt })); break; } - QCOMPARE(w.topLevelItemCount(), expectedItems.count()); + QCOMPARE(w.topLevelItemCount(), expectedItems.size()); for (int i = 0; i < w.topLevelItemCount(); ++i) QCOMPARE(w.topLevelItem(i)->text(0), expectedItems.at(i)); - for (int k = 0; k < persistent.count(); ++k) + for (int k = 0; k < persistent.size(); ++k) QCOMPARE(persistent.at(k).row(), expectedRows.at(k)); } } @@ -2328,13 +2308,13 @@ void tst_QTreeWidget::insertExpandedItemsWithSorting() QTreeWidgetItem *child = new QTreeWidgetItem(parent, {text}); items << child; } - QCOMPARE(parent->childCount(), childTexts.count()); + QCOMPARE(parent->childCount(), childTexts.size()); QVERIFY(parent->isExpanded()); } - QCOMPARE(tree.model()->rowCount(), parentTexts.count()); + QCOMPARE(tree.model()->rowCount(), parentTexts.size()); // verify that the items are still expanded - for (const QTreeWidgetItem *item : qAsConst(items)) { + for (const QTreeWidgetItem *item : std::as_const(items)) { if (item->childCount() > 0) QVERIFY(item->isExpanded()); QModelIndex idx = tree.indexFromItem(item); @@ -2352,10 +2332,10 @@ void tst_QTreeWidget::insertExpandedItemsWithSorting() PersistentModelIndexVec children; for (int i = 0; i < model->rowCount(parents.constFirst()); ++i) children.push_back(model->index(i, 0, parents.constFirst())); - for (int i = 0; i < parentResult.count(); ++i) { + for (int i = 0; i < parentResult.size(); ++i) { QTreeWidgetItem *item = tree.topLevelItem(i); QCOMPARE(item->text(0), parentResult.at(i)); - for (int j = 0; j < childResult.count(); ++j) + for (int j = 0; j < childResult.size(); ++j) QCOMPARE(item->child(j)->text(0), childResult.at(j)); } } @@ -2447,19 +2427,19 @@ void tst_QTreeWidget::changeDataWithSorting() QTreeWidgetItem *item = w.topLevelItem(itemIndex); item->setText(0, newValue); - for (int i = 0; i < expectedItems.count(); ++i) { + for (int i = 0; i < expectedItems.size(); ++i) { QCOMPARE(w.topLevelItem(i)->text(0), expectedItems.at(i)); - for (const QPersistentModelIndex &p : qAsConst(persistent)) { + for (const QPersistentModelIndex &p : std::as_const(persistent)) { if (p.row() == i) // the same toplevel row QCOMPARE(p.internalPointer(), static_cast<void *>(w.topLevelItem(i))); } } - for (int k = 0; k < persistent.count(); ++k) + for (int k = 0; k < persistent.size(); ++k) QCOMPARE(persistent.at(k).row(), expectedRows.at(k)); - QCOMPARE(dataChangedSpy.count(), 1); - QCOMPARE(layoutChangedSpy.count(), reorderingExpected ? 1 : 0); + QCOMPARE(dataChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), reorderingExpected ? 1 : 0); } void tst_QTreeWidget::changeDataWithStableSorting_data() @@ -2616,19 +2596,19 @@ void tst_QTreeWidget::changeDataWithStableSorting() item->setText(0, newValue); if (forceChange) item->emitDataChanged(); - for (int i = 0; i < expectedItems.count(); ++i) { + for (int i = 0; i < expectedItems.size(); ++i) { QCOMPARE(w.topLevelItem(i)->text(0), expectedItems.at(i)); - for (const QPersistentModelIndex &p : qAsConst(persistent)) { + for (const QPersistentModelIndex &p : std::as_const(persistent)) { if (p.row() == i) // the same toplevel row QCOMPARE(p.internalPointer(), static_cast<void *>(w.topLevelItem(i))); } } - for (int k = 0; k < persistent.count(); ++k) + for (int k = 0; k < persistent.size(); ++k) QCOMPARE(persistent.at(k).row(), expectedRows.at(k)); - QCOMPARE(dataChangedSpy.count(), 1); - QCOMPARE(layoutChangedSpy.count(), reorderingExpected ? 1 : 0); + QCOMPARE(dataChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), reorderingExpected ? 1 : 0); } void tst_QTreeWidget::sizeHint_data() @@ -2647,6 +2627,12 @@ void tst_QTreeWidget::sizeHint() QFETCH(Qt::ScrollBarPolicy, scrollBarPolicy); QFETCH(QSize, viewSize); + const QString defaultStyle = QApplication::style()->name(); + QApplication::setStyle("fusion"); + const auto resetStyle = qScopeGuard([defaultStyle]{ + QApplication::setStyle(defaultStyle); + }); + QTreeWidget view; view.setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); view.setVerticalScrollBarPolicy(scrollBarPolicy); @@ -2664,6 +2650,7 @@ void tst_QTreeWidget::sizeHint() QTRY_COMPARE(view.size(), viewSize); } + QApplication::processEvents(); // execute delayed layouts auto sizeHint = view.sizeHint(); view.hide(); QCOMPARE(view.sizeHint(), sizeHint); @@ -2734,8 +2721,8 @@ void tst_QTreeWidget::sortedIndexOfChild() tw.sortItems(0, sortOrder); tw.expandAll(); - QCOMPARE(itms.count(), expectedIndexes.count()); - for (int j = 0; j < expectedIndexes.count(); ++j) + QCOMPARE(itms.size(), expectedIndexes.size()); + for (int j = 0; j < expectedIndexes.size(); ++j) QCOMPARE(top->indexOfChild(itms.at(j)), expectedIndexes.at(j)); } @@ -2760,8 +2747,8 @@ void tst_QTreeWidget::expandAndCallapse() tw.collapseItem(top); tw.collapseItem(top); - QCOMPARE(spy0.count(), 3); - QCOMPARE(spy1.count(), 2); + QCOMPARE(spy0.size(), 3); + QCOMPARE(spy1.size(), 2); } void tst_QTreeWidget::setDisabled() @@ -2874,13 +2861,13 @@ void tst_QTreeWidget::removeSelectedItem() QItemSelectionModel *selModel = w->selectionModel(); QCOMPARE(selModel->hasSelection(), true); - QCOMPARE(selModel->selectedRows().count(), 1); + QCOMPARE(selModel->selectedRows().size(), 1); const QScopedPointer<QTreeWidgetItem> taken(w->takeTopLevelItem(2)); QCOMPARE(taken->text(0), QLatin1String("C")); QCOMPARE(selModel->hasSelection(), false); - QCOMPARE(selModel->selectedRows().count(), 0); + QCOMPARE(selModel->selectedRows().size(), 0); QItemSelection sel = selModel->selection(); QCOMPARE(selModel->isSelected(w->model()->index(0,0)), false); } @@ -2991,7 +2978,7 @@ protected: auto newItem = new QTreeWidgetItem({QString::number(i++)}); m_list.append(newItem); insertTopLevelItem(0, newItem); - while (m_list.count() > 10) + while (m_list.size() > 10) delete m_list.takeFirst(); } QTreeWidget::timerEvent(event); @@ -3305,7 +3292,7 @@ void tst_QTreeWidget::emitDataChanged() auto item = new PublicTreeItem; tree.insertTopLevelItem(0, item); item->emitDataChanged(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } void tst_QTreeWidget::setCurrentItemExpandsParent() @@ -3587,6 +3574,21 @@ void tst_QTreeWidget::reparentHiddenItem() QVERIFY(grandChild->isHidden()); } +void tst_QTreeWidget::persistentChildIndex() // QTBUG-90030 +{ + QTreeWidget tree; + QTreeWidgetItem *toplevel = new QTreeWidgetItem(QStringList{QStringLiteral("toplevel")}); + tree.addTopLevelItem(toplevel); + QModelIndex firstIndex = tree.model()->index(0, 0); + QTreeWidgetItem *child1 = new QTreeWidgetItem(QStringList{QStringLiteral("child1")}); + QTreeWidgetItem *child2 = new QTreeWidgetItem(QStringList{QStringLiteral("child2")}); + toplevel->addChildren({child1, child2}); + QPersistentModelIndex persistentIdx = tree.model()->index(1, 0, firstIndex); + QCOMPARE(persistentIdx.data().toString(), QStringLiteral("child2")); + tree.model()->removeRows(0, 1, firstIndex); + QCOMPARE(persistentIdx.data().toString(), QStringLiteral("child2")); +} + #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) void tst_QTreeWidget::clearItemData() { @@ -3625,5 +3627,72 @@ void tst_QTreeWidget::clearItemData() } #endif +void tst_QTreeWidget::createPersistentOnLayoutAboutToBeChanged() // QTBUG-93466 +{ + QTreeWidget widget; + QCOMPARE(widget.model()->columnCount(), 1); + widget.model()->insertRows(0, 3); + for (int row = 0; row < 3; ++row) + widget.model()->setData(widget.model()->index(row, 0), row); + QList<QPersistentModelIndex> idxList; + QSignalSpy layoutAboutToBeChangedSpy(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged); + QSignalSpy layoutChangedSpy(widget.model(), &QAbstractItemModel::layoutChanged); + connect(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged, this, [&idxList, &widget](){ + idxList.clear(); + for (int row = 0; row < 3; ++row) + idxList << QPersistentModelIndex(widget.model()->index(row, 0)); + }); + connect(widget.model(), &QAbstractItemModel::layoutChanged, this, [&idxList](){ + QCOMPARE(idxList.size(), 3); + QCOMPARE(idxList.at(0).row(), 1); + QCOMPARE(idxList.at(0).column(), 0); + QCOMPARE(idxList.at(0).data().toInt(), 0); + QCOMPARE(idxList.at(1).row(), 0); + QCOMPARE(idxList.at(1).column(), 0); + QCOMPARE(idxList.at(1).data().toInt(), -1); + QCOMPARE(idxList.at(2).row(), 2); + QCOMPARE(idxList.at(2).column(), 0); + QCOMPARE(idxList.at(2).data().toInt(), 2); + }); + widget.model()->setData(widget.model()->index(1, 0), -1); + widget.model()->sort(0); + QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), 1); +} + +void tst_QTreeWidget::createPersistentOnLayoutAboutToBeChangedAutoSort() // QTBUG-93466 +{ + QTreeWidget widget; + QCOMPARE(widget.model()->columnCount(), 1); + widget.model()->insertRows(0, 3); + for (int row = 0; row < 3; ++row) + widget.model()->setData(widget.model()->index(row, 0), row); + widget.sortByColumn(0, Qt::AscendingOrder); + widget.setSortingEnabled(true); + QList<QPersistentModelIndex> idxList; + QSignalSpy layoutAboutToBeChangedSpy(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged); + QSignalSpy layoutChangedSpy(widget.model(), &QAbstractItemModel::layoutChanged); + connect(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged, this, [&idxList, &widget](){ + idxList.clear(); + for (int row = 0; row < 3; ++row) + idxList << QPersistentModelIndex(widget.model()->index(row, 0)); + }); + connect(widget.model(), &QAbstractItemModel::layoutChanged, this, [&idxList](){ + QCOMPARE(idxList.size(), 3); + QCOMPARE(idxList.at(0).row(), 1); + QCOMPARE(idxList.at(0).column(), 0); + QCOMPARE(idxList.at(0).data().toInt(), 0); + QCOMPARE(idxList.at(1).row(), 0); + QCOMPARE(idxList.at(1).column(), 0); + QCOMPARE(idxList.at(1).data().toInt(), -1); + QCOMPARE(idxList.at(2).row(), 2); + QCOMPARE(idxList.at(2).column(), 0); + QCOMPARE(idxList.at(2).data().toInt(), 2); + }); + widget.model()->setData(widget.model()->index(1, 0), -1); + QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), 1); +} + QTEST_MAIN(tst_QTreeWidget) #include "tst_qtreewidget.moc" diff --git a/tests/auto/widgets/itemviews/qtreewidgetitemiterator/CMakeLists.txt b/tests/auto/widgets/itemviews/qtreewidgetitemiterator/CMakeLists.txt index c62b0d4c6f..d82eaadced 100644 --- a/tests/auto/widgets/itemviews/qtreewidgetitemiterator/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qtreewidgetitemiterator/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qtreewidgetitemiterator.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtreewidgetitemiterator Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtreewidgetitemiterator LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qtreewidgetitemiterator SOURCES tst_qtreewidgetitemiterator.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/itemviews/qtreewidgetitemiterator/tst_qtreewidgetitemiterator.cpp b/tests/auto/widgets/itemviews/qtreewidgetitemiterator/tst_qtreewidgetitemiterator.cpp index 68d149fc6e..a650eb229e 100644 --- a/tests/auto/widgets/itemviews/qtreewidgetitemiterator/tst_qtreewidgetitemiterator.cpp +++ b/tests/auto/widgets/itemviews/qtreewidgetitemiterator/tst_qtreewidgetitemiterator.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTreeWidget> @@ -891,7 +866,7 @@ void tst_QTreeWidgetItemIterator::iteratorflags() QTreeWidgetItemIterator it(testWidget, iteratorflags); it += start; int iMatch = 0; - while (*it && iMatch < matches.count()) { + while (*it && iMatch < matches.size()) { QTreeWidgetItem *item = *it; QCOMPARE(item->text(0), matches[iMatch]); ++it; diff --git a/tests/auto/widgets/kernel/CMakeLists.txt b/tests/auto/widgets/kernel/CMakeLists.txt index c94c48aaa4..2d4880ea3c 100644 --- a/tests/auto/widgets/kernel/CMakeLists.txt +++ b/tests/auto/widgets/kernel/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from kernel.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause add_subdirectory(qapplication) add_subdirectory(qboxlayout) @@ -9,16 +10,14 @@ add_subdirectory(qstackedlayout) add_subdirectory(qtooltip) add_subdirectory(qwidget_window) add_subdirectory(qwidgetmetatype) +add_subdirectory(qwidgetrepaintmanager) add_subdirectory(qwidgetsvariant) add_subdirectory(qwindowcontainer) add_subdirectory(qsizepolicy) if(NOT APPLE) add_subdirectory(qgesturerecognizer) endif() -# QTBUG-87668 # special case -if(NOT ANDROID) - add_subdirectory(qwidget) -endif() +add_subdirectory(qwidget) if(QT_FEATURE_shortcut) add_subdirectory(qshortcut) endif() diff --git a/tests/auto/widgets/kernel/qaction/CMakeLists.txt b/tests/auto/widgets/kernel/qaction/CMakeLists.txt index dd70d5448f..9d1985da0b 100644 --- a/tests/auto/widgets/kernel/qaction/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qaction/CMakeLists.txt @@ -1,15 +1,23 @@ -# Generated from qaction.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qaction Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qaction LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qaction SOURCES tst_qaction.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/kernel/qaction/tst_qaction.cpp b/tests/auto/widgets/kernel/qaction/tst_qaction.cpp index 63d49e1216..2820fd710b 100644 --- a/tests/auto/widgets/kernel/qaction/tst_qaction.cpp +++ b/tests/auto/widgets/kernel/qaction/tst_qaction.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QDialog> #include <QMainWindow> @@ -36,10 +11,14 @@ #include <qaction.h> #include <qactiongroup.h> #include <qmenu.h> +#include <qmenubar.h> +#include <qtoolbar.h> #include <qpa/qplatformtheme.h> #include <qpa/qplatformintegration.h> #include <private/qguiapplication_p.h> +#include <QtWidgets/private/qapplication_p.h> + class tst_QAction : public QObject { Q_OBJECT @@ -65,6 +44,8 @@ private slots: void disableShortcutsWithBlockedWidgets_data(); void disableShortcutsWithBlockedWidgets(); void shortcutFromKeyEvent(); // QTBUG-48325 + void disableShortcutInMenuAction_data(); + void disableShortcutInMenuAction(); #endif private: @@ -132,7 +113,6 @@ void tst_QAction::actionEvent() // add action MyWidget testWidget(this); testWidget.show(); - QApplication::setActiveWindow(&testWidget); testWidget.addAction(&a); qApp->processEvents(); @@ -162,7 +142,7 @@ void tst_QAction::alternateShortcuts() MyWidget testWidget(this); testWidget.show(); - QApplication::setActiveWindow(&testWidget); + QApplicationPrivate::setActiveWindow(&testWidget); { QAction act(&testWidget); @@ -174,11 +154,11 @@ void tst_QAction::alternateShortcuts() act.setAutoRepeat(true); QTest::keyClick(&testWidget, Qt::Key_A, Qt::ControlModifier); - QCOMPARE(spy.count(), 1); //act should have been triggered + QCOMPARE(spy.size(), 1); //act should have been triggered act.setAutoRepeat(false); QTest::keyClick(&testWidget, Qt::Key_A, Qt::ControlModifier); - QCOMPARE(spy.count(), 2); //act should have been triggered a 2nd time + QCOMPARE(spy.size(), 2); //act should have been triggered a 2nd time //end of the scope of the action, it will be destroyed and removed from wid //This action should also unregister its shortcuts @@ -193,7 +173,7 @@ void tst_QAction::keysequence() { MyWidget testWidget(this); testWidget.show(); - QApplication::setActiveWindow(&testWidget); + QApplicationPrivate::setActiveWindow(&testWidget); { QAction act(&testWidget); @@ -208,12 +188,12 @@ void tst_QAction::keysequence() act.setAutoRepeat(true); QTest::keySequence(&testWidget, ks); QCoreApplication::processEvents(); - QCOMPARE(spy.count(), 1); // act should have been triggered + QCOMPARE(spy.size(), 1); // act should have been triggered act.setAutoRepeat(false); QTest::keySequence(&testWidget, ks); QCoreApplication::processEvents(); - QCOMPARE(spy.count(), 2); //act should have been triggered a 2nd time + QCOMPARE(spy.size(), 2); //act should have been triggered a 2nd time // end of the scope of the action, it will be destroyed and removed from widget // This action should also unregister its shortcuts @@ -227,7 +207,7 @@ void tst_QAction::enabledVisibleInteraction() { MyWidget testWidget(this); testWidget.show(); - QApplication::setActiveWindow(&testWidget); + QApplicationPrivate::setActiveWindow(&testWidget); QAction act(nullptr); // check defaults @@ -249,15 +229,15 @@ void tst_QAction::enabledVisibleInteraction() act.setEnabled(true); act.setVisible(false); QTest::keyClick(&testWidget, Qt::Key_T, Qt::ControlModifier); - QCOMPARE(spy.count(), 0); //act is not visible, so don't trigger + QCOMPARE(spy.size(), 0); //act is not visible, so don't trigger act.setVisible(false); act.setEnabled(true); QTest::keyClick(&testWidget, Qt::Key_T, Qt::ControlModifier); - QCOMPARE(spy.count(), 0); //act is not visible, so don't trigger + QCOMPARE(spy.size(), 0); //act is not visible, so don't trigger act.setVisible(true); act.setEnabled(true); QTest::keyClick(&testWidget, Qt::Key_T, Qt::ControlModifier); - QCOMPARE(spy.count(), 1); //act is visible and enabled, so trigger + QCOMPARE(spy.size(), 1); //act is visible and enabled, so trigger } #endif // QT_CONFIG(shortcut) @@ -275,12 +255,12 @@ void tst_QAction::task229128TriggeredSignalWhenInActiongroup() QSignalSpy actionSpy(checkedAction, QOverload<bool>::of(&QAction::triggered)); QSignalSpy actionGroupSpy(&ag, QOverload<QAction*>::of(&QActionGroup::triggered)); - QCOMPARE(actionGroupSpy.count(), 0); - QCOMPARE(actionSpy.count(), 0); + QCOMPARE(actionGroupSpy.size(), 0); + QCOMPARE(actionSpy.size(), 0); checkedAction->trigger(); // check that both the group and the action have emitted the signal - QCOMPARE(actionGroupSpy.count(), 1); - QCOMPARE(actionSpy.count(), 1); + QCOMPARE(actionGroupSpy.size(), 1); + QCOMPARE(actionSpy.size(), 1); } #if QT_CONFIG(shortcut) @@ -292,7 +272,6 @@ void tst_QAction::repeat() MyWidget testWidget(this); testWidget.show(); - QApplication::setActiveWindow(&testWidget); QVERIFY(QTest::qWaitForWindowActive(&testWidget)); QAction act(&testWidget); @@ -303,7 +282,7 @@ void tst_QAction::repeat() act.setAutoRepeat(true); QTest::keyPress(&testWidget, Qt::Key_F); QTest::keyRelease(&testWidget, Qt::Key_F); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); QTest::keyPress(&testWidget, Qt::Key_F); @@ -311,7 +290,7 @@ void tst_QAction::repeat() QTest::simulateEvent(&testWidget, true, Qt::Key_F, Qt::NoModifier, QString("f"), true); QTest::simulateEvent(&testWidget, true, Qt::Key_F, Qt::NoModifier, QString("f"), true); QTest::keyRelease(&testWidget, Qt::Key_F); - QCOMPARE(spy.count(), 3); + QCOMPARE(spy.size(), 3); spy.clear(); act.setAutoRepeat(false); @@ -319,14 +298,14 @@ void tst_QAction::repeat() QTest::simulateEvent(&testWidget, true, Qt::Key_F, Qt::NoModifier, QString("f"), true); QTest::simulateEvent(&testWidget, true, Qt::Key_F, Qt::NoModifier, QString("f"), true); QTest::keyRelease(&testWidget, Qt::Key_F); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); act.setAutoRepeat(true); QTest::keyPress(&testWidget, Qt::Key_F); QTest::simulateEvent(&testWidget, true, Qt::Key_F, Qt::NoModifier, QString("f"), true); QTest::keyRelease(&testWidget, Qt::Key_F); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); } void tst_QAction::disableShortcutsWithBlockedWidgets_data() @@ -371,12 +350,11 @@ void tst_QAction::disableShortcutsWithBlockedWidgets() dialog.show(); QVERIFY(QTest::qWaitForWindowExposed(&dialog)); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); QSignalSpy spy(&action, &QAction::triggered); QTest::keyPress(&window, Qt::Key_1); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } class ShortcutOverrideWidget : public QWidget @@ -414,10 +392,83 @@ void tst_QAction::shortcutFromKeyEvent() // shortcut route for us QKeyEvent e(QEvent::KeyPress, Qt::Key_1, Qt::NoModifier); QApplication::sendEvent(&testWidget, &e); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(testWidget.shortcutOverrideCount, 1); } +/* + Ignore actions in menus whose menu action has been hidden or disabled. + The menu entry will not be in the menu bar or parent menu, so the action + is not reachable through interactive means. QTBUG-25743 +*/ +void tst_QAction::disableShortcutInMenuAction_data() +{ + QTest::addColumn<QByteArray>("property"); + + QTest::addRow("visible") << QByteArray("visible"); + QTest::addRow("enabled") << QByteArray("enabled"); +} + +void tst_QAction::disableShortcutInMenuAction() +{ + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + + QFETCH(QByteArray, property); + + QMainWindow mw; + QMenu *testMenu = mw.menuBar()->addMenu("Test"); + QAction *testAction = testMenu->addAction("Test Action"); + testAction->setShortcut(Qt::ControlModifier | Qt::Key_A); + QToolBar *toolBar = new QToolBar; + mw.addToolBar(toolBar); + + mw.show(); + QVERIFY(QTest::qWaitForWindowActive(&mw)); + + int expectedTriggerCount = 0; + QSignalSpy spy(testAction, &QAction::triggered); + + QKeyEvent event(QEvent::KeyPress, Qt::Key_A, Qt::ControlModifier); + QApplication::sendEvent(&mw, &event); + QCOMPARE(spy.size(), ++expectedTriggerCount); + + testMenu->menuAction()->setProperty(property, false); + QApplication::sendEvent(&mw, &event); + QCOMPARE(spy.size(), expectedTriggerCount); + + testMenu->menuAction()->setProperty(property, true); + QApplication::sendEvent(&mw, &event); + QCOMPARE(spy.size(), ++expectedTriggerCount); + + // If the action lives somewhere else, then keep firing even + // if the menu has been hidden or disabled. + toolBar->addAction(testAction); + QApplication::sendEvent(&mw, &event); + QCOMPARE(spy.size(), ++expectedTriggerCount); + + testMenu->menuAction()->setProperty(property, false); + QApplication::sendEvent(&mw, &event); + QCOMPARE(spy.size(), ++expectedTriggerCount); + + // unless all other widgets in which the action lives have + // been hidden... + toolBar->hide(); + QApplication::sendEvent(&mw, &event); + QCOMPARE(spy.size(), expectedTriggerCount); + + // ... or disabled + toolBar->show(); + toolBar->setEnabled(false); + QApplication::sendEvent(&mw, &event); + QCOMPARE(spy.size(), expectedTriggerCount); + + // back to normal + toolBar->setEnabled(true); + QApplication::sendEvent(&mw, &event); + QCOMPARE(spy.size(), ++expectedTriggerCount); +} + #endif // QT_CONFIG(shortcut) QTEST_MAIN(tst_QAction) diff --git a/tests/auto/widgets/kernel/qactiongroup/CMakeLists.txt b/tests/auto/widgets/kernel/qactiongroup/CMakeLists.txt index 927802103c..e26ee75bc1 100644 --- a/tests/auto/widgets/kernel/qactiongroup/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qactiongroup/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qactiongroup.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qactiongroup Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qactiongroup LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qactiongroup SOURCES tst_qactiongroup.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/kernel/qactiongroup/tst_qactiongroup.cpp b/tests/auto/widgets/kernel/qactiongroup/tst_qactiongroup.cpp index 7462fd8bbf..0d42340bfb 100644 --- a/tests/auto/widgets/kernel/qactiongroup/tst_qactiongroup.cpp +++ b/tests/auto/widgets/kernel/qactiongroup/tst_qactiongroup.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/kernel/qapplication/BLACKLIST b/tests/auto/widgets/kernel/qapplication/BLACKLIST index cce10602e4..c68c7d6b14 100644 --- a/tests/auto/widgets/kernel/qapplication/BLACKLIST +++ b/tests/auto/widgets/kernel/qapplication/BLACKLIST @@ -1,5 +1,3 @@ -[sendEventsOnProcessEvents] -ubuntu-20.04 [touchEventPropagation] # QTBUG-66745 opensuse-leap diff --git a/tests/auto/widgets/kernel/qapplication/CMakeLists.txt b/tests/auto/widgets/kernel/qapplication/CMakeLists.txt index 3bb7e31691..524db06560 100644 --- a/tests/auto/widgets/kernel/qapplication/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qapplication/CMakeLists.txt @@ -1,5 +1,17 @@ -# Generated from qapplication.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qapplication LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() add_subdirectory(desktopsettingsaware) add_subdirectory(modal) add_subdirectory(test) + +add_dependencies(tst_qapplication + desktopsettingsaware_helper + modal_helper +) diff --git a/tests/auto/widgets/kernel/qapplication/desktopsettingsaware/CMakeLists.txt b/tests/auto/widgets/kernel/qapplication/desktopsettingsaware/CMakeLists.txt index a391d5b13d..f094a451d2 100644 --- a/tests/auto/widgets/kernel/qapplication/desktopsettingsaware/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qapplication/desktopsettingsaware/CMakeLists.txt @@ -1,15 +1,16 @@ -# Generated from desktopsettingsaware.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## desktopsettingsaware Binary: ##################################################################### -qt_internal_add_executable(desktopsettingsaware_helper # special case +qt_internal_add_executable(desktopsettingsaware_helper SOURCES main.cpp - OUTPUT_DIRECTORY # special case - ${CMAKE_CURRENT_BINARY_DIR}/.. # special case - PUBLIC_LIBRARIES + OUTPUT_DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR}/.. + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/kernel/qapplication/desktopsettingsaware/main.cpp b/tests/auto/widgets/kernel/qapplication/desktopsettingsaware/main.cpp index 5e86bcb529..1bf3eef55c 100644 --- a/tests/auto/widgets/kernel/qapplication/desktopsettingsaware/main.cpp +++ b/tests/auto/widgets/kernel/qapplication/desktopsettingsaware/main.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QApplication> diff --git a/tests/auto/widgets/kernel/qapplication/modal/CMakeLists.txt b/tests/auto/widgets/kernel/qapplication/modal/CMakeLists.txt index 95c429f936..79c5660650 100644 --- a/tests/auto/widgets/kernel/qapplication/modal/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qapplication/modal/CMakeLists.txt @@ -1,16 +1,17 @@ -# Generated from modal.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## modal Binary: ##################################################################### -qt_internal_add_executable(modal_helper # special case +qt_internal_add_executable(modal_helper SOURCES base.cpp base.h main.cpp - OUTPUT_DIRECTORY # special case - ${CMAKE_CURRENT_BINARY_DIR}/.. # special case - PUBLIC_LIBRARIES + OUTPUT_DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR}/.. + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/kernel/qapplication/modal/base.cpp b/tests/auto/widgets/kernel/qapplication/modal/base.cpp index 249d402f2e..49a90723dd 100644 --- a/tests/auto/widgets/kernel/qapplication/modal/base.cpp +++ b/tests/auto/widgets/kernel/qapplication/modal/base.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "base.h" diff --git a/tests/auto/widgets/kernel/qapplication/modal/base.h b/tests/auto/widgets/kernel/qapplication/modal/base.h index 153d9ca420..168da92f97 100644 --- a/tests/auto/widgets/kernel/qapplication/modal/base.h +++ b/tests/auto/widgets/kernel/qapplication/modal/base.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef BASE_H #define BASE_H diff --git a/tests/auto/widgets/kernel/qapplication/modal/main.cpp b/tests/auto/widgets/kernel/qapplication/modal/main.cpp index 9dcb6732fa..3f3496834d 100644 --- a/tests/auto/widgets/kernel/qapplication/modal/main.cpp +++ b/tests/auto/widgets/kernel/qapplication/modal/main.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QApplication> #include "base.h" diff --git a/tests/auto/widgets/kernel/qapplication/test/CMakeLists.txt b/tests/auto/widgets/kernel/qapplication/test/CMakeLists.txt index ad5209f9bf..fc80b8af7e 100644 --- a/tests/auto/widgets/kernel/qapplication/test/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qapplication/test/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from test.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## test Test: @@ -8,35 +9,23 @@ list(APPEND test_data "../tmp/README") list(APPEND test_data "../modal") -qt_internal_add_test(tst_qapplication # special case +qt_internal_add_test(tst_qapplication SOURCES ../tst_qapplication.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate Qt::Widgets Qt::WidgetsPrivate TESTDATA ${test_data} - OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." # special case + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." ) ## Scopes: ##################################################################### -qt_internal_extend_target(tst_qapplication CONDITION builtin_testdata # special case +qt_internal_extend_target(tst_qapplication CONDITION builtin_testdata DEFINES BUILTIN_TESTDATA ) - -#### Keys ignored in scope 3:.:.:test.pro:NOT ANDROID: -# SUBPROGRAMS = "desktopsettingsaware" "modal" - -#### Keys ignored in scope 6:.:.:test.pro:NOT ANDROID: -# TEST_HELPER_INSTALLS = "../debug/helper" - -#### Keys ignored in scope 8:.:.:test.pro:NOT ANDROID: -# TEST_HELPER_INSTALLS = "../release/helper" - -#### Keys ignored in scope 10:.:.:test.pro:NOT ANDROID: -# TEST_HELPER_INSTALLS = "../helper" diff --git a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp index 8216cfbab0..2dec3f30e9 100644 --- a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp +++ b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #define QT_STATICPLUGIN #include <QtWidgets/qstyleplugin.h> @@ -119,12 +94,15 @@ private slots: void libraryPaths_qt_plugin_path_2(); #endif +#ifdef QT_BUILD_INTERNAL void sendPostedEvents(); +#endif // ifdef QT_BUILD_INTERNAL void thread(); void desktopSettingsAware(); void setActiveWindow(); + void activateDeactivateEvent(); void focusWidget(); void focusChanged(); @@ -152,6 +130,7 @@ private slots: void wheelEventPropagation(); void qtbug_12673(); + void qtbug_103611(); void noQuitOnHide(); void globalStaticObjectDestruction(); // run this last @@ -187,6 +166,21 @@ void tst_QApplication::sendEventsOnProcessEvents() QCoreApplication::postEvent(&app, new QEvent(QEvent::Type(QEvent::User + 1))); QCoreApplication::processEvents(); + +#ifdef Q_OS_LINUX + if ((QSysInfo::productType() == "rhel" && QSysInfo::productVersion().startsWith(u'9')) + || (QSysInfo::productType() == "ubuntu" && QSysInfo::productVersion().startsWith(u'2'))) + { + QFile f("/proc/self/maps"); + QVERIFY(f.open(QIODevice::ReadOnly)); + + QByteArray libs = f.readAll(); + if (libs.contains("libqgtk3.") || libs.contains("libqgtk3TestInfix.")) { + QEXPECT_FAIL("", "Fails if qgtk3 (Glib) is loaded, see QTBUG-87137", Abort); + } + } +#endif + QVERIFY(spy.recordedEvents.contains(QEvent::User + 1)); } @@ -239,10 +233,12 @@ void tst_QApplication::staticSetup() EventWatcher() { qApp->installEventFilter(this); +#if QT_DEPRECATED_SINCE(6, 0) QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED QObject::connect(qApp, &QApplication::paletteChanged, [&]{ ++palette_changed; }); QObject::connect(qApp, &QApplication::fontChanged, [&]{ ++font_changed; }); QT_WARNING_POP +#endif } protected: @@ -272,8 +268,13 @@ QT_WARNING_POP font.setBold(!font.bold()); qApp->setFont(font); QApplication::processEvents(); +#if QT_DEPRECATED_SINCE(6, 0) QCOMPARE(watcher.palette_changed, 2); QCOMPARE(watcher.font_changed, 2); +#else + QCOMPARE(watcher.palette_changed, 1); + QCOMPARE(watcher.font_changed, 1); +#endif } @@ -310,10 +311,8 @@ void tst_QApplication::alert() QApplication::alert(&widget, -1); QApplication::alert(&widget, 250); widget2.activateWindow(); - QApplication::setActiveWindow(&widget2); QApplication::alert(&widget, 0); widget.activateWindow(); - QApplication::setActiveWindow(&widget); QApplication::alert(&widget, 200); } @@ -502,7 +501,7 @@ static char **QString2cstrings(const QString &args) static QByteArrayList cache; const auto &list = QStringView{ args }.split(' '); - auto argarray = new char*[list.count() + 1]; + auto argarray = new char*[list.size() + 1]; int i = 0; for (; i < list.size(); ++i ) { @@ -591,7 +590,7 @@ void tst_QApplication::lastWindowClosed() QTimer::singleShot(1000, dialog.data(), &QDialog::accept); dialog->exec(); QVERIFY(dialog); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QPointer<CloseWidget>widget = new CloseWidget; widget->setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("CloseWidget")); @@ -600,7 +599,7 @@ void tst_QApplication::lastWindowClosed() QObject::connect(&app, &QGuiApplication::lastWindowClosed, widget.data(), &QObject::deleteLater); QCoreApplication::exec(); QVERIFY(!widget); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); delete dialog; @@ -618,7 +617,7 @@ void tst_QApplication::lastWindowClosed() QTimer::singleShot(1000, &app, &QApplication::closeAllWindows); QCoreApplication::exec(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } class QuitOnLastWindowClosedDialog : public QDialog @@ -651,8 +650,8 @@ public slots: other.exec(); // verify that the eventloop ran and let the timer fire - QCOMPARE(spy.count(), 1); - QCOMPARE(appSpy.count(), 1); + QCOMPARE(spy.size(), 1); + QCOMPARE(appSpy.size(), 1); } private: @@ -677,7 +676,7 @@ public slots: timer1.setSingleShot(true); timer1.start(1000); dialog.exec(); - QCOMPARE(spy1.count(), 1); + QCOMPARE(spy1.size(), 1); show(); } @@ -698,7 +697,7 @@ void tst_QApplication::quitOnLastWindowClosed() QCoreApplication::exec(); // lastWindowClosed() signal should only be sent after the last dialog is closed - QCOMPARE(appSpy.count(), 2); + QCOMPARE(appSpy.size(), 2); } { int argc = 0; @@ -713,8 +712,8 @@ void tst_QApplication::quitOnLastWindowClosed() timer1.setSingleShot(true); timer1.start(1000); dialog.exec(); - QCOMPARE(spy1.count(), 1); - QCOMPARE(appSpy.count(), 0); + QCOMPARE(spy1.size(), 1); + QCOMPARE(appSpy.size(), 0); QTimer timer2; connect(&timer2, &QTimer::timeout, &app, &QCoreApplication::quit); @@ -723,8 +722,8 @@ void tst_QApplication::quitOnLastWindowClosed() timer2.start(1000); int returnValue = QCoreApplication::exec(); QCOMPARE(returnValue, 0); - QCOMPARE(spy2.count(), 1); - QCOMPARE(appSpy.count(), 0); + QCOMPARE(spy2.size(), 1); + QCOMPARE(appSpy.size(), 0); } { int argc = 0; @@ -755,8 +754,8 @@ void tst_QApplication::quitOnLastWindowClosed() QCoreApplication::exec(); - QCOMPARE(spy.count(), 1); - QVERIFY(spy2.count() < 15); // Should be around 10 if closing caused the quit + QCOMPARE(spy.size(), 1); + QVERIFY(spy2.size() < 15); // Should be around 10 if closing caused the quit } bool quitApplicationTriggered = false; @@ -786,13 +785,13 @@ void tst_QApplication::quitOnLastWindowClosed() QCoreApplication::exec(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QVERIFY(quitApplicationTriggered); } { int argc = 0; QApplication app(argc, nullptr); - QSignalSpy appSpy(&app, &QApplication::lastWindowClosed); + QSignalSpy appSpy(&app, &QGuiApplication::lastWindowClosed); // exec a dialog for 1 second, then show the window QuitOnLastWindowClosedWindow window; @@ -808,8 +807,8 @@ void tst_QApplication::quitOnLastWindowClosed() QCOMPARE(returnValue, 0); // failure here means the timer above didn't fire, and the // quit was caused the dialog being closed (not the window) - QCOMPARE(timerSpy.count(), 1); - QCOMPARE(appSpy.count(), 2); + QCOMPARE(timerSpy.size(), 1); + QCOMPARE(appSpy.size(), 2); } { int argc = 0; @@ -858,7 +857,7 @@ void tst_QApplication::quitOnLastWindowClosed() QTimer::singleShot(100, &w1, &QWidget::close); QCoreApplication::exec(); - QVERIFY(timerSpy.count() < 10); + QVERIFY(timerSpy.size() < 10); } } @@ -902,6 +901,7 @@ void tst_QApplication::closeAllWindows() { int argc = 0; QApplication app(argc, nullptr); + app.setAttribute(Qt::AA_DontUseNativeDialogs, true); // create some windows new QWidget; @@ -910,7 +910,7 @@ void tst_QApplication::closeAllWindows() // show all windows auto topLevels = QApplication::topLevelWidgets(); - for (QWidget *w : qAsConst(topLevels)) { + for (QWidget *w : std::as_const(topLevels)) { w->show(); QVERIFY(QTest::qWaitForWindowExposed(w)); } @@ -927,14 +927,14 @@ void tst_QApplication::closeAllWindows() PromptOnCloseWidget *promptOnCloseWidget = new PromptOnCloseWidget; // show all windows topLevels = QApplication::topLevelWidgets(); - for (QWidget *w : qAsConst(topLevels)) { + for (QWidget *w : std::as_const(topLevels)) { w->show(); QVERIFY(QTest::qWaitForWindowExposed(w)); } // close the last window to open the prompt (eventloop recurses) promptOnCloseWidget->close(); // all windows should not be visible, except the one that opened the prompt - for (QWidget *w : qAsConst(topLevels)) { + for (QWidget *w : std::as_const(topLevels)) { if (w == promptOnCloseWidget) QVERIFY(w->isVisible()); else @@ -946,8 +946,8 @@ void tst_QApplication::closeAllWindows() bool isPathListIncluded(const QStringList &l, const QStringList &r) { - int size = r.count(); - if (size > l.count()) + int size = r.size(); + if (size > l.size()) return false; #if defined (Q_OS_WIN) Qt::CaseSensitivity cs = Qt::CaseInsensitive; @@ -955,13 +955,13 @@ bool isPathListIncluded(const QStringList &l, const QStringList &r) Qt::CaseSensitivity cs = Qt::CaseSensitive; #endif int i = 0, j = 0; - for ( ; i < l.count() && j < r.count(); ++i) { + for ( ; i < l.size() && j < r.size(); ++i) { if (QDir::toNativeSeparators(l[i]).compare(QDir::toNativeSeparators(r[j]), cs) == 0) { ++j; i = -1; } } - return j == r.count(); + return j == r.size(); } #if QT_CONFIG(library) @@ -1021,7 +1021,7 @@ void tst_QApplication::libraryPaths() { qCDebug(lcTests) << "Initial library path:" << QApplication::libraryPaths(); - int count = QApplication::libraryPaths().count(); + int count = QApplication::libraryPaths().size(); #if 0 // this test doesn't work if KDE 4 is installed QCOMPARE(count, 1); // before creating QApplication, only the PluginsPath is in the libraryPaths() @@ -1030,9 +1030,9 @@ void tst_QApplication::libraryPaths() QApplication::addLibraryPath(installPathPlugins); qCDebug(lcTests) << "installPathPlugins" << installPathPlugins; qCDebug(lcTests) << "After adding plugins path:" << QApplication::libraryPaths(); - QCOMPARE(QApplication::libraryPaths().count(), count); + QCOMPARE(QApplication::libraryPaths().size(), count); QApplication::addLibraryPath(testDir); - QCOMPARE(QApplication::libraryPaths().count(), count + 1); + QCOMPARE(QApplication::libraryPaths().size(), count + 1); // creating QApplication adds the applicationDirPath to the libraryPath int argc = 1; @@ -1042,19 +1042,19 @@ void tst_QApplication::libraryPaths() // On Windows CE these are identical and might also be the case for other // systems too if (appDirPath != installPathPlugins) - QCOMPARE(QApplication::libraryPaths().count(), count + 2); + QCOMPARE(QApplication::libraryPaths().size(), count + 2); } { int argc = 1; QApplication app(argc, &argv0); qCDebug(lcTests) << "Initial library path:" << QCoreApplication::libraryPaths(); - int count = QCoreApplication::libraryPaths().count(); + int count = QCoreApplication::libraryPaths().size(); QString installPathPlugins = QLibraryInfo::path(QLibraryInfo::PluginsPath); QCoreApplication::addLibraryPath(installPathPlugins); qCDebug(lcTests) << "installPathPlugins" << installPathPlugins; qCDebug(lcTests) << "After adding plugins path:" << QCoreApplication::libraryPaths(); - QCOMPARE(QCoreApplication::libraryPaths().count(), count); + QCOMPARE(QCoreApplication::libraryPaths().size(), count); QString appDirPath = QCoreApplication::applicationDirPath(); @@ -1062,14 +1062,14 @@ void tst_QApplication::libraryPaths() QCoreApplication::addLibraryPath(appDirPath + "/.."); qCDebug(lcTests) << "appDirPath" << appDirPath; qCDebug(lcTests) << "After adding appDirPath && appDirPath + /..:" << QCoreApplication::libraryPaths(); - QCOMPARE(QCoreApplication::libraryPaths().count(), count + 1); + QCOMPARE(QCoreApplication::libraryPaths().size(), count + 1); #ifdef Q_OS_MACOS QCoreApplication::addLibraryPath(appDirPath + "/../MacOS"); #else QCoreApplication::addLibraryPath(appDirPath + "/tmp/.."); #endif qCDebug(lcTests) << "After adding appDirPath + /tmp/..:" << QCoreApplication::libraryPaths(); - QCOMPARE(QCoreApplication::libraryPaths().count(), count + 1); + QCOMPARE(QCoreApplication::libraryPaths().size(), count + 1); } } @@ -1136,11 +1136,12 @@ void tst_QApplication::libraryPaths_qt_plugin_path_2() << QCoreApplication::applicationDirPath(); QVERIFY(isPathListIncluded(QCoreApplication::libraryPaths(), expected)); - qputenv("QT_PLUGIN_PATH", QByteArray()); + qputenv("QT_PLUGIN_PATH", nullptr); } } #endif +#ifdef QT_BUILD_INTERNAL class SendPostedEventsTester : public QObject { Q_OBJECT @@ -1162,14 +1163,14 @@ void SendPostedEventsTester::doTest() QPointer<SendPostedEventsTester> p = this; QApplication::postEvent(this, new QEvent(QEvent::User)); // DeferredDelete should not be delivered until returning from this function - QApplication::postEvent(this, new QDeferredDeleteEvent()); + deleteLater(); QEventLoop eventLoop; QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection); eventLoop.exec(); QVERIFY(p != nullptr); - QCOMPARE(eventSpy.count(), 2); + QCOMPARE(eventSpy.size(), 2); QCOMPARE(eventSpy.at(0), int(QEvent::MetaCall)); QCOMPARE(eventSpy.at(1), int(QEvent::User)); eventSpy.clear(); @@ -1186,6 +1187,7 @@ void tst_QApplication::sendPostedEvents() (void) QCoreApplication::exec(); QVERIFY(p.isNull()); } +#endif void tst_QApplication::thread() { @@ -1330,12 +1332,9 @@ void DeleteLaterWidget::checkDeleteLater() void tst_QApplication::testDeleteLater() { -#ifdef Q_OS_MAC - QSKIP("This test fails and then hangs on OS X, see QTBUG-24318"); -#endif int argc = 0; QApplication app(argc, nullptr); - connect(&app, &QApplication::lastWindowClosed, &app, &QCoreApplication::quit); + connect(&app, &QGuiApplication::lastWindowClosed, &app, &QCoreApplication::quit); DeleteLaterWidget *wgt = new DeleteLaterWidget(&app); QTimer::singleShot(500, wgt, &DeleteLaterWidget::runTest); @@ -1530,7 +1529,7 @@ void tst_QApplication::desktopSettingsAware() environment += QLatin1String("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM=1"); testProcess.setEnvironment(environment); #endif - testProcess.start("desktopsettingsaware_helper"); + testProcess.start("./desktopsettingsaware_helper"); QVERIFY2(testProcess.waitForStarted(), qPrintable(QString::fromLatin1("Cannot start 'desktopsettingsaware_helper': %1").arg(testProcess.errorString()))); QVERIFY(testProcess.waitForFinished(10000)); @@ -1558,11 +1557,61 @@ void tst_QApplication::setActiveWindow() delete pb2; w->show(); - QApplication::setActiveWindow(w); // needs this on twm (focus follows mouse) + QApplicationPrivate::setActiveWindow(w); // needs this on twm (focus follows mouse) QVERIFY(pb1->hasFocus()); delete w; } +void tst_QApplication::activateDeactivateEvent() +{ + // Ensure that QWindows (other than QWidgetWindow) + // are activated / deactivated. + class Window : public QWindow + { + public: + using QWindow::QWindow; + + int activateCount = 0; + int deactivateCount = 0; + protected: + bool event(QEvent *e) override + { + switch (e->type()) { + case QEvent::WindowActivate: + ++activateCount; + break; + case QEvent::WindowDeactivate: + ++deactivateCount; + break; + default: + break; + } + return QWindow::event(e); + } + }; + + int argc = 0; + QApplication app(argc, nullptr); + + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + + Window w1; + Window w2; + + w1.show(); + w1.requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(&w1)); + QCOMPARE(w1.activateCount, 1); + QCOMPARE(w1.deactivateCount, 0); + + w2.show(); + w2.requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(&w2)); + QCOMPARE(w1.deactivateCount, 1); + QCOMPARE(w2.activateCount, 1); +} + void tst_QApplication::focusWidget() { int argc = 0; @@ -1573,7 +1622,7 @@ void tst_QApplication::focusWidget() QTextEdit te; te.show(); - QApplication::setActiveWindow(&te); + QApplicationPrivate::setActiveWindow(&te); QVERIFY(QTest::qWaitForWindowActive(&te)); const auto focusWidget = QApplication::focusWidget(); @@ -1589,7 +1638,7 @@ void tst_QApplication::focusWidget() QTextEdit te(&w); w.show(); - QApplication::setActiveWindow(&w); + QApplicationPrivate::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); const auto focusWidget = QApplication::focusWidget(); @@ -1622,22 +1671,22 @@ void tst_QApplication::focusChanged() hbox1.addWidget(&le1); hbox1.addWidget(&pb1); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); parent1.show(); - QApplication::setActiveWindow(&parent1); // needs this on twm (focus follows mouse) - QCOMPARE(spy.count(), 1); - QCOMPARE(spy.at(0).count(), 2); + QApplicationPrivate::setActiveWindow(&parent1); // needs this on twm (focus follows mouse) + QCOMPARE(spy.size(), 1); + QCOMPARE(spy.at(0).size(), 2); old = qvariant_cast<QWidget*>(spy.at(0).at(0)); now = qvariant_cast<QWidget*>(spy.at(0).at(1)); QCOMPARE(now, &le1); QCOMPARE(now, QApplication::focusWidget()); QVERIFY(!old); spy.clear(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); pb1.setFocus(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); old = qvariant_cast<QWidget*>(spy.at(0).at(0)); now = qvariant_cast<QWidget*>(spy.at(0).at(1)); QCOMPARE(now, &pb1); @@ -1646,7 +1695,7 @@ void tst_QApplication::focusChanged() spy.clear(); lb1.setFocus(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); old = qvariant_cast<QWidget*>(spy.at(0).at(0)); now = qvariant_cast<QWidget*>(spy.at(0).at(1)); QCOMPARE(now, &lb1); @@ -1655,7 +1704,7 @@ void tst_QApplication::focusChanged() spy.clear(); lb1.clearFocus(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); old = qvariant_cast<QWidget*>(spy.at(0).at(0)); now = qvariant_cast<QWidget*>(spy.at(0).at(1)); QVERIFY(!now); @@ -1674,10 +1723,10 @@ void tst_QApplication::focusChanged() hbox2.addWidget(&pb2); parent2.show(); - QApplication::setActiveWindow(&parent2); // needs this on twm (focus follows mouse) - QVERIFY(spy.count() > 0); // one for deactivation, one for activation on Windows - old = qvariant_cast<QWidget*>(spy.at(spy.count()-1).at(0)); - now = qvariant_cast<QWidget*>(spy.at(spy.count()-1).at(1)); + QApplicationPrivate::setActiveWindow(&parent2); // needs this on twm (focus follows mouse) + QVERIFY(spy.size() > 0); // one for deactivation, one for activation on Windows + old = qvariant_cast<QWidget*>(spy.at(spy.size()-1).at(0)); + now = qvariant_cast<QWidget*>(spy.at(spy.size()-1).at(1)); QCOMPARE(now, &le2); QCOMPARE(now, QApplication::focusWidget()); QVERIFY(!old); @@ -1702,10 +1751,10 @@ void tst_QApplication::focusChanged() tab.simulate(now); if (!tabAllControls) { - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QCOMPARE(now, QApplication::focusWidget()); } else { - QVERIFY(spy.count() > 0); + QVERIFY(spy.size() > 0); old = qvariant_cast<QWidget*>(spy.at(0).at(0)); now = qvariant_cast<QWidget*>(spy.at(0).at(1)); QCOMPARE(now, &pb2); @@ -1715,11 +1764,11 @@ void tst_QApplication::focusChanged() } if (!tabAllControls) { - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QCOMPARE(now, QApplication::focusWidget()); } else { tab.simulate(now); - QVERIFY(spy.count() > 0); + QVERIFY(spy.size() > 0); old = qvariant_cast<QWidget*>(spy.at(0).at(0)); now = qvariant_cast<QWidget*>(spy.at(0).at(1)); QCOMPARE(now, &le2); @@ -1729,11 +1778,11 @@ void tst_QApplication::focusChanged() } if (!tabAllControls) { - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QCOMPARE(now, QApplication::focusWidget()); } else { backtab.simulate(now); - QVERIFY(spy.count() > 0); + QVERIFY(spy.size() > 0); old = qvariant_cast<QWidget*>(spy.at(0).at(0)); now = qvariant_cast<QWidget*>(spy.at(0).at(1)); QCOMPARE(now, &pb2); @@ -1744,12 +1793,12 @@ void tst_QApplication::focusChanged() if (!tabAllControls) { - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QCOMPARE(now, QApplication::focusWidget()); old = &pb2; } else { backtab.simulate(now); - QVERIFY(spy.count() > 0); + QVERIFY(spy.size() > 0); old = qvariant_cast<QWidget*>(spy.at(0).at(0)); now = qvariant_cast<QWidget*>(spy.at(0).at(1)); QCOMPARE(now, &le2); @@ -1760,10 +1809,10 @@ void tst_QApplication::focusChanged() click.simulate(old); if (!(pb2.focusPolicy() & Qt::ClickFocus)) { - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QCOMPARE(now, QApplication::focusWidget()); } else { - QVERIFY(spy.count() > 0); + QVERIFY(spy.size() > 0); old = qvariant_cast<QWidget*>(spy.at(0).at(0)); now = qvariant_cast<QWidget*>(spy.at(0).at(1)); QCOMPARE(now, &pb2); @@ -1772,7 +1821,7 @@ void tst_QApplication::focusChanged() spy.clear(); click.simulate(old); - QVERIFY(spy.count() > 0); + QVERIFY(spy.size() > 0); old = qvariant_cast<QWidget*>(spy.at(0).at(0)); now = qvariant_cast<QWidget*>(spy.at(0).at(1)); QCOMPARE(now, &le2); @@ -1782,16 +1831,16 @@ void tst_QApplication::focusChanged() } parent1.activateWindow(); - QApplication::setActiveWindow(&parent1); // needs this on twm (focus follows mouse) - QVERIFY(spy.count() == 1 || spy.count() == 2); // one for deactivation, one for activation on Windows + QApplicationPrivate::setActiveWindow(&parent1); // needs this on twm (focus follows mouse) + QVERIFY(spy.size() == 1 || spy.size() == 2); // one for deactivation, one for activation on Windows //on windows, the change of focus is made in 2 steps //(the focusChanged SIGNAL is emitted twice) - if (spy.count()==1) - old = qvariant_cast<QWidget*>(spy.at(spy.count()-1).at(0)); + if (spy.size()==1) + old = qvariant_cast<QWidget*>(spy.at(spy.size()-1).at(0)); else - old = qvariant_cast<QWidget*>(spy.at(spy.count()-2).at(0)); - now = qvariant_cast<QWidget*>(spy.at(spy.count()-1).at(1)); + old = qvariant_cast<QWidget*>(spy.at(spy.size()-2).at(0)); + now = qvariant_cast<QWidget*>(spy.at(spy.size()-1).at(1)); QCOMPARE(now, &le1); QCOMPARE(now, QApplication::focusWidget()); QCOMPARE(old, &le2); @@ -1865,7 +1914,7 @@ void tst_QApplication::focusMouseClick() // front most widget has Qt::TabFocus, parent widget accepts clicks as well // now send a mouse button press event and check what happens with the focus // it should be given to the parent widget - QMouseEvent ev(QEvent::MouseButtonPress, QPointF(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + QMouseEvent ev(QEvent::MouseButtonPress, QPointF(), w.mapToGlobal(QPointF()), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QSpontaneKeyEvent::setSpontaneous(&ev); QVERIFY(ev.spontaneous()); qApp->notify(&w2, &ev); @@ -2026,11 +2075,11 @@ void tst_QApplication::topLevelWidgets() #endif QCoreApplication::processEvents(); QVERIFY(QApplication::topLevelWidgets().contains(w)); - QCOMPARE(QApplication::topLevelWidgets().count(), 1); + QCOMPARE(QApplication::topLevelWidgets().size(), 1); delete w; w = nullptr; QCoreApplication::processEvents(); - QCOMPARE(QApplication::topLevelWidgets().count(), 0); + QCOMPARE(QApplication::topLevelWidgets().size(), 0); } @@ -2447,7 +2496,7 @@ void tst_QApplication::wheelEventPropagation() int vcount = 0; int hcount = 0; - for (const auto &event : qAsConst(events)) { + for (const auto &event : std::as_const(events)) { const QPoint pixelDelta = event.orientation == Qt::Vertical ? QPoint(0, -scrollStep) : QPoint(-scrollStep, 0); const QPoint angleDelta = event.orientation == Qt::Vertical ? QPoint(0, -120) : QPoint(-120, 0); QWindowSystemInterface::handleWheelEvent(outerArea.windowHandle(), center, global, @@ -2458,10 +2507,10 @@ void tst_QApplication::wheelEventPropagation() else ++hcount; QCoreApplication::processEvents(); - QCOMPARE(innerVSpy.count(), innerScrolls ? vcount : 0); - QCOMPARE(innerHSpy.count(), innerScrolls ? hcount : 0); - QCOMPARE(outerVSpy.count(), innerScrolls ? 0 : vcount); - QCOMPARE(outerHSpy.count(), innerScrolls ? 0 : hcount); + QCOMPARE(innerVSpy.size(), innerScrolls ? vcount : 0); + QCOMPARE(innerHSpy.size(), innerScrolls ? hcount : 0); + QCOMPARE(outerVSpy.size(), innerScrolls ? 0 : vcount); + QCOMPARE(outerHSpy.size(), innerScrolls ? 0 : hcount); } } @@ -2470,7 +2519,7 @@ void tst_QApplication::qtbug_12673() #if QT_CONFIG(process) QProcess testProcess; QStringList arguments; - testProcess.start("modal_helper", arguments); + testProcess.start("./modal_helper", arguments); QVERIFY2(testProcess.waitForStarted(), qPrintable(QString::fromLatin1("Cannot start 'modal_helper': %1").arg(testProcess.errorString()))); QVERIFY(testProcess.waitForFinished(20000)); @@ -2480,6 +2529,20 @@ void tst_QApplication::qtbug_12673() #endif } +void tst_QApplication::qtbug_103611() +{ + { + int argc = 0; + QApplication app(argc, nullptr); + auto ll = QLocale().uiLanguages(); + } + { + int argc = 0; + QApplication app(argc, nullptr); + auto ll = QLocale().uiLanguages(); + } +} + class NoQuitOnHideWidget : public QWidget { Q_OBJECT @@ -2509,8 +2572,26 @@ public: explicit ShowCloseShowWidget(bool showAgain, QWidget *parent = nullptr) : QWidget(parent), showAgain(showAgain) { + int timeout = 500; +#ifdef Q_OS_ANDROID + // On Android, CI Android emulator is not running HW accelerated graphics and can be slow, + // use a longer timeout to avoid flaky failures + timeout = 1000; +#endif + QTimer::singleShot(timeout, this, [] () { QCoreApplication::exit(1); }); + } + + bool shown = false; + +protected: + void showEvent(QShowEvent *) override + { QTimer::singleShot(0, this, &ShowCloseShowWidget::doClose); - QTimer::singleShot(500, this, [] () { QCoreApplication::exit(1); }); + shown = true; + } + void hideEvent(QHideEvent *) override + { + shown = false; } private slots: @@ -2526,16 +2607,21 @@ private: void tst_QApplication::abortQuitOnShow() { + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Wayland: This crash, see QTBUG-123172."); + int argc = 0; QApplication app(argc, nullptr); ShowCloseShowWidget window1(false); window1.setWindowTitle(QLatin1String(QTest::currentTestFunction())); window1.show(); + QVERIFY(QTest::qWaitFor([&window1](){ return window1.shown; })); QCOMPARE(QCoreApplication::exec(), 0); ShowCloseShowWidget window2(true); window2.setWindowTitle(QLatin1String(QTest::currentTestFunction())); window2.show(); + QVERIFY(QTest::qWaitFor([&window2](){ return window2.shown; })); QCOMPARE(QCoreApplication::exec(), 1); } @@ -2550,7 +2636,7 @@ void tst_QApplication::staticFunctions() QApplication::activeModalWidget(); QApplication::focusWidget(); QApplication::activeWindow(); - QApplication::setActiveWindow(nullptr); + QApplicationPrivate::setActiveWindow(nullptr); QApplication::widgetAt(QPoint(0, 0)); QApplication::topLevelAt(QPoint(0, 0)); QTest::ignoreMessage(QtWarningMsg, "Must construct a QApplication first."); diff --git a/tests/auto/widgets/kernel/qboxlayout/CMakeLists.txt b/tests/auto/widgets/kernel/qboxlayout/CMakeLists.txt index 8fdc0d21a9..5b60382fba 100644 --- a/tests/auto/widgets/kernel/qboxlayout/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qboxlayout/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qboxlayout.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qboxlayout Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qboxlayout LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qboxlayout SOURCES tst_qboxlayout.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::TestPrivate Qt::Widgets diff --git a/tests/auto/widgets/kernel/qboxlayout/tst_qboxlayout.cpp b/tests/auto/widgets/kernel/qboxlayout/tst_qboxlayout.cpp index a410a7bf00..4313d9891c 100644 --- a/tests/auto/widgets/kernel/qboxlayout/tst_qboxlayout.cpp +++ b/tests/auto/widgets/kernel/qboxlayout/tst_qboxlayout.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -141,7 +116,7 @@ void tst_QBoxLayout::insertLayout() QCOMPARE(dummyParentLayout->count(), 1); // add subLayout to another layout - QTest::ignoreMessage(QtWarningMsg, "QLayout::addChildLayout: layout \"\" already has a parent"); + QTest::ignoreMessage(QtWarningMsg, "QLayout::addChildLayout: layout QHBoxLayout \"\" already has a parent"); vbox->addLayout(subLayout); QCOMPARE((subLayout->parent() == vbox), (vbox->count() == 1)); } @@ -217,7 +192,7 @@ void tst_QBoxLayout::setStyleShouldChangeSpacing() window.setWindowTitle(QTest::currentTestFunction()); QHBoxLayout *hbox = new QHBoxLayout(&window); QPushButton *pb1 = new QPushButton(tr("The spacing between this")); - QPushButton *pb2 = new QPushButton(tr("and this button should depend on the style of the parent widget"));; + QPushButton *pb2 = new QPushButton(tr("and this button should depend on the style of the parent widget")); pb1->setAttribute(Qt::WA_LayoutUsesWidgetRect); pb2->setAttribute(Qt::WA_LayoutUsesWidgetRect); hbox->addWidget(pb1); @@ -542,14 +517,14 @@ void tst_QBoxLayout::testLayoutEngine() QHBoxLayout box; box.setSpacing(spacing); int i; - for (i = 0; i < itemDescriptions.count(); ++i) { + for (i = 0; i < itemDescriptions.size(); ++i) { Descr descr = itemDescriptions.at(i); LayoutItem *li = new LayoutItem(descr); box.addItem(li); box.setStretch(i, descr.stretch); } box.setGeometry(QRect(0,0,size,100)); - for (i = 0; i < expectedSizes.count(); ++i) { + for (i = 0; i < expectedSizes.size(); ++i) { int xSize = expectedSizes.at(i); int xPos = expectedPositions.at(i); QLayoutItem *item = box.itemAt(i); diff --git a/tests/auto/widgets/kernel/qformlayout/BLACKLIST b/tests/auto/widgets/kernel/qformlayout/BLACKLIST deleted file mode 100644 index 375682e788..0000000000 --- a/tests/auto/widgets/kernel/qformlayout/BLACKLIST +++ /dev/null @@ -1,3 +0,0 @@ -# QTBUG-87401 -[wrapping] -android diff --git a/tests/auto/widgets/kernel/qformlayout/CMakeLists.txt b/tests/auto/widgets/kernel/qformlayout/CMakeLists.txt index b084fe1634..9e1da4c6a3 100644 --- a/tests/auto/widgets/kernel/qformlayout/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qformlayout/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qformlayout.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qformlayout Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qformlayout LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qformlayout SOURCES tst_qformlayout.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::TestPrivate Qt::Widgets diff --git a/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp b/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp index 4aa9f8ac2d..9638823538 100644 --- a/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp +++ b/tests/auto/widgets/kernel/qformlayout/tst_qformlayout.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -135,6 +110,9 @@ private slots: void takeRow_QLayout(); void setWidget(); void setLayout(); + void hideShowRow(); + void showWithHiddenRow(); + void hiddenRowAndStretch(); /* QLayoutItem *itemAt(int row, ItemRole role) const; @@ -274,7 +252,9 @@ void tst_QFormLayout::wrapping() fl->setRowWrapPolicy(QFormLayout::WrapLongRows); QLineEdit *le = new QLineEdit; - QLabel *lbl = new QLabel("A long label"); + QLabel *lbl = new QLabel("A long label which is actually long enough to trigger wrapping," + " even on Android and even if it is executed on a tiling window" + " manager which forces the window into fullscreen mode."); le->setMinimumWidth(200); fl->addRow(lbl, le); @@ -814,6 +794,7 @@ void tst_QFormLayout::removeRow_QWidget() QCOMPARE(layout->rowCount(), 0); QWidget *w3 = new QWidget; + QTest::ignoreMessage(QtWarningMsg, "QFormLayout::takeRow: Invalid widget"); layout->removeRow(w3); delete w3; } @@ -854,6 +835,7 @@ void tst_QFormLayout::removeRow_QLayout() QCOMPARE(layout->rowCount(), 0); QHBoxLayout *l3 = new QHBoxLayout; + QTest::ignoreMessage(QtWarningMsg, "QFormLayout::takeRow: Invalid layout"); layout->removeRow(l3); delete l3; } @@ -893,6 +875,7 @@ void tst_QFormLayout::takeRow() QCOMPARE(layout->rowCount(), 0); QCOMPARE(result.fieldItem->widget(), w1.data()); + QTest::ignoreMessage(QtWarningMsg, "QFormLayout::takeRow: Invalid row 0"); result = layout->takeRow(0); QVERIFY(!result.fieldItem); @@ -933,6 +916,7 @@ void tst_QFormLayout::takeRow_QWidget() QCOMPARE(layout->rowCount(), 0); QWidget *w3 = new QWidget; + QTest::ignoreMessage(QtWarningMsg, "QFormLayout::takeRow: Invalid widget"); result = layout->takeRow(w3); delete w3; @@ -980,6 +964,7 @@ void tst_QFormLayout::takeRow_QLayout() QCOMPARE(layout->rowCount(), 0); QHBoxLayout *l3 = new QHBoxLayout; + QTest::ignoreMessage(QtWarningMsg, "QFormLayout::takeRow: Invalid layout"); result = layout->takeRow(l3); delete l3; @@ -1009,7 +994,9 @@ void tst_QFormLayout::setWidget() QCOMPARE(layout.rowCount(), 6); // should be ignored and generate warnings + QTest::ignoreMessage(QtWarningMsg, "QFormLayoutPrivate::setItem: Cell (3, 1) already occupied"); layout.setWidget(3, QFormLayout::FieldRole, &w4); + QTest::ignoreMessage(QtWarningMsg, "QFormLayoutPrivate::setItem: Invalid cell (-1, 1)"); layout.setWidget(-1, QFormLayout::FieldRole, &w4); { @@ -1077,7 +1064,9 @@ void tst_QFormLayout::setLayout() QCOMPARE(layout.rowCount(), 6); // should be ignored and generate warnings + QTest::ignoreMessage(QtWarningMsg, "QFormLayoutPrivate::setItem: Cell (3, 1) already occupied"); layout.setLayout(3, QFormLayout::FieldRole, &l4); + QTest::ignoreMessage(QtWarningMsg, "QLayout::addChildLayout: layout QHBoxLayout \"\" already has a parent"); layout.setLayout(-1, QFormLayout::FieldRole, &l4); QCOMPARE(layout.count(), 3); QCOMPARE(layout.rowCount(), 6); @@ -1123,6 +1112,191 @@ void tst_QFormLayout::setLayout() } } +void tst_QFormLayout::hideShowRow() +{ + QWidget topLevel; + QFormLayout layout; + + const auto makeComplex = []{ + QHBoxLayout *hboxField = new QHBoxLayout; + hboxField->addWidget(new QLineEdit("Left")); + hboxField->addWidget(new QLineEdit("Right")); + return hboxField; + }; + + layout.addRow("Label", new QLineEdit("one")); + layout.addRow("Label", new QLineEdit("two")); + layout.addRow("Label", new QLineEdit("three")); + layout.addRow("Label", makeComplex()); + layout.addRow(new QLineEdit("five")); // spanning widget + layout.addRow(makeComplex()); // spanning layout + + topLevel.setLayout(&layout); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + // returns the top-left position of the items in a row + const auto rowPosition = [&layout](int row) { + QRect rect; + if (QLayoutItem *spanningItem = layout.itemAt(row, QFormLayout::SpanningRole)) { + rect = spanningItem->geometry(); + } else { + if (QLayoutItem *labelItem = layout.itemAt(row, QFormLayout::LabelRole)) { + rect = labelItem->geometry(); + } + if (QLayoutItem *fieldItem = layout.itemAt(row, QFormLayout::FieldRole)) { + rect |= fieldItem->geometry(); + } + } + return rect.topLeft(); + }; + + // returns the first widget in a row, even if that row is taken by a layout + const auto rowInputWidget = [&layout](int row) -> QWidget* { + auto fieldItem = layout.itemAt(row, QFormLayout::FieldRole); + if (!fieldItem) + return nullptr; + QWidget *fieldWidget = fieldItem->widget(); + // we happen to know our layout structure + if (!fieldWidget) + fieldWidget = fieldItem->layout()->itemAt(0)->widget(); + return fieldWidget; + }; + + // record the reference positions for all rows + QList<QPoint> rowPositions(layout.rowCount()); + for (int row = 0; row < layout.rowCount(); ++row) + rowPositions[row] = rowPosition(row); + + // hide each row in turn, the next row should take the space of the hidden row + for (int row = 0; row < layout.rowCount(); ++ row) { + layout.setRowVisible(row, false); + QVERIFY(!layout.isRowVisible(row)); + if (row < layout.rowCount() - 1) + QTRY_COMPARE(rowPosition(row + 1), rowPositions[row]); + layout.setRowVisible(row, true); + QVERIFY(layout.isRowVisible(row)); + } + + // Hiding only the label or only the field doesn't hide the row. + for (int row = 0; row < layout.rowCount() - 1; ++row) { + const auto labelItem = layout.itemAt(0, QFormLayout::LabelRole); + if (labelItem) { + labelItem->widget()->hide(); + QVERIFY(layout.isRowVisible(row)); + QCOMPARE(rowPosition(row), rowPositions[row]); + layout.itemAt(0, QFormLayout::LabelRole)->widget()->show(); + } + const auto fieldItem = layout.itemAt(0, QFormLayout::FieldRole); + if (fieldItem) { + fieldItem->widget()->hide(); + QVERIFY(layout.isRowVisible(row)); + QCOMPARE(rowPosition(row), rowPositions[row]); + layout.itemAt(0, QFormLayout::FieldRole)->widget()->show(); + } + } + + // If we hide both label and field, then the row should be considered hidden and the + // following row should move up into the space of the hidden row. We can only test + // this if both label and field are widgets, or if there is a spanning widget. + for (int row = 0; row < layout.rowCount() - 1; ++row) { + QWidget *labelWidget = nullptr; + if (auto labelItem = layout.itemAt(row, QFormLayout::LabelRole)) + labelWidget = labelItem->widget(); + QWidget *fieldWidget = nullptr; + if (auto fieldItem = layout.itemAt(row, QFormLayout::FieldRole)) + fieldWidget = fieldItem->widget(); + + if (!fieldWidget) + continue; + if (labelWidget) + labelWidget->hide(); + fieldWidget->hide(); + QVERIFY(!layout.isRowVisible(row)); + QVERIFY(!layout.isRowVisible(fieldWidget)); + if (labelWidget) + QVERIFY(!layout.isRowVisible(labelWidget)); + QTRY_COMPARE(rowPosition(row + 1), rowPositions[row]); + if (labelWidget) + labelWidget->show(); + fieldWidget->show(); + } + + // hiding a row where a widget has focus must move focus to a widget in the next row + for (int row = 0; row < layout.rowCount(); ++row) { + QWidget *inputWidget = rowInputWidget(row); + QVERIFY(inputWidget); + inputWidget->setFocus(); + layout.setRowVisible(row, false); + QVERIFY(!inputWidget->hasFocus()); + } + + // Now hide all rows, hide the toplevel widget, and show the toplevel widget again. + // None of the widgets inside must be visible. + for (int row = 0; row < layout.rowCount(); ++row) + layout.setRowVisible(row, false); + topLevel.hide(); + topLevel.show(); + for (int row = 0; row < layout.rowCount(); ++row) + QVERIFY(rowInputWidget(row)->isHidden()); +} + +void tst_QFormLayout::showWithHiddenRow() +{ + QWidget topLevel; + QFormLayout layout; + + for (int row = 0; row < 3; ++row) + layout.addRow(QString("Label %1").arg(row), new QLineEdit); + layout.setRowVisible(1, false); + + topLevel.setLayout(&layout); + topLevel.show(); +} + +/* + Test that hiding rows does not leave outdated layout data behind + in hidden items that results in out-of-bounds array access. See + QTBUG-109237. +*/ +void tst_QFormLayout::hiddenRowAndStretch() +{ + QWidget topLevel; + QFormLayout layout; + layout.setRowWrapPolicy(QFormLayout::WrapAllRows); + + // We need our own stretcher item so that QFormLayout doesn't insert + // it's own, as that would grow the size of the layout data array again. + QSpacerItem *stretch = new QSpacerItem(100, 100, QSizePolicy::Expanding, QSizePolicy::Expanding); + layout.setItem(0, QFormLayout::FieldRole, stretch); + + QLabel *lastLabel = nullptr; + QLineEdit *lastField = nullptr; + for (int row = 1; row < 4; ++row) { + QLabel *label = new QLabel(QString("Label %1").arg(row)); + label->setWordWrap(true); + QLineEdit *field = new QLineEdit; + layout.setWidget(row, QFormLayout::LabelRole, label); + layout.setWidget(row, QFormLayout::FieldRole, field); + if (row == 3) { + lastLabel = label; + lastField = field; + } + } + + Q_ASSERT(lastLabel); + Q_ASSERT(lastField); + + topLevel.setLayout(&layout); + topLevel.sizeHint(); + + lastLabel->setVisible(false); + lastField->setVisible(false); + + // should not assert here + topLevel.show(); +} + void tst_QFormLayout::itemAt() { QWidget topLevel; diff --git a/tests/auto/widgets/kernel/qgesturerecognizer/BLACKLIST b/tests/auto/widgets/kernel/qgesturerecognizer/BLACKLIST deleted file mode 100644 index 16fc53e75b..0000000000 --- a/tests/auto/widgets/kernel/qgesturerecognizer/BLACKLIST +++ /dev/null @@ -1,3 +0,0 @@ -# QTBUG-87403 -[swipeGesture] -android diff --git a/tests/auto/widgets/kernel/qgesturerecognizer/CMakeLists.txt b/tests/auto/widgets/kernel/qgesturerecognizer/CMakeLists.txt index 9dfd6cb20c..ffa54992d3 100644 --- a/tests/auto/widgets/kernel/qgesturerecognizer/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qgesturerecognizer/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qgesturerecognizer.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgesturerecognizer Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgesturerecognizer LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgesturerecognizer SOURCES tst_qgesturerecognizer.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/kernel/qgesturerecognizer/tst_qgesturerecognizer.cpp b/tests/auto/widgets/kernel/qgesturerecognizer/tst_qgesturerecognizer.cpp index b65f6a5ff6..cdab480d84 100644 --- a/tests/auto/widgets/kernel/qgesturerecognizer/tst_qgesturerecognizer.cpp +++ b/tests/auto/widgets/kernel/qgesturerecognizer/tst_qgesturerecognizer.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QTest> @@ -53,6 +28,7 @@ private Q_SLOTS: void pinchGesture(); void swipeGesture_data(); void swipeGesture(); + void touchReplay(); #endif // !QT_NO_GESTURES private: @@ -95,7 +71,7 @@ TestWidget::TestWidget(const GestureTypeVector &gestureTypes) { setAttribute(Qt::WA_AcceptTouchEvents); - foreach (Qt::GestureType gestureType, gestureTypes) { + for (Qt::GestureType gestureType : gestureTypes) { grabGesture(gestureType); m_receivedGestures.insert(gestureType, false); } @@ -293,7 +269,7 @@ void tst_QGestureRecognizer::swipeGesture() // Press point #3 points.append(points.last() + fingerDistance); - swipeSequence.press(points.size() - 1, points.last(), &widget); + swipeSequence.stationary(0).stationary(1).press(points.size() - 1, points.last(), &widget); swipeSequence.commit(); Q_ASSERT(points.size() == swipePoints); @@ -326,6 +302,25 @@ void tst_QGestureRecognizer::swipeGesture() } } +void tst_QGestureRecognizer::touchReplay() +{ + const Qt::GestureType gestureType = Qt::TapGesture; + QWidget parent; + TestWidget widget(GestureTypeVector(1, gestureType)); + widget.setParent(&parent); + widget.setGeometry(0, 0, 100, 100); + parent.adjustSize(); + parent.show(); + QVERIFY(QTest::qWaitForWindowActive(&parent)); + + QWindow* windowHandle = parent.window()->windowHandle(); + const QPoint globalPos = QPoint(42, 16); + QTest::touchEvent(windowHandle, m_touchDevice).press(1, globalPos); + QTest::touchEvent(windowHandle, m_touchDevice).release(1, globalPos); + + QVERIFY(widget.gestureReceived(gestureType)); +} + #endif // !QT_NO_GESTURES QTEST_MAIN(tst_QGestureRecognizer) diff --git a/tests/auto/widgets/kernel/qgridlayout/CMakeLists.txt b/tests/auto/widgets/kernel/qgridlayout/CMakeLists.txt index c9ac563f01..bf72bc0ae6 100644 --- a/tests/auto/widgets/kernel/qgridlayout/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qgridlayout/CMakeLists.txt @@ -1,14 +1,21 @@ -# Generated from qgridlayout.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgridlayout Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgridlayout LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgridlayout SOURCES sortdialog.ui tst_qgridlayout.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/kernel/qgridlayout/tst_qgridlayout.cpp b/tests/auto/widgets/kernel/qgridlayout/tst_qgridlayout.cpp index e3bd1d1553..3c325699a7 100644 --- a/tests/auto/widgets/kernel/qgridlayout/tst_qgridlayout.cpp +++ b/tests/auto/widgets/kernel/qgridlayout/tst_qgridlayout.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -75,6 +50,7 @@ private slots: void taskQTBUG_40609_addingWidgetToItsOwnLayout(); void taskQTBUG_40609_addingLayoutToItself(); void taskQTBUG_52357_spacingWhenItemIsHidden(); + void taskQTBUG_91261_itemIndexRange(); void replaceWidget(); void dontCrashWhenExtendsToEnd(); }; @@ -82,7 +58,8 @@ private slots: static inline int visibleTopLevelWidgetCount() { int result= 0; - foreach (const QWidget *topLevel, QApplication::topLevelWidgets()) { + const auto topLevels = QApplication::topLevelWidgets(); + for (const QWidget *topLevel : topLevels) { if (topLevel->isVisible()) ++result; } @@ -234,6 +211,9 @@ void tst_QGridLayout::badDistributionBug() void tst_QGridLayout::setMinAndMaxSize() { + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("This test crashes on Wayland, see also QTBUG-107184"); + QWidget widget; setFrameless(&widget); QGridLayout layout(&widget); @@ -682,7 +662,7 @@ void tst_QGridLayout::spacingsAndMargins() QSKIP("The screen is too small to run this test case"); // We are relying on the order here... - for (int pi = 0; pi < sizehinters.count(); ++pi) { + for (int pi = 0; pi < sizehinters.size(); ++pi) { QPoint pt = sizehinters.at(pi)->mapTo(&toplevel, QPoint(0, 0)); QCOMPARE(pt, expectedpositions.at(pi)); } @@ -852,7 +832,7 @@ void tst_QGridLayout::minMaxSize() QList<QPointer<SizeHinterFrame> > sizehinters; for (int i = 0; i < rows; ++i) { for (int j = 0; j < columns; ++j) { - SizeInfo si = sizeinfos.at(sizehinters.count()); + SizeInfo si = sizeinfos.at(sizehinters.size()); int numpixels = si.hfwNumPixels; if (pass == 1 && numpixels == -1) numpixels = -2; //### yuk, (and don't fake it if it already tests sizehint) @@ -881,7 +861,7 @@ void tst_QGridLayout::minMaxSize() QTRY_COMPARE(toplevel.size(), toplevel.sizeHint()); } // We are relying on the order here... - for (int pi = 0; pi < sizehinters.count(); ++pi) { + for (int pi = 0; pi < sizehinters.size(); ++pi) { QPoint pt = sizehinters.at(pi)->mapTo(&toplevel, QPoint(0, 0)); QCOMPARE(pt, sizeinfos.at(pi).expectedPos); } @@ -1051,7 +1031,7 @@ void tst_QGridLayout::styleDependentSpacingsAndMargins() widget.adjustSize(); QApplication::processEvents(); - for (int pi = 0; pi < expectedpositions.count(); ++pi) { + for (int pi = 0; pi < expectedpositions.size(); ++pi) { QCOMPARE(sizehinters.at(pi)->pos(), expectedpositions.at(pi)); } } @@ -1441,7 +1421,7 @@ void tst_QGridLayout::layoutSpacing() QLayout *layout = widget->layout(); QVERIFY(layout); - for (int pi = 0; pi < expectedpositions.count(); ++pi) { + for (int pi = 0; pi < expectedpositions.size(); ++pi) { QLayoutItem *item = layout->itemAt(pi); //qDebug() << item->widget()->pos(); QCOMPARE(item->widget()->pos(), expectedpositions.at(pi)); @@ -1666,6 +1646,56 @@ void tst_QGridLayout::taskQTBUG_52357_spacingWhenItemIsHidden() QTRY_COMPARE_WITH_TIMEOUT(tempWidth, button1.width() + button3.width() + layout.spacing(), 1000); } +void tst_QGridLayout::taskQTBUG_91261_itemIndexRange() +{ + QWidget widget; + QGridLayout lay(&widget); + QPushButton *btn = new QPushButton(&widget); + lay.addWidget(btn, 0, 0); + + { + auto ptr = lay.itemAt(-1); + QCOMPARE(ptr, nullptr); + + ptr = lay.itemAt(0); + QCOMPARE(ptr->widget(), btn); + + ptr = lay.itemAt(1); + QCOMPARE(ptr, nullptr); + } + + { + int row = -1; + int column = -1; + int rowSpan; + int columnSpan; + + lay.getItemPosition(-1, &row, &column, &rowSpan, &columnSpan); + QCOMPARE(row, -1); + QCOMPARE(column, -1); + + lay.getItemPosition(1, &row, &column, &rowSpan, &columnSpan); + QCOMPARE(row, -1); + QCOMPARE(column, -1); + + lay.getItemPosition(0, &row, &column, &rowSpan, &columnSpan); + QCOMPARE(row, 0); + QCOMPARE(column, 0); + } + + { + auto ptr = lay.takeAt(-1); + QCOMPARE(ptr, nullptr); + + ptr = lay.takeAt(1); + QCOMPARE(ptr, nullptr); + + ptr = lay.takeAt(0); + QCOMPARE(ptr->widget(), btn); + delete ptr; + } +} + void tst_QGridLayout::replaceWidget() { QWidget wdg; diff --git a/tests/auto/widgets/kernel/qlayout/CMakeLists.txt b/tests/auto/widgets/kernel/qlayout/CMakeLists.txt index 0ee2ce145e..6bda750c0f 100644 --- a/tests/auto/widgets/kernel/qlayout/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qlayout/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qlayout.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qlayout Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qlayout LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -13,27 +20,10 @@ list(APPEND test_data ${test_data_glob}) qt_internal_add_test(tst_qlayout SOURCES tst_qlayout.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::TestPrivate Qt::Widgets Qt::WidgetsPrivate TESTDATA ${test_data} ) - -## Scopes: -##################################################################### - -if(ANDROID AND NOT ANDROID_EMBEDDED) - # Resources: - set(testdata_resource_files - "baseline/smartmaxsize" - ) - - qt_internal_add_resource(tst_qlayout "testdata" - PREFIX - "/" - FILES - ${testdata_resource_files} - ) -endif() diff --git a/tests/auto/widgets/kernel/qlayout/testdata.qrc b/tests/auto/widgets/kernel/qlayout/testdata.qrc deleted file mode 100644 index 24e8e56263..0000000000 --- a/tests/auto/widgets/kernel/qlayout/testdata.qrc +++ /dev/null @@ -1,5 +0,0 @@ -<RCC> - <qresource prefix="/"> - <file>baseline/smartmaxsize</file> - </qresource> -</RCC> diff --git a/tests/auto/widgets/kernel/qlayout/tst_qlayout.cpp b/tests/auto/widgets/kernel/qlayout/tst_qlayout.cpp index 2a497df084..bd170ca8ab 100644 --- a/tests/auto/widgets/kernel/qlayout/tst_qlayout.cpp +++ b/tests/auto/widgets/kernel/qlayout/tst_qlayout.cpp @@ -1,35 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <qcoreapplication.h> +#include <qmetaobject.h> #include <qdebug.h> #include <qboxlayout.h> #include <qmenubar.h> @@ -69,6 +45,7 @@ private slots: void controlTypes2(); void adjustSizeShouldMakeSureLayoutIsActivated(); void testRetainSizeWhenHidden(); + void removeWidget(); }; tst_QLayout::tst_QLayout() @@ -248,6 +225,22 @@ void tst_QLayout::setContentsMargins() layout.setContentsMargins(52, 53, 54, 55); QVERIFY(!layout.invalidated); + + MyLayout otherLayout; // with default contents margins + QVERIFY(layout.contentsMargins() != otherLayout.contentsMargins()); + layout.unsetContentsMargins(); + QCOMPARE(layout.contentsMargins(), otherLayout.contentsMargins()); + + layout.setContentsMargins(10, 20, 30, 40); + QVERIFY(layout.contentsMargins() != otherLayout.contentsMargins()); + + int contentsMarginsPropertyIndex = QLayout::staticMetaObject.indexOfProperty("contentsMargins"); + QVERIFY(contentsMarginsPropertyIndex >= 0); + QMetaProperty contentsMarginsProperty = QLayout::staticMetaObject.property(contentsMarginsPropertyIndex); + QVERIFY(contentsMarginsProperty.isValid()); + QVERIFY(contentsMarginsProperty.isResettable()); + QVERIFY(contentsMarginsProperty.reset(&layout)); + QCOMPARE(layout.contentsMargins(), otherLayout.contentsMargins()); } class EventReceiver : public QObject @@ -337,7 +330,7 @@ void tst_QLayout::adjustSizeShouldMakeSureLayoutIsActivated() void tst_QLayout::testRetainSizeWhenHidden() { -#if (defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)) +#ifdef Q_OS_ANDROID QSKIP("Test does not work on platforms which default to showMaximized()"); #endif @@ -381,5 +374,34 @@ void tst_QLayout::testRetainSizeWhenHidden() QCOMPARE(widget.sizeHint().height(), normalHeight); } +void tst_QLayout::removeWidget() +{ + QHBoxLayout layout; + QCOMPARE(layout.count(), 0); + std::unique_ptr<QWidget> w(new QWidget); + layout.addWidget(w.get()); + QCOMPARE(layout.count(), 1); + layout.removeWidget(w.get()); + QCOMPARE(layout.count(), 0); + + QPointer<QLayout> childLayout(new QHBoxLayout); + layout.addLayout(childLayout); + QCOMPARE(layout.count(), 1); + + layout.removeWidget(nullptr); + QCOMPARE(layout.count(), 1); + + layout.removeItem(childLayout); + QCOMPARE(layout.count(), 0); + + QVERIFY(!childLayout.isNull()); + + // Test inactive layout consumes ChildRemoved event (QTBUG-124151) + layout.addWidget(w.get()); + layout.setEnabled(false); + w.reset(); + layout.setEnabled(true); +} + QTEST_MAIN(tst_QLayout) #include "tst_qlayout.moc" diff --git a/tests/auto/widgets/kernel/qshortcut/CMakeLists.txt b/tests/auto/widgets/kernel/qshortcut/CMakeLists.txt index 06bd2d6ba8..517286f324 100644 --- a/tests/auto/widgets/kernel/qshortcut/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qshortcut/CMakeLists.txt @@ -1,15 +1,38 @@ -# Generated from qshortcut.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qshortcut Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qshortcut LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qshortcut SOURCES tst_qshortcut.cpp INCLUDE_DIRECTORIES .. - PUBLIC_LIBRARIES + LIBRARIES + Qt::Gui + Qt::GuiPrivate + Qt::Widgets + Qt::WidgetsPrivate +) + +qt_internal_add_test(tst_qguishortcut_with_qapplication + SOURCES + ../../../gui/kernel/qshortcut/tst_qshortcut.cpp + DEFINES + tst_QShortcut=tst_QGuiShortcutWithQApplication + INCLUDE_DIRECTORIES + .. + LIBRARIES Qt::Gui + Qt::GuiPrivate Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/kernel/qshortcut/tst_qshortcut.cpp b/tests/auto/widgets/kernel/qshortcut/tst_qshortcut.cpp index 6832f7a05a..d34df43b01 100644 --- a/tests/auto/widgets/kernel/qshortcut/tst_qshortcut.cpp +++ b/tests/auto/widgets/kernel/qshortcut/tst_qshortcut.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -45,11 +20,72 @@ #include <qshortcut.h> #include <qscreen.h> +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/qpa/qplatformintegration.h> + +#include <QtWidgets/private/qapplication_p.h> + QT_BEGIN_NAMESPACE class QMainWindow; class QTextEdit; QT_END_NAMESPACE +class TestEdit : public QTextEdit +{ + Q_OBJECT +public: + TestEdit(QWidget *parent, const char *name) + : QTextEdit(parent) + { + setObjectName(name); + } + +protected: + bool event(QEvent *e) override + { + // Make testedit allow any Ctrl+Key as shortcut + if (e->type() == QEvent::ShortcutOverride) { + QKeyEvent *ke = static_cast<QKeyEvent*>(e); + if (ke->modifiers() == Qt::ControlModifier + && ke->key() > Qt::Key_Any + && ke->key() < Qt::Key_ydiaeresis) { + ke->ignore(); + return true; + } + } + + // If keypress not processed as normal, check for + // Ctrl+Key event, and input custom string for + // result comparison. + if (e->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast<QKeyEvent*>(e); + if (ke->modifiers() && ke->key() > Qt::Key_Any + && ke->key() < Qt::Key_ydiaeresis) { + const QChar c = QLatin1Char(char(ke->key())); + if (ke->modifiers() == Qt::ControlModifier) + insertPlainText(QLatin1String("<Ctrl+") + c + QLatin1Char('>')); + else if (ke->modifiers() == Qt::AltModifier) + insertPlainText(QLatin1String("<Alt+") + c + QLatin1Char('>')); + else if (ke->modifiers() == Qt::ShiftModifier) + insertPlainText(QLatin1String("<Shift+") + c + QLatin1Char('>')); + return true; + } + } + return QTextEdit::event(e); + } +}; + +class MainWindow : public QMainWindow +{ +public: + MainWindow(); + + TestEdit *testEdit() const { return m_testEdit; } + +private: + TestEdit *m_testEdit; +}; + class tst_QShortcut : public QObject { Q_OBJECT @@ -140,62 +176,7 @@ protected: void testElement(); Result ambigResult; -}; - -class TestEdit : public QTextEdit -{ - Q_OBJECT -public: - TestEdit(QWidget *parent, const char *name) - : QTextEdit(parent) - { - setObjectName(name); - } - -protected: - bool event(QEvent *e) override - { - // Make testedit allow any Ctrl+Key as shortcut - if (e->type() == QEvent::ShortcutOverride) { - QKeyEvent *ke = static_cast<QKeyEvent*>(e); - if (ke->modifiers() == Qt::ControlModifier - && ke->key() > Qt::Key_Any - && ke->key() < Qt::Key_ydiaeresis) { - ke->ignore(); - return true; - } - } - - // If keypress not processed as normal, check for - // Ctrl+Key event, and input custom string for - // result comparison. - if (e->type() == QEvent::KeyPress) { - QKeyEvent *ke = static_cast<QKeyEvent*>(e); - if (ke->modifiers() && ke->key() > Qt::Key_Any - && ke->key() < Qt::Key_ydiaeresis) { - const QChar c = QLatin1Char(char(ke->key())); - if (ke->modifiers() == Qt::ControlModifier) - insertPlainText(QLatin1String("<Ctrl+") + c + QLatin1Char('>')); - else if (ke->modifiers() == Qt::AltModifier) - insertPlainText(QLatin1String("<Alt+") + c + QLatin1Char('>')); - else if (ke->modifiers() == Qt::ShiftModifier) - insertPlainText(QLatin1String("<Shift+") + c + QLatin1Char('>')); - return true; - } - } - return QTextEdit::event(e); - } -}; - -class MainWindow : public QMainWindow -{ -public: - MainWindow(); - - TestEdit *testEdit() const { return m_testEdit; } - -private: - TestEdit *m_testEdit; + QScopedPointer<MainWindow> mainWindow; }; MainWindow::MainWindow() @@ -334,11 +315,11 @@ void tst_QShortcut::number_data() Shift + Qt::Key_Plus on Shift + Qt::Key_Pluss Qt::Key_Plus on Shift + Qt::Key_Pluss */ - QTest::newRow("N002 - slot1") << SetupAccel << TriggerSlot1 << QString() << int(Qt::SHIFT | Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N002:Shift+M - [Shift+M]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_M) << int('M') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("N002 - slot1") << SetupAccel << TriggerSlot1 << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_M).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N002:Shift+M - [Shift+M]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_M).toCombined() << int('M') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N002:M - [Shift+M]") << TestAccel << NoWidget << QString() << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N002 - slot2") << SetupAccel << TriggerSlot2 << QString() << int(Qt::SHIFT | Qt::Key_Plus) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N002:Shift++ [Shift++]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; + QTest::newRow("N002 - slot2") << SetupAccel << TriggerSlot2 << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Plus).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N002:Shift++ [Shift++]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Plus).toCombined() << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("N002:+ [Shift++]") << TestAccel << NoWidget << QString() << int(Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N002 - clear") << ClearAll << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all @@ -356,8 +337,8 @@ void tst_QShortcut::number_data() Qt::Key_F1 on Shift + Qt::Key_F1 */ - QTest::newRow("N004 - slot1") << SetupAccel << TriggerSlot1 << QString() << int(Qt::SHIFT | Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N004:Shift+F1 - [Shift+F1]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("N004 - slot1") << SetupAccel << TriggerSlot1 << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_F1).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N004:Shift+F1 - [Shift+F1]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_F1).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N004:F1 - [Shift+F1]") << TestAccel << NoWidget << QString() << int(Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N004 - clear") << ClearAll << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all @@ -373,7 +354,7 @@ void tst_QShortcut::number_data() //QTest::newRow("N005a:Shift+Tab - [Tab]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT + Qt::Key_Tab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; // (Shift+)BackTab != Tab, but Shift+BackTab == Shift+Tab QTest::newRow("N005a:Backtab - [Tab]") << TestAccel << NoWidget << QString() << int(Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N005a:Shift+Backtab - [Tab]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N005a:Shift+Backtab - [Tab]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Backtab).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N005a - clear") << ClearAll << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences @@ -382,11 +363,11 @@ void tst_QShortcut::number_data() Qt::Key_Backtab on Shift + Qt::Key_Tab Shift + Qt::Key_Backtab on Shift + Qt::Key_Tab */ - QTest::newRow("N005b - slot1") << SetupAccel << TriggerSlot1 << QString() << int(Qt::SHIFT | Qt::Key_Tab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N005b - slot1") << SetupAccel << TriggerSlot1 << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Tab).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N005b:Tab - [Shift+Tab]") << TestAccel << NoWidget << QString() << int(Qt::Key_Tab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N005b:Shift+Tab - [Shift+Tab]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_Tab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("N005b:Shift+Tab - [Shift+Tab]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Tab).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N005b:BackTab - [Shift+Tab]") << TestAccel << NoWidget << QString() << int(Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N005b:Shift+BackTab - [Shift+Tab]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("N005b:Shift+BackTab - [Shift+Tab]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Backtab).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N005b - clear") << ClearAll << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences @@ -399,10 +380,10 @@ void tst_QShortcut::number_data() QTest::newRow("N006a:Tab - [BackTab]") << TestAccel << NoWidget << QString() << int(Qt::Key_Tab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // This should work, since platform dependent code will transform the // Shift+Tab into a Shift+BackTab, which should trigger the shortcut - QTest::newRow("N006a:Shift+Tab - [BackTab]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_Tab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; //XFAIL + QTest::newRow("N006a:Shift+Tab - [BackTab]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Tab).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; //XFAIL QTest::newRow("N006a:BackTab - [BackTab]") << TestAccel << NoWidget << QString() << int(Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; //commented out because the behaviour changed, those tests should be updated - //QTest::newRow("N006a:Shift+BackTab - [BackTab]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT + Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + //QTest::newRow("N006a:Shift+BackTab - [BackTab]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Backtab).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N006a - clear") << ClearAll << NoWidget<< QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences @@ -411,11 +392,11 @@ void tst_QShortcut::number_data() Qt::Key_Backtab on Shift + Qt::Key_Backtab Shift + Qt::Key_Backtab on Shift + Qt::Key_Backtab */ - QTest::newRow("N006b - slot1") << SetupAccel << TriggerSlot1 << QString() << int(Qt::SHIFT | Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N006b - slot1") << SetupAccel << TriggerSlot1 << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Backtab).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N006b:Tab - [Shift+BackTab]") << TestAccel << NoWidget << QString() << int(Qt::Key_Tab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N006b:Shift+Tab - [Shift+BackTab]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_Tab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("N006b:Shift+Tab - [Shift+BackTab]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Tab).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N006b:BackTab - [Shift+BackTab]") << TestAccel << NoWidget << QString() << int(Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N006b:Shift+BackTab - [Shift+BackTab]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; //XFAIL + QTest::newRow("N006b:Shift+BackTab - [Shift+BackTab]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Backtab).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; //XFAIL QTest::newRow("N006b - clear") << ClearAll << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all //=========================================== @@ -428,9 +409,9 @@ void tst_QShortcut::number_data() Shift + Qt::Key_F1 */ QTest::newRow("N007 - slot1") << SetupAccel << TriggerSlot1 << QString() << int(Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N007 - slot2") << SetupAccel << TriggerSlot2 << QString() << int(Qt::SHIFT | Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N007 - slot2") << SetupAccel << TriggerSlot2 << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_F1).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N007:F1") << TestAccel << NoWidget << QString() << int(Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("N007:Shift + F1") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; + QTest::newRow("N007:Shift + F1") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_F1).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("N007 - clear") << ClearAll << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences @@ -440,13 +421,13 @@ void tst_QShortcut::number_data() Alt + Qt::Key_M */ QTest::newRow("N01 - slot1") << SetupAccel << TriggerSlot1 << QString() << int(Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N02 - slot2") << SetupAccel << TriggerSlot2 << QString() << int(Qt::SHIFT | Qt::Key_M) << int('M') << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N03 - slot1") << SetupAccel << TriggerSlot1 << QString() << int(Qt::CTRL | Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N04 - slot2") << SetupAccel << TriggerSlot2 << QString() << int(Qt::ALT | Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N02 - slot2") << SetupAccel << TriggerSlot2 << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_M).toCombined() << int('M') << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N03 - slot1") << SetupAccel << TriggerSlot1 << QString() << QKeyCombination(Qt::CTRL, Qt::Key_M).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N04 - slot2") << SetupAccel << TriggerSlot2 << QString() << QKeyCombination(Qt::ALT, Qt::Key_M).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N:Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("N:Shift+Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_M) << int('M') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; - QTest::newRow("N:Ctrl+Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::CTRL | Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("N:Alt+Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::ALT | Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; + QTest::newRow("N:Shift+Qt::Key_M") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_M).toCombined() << int('M') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; + QTest::newRow("N:Ctrl+Qt::Key_M") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::CTRL, Qt::Key_M).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("N:Alt+Qt::Key_M") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::ALT, Qt::Key_M).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; /* Testing Single Sequence Ambiguity Qt::Key_M on shortcut2 @@ -461,11 +442,11 @@ void tst_QShortcut::number_data() Qt::Key_K */ QTest::newRow("N06 - slot1") << SetupAccel << TriggerSlot1 << QString() << int(Qt::Key_Aring) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N07 - slot2") << SetupAccel << TriggerSlot2 << QString() << int(Qt::SHIFT | Qt::Key_Aring) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N07 - slot2") << SetupAccel << TriggerSlot2 << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Aring).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N08 - slot2") << SetupAccel << TriggerSlot1 << QString() << int(Qt::Key_K) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N:Qt::Key_aring") << TestAccel << NoWidget << QString() << int(Qt::Key_Aring) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("N:Qt::Key_Aring") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_Aring) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; + QTest::newRow("N:Qt::Key_Aring") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Aring).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("N:Qt::Key_aring - Text Form") << TestAccel << NoWidget << QString() << 0 << 0xC5 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N:Qt::Key_Aring - Text Form") << TestAccel << NoWidget << QString() << int(Qt::SHIFT) << 0xC5 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("N:Qt::Qt::Key_K") << TestAccel << NoWidget << QString() << int(Qt::Key_K) << int('k') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; @@ -478,11 +459,11 @@ void tst_QShortcut::number_data() */ QTest::newRow("N10 - slot1") << SetupAccel << TriggerSlot1 << QString() << int(Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N11 - slot2") << SetupAccel << TriggerSlot2 << QString() << int(Qt::Key_I) << 0 << int(Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N12 - slot1") << SetupAccel << TriggerSlot1 << QString() << int(Qt::SHIFT | Qt::Key_I) << 0 << int(Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << NoResult; + QTest::newRow("N12 - slot1") << SetupAccel << TriggerSlot1 << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_I).toCombined() << 0 << int(Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N:Qt::Key_M (2)") << TestAccel << NoWidget << QString() << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N:Qt::Key_I, Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::Key_I) << int('i') << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << Slot2Triggered; - QTest::newRow("N:Shift+Qt::Key_I, Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_I) << int('I') << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("N:Shift+Qt::Key_I, Qt::Key_M") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_I).toCombined() << int('I') << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N:end") << TestEnd << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; } @@ -524,10 +505,10 @@ void tst_QShortcut::text_data() Ctrl + Qt::Key_Plus on Ctrl + Qt::Key_Pluss */ QTest::newRow("T002 - slot1") << SetupAccel << TriggerSlot1 << QString("Shift+M") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("T002:Shift+M - [Shift+M]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_M) << int('M') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("T002:Shift+M - [Shift+M]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_M).toCombined() << int('M') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("T002:M - [Shift+M]") << TestAccel << NoWidget << QString() << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T002 - slot2") << SetupAccel << TriggerSlot2 << QString("Shift++") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("T002:Shift++ [Shift++]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; + QTest::newRow("T002:Shift++ [Shift++]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Plus).toCombined() << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("T002:+ [Shift++]") << TestAccel << NoWidget << QString() << int(Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T002 - clear") << ClearAll << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all @@ -539,7 +520,7 @@ void tst_QShortcut::text_data() QTest::newRow("T002b - slot1") << SetupAccel << TriggerSlot1 << QString("Ctrl++") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; //commented out because the behaviour changed, those tests should be updated //QTest::newRow("T002b:Shift+Ctrl++ [Ctrl++]")<< TestAccel << NoWidget << QString() << int(Qt::SHIFT + Qt::CTRL + Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("T002b:Ctrl++ [Ctrl++]") << TestAccel << NoWidget << QString() << int(Qt::CTRL | Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("T002b:Ctrl++ [Ctrl++]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::CTRL, Qt::Key_Plus).toCombined() << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("T002b:+ [Ctrl++]") << TestAccel << NoWidget << QString() << int(Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T002b - clear") << ClearAll << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all @@ -558,7 +539,7 @@ void tst_QShortcut::text_data() Qt::Key_F1 on Shift + Qt::Key_F1 */ QTest::newRow("T004 - slot1") << SetupAccel << TriggerSlot1 << QString("Shift+F1") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("T004:Shift+F1 - [Shift+F1]") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("T004:Shift+F1 - [Shift+F1]") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_F1).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("T004:F1 - [Shift+F1]") << TestAccel << NoWidget << QString() << int(Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T004 - clear") << ClearAll << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all @@ -574,7 +555,7 @@ void tst_QShortcut::text_data() QTest::newRow("T007 - slot1") << SetupAccel << TriggerSlot1 << QString("F1") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T007 - slot2") << SetupAccel << TriggerSlot2 << QString("Shift+F1") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T007:F1") << TestAccel << NoWidget << QString() << int(Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("T007:Shift + F1") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; + QTest::newRow("T007:Shift + F1") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_F1).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("T007 - clear") << ClearAll << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences @@ -589,9 +570,9 @@ void tst_QShortcut::text_data() QTest::newRow("T04 - slot2") << SetupAccel << TriggerSlot2 << QString("Alt+M") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T:Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("T:Shift + Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_M) << int('M') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; - QTest::newRow("T:Ctrl + Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::CTRL | Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("T:Alt + Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::ALT | Qt::Key_M) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; + QTest::newRow("T:Shift + Qt::Key_M") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_M).toCombined() << int('M') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; + QTest::newRow("T:Ctrl + Qt::Key_M") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::CTRL, Qt::Key_M).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("T:Alt + Qt::Key_M") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::ALT, Qt::Key_M).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; /* Testing Single Sequence Ambiguity Qt::Key_M on shortcut2 @@ -610,7 +591,7 @@ void tst_QShortcut::text_data() QTest::newRow("T07 - slot2") << SetupAccel << TriggerSlot2 << QString::fromLatin1("Shift+\x0C5")<< 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T08 - slot2") << SetupAccel << TriggerSlot1 << QString("K") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T:Qt::Key_aring") << TestAccel << NoWidget << QString() << int(Qt::Key_Aring) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("T:Qt::Key_Aring") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_Aring) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; + QTest::newRow("T:Qt::Key_Aring") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_Aring).toCombined() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("T:Qt::Key_aring - Text Form") << TestAccel << NoWidget << QString() << 0 << 0xC5 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("T:Qt::Key_Aring - Text Form") << TestAccel << NoWidget << QString() << int(Qt::SHIFT) << 0xC5 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("T:Qt::Key_K") << TestAccel << NoWidget << QString() << int(Qt::Key_K) << int('k') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; @@ -626,7 +607,7 @@ void tst_QShortcut::text_data() QTest::newRow("T12 - slot1") << SetupAccel << TriggerSlot1 << QString("Shift+I, M")<< 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T:Qt::Key_M (2)") << TestAccel << NoWidget << QString() << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("T:Qt::Key_I, Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::Key_I) << int('i') << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << Slot2Triggered; - QTest::newRow("T:Shift+Qt::Key_I, Qt::Key_M") << TestAccel << NoWidget << QString() << int(Qt::SHIFT | Qt::Key_I) << int('I') << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << Slot1Triggered; + QTest::newRow("T:Shift+Qt::Key_I, Qt::Key_M") << TestAccel << NoWidget << QString() << QKeyCombination(Qt::SHIFT, Qt::Key_I).toCombined() << int('I') << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("T:end") << TestEnd << NoWidget << QString() << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; } @@ -1083,7 +1064,6 @@ void tst_QShortcut::context() // Focus on 'other1' edit, so Active Window context should trigger other1->activateWindow(); // <--- - QApplication::setActiveWindow(other1); QCOMPARE(QApplication::activeWindow(), other1->window()); QCOMPARE(QApplication::focusWidget(), static_cast<QWidget *>(other1)); @@ -1175,7 +1155,6 @@ void tst_QShortcut::duplicatedShortcutOverride() w.resize(200, 200); w.move(QGuiApplication::primaryScreen()->availableGeometry().center() - QPoint(100, 100)); w.show(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QTest::keyPress(w.windowHandle(), Qt::Key_A); QCoreApplication::processEvents(); @@ -1273,8 +1252,6 @@ void tst_QShortcut::testElement() if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) QSKIP("Wayland: This fails. Figure out why."); - static QScopedPointer<MainWindow> mainW; - currentResult = NoResult; QFETCH(tst_QShortcut::Action, action); QFETCH(tst_QShortcut::Widget, testWidget); @@ -1289,28 +1266,35 @@ void tst_QShortcut::testElement() QFETCH(int, c4); QFETCH(tst_QShortcut::Result, result); - if (mainW.isNull()) { - mainW.reset(new MainWindow); - mainW->setWindowTitle(QTest::currentTestFunction()); - mainW->show(); - mainW->activateWindow(); - QVERIFY(QTest::qWaitForWindowActive(mainW.data())); - } + auto mainWindowDeleter = qScopeGuard([&]{ + if (action == TestEnd) + mainWindow.reset(); + }); + + if (mainWindow.isNull()) + mainWindow.reset(new MainWindow); + mainWindow->setWindowTitle(QTest::currentTestFunction()); + mainWindow->show(); + mainWindow->activateWindow(); + // Don't use QVERIFY here; the data function uses QEXPECT_FAIL, + // which would result in an XPASS failure. + if (!QTest::qWaitForWindowActive(mainWindow.data())) + QVERIFY(false); switch (action) { case ClearAll: - qDeleteAll(mainW->findChildren<QShortcut *>()); + qDeleteAll(mainWindow->findChildren<QShortcut *>()); break; case SetupAccel: - setupShortcut(mainW.data(), txt, testWidget, txt.isEmpty() + setupShortcut(mainWindow.data(), txt, testWidget, txt.isEmpty() ? QKeySequence(k1, k2, k3, k4) : QKeySequence::fromString(txt)); break; case TestAccel: - sendKeyEvents(mainW.data(), k1, char16_t(c1), k2, char16_t(c2), k3, char16_t(c3), k4, char16_t(c4)); + sendKeyEvents(mainWindow.data(), k1, char16_t(c1), k2, char16_t(c2), k3, char16_t(c3), k4, char16_t(c4)); QCOMPARE(currentResult, result); break; case TestEnd: - mainW.reset(); + // taken care of by the mainWindowDeleter break; } } @@ -1359,14 +1343,17 @@ void tst_QShortcut::keys() QSignalSpy spy(sc, &QShortcut::activated); le.setFocus(); le.show(); - QVERIFY(QTest::qWaitForWindowActive(&le)); + if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QVERIFY(QTest::qWaitForWindowActive(&le)); + else + QTRY_VERIFY(le.windowHandle()->isActive()); QCOMPARE(QApplication::focusWidget(), &le); QTest::keyEvent(QTest::Press, QApplication::focusWidget(), Qt::Key_Enter); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QTest::keyEvent(QTest::Press, QApplication::focusWidget(), Qt::Key_Return); - QTRY_COMPARE(spy.count(), 2); + QTRY_COMPARE(spy.size(), 2); } QTEST_MAIN(tst_QShortcut) diff --git a/tests/auto/widgets/kernel/qsizepolicy/CMakeLists.txt b/tests/auto/widgets/kernel/qsizepolicy/CMakeLists.txt index 36450d4f80..efdd72db73 100644 --- a/tests/auto/widgets/kernel/qsizepolicy/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qsizepolicy/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qsizepolicy.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qsizepolicy Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsizepolicy LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qsizepolicy SOURCES tst_qsizepolicy.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets Qt::WidgetsPrivate diff --git a/tests/auto/widgets/kernel/qsizepolicy/tst_qsizepolicy.cpp b/tests/auto/widgets/kernel/qsizepolicy/tst_qsizepolicy.cpp index d6a5ec218d..19d267f7e5 100644 --- a/tests/auto/widgets/kernel/qsizepolicy/tst_qsizepolicy.cpp +++ b/tests/auto/widgets/kernel/qsizepolicy/tst_qsizepolicy.cpp @@ -1,32 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -#include <QTest> #include <qsizepolicy.h> Q_DECLARE_METATYPE(Qt::Orientations) @@ -34,6 +8,8 @@ Q_DECLARE_METATYPE(QSizePolicy) Q_DECLARE_METATYPE(QSizePolicy::Policy) Q_DECLARE_METATYPE(QSizePolicy::ControlType) +#include <QTest> + class tst_QSizePolicy : public QObject { Q_OBJECT @@ -109,7 +85,7 @@ void tst_QSizePolicy::constExpr() { /* gcc < 4.8.0 has problems with init'ing variant members in constexpr ctors */ /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54922 */ -#if !defined(Q_CC_GNU) || defined(Q_CC_INTEL) || defined(Q_CC_CLANG) || Q_CC_GNU >= 408 +#if !defined(Q_CC_GNU) || defined(Q_CC_CLANG) || Q_CC_GNU >= 408 // check that certain ctors are constexpr (compile-only): { constexpr QSizePolicy sp; Q_UNUSED(sp); } { constexpr QSizePolicy sp = QSizePolicy(); Q_UNUSED(sp); } @@ -119,7 +95,7 @@ void tst_QSizePolicy::constExpr() { // QTBUG-69983: For ControlType != QSizePolicy::DefaultType, qCountTrailingZeroBits() // is used, which MSVC 15.8.1 does not consider constexpr due to built-ins -# if defined(QT_HAS_CONSTEXPR_BUILTINS) && (!defined(Q_CC_MSVC) || _MSC_VER < 1915) +# if defined(QT_HAS_CONSTEXPR_BITOPS) constexpr auto sp = QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed, QSizePolicy::CheckBox); # else constexpr auto sp = QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding, QSizePolicy::DefaultType); diff --git a/tests/auto/widgets/kernel/qstackedlayout/CMakeLists.txt b/tests/auto/widgets/kernel/qstackedlayout/CMakeLists.txt index 2c1c3da277..5701f455b5 100644 --- a/tests/auto/widgets/kernel/qstackedlayout/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qstackedlayout/CMakeLists.txt @@ -1,13 +1,21 @@ -# Generated from qstackedlayout.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qstackedlayout Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qstackedlayout LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qstackedlayout SOURCES tst_qstackedlayout.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/kernel/qstackedlayout/tst_qstackedlayout.cpp b/tests/auto/widgets/kernel/qstackedlayout/tst_qstackedlayout.cpp index 711ffeda03..ca002a1375 100644 --- a/tests/auto/widgets/kernel/qstackedlayout/tst_qstackedlayout.cpp +++ b/tests/auto/widgets/kernel/qstackedlayout/tst_qstackedlayout.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -36,6 +11,8 @@ #include <QPushButton> #include <QSignalSpy> +#include <QtWidgets/private/qapplication_p.h> + class tst_QStackedLayout : public QObject { Q_OBJECT @@ -132,7 +109,7 @@ void tst_QStackedLayout::testCase() // One widget added to layout QWidget *w1 = new QWidget(testWidget); testLayout->addWidget(w1); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toInt(), 0); spy.clear(); QCOMPARE(testLayout->currentIndex(), 0); @@ -149,7 +126,7 @@ void tst_QStackedLayout::testCase() // Change the current index testLayout->setCurrentIndex(1); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toInt(), 1); spy.clear(); QCOMPARE(testLayout->currentIndex(), 1); @@ -163,7 +140,7 @@ void tst_QStackedLayout::testCase() // Second widget removed from layout; back to nothing testLayout->removeWidget(w2); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toInt(), -1); spy.clear(); QCOMPARE(testLayout->currentIndex(), -1); @@ -312,7 +289,7 @@ void tst_QStackedLayout::keepFocusAfterSetCurrent() stackLayout->setCurrentIndex(0); testWidget->show(); - QApplication::setActiveWindow(testWidget); + QApplicationPrivate::setActiveWindow(testWidget); QVERIFY(QTest::qWaitForWindowActive(testWidget)); edit1->setFocus(); diff --git a/tests/auto/widgets/kernel/qtooltip/CMakeLists.txt b/tests/auto/widgets/kernel/qtooltip/CMakeLists.txt index a07cd1aa65..af2de80e24 100644 --- a/tests/auto/widgets/kernel/qtooltip/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qtooltip/CMakeLists.txt @@ -1,13 +1,21 @@ -# Generated from qtooltip.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtooltip Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtooltip LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qtooltip SOURCES tst_qtooltip.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/kernel/qtooltip/tst_qtooltip.cpp b/tests/auto/widgets/kernel/qtooltip/tst_qtooltip.cpp index 092adc58af..bc0624c9ab 100644 --- a/tests/auto/widgets/kernel/qtooltip/tst_qtooltip.cpp +++ b/tests/auto/widgets/kernel/qtooltip/tst_qtooltip.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -36,6 +11,8 @@ #include <qwhatsthis.h> #include <qscreen.h> +#include <QtWidgets/private/qapplication_p.h> + class tst_QToolTip : public QObject { Q_OBJECT @@ -43,12 +20,13 @@ class tst_QToolTip : public QObject private slots: void init(); void cleanup(); - void task183679_data(); - void task183679(); + void keyEvent_data(); + void keyEvent(); void whatsThis(); void setPalette(); void qtbug64550_stylesheet(); void dontCrashOutsideScreenGeometry(); + void marginSetWithStyleSheet(); }; void tst_QToolTip::init() @@ -62,11 +40,11 @@ void tst_QToolTip::cleanup() qApp->setStyleSheet(QString()); } -class Widget_task183679 : public QWidget +class Widget : public QWidget { Q_OBJECT public: - Widget_task183679(QWidget *parent = nullptr) : QWidget(parent) {} + Widget(QWidget *parent = nullptr) : QWidget(parent) {} void showDelayedToolTip(int msecs) { @@ -78,25 +56,32 @@ public: private slots: void showToolTip() { - QToolTip::showText(mapToGlobal(QPoint(0, 0)), Widget_task183679::toolTipText(), this); + QToolTip::showText(mapToGlobal(QPoint(0, 0)), Widget::toolTipText(), this); } }; Q_DECLARE_METATYPE(Qt::Key) -void tst_QToolTip::task183679_data() +void tst_QToolTip::keyEvent_data() { QTest::addColumn<Qt::Key>("key"); QTest::addColumn<bool>("visible"); - QTest::newRow("non-modifier") << Qt::Key_A << true; + QTest::newRow("non-modifier") << Qt::Key_A << +#if defined(Q_OS_MACOS) + // macOS natively hides tooltips on non-modifier key events, + // so QTipLabel::eventFilter does the same. Match that here. + false; +#else + true; +#endif QTest::newRow("Shift") << Qt::Key_Shift << true; QTest::newRow("Control") << Qt::Key_Control << true; QTest::newRow("Alt") << Qt::Key_Alt << true; QTest::newRow("Meta") << Qt::Key_Meta << true; } -void tst_QToolTip::task183679() +void tst_QToolTip::keyEvent() { if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) QSKIP("Wayland: This fails. Figure out why."); @@ -104,11 +89,7 @@ void tst_QToolTip::task183679() QFETCH(Qt::Key, key); QFETCH(bool, visible); -#ifdef Q_OS_MAC - QSKIP("This test fails in the CI system, QTBUG-30040"); -#endif - - Widget_task183679 widget; + Widget widget; widget.move(QGuiApplication::primaryScreen()->availableGeometry().topLeft() + QPoint(50, 50)); // Ensure cursor is not over tooltip, which causes it to hide #ifndef QT_NO_CURSOR @@ -117,7 +98,6 @@ void tst_QToolTip::task183679() widget.setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1Char(' ') + QLatin1String(QTest::currentDataTag())); widget.show(); - QApplication::setActiveWindow(&widget); QVERIFY(QTest::qWaitForWindowActive(&widget)); widget.showDelayedToolTip(100); @@ -137,7 +117,8 @@ void tst_QToolTip::task183679() static QWidget *findWhatsThat() { - foreach (QWidget *widget, QApplication::topLevelWidgets()) { + const auto widgets = QApplication::topLevelWidgets(); + for (QWidget *widget : widgets) { if (widget->inherits("QWhatsThat")) return widget; } @@ -208,10 +189,9 @@ void tst_QToolTip::qtbug64550_stylesheet() if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) QSKIP("Wayland: This fails. Figure out why."); - Widget_task183679 widget; + Widget widget; widget.setStyleSheet(QStringLiteral("* { font-size: 48pt; }\n")); widget.show(); - QApplication::setActiveWindow(&widget); QVERIFY(QTest::qWaitForWindowActive(&widget)); widget.showDelayedToolTip(100); @@ -220,7 +200,7 @@ void tst_QToolTip::qtbug64550_stylesheet() QVERIFY(toolTip); QTRY_VERIFY(toolTip->isVisible()); - const QRect boundingRect = QFontMetrics(widget.font()).boundingRect(Widget_task183679::toolTipText()); + const QRect boundingRect = QFontMetrics(widget.font()).boundingRect(Widget::toolTipText()); const QSize toolTipSize = toolTip->size(); QVERIFY2(toolTipSize.width() >= boundingRect.width() && toolTipSize.height() >= boundingRect.height(), @@ -233,5 +213,30 @@ void tst_QToolTip::dontCrashOutsideScreenGeometry() { QToolTip::hideText(); } +void tst_QToolTip::marginSetWithStyleSheet() +{ + const char *toolTipText = "Test Tool Tip"; + + qApp->setStyleSheet("QToolTip {font-size: 8px; margin: 5px;}"); + QToolTip::showText(QGuiApplication::primaryScreen()->availableGeometry().topLeft(), toolTipText); + QTRY_VERIFY(QToolTip::isVisible()); + QWidget *toolTip = findToolTip(); + QVERIFY(toolTip); + QTRY_VERIFY(toolTip->isVisible()); + int toolTipHeight = toolTip->size().height(); + qApp->setStyleSheet(QString()); + QToolTip::hideText(); + + qApp->setStyleSheet("QToolTip {font-size: 8px; margin: 10px;}"); + QToolTip::showText(QGuiApplication::primaryScreen()->availableGeometry().topLeft(), toolTipText); + QTRY_VERIFY(QToolTip::isVisible()); + toolTip = findToolTip(); + QVERIFY(toolTip); + QTRY_VERIFY(toolTip->isVisible()); + QCOMPARE_LE(toolTip->size().height(), toolTipHeight + 10); + qApp->setStyleSheet(QString()); + QToolTip::hideText(); +} + QTEST_MAIN(tst_QToolTip) #include "tst_qtooltip.moc" diff --git a/tests/auto/widgets/kernel/qwidget/BLACKLIST b/tests/auto/widgets/kernel/qwidget/BLACKLIST index 7bc17127f5..4c17af245b 100644 --- a/tests/auto/widgets/kernel/qwidget/BLACKLIST +++ b/tests/auto/widgets/kernel/qwidget/BLACKLIST @@ -1,71 +1,49 @@ -# OSX QTBUG-25300 QTBUG-45502 -[normalGeometry] -ubuntu-16.04 -[restoreVersion1Geometry] -ubuntu-16.04 -[focusProxyAndInputMethods] -rhel-7.6 -centos -opensuse-leap -ubuntu [raise] opensuse-leap -# QTBUG-68175 -opensuse-42.3 -[childEvents] -macos -[renderInvisible] -macos -[optimizedResizeMove] -osx [optimizedResize_topLevel] osx +[render_windowOpacity] +macos arm [render_systemClip] osx -[showMinimizedKeepsFocus] -macos -[maskedUpdate] -opensuse -opensuse-leap -[moveInResizeEvent] -ubuntu-16.04 [multipleToplevelFocusCheck] -rhel-7.6 centos opensuse-leap ubuntu - -[syntheticEnterLeave] -macos # Can't move cursor (QTBUG-76312) -[taskQTBUG_4055_sendSyntheticEnterLeave] -macos # Can't move cursor (QTBUG-76312) - +sles-15 # QTBUG-87668 -[reparent] -android -[windowState] -android [showMinimizedKeepsFocus] android +macos-13 ci +macos-14 ci [normalGeometry] android [saveRestoreGeometry] android [optimizedResizeMove] android -[resizeEvent] -android [update] android [scroll] android -[setWindowGeometry] +[moveChild] android -[windowMoveResize] +[multipleToplevelFocusCheck] android -[moveChild] +[renderInvisible] android -[showAndMoveChild] +[updateWhileMinimized] android -[multipleToplevelFocusCheck] +[doubleRepaint] +android +[setMaskInResizeEvent] +android +[activateWindow] +android +[optimizedResize_topLevel] +android +[hoverPosition] +macos-14 x86 +# QTBUG-124291 +[setParentChangesFocus:make dialog parentless, after] android diff --git a/tests/auto/widgets/kernel/qwidget/CMakeLists.txt b/tests/auto/widgets/kernel/qwidget/CMakeLists.txt index d04db40e87..f18bc98bb3 100644 --- a/tests/auto/widgets/kernel/qwidget/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qwidget/CMakeLists.txt @@ -1,20 +1,15 @@ -# Generated from qwidget.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qwidget Test: ##################################################################### -qt_internal_add_test(tst_qwidget - SOURCES - tst_qwidget.cpp - PUBLIC_LIBRARIES - Qt::CorePrivate - Qt::Gui - Qt::GuiPrivate - Qt::TestPrivate - Qt::Widgets - Qt::WidgetsPrivate -) +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qwidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() # Resources: set(qwidget_resource_files @@ -24,17 +19,20 @@ set(qwidget_resource_files "hellotr_la.qm" ) -qt_internal_add_resource(tst_qwidget "qwidget" - PREFIX - "/" - FILES - ${qwidget_resource_files} +qt_internal_add_test(tst_qwidget + SOURCES + tst_qwidget.cpp + LIBRARIES + Qt::CorePrivate + Qt::Gui + Qt::GuiPrivate + Qt::TestPrivate + Qt::Widgets + Qt::WidgetsPrivate + TESTDATA ${qwidget_resource_files} + BUILTIN_TESTDATA ) - -#### Keys ignored in scope 1:.:.:qwidget.pro:<TRUE>: -# testcase.timeout = "600" - ## Scopes: ##################################################################### @@ -43,16 +41,8 @@ qt_internal_extend_target(tst_qwidget CONDITION AIX -fpermissive ) -qt_internal_extend_target(tst_qwidget CONDITION APPLE - SOURCES - tst_qwidget_mac_helpers.mm - PUBLIC_LIBRARIES - ${FWAppKit} - ${FWSecurity} -) - qt_internal_extend_target(tst_qwidget CONDITION WIN32 - PUBLIC_LIBRARIES + LIBRARIES gdi32 user32 ) diff --git a/tests/auto/widgets/kernel/qwidget/qwidget.qrc b/tests/auto/widgets/kernel/qwidget/qwidget.qrc deleted file mode 100644 index 597ea872a5..0000000000 --- a/tests/auto/widgets/kernel/qwidget/qwidget.qrc +++ /dev/null @@ -1,8 +0,0 @@ -<!DOCTYPE RCC><RCC version="1.0"> - <qresource> - <file>geometry.dat</file> - <file>geometry-maximized.dat</file> - <file>geometry-fullscreen.dat</file> - <file>hellotr_la.qm</file> - </qresource> -</RCC> diff --git a/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Motif_data0.qsnap b/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Motif_data0.qsnap Binary files differdeleted file mode 100644 index b3473cdefe..0000000000 --- a/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Motif_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Motif_data1.qsnap b/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Motif_data1.qsnap Binary files differdeleted file mode 100644 index 10007733ca..0000000000 --- a/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Motif_data1.qsnap +++ /dev/null diff --git a/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Motif_data2.qsnap b/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Motif_data2.qsnap Binary files differdeleted file mode 100644 index cde5964a30..0000000000 --- a/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Motif_data2.qsnap +++ /dev/null diff --git a/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Motif_data3.qsnap b/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Motif_data3.qsnap Binary files differdeleted file mode 100644 index 23ea1410e4..0000000000 --- a/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Motif_data3.qsnap +++ /dev/null diff --git a/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Windows_data0.qsnap b/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Windows_data0.qsnap Binary files differdeleted file mode 100644 index a8918c1d1b..0000000000 --- a/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Windows_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Windows_data1.qsnap b/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Windows_data1.qsnap Binary files differdeleted file mode 100644 index 0981cf5dd1..0000000000 --- a/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Windows_data1.qsnap +++ /dev/null diff --git a/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Windows_data2.qsnap b/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Windows_data2.qsnap Binary files differdeleted file mode 100644 index 75d09136cf..0000000000 --- a/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Windows_data2.qsnap +++ /dev/null diff --git a/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Windows_data3.qsnap b/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Windows_data3.qsnap Binary files differdeleted file mode 100644 index f58f74d030..0000000000 --- a/tests/auto/widgets/kernel/qwidget/testdata/paintEvent/res_Windows_data3.qsnap +++ /dev/null diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index 014e31d1a9..94bf9fff5c 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "../../../shared/highdpi.h" @@ -38,6 +13,7 @@ #include <qlineedit.h> #include <qlistview.h> #include <qmessagebox.h> +#include <qmimedata.h> #include <qpainter.h> #include <qpoint.h> #include <qpushbutton.h> @@ -52,6 +28,7 @@ #include <qmainwindow.h> #include <qdockwidget.h> #include <qrandom.h> +#include <qsignalspy.h> #include <qstylehints.h> #include <qtoolbar.h> #include <qtoolbutton.h> @@ -61,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> @@ -73,15 +51,13 @@ #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> using namespace QTestPrivate; +using namespace Qt::StringLiterals; #if defined(Q_OS_WIN) # include <QtCore/qt_windows.h> @@ -146,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 @@ -159,7 +163,10 @@ public: public slots: void initTestCase(); void cleanup(); + private slots: + void nativeWindowAttribute(); + void addActionOverloads(); void getSetCheck(); void fontPropagation(); void fontPropagation2(); @@ -184,10 +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(); @@ -195,12 +207,16 @@ private slots: void appFocusWidgetWhenLosingFocusProxy(); void explicitTabOrderWithComplexWidget(); void explicitTabOrderWithSpinBox_QTBUG81097(); + void tabOrderList(); + void tabOrderComboBox_data(); + void tabOrderComboBox(); #if defined(Q_OS_WIN) void activation(); #endif void reparent(); void setScreen(); void windowState(); + void resizePropagation(); void showMaximized(); void showFullScreen(); void showMinimized(); @@ -218,6 +234,8 @@ private slots: void saveRestoreGeometry(); void restoreVersion1Geometry_data(); void restoreVersion1Geometry(); + void restoreGeometryAfterScreenChange_data(); + void restoreGeometryAfterScreenChange(); void widgetAt(); #ifdef Q_OS_MACOS @@ -238,11 +256,14 @@ private slots: void ensureCreated(); void createAndDestroy(); + void eventsAndAttributesOnDestroy(); void winIdChangeEvent(); void persistentWinId(); void showNativeChild(); + void closeAndShowNativeChild(); + void closeAndShowWithNativeChild(); void transientParent(); - void qobject_castInDestroyedSlot(); + void qobject_castOnDestruction(); void showHideEvent_data(); void showHideEvent(); @@ -294,6 +315,8 @@ private slots: void childEvents(); void render(); + void renderChildFillsBackground(); + void renderTargetOffset(); void renderInvisible(); void renderWithPainter(); void render_task188133(); @@ -328,6 +351,8 @@ private slots: void resizeInPaintEvent(); void opaqueChildren(); + void dumpObjectTree(); + void setMaskInResizeEvent(); void moveInResizeEvent(); @@ -356,7 +381,10 @@ private slots: void maskedUpdate(); #ifndef QT_NO_CURSOR void syntheticEnterLeave(); + void enterLeaveOnWindowShowHide_data(); + void enterLeaveOnWindowShowHide(); void taskQTBUG_4055_sendSyntheticEnterLeave(); + void hoverPosition(); void underMouse(); void taskQTBUG_27643_enterEvents(); #endif @@ -368,6 +396,8 @@ private slots: void focusWidget_task254563(); void rectOutsideCoordinatesLimit_task144779(); void setGraphicsEffect(); + void render_graphicsEffect_data(); + void render_graphicsEffect(); #ifdef QT_BUILD_INTERNAL void destroyBackingStore(); @@ -378,7 +408,8 @@ private slots: void openModal_taskQTBUG_5804(); void focusProxy(); - void focusProxyAndInputMethods(); + void imEnabledNotImplemented(); + #ifdef QT_BUILD_INTERNAL void scrollWithoutBackingStore(); #endif @@ -425,10 +456,27 @@ private slots: void receivesApplicationFontChangeEvent(); void receivesApplicationPaletteChangeEvent(); void deleteWindowInCloseEvent(); + void quitOnClose(); -private: - bool ensureScreenSize(int width, int height); + void setParentChangesFocus_data(); + void setParentChangesFocus(); + + void activateWhileModalHidden(); +#ifdef Q_OS_ANDROID + void showFullscreenAndroid(); +#endif + + void setVisibleDuringDestruction(); + + void explicitShowHide(); + + void dragEnterLeaveSymmetry(); + + void reparentWindowHandles_data(); + void reparentWindowHandles(); + +private: const QString m_platform; QSize m_testWidgetSize; QPoint m_availableTopLeft; @@ -436,13 +484,17 @@ private: const bool m_windowsAnimationsEnabled; QPointingDevice *m_touchScreen; const int m_fuzz; -}; + QPalette simplePalette(); -bool tst_QWidget::ensureScreenSize(int width, int height) -{ - const QSize available = QGuiApplication::primaryScreen()->availableGeometry().size(); - return (available.width() >= width && available.height() >= height); -} +private: + enum class ScreenPosition { + OffAbove, + OffLeft, + OffBelow, + OffRight, + Contained + }; +}; // Testing get/set functions void tst_QWidget::getSetCheck() @@ -458,18 +510,24 @@ void tst_QWidget::getSetCheck() QVERIFY(var1.data() != obj1.style()); QVERIFY(obj1.style() != nullptr); // style can never be 0 for a widget + const QRegularExpression negativeNotPossible(u"^.*Negative sizes \\(.*\\) are not possible$"_s); + const QRegularExpression largestAllowedSize(u"^.*The largest allowed size is \\(.*\\)$"_s); // int QWidget::minimumWidth() // void QWidget::setMinimumWidth(int) obj1.setMinimumWidth(0); QCOMPARE(obj1.minimumWidth(), 0); + QTest::ignoreMessage(QtWarningMsg, negativeNotPossible); obj1.setMinimumWidth(INT_MIN); QCOMPARE(obj1.minimumWidth(), 0); // A widgets width can never be less than 0 + QTest::ignoreMessage(QtWarningMsg, largestAllowedSize); obj1.setMinimumWidth(INT_MAX); child1.setMinimumWidth(0); QCOMPARE(child1.minimumWidth(), 0); + QTest::ignoreMessage(QtWarningMsg, negativeNotPossible); child1.setMinimumWidth(INT_MIN); QCOMPARE(child1.minimumWidth(), 0); // A widgets width can never be less than 0 + QTest::ignoreMessage(QtWarningMsg, largestAllowedSize); child1.setMinimumWidth(INT_MAX); QCOMPARE(child1.minimumWidth(), QWIDGETSIZE_MAX); // The largest minimum size should only be as big as the maximium @@ -477,14 +535,18 @@ void tst_QWidget::getSetCheck() // void QWidget::setMinimumHeight(int) obj1.setMinimumHeight(0); QCOMPARE(obj1.minimumHeight(), 0); + QTest::ignoreMessage(QtWarningMsg, negativeNotPossible); obj1.setMinimumHeight(INT_MIN); QCOMPARE(obj1.minimumHeight(), 0); // A widgets height can never be less than 0 + QTest::ignoreMessage(QtWarningMsg, largestAllowedSize); obj1.setMinimumHeight(INT_MAX); child1.setMinimumHeight(0); QCOMPARE(child1.minimumHeight(), 0); + QTest::ignoreMessage(QtWarningMsg, negativeNotPossible); child1.setMinimumHeight(INT_MIN); QCOMPARE(child1.minimumHeight(), 0); // A widgets height can never be less than 0 + QTest::ignoreMessage(QtWarningMsg, largestAllowedSize); child1.setMinimumHeight(INT_MAX); QCOMPARE(child1.minimumHeight(), QWIDGETSIZE_MAX); // The largest minimum size should only be as big as the maximium @@ -492,8 +554,10 @@ void tst_QWidget::getSetCheck() // void QWidget::setMaximumWidth(int) obj1.setMaximumWidth(0); QCOMPARE(obj1.maximumWidth(), 0); + QTest::ignoreMessage(QtWarningMsg, negativeNotPossible); obj1.setMaximumWidth(INT_MIN); QCOMPARE(obj1.maximumWidth(), 0); // A widgets width can never be less than 0 + QTest::ignoreMessage(QtWarningMsg, largestAllowedSize); obj1.setMaximumWidth(INT_MAX); QCOMPARE(obj1.maximumWidth(), QWIDGETSIZE_MAX); // QWIDGETSIZE_MAX is the abs max, not INT_MAX @@ -501,8 +565,10 @@ void tst_QWidget::getSetCheck() // void QWidget::setMaximumHeight(int) obj1.setMaximumHeight(0); QCOMPARE(obj1.maximumHeight(), 0); + QTest::ignoreMessage(QtWarningMsg, negativeNotPossible); obj1.setMaximumHeight(INT_MIN); QCOMPARE(obj1.maximumHeight(), 0); // A widgets height can never be less than 0 + QTest::ignoreMessage(QtWarningMsg, largestAllowedSize); obj1.setMaximumHeight(INT_MAX); QCOMPARE(obj1.maximumHeight(), QWIDGETSIZE_MAX); // QWIDGETSIZE_MAX is the abs max, not INT_MAX @@ -562,6 +628,7 @@ void tst_QWidget::getSetCheck() QBoxLayout *var11 = new QBoxLayout(QBoxLayout::LeftToRight); obj1.setLayout(var11); QCOMPARE(static_cast<QLayout *>(var11), obj1.layout()); + QTest::ignoreMessage(QtWarningMsg, "QWidget::setLayout: Cannot set layout to 0"); obj1.setLayout(nullptr); QCOMPARE(static_cast<QLayout *>(var11), obj1.layout()); // You cannot set a 0-pointer layout, that keeps the current delete var11; // This will remove the layout from the widget @@ -585,7 +652,7 @@ void tst_QWidget::getSetCheck() #if defined (Q_OS_WIN) obj1.setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint); const HWND handle = reinterpret_cast<HWND>(obj1.winId()); // explicitly create window handle - QVERIFY(GetWindowLong(handle, GWL_STYLE) & LONG(WS_POPUP)); + QVERIFY(GetWindowLongPtr(handle, GWL_STYLE) & LONG_PTR(WS_POPUP)); #endif } @@ -607,6 +674,9 @@ tst_QWidget::tst_QWidget() palette.setColor(QPalette::ToolTipBase, QColor(12, 13, 14)); palette.setColor(QPalette::Text, QColor(21, 22, 23)); QApplication::setPalette(palette, "QPropagationTestWidget"); + + if (QApplication::platformName().startsWith(QLatin1String("wayland"))) + qputenv("QT_WAYLAND_DISABLE_WINDOWDECORATION", "1"); } tst_QWidget::~tst_QWidget() @@ -619,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; @@ -641,6 +715,90 @@ void tst_QWidget::cleanup() QTRY_VERIFY(QApplication::topLevelWidgets().isEmpty()); } +template <typename T> +struct ImplicitlyConvertibleTo { + T t; + operator const T() const { return t; } + operator T() { return t; } +}; + +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: + // (text), (icon, text), (icon, text, shortcut), (text, shortcut) + // each with a good sample of ways to QObject::connect() to + // QAction::triggered(bool) + QWidget w; + + // don't just pass QString etc - that'd be too easy (think QStringBuilder) + ImplicitlyConvertibleTo<QString> text = {QStringLiteral("foo")}; + ImplicitlyConvertibleTo<QIcon> icon; + + const auto check = [&](auto &...args) { // don't need to perfectly-forward, only lvalues passed + w.addAction(args...); + + w.addAction(args..., &w, SLOT(deleteLater())); + w.addAction(args..., &w, &QObject::deleteLater); + w.addAction(args..., testFunction0); + w.addAction(args..., &w, testFunction0); + w.addAction(args..., testFunction1); + w.addAction(args..., &w, testFunction1); + w.addAction(args..., [&](bool b) { w.setEnabled(b); }); + w.addAction(args..., &w, [&](bool b) { w.setEnabled(b); }); + + w.addAction(args..., &w, SLOT(deleteLater()), Qt::QueuedConnection); + w.addAction(args..., &w, &QObject::deleteLater, Qt::QueuedConnection); + // doesn't exist: w.addAction(args..., testFunction0, Qt::QueuedConnection); + w.addAction(args..., &w, testFunction0, Qt::QueuedConnection); + // doesn't exist: w.addAction(args..., testFunction1, Qt::QueuedConnection); + w.addAction(args..., &w, testFunction1, Qt::QueuedConnection); + // doesn't exist: w.addAction(args..., [&](bool b) { w.setEnabled(b); }, Qt::QueuedConnection); + w.addAction(args..., &w, [&](bool b) { w.setEnabled(b); }, Qt::QueuedConnection); + }; + const auto check1 = [&](auto &arg, auto &...args) { + check(arg, args...); + check(std::as_const(arg), args...); + }; + const auto check2 = [&](auto &arg1, auto &arg2, auto &...args) { + check1(arg1, arg2, args...); + check1(arg1, std::as_const(arg2), args...); + }; + [[maybe_unused]] + const auto check3 = [&](auto &arg1, auto &arg2, auto &arg3) { + check2(arg1, arg2, arg3); + check2(arg1, arg2, std::as_const(arg3)); + }; + + check1(text); + check2(icon, text); +#ifndef QT_NO_SHORTCUT + ImplicitlyConvertibleTo<QKeySequence> keySequence = {Qt::CTRL | Qt::Key_C}; + check2(text, keySequence); + check3(icon, text, keySequence); +#endif +} + void tst_QWidget::fontPropagation() { QScopedPointer<QWidget> testWidget(new QWidget); @@ -1163,6 +1321,8 @@ void tst_QWidget::ignoreKeyEventsWhenDisabled_QTBUG27417() centerOnScreen(&lineEdit); lineEdit.setDisabled(true); lineEdit.show(); + QTest::ignoreMessage(QtWarningMsg, "Keyboard event not accepted by receiving widget"); + QTest::ignoreMessage(QtWarningMsg, "Keyboard event not accepted by receiving widget"); QTest::keyClick(&lineEdit, Qt::Key_A); QTRY_VERIFY(lineEdit.text().isEmpty()); } @@ -1777,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()); @@ -1819,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); @@ -1835,6 +1996,112 @@ 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() +{ + QLoggingCategory::setFilterRules("qt.widgets.focus=true"); + 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)) @@ -1858,7 +2125,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()); @@ -1894,23 +2160,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 @@ -1919,7 +2191,6 @@ void tst_QWidget::reverseTabOrder() container.show(); container.activateWindow(); - QApplication::setActiveWindow(&container); QVERIFY(QTest::qWaitForWindowActive(&container)); QTRY_VERIFY(firstEdit->hasFocus()); @@ -1954,6 +2225,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)) @@ -1981,7 +2385,6 @@ void tst_QWidget::tabOrderWithProxy() container.show(); container.activateWindow(); - QApplication::setActiveWindow(&container); QVERIFY(QTest::qWaitForWindowActive(&container)); QTRY_VERIFY(firstEdit->hasFocus()); @@ -2015,6 +2418,81 @@ void tst_QWidget::tabOrderWithProxy() QVERIFY(firstEdit->hasFocus()); } +static QString focusWidgetName() +{ + return QApplication::focusWidget() != nullptr + ? QApplication::focusWidget()->objectName() + : QStringLiteral("No focus widget"); +} + +void tst_QWidget::tabOrderWithProxyDisabled() +{ + Container container; + container.setWindowTitle(QLatin1String(QTest::currentTestFunction())); + + QLineEdit lineEdit1; + lineEdit1.setObjectName("lineEdit1"); + + QWidget containingWidget; + containingWidget.setFocusPolicy(Qt::StrongFocus); + auto *containingLayout = new QVBoxLayout; + QLineEdit lineEdit2; + lineEdit2.setObjectName("lineEdit2"); + QLineEdit lineEdit3; + lineEdit3.setObjectName("lineEdit3"); + containingLayout->addWidget(&lineEdit2); + containingLayout->addWidget(&lineEdit3); + containingWidget.setLayout(containingLayout); + containingWidget.setFocusProxy(&lineEdit2); + lineEdit2.setEnabled(false); + + container.box->addWidget(&lineEdit1); + container.box->addWidget(&containingWidget); + + container.show(); + container.activateWindow(); + + if (!QTest::qWaitForWindowActive(&container)) + QSKIP("Window failed to activate, skipping test"); + + QVERIFY2(lineEdit1.hasFocus(), + qPrintable(focusWidgetName())); + container.tab(); + QVERIFY2(!lineEdit2.hasFocus(), + qPrintable(focusWidgetName())); + QVERIFY2(lineEdit3.hasFocus(), + qPrintable(focusWidgetName())); + container.tab(); + QVERIFY2(lineEdit1.hasFocus(), + qPrintable(focusWidgetName())); + container.backTab(); + QVERIFY2(lineEdit3.hasFocus(), + qPrintable(focusWidgetName())); + container.backTab(); + QVERIFY2(!lineEdit2.hasFocus(), + qPrintable(focusWidgetName())); + QVERIFY2(lineEdit1.hasFocus(), + qPrintable(focusWidgetName())); +} + +//#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)) @@ -2055,7 +2533,6 @@ void tst_QWidget::tabOrderWithCompoundWidgets() container.show(); container.activateWindow(); - QApplication::setActiveWindow(&container); QVERIFY(QTest::qWaitForWindowActive(&container)); lastEdit->setFocus(); @@ -2106,34 +2583,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() @@ -2157,28 +2780,27 @@ void tst_QWidget::tabOrderWithCompoundWidgetsNoFocusPolicy() container.show(); container.activateWindow(); - QApplication::setActiveWindow(&container); if (!QTest::qWaitForWindowActive(&container)) QSKIP("Window failed to activate, skipping test"); QVERIFY2(spinbox1.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); container.tab(); QVERIFY2(!spinbox2.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); QVERIFY2(spinbox3.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); container.tab(); QVERIFY2(spinbox1.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); container.backTab(); QVERIFY2(spinbox3.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); container.backTab(); QVERIFY2(!spinbox2.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); QVERIFY2(spinbox1.hasFocus(), - qPrintable(QApplication::focusWidget()->objectName())); + qPrintable(focusWidgetName())); } void tst_QWidget::tabOrderNoChange() @@ -2263,7 +2885,6 @@ void tst_QWidget::appFocusWidgetWithFocusProxyLater() QLineEdit *lineEdit = new QLineEdit(&window); lineEdit->setFocus(); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); QCOMPARE(QApplication::focusWidget(), lineEdit); @@ -2291,7 +2912,6 @@ void tst_QWidget::appFocusWidgetWhenLosingFocusProxy() lineEdit->setFocusProxy(lineEditFocusProxy); lineEdit->setFocus(); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); QCOMPARE(QApplication::focusWidget(), lineEditFocusProxy); QVERIFY(lineEdit->hasFocus()); @@ -2318,7 +2938,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); @@ -2347,7 +2966,6 @@ void tst_QWidget::explicitTabOrderWithSpinBox_QTBUG81097() QWidget::setTabOrder(spinBoxTwo, lineEdit); spinBoxOne->setFocus(); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); QTRY_COMPARE(QApplication::focusWidget(), spinBoxOne); @@ -2394,6 +3012,24 @@ void tst_QWidget::activation() } #endif // Q_OS_WIN +struct WindowStateChangeWatcher : public QObject +{ + WindowStateChangeWatcher(QWidget *widget) + { + Q_ASSERT(widget->window()->windowHandle()); + widget->window()->windowHandle()->installEventFilter(this); + lastWindowStates = widget->window()->windowHandle()->windowState(); + } + Qt::WindowStates lastWindowStates; +protected: + bool eventFilter(QObject *receiver, QEvent *event) override + { + if (event->type() == QEvent::WindowStateChange) + lastWindowStates = static_cast<QWindow *>(receiver)->windowState(); + return QObject::eventFilter(receiver, event); + } +}; + void tst_QWidget::windowState() { #ifdef Q_OS_MACOS @@ -2407,12 +3043,14 @@ void tst_QWidget::windowState() QPoint pos; QSize size = m_testWidgetSize; - if (QGuiApplicationPrivate::platformIntegration()->defaultWindowState(Qt::Widget) - == Qt::WindowFullScreen) { + const Qt::WindowState defaultWidgetState = + QGuiApplicationPrivate::platformIntegration()->defaultWindowState(Qt::Widget); + if (defaultWidgetState == Qt::WindowFullScreen) size = QGuiApplication::primaryScreen()->size(); - } else { + else if (defaultWidgetState == Qt::WindowMaximized) + size = QGuiApplication::primaryScreen()->availableSize(); + else pos = QPoint(10, 10); - } QWidget widget1; widget1.move(pos); @@ -2527,6 +3165,119 @@ void tst_QWidget::windowState() QTRY_COMPARE(widget1.size(), size); } +// Test propagation of size and state from platform window to QWidget +// Windows and linux/XCB only +void tst_QWidget::resizePropagation() +{ +#if !defined(Q_OS_LINUX) && !defined(Q_OS_WIN) + QSKIP("resizePropagation test is designed for Linux/XCB and Windows only"); +#endif + const bool xcb = (m_platform == QStringLiteral("xcb")); +#ifdef Q_OS_LINUX + if (!xcb) + QSKIP("resizePropagation test is designed for XCB only"); +#endif + + // Windows: + // When a widget is maximized after it has been resized, the widget retains its original 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); + int count = 0; + + const QSize screenSize = QGuiApplication::primaryScreen()->size(); + const QSize size1 = QSize(screenSize.width() * 0.5, screenSize.height() * 0.5); + const QSize size2 = QSize(screenSize.width() * 0.625, screenSize.height() * 0.833); + + 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 == Equal) + count = spy.count(); + + // Resize if required + if (size.isValid()) + widget.resize(size); + + // Wait for the widget anyway + QVERIFY(QTest::qWaitForWindowExposed(&widget)); + + // Check signal count and qDebug output for fail analysis + 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 + QTRY_COMPARE(widget.windowState(), windowState); + QTRY_COMPARE(window->windowState(), windowState); + + // Check target size with fail or warning + switch (checkTargetSize) { + case Fail: + QCOMPARE(widget.size(), 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, Equal, Fail); + if (QTest::currentTestFailed()) + return; + + // test state transition, state and size consistency after resize + 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, Equal, xcb ? Warn : Fail); + if (QTest::currentTestFailed()) + return; + + // test state transition, state and size consistency after maximize + widget.showMaximized(); + verifyResize(QSize(), Qt::WindowMaximized, Greater, xcb ? Fail : Warn); + if (QTest::currentTestFailed()) + return; + +#ifdef Q_OS_WIN + QCOMPARE(widget.size(), size2); +#endif +} + void tst_QWidget::showMaximized() { QWidget plain; @@ -2716,7 +3467,7 @@ void tst_QWidget::resizeEvent() wParent.setWindowTitle(QLatin1String(QTest::currentTestFunction())); wParent.resize(m_testWidgetSize); ResizeWidget wChild(&wParent); - wParent.show(); + QTestPrivate::androidCompatibleShow(&wParent); QVERIFY(QTest::qWaitForWindowExposed(&wParent)); QCOMPARE (wChild.m_resizeEventCount, 1); // initial resize event before paint wParent.hide(); @@ -2725,7 +3476,7 @@ void tst_QWidget::resizeEvent() safeSize.setWidth(639); wChild.resize(safeSize); QCOMPARE (wChild.m_resizeEventCount, 1); - wParent.show(); + QTestPrivate::androidCompatibleShow(&wParent); QCOMPARE (wChild.m_resizeEventCount, 2); } @@ -2733,7 +3484,7 @@ void tst_QWidget::resizeEvent() ResizeWidget wTopLevel; wTopLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction())); wTopLevel.resize(m_testWidgetSize); - wTopLevel.show(); + QTestPrivate::androidCompatibleShow(&wTopLevel); QVERIFY(QTest::qWaitForWindowExposed(&wTopLevel)); QCOMPARE (wTopLevel.m_resizeEventCount, 1); // initial resize event before paint for toplevels wTopLevel.hide(); @@ -2742,7 +3493,7 @@ void tst_QWidget::resizeEvent() safeSize.setWidth(639); wTopLevel.resize(safeSize); QCOMPARE (wTopLevel.m_resizeEventCount, 1); - wTopLevel.show(); + QTestPrivate::androidCompatibleShow(&wTopLevel); QVERIFY(QTest::qWaitForWindowExposed(&wTopLevel)); QCOMPARE (wTopLevel.m_resizeEventCount, 2); } @@ -2818,7 +3569,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(); @@ -2842,7 +3593,6 @@ void tst_QWidget::showMinimizedKeepsFocus() QWidget *child = new QWidget(&window); child->setFocusPolicy(Qt::StrongFocus); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); child->setFocus(); QTRY_COMPARE(window.focusWidget(), child); @@ -2861,7 +3611,6 @@ void tst_QWidget::showMinimizedKeepsFocus() QWidget *child = new QWidget(&window); child->setFocusPolicy(Qt::StrongFocus); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); child->setFocus(); QTRY_COMPARE(window.focusWidget(), child); @@ -2881,7 +3630,6 @@ void tst_QWidget::showMinimizedKeepsFocus() QWidget *child = new QWidget(&window); child->setFocusPolicy(Qt::StrongFocus); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); child->setFocus(); QTRY_COMPARE(window.focusWidget(), child); @@ -2902,7 +3650,6 @@ void tst_QWidget::showMinimizedKeepsFocus() QWidget *child = new QWidget(&window); child->setFocusPolicy(Qt::StrongFocus); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); child->setFocus(); QTRY_COMPARE(window.focusWidget(), child); @@ -2919,7 +3666,6 @@ void tst_QWidget::showMinimizedKeepsFocus() QTRY_COMPARE(QApplication::focusWidget(), nullptr); window.showNormal(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); #ifdef Q_OS_MACOS if (!macHasAccessToWindowsServer()) @@ -2957,8 +3703,8 @@ void tst_QWidget::reparent() pal2.setColor(childTLW.backgroundRole(), Qt::yellow); childTLW.setPalette(pal2); - parent.show(); - childTLW.show(); + QTestPrivate::androidCompatibleShow(&parent); + QTestPrivate::androidCompatibleShow(&childTLW); QVERIFY(QTest::qWaitForWindowExposed(&parent)); parent.move(parentPosition); @@ -2968,7 +3714,7 @@ void tst_QWidget::reparent() child.setParent(nullptr, child.windowFlags() & ~Qt::WindowType_Mask); child.setGeometry(childPos.x(), childPos.y(), child.width(), child.height()); - child.show(); + QTestPrivate::androidCompatibleShow(&child); #if 0 // QTBUG-26424 if (m_platform == QStringLiteral("xcb")) @@ -2984,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); @@ -3073,109 +3819,111 @@ void tst_QWidget::hideWhenFocusWidgetIsChild() void tst_QWidget::normalGeometry() { -#ifdef Q_OS_MACOS - QSKIP("QTBUG-52974"); -#endif - if (m_platform == QStringLiteral("wayland")) QSKIP("Wayland: This fails. Figure out why."); QWidget parent; + QCOMPARE(parent.normalGeometry(), parent.geometry()); parent.setWindowTitle("NormalGeometry parent"); QWidget *child = new QWidget(&parent); QCOMPARE(parent.normalGeometry(), parent.geometry()); QCOMPARE(child->normalGeometry(), QRect()); - const QRect testGeom = QRect(m_availableTopLeft + QPoint(100 ,100), m_testWidgetSize); - parent.setGeometry(testGeom); + parent.setGeometry(QRect(m_availableTopLeft + QPoint(100 ,100), m_testWidgetSize)); parent.showNormal(); QVERIFY(QTest::qWaitForWindowExposed(&parent)); - QApplication::processEvents(); + WindowStateChangeWatcher stateChangeWatcher(&parent); - QRect geom = parent.geometry(); - // ### the window manager places the top-left corner at - // ### 100,100... making geom something like 102,124 (offset by - // ### the frame/frame)... this indicates a rather large different - // ### between how X11 and Windows works - // QCOMPARE(geom, QRect(100, 100, 200, 200)); - QCOMPARE(parent.normalGeometry(), geom); + const QRect normalGeometry = parent.geometry(); + // We can't make any assumptions about the actual geometry compared to the + // requested geometry. In this test, we only care about normalGeometry. + QCOMPARE(parent.normalGeometry(), normalGeometry); parent.setWindowState(parent.windowState() ^ Qt::WindowMaximized); QTRY_VERIFY(parent.windowState() & Qt::WindowMaximized); - QTRY_VERIFY(parent.geometry() != geom); - QTRY_COMPARE(parent.normalGeometry(), geom); + QTRY_COMPARE(stateChangeWatcher.lastWindowStates, parent.windowState()); + QTRY_VERIFY(parent.geometry() != normalGeometry); + QTRY_COMPARE(parent.normalGeometry(), normalGeometry); parent.setWindowState(parent.windowState() ^ Qt::WindowMaximized); QTRY_VERIFY(!(parent.windowState() & Qt::WindowMaximized)); - QTRY_COMPARE(parent.geometry(), geom); - QTRY_COMPARE(parent.normalGeometry(), geom); + QTRY_COMPARE(stateChangeWatcher.lastWindowStates, parent.windowState()); + QTRY_COMPARE(parent.geometry(), normalGeometry); + QTRY_COMPARE(parent.normalGeometry(), normalGeometry); parent.showMaximized(); - QTRY_VERIFY(parent.windowState() & Qt::WindowMaximized); - QTRY_VERIFY(parent.geometry() != geom); - QCOMPARE(parent.normalGeometry(), geom); + QTRY_VERIFY(parent.windowHandle()->windowState() & Qt::WindowMaximized); + QTRY_COMPARE(stateChangeWatcher.lastWindowStates, parent.windowState()); + QTRY_VERIFY(parent.geometry() != normalGeometry); + QCOMPARE(parent.normalGeometry(), normalGeometry); parent.showNormal(); QTRY_VERIFY(!(parent.windowState() & Qt::WindowMaximized)); - QTRY_COMPARE(parent.geometry(), geom); - QCOMPARE(parent.normalGeometry(), geom); + QTRY_COMPARE(stateChangeWatcher.lastWindowStates, parent.windowState()); + QTRY_COMPARE(parent.geometry(), normalGeometry); + QCOMPARE(parent.normalGeometry(), normalGeometry); + + parent.setWindowState(parent.windowState() ^ Qt::WindowFullScreen); + QTRY_VERIFY(parent.windowState() & Qt::WindowFullScreen); + QTRY_COMPARE(stateChangeWatcher.lastWindowStates, parent.windowState()); + QTRY_VERIFY(parent.geometry() != normalGeometry); + QTRY_COMPARE(parent.normalGeometry(), normalGeometry); + + parent.setWindowState(Qt::WindowNoState); + QTRY_VERIFY(!(parent.windowState() & Qt::WindowFullScreen)); + QTRY_COMPARE(stateChangeWatcher.lastWindowStates, parent.windowState()); + QTRY_COMPARE(parent.geometry(), normalGeometry); + QTRY_COMPARE(parent.normalGeometry(), normalGeometry); + + parent.showFullScreen(); + QTRY_VERIFY(parent.window()->windowState() & Qt::WindowFullScreen); + QTRY_COMPARE(stateChangeWatcher.lastWindowStates, parent.windowState()); + QTRY_VERIFY(parent.geometry() != normalGeometry); + QTRY_COMPARE(parent.normalGeometry(), normalGeometry); + + parent.showNormal(); + QTRY_VERIFY(!(parent.windowHandle()->windowState() & Qt::WindowFullScreen)); + QTRY_COMPARE(stateChangeWatcher.lastWindowStates, parent.windowState()); + QTRY_COMPARE(parent.geometry(), normalGeometry); + QTRY_COMPARE(parent.normalGeometry(), normalGeometry); - parent.setWindowState(parent.windowState() ^ Qt::WindowMinimized); - QTest::qWait(10); - parent.setWindowState(parent.windowState() ^ Qt::WindowMaximized); - QTest::qWait(10); if (m_platform == QStringLiteral("xcb")) QSKIP("QTBUG-26424"); - QTRY_VERIFY(parent.windowState() & (Qt::WindowMinimized|Qt::WindowMaximized)); + + parent.setWindowState(parent.windowState() ^ Qt::WindowMaximized); + QTRY_VERIFY(stateChangeWatcher.lastWindowStates & Qt::WindowMaximized); + parent.setWindowState(parent.windowState() ^ Qt::WindowMinimized); + QTRY_VERIFY(stateChangeWatcher.lastWindowStates & Qt::WindowMinimized); + + QTRY_COMPARE(parent.windowState() & (Qt::WindowMinimized|Qt::WindowMaximized), Qt::WindowMinimized|Qt::WindowMaximized); + QTRY_VERIFY(stateChangeWatcher.lastWindowStates & (Qt::WindowMinimized|Qt::WindowMaximized)); // ### when minimized and maximized at the same time, the geometry // ### does *NOT* have to be the normal geometry, it could be the // ### maximized geometry. // QCOMPARE(parent.geometry(), geom); - QTRY_COMPARE(parent.normalGeometry(), geom); + QTRY_COMPARE(parent.normalGeometry(), normalGeometry); parent.setWindowState(parent.windowState() ^ Qt::WindowMinimized); - QTest::qWait(10); QTRY_VERIFY(!(parent.windowState() & Qt::WindowMinimized)); QTRY_VERIFY(parent.windowState() & Qt::WindowMaximized); - QTRY_VERIFY(parent.geometry() != geom); - QTRY_COMPARE(parent.normalGeometry(), geom); + QTRY_COMPARE(stateChangeWatcher.lastWindowStates, parent.windowState()); + QTRY_VERIFY(parent.geometry() != normalGeometry); + QTRY_COMPARE(parent.normalGeometry(), normalGeometry); parent.setWindowState(parent.windowState() ^ Qt::WindowMaximized); - QTest::qWait(10); QTRY_VERIFY(!(parent.windowState() & Qt::WindowMaximized)); - QTRY_COMPARE(parent.geometry(), geom); - QTRY_COMPARE(parent.normalGeometry(), geom); - - parent.setWindowState(parent.windowState() ^ Qt::WindowFullScreen); - QTest::qWait(10); - QTRY_VERIFY(parent.windowState() & Qt::WindowFullScreen); - QTRY_VERIFY(parent.geometry() != geom); - QTRY_COMPARE(parent.normalGeometry(), geom); - - parent.setWindowState(parent.windowState() ^ Qt::WindowFullScreen); - QTest::qWait(10); - QVERIFY(!(parent.windowState() & Qt::WindowFullScreen)); - QTRY_COMPARE(parent.geometry(), geom); - QTRY_COMPARE(parent.normalGeometry(), geom); - - parent.showFullScreen(); - QTest::qWait(10); - QTRY_VERIFY(parent.windowState() & Qt::WindowFullScreen); - QTRY_VERIFY(parent.geometry() != geom); - QTRY_COMPARE(parent.normalGeometry(), geom); - - parent.showNormal(); - QTest::qWait(10); - QTRY_VERIFY(!(parent.windowState() & Qt::WindowFullScreen)); - QTRY_COMPARE(parent.geometry(), geom); - QTRY_COMPARE(parent.normalGeometry(), geom); + QTRY_COMPARE(stateChangeWatcher.lastWindowStates, parent.windowState()); + QTRY_COMPARE(parent.geometry(), normalGeometry); + QTRY_COMPARE(parent.normalGeometry(), normalGeometry); parent.showNormal(); + stateChangeWatcher.lastWindowStates = {}; parent.setWindowState(Qt:: WindowFullScreen | Qt::WindowMaximized); parent.setWindowState(Qt::WindowMinimized | Qt:: WindowFullScreen | Qt::WindowMaximized); parent.setWindowState(Qt:: WindowFullScreen | Qt::WindowMaximized); - QTest::qWait(10); - QTRY_COMPARE(parent.normalGeometry(), geom); + // the actual window will be either fullscreen or maximized + QTRY_VERIFY(stateChangeWatcher.lastWindowStates & (Qt:: WindowFullScreen | Qt::WindowMaximized)); + QTRY_COMPARE(parent.normalGeometry(), normalGeometry); } void tst_QWidget::setGeometry() @@ -3184,9 +3932,8 @@ void tst_QWidget::setGeometry() tlw.setWindowTitle(QLatin1String(QTest::currentTestFunction())); QWidget child(&tlw); - const QPoint topLeft = QGuiApplication::primaryScreen()->availableGeometry().topLeft(); const QSize initialSize = 2 * m_testWidgetSize; - QRect tr(topLeft + QPoint(100,100), initialSize); + QRect tr(m_availableTopLeft + QPoint(100,100), initialSize); QRect cr(50,50,50,50); tlw.setGeometry(tr); child.setGeometry(cr); @@ -3195,15 +3942,14 @@ void tst_QWidget::setGeometry() QCOMPARE(child.geometry(), cr); tlw.setParent(nullptr, Qt::Window|Qt::FramelessWindowHint); - tr = QRect(topLeft, initialSize / 2); + tr = QRect(m_availableTopLeft, initialSize / 2); tlw.setGeometry(tr); QCOMPARE(tlw.geometry(), tr); tlw.showNormal(); - QTest::qWait(50); - if (tlw.frameGeometry() != tlw.geometry()) + if (!QTest::qWaitFor([&tlw]{ return tlw.frameGeometry() == tlw.geometry(); })) QSKIP("Your window manager is too broken for this test"); - if (m_platform == QStringLiteral("xcb")) - QSKIP("QTBUG-26424"); + if (m_platform == QStringLiteral("xcb") && tlw.geometry() != tr) + QEXPECT_FAIL("", "QTBUG-26424", Continue); QCOMPARE(tlw.geometry(), tr); } @@ -3378,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); @@ -3394,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); @@ -3423,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); @@ -3439,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); @@ -3477,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); @@ -3496,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); @@ -3542,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) @@ -3560,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); @@ -3575,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 @@ -3697,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); @@ -3852,10 +4608,9 @@ 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.count(), 46); + QCOMPARE(savedGeometry.size(), 46); f.close(); QWidget widget; @@ -3909,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 @@ -4067,7 +4884,8 @@ void tst_QWidget::testDeletionInEventHandlers() w = new Widget; w->show(); w->deleteThis = true; - QMouseEvent me(QEvent::MouseButtonRelease, QPoint(1, 1), Qt::LeftButton, Qt::LeftButton, Qt::KeyboardModifiers()); + QMouseEvent me(QEvent::MouseButtonRelease, QPoint(1, 1), w->mapToGlobal(QPoint(1, 1)), + Qt::LeftButton, Qt::LeftButton, Qt::KeyboardModifiers()); qApp->notify(w, &me); QVERIFY(w.isNull()); delete w; @@ -4106,7 +4924,8 @@ void tst_QWidget::testDeletionInEventHandlers() w->setMouseTracking(true); w->show(); w->deleteThis = true; - QMouseEvent me2 = QMouseEvent(QEvent::MouseMove, QPoint(0, 0), Qt::NoButton, Qt::NoButton, Qt::NoModifier); + QMouseEvent me2 = QMouseEvent(QEvent::MouseMove, QPoint(0, 0), w->mapToGlobal(QPoint(0, 0)), + Qt::NoButton, Qt::NoButton, Qt::NoModifier); QApplication::sendEvent(w, &me2); QVERIFY(w.isNull()); delete w; @@ -4172,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()); @@ -4195,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(); @@ -4320,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); } @@ -4493,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 @@ -4548,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() { { @@ -4687,6 +5594,75 @@ void tst_QWidget::showNativeChild() QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); } +void tst_QWidget::closeAndShowNativeChild() +{ + QWidget topLevel; + QWidget *nativeChild = new QWidget; + nativeChild->winId(); + nativeChild->setFixedSize(200, 200); + + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(nativeChild); + topLevel.setLayout(layout); + + topLevel.show(); + QVERIFY(!nativeChild->isHidden()); + nativeChild->close(); + QVERIFY(nativeChild->isHidden()); + nativeChild->show(); + QVERIFY(!nativeChild->isHidden()); +} + +void tst_QWidget::closeAndShowWithNativeChild() +{ + bool dontCreateNativeWidgetSiblings = QApplication::testAttribute(Qt::AA_DontCreateNativeWidgetSiblings); + auto resetAttribute = qScopeGuard([&]{ + QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, dontCreateNativeWidgetSiblings); + }); + QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); + + QWidget topLevel; + topLevel.setObjectName("TopLevel"); + QWidget *nativeChild = new QWidget; + nativeChild->setObjectName("NativeChild"); + nativeChild->setFixedSize(200, 200); + QWidget *normalChild = new QWidget; + normalChild->setObjectName("NormalChild"); + normalChild->setFixedSize(200, 200); + + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(nativeChild); + layout->addWidget(normalChild); + topLevel.setLayout(layout); + + 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)); + const QSize originalSize = topLevel.size(); + topLevel.close(); + + // all children must have the same state + QCOMPARE(nativeChild->isHidden(), normalChild->isHidden()); + QCOMPARE(nativeChild->isVisible(), normalChild->isVisible()); + QCOMPARE(nativeChild->testAttribute(Qt::WA_WState_Visible), + normalChild->testAttribute(Qt::WA_WState_Visible)); + QCOMPARE(nativeChild->testAttribute(Qt::WA_WState_Hidden), + normalChild->testAttribute(Qt::WA_WState_Hidden)); + QCOMPARE(nativeChild->testAttribute(Qt::WA_WState_ExplicitShowHide), + normalChild->testAttribute(Qt::WA_WState_ExplicitShowHide)); + + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + QCOMPARE(topLevel.size(), originalSize); +} + class ShowHideEventWidget : public QWidget { public: @@ -4836,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); @@ -4849,6 +5826,7 @@ void tst_QWidget::update() w.reset(); UpdateWidget child(&w); + child.setPalette(simplePalette()); child.setGeometry(10, 10, 80, 80); child.show(); @@ -4920,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(); @@ -4999,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); @@ -5083,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(QGuiApplication::primaryScreen()->geometry().center() - QPoint(250, 250)); + updateWidget.move(m_availableTopLeft); updateWidget.showNormal(); - QApplication::setActiveWindow(&updateWidget); QVERIFY(QTest::qWaitForWindowActive(&updateWidget)); QVERIFY(updateWidget.numPaintEvents > 0); @@ -5162,34 +6143,74 @@ void tst_QWidget::scrollNativeChildren() #endif // Mac OS -class DestroyedSlotChecker : public QObject +/* + This class is used as a slot object to test two different steps of + QWidget destruction. + + The first step is connecting the destroyed() signal to an object of + this class (through its operator()). In widgets, destroyed() is + emitted by ~QWidget, and not by ~QObject. This means that in our + operator() we expect the sender of the signal to still be a + QWidget. + + The connection realized at the first step means that now there's + an instance of this class owned by the sender object. That instance + is destroyed when the signal/slot connections are destroyed. + That happens in ~QObject, not in ~QWidget. Therefore, in the + destructor of this class, check that indeed the target is no longer + a QWidget but just a QObject. +*/ +class QObjectCastChecker { - Q_OBJECT - public: - bool wasQWidget = false; + explicit QObjectCastChecker(QWidget *target) + : m_target(target) + { + } -public slots: - void destroyedSlot(QObject *object) + ~QObjectCastChecker() { - wasQWidget = (qobject_cast<QWidget *>(object) != nullptr || object->isWidgetType()); + if (!m_target) + return; + + // When ~QObject is reached, check that indeed the object is no + // longer a QWidget. This relies on slots being disconnected in + // ~QObject (and this "slot object" being destroyed there). + QVERIFY(!qobject_cast<QWidget *>(m_target)); + QVERIFY(!dynamic_cast<QWidget *>(m_target)); + QVERIFY(!m_target->isWidgetType()); } -}; -/* - Test that qobject_cast<QWidget*> returns 0 in a slot - connected to QObject::destroyed. -*/ -void tst_QWidget::qobject_castInDestroyedSlot() -{ - DestroyedSlotChecker checker; + QObjectCastChecker(QObjectCastChecker &&other) noexcept + : m_target(std::exchange(other.m_target, nullptr)) + {} - QWidget *widget = new QWidget(); + QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QObjectCastChecker) - QObject::connect(widget, &QObject::destroyed, &checker, &DestroyedSlotChecker::destroyedSlot); - delete widget; + void swap(QObjectCastChecker &other) noexcept + { + qSwap(m_target, other.m_target); + } + + void operator()(QObject *object) const + { + // Test that in a slot connected to destroyed() the emitter is + // still a QWidget. This is because ~QWidget() itself emits the + // signal. + QVERIFY(qobject_cast<QWidget *>(object)); + QVERIFY(dynamic_cast<QWidget *>(object)); + QVERIFY(object->isWidgetType()); + } - QVERIFY(checker.wasQWidget); +private: + Q_DISABLE_COPY(QObjectCastChecker) + QObject *m_target; +}; + +void tst_QWidget::qobject_castOnDestruction() +{ + QWidget widget; + QObject::connect(&widget, &QObject::destroyed, QObjectCastChecker(&widget)); } // Since X11 WindowManager operations are all async, and we have no way to know if the window @@ -5235,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(); @@ -5254,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); @@ -5272,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); @@ -5298,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); @@ -5313,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); @@ -5323,7 +6344,7 @@ void tst_QWidget::setWindowGeometry() QTRY_COMPARE(widget.geometry(), rect); // show() again, geometry() should still be the same - widget.show(); + QTestPrivate::androidCompatibleShow(&widget); if (rect.isValid()) QVERIFY(QTest::qWaitForWindowExposed(&widget)); QTRY_COMPARE(widget.geometry(), rect); @@ -5349,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); @@ -5364,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); @@ -5374,7 +6395,7 @@ void tst_QWidget::setWindowGeometry() QTRY_COMPARE(widget.geometry(), rect); // show() again, geometry() should still be the same - widget.show(); + QTestPrivate::androidCompatibleShow(&widget); if (rect.isValid()) QVERIFY(QTest::qWaitForWindowExposed(&widget)); QTest::qWait(10); @@ -5443,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(); @@ -5473,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) @@ -5502,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(); @@ -5522,7 +6543,7 @@ void tst_QWidget::windowMoveResize() QTRY_COMPARE(widget.size(), rect.size()); // show() again, pos() should be the same - widget.show(); + QTestPrivate::androidCompatibleShow(&widget); if (rect.isValid()) QVERIFY(QTest::qWaitForWindowExposed(&widget)); QApplication::processEvents(); @@ -5553,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(); @@ -5573,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(); @@ -5593,7 +6614,7 @@ void tst_QWidget::windowMoveResize() QTRY_COMPARE(widget.size(), rect.size()); // show() again, pos() should be the same - widget.show(); + QTestPrivate::androidCompatibleShow(&widget); if (rect.isValid()) QVERIFY(QTest::qWaitForWindowExposed(&widget)); QTest::qWait(10); @@ -5735,8 +6756,7 @@ void tst_QWidget::moveChild() parent.setStyle(style.data()); ColorWidget child(&parent, Qt::Widget, Qt::blue); - parent.setGeometry(QRect(parent.screen()->availableGeometry().topLeft() + QPoint(50, 50), - QSize(200, 200))); + parent.setGeometry(QRect(m_availableTopLeft + QPoint(50, 50), QSize(200, 200))); child.setGeometry(25, 25, 50, 50); #ifndef QT_NO_CURSOR // Try to make sure the cursor is not in a taskbar area to prevent tooltips or window highlighting QCursor::setPos(parent.geometry().topRight() + QPoint(50 , 50)); @@ -5772,16 +6792,18 @@ void tst_QWidget::moveChild() QTRY_COMPARE(pos, child.pos()); QTRY_COMPARE(parent.r, QRegion(oldGeometry) - child.geometry()); -#if !defined(Q_OS_MACOS) + // should be scrolled in backingstore QCOMPARE(child.r, QRegion()); -#endif VERIFY_COLOR(child, child.rect(), child.color); VERIFY_COLOR(parent, QRegion(parent.rect()) - child.geometry(), parent.color); } 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); @@ -5799,7 +6821,6 @@ void tst_QWidget::showAndMoveChild() parent.setGeometry(desktopDimensions); parent.setPalette(Qt::red); parent.show(); - QApplication::setActiveWindow(&parent); QVERIFY(QTest::qWaitForWindowActive(&parent)); QWidget child(&parent); @@ -5918,17 +6939,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); @@ -5936,16 +6957,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); } @@ -6008,7 +7029,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 @@ -6054,7 +7075,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 @@ -6230,34 +7251,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() { @@ -6383,6 +7376,9 @@ void tst_QWidget::setCursor() void tst_QWidget::setToolTip() { + if (QApplication::platformName().startsWith(QLatin1String("wayland"))) + QSKIP("Setting mouse cursor position is not possible on Wayland"); + QWidget widget; widget.resize(200, 200); // Showing the widget is not required for the tooltip event count test @@ -6404,6 +7400,9 @@ void tst_QWidget::setToolTip() QCOMPARE(widget.toolTip(), QString()); QCOMPARE(spy.count(), 2); + const int wakeUpDelay = widget.style()->styleHint(QStyle::SH_ToolTip_WakeUpDelay); + const int fallAsleepDelay = widget.style()->styleHint(QStyle::SH_ToolTip_FallAsleepDelay); + for (int pass = 0; pass < 2; ++pass) { QCursor::setPos(m_safeCursorPos); QScopedPointer<QWidget> popup(new QWidget(nullptr, Qt::Popup)); @@ -6423,12 +7422,12 @@ void tst_QWidget::setToolTip() QWindow *popupWindow = popup->windowHandle(); QTest::qWait(10); QTest::mouseMove(popupWindow, QPoint(25, 25)); - QTest::qWait(900); // delay is 700 + QTest::qWait(wakeUpDelay + 200); QCOMPARE(spy1.count(), 1); QCOMPARE(spy2.count(), 0); if (pass == 0) - QTest::qWait(2200); // delay is 2000 + QTest::qWait(fallAsleepDelay + 200); QTest::mouseMove(popupWindow); } @@ -6455,7 +7454,7 @@ void tst_QWidget::testWindowIconChangeEventPropagation() QWidgetList widgets; widgets << &topLevelWidget << &topLevelChild << &dialog << &dialogChild; - QCOMPARE(widgets.count(), 4); + QCOMPARE(widgets.size(), 4); topLevelWidget.show(); dialog.show(); @@ -6469,13 +7468,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)); } @@ -6484,7 +7483,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(); @@ -6501,7 +7500,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); @@ -6519,7 +7518,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); @@ -6676,14 +7675,12 @@ void tst_QWidget::clean_qt_x11_enforce_cursor() child->setAttribute(Qt::WA_SetCursor, true); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); QTest::qWait(100); QCursor::setPos(window.geometry().center()); QTest::qWait(100); child->setFocus(); - QApplication::processEvents(); QTest::qWait(100); delete w; @@ -6719,8 +7716,19 @@ public: bool eventFilter(QObject *object, QEvent *event) override { QWidget *widget = qobject_cast<QWidget *>(object); - if (widget && !event->spontaneous()) - events.append(qMakePair(widget, event->type())); + if (widget && !event->spontaneous()) { + switch (event->type()) { + // we might get those events if we couldn't move the cursor + case QEvent::Enter: + case QEvent::Leave: + // we might get this on systems that have an input method installed + case QEvent::InputMethodQuery: + break; + default: + events.append(qMakePair(widget, event->type())); + break; + } + } return false; } @@ -6762,11 +7770,6 @@ void tst_QWidget::childEvents() { EventRecorder::EventList expected; - // Move away the cursor; otherwise it might result in an enter event if it's - // inside the widget when the widget is shown. - QCursor::setPos(m_safeCursorPos); - QTest::qWait(100); - { // no children created, not shown QWidget widget; @@ -6806,7 +7809,9 @@ void tst_QWidget::childEvents() << qMakePair(&widget, QEvent::Move) << qMakePair(&widget, QEvent::Resize) << qMakePair(&widget, QEvent::Show) +#ifndef Q_OS_ANDROID << qMakePair(&widget, QEvent::CursorChange) +#endif << qMakePair(&widget, QEvent::ShowToParent); QVERIFY2(spy.eventList() == expected, @@ -6895,7 +7900,9 @@ void tst_QWidget::childEvents() << qMakePair(&widget, QEvent::Move) << qMakePair(&widget, QEvent::Resize) << qMakePair(&widget, QEvent::Show) +#ifndef Q_OS_ANDROID << qMakePair(&widget, QEvent::CursorChange) +#endif << qMakePair(&widget, QEvent::ShowToParent); QVERIFY2(spy.eventList() == expected, @@ -6987,7 +7994,9 @@ void tst_QWidget::childEvents() << qMakePair(&widget, QEvent::Move) << qMakePair(&widget, QEvent::Resize) << qMakePair(&widget, QEvent::Show) +#ifndef Q_OS_ANDROID << qMakePair(&widget, QEvent::CursorChange) +#endif << qMakePair(&widget, QEvent::ShowToParent); QVERIFY2(spy.eventList() == expected, @@ -7042,7 +8051,9 @@ private: void tst_QWidget::render() { - return; +#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 @@ -7057,16 +8068,11 @@ void tst_QWidget::render() RenderWidget target(&source); target.resize(source.size()); target.show(); - - QCoreApplication::processEvents(); - QCoreApplication::sendPostedEvents(); - QTest::qWait(250); + QVERIFY(QTest::qWaitForWindowExposed(&target)); const QImage sourceImage = source.grab(QRect(QPoint(0, 0), QSize(-1, -1))).toImage(); - QCoreApplication::processEvents(); QImage targetImage = target.grab(QRect(QPoint(0, 0), QSize(-1, -1))).toImage(); - QCoreApplication::processEvents(); - QCOMPARE(sourceImage, targetImage); + QTRY_COMPARE(sourceImage, targetImage); // Fill target.rect() will Qt::red and render // QRegion(0, 0, source->width(), source->height() / 2, QRegion::Ellipse) @@ -7079,66 +8085,73 @@ void tst_QWidget::render() QVERIFY(sourceImage != targetImage); QCOMPARE(targetImage.pixel(target.width() / 2, 29), QColor(Qt::red).rgb()); + if (targetImage.devicePixelRatioF() > 1) + QEXPECT_FAIL("", "This test fails on high-DPI displays", Continue); QCOMPARE(targetImage.pixel(target.width() / 2, 30), sourceImage.pixel(source.width() / 2, 0)); +} - // Test that a child widget properly fills its background - { - QWidget window; - window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); - window.resize(100, 100); - // prevent custom styles - window.setStyle(QStyleFactory::create(QLatin1String("Windows"))); - window.show(); - QVERIFY(QTest::qWaitForWindowExposed(&window)); - QWidget child(&window); - child.resize(window.size()); - child.show(); +// Test that a child widget properly fills its background +void tst_QWidget::renderChildFillsBackground() +{ + QWidget window; + window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); + window.resize(100, 100); + // prevent custom styles + window.setStyle(QStyleFactory::create(QLatin1String("Windows"))); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + QWidget child(&window); + child.resize(window.size()); + child.show(); - QCoreApplication::processEvents(); - const QPixmap childPixmap = child.grab(QRect(QPoint(0, 0), QSize(-1, -1))); - const QPixmap windowPixmap = window.grab(QRect(QPoint(0, 0), QSize(-1, -1))); - QCOMPARE(childPixmap, windowPixmap); - } + QCoreApplication::processEvents(); + const QPixmap childPixmap = child.grab(QRect(QPoint(0, 0), QSize(-1, -1))); + const QPixmap windowPixmap = window.grab(QRect(QPoint(0, 0), QSize(-1, -1))); +#ifndef Q_OS_ANDROID + // On Android all widgets are shown maximized, so the pixmaps + // will be similar + if (!m_platform.startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QEXPECT_FAIL("", "This test fails on all platforms", Continue); +#endif + QCOMPARE(childPixmap, windowPixmap); +} - { // Check that the target offset is correct. - QWidget widget; - widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); - widget.resize(200, 200); - widget.setAutoFillBackground(true); - widget.setPalette(Qt::red); - // prevent custom styles - widget.setStyle(QStyleFactory::create(QLatin1String("Windows"))); - widget.show(); - QVERIFY(QTest::qWaitForWindowExposed(&widget)); - QImage image(widget.size(), QImage::Format_RGB32); - image.fill(QColor(Qt::blue).rgb()); - - // Target offset (0, 0) - widget.render(&image, QPoint(), QRect(20, 20, 100, 100)); - QCOMPARE(image.pixel(0, 0), QColor(Qt::red).rgb()); - QCOMPARE(image.pixel(99, 99), QColor(Qt::red).rgb()); - QCOMPARE(image.pixel(100, 100), QColor(Qt::blue).rgb()); - - // Target offset (20, 20). - image.fill(QColor(Qt::blue).rgb()); - widget.render(&image, QPoint(20, 20), QRect(20, 20, 100, 100)); - QCOMPARE(image.pixel(0, 0), QColor(Qt::blue).rgb()); - QCOMPARE(image.pixel(19, 19), QColor(Qt::blue).rgb()); - QCOMPARE(image.pixel(20, 20), QColor(Qt::red).rgb()); - QCOMPARE(image.pixel(119, 119), QColor(Qt::red).rgb()); - 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. +void tst_QWidget::renderTargetOffset() +{ // Check that the target offset is correct. + QWidget widget; + widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); + widget.resize(200, 200); + widget.setAutoFillBackground(true); + widget.setPalette(Qt::red); + // prevent custom styles + widget.setStyle(QStyleFactory::create(QLatin1String("Windows"))); + widget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&widget)); + QImage image(widget.size(), QImage::Format_RGB32); + image.fill(QColor(Qt::blue).rgb()); + + // Target offset (0, 0) + widget.render(&image, QPoint(), QRect(20, 20, 100, 100)); + QCOMPARE(image.pixel(0, 0), QColor(Qt::red).rgb()); + QCOMPARE(image.pixel(99, 99), QColor(Qt::red).rgb()); + QCOMPARE(image.pixel(100, 100), QColor(Qt::blue).rgb()); + + // Target offset (20, 20). + image.fill(QColor(Qt::blue).rgb()); + widget.render(&image, QPoint(20, 20), QRect(20, 20, 100, 100)); + QCOMPARE(image.pixel(0, 0), QColor(Qt::blue).rgb()); + QCOMPARE(image.pixel(19, 19), QColor(Qt::blue).rgb()); + QCOMPARE(image.pixel(20, 20), QColor(Qt::red).rgb()); + QCOMPARE(image.pixel(119, 119), QColor(Qt::red).rgb()); + QCOMPARE(image.pixel(120, 120), QColor(Qt::blue).rgb()); +} + +// 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; @@ -7159,6 +8172,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())); @@ -7176,12 +8192,10 @@ void tst_QWidget::renderInvisible() dummyFocusWidget.move(calendar->geometry().bottomLeft() + QPoint(0, 100)); dummyFocusWidget.show(); QVERIFY(QTest::qWaitForWindowExposed(&dummyFocusWidget)); - QCoreApplication::processEvents(); - QTest::qWait(120); // 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"); @@ -7191,9 +8205,8 @@ void tst_QWidget::renderInvisible() // Create resized reference image. const QSize calendarSizeResized = calendar->size() + QSize(50, 50); calendar->resize(calendarSizeResized); - QCoreApplication::processEvents(); 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"); @@ -7202,12 +8215,11 @@ void tst_QWidget::renderInvisible() // Explicitly hide the calendar. calendar->hide(); - QCoreApplication::processEvents(); QTest::qWait(30); 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"); @@ -7223,7 +8235,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"); @@ -7232,11 +8244,10 @@ void tst_QWidget::renderInvisible() } calendar->hide(); - QCoreApplication::processEvents(); 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"); @@ -7250,7 +8261,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"); @@ -7259,7 +8270,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"); @@ -7273,7 +8284,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"); @@ -7300,7 +8311,6 @@ void tst_QWidget::renderInvisible() // Navigation bar isn't explicitly hidden anymore. navigationBar->show(); - QCoreApplication::processEvents(); QTest::qWait(30); QVERIFY(!calendar->isVisible()); @@ -7312,7 +8322,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"); @@ -7324,7 +8334,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); @@ -8011,7 +9021,7 @@ void tst_QWidget::moveWindowInShowEvent_data() QTest::addColumn<QPoint>("initial"); QTest::addColumn<QPoint>("position"); - QPoint p = QGuiApplication::primaryScreen()->availableGeometry().topLeft(); + QPoint p = m_availableTopLeft; QTest::newRow("1") << p << (p + QPoint(10, 10)); QTest::newRow("2") << (p + QPoint(10,10)) << p; @@ -8048,21 +9058,15 @@ void tst_QWidget::moveWindowInShowEvent() // show it widget.showNormal(); QVERIFY(QTest::qWaitForWindowExposed(&widget)); - QTest::qWait(100); // it should have moved QCOMPARE(widget.pos(), position); } void tst_QWidget::repaintWhenChildDeleted() { -#ifdef Q_OS_WIN - QTest::qWait(1000); -#endif ColorWidget w(nullptr, Qt::FramelessWindowHint, Qt::red); w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); - QPoint startPoint = w.screen()->availableGeometry().topLeft(); - startPoint.rx() += 50; - startPoint.ry() += 50; + const QPoint startPoint = m_availableTopLeft + QPoint(50, 50); w.setGeometry(QRect(startPoint, QSize(100, 100))); w.show(); QVERIFY(QTest::qWaitForWindowExposed(&w)); @@ -8085,9 +9089,7 @@ void tst_QWidget::hideOpaqueChildWhileHidden() { ColorWidget w(nullptr, Qt::FramelessWindowHint, Qt::red); w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); - QPoint startPoint = w.screen()->availableGeometry().topLeft(); - startPoint.rx() += 50; - startPoint.ry() += 50; + const QPoint startPoint = m_availableTopLeft + QPoint(50, 50); w.setGeometry(QRect(startPoint, QSize(100, 100))); ColorWidget child(&w, Qt::Widget, Qt::blue); @@ -8136,6 +9138,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; @@ -8163,7 +9166,8 @@ void tst_QWidget::updateWhileMinimized() qDebug() << "xcb: XDG_CURRENT_DESKTOP=" << desktop; if (desktop == QStringLiteral("ubuntu:GNOME") || desktop == QStringLiteral("GNOME-Classic:GNOME") - || desktop == QStringLiteral("GNOME")) + || desktop == QStringLiteral("GNOME") + || desktop.isEmpty()) // on local VMs count = 1; } QCOMPARE(widget.numPaintEvents, count); @@ -8708,6 +9712,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); @@ -8724,9 +9729,6 @@ void tst_QWidget::doubleRepaint() // Minmize: Should not trigger a repaint. widget.showMinimized(); QTest::qWait(10); -#if defined(Q_OS_QNX) - QEXPECT_FAIL("", "Platform does not support showMinimized()", Continue); -#endif QCOMPARE(widget.numPaintEvents, 0); widget.numPaintEvents = 0; @@ -8742,9 +9744,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); @@ -8798,6 +9801,46 @@ void tst_QWidget::opaqueChildren() QCOMPARE(qt_widget_private(&grandChild)->getOpaqueChildren(), QRegion()); } +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); + w.move(100, 100); + w.resize(200, 200); + + QLineEdit le(&w); + Q_SET_OBJECT_NAME(le); + le.resize(200, 200); + + { + const char * const expected[] = { + "QWidget::w I", + " QLineEdit::le I", + " QWidgetLineControl:: ", + }; + for (const char *line : expected) + QTest::ignoreMessage(QtDebugMsg, line); + w.dumpObjectTree(); + } + + QTestPrivate::androidCompatibleShow(&w); + QVERIFY(QTest::qWaitForWindowActive(&w)); + + { + const char * const expected[] = { + "QWidget::w <200x200+100+100>", + " QLineEdit::le F<200x200+0+0>", + " QWidgetLineControl:: ", + }; + for (const char *line : expected) + QTest::ignoreMessage(QtDebugMsg, line); + w.dumpObjectTree(); + } +} class MaskSetWidget : public QWidget { @@ -8812,6 +9855,8 @@ public: paintedRegion += event->region(); for (const QRect &r : event->region()) p.fillRect(r, Qt::red); + + repainted = true; } void resizeEvent(QResizeEvent *) override @@ -8820,6 +9865,7 @@ public: } QRegion paintedRegion; + bool repainted = false; public slots: void resizeDown() { setGeometry(QRect(0, 50, 50, 50)); } @@ -8829,6 +9875,7 @@ public slots: void tst_QWidget::setMaskInResizeEvent() { UpdateWidget w; + w.setPalette(simplePalette()); w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); w.reset(); w.resize(200, 200); @@ -8846,19 +9893,19 @@ void tst_QWidget::setMaskInResizeEvent() w.reset(); testWidget.paintedRegion = QRegion(); - QTimer::singleShot(0, &testWidget, SLOT(resizeDown())); - QTest::qWait(100); + testWidget.resizeDown(); QRegion expectedParentUpdate(0, 0, 100, 10); // Old testWidget area. expectedParentUpdate += testWidget.geometry(); // New testWidget area. + QTRY_VERIFY(testWidget.repainted); QTRY_COMPARE(w.paintedRegion, expectedParentUpdate); QTRY_COMPARE(testWidget.paintedRegion, testWidget.mask()); testWidget.paintedRegion = QRegion(); - // Now resize the widget again, but in the oposite direction - QTimer::singleShot(0, &testWidget, SLOT(resizeUp())); - QTest::qWait(100); - + testWidget.repainted = false; + // Now resize the widget again, but in the opposite direction + testWidget.resizeUp(); + QTRY_VERIFY(testWidget.repainted); QTRY_COMPARE(testWidget.paintedRegion, testWidget.mask()); } @@ -8908,6 +9955,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(); @@ -9292,28 +10340,32 @@ void tst_QWidget::translucentWidget() label.setWindowTitle(QLatin1String(QTest::currentTestFunction())); label.setFixedSize(16,16); label.setAttribute(Qt::WA_TranslucentBackground); - const QPoint labelPos = QGuiApplication::primaryScreen()->availableGeometry().topLeft(); - label.move(labelPos); + label.move(m_availableTopLeft); label.show(); QVERIFY(QTest::qWaitForWindowExposed(&label)); QPixmap widgetSnapshot = -#ifdef Q_OS_WIN - QGuiApplication::primaryScreen()->grabWindow(0, labelPos.x(), labelPos.y(), label.width(), label.height()); -#else label.grab(QRect(QPoint(0, 0), label.size())); -#endif const QImage actual = widgetSnapshot.toImage().convertToFormat(QImage::Format_RGB32); QImage expected = pm.toImage().scaled(label.devicePixelRatio() * pm.size()); expected.setDevicePixelRatio(label.devicePixelRatio()); +#ifdef Q_OS_ANDROID + // Android uses Format_ARGB32_Premultiplied by default + expected = expected.convertToFormat(QImage::Format_RGB32); +#endif QCOMPARE(actual.size(),expected.size()); QCOMPARE(actual,expected); const QWindow *window = label.windowHandle(); - const QSurfaceFormat translucentFormat = window->requestedFormat(); + const QSurfaceFormat translucentFormat = window->format(); label.setAttribute(Qt::WA_TranslucentBackground, false); - const QSurfaceFormat opaqueFormat = window->requestedFormat(); - QVERIFY(translucentFormat != opaqueFormat); + // Changing WA_TranslucentBackground with an already created native window + // has no effect since Qt 5.0 due to the introduction of QWindow et al. + // This means that the change must *not* be reflected in the + // QSurfaceFormat, because there is no change when it comes to the + // underlying native window. Otherwise the state would no longer + // describe reality (the native window) See QTBUG-85714. + QVERIFY(translucentFormat == window->format()); } class MaskResizeTestWidget : public QWidget @@ -9355,11 +10407,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(); @@ -9389,6 +10442,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); @@ -9668,6 +10722,8 @@ void tst_QWidget::syntheticEnterLeave() }; QCursor::setPos(m_safeCursorPos); + if (!QTest::qWaitFor([this]{ return QCursor::pos() == m_safeCursorPos; })) + QSKIP("Can't move cursor"); MyWidget window; window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); @@ -9708,7 +10764,8 @@ void tst_QWidget::syntheticEnterLeave() // Position the cursor in the middle of the window. const QPoint globalPos = window.mapToGlobal(QPoint(100, 100)); QCursor::setPos(globalPos); // Enter child2 and grandChild. - QTest::qWait(300); + if (!QTest::qWaitFor([globalPos]{ return QCursor::pos() == globalPos; })) + QSKIP("Can't move cursor"); QCOMPARE(window.numLeaveEvents, 0); QCOMPARE(child2->numLeaveEvents, 0); @@ -9759,6 +10816,129 @@ void tst_QWidget::syntheticEnterLeave() #endif #ifndef QT_NO_CURSOR +void tst_QWidget::enterLeaveOnWindowShowHide_data() +{ + QTest::addColumn<Qt::WindowType>("windowType"); + QTest::addRow("dialog") << Qt::Dialog; + QTest::addRow("popup") << Qt::Popup; +} + + +/*! + Verify that a window that has the mouse gets a leave event + when a dialog or popup opens (even if that dialog or popup is + not under the mouse), and an enter event when the secondary window + closes again (while the mouse is still over the original widget. + + Since mouse grabbing might cause some event interaction, simulate + the opening of the secondary window from a mouse press, like we would with + a button or context menu. See QTBUG-78970. +*/ +void tst_QWidget::enterLeaveOnWindowShowHide() +{ + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + + QFETCH(Qt::WindowType, windowType); + class Widget : public QWidget + { + public: + int numEnterEvents = 0; + int numLeaveEvents = 0; + QPoint enterPosition; + Qt::WindowType secondaryWindowType = {}; + protected: + void enterEvent(QEnterEvent *e) override + { + enterPosition = e->position().toPoint(); + ++numEnterEvents; + } + void leaveEvent(QEvent *) override + { + enterPosition = {}; + ++numLeaveEvents; + } + void mousePressEvent(QMouseEvent *e) override + { + QWidget *secondary = nullptr; + switch (secondaryWindowType) { + case Qt::Dialog: { + QDialog *dialog = new QDialog(this); + dialog->setModal(true); + dialog->setWindowModality(Qt::ApplicationModal); + secondary = dialog; + break; + } + case Qt::Popup: { + QMenu *menu = new QMenu(this); + menu->addAction("Action 1"); + menu->addAction("Action 2"); + secondary = menu; + break; + } + default: + QVERIFY2(false, "Test case not implemented for window type"); + break; + } + + QPoint secondaryPos = e->globalPosition().toPoint(); + if (e->button() == Qt::LeftButton) + secondaryPos += QPoint(10, 10); // cursor outside secondary + else + secondaryPos -= QPoint(10, 10); // cursor inside secondary + secondary->move(secondaryPos); + secondary->show(); + if (!QTest::qWaitForWindowExposed(secondary)) + QEXPECT_FAIL("", "Secondary window failed to show, test will fail", Abort); + if (secondaryWindowType == Qt::Dialog && QGuiApplication::platformName() == "windows") + QTest::qWait(1000); // on Windows, we have to wait for fade-in effects + } + }; + + int expectedEnter = 0; + int expectedLeave = 0; + + Widget widget; + widget.secondaryWindowType = windowType; + const QRect screenGeometry = widget.screen()->availableGeometry(); + const QPoint cursorPos = screenGeometry.topLeft() + QPoint(50, 50); + widget.setGeometry(QRect(cursorPos - QPoint(50, 50), screenGeometry.size() / 4)); + QCursor::setPos(cursorPos); + + if (!QTest::qWaitFor([&]{ return widget.geometry().contains(QCursor::pos()); })) + QSKIP("We can't move the cursor"); + widget.show(); + QVERIFY(QTest::qWaitForWindowActive(&widget)); + + ++expectedEnter; + QTRY_COMPARE_WITH_TIMEOUT(widget.numEnterEvents, expectedEnter, 1000); + QCOMPARE(widget.enterPosition, widget.mapFromGlobal(cursorPos)); + QVERIFY(widget.underMouse()); + + QTest::mouseClick(&widget, Qt::LeftButton, {}, widget.mapFromGlobal(cursorPos)); + ++expectedLeave; + QTRY_COMPARE_WITH_TIMEOUT(widget.numLeaveEvents, expectedLeave, 1000); + QVERIFY(!widget.underMouse()); + QTRY_VERIFY(QApplication::activeModalWidget() || QApplication::activePopupWidget()); + if (QApplication::activeModalWidget()) + QApplication::activeModalWidget()->close(); + else if (QApplication::activePopupWidget()) + QApplication::activePopupWidget()->close(); + ++expectedEnter; + // Use default timeout, the test is flaky on Windows otherwise. + QTRY_VERIFY(widget.numEnterEvents >= expectedEnter); + // When a modal dialog closes we might get more than one enter event on macOS. + // This seems to depend on timing, so we tolerate that flakiness for now. + if (widget.numEnterEvents > expectedEnter && QGuiApplication::platformName() == "cocoa") + QEXPECT_FAIL("dialog", "On macOS, we might get more than one Enter event", Continue); + + QCOMPARE(widget.numEnterEvents, expectedEnter); + QCOMPARE(widget.enterPosition, widget.mapFromGlobal(cursorPos)); + QVERIFY(widget.underMouse()); +} +#endif + +#ifndef QT_NO_CURSOR void tst_QWidget::taskQTBUG_4055_sendSyntheticEnterLeave() { if (m_platform == QStringLiteral("wayland")) @@ -9788,69 +10968,175 @@ void tst_QWidget::taskQTBUG_4055_sendSyntheticEnterLeave() int numEnterEvents = 0, numMouseMoveEvents = 0; }; - QCursor::setPos(m_safeCursorPos); - - SELParent parent; - parent.setWindowTitle(QLatin1String(QTest::currentTestFunction())); - parent.move(200, 200); - parent.resize(200, 200); - SELChild child(&parent); - child.resize(200, 200); - parent.show(); - QVERIFY(QTest::qWaitForWindowActive(&parent)); - - QCursor::setPos(child.mapToGlobal(QPoint(100, 100))); - // Make sure the cursor has entered the child. - QTRY_VERIFY(child.numEnterEvents > 0); - - child.hide(); - child.reset(); - child.show(); - - // Make sure the child gets enter event and no mouse move event. - QTRY_COMPARE(child.numEnterEvents, 1); - QCOMPARE(child.numMouseMoveEvents, 0); - - child.hide(); - child.reset(); - child.setMouseTracking(true); - child.show(); - - // Make sure the child gets enter event. - // Note that we verify event->button() and event->buttons() - // in SELChild::mouseMoveEvent(). - QTRY_COMPARE(child.numEnterEvents, 1); - QCOMPARE(child.numMouseMoveEvents, 0); - - // Sending synthetic enter/leave trough the parent's mousePressEvent handler. - parent.child = &child; - - child.hide(); - child.reset(); - QTest::mouseClick(&parent, Qt::LeftButton); - - // Make sure the child gets enter event. - QTRY_COMPARE(child.numEnterEvents, 1); - QCOMPARE(child.numMouseMoveEvents, 0); - - child.hide(); - child.reset(); - QTest::keyPress(&parent, Qt::Key_Shift); - QTest::mouseClick(&parent, Qt::LeftButton); - - // Make sure the child gets enter event - QTRY_COMPARE(child.numEnterEvents, 1); - QCOMPARE(child.numMouseMoveEvents, 0); - QTest::keyRelease(&child, Qt::Key_Shift); - child.hide(); - child.reset(); - child.setMouseTracking(false); - QTest::mouseClick(&parent, Qt::LeftButton); - - // Make sure the child gets enter event and no mouse move event. - QTRY_COMPARE(child.numEnterEvents, 1); - QCOMPARE(child.numMouseMoveEvents, 0); + QCursor::setPos(m_safeCursorPos); + if (!QTest::qWaitFor([this]{ return QCursor::pos() == m_safeCursorPos; })) + QSKIP("Can't move cursor"); + + SELParent parent; + parent.setWindowTitle(QLatin1String(QTest::currentTestFunction())); + parent.move(200, 200); + parent.resize(200, 200); + SELChild child(&parent); + child.resize(200, 200); + parent.show(); + QVERIFY(QTest::qWaitForWindowActive(&parent)); + + const QPoint childPos = child.mapToGlobal(QPoint(100, 100)); + QCursor::setPos(childPos); + if (!QTest::qWaitFor([childPos]{ return QCursor::pos() == childPos; })) + QSKIP("Can't move cursor"); + + // Make sure the cursor has entered the child. + QTRY_VERIFY(child.numEnterEvents > 0); + + child.hide(); + child.reset(); + child.show(); + + // Make sure the child gets enter event and no mouse move event. + QTRY_COMPARE(child.numEnterEvents, 1); + QCOMPARE(child.numMouseMoveEvents, 0); + + child.hide(); + child.reset(); + child.setMouseTracking(true); + child.show(); + + // Make sure the child gets enter event. + // Note that we verify event->button() and event->buttons() + // in SELChild::mouseMoveEvent(). + QTRY_COMPARE(child.numEnterEvents, 1); + QCOMPARE(child.numMouseMoveEvents, 0); + + // Sending synthetic enter/leave through the parent's mousePressEvent handler. + parent.child = &child; + + child.hide(); + child.reset(); + QTest::mouseClick(&parent, Qt::LeftButton); + + // Make sure the child gets enter event. + QTRY_COMPARE(child.numEnterEvents, 1); + QCOMPARE(child.numMouseMoveEvents, 0); + + child.hide(); + child.reset(); + QTest::keyPress(&parent, Qt::Key_Shift); + QTest::mouseClick(&parent, Qt::LeftButton); + + // Make sure the child gets enter event + QTRY_COMPARE(child.numEnterEvents, 1); + QCOMPARE(child.numMouseMoveEvents, 0); + QTest::keyRelease(&child, Qt::Key_Shift); + child.hide(); + child.reset(); + child.setMouseTracking(false); + QTest::mouseClick(&parent, Qt::LeftButton); + + // Make sure the child gets enter event and no mouse move event. + 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() @@ -9956,6 +11242,7 @@ void tst_QWidget::focusWidget_task254563() void tst_QWidget::destroyBackingStore() { UpdateWidget w; + w.setPalette(simplePalette()); w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); centerOnScreen(&w); w.reset(); @@ -10080,6 +11367,157 @@ void tst_QWidget::setGraphicsEffect() QVERIFY(!blurEffect); } + +class TestGraphicsEffect : public QGraphicsEffect +{ +public: + TestGraphicsEffect(QObject *parent = nullptr) + : QGraphicsEffect(parent) + { + m_pattern = QPixmap(10, 10); + m_pattern.fill(Qt::lightGray); + QPainter p(&m_pattern); + p.fillRect(QRectF(0, 0, 5, 5), QBrush(Qt::darkGray)); + p.fillRect(QRectF(5, 5, 5, 5), QBrush(Qt::darkGray)); + } + void setExtent(int extent) + { + m_extent = extent; + } + QRectF boundingRectFor(const QRectF &sr) const override + { + return QRectF(sr.x() - m_extent, sr.y() - m_extent, + sr.width() + 2 * m_extent, sr.height() + 2 * m_extent); + } +protected: + void draw(QPainter *painter) override + { + QBrush brush; + brush.setTexture(m_pattern); + brush.setStyle(Qt::TexturePattern); + QPaintDevice *p = painter->device(); + painter->fillRect(QRect(-m_extent, -m_extent, + p->width() + m_extent, p->height() + m_extent), brush); + } + QPixmap m_pattern; + int m_extent = 0; +}; + +static QImage fillExpected1() +{ + QImage expected(QSize(40, 40), QImage::Format_RGB32); + QPainter p(&expected); + p.fillRect(QRect{{0, 0}, expected.size()}, QBrush(Qt::gray)); + p.fillRect(QRect(10, 10, 10, 10), QBrush(Qt::red)); + p.fillRect(QRect(20, 20, 10, 10), QBrush(Qt::blue)); + return expected; +} +static QImage fillExpected2() +{ + QImage expected = fillExpected1(); + QPainter p(&expected); + p.fillRect(QRect(10, 10, 5, 5), QBrush(Qt::darkGray)); + p.fillRect(QRect(15, 15, 5, 5), QBrush(Qt::darkGray)); + p.fillRect(QRect(15, 10, 5, 5), QBrush(Qt::lightGray)); + p.fillRect(QRect(10, 15, 5, 5), QBrush(Qt::lightGray)); + return expected; +} +static QImage fillExpected3() +{ + QImage expected(QSize(40, 40), QImage::Format_RGB32); + QPixmap pattern; + pattern = QPixmap(10, 10); + pattern.fill(Qt::lightGray); + QPainter p(&pattern); + p.fillRect(QRectF(0, 0, 5, 5), QBrush(Qt::darkGray)); + p.fillRect(QRectF(5, 5, 5, 5), QBrush(Qt::darkGray)); + QBrush brush; + brush.setTexture(pattern); + brush.setStyle(Qt::TexturePattern); + QPainter p2(&expected); + p2.fillRect(QRect{{0, 0}, expected.size()}, brush); + return expected; +} +static QImage fillExpected4() +{ + QImage expected = fillExpected1(); + QPixmap pattern; + pattern = QPixmap(10, 10); + pattern.fill(Qt::lightGray); + QPainter p(&pattern); + p.fillRect(QRectF(0, 0, 5, 5), QBrush(Qt::darkGray)); + p.fillRect(QRectF(5, 5, 5, 5), QBrush(Qt::darkGray)); + QBrush brush; + brush.setTexture(pattern); + brush.setStyle(Qt::TexturePattern); + QPainter p2(&expected); + p2.fillRect(QRect{{15, 15}, QSize{20, 20}}, brush); + return expected; +} + +void tst_QWidget::render_graphicsEffect_data() +{ + QTest::addColumn<QImage>("expected"); + QTest::addColumn<bool>("topLevelEffect"); + QTest::addColumn<bool>("child1Effect"); + QTest::addColumn<bool>("child2Effect"); + QTest::addColumn<int>("extent"); + + QTest::addRow("no_effect") << fillExpected1() << false << false << false << 0; + QTest::addRow("first_child_effect") << fillExpected2() << false << true << false << 0; + QTest::addRow("top_level_effect") << fillExpected3() << true << false << false << 0; + QTest::addRow("effect_with_extent") << fillExpected4() << false << false << true << 5; +} + +void tst_QWidget::render_graphicsEffect() +{ + QFETCH(QImage, expected); + QFETCH(bool, topLevelEffect); + QFETCH(bool, child1Effect); + QFETCH(bool, child2Effect); + QFETCH(int, extent); + + QScopedPointer<QWidget> topLevel(new QWidget); + topLevel->setPalette(Qt::gray); + topLevel->resize(40, 40); + topLevel->setWindowTitle(QLatin1String(QTest::currentTestFunction()) + QLatin1String("::") + + QLatin1String(QTest::currentDataTag())); + + // Render widget with 2 child widgets + QImage image(topLevel->size(), QImage::Format_RGB32); + image.fill(QColor(Qt::gray).rgb()); + + QPainter painter(&image); + + QWidget *childWidget1(new QWidget(topLevel.data())); + childWidget1->setAutoFillBackground(true); + childWidget1->setPalette(Qt::red); + childWidget1->resize(10, 10); + childWidget1->move(10, 10); + QWidget *childWidget2(new QWidget(topLevel.data())); + childWidget2->setAutoFillBackground(true); + childWidget2->setPalette(Qt::blue); + childWidget2->resize(10, 10); + childWidget2->move(20, 20); + + TestGraphicsEffect *graphicsEffect(new TestGraphicsEffect(topLevel.data())); + if (topLevelEffect) + topLevel->setGraphicsEffect(graphicsEffect); + if (child1Effect) + childWidget1->setGraphicsEffect(graphicsEffect); + if (child2Effect) + childWidget2->setGraphicsEffect(graphicsEffect); + graphicsEffect->setExtent(extent); + + // Render without effect + topLevel->render(&painter); +#ifdef RENDER_DEBUG + image.save("render_GraphicsEffect" + QTest::currentDataTag() + ".png"); + expected.save("render_GraphicsEffect_expected" + QTest::currentDataTag() + ".png"); +#endif + QCOMPARE(image, expected); +} + void tst_QWidget::activateWindow() { if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) @@ -10125,6 +11563,9 @@ void tst_QWidget::activateWindow() void tst_QWidget::openModal_taskQTBUG_5804() { +#ifdef Q_OS_ANDROID + QSKIP("This test hangs on Android"); +#endif class Widget : public QWidget { public: @@ -10207,9 +11648,16 @@ void tst_QWidget::focusProxy() window.setFocus(); window.show(); - window.activateWindow(); - if (!QTest::qWaitForWindowExposed(&window) || !QTest::qWaitForWindowActive(&window)) - QSKIP("Window activation failed"); + if (!QTest::qWaitForWindowExposed(&window)) + QSKIP("Window exposed failed"); + if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) { + window.activateWindow(); + if (!QTest::qWaitForWindowActive(&window)) + QSKIP("Window activation failed"); + } else { + if (!QTest::qWaitFor([&]() { return window.windowHandle()->isActive(); }, 5000)) + QSKIP("Window activation failed"); + } // given a widget without focus proxy QVERIFY(window.hasFocus()); @@ -10265,32 +11713,42 @@ 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); + QSKIP("QWindow::requestActivate() is not supported."); - QVERIFY(!child->hasFocus()); - QVERIFY(!toplevel->hasFocus()); + // 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. + QWidget topLevel; + QWidget plain(&topLevel); + QLineEdit edit(&topLevel); + topLevel.show(); - 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()); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + QVERIFY(QTest::qWaitForWindowActive(&topLevel)); + + // A plain widget should return false for ImEnabled + plain.setFocus(Qt::OtherFocusReason); + QCOMPARE(QApplication::focusWidget(), &plain); + QVariant imEnabled = QApplication::inputMethod()->queryFocusObject(Qt::ImEnabled, QVariant()); + QVERIFY(imEnabled.isValid()); + QVERIFY(!imEnabled.toBool()); + + // But a lineedit should return true + edit.setFocus(Qt::OtherFocusReason); + QCOMPARE(QApplication::focusWidget(), &edit); + imEnabled = QApplication::inputMethod()->queryFocusObject(Qt::ImEnabled, QVariant()); + QVERIFY(imEnabled.isValid()); + QVERIFY(imEnabled.toBool()); + + // 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()); } #ifdef QT_BUILD_INTERNAL @@ -10349,7 +11807,7 @@ void tst_QWidget::taskQTBUG_7532_tabOrderWithFocusProxy() void tst_QWidget::movedAndResizedAttributes() { // Use Qt::Tool as fully decorated windows have a minimum width of 160 on - QWidget w(nullptr, Qt::Tool); + QWidget w; w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); w.show(); @@ -10387,11 +11845,11 @@ void tst_QWidget::movedAndResizedAttributes() QVERIFY(!w.testAttribute(Qt::WA_Resized)); w.showNormal(); - w.move(10,10); + w.move(m_availableTopLeft); QVERIFY(w.testAttribute(Qt::WA_Moved)); QVERIFY(!w.testAttribute(Qt::WA_Resized)); - w.resize(100, 100); + w.resize(m_testWidgetSize); QVERIFY(w.testAttribute(Qt::WA_Moved)); QVERIFY(w.testAttribute(Qt::WA_Resized)); } @@ -10649,7 +12107,6 @@ void tst_QWidget::grabMouse() layout->addWidget(grabber); centerOnScreen(&w); w.show(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QStringList expectedLog; @@ -10686,7 +12143,6 @@ void tst_QWidget::grabKeyboard() layout->addWidget(nonGrabber); centerOnScreen(&w); w.show(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); nonGrabber->setFocus(); grabber->grabKeyboard(); @@ -10780,6 +12236,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; @@ -11194,9 +12653,10 @@ void tst_QWidget::underMouse() QCOMPARE(QApplication::activePopupWidget(), &popupWidget); // Send an artificial leave event for window, as it won't get generated automatically - // due to cursor not actually being over the window. - QWindowSystemInterface::handleLeaveEvent(window); - QApplication::processEvents(); + // due to cursor not actually being over the window. The Cocoa and offscreen plugins + // do this for us. + if (QGuiApplication::platformName() != "cocoa" && QGuiApplication::platformName() != "offscreen") + QWindowSystemInterface::handleLeaveEvent<QWindowSystemInterface::SynchronousDelivery>(window); // If there is an active popup, undermouse should not be reported (QTBUG-27478), // but opening a popup causes leave for widgets under mouse. @@ -11298,10 +12758,6 @@ void tst_QWidget::underMouse() // Mouse leaves popup and enters topLevelWidget, should cause leave for popup // but no enter to topLevelWidget. -#ifdef Q_OS_DARWIN - // Artificial leave event needed for Cocoa. - QWindowSystemInterface::handleLeaveEvent(popupWindow); -#endif QTest::mouseMove(popupWindow, popupWindow->mapFromGlobal(window->mapToGlobal(inWindowPoint))); QApplication::processEvents(); QVERIFY(!topLevelWidget.underMouse()); @@ -11385,9 +12841,6 @@ public: // when mousing over it. void tst_QWidget::taskQTBUG_27643_enterEvents() { -#ifdef Q_OS_MACOS - QSKIP("QTBUG-52974: this test can crash!"); -#endif // Move the mouse cursor to a safe location so it won't interfere QCursor::setPos(m_safeCursorPos); @@ -11494,6 +12947,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); @@ -11592,60 +13046,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); @@ -11721,6 +13169,36 @@ void tst_QWidget::closeEvent() widget.windowHandle()->close(); widget.windowHandle()->close(); QCOMPARE(widget.closeCount, 1); + + CloseCountingWidget widget2; + widget2.show(); + QVERIFY(QTest::qWaitForWindowExposed(&widget2)); + widget2.close(); + widget2.close(); + QCOMPARE(widget2.closeCount, 1); + widget2.closeCount = 0; + + widget2.show(); + QVERIFY(QTest::qWaitForWindowExposed(&widget2)); + widget2.close(); + QCOMPARE(widget2.closeCount, 1); + + CloseCountingWidget widget3; + widget3.close(); + widget3.close(); + QEXPECT_FAIL("", "Closing a widget without a window will unconditionally send close events", Continue); + QCOMPARE(widget3.closeCount, 0); + + QWidget parent; + CloseCountingWidget child; + child.setParent(&parent); + parent.show(); + QVERIFY(QTest::qWaitForWindowExposed(&parent)); + child.close(); + QCOMPARE(child.closeCount, 1); + child.close(); + QEXPECT_FAIL("", "Closing a widget without a window will unconditionally send close events", Continue); + QCOMPARE(child.closeCount, 1); } void tst_QWidget::closeWithChildWindow() @@ -11922,10 +13400,602 @@ protected: void tst_QWidget::deleteWindowInCloseEvent() { - // Just checking if closing this widget causes a crash +#ifdef Q_OS_ANDROID + QSKIP("This test crashes on Android"); +#endif + QSignalSpy quitSpy(qApp, &QGuiApplication::lastWindowClosed); + + // Closing this widget should not cause a crash auto widget = new DeleteOnCloseEventWidget; - widget->close(); - QVERIFY(true); + widget->show(); + QVERIFY(QTest::qWaitForWindowExposed(widget)); + QTimer::singleShot(0, widget, [&]{ + widget->close(); + }); + QApplication::exec(); + + // It should still result in a single lastWindowClosed emit + QCOMPARE(quitSpy.size(), 1); +} + +/*! + Verify that both closing and deleting the last (only) window-widget + exits the application event loop. +*/ +void tst_QWidget::quitOnClose() +{ + QSignalSpy quitSpy(qApp, &QGuiApplication::lastWindowClosed); + + std::unique_ptr<QWidget>widget(new QWidget); + widget->show(); + QVERIFY(QTest::qWaitForWindowExposed(widget.get())); + + // QGuiApplication::lastWindowClosed is documented to only be emitted + // when we are in exec() + QTimer::singleShot(0, widget.get(), [&]{ + widget->close(); + }); + QApplication::exec(); + QCOMPARE(quitSpy.size(), 1); + + widget->show(); + QVERIFY(QTest::qWaitForWindowExposed(widget.get())); + QTimer::singleShot(0, widget.get(), [&]{ + widget.reset(); + }); + QApplication::exec(); + QCOMPARE(quitSpy.size(), 2); +} + +void tst_QWidget::setParentChangesFocus_data() +{ + QTest::addColumn<Qt::WindowType>("initialType"); + QTest::addColumn<bool>("initialParent"); + QTest::addColumn<Qt::WindowType>("targetType"); + QTest::addColumn<bool>("targetParent"); + QTest::addColumn<bool>("reparentBeforeShow"); + QTest::addColumn<QString>("focusWidget"); + + for (const bool before : {true, false}) { + const char *tag = before ? "before" : "after"; + QTest::addRow("give dialog parent, %s", tag) + << Qt::Dialog << false << Qt::Dialog << true << before << "lineEdit"; + QTest::addRow("make dialog parentless, %s", tag) + << Qt::Dialog << true << Qt::Dialog << false << before << "lineEdit"; + QTest::addRow("dialog to sheet, %s", tag) + << Qt::Dialog << true << Qt::Sheet << true << before << "lineEdit"; + QTest::addRow("window to widget, %s", tag) + << Qt::Window << true << Qt::Widget << true << before << "windowEdit"; + QTest::addRow("widget to window, %s", tag) + << Qt::Widget << true << Qt::Window << true << before << "lineEdit"; + } +} + +void tst_QWidget::setParentChangesFocus() +{ + QFETCH(Qt::WindowType, initialType); + QFETCH(bool, initialParent); + QFETCH(Qt::WindowType, targetType); + QFETCH(bool, targetParent); + QFETCH(bool, reparentBeforeShow); + QFETCH(QString, focusWidget); + + QWidget window; + window.setObjectName("window"); + QLineEdit *windowEdit = new QLineEdit(&window); + windowEdit->setObjectName("windowEdit"); + windowEdit->setFocus(); + + std::unique_ptr<QWidget> secondary(new QWidget(initialParent ? &window : nullptr, initialType)); + secondary->setObjectName("secondary"); + QLineEdit *lineEdit = new QLineEdit(secondary.get()); + lineEdit->setObjectName("lineEdit"); + QPushButton *pushButton = new QPushButton(secondary.get()); + pushButton->setObjectName("pushButton"); + lineEdit->setFocus(); + + window.show(); + QVERIFY(QTest::qWaitForWindowActive(&window)); + + if (reparentBeforeShow) { + secondary->setParent(targetParent ? &window : nullptr, targetType); + // making a widget into a window doesn't set a focusWidget until shown + if (secondary->focusWidget()) + QCOMPARE(secondary->focusWidget()->objectName(), focusWidget); + } + secondary->show(); + QApplicationPrivate::setActiveWindow(secondary.get()); + QVERIFY(QTest::qWaitForWindowActive(secondary.get())); + + if (!reparentBeforeShow) { + secondary->setParent(targetParent ? &window : nullptr, targetType); + secondary->show(); // reparenting hides, so show again + 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(); + QVERIFY(QTest::qWaitForWindowActive(&dialog)); + QVERIFY(dialog.isActiveWindow()); + QCOMPARE(QApplication::activeWindow(), &dialog); + + dialog.hide(); + QTRY_VERIFY(!dialog.isVisible()); + + QMainWindow window; + window.show(); + QVERIFY(QTest::qWaitForWindowActive(&window)); + QVERIFY(window.isActiveWindow()); + 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() +{ + QWidget w; + w.setAutoFillBackground(true); + QPalette p = w.palette(); + p.setColor(QPalette::Window, Qt::red); + w.setPalette(p); + + // Need to toggle showFullScreen() twice, see QTBUG-101968 + w.showFullScreen(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + w.showFullScreen(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + + // Make sure that the lower part of the screen contains the red widget, not + // the buttons. + + const QRect fullGeometry = w.screen()->geometry(); + // Take a rect of (20 x 20) from the bottom area + const QRect grabArea(10, fullGeometry.height() - 30, 20, 20); + const QImage img = grabFromWidget(&w, grabArea).toImage().convertedTo(QImage::Format_RGB32); + + QPixmap expectedPix(20, 20); + expectedPix.fill(Qt::red); + const QImage expectedImg = expectedPix.toImage().convertedTo(QImage::Format_RGB32); + + QCOMPARE(img, expectedImg); +} +#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) diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget_mac_helpers.h b/tests/auto/widgets/kernel/qwidget/tst_qwidget_mac_helpers.h deleted file mode 100644 index 87de300da9..0000000000 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget_mac_helpers.h +++ /dev/null @@ -1,35 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include <QtCore/QString> -#include <QtCore/QPair> -#include <QtWidgets/QWidget> - -#pragma once // Yeah, it's deprecated in general, but it's standard practice for Mac OS X. - -QString nativeWindowTitle(QWidget *widget, Qt::WindowState state); -bool nativeWindowModified(QWidget *widget); diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget_mac_helpers.mm b/tests/auto/widgets/kernel/qwidget/tst_qwidget_mac_helpers.mm deleted file mode 100644 index e2d00aa25b..0000000000 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget_mac_helpers.mm +++ /dev/null @@ -1,57 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// some versions of CALayer.h use 'slots' as an identifier -#define QT_NO_KEYWORDS - -#include "tst_qwidget_mac_helpers.h" -#include <QApplication> -#include <qpa/qplatformnativeinterface.h> -#include <private/qcore_mac_p.h> - -#include <AppKit/AppKit.h> - -QString nativeWindowTitle(QWidget *window, Qt::WindowState state) -{ - QWindow *qwindow = window->windowHandle(); - NSWindow *nswindow = (NSWindow *) qApp->platformNativeInterface()->nativeResourceForWindow("nswindow", qwindow); - QCFString macTitle; - if (state == Qt::WindowMinimized) { - macTitle = reinterpret_cast<CFStringRef>([[nswindow miniwindowTitle] retain]); - } else { - macTitle = reinterpret_cast<CFStringRef>([[nswindow title] retain]); - } - return macTitle; -} - -bool nativeWindowModified(QWidget *widget) -{ - QWindow *qwindow = widget->windowHandle(); - NSWindow *nswindow = (NSWindow *) qApp->platformNativeInterface()->nativeResourceForWindow("nswindow", qwindow); - return [nswindow isDocumentEdited]; -} diff --git a/tests/auto/widgets/kernel/qwidget_window/BLACKLIST b/tests/auto/widgets/kernel/qwidget_window/BLACKLIST index 7eae8254ac..77853a3e8c 100644 --- a/tests/auto/widgets/kernel/qwidget_window/BLACKLIST +++ b/tests/auto/widgets/kernel/qwidget_window/BLACKLIST @@ -1,7 +1,3 @@ -[tst_resize_count] -# QTBUG-66345 -opensuse-42.3 -ubuntu-16.04 # QTBUG-87412 [tst_move_show] android @@ -17,3 +13,6 @@ android android [mouseMoveWithPopup] android +# QTBUG-96270 +[tst_paintEventOnSecondShow] +opensuse diff --git a/tests/auto/widgets/kernel/qwidget_window/CMakeLists.txt b/tests/auto/widgets/kernel/qwidget_window/CMakeLists.txt index 9bae267970..af60c92cbf 100644 --- a/tests/auto/widgets/kernel/qwidget_window/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qwidget_window/CMakeLists.txt @@ -1,16 +1,24 @@ -# Generated from qwidget_window.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qwidget_window Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qwidget_window LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qwidget_window SOURCES tst_qwidget_window.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate Qt::TestPrivate Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp index 654d8e58ba..e771737ae0 100644 --- a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp +++ b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -43,6 +18,7 @@ #include <qlabel.h> #include <qmainwindow.h> #include <qtoolbar.h> +#include <qsignalspy.h> #include <private/qwindow_p.h> #include <private/qguiapplication_p.h> #include <qpa/qplatformintegration.h> @@ -52,6 +28,8 @@ #include <QtTest/private/qtesthelpers_p.h> +#include <QtWidgets/private/qapplication_p.h> + using namespace QTestPrivate; // Compare a window position that may go through scaling in the platform plugin with fuzz. @@ -75,6 +53,7 @@ public: tst_QWidget_window(); public slots: + void init(); void initTestCase(); void cleanupTestCase(); void cleanup(); @@ -90,6 +69,8 @@ private slots: void tst_show_resize(); void tst_show_resize_hide_show(); + void close(); + void tst_windowFilePathAndwindowTitle_data(); void tst_windowFilePathAndwindowTitle(); void tst_windowFilePath_data(); @@ -104,6 +85,7 @@ private slots: void tst_dnd(); void tst_dnd_events(); void tst_dnd_propagation(); + void tst_dnd_destroyOnDrop(); #endif void tst_qtbug35600(); @@ -129,6 +111,13 @@ private slots: void mouseMoveWithPopup_data(); void mouseMoveWithPopup(); + void showHideWindowHandle_data(); + void showHideWindowHandle(); + + void resetFocusObjectOnDestruction(); + + void cleanupOnDestruction(); + private: QSize m_testWidgetSize; const int m_fuzz; @@ -146,6 +135,11 @@ void tst_QWidget_window::initTestCase() { } +void tst_QWidget_window::init() +{ + QTest::failOnWarning(QRegularExpression(".*No such slot.*")); +} + void tst_QWidget_window::cleanupTestCase() { } @@ -247,6 +241,70 @@ void tst_QWidget_window::tst_show_resize_hide_show() QCOMPARE(w.size(), m_testWidgetSize); } +void tst_QWidget_window::close() +{ + // Verfy that closing a QWidgetWindow deletes its platform window, + // as expected of a QWindow subclass. This must be done also + // if QWidget API is used to close. The QCloseEvent must not be + // spontaneous if the close is triggered by a Qt API that the application + // would call in response to an event, and spontaneous if it is directly + // caused by user interaction, such as clicking the (x) in the titlebar. + // We can simulate this only by generating a WindowSystemEvent. + // Children of the window should get a hide event (never spontaneous when + // caused by closing the window). + + struct Widget : public QWidget + { + using QWidget::QWidget; + int spontClose = -1; + int spontHide = -1; + protected: + void hideEvent(QHideEvent *e) override + { spontHide = e->spontaneous() ? 1 : 0; } + void closeEvent(QCloseEvent *e) override + { spontClose = e->spontaneous() ? 1 : 0; } + }; + + // QWindow::close() + { + Widget w; + Widget child(&w); + w.winId(); + QVERIFY(w.windowHandle()); + QVERIFY(w.windowHandle()->handle()); + w.windowHandle()->close(); + QCOMPARE(w.spontClose, 0); + QCOMPARE(child.spontHide, -1); // was never shown + QVERIFY(w.windowHandle()); + QVERIFY(!w.windowHandle()->handle()); + } + + // QWidget::close() + { + Widget w; + Widget child(&w); + w.show(); + QVERIFY(w.windowHandle()); + QVERIFY(w.windowHandle()->handle()); + w.close(); + QCOMPARE(w.spontClose, 0); + QCOMPARE(child.spontHide, 0); + QVERIFY(w.windowHandle()); + QVERIFY(!w.windowHandle()->handle()); + } + + // User-initiated close + { + Widget w; + Widget child(&w); + w.show(); + QWindowSystemInterface::handleCloseEvent(w.windowHandle()); + QApplication::processEvents(); + QCOMPARE(w.spontClose, 1); + QCOMPARE(child.spontHide, 0); + } +} + class PaintTestWidget : public QWidget { public: @@ -368,14 +426,14 @@ void tst_QWidget_window::tst_windowFilePath() void tst_QWidget_window::tst_showWithoutActivating() { QString platformName = QGuiApplication::platformName().toLower(); - if (platformName == "cocoa") - QSKIP("Cocoa: This fails. Figure out why."); - else if (platformName != QStringLiteral("xcb") - && platformName != QStringLiteral("windows") - && platformName != QStringLiteral("ios") - && platformName != QStringLiteral("tvos") - && platformName != QStringLiteral("watchos")) - QSKIP("Qt::WA_ShowWithoutActivating is currently supported only on xcb, windows, and ios/tvos/watchos platforms."); + if (platformName != QStringLiteral("xcb") + && platformName != QStringLiteral("windows") + && platformName != QStringLiteral("cocoa") + && platformName != QStringLiteral("ios") + && platformName != QStringLiteral("tvos") + && platformName != QStringLiteral("watchos")) + QSKIP("Qt::WA_ShowWithoutActivating is currently supported only on xcb, " \ + "windows, and macos/ios/tvos/watchos platforms."); QWidget w1; w1.setAttribute(Qt::WA_ShowWithoutActivating); @@ -399,6 +457,7 @@ void tst_QWidget_window::tst_paintEventOnSecondShow() { PaintTestWidget w; w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); w.hide(); w.paintEventCount = 0; @@ -632,7 +691,6 @@ void tst_QWidget_window::tst_dnd() dndTestWidget.show(); QVERIFY(QTest::qWaitForWindowExposed(&dndTestWidget)); - qApp->setActiveWindow(&dndTestWidget); QVERIFY(QTest::qWaitForWindowActive(&dndTestWidget)); QMimeData mimeData; @@ -872,6 +930,78 @@ void tst_QWidget_window::tst_dnd_propagation() QCOMPARE(target.mDndEvents, "enter leave enter drop "); } + +class ReparentSelfOnDropWidget : public QWidget +{ +public: + ReparentSelfOnDropWidget(QWidget *newFutureParent) + : m_newFutureParent(newFutureParent) + { + setAcceptDrops(true); + + const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry(); + auto width = availableGeometry.width() / 6; + auto height = availableGeometry.height() / 4; + + setGeometry(availableGeometry.x() + 200, availableGeometry.y() + 200, width, height); + + QLabel *label = new QLabel(QStringLiteral("Test"), this); + label->setGeometry(40, 40, 60, 60); + label->setAcceptDrops(true); + } + + void dragEnterEvent(QDragEnterEvent *event) override + { + event->accept(); + } + + void dragMoveEvent(QDragMoveEvent *event) override + { + event->acceptProposedAction(); + } + + void dropEvent(QDropEvent *event) override + { + event->accept(); + // Turn 'this' from a top-level widget to a child widget. + // This destroys the QWidgetWindow since the widget is no longer top-level. + setParent(m_newFutureParent); + } + +private: + QWidget *m_newFutureParent; +}; + +void tst_QWidget_window::tst_dnd_destroyOnDrop() +{ + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Wayland: This fails. Figure out why."); + + QMimeData mimeData; + mimeData.setText(QLatin1String("testmimetext")); + + QWidget newParent; + newParent.resize(400, 400); + newParent.show(); + QVERIFY(QTest::qWaitForWindowActive(&newParent)); + + ReparentSelfOnDropWidget *target = new ReparentSelfOnDropWidget(&newParent); + target->show(); + QVERIFY(QTest::qWaitForWindowActive(target)); + + Qt::DropActions supportedActions = Qt::DropAction::CopyAction; + QWindow *window = target->windowHandle(); + + auto posInsideDropTarget = QHighDpi::toNativePixels(QPoint(20, 20), window->screen()); + auto posInsideLabel = QHighDpi::toNativePixels(QPoint(60, 60), window->screen()); + + QWindowSystemInterface::handleDrag(window, &mimeData, posInsideDropTarget, supportedActions, {}, {}); + QWindowSystemInterface::handleDrag(window, &mimeData, posInsideLabel, supportedActions, {}, {}); + QWindowSystemInterface::handleDrop(window, &mimeData, posInsideLabel, supportedActions, {}, {}); + + QGuiApplication::processEvents(); +} + #endif void tst_QWidget_window::tst_qtbug35600() @@ -1332,6 +1462,9 @@ void tst_QWidget_window::mouseMoveWithPopup_data() void tst_QWidget_window::mouseMoveWithPopup() { + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Wayland: Skip this test, see also QTBUG-107154"); + QFETCH(Qt::WindowType, windowType); class Window : public QWidget @@ -1397,7 +1530,7 @@ void tst_QWidget_window::mouseMoveWithPopup() QSKIP("Failed to expose window!"); QCOMPARE(QApplication::activePopupWidget(), nullptr); - QCOMPARE(QApplication::activeWindow(), &topLevel); + QTRY_COMPARE(QApplication::activeWindow(), &topLevel); QPoint mousePos = topLevel.geometry().center(); QWindow *window = nullptr; @@ -1452,6 +1585,9 @@ void tst_QWidget_window::mouseMoveWithPopup() topLevel.resetCounters(); topLevel.popup->resetCounters(); + QTRY_VERIFY(QApplication::activeWindow() == topLevel.popup + || QApplication::activePopupWidget() == topLevel.popup); + // nested popup, same procedure QCOMPARE(mouseAction(Qt::RightButton), QEvent::MouseButtonPress); QVERIFY(topLevel.popup); @@ -1504,5 +1640,214 @@ void tst_QWidget_window::mouseMoveWithPopup() QCOMPARE(topLevel.popup->mouseReleaseCount, 1); } +struct ShowHideEntry { + QEvent::Type action; + Qt::WindowType target; + using List = QList<ShowHideEntry>; +}; + +void tst_QWidget_window::showHideWindowHandle_data() +{ + QTest::addColumn<ShowHideEntry::List>("entries"); + + QTest::addRow("show/hide widget") << ShowHideEntry::List{ + { QEvent::Show, Qt::Widget }, { QEvent::Hide, Qt::Widget } + }; + QTest::addRow("show/hide window") << ShowHideEntry::List{ + { QEvent::Show, Qt::Window }, { QEvent::Hide, Qt::Window } + }; + QTest::addRow("show widget, hide window") << ShowHideEntry::List{ + { QEvent::Show, Qt::Widget }, { QEvent::Hide, Qt::Window } + }; + QTest::addRow("show window, hide widget") << ShowHideEntry::List{ + { QEvent::Show, Qt::Window }, { QEvent::Hide, Qt::Widget } + }; + QTest::addRow("show/hide widget, then show window, hide widget") << ShowHideEntry::List{ + { QEvent::Show, Qt::Widget }, { QEvent::Hide, Qt::Widget }, + { QEvent::Show, Qt::Window }, { QEvent::Hide, Qt::Widget } + }; + QTest::addRow("show widget, close widget, show widget") << ShowHideEntry::List{ + { QEvent::Show, Qt::Widget }, { QEvent::Close, Qt::Widget }, { QEvent::Show, Qt::Widget } + }; + QTest::addRow("show widget, close widget, show window") << ShowHideEntry::List{ + { QEvent::Show, Qt::Widget }, { QEvent::Close, Qt::Widget }, { QEvent::Show, Qt::Window } + }; + QTest::addRow("show widget, close window, show widget") << ShowHideEntry::List{ + { QEvent::Show, Qt::Widget }, { QEvent::Close, Qt::Window }, { QEvent::Show, Qt::Widget } + }; + QTest::addRow("show widget, close window, show window") << ShowHideEntry::List{ + { QEvent::Show, Qt::Widget }, { QEvent::Close, Qt::Window }, { QEvent::Show, Qt::Window } + }; +} + +void tst_QWidget_window::showHideWindowHandle() +{ + QWidget parent; + parent.setObjectName("Parent"); + QCOMPARE(parent.isVisible(), false); + QCOMPARE(parent.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(parent.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(parent.testAttribute(Qt::WA_WState_Hidden), true); + + QWidget child; + child.setObjectName("Child"); + QCOMPARE(child.isVisible(), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), true); + + child.setParent(&parent); + QCOMPARE(child.isVisible(), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false); + + QFETCH(QList<ShowHideEntry>, entries); + for (const auto entry : entries) { + + if (entry.action == QEvent::Show) { + if (entry.target == Qt::Window && !parent.windowHandle()) { + parent.setAttribute(Qt::WA_NativeWindow); + QVERIFY(parent.windowHandle()); + + QCOMPARE(parent.isVisible(), false); + QCOMPARE(parent.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(parent.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(parent.testAttribute(Qt::WA_WState_Hidden), true); + } + + bool wasExplicitShowHide = parent.testAttribute(Qt::WA_WState_ExplicitShowHide); + + if (entry.target == Qt::Widget) + parent.show(); + else + parent.windowHandle()->show(); + + QVERIFY(QTest::qWaitForWindowActive(&parent)); + + QCOMPARE(parent.isVisible(), true); + QVERIFY(parent.windowHandle()); + QCOMPARE(parent.windowHandle()->isVisible(), true); + + QCOMPARE(parent.testAttribute(Qt::WA_WState_Visible), true); + QCOMPARE(parent.testAttribute(Qt::WA_WState_Hidden), false); + QCOMPARE(parent.testAttribute(Qt::WA_WState_ExplicitShowHide), + entry.target == Qt::Widget || wasExplicitShowHide); + + QCOMPARE(child.isVisible(), true); + QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Visible), true); + QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false); + + } else if (entry.action == QEvent::Hide) { + + bool wasExplicitShowHide = parent.testAttribute(Qt::WA_WState_ExplicitShowHide); + + if (entry.target == Qt::Widget) + parent.hide(); + else + parent.windowHandle()->hide(); + + QCOMPARE(parent.isVisible(), false); + QVERIFY(parent.windowHandle()); + QCOMPARE(parent.windowHandle()->isVisible(), false); + + QCOMPARE(parent.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(parent.testAttribute(Qt::WA_WState_Hidden), true); + QCOMPARE(parent.testAttribute(Qt::WA_WState_ExplicitShowHide), + entry.target == Qt::Widget || wasExplicitShowHide); + + QCOMPARE(child.isVisible(), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false); + + } else if (entry.action == QEvent::Close) { + + bool wasExplicitShowHide = parent.testAttribute(Qt::WA_WState_ExplicitShowHide); + + if (entry.target == Qt::Widget) + parent.close(); + else + parent.windowHandle()->close(); + + QCOMPARE(parent.isVisible(), false); + QVERIFY(parent.windowHandle()); + QCOMPARE(parent.windowHandle()->isVisible(), false); + + QCOMPARE(parent.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(parent.testAttribute(Qt::WA_WState_Hidden), true); + QCOMPARE(parent.testAttribute(Qt::WA_WState_ExplicitShowHide), + entry.target == Qt::Widget || wasExplicitShowHide); + + QCOMPARE(child.isVisible(), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_ExplicitShowHide), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Visible), false); + QCOMPARE(child.testAttribute(Qt::WA_WState_Hidden), false); + } + } +} + +void tst_QWidget_window::resetFocusObjectOnDestruction() +{ + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + + QSignalSpy focusObjectChangedSpy(qApp, &QGuiApplication::focusObjectChanged); + + // single top level widget that has focus + std::unique_ptr<QWidget> widget(new QWidget); + widget->setObjectName("Widget 1"); + widget->setFocus(); + widget->show(); + QVERIFY(QTest::qWaitForWindowActive(widget.get())); + + int activeCount = focusObjectChangedSpy.size(); + widget.reset(); + QVERIFY(focusObjectChangedSpy.size() > activeCount); + QCOMPARE(focusObjectChangedSpy.last().last().value<QObject*>(), nullptr); + focusObjectChangedSpy.clear(); + + // top level widget with focused child + widget.reset(new QWidget); + widget->setObjectName("Widget 2"); + QWidget *child = new QWidget(widget.get()); + child->setObjectName("Child widget"); + child->setFocus(); + widget->show(); + QVERIFY(QTest::qWaitForWindowActive(widget.get())); + + activeCount = focusObjectChangedSpy.size(); + widget.reset(); + // we might get more than one signal emission + QVERIFY(focusObjectChangedSpy.size() > activeCount); + QCOMPARE(focusObjectChangedSpy.last().last().value<QObject*>(), nullptr); +} + +class CreateDestroyWidget : public QWidget +{ +public: + using QWidget::create; + using QWidget::destroy; +}; + +void tst_QWidget_window::cleanupOnDestruction() +{ + CreateDestroyWidget widget; + QWidget child(&widget); + + QWidget grandChild(&child); + // Ensure there's not a 1:1 native window hierarhcy that we could + // recurse during QWidget::destroy(), triggering the issue that + // we were failing to clean up when not destroyed via QWidget. + grandChild.setAttribute(Qt::WA_DontCreateNativeAncestors); + grandChild.winId(); + + widget.destroy(); + widget.create(); + + widget.show(); +} + QTEST_MAIN(tst_QWidget_window) #include "tst_qwidget_window.moc" diff --git a/tests/auto/widgets/kernel/qwidgetaction/CMakeLists.txt b/tests/auto/widgets/kernel/qwidgetaction/CMakeLists.txt index 58dae2d076..fb5409464d 100644 --- a/tests/auto/widgets/kernel/qwidgetaction/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qwidgetaction/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qwidgetaction.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qwidgetaction Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qwidgetaction LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qwidgetaction SOURCES tst_qwidgetaction.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::TestPrivate Qt::Widgets diff --git a/tests/auto/widgets/kernel/qwidgetaction/tst_qwidgetaction.cpp b/tests/auto/widgets/kernel/qwidgetaction/tst_qwidgetaction.cpp index 384c07258b..a06e072b71 100644 --- a/tests/auto/widgets/kernel/qwidgetaction/tst_qwidgetaction.cpp +++ b/tests/auto/widgets/kernel/qwidgetaction/tst_qwidgetaction.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -74,6 +49,18 @@ void tst_QWidgetAction::cleanup() void tst_QWidgetAction::defaultWidget() { + // check that QWidgetAction deals with the widget being deleted before itself: + { + QToolBar tb1; + + QComboBox combo(&tb1); + + auto action = new QWidgetAction(&tb1); + action->setDefaultWidget(&combo); + + tb1.addAction(action); + } + // check that QWidgetAction takes ownership of the widget: { QToolBar tb1; @@ -235,7 +222,7 @@ void tst_QWidgetAction::customWidget() tb1.addAction(action); QList<QWidget *> combos = action->createdWidgets(); - QCOMPARE(combos.count(), 1); + QCOMPARE(combos.size(), 1); QPointer<QComboBox> combo1 = qobject_cast<QComboBox *>(combos.at(0)); QVERIFY(combo1); @@ -243,7 +230,7 @@ void tst_QWidgetAction::customWidget() tb2.addAction(action); combos = action->createdWidgets(); - QCOMPARE(combos.count(), 2); + QCOMPARE(combos.size(), 2); QCOMPARE(combos.at(0), combo1.data()); QPointer<QComboBox> combo2 = qobject_cast<QComboBox *>(combos.at(1)); diff --git a/tests/auto/widgets/kernel/qwidgetmetatype/CMakeLists.txt b/tests/auto/widgets/kernel/qwidgetmetatype/CMakeLists.txt index 3d8c3bbf4b..431a584a60 100644 --- a/tests/auto/widgets/kernel/qwidgetmetatype/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qwidgetmetatype/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qwidgetmetatype.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qwidgetmetatype Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qwidgetmetatype LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qwidgetmetatype SOURCES tst_qwidgetmetatype.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/kernel/qwidgetmetatype/tst_qwidgetmetatype.cpp b/tests/auto/widgets/kernel/qwidgetmetatype/tst_qwidgetmetatype.cpp index 425380a99e..885c26a128 100644 --- a/tests/auto/widgets/kernel/qwidgetmetatype/tst_qwidgetmetatype.cpp +++ b/tests/auto/widgets/kernel/qwidgetmetatype/tst_qwidgetmetatype.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@kdab.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/kernel/qwidgetrepaintmanager/CMakeLists.txt b/tests/auto/widgets/kernel/qwidgetrepaintmanager/CMakeLists.txt new file mode 100644 index 0000000000..ae91af064c --- /dev/null +++ b/tests/auto/widgets/kernel/qwidgetrepaintmanager/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qwidgetrepaintmanager LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qwidgetrepaintmanager + SOURCES + tst_qwidgetrepaintmanager.cpp + LIBRARIES + Qt::CorePrivate + Qt::Gui + Qt::GuiPrivate + Qt::TestPrivate + Qt::Widgets + Qt::WidgetsPrivate +) diff --git a/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp b/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp new file mode 100644 index 0000000000..9059a9262e --- /dev/null +++ b/tests/auto/widgets/kernel/qwidgetrepaintmanager/tst_qwidgetrepaintmanager.cpp @@ -0,0 +1,831 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + + +#include <QTest> +#include <QPainter> +#include <QScrollArea> +#include <QScrollBar> +#include <QApplication> + +#include <private/qhighdpiscaling_p.h> +#include <private/qwidget_p.h> +#include <private/qwidgetrepaintmanager_p.h> +#include <qpa/qplatformintegration.h> +#include <qpa/qplatformbackingstore.h> +#include <private/qguiapplication_p.h> + +//#define MANUAL_DEBUG + +class TestWidget : public QWidget +{ +public: + TestWidget(QWidget *parent = nullptr) + : QWidget(parent) + { + } + + QSize sizeHint() const override + { + const int screenWidth = QGuiApplication::primaryScreen()->geometry().width(); + const int width = qMax(200, 100 * ((screenWidth + 500) / 1000)); + return isWindow() ? QSize(width, width) : QSize(width - 40, width - 40); + } + + void initialShow() + { + show(); + if (isWindow()) { + QVERIFY(QTest::qWaitForWindowExposed(this)); + QVERIFY(waitForPainted()); + } + paintedRegions = {}; + } + + bool waitForPainted(int timeout = 5000) + { + int remaining = timeout; + QDeadlineTimer deadline(remaining, Qt::PreciseTimer); + if (!QTest::qWaitFor([this]{ return !paintedRegions.isEmpty(); }, timeout)) + return false; + + // In case of multiple paint events: + // Process events and wait until all have been consumed, + // i.e. paintedRegions no longer changes. + QRegion reg; + while (remaining > 0 && reg != paintedRegions) { + reg = paintedRegions; + QCoreApplication::processEvents(QEventLoop::AllEvents, remaining); + if (reg == paintedRegions) + return true; + + remaining = int(deadline.remainingTime()); + } + return false; + } + + QRegion takePaintedRegions() + { + QRegion result = paintedRegions; + paintedRegions = {}; + return result; + } + QRegion paintedRegions; + + bool event(QEvent *event) override + { + const auto type = event->type(); + if (type == QEvent::WindowActivate || type == QEvent::WindowDeactivate) + return true; + if (type == QEvent::UpdateRequest) + ++updateRequests; + return QWidget::event(event); + } + int updateRequests = 0; + +protected: + void paintEvent(QPaintEvent *event) override + { + paintedRegions += event->region(); + QPainter painter(this); + const QBrush patternBrush = isWindow() ? QBrush(Qt::blue, Qt::VerPattern) + : QBrush(Qt::red, Qt::HorPattern); + painter.fillRect(rect(), patternBrush); + } +}; + +class OpaqueWidget : public QWidget +{ +public: + OpaqueWidget(const QColor &col, QWidget *parent = nullptr) + : QWidget(parent), fillColor(col) + { + setAttribute(Qt::WA_OpaquePaintEvent); + } + + bool event(QEvent *event) override + { + const auto type = event->type(); + if (type == QEvent::WindowActivate || type == QEvent::WindowDeactivate) + return true; + return QWidget::event(event); + } + +protected: + void paintEvent(QPaintEvent *e) override + { + Q_UNUSED(e); + QPainter painter(this); + fillColor.setBlue(paintCount % 255); + painter.fillRect(e->rect(), fillColor); +#ifdef MANUAL_DEBUG + ++paintCount; + painter.drawText(rect(), Qt::AlignCenter, QString::number(paintCount)); +#endif + } + +private: + QColor fillColor; + int paintCount = 0; +}; + +class Draggable : public OpaqueWidget +{ +public: + Draggable(QWidget *parent = nullptr) + : OpaqueWidget(Qt::white, parent) + { + } + + Draggable(const QColor &col, QWidget *parent = nullptr) + : OpaqueWidget(col, parent) + { + left = new OpaqueWidget(Qt::gray, this); + top = new OpaqueWidget(Qt::gray, this); + right = new OpaqueWidget(Qt::gray, this); + bottom = new OpaqueWidget(Qt::gray, this); + } + + QSize sizeHint() const override { + return QSize(100, 100); + } + +protected: + void resizeEvent(QResizeEvent *) override + { + if (!left) + return; + left->setGeometry(0, 0, 10, height()); + top->setGeometry(10, 0, width() - 10, 10); + right->setGeometry(width() - 10, 10, 10, height() - 10); + bottom->setGeometry(10, height() - 10, width() - 10, 10); + } + + void mousePressEvent(QMouseEvent *e) override + { + lastPos = e->position().toPoint(); + } + void mouseMoveEvent(QMouseEvent *e) override + { + QPoint pos = geometry().topLeft(); + pos += e->position().toPoint() - lastPos; + move(pos); + } + void mouseReleaseEvent(QMouseEvent *) override + { + lastPos = {}; + } + +private: + OpaqueWidget *left = nullptr; + OpaqueWidget *top = nullptr; + OpaqueWidget *right = nullptr; + OpaqueWidget *bottom = nullptr; + QPoint lastPos; +}; + +class TestScene : public QWidget +{ +public: + TestScene() + { + setObjectName("scene"); + + // opaque because it has an opaque background color and autoFillBackground is set + area = new QWidget(this); + area->setObjectName("area"); + area->setAutoFillBackground(true); + QPalette palette; + palette.setColor(QPalette::Window, QColor::fromRgb(0, 0, 0)); + area->setPalette(palette); + + // all these children set WA_OpaquePaintEvent + redChild = new Draggable(Qt::red, area); + redChild->setObjectName("redChild"); + + greenChild = new Draggable(Qt::green, area); + greenChild->setObjectName("greenChild"); + + yellowChild = new Draggable(Qt::yellow, this); + yellowChild->setObjectName("yellowChild"); + + nakedChild = new Draggable(this); + nakedChild->move(300, 0); + nakedChild->setObjectName("nakedChild"); + + bar = new OpaqueWidget(Qt::darkGray, this); + bar->setObjectName("bar"); + } + + QWidget *area; + QWidget *redChild; + QWidget *greenChild; + QWidget *yellowChild; + QWidget *nakedChild; + QWidget *bar; + + QSize sizeHint() const override { return QSize(400, 400); } + + bool event(QEvent *event) override + { + const auto type = event->type(); + if (type == QEvent::WindowActivate || type == QEvent::WindowDeactivate) + return true; + return QWidget::event(event); + } + +protected: + void resizeEvent(QResizeEvent *) override + { + area->setGeometry(50, 50, width() - 100, height() - 100); + bar->setGeometry(width() / 2 - 25, height() / 2, 50, height() / 2); + } +}; + +class tst_QWidgetRepaintManager : public QObject +{ + Q_OBJECT + +public: + tst_QWidgetRepaintManager(); + +public slots: + void initTestCase(); + void cleanup(); + +private slots: + void basic(); + void children(); + void opaqueChildren(); + void staticContents(); + void scroll(); + void paintOnScreenUpdates(); + +#if defined(QT_BUILD_INTERNAL) + void scrollWithOverlap(); + void overlappedRegion(); + void fastMove(); + void moveAccross(); + void moveInOutOverlapped(); + +protected: + /* + This helper compares the widget as rendered into the backingstore with the widget + as rendered via QWidget::grab. The latter always produces a fully rendered image, + so differences indicate bugs in QWidgetRepaintManager's or QWidget's painting code. + */ + bool compareWidget(QWidget *w) + { + QBackingStore *backingStore = w->window()->backingStore(); + Q_ASSERT(backingStore && backingStore->handle()); + QPlatformBackingStore *platformBackingStore = backingStore->handle(); + + if (!waitForFlush(w)) { + qWarning() << "Widget" << w << "failed to flush"; + return false; + } + + QImage backingstoreContent = platformBackingStore->toImage(); + if (!w->isWindow()) { + const qreal dpr = w->devicePixelRatioF(); + const QPointF offset = w->mapTo(w->window(), QPointF(0, 0)) * dpr; + backingstoreContent = backingstoreContent.copy(offset.x(), offset.y(), w->width() * dpr, w->height() * dpr); + } + const QImage widgetRender = w->grab().toImage().convertToFormat(backingstoreContent.format()); + + const bool result = backingstoreContent == widgetRender; + +#ifdef MANUAL_DEBUG + if (!result) { + backingstoreContent.save(QString("/tmp/backingstore_%1_%2.png").arg(QTest::currentTestFunction(), QTest::currentDataTag())); + widgetRender.save(QString("/tmp/grab_%1_%2.png").arg(QTest::currentTestFunction(), QTest::currentDataTag())); + } +#endif + return result; + }; + + QRegion dirtyRegion(QWidget *widget) const + { + return QWidgetPrivate::get(widget)->dirty; + } + bool waitForFlush(QWidget *widget) const + { + if (!widget) + return true; + + auto *repaintManager = QWidgetPrivate::get(widget->window())->maybeRepaintManager(); + + if (!repaintManager) + return true; + + return QTest::qWaitFor([repaintManager]{ return !repaintManager->isDirty(); } ); + }; +#endif // QT_BUILD_INTERNAL + + +private: + const int m_fuzz; + bool m_implementsScroll = false; +}; + +tst_QWidgetRepaintManager::tst_QWidgetRepaintManager() : + m_fuzz(int(QHighDpiScaling::factor(QGuiApplication::primaryScreen()))) +{ +} + +void tst_QWidgetRepaintManager::initTestCase() +{ + QWidget widget; + widget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&widget)); + + m_implementsScroll = widget.backingStore()->handle()->scroll(QRegion(widget.rect()), 1, 1); + qInfo() << QGuiApplication::platformName() << "QPA backend implements scroll:" << m_implementsScroll; +} + +void tst_QWidgetRepaintManager::cleanup() +{ + QVERIFY(QApplication::topLevelWidgets().isEmpty()); +} + +void tst_QWidgetRepaintManager::basic() +{ + TestWidget widget; + widget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&widget)); + + QCOMPARE(widget.takePaintedRegions(), QRegion(0, 0, widget.width(), widget.height())); + + widget.update(); + QVERIFY(widget.waitForPainted()); + QCOMPARE(widget.takePaintedRegions(), QRegion(0, 0, widget.width(), widget.height())); + + widget.repaint(); + QCOMPARE(widget.takePaintedRegions(), QRegion(0, 0, widget.width(), widget.height())); +} + +/*! + Children cannot assumed to be fully opaque, so the parent will repaint when the + child repaints. +*/ +void tst_QWidgetRepaintManager::children() +{ + if (QStringList{"android"}.contains(QGuiApplication::platformName())) + QSKIP("This test fails on Android"); + + TestWidget widget; + widget.initialShow(); + + TestWidget *child1 = new TestWidget(&widget); + child1->move(20, 20); + child1->show(); + QVERIFY(QTest::qWaitForWindowExposed(child1)); + QVERIFY(child1->waitForPainted()); + QCOMPARE(widget.takePaintedRegions(), QRegion(child1->geometry())); + QCOMPARE(child1->takePaintedRegions(), QRegion(child1->rect())); + + child1->move(20, 30); + QVERIFY(widget.waitForPainted()); + // both the old and the new area covered by child1 need to be repainted + QCOMPARE(widget.takePaintedRegions(), QRegion(20, 20, child1->width(), child1->height() + 10)); + QCOMPARE(child1->takePaintedRegions(), QRegion(child1->rect())); + + TestWidget *child2 = new TestWidget(&widget); + child2->move(30, 30); + child2->raise(); + child2->show(); + + QVERIFY(child2->waitForPainted()); + QCOMPARE(widget.takePaintedRegions(), QRegion(child2->geometry())); + QCOMPARE(child1->takePaintedRegions(), QRegion(10, 0, child2->width() - 10, child2->height())); + QCOMPARE(child2->takePaintedRegions(), QRegion(child2->rect())); + + child1->hide(); + QVERIFY(widget.waitForPainted()); + QCOMPARE(widget.paintedRegions, QRegion(child1->geometry())); +} + +void tst_QWidgetRepaintManager::opaqueChildren() +{ + if (QStringList{"android"}.contains(QGuiApplication::platformName())) + QSKIP("This test fails on Android"); + + TestWidget widget; + widget.initialShow(); + + TestWidget *child1 = new TestWidget(&widget); + child1->move(20, 20); + child1->setAttribute(Qt::WA_OpaquePaintEvent); + child1->show(); + + QVERIFY(child1->waitForPainted()); + QCOMPARE(widget.takePaintedRegions(), QRegion()); + QCOMPARE(child1->takePaintedRegions(), child1->rect()); + + child1->move(20, 30); + QVERIFY(widget.waitForPainted()); + QCOMPARE(widget.takePaintedRegions(), QRegion(20, 20, child1->width(), 10)); + if (!m_implementsScroll) + QEXPECT_FAIL("", "child1 shouldn't get painted, we can just move the area of the backingstore", Continue); + QCOMPARE(child1->takePaintedRegions(), QRegion()); +} + +/*! + When resizing to be larger, a widget with Qt::WA_StaticContents set + should only repaint the newly revealed areas. +*/ +void tst_QWidgetRepaintManager::staticContents() +{ + const auto *integration = QGuiApplicationPrivate::platformIntegration(); + if (!integration->hasCapability(QPlatformIntegration::BackingStoreStaticContents)) + QSKIP("Platform does not support static backingstore content"); + + TestWidget widget; + widget.setAttribute(Qt::WA_StaticContents); + widget.initialShow(); + + // Trigger resize via QWindow (similar to QWSI code path) + QVERIFY(widget.windowHandle()); + QSize oldSize = widget.size(); + widget.windowHandle()->resize(widget.width(), widget.height() + 10); + QVERIFY(widget.waitForPainted()); + QCOMPARE(widget.takePaintedRegions(), QRegion(0, oldSize.width(), widget.width(), 10)); + + // Trigger resize via QWidget + oldSize = widget.size(); + widget.resize(widget.width() + 10, widget.height()); + QVERIFY(widget.waitForPainted()); + QEXPECT_FAIL("", "QWidgetPrivate::setGeometry_sys wrongly triggers full update", Continue); + QCOMPARE(widget.takePaintedRegions(), QRegion(oldSize.width(), 0, 10, widget.height())); +} + +/*! + Scrolling a widget. +*/ +void tst_QWidgetRepaintManager::scroll() +{ + if (QStringList{"android"}.contains(QGuiApplication::platformName())) + QSKIP("This test fails on Android"); + + TestWidget widget; + widget.initialShow(); + + widget.scroll(10, 0); + QVERIFY(widget.waitForPainted()); + if (!m_implementsScroll) + QEXPECT_FAIL("", "This should just repaint the newly exposed region", Continue); + QCOMPARE(widget.takePaintedRegions(), QRegion(0, 0, 10, widget.height())); + + TestWidget *child = new TestWidget(&widget); + child->move(20, 20); + child->initialShow(); + + // a potentially semi-transparent child scrolling needs a full repaint + child->scroll(10, 0); + QVERIFY(child->waitForPainted()); + QCOMPARE(child->takePaintedRegions(), child->rect()); + QCOMPARE(widget.takePaintedRegions(), child->geometry()); + + // a explicitly opaque child scrolling only needs the child to repaint newly exposed regions + child->setAttribute(Qt::WA_OpaquePaintEvent); + child->scroll(10, 0); + QVERIFY(child->waitForPainted()); + if (!m_implementsScroll) + QEXPECT_FAIL("", "This should just repaint the newly exposed region", Continue); + QCOMPARE(child->takePaintedRegions(), QRegion(0, 0, 10, child->height())); + QCOMPARE(widget.takePaintedRegions(), QRegion()); +} + +class PaintOnScreenWidget : public TestWidget +{ +public: + using TestWidget::TestWidget; + + // Explicit override to prevent noPaintOnScreen on Windows + QPaintEngine *paintEngine() const override + { + return nullptr; + } +}; + +void tst_QWidgetRepaintManager::paintOnScreenUpdates() +{ + { + TestWidget topLevel; + topLevel.setObjectName("TopLevel"); + topLevel.resize(500, 500); + TestWidget renderToTextureWidget(&topLevel); + renderToTextureWidget.setObjectName("RenderToTexture"); + renderToTextureWidget.setGeometry(0, 0, 200, 200); + QWidgetPrivate::get(&renderToTextureWidget)->setRenderToTexture(); + + PaintOnScreenWidget paintOnScreenWidget(&topLevel); + paintOnScreenWidget.setObjectName("PaintOnScreen"); + paintOnScreenWidget.setGeometry(200, 200, 300, 300); + + topLevel.initialShow(); + + // Updating before toggling WA_PaintOnScreen should work fine + paintOnScreenWidget.update(); + paintOnScreenWidget.waitForPainted(); + QVERIFY(paintOnScreenWidget.waitForPainted()); + +#ifdef Q_OS_ANDROID + QEXPECT_FAIL("", "This test fails on Android", Abort); +#endif + QCOMPARE(paintOnScreenWidget.takePaintedRegions(), paintOnScreenWidget.rect()); + + renderToTextureWidget.update(); + QVERIFY(renderToTextureWidget.waitForPainted()); + QCOMPARE(renderToTextureWidget.takePaintedRegions(), renderToTextureWidget.rect()); + + // Then toggle WA_PaintOnScreen + paintOnScreenWidget.setAttribute(Qt::WA_PaintOnScreen); + + // The render-to-texture widget updates fine + renderToTextureWidget.update(); + QVERIFY(renderToTextureWidget.waitForPainted()); + QCOMPARE(renderToTextureWidget.takePaintedRegions(), renderToTextureWidget.rect()); + + // Updating the paint-on-screen texture widget will not result + // in a paint event, but should result in an update request. + paintOnScreenWidget.updateRequests = 0; + paintOnScreenWidget.update(); + QVERIFY(QTest::qWaitFor([&]{ return paintOnScreenWidget.updateRequests > 0; })); + + // And should not prevent the render-to-texture widget from receiving updates + renderToTextureWidget.update(); + QVERIFY(renderToTextureWidget.waitForPainted()); + QCOMPARE(renderToTextureWidget.takePaintedRegions(), renderToTextureWidget.rect()); + } + + { + TestWidget paintOnScreenTopLevel; + paintOnScreenTopLevel.setObjectName("PaintOnScreenTopLevel"); + paintOnScreenTopLevel.setAttribute(Qt::WA_PaintOnScreen); + + paintOnScreenTopLevel.initialShow(); + + paintOnScreenTopLevel.updateRequests = 0; + paintOnScreenTopLevel.update(); + QVERIFY(QTest::qWaitFor([&]{ return paintOnScreenTopLevel.updateRequests > 0; })); + + // Turn off paint on screen and make it a render-to-texture widget. + // This will lead us into a QWidgetRepaintManager::markDirty() code + // path that checks updateRequestSent, which is still set from the + // update above since paint-on-screen handling doesn't reset it. + paintOnScreenTopLevel.setAttribute(Qt::WA_PaintOnScreen, false); + QWidgetPrivate::get(&paintOnScreenTopLevel)->setRenderToTexture(); + paintOnScreenTopLevel.update(); + QVERIFY(QTest::qWaitFor([&]{ return paintOnScreenTopLevel.updateRequests > 1; })); + } +} + +#if defined(QT_BUILD_INTERNAL) + +/*! + Verify that overlapping children are repainted correctly when + a widget is moved (via a scroll area) for such a distance that + none of the old area is still visible. QTBUG-26269 +*/ +void tst_QWidgetRepaintManager::scrollWithOverlap() +{ + if (QStringList{"android"}.contains(QGuiApplication::platformName())) + QSKIP("This test fails on Android"); + + class MainWindow : public QWidget + { + public: + MainWindow(QWidget *parent = 0) + : QWidget(parent, Qt::WindowStaysOnTopHint) + { + m_scrollArea = new QScrollArea(this); + m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + QWidget *w = new QWidget; + w->setPalette(QPalette(Qt::gray)); + w->setAutoFillBackground(true); + m_scrollArea->setWidget(w); + m_scrollArea->resize(500, 100); + w->resize(5000, 600); + + m_topWidget = new QWidget(this); + m_topWidget->setPalette(QPalette(Qt::red)); + m_topWidget->setAutoFillBackground(true); + m_topWidget->resize(300, 200); + + resize(600, 300); + } + + void resizeEvent(QResizeEvent *e) override + { + QWidget::resizeEvent(e); + // move scroll area and top widget to the center of the main window + scrollArea()->move((width() - scrollArea()->width()) / 2, (height() - scrollArea()->height()) / 2); + topWidget()->move((width() - topWidget()->width()) / 2, (height() - topWidget()->height()) / 2); + } + + + inline QScrollArea *scrollArea() const { return m_scrollArea; } + inline QWidget *topWidget() const { return m_topWidget; } + + private: + QScrollArea *m_scrollArea; + QWidget *m_topWidget; + }; + + MainWindow w; + w.show(); + + QVERIFY(QTest::qWaitForWindowActive(&w)); + + bool result = compareWidget(w.topWidget()); + // if this fails already, then the system we test on can't compare screenshots from grabbed widgets, + // and we have to skip this test. Possible reasons are differences in surface formats or DPI, or + // unrelated bugs in QPlatformBackingStore::toImage or QWidget::grab. + if (!result) + QSKIP("Cannot compare QWidget::grab with QScreen::grabWindow on this machine"); + + // scroll the horizontal slider to the right side + { + w.scrollArea()->horizontalScrollBar()->setValue(w.scrollArea()->horizontalScrollBar()->maximum()); + QVERIFY(compareWidget(w.topWidget())); + } + + // scroll the vertical slider down + { + w.scrollArea()->verticalScrollBar()->setValue(w.scrollArea()->verticalScrollBar()->maximum()); + QVERIFY(compareWidget(w.topWidget())); + } + + // hide the top widget + { + w.topWidget()->hide(); + QVERIFY(compareWidget(w.scrollArea()->viewport())); + } + + // scroll the horizontal slider to the left side + { + w.scrollArea()->horizontalScrollBar()->setValue(w.scrollArea()->horizontalScrollBar()->minimum()); + QVERIFY(compareWidget(w.scrollArea()->viewport())); + } + + // scroll the vertical slider up + { + w.scrollArea()->verticalScrollBar()->setValue(w.scrollArea()->verticalScrollBar()->minimum()); + QVERIFY(compareWidget(w.scrollArea()->viewport())); + } +} + +/*! + This tests QWidgetPrivate::overlappedRegion, which however is only used in the + QWidgetRepaintManager, so the test is here. +*/ +void tst_QWidgetRepaintManager::overlappedRegion() +{ + TestScene scene; + + if (scene.screen()->availableSize().width() < scene.sizeHint().width() + || scene.screen()->availableSize().height() < scene.sizeHint().height()) { + QSKIP("The screen on this system is too small for this test"); + } + + scene.show(); + QVERIFY(QTest::qWaitForWindowExposed(&scene)); + + auto overlappedRegion = [](QWidget *widget, bool breakAfterFirst = false){ + auto *priv = QWidgetPrivate::get(widget); + // overlappedRegion works on parent coordinates (crect, i.e. QWidget::geometry) + return priv->overlappedRegion(widget->geometry(), breakAfterFirst); + }; + + // the yellow child is not overlapped + QVERIFY(overlappedRegion(scene.yellowChild).isEmpty()); + // the green child is partially overlapped by the yellow child, which + // is at position -50, -50 relative to the green child (and 100x100 large) + QRegion overlap = overlappedRegion(scene.greenChild); + QVERIFY(!overlap.isEmpty()); + QCOMPARE(overlap, QRegion(QRect(-50, -50, 100, 100))); + // the red child is completely obscured by the green child, and partially + // obscured by the yellow child. How exactly this is divided into rects is + // irrelevant for the test. + overlap = overlappedRegion(scene.redChild); + QVERIFY(!overlap.isEmpty()); + QCOMPARE(overlap.boundingRect(), QRect(-50, -50, 150, 150)); + + // moving the red child out of obscurity + scene.redChild->move(100, 0); + overlap = overlappedRegion(scene.redChild); + QTRY_VERIFY(overlap.isEmpty()); + + // moving the red child down so it's partially behind the bar + scene.redChild->move(100, 100); + overlap = overlappedRegion(scene.redChild); + QTRY_VERIFY(!overlap.isEmpty()); + + // moving the yellow child so it is partially overlapped by the bar + scene.yellowChild->move(200, 200); + overlap = overlappedRegion(scene.yellowChild); + QTRY_VERIFY(!overlap.isEmpty()); +} + +void tst_QWidgetRepaintManager::fastMove() +{ + TestScene scene; + scene.show(); + QVERIFY(QTest::qWaitForWindowExposed(&scene)); + + QWidgetRepaintManager *repaintManager = QWidgetPrivate::get(&scene)->maybeRepaintManager(); + QVERIFY(repaintManager->dirtyRegion().isEmpty()); + + // moving yellow; nothing obscured + scene.yellowChild->move(QPoint(25, 0)); + QVERIFY(repaintManager->dirtyRegion().isEmpty()); // fast move + if (m_implementsScroll) { + QCOMPARE(repaintManager->dirtyWidgetList(), QList<QWidget *>() << &scene); + QVERIFY(dirtyRegion(scene.yellowChild).isEmpty()); + } else { + QCOMPARE(repaintManager->dirtyWidgetList(), QList<QWidget *>() << scene.yellowChild << &scene); + QCOMPARE(dirtyRegion(scene.yellowChild), QRect(0, 0, 100, 100)); + } + QCOMPARE(dirtyRegion(&scene), QRect(0, 0, 25, 100)); + QTRY_VERIFY(dirtyRegion(&scene).isEmpty()); + QVERIFY(compareWidget(&scene)); +} + +void tst_QWidgetRepaintManager::moveAccross() +{ + TestScene scene; + scene.show(); + QVERIFY(QTest::qWaitForWindowExposed(&scene)); + + QWidgetRepaintManager *repaintManager = QWidgetPrivate::get(&scene)->maybeRepaintManager(); + QVERIFY(repaintManager->dirtyRegion().isEmpty()); + + for (int i = 0; i < 4; ++i) { + scene.greenChild->move(scene.greenChild->pos() + QPoint(25, 0)); + waitForFlush(&scene); + } + QVERIFY(compareWidget(&scene)); + + for (int i = 0; i < 16; ++i) { + scene.redChild->move(scene.redChild->pos() + QPoint(25, 0)); + waitForFlush(&scene); + } + QVERIFY(compareWidget(&scene)); + + for (int i = 0; i < qMin(scene.area->width(), scene.area->height()); i += 25) { + scene.yellowChild->move(scene.yellowChild->pos() + QPoint(25, 25)); + waitForFlush(&scene); + } + QVERIFY(compareWidget(&scene)); +} + +void tst_QWidgetRepaintManager::moveInOutOverlapped() +{ + TestScene scene; + scene.show(); + QVERIFY(QTest::qWaitForWindowExposed(&scene)); + + QWidgetRepaintManager *repaintManager = QWidgetPrivate::get(&scene)->maybeRepaintManager(); + QVERIFY(repaintManager->dirtyRegion().isEmpty()); + + // yellow out + scene.yellowChild->move(QPoint(-100, 0)); + QVERIFY(!repaintManager->dirtyRegion().isEmpty()); // invalid dest rect + QVERIFY(repaintManager->dirtyWidgetList().isEmpty()); + QVERIFY(waitForFlush(&scene)); + QVERIFY(compareWidget(&scene)); + + // yellow in, obscured by bar + scene.yellowChild->move(QPoint(scene.width() / 2, scene.height() / 2)); + QVERIFY(!repaintManager->dirtyRegion().isEmpty()); // invalid source rect + QVERIFY(repaintManager->dirtyWidgetList().isEmpty()); + QVERIFY(waitForFlush(&scene)); + QVERIFY(compareWidget(&scene)); + + // green out + scene.greenChild->move(QPoint(-100, 0)); + QVERIFY(!repaintManager->dirtyRegion().isEmpty()); // invalid dest rect + QVERIFY(repaintManager->dirtyWidgetList().isEmpty()); + QVERIFY(waitForFlush(&scene)); + QVERIFY(compareWidget(&scene)); + + // green back in, obscured by bar + scene.greenChild->move(QPoint(scene.area->width() / 2 - 50, scene.area->height() / 2 - 50)); + QVERIFY(!repaintManager->dirtyRegion().isEmpty()); // invalid source rect + QVERIFY(repaintManager->dirtyWidgetList().isEmpty()); + QVERIFY(waitForFlush(&scene)); + QVERIFY(compareWidget(&scene)); + + // red back under green + scene.redChild->move(scene.greenChild->pos()); + QVERIFY(!repaintManager->dirtyRegion().isEmpty()); // destination rect obscured + QVERIFY(repaintManager->dirtyWidgetList().isEmpty()); + QVERIFY(waitForFlush(&scene)); + QVERIFY(compareWidget(&scene)); +} +#endif //# defined(QT_BUILD_INTERNAL) + +QTEST_MAIN(tst_QWidgetRepaintManager) +#include "tst_qwidgetrepaintmanager.moc" diff --git a/tests/auto/widgets/kernel/qwidgetsvariant/CMakeLists.txt b/tests/auto/widgets/kernel/qwidgetsvariant/CMakeLists.txt index f387be7306..bb3c1e2ad6 100644 --- a/tests/auto/widgets/kernel/qwidgetsvariant/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qwidgetsvariant/CMakeLists.txt @@ -1,15 +1,22 @@ -# Generated from qwidgetsvariant.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qwidgetsvariant Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qwidgetsvariant LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qwidgetsvariant SOURCES tst_qwidgetsvariant.cpp INCLUDE_DIRECTORIES ../../../other/qvariant_common - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/kernel/qwidgetsvariant/tst_qwidgetsvariant.cpp b/tests/auto/widgets/kernel/qwidgetsvariant/tst_qwidgetsvariant.cpp index d33f734af0..0a03fb9e1d 100644 --- a/tests/auto/widgets/kernel/qwidgetsvariant/tst_qwidgetsvariant.cpp +++ b/tests/auto/widgets/kernel/qwidgetsvariant/tst_qwidgetsvariant.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/kernel/qwindowcontainer/CMakeLists.txt b/tests/auto/widgets/kernel/qwindowcontainer/CMakeLists.txt index 63a3f9632d..787505972f 100644 --- a/tests/auto/widgets/kernel/qwindowcontainer/CMakeLists.txt +++ b/tests/auto/widgets/kernel/qwindowcontainer/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qwindowcontainer.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qwindowcontainer Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qwindowcontainer LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qwindowcontainer SOURCES tst_qwindowcontainer.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/kernel/qwindowcontainer/tst_qwindowcontainer.cpp b/tests/auto/widgets/kernel/qwindowcontainer/tst_qwindowcontainer.cpp index 9dead3d3ea..52aaf094b4 100644 --- a/tests/auto/widgets/kernel/qwindowcontainer/tst_qwindowcontainer.cpp +++ b/tests/auto/widgets/kernel/qwindowcontainer/tst_qwindowcontainer.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -32,12 +7,14 @@ #include <qapplication.h> #include <qwindow.h> #include <qwidget.h> +#include <qlineedit.h> #include <qdockwidget.h> #include <qmainwindow.h> #include <qscreen.h> #include <qscopedpointer.h> #include <qevent.h> +#include <qboxlayout.h> class Window : public QWindow @@ -71,16 +48,20 @@ public: private slots: void testShow(); void testPositionAndSize(); + void testSizeHints(); void testExposeObscure(); void testOwnership(); void testBehindTheScenesDeletion(); void testUnparenting(); + void testReparenting(); void testUnparentReparent(); void testActivation(); void testAncestorChange(); void testDockWidget(); void testNativeContainerParent(); void testPlatformSurfaceEvent(); + void embedWidgetWindow(); + void testFocus(); void cleanup(); private: @@ -132,7 +113,29 @@ void tst_QWindowContainer::testPositionAndSize() QCOMPARE(window->height(), container->height()); } +void tst_QWindowContainer::testSizeHints() +{ + QScopedPointer<QWidget> tlw(new QWidget); + QWindow *window = new QWindow(); + window->setMinimumSize(QSize(200, 200)); + window->setGeometry(m_availableGeometry.x() + 300, m_availableGeometry.y() + 400, 500, 600); + + QScopedPointer<QWidget> container(QWidget::createWindowContainer(window)); + container->setWindowTitle(QTest::currentTestFunction()); + + QVBoxLayout *vbox = new QVBoxLayout(tlw.data()); + vbox->addWidget(container.data()); + vbox->setContentsMargins(0, 0, 0, 0); + // Size hints should work regardless of visibility + QCOMPARE(container->minimumSizeHint(), window->minimumSize()); + QCOMPARE(vbox->minimumSize(), window->minimumSize()); + + // Respect dynamic updates + window->setMinimumSize(QSize(210, 210)); + QCOMPARE(container->minimumSizeHint(), window->minimumSize()); + QCOMPARE(vbox->minimumSize(), window->minimumSize()); +} void tst_QWindowContainer::testExposeObscure() { @@ -231,12 +234,12 @@ void tst_QWindowContainer::testActivation() void tst_QWindowContainer::testUnparenting() { - QWindow *window = new QWindow(); + QPointer<QWindow> window(new QWindow()); QScopedPointer<QWidget> container(QWidget::createWindowContainer(window)); container->setWindowTitle(QTest::currentTestFunction()); container->setGeometry(m_availableGeometry.x() + 100, m_availableGeometry.y() + 100, 200, 100); - window->setParent(0); + window->setParent(nullptr); container->show(); @@ -244,6 +247,26 @@ void tst_QWindowContainer::testUnparenting() // Window should not be made visible by container.. QVERIFY(!window->isVisible()); + + container.reset(); + QVERIFY(window); + delete window; +} + +void tst_QWindowContainer::testReparenting() +{ + QPointer<QWindow> window1(new QWindow()); + QScopedPointer<QWindow> window2(new QWindow()); + QScopedPointer<QWidget> container(QWidget::createWindowContainer(window1)); + + window1->setParent(window2.data()); + + // Not deleted with container + container.reset(); + QVERIFY(window1); + // but deleted with new parent + window2.reset(); + QVERIFY(!window1); } void tst_QWindowContainer::testUnparentReparent() @@ -414,6 +437,99 @@ void tst_QWindowContainer::testPlatformSurfaceEvent() QVERIFY(ok); } +void tst_QWindowContainer::embedWidgetWindow() +{ + { + QWidget parent; + QWidget *widget = new QWidget; + widget->show(); + QVERIFY(QTest::qWaitForWindowExposed(widget)); + QVERIFY(widget->windowHandle()); + QPointer<QWindow> widgetWindow = widget->windowHandle(); + auto *container = QWidget::createWindowContainer(widgetWindow, &parent); + QCOMPARE(container, widget); + QCOMPARE(widget->parent(), &parent); + delete widget; + QTRY_VERIFY(widgetWindow.isNull()); + } + + QPointer<QWidget> widget = new QWidget; + QPointer<QWindow> widgetWindow; + { + QWidget parent; + widget->show(); + QVERIFY(QTest::qWaitForWindowExposed(widget)); + QVERIFY(widget->windowHandle()); + widgetWindow = widget->windowHandle(); + auto *container = QWidget::createWindowContainer(widgetWindow, &parent); + QCOMPARE(container, widget); + QCOMPARE(widget->parent(), &parent); + } + QTRY_VERIFY(widget.isNull()); + QTRY_VERIFY(widgetWindow.isNull()); + +} + +void tst_QWindowContainer::testFocus() +{ + QWidget root; + root.setGeometry(m_availableGeometry); + + QLineEdit *lineEdit = new QLineEdit(&root); + lineEdit->setGeometry(0, 0, m_availableGeometry.width() * 0.1, 17); + lineEdit->setFocusPolicy(Qt::FocusPolicy::StrongFocus); + + QWindow *embedded = new QWindow(); + QWidget *container = QWidget::createWindowContainer(embedded, &root); + container->setGeometry(0, lineEdit->height() + 10, m_availableGeometry.width() * 0.2, m_availableGeometry.height() - (lineEdit->height() + 10)); + container->setFocusPolicy(Qt::StrongFocus); + + root.show(); + QVERIFY(QTest::qWaitForWindowExposed(&root)); + lineEdit->setFocus(); + QTRY_VERIFY(lineEdit->hasFocus()); + QCOMPARE(QGuiApplication::focusWindow(), root.windowHandle()); + QCOMPARE(QApplication::focusWidget(), lineEdit); + + // embedded window gets focused because of mouse click + QPoint embeddedCenter = container->rect().center(); + QTest::mousePress(root.windowHandle(), Qt::LeftButton, {}, embeddedCenter); + QVERIFY(QTest::qWaitForWindowFocused(embedded)); + QVERIFY(container->hasFocus()); + QCOMPARE(QGuiApplication::focusWindow(), embedded); + QCOMPARE(QApplication::focusWidget(), container); + QVERIFY(!lineEdit->hasFocus()); + + QTest::mouseClick(lineEdit, Qt::LeftButton, {}); + QVERIFY(QTest::qWaitForWindowFocused(root.windowHandle())); + QCOMPARE(QGuiApplication::focusWindow(), root.windowHandle()); + QCOMPARE(QApplication::focusWidget(), lineEdit); + QVERIFY(lineEdit->hasFocus()); + + // embedded window gets focused because of Tab key event + QTest::keyClick(root.windowHandle(), Qt::Key_Tab); + QVERIFY(QTest::qWaitForWindowFocused(embedded)); + QVERIFY(container->hasFocus()); + QCOMPARE(QGuiApplication::focusWindow(), embedded); + QCOMPARE(QApplication::focusWidget(), container); + QVERIFY(!lineEdit->hasFocus()); + // A key tab event sent to the root window should cause + // the nextInFocusChain of the window container to get focused + QTest::keyClick(root.windowHandle(), Qt::Key_Tab); + QVERIFY(QTest::qWaitForWindowFocused(root.windowHandle())); + QCOMPARE(QGuiApplication::focusWindow(), root.windowHandle()); + QCOMPARE(QApplication::focusWidget(), lineEdit); + QVERIFY(lineEdit->hasFocus()); + + // embedded window gets focused programmatically + embedded->requestActivate(); + QVERIFY(QTest::qWaitForWindowFocused(embedded)); + QVERIFY(container->hasFocus()); + QCOMPARE(QGuiApplication::focusWindow(), embedded); + QCOMPARE(QApplication::focusWidget(), container); + QVERIFY(!lineEdit->hasFocus()); +} + QTEST_MAIN(tst_QWindowContainer) #include "tst_qwindowcontainer.moc" diff --git a/tests/auto/widgets/styles/CMakeLists.txt b/tests/auto/widgets/styles/CMakeLists.txt index b1c9d1fce3..a957dfd373 100644 --- a/tests/auto/widgets/styles/CMakeLists.txt +++ b/tests/auto/widgets/styles/CMakeLists.txt @@ -1,9 +1,12 @@ -# Generated from styles.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause if(APPLE) add_subdirectory(qmacstyle) endif() -add_subdirectory(qstyle) +if(QT_FEATURE_private_tests) + add_subdirectory(qstyle) +endif() add_subdirectory(qstyleoption) if(QT_FEATURE_private_tests AND NOT ANDROID AND NOT QNX AND NOT UIKIT) add_subdirectory(qstylesheetstyle) diff --git a/tests/auto/widgets/styles/qmacstyle/CMakeLists.txt b/tests/auto/widgets/styles/qmacstyle/CMakeLists.txt index fcff4fcc21..861a327418 100644 --- a/tests/auto/widgets/styles/qmacstyle/CMakeLists.txt +++ b/tests/auto/widgets/styles/qmacstyle/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qmacstyle.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qmacstyle Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmacstyle LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qmacstyle SOURCES tst_qmacstyle.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/styles/qmacstyle/tst_qmacstyle.cpp b/tests/auto/widgets/styles/qmacstyle/tst_qmacstyle.cpp index e9802cd716..9cfd50bc08 100644 --- a/tests/auto/widgets/styles/qmacstyle/tst_qmacstyle.cpp +++ b/tests/auto/widgets/styles/qmacstyle/tst_qmacstyle.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -83,6 +58,10 @@ private: #define SIZE(x, y, z) \ ((size == QStyleHelper::SizeLarge) ? (x) : (size == QStyleHelper::SizeSmall) ? (y) : (z)) +static bool bigSurOrAbove() { + return QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSBigSur; +} + void tst_QMacStyle::sizeHints_data() { QTest::addColumn<QStyleHelper::WidgetSizePolicy>("size"); @@ -133,7 +112,7 @@ void tst_QMacStyle::sizeHints() QCOMPARE(sh(&comboBox3).height(), SIZE(32, -1, -1)); QSlider slider1(Qt::Horizontal, &w); - QCOMPARE(sh(&slider1).height(), SIZE(15, 12, 10)); + QCOMPARE(sh(&slider1).height(), SIZE(bigSurOrAbove() ? 18 : 15, 12, 10)); slider1.setTickPosition(QSlider::TicksAbove); QCOMPARE(sh(&slider1).height(), SIZE(24, 17, 16)); // Builder @@ -142,7 +121,7 @@ void tst_QMacStyle::sizeHints() QCOMPARE(sh(&slider1).height(), SIZE(24, 17, 16)); // Builder slider1.setTickPosition(QSlider::TicksBothSides); - QVERIFY(sh(&slider1).height() > SIZE(15, 12, 10)); // common sense + QVERIFY(sh(&slider1).height() > SIZE(bigSurOrAbove() ? 18 : 15, 12, 10)); // common sense QPushButton ok1("OK", &w); QPushButton cancel1("Cancel", &w); diff --git a/tests/auto/widgets/styles/qstyle/CMakeLists.txt b/tests/auto/widgets/styles/qstyle/CMakeLists.txt index c3b7b44e28..2c617ebe49 100644 --- a/tests/auto/widgets/styles/qstyle/CMakeLists.txt +++ b/tests/auto/widgets/styles/qstyle/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qstyle.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qstyle Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qstyle LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qstyle SOURCES tst_qstyle.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::TestPrivate Qt::Widgets diff --git a/tests/auto/widgets/styles/qstyle/tst_qstyle.cpp b/tests/auto/widgets/styles/qstyle/tst_qstyle.cpp index 0578d5ce70..95e24da21d 100644 --- a/tests/auto/widgets/styles/qstyle/tst_qstyle.cpp +++ b/tests/auto/widgets/styles/qstyle/tst_qstyle.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -92,6 +67,11 @@ private slots: void testProxyCalled(); void testStyleOptionInit(); + + void sliderPositionFromValue_data(); + void sliderPositionFromValue(); + void sliderValueFromPosition_data(); + void sliderValueFromPosition(); private: bool testAllFunctions(QStyle *); bool testScrollBarSubControls(const QStyle *style); @@ -653,7 +633,7 @@ void tst_QStyle::testStyleOptionInit() QStringList keys = QStyleFactory::keys(); keys.prepend(QString()); // QCommonStyle marker - for (const QString &key : qAsConst(keys)) { + for (const QString &key : std::as_const(keys)) { QStyle* style = key.isEmpty() ? new QCommonStyle : QStyleFactory::create(key); TestStyleOptionInitProxy testStyle; testStyle.setBaseStyle(style); @@ -662,5 +642,108 @@ void tst_QStyle::testStyleOptionInit() } } +void tst_QStyle::sliderPositionFromValue_data() +{ + QTest::addColumn<int>("min"); + QTest::addColumn<int>("max"); + QTest::addColumn<int>("value"); + QTest::addColumn<int>("span"); + QTest::addColumn<bool>("upsideDown"); + QTest::addColumn<int>("position"); + + QTest::addRow("no span") << 12 << 56 << 34 << 0 << false << 0; + QTest::addRow("no span inverse") << 12 << 56 << 34 << 0 << true << 0; + + QTest::addRow("value too small") << 34 << 56 << 12 << 2000 << false << 0; + QTest::addRow("value too small inverse") << 34 << 56 << 12 << 2000 << true << 2000; + + QTest::addRow("no-range") << 12 << 12 << 12 << 2000 << false << 0; + QTest::addRow("no-range-inverse") << 12 << 12 << 12 << 2000 << true << 0; + + QTest::addRow("close-to-max") << 12 << 34 << 33 << 2000 << false << 1909; + QTest::addRow("at-max") << 12 << 34 << 34 << 2000 << false << 2000; + QTest::addRow("value too large") << 12 << 34 << 35 << 2000 << false << 2000; + QTest::addRow("close-to-max-inverse") << 12 << 34 << 33 << 2000 << true << 91; + QTest::addRow("at-max-inverse") << 12 << 34 << 34 << 2000 << true << 0; + QTest::addRow("value too large-inverse") << 12 << 34 << 35 << 2000 << true << 0; + + QTest::addRow("big-range") << 100000 << 700000 << 250000 << 2000 << false << 500; + QTest::addRow("big-range-inverse") << 100000 << 700000 << 250000 << 2000 << true << 1500; + + QTest::addRow("across-zero") << -1000 << 1000 << -500 << 100 << false << 25; + QTest::addRow("across-zero-inverse") << -1000 << 1000 << -500 << 100 << true << 75; + + QTest::addRow("span>range") << 0 << 100 << 60 << 2000 << false << 1200; + QTest::addRow("span>range-inverse") << 0 << 100 << 60 << 2000 << true << 800; + + QTest::addRow("overflow1 (QTBUG-101581)") << -1 << INT_MAX << 235 << 891 << false << 0; + QTest::addRow("overflow2") << INT_MIN << INT_MAX << 10 << 100 << false << 50; + QTest::addRow("overflow2-inverse") << INT_MIN << INT_MAX << 10 << 100 << true << 49; + QTest::addRow("overflow3") << INT_MIN << INT_MAX << -10 << 100 << false << 49; + QTest::addRow("overflow3-inverse") << INT_MIN << INT_MAX << -10 << 100 << true << 50; +} + +void tst_QStyle::sliderPositionFromValue() +{ + QFETCH(int, min); + QFETCH(int, max); + QFETCH(int, value); + QFETCH(int, span); + QFETCH(bool, upsideDown); + QFETCH(int, position); + + QCOMPARE(QStyle::sliderPositionFromValue(min, max, value, span, upsideDown), position); +} + +void tst_QStyle::sliderValueFromPosition_data() +{ + QTest::addColumn<int>("min"); + QTest::addColumn<int>("max"); + QTest::addColumn<int>("position"); + QTest::addColumn<int>("span"); + QTest::addColumn<bool>("upsideDown"); + QTest::addColumn<int>("value"); + + QTest::addRow("position zero") << 0 << 100 << 0 << 2000 << false << 0; + QTest::addRow("position zero inverse") << 0 << 100 << 0 << 2000 << true << 100; + + QTest::addRow("span zero") << 0 << 100 << 1200 << 0 << false << 0; + QTest::addRow("span zero inverse") << 0 << 100 << 1200 << 0 << true << 100; + + QTest::addRow("position > span") << -300 << -200 << 2 << 1 << false << -200; + QTest::addRow("position > span inverse") << -300 << -200 << 2 << 1 << true << -300; + + QTest::addRow("large") << 0 << 100 << 1200 << 2000 << false << 60; + QTest::addRow("large-inverse") << 0 << 100 << 1200 << 2000 << true << 40; + + QTest::addRow("normal") << 0 << 100 << 12 << 20 << false << 60; + QTest::addRow("inverse") << 0 << 100 << 12 << 20 << true << 40; + + QTest::addRow("overflow1") << -1 << INT_MAX << 10 << 10 << false << INT_MAX; + QTest::addRow("overflow1-inverse") << -1 << INT_MAX << 10 << 10 << true << -1; + QTest::addRow("overflow2") << INT_MIN << INT_MAX << 5 << 10 << false << 0; + QTest::addRow("overflow2-inverse") << INT_MIN << INT_MAX << 5 << 10 << true << -1; + QTest::addRow("overflow3") << INT_MIN << 0 << 0 << 10 << false << INT_MIN; + QTest::addRow("overflow3-inverse") << INT_MIN << 0 << 0 << 10 << true << 0; + + QTest::addRow("overflow4") << 0 << INT_MAX << INT_MAX/2-6 << INT_MAX/2-5 << false << INT_MAX-2; + QTest::addRow("overflow4-inverse") << 0 << INT_MAX << INT_MAX/2-6 << INT_MAX/2-5 << true << 2; + + QTest::addRow("overflow5") << 0 << 4 << INT_MAX/4 << INT_MAX << false << 1; + QTest::addRow("overflow5-inverse") << 0 << 4 << INT_MAX/4 << INT_MAX << true << 3; +} + +void tst_QStyle::sliderValueFromPosition() +{ + QFETCH(int, min); + QFETCH(int, max); + QFETCH(int, position); + QFETCH(int, span); + QFETCH(bool, upsideDown); + QFETCH(int, value); + + QCOMPARE(QStyle::sliderValueFromPosition(min, max, position, span, upsideDown), value); +} + QTEST_MAIN(tst_QStyle) #include "tst_qstyle.moc" diff --git a/tests/auto/widgets/styles/qstyleoption/CMakeLists.txt b/tests/auto/widgets/styles/qstyleoption/CMakeLists.txt index 05853dfb62..e0a4cc38e2 100644 --- a/tests/auto/widgets/styles/qstyleoption/CMakeLists.txt +++ b/tests/auto/widgets/styles/qstyleoption/CMakeLists.txt @@ -1,16 +1,20 @@ -# Generated from qstyleoption.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qstyleoption Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qstyleoption LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qstyleoption SOURCES tst_qstyleoption.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) - -#### Keys ignored in scope 1:.:.:qstyleoption.pro:<TRUE>: -# TEMPLATE = "app" diff --git a/tests/auto/widgets/styles/qstyleoption/tst_qstyleoption.cpp b/tests/auto/widgets/styles/qstyleoption/tst_qstyleoption.cpp index a08b251c35..f173c9a8df 100644 --- a/tests/auto/widgets/styles/qstyleoption/tst_qstyleoption.cpp +++ b/tests/auto/widgets/styles/qstyleoption/tst_qstyleoption.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/styles/qstylesheetstyle/CMakeLists.txt b/tests/auto/widgets/styles/qstylesheetstyle/CMakeLists.txt index bf4e52862c..6543ec9961 100644 --- a/tests/auto/widgets/styles/qstylesheetstyle/CMakeLists.txt +++ b/tests/auto/widgets/styles/qstylesheetstyle/CMakeLists.txt @@ -1,37 +1,33 @@ -# Generated from qstylesheetstyle.pro. - -if(NOT QT_FEATURE_private_tests) - return() -endif() +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qstylesheetstyle Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qstylesheetstyle LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +# Resources: +set(resources_resource_files + "images/testimage.png" + "images/testimage@2x.png" +) + qt_internal_add_test(tst_qstylesheetstyle SOURCES tst_qstylesheetstyle.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::GuiPrivate Qt::TestPrivate Qt::Widgets Qt::WidgetsPrivate + TESTDATA ${resources_resource_files} + BUILTIN_TESTDATA ) -# Resources: -set(resources_resource_files - "images/testimage.png" - "images/testimage@2x.png" -) - -qt_internal_add_resource(tst_qstylesheetstyle "resources" - PREFIX - "/" - FILES - ${resources_resource_files} -) - - -#### Keys ignored in scope 1:.:.:qstylesheetstyle.pro:<TRUE>: # _REQUIREMENTS = "qtConfig(private_tests)" diff --git a/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp b/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp index f2f9be6bb6..a9a1817b8a 100644 --- a/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp +++ b/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtWidgets/QApplication> #include <QtWidgets/QCheckBox> @@ -63,6 +38,8 @@ #include <private/qstylesheetstyle_p.h> #include <private/qhighdpiscaling_p.h> #include <QtTest/private/qtesthelpers_p.h> +#include <qpa/qplatformtheme.h> +#include <QtWidgets/private/qapplication_p.h> using namespace QTestPrivate; @@ -77,6 +54,7 @@ private slots: void cleanup(); void repolish(); void repolish_without_crashing(); + void repolish_children(); void numinstances(); void widgetsBeforeAppStyleSheet(); void widgetsAfterAppStyleSheet(); @@ -92,6 +70,7 @@ private slots: void layoutSpacing(); #endif void qproperty(); + void qproperty_styleSheet(); void palettePropagation_data(); void palettePropagation(); void fontPropagation_data(); @@ -106,6 +85,8 @@ private slots: #endif void background(); void tabAlignment(); + void tabFont_data(); + void tabFont(); void attributesList(); void minmaxSizes(); void task206238_twice(); @@ -113,6 +94,7 @@ private slots: void proxyStyle(); void dialogButtonBox(); void emptyStyleSheet(); + void toolTip_data(); void toolTip(); void embeddedFonts(); void opaquePaintEvent_data(); @@ -125,6 +107,9 @@ private slots: void QTBUG36933_brokenPseudoClassLookup(); void styleSheetChangeBeforePolish(); void placeholderColor(); + void accent(); + void enumPropertySelector_data(); + void enumPropertySelector(); //at the end because it mess with the style. void widgetStyle(); void appStyle(); @@ -135,6 +120,11 @@ private slots: void highdpiImages_data(); void highdpiImages(); + void iconSizes_data(); + void iconSizes(); + void inheritWidgetPalette_data(); + void inheritWidgetPalette(); + private: static QColor COLOR(const QWidget &w) { @@ -435,6 +425,19 @@ void tst_QStyleSheetStyle::repolish_without_crashing() QCOMPARE(COLOR(*label), QColor(Qt::red)); } +void tst_QStyleSheetStyle::repolish_children() +{ + QWidget parent; + parent.setStyleSheet("QPushButton { color: red; background: white }"); + QPushButton p2(&parent); + // a layout would call show, triggering a polish of the child while + // the parent on which the style sheet is set remains unpolished + p2.show(); + QCOMPARE(BACKGROUND(p2), Qt::white); + parent.setStyleSheet("QPushButton { color: red; background: red }"); + QCOMPARE(BACKGROUND(p2), Qt::red); +} + void tst_QStyleSheetStyle::widgetStyle() { qApp->setStyleSheet(QString()); @@ -670,6 +673,23 @@ void tst_QStyleSheetStyle::qproperty() QCOMPARE(pb.isChecked(), false); } +void tst_QStyleSheetStyle::qproperty_styleSheet() +{ + QWidget w; + auto checkBox = new QCheckBox("check", &w); + QString sheet = R"(QCheckBox { qproperty-styleSheet: "QCheckBox { qproperty-text: foobar; }"; })"; + + QVERIFY(w.property("styleSheet").toString().isEmpty()); + + w.setStyleSheet(sheet); + QCOMPARE(checkBox->text(), "check"); + + //recursion crash + w.ensurePolished(); + QCOMPARE(w.property("styleSheet").toString(), sheet); + QCOMPARE(checkBox->text(), "foobar"); +} + namespace ns { class PushButton1 : public QPushButton { Q_OBJECT @@ -945,7 +965,7 @@ void tst_QStyleSheetStyle::focusColors() // ten pixels of the right color requires quite a many characters, as the // majority of the pixels will have slightly different colors due to the // anti-aliasing effect. -#if !defined(Q_OS_WIN32) && !(defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(Q_CC_INTEL)) +#if !defined(Q_OS_WIN32) && !(defined(Q_OS_LINUX) && defined(Q_CC_GNU)) QSKIP("This is a fragile test which fails on many esoteric platforms because of focus problems" " (for example, QTBUG-33959)." "That doesn't mean that the feature doesn't work in practice."); @@ -960,7 +980,6 @@ void tst_QStyleSheetStyle::focusColors() centerOnScreen(&frame); frame.show(); - QApplication::setActiveWindow(&frame); QVERIFY(QTest::qWaitForWindowActive(&frame)); for (QWidget *widget : frame.widgets()) { @@ -1006,7 +1025,6 @@ void tst_QStyleSheetStyle::hoverColors() QCursor::setPos(frame.geometry().topLeft() - QPoint(100, 0)); frame.show(); - QApplication::setActiveWindow(&frame); QVERIFY(QTest::qWaitForWindowActive(&frame)); QWindow *frameWindow = frame.windowHandle(); @@ -1179,6 +1197,100 @@ void tst_QStyleSheetStyle::tabAlignment() QVERIFY(bar->geometry().top() > 100); } +void tst_QStyleSheetStyle::tabFont_data() +{ + QTest::addColumn<QFont>("tabFont"); + QTest::addColumn<QTabWidget::TabPosition>("tabPosition"); + QTest::addColumn<bool>("closable"); + + QFont medium; + medium.setPixelSize(24); + QFont large; + large.setPixelSize(36); + QFont bold; + bold.setBold(true); + + QTest::newRow("medium, horizontal") << medium << QTabWidget::North << false; + QTest::newRow("large, vertical") << large << QTabWidget::West << false; + QTest::newRow("bold, horizontal, closable") << bold << QTabWidget::North << true; + QTest::newRow("bold, vertical, closable") << bold << QTabWidget::West << true; +} + +void tst_QStyleSheetStyle::tabFont() +{ + QFETCH(QFont, tabFont); + QFETCH(QTabWidget::TabPosition, tabPosition); + QFETCH(bool, closable); + const bool vertical = tabPosition == QTabWidget::West || tabPosition == QTabWidget::East; + const QString tab0Text("Tab title"); + const QString tab1Text("Very Long Tab title"); + + // macOS style centers tabs and messes up the test + QWindowsStyle windowsStyle; + QWidget topLevel; + topLevel.setStyle(&windowsStyle); + topLevel.setWindowTitle(QTest::currentTestFunction()); + QTabWidget tabWidget; + tabWidget.setStyle(&windowsStyle); + tabWidget.setTabPosition(tabPosition); + tabWidget.addTab(new QWidget, tab0Text); + tabWidget.addTab(new QWidget, tab1Text); + QTabWidget styledWidget; + styledWidget.setStyle(&windowsStyle); + styledWidget.setTabPosition(tabPosition); + styledWidget.addTab(new QWidget, tab0Text); + styledWidget.addTab(new QWidget, tab1Text); + + QTabBar *bar = tabWidget.tabBar(); + QTabBar *styledBar = styledWidget.tabBar(); + QVERIFY(bar && styledBar); + bar->setStyle(&windowsStyle); + bar->setTabsClosable(closable); + styledBar->setStyle(&windowsStyle); + styledBar->setTabsClosable(closable); + + QBoxLayout box(vertical ? QBoxLayout::LeftToRight : QBoxLayout::TopToBottom); + box.addWidget(&tabWidget); + box.addWidget(&styledWidget); + topLevel.setLayout(&box); + + topLevel.resize(600, 600); + centerOnScreen(&topLevel); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + const QFont defaultFont = tabWidget.font(); + if (QFontMetrics(defaultFont).size(Qt::TextShowMnemonic, tab0Text).width() >= + QFontMetrics(tabFont).size(Qt::TextShowMnemonic, tab0Text).width()) { + QSKIP("The used font is not larger when bold"); + } + + const QRect defaultRect = bar->tabRect(0); + QCOMPARE(styledBar->tabRect(0), defaultRect); + + tabWidget.setFont(tabFont); + const QRect rectWithFont = bar->tabRect(0); + if (vertical) + QVERIFY(rectWithFont.height() > defaultRect.height()); + else + QVERIFY(rectWithFont.width() > defaultRect.width()); + + QString styleSheet = "QTabBar::tab:first {"; + if (tabFont.pixelSize() != -1) + styleSheet += QString(" font-size: %1px;").arg(tabFont.pixelSize()); + if (tabFont.bold()) + styleSheet += " font-weight: bold;"; + styleSheet += "}"; + + styledWidget.setStyleSheet(styleSheet); + const QRect rectWithStyle = styledBar->tabRect(0); + + if (vertical) + QCOMPARE(rectWithStyle.height(), rectWithFont.height()); + else + QCOMPARE(rectWithStyle.width(), rectWithFont.width()); +} + void tst_QStyleSheetStyle::attributesList() { const QColor blue(Qt::blue); @@ -1545,19 +1657,32 @@ private: const QString m_oldStyleName; }; +void tst_QStyleSheetStyle::toolTip_data() +{ + QTest::addColumn<QString>("style"); + + QTest::newRow("fusion") << QString("Fusion"); +#ifdef Q_OS_WINDOWS + QTest::newRow("windowsvista") << QString("WindowsVista"); +#endif +} + void tst_QStyleSheetStyle::toolTip() { + QFETCH(QString, style); + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) QSKIP("Wayland: This fails. Figure out why."); - qApp->setStyleSheet(QString()); QWidget w; w.resize(m_testSize); w.setWindowTitle(QTest::currentTestFunction()); + // Use "Fusion" to prevent the Vista style from clobbering the tooltip palette in polish(). - QStyle *fusionStyle = QStyleFactory::create(QLatin1String("Fusion")); - QVERIFY(fusionStyle); - ApplicationStyleSetter as(fusionStyle); + QStyle *appStyle = QStyleFactory::create(style); + QVERIFY(appStyle); + ApplicationStyleSetter as(appStyle); + QHBoxLayout layout(&w); w.setLayout(&layout); @@ -1585,37 +1710,65 @@ void tst_QStyleSheetStyle::toolTip() wid4->setToolTip("this is wid4"); wid4->setObjectName("wid4"); + QWidget *wid5 = new QPushButton("wid5", &w); + layout.addWidget(wid5); + wid5->setStyleSheet("QToolTip { background: #ff0; color: #f00 }"); + wid5->setToolTip("this is wid5"); + wid5->setObjectName("wid5"); + centerOnScreen(&w); w.show(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); - const QColor normalToolTip = QToolTip::palette().color(QPalette::Inactive, QPalette::ToolTipBase); + QColor normalToolTipBgColor = QToolTip::palette().color(QPalette::Inactive, QPalette::ToolTipBase); + +#ifdef Q_OS_MACOS + // macOS uses tool tip text color set in label palette + const QPalette *labelPalette = QGuiApplicationPrivate::platformTheme()->palette(QPlatformTheme::LabelPalette); + QColor normalToolTipFgColor = labelPalette->color(QPalette::Inactive, QPalette::ToolTipText); +#else + QColor normalToolTipFgColor = QToolTip::palette().color(QPalette::Inactive, QPalette::ToolTipText); +#endif + // Tooltip on the widget without stylesheet, then to other widget, // including one without stylesheet (the tooltip will be reused, // but its color must change) - const QWidgetList widgets{wid4, wid1, wid2, wid3, wid4}; - const QList<QColor> colors { normalToolTip, QColor("#ae2"), QColor("#f81"), QColor("#0b8"), - normalToolTip }; + const QWidgetList widgets{wid4, wid1, wid2, wid3, wid4, wid5}; + const QList<QColor> bgcolors { normalToolTipBgColor, QColor("#ae2"), QColor("#f81"), + QColor("#0b8"), normalToolTipBgColor, QColor("#ff0")}; + const QList<QColor> fgcolors { normalToolTipFgColor, normalToolTipFgColor, normalToolTipFgColor, + normalToolTipFgColor, normalToolTipFgColor, QColor("#f00")}; QWidgetList topLevels; - for (int i = 0; i < widgets.count() ; ++i) { + for (int i = 0; i < widgets.size() ; ++i) { QWidget *wid = widgets.at(i); - QColor col = colors.at(i); + QColor bgColor = bgcolors.at(i); + QColor fgColor = fgcolors.at(i); QToolTip::showText( QPoint(0,0) , "This is " + wid->objectName(), wid); topLevels = QApplication::topLevelWidgets(); QWidget *tooltip = nullptr; - for (QWidget *widget : qAsConst(topLevels)) { + for (QWidget *widget : std::as_const(topLevels)) { if (widget->inherits("QTipLabel")) { tooltip = widget; break; } } + QVERIFY(tooltip); QTRY_VERIFY(tooltip->isVisible()); // Wait until Roll-Effect is finished (Windows Vista) - QCOMPARE(tooltip->palette().color(tooltip->backgroundRole()), col); + +#ifdef Q_OS_WINDOWS + // If tooltip palette contains empty resolve mask, validate with inherited palette + if (!tooltip->palette().resolveMask()) { + bgColor = w.palette().color(tooltip->backgroundRole()); + fgColor = w.palette().color(tooltip->foregroundRole()); + } +#endif + + QCOMPARE(tooltip->palette().color(tooltip->backgroundRole()), bgColor); + QCOMPARE(tooltip->palette().color(tooltip->foregroundRole()), fgColor); } QToolTip::showText( QPoint(0,0) , "This is " + wid3->objectName(), wid3); @@ -1623,7 +1776,7 @@ void tst_QStyleSheetStyle::toolTip() delete wid3; //should not crash; QTest::qWait(10); topLevels = QApplication::topLevelWidgets(); - for (QWidget *widget : qAsConst(topLevels)) + for (QWidget *widget : std::as_const(topLevels)) widget->update(); //should not crash either } @@ -1733,7 +1886,6 @@ void tst_QStyleSheetStyle::complexWidgetFocus() centerOnScreen(&frame); frame.show(); - QApplication::setActiveWindow(&frame); QVERIFY(QTest::qWaitForWindowActive(&frame)); for (QWidget *widget : widgets) { widget->setFocus(); @@ -1822,7 +1974,6 @@ void tst_QStyleSheetStyle::task232085_spinBoxLineEditBg() centerOnScreen(&frame); frame.show(); - QApplication::setActiveWindow(&frame); spinbox->setFocus(); QVERIFY(QTest::qWaitForWindowActive(&frame)); @@ -1958,7 +2109,6 @@ void tst_QStyleSheetStyle::QTBUG36933_brokenPseudoClassLookup() QVERIFY(QTest::qWaitForWindowExposed(&widget)); widget.activateWindow(); - QApplication::setActiveWindow(&widget); QVERIFY(QTest::qWaitForWindowActive(&widget)); QHeaderView *verticalHeader = widget.verticalHeader(); @@ -2231,11 +2381,151 @@ void tst_QStyleSheetStyle::placeholderColor() QLineEdit le2; le2.setEnabled(false); le1.ensurePolished(); - QCOMPARE(le1.palette().placeholderText(), red); + QColor phColor = le1.palette().placeholderText().color(); + QCOMPARE(phColor.rgb(), red.rgb()); + QVERIFY(phColor.alpha() < red.alpha()); + le2.ensurePolished(); - QCOMPARE(le2.palette().placeholderText(), red); + phColor = le2.palette().placeholderText().color(); + QCOMPARE(phColor.rgb(), red.rgb()); + QVERIFY(phColor.alpha() < red.alpha()); + le2.setEnabled(true); - QCOMPARE(le2.palette().placeholderText(), red); + phColor = le2.palette().placeholderText().color(); + QCOMPARE(phColor.rgb(), red.rgb()); + QVERIFY(phColor.alpha() < red.alpha()); + + const char *phSpec = "#aabbccdd"; + le1.setStyleSheet(QString("QLineEdit { placeholder-text-color: %1; }").arg(phSpec)); + QCOMPARE(le1.palette().placeholderText().color(), QColor(phSpec)); +} + +void tst_QStyleSheetStyle::accent() +{ + QLineEdit lineEdit; + const QColor universe(42, 42, 42); + lineEdit.setStyleSheet(QString("QLineEdit { accent-color: %1; }").arg(universe.name())); + lineEdit.ensurePolished(); + QCOMPARE(lineEdit.palette().accent().color(), universe); +} + +void tst_QStyleSheetStyle::enumPropertySelector_data() +{ + QTest::addColumn<QString>("styleSheet"); + + QTest::addRow("Enum value") << R"(QToolButton[popupMode=MenuButtonPopup] { padding-right: 40px; })"; + QTest::addRow("Int value") << R"(QToolButton[popupMode="1"] { padding-right: 40px; })"; +} + +void tst_QStyleSheetStyle::enumPropertySelector() +{ + QFETCH(QString, styleSheet); + + QToolButton button; + QMenu menu; + menu.addAction("Action1"); + QPixmap pm(50, 50); + pm.fill(Qt::red); + button.setIcon(pm); + button.setMenu(&menu); + button.setPopupMode(QToolButton::MenuButtonPopup); + + button.show(); + const QSize unstyledSizeHint = button.sizeHint(); + + qApp->setStyleSheet(styleSheet); + const QSize styledSizeHint = button.sizeHint(); + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QEXPECT_FAIL("Enum value", "In Qt 5, style sheet selectors have to use integer enum values", Continue); +#endif + + QVERIFY(styledSizeHint.width() > unstyledSizeHint.width()); +} + +void tst_QStyleSheetStyle::iconSizes_data() +{ + QTest::addColumn<QString>("styleSheet"); + QTest::addColumn<QFont>("font"); + QTest::addColumn<QSize>("iconSize"); + + const int defaultSize = QApplication::style()->pixelMetric(QStyle::PM_ButtonIconSize); + + QFont smallFont; + smallFont.setPointSizeF(9.0); + QFont largeFont; + largeFont.setPointSizeF(24.0); + QFont hugeFont; + hugeFont.setPointSizeF(40.0); + + QTest::addRow("default") << QString() << QFont() << QSize(defaultSize, defaultSize); + QTest::addRow("pixels") << "icon-size: 50px" << QFont() << QSize(50, 50); + QTest::addRow("points") << "icon-size: 20pt" << QFont() << QSize(15, 15); + QTest::addRow("pixels with font") << "icon-size: 50px" << smallFont << QSize(50, 50); + QTest::addRow("points with font") << "icon-size: 20pt" << largeFont << QSize(15, 15); + + const QFontMetrics defaultMetrics{QFont()}; + const QFontMetrics smallMetrics(smallFont); + const QFontMetrics largeMetrics(largeFont); + const QFontMetrics hugeMetrics(hugeFont); + QTest::addRow("1em, default font") << "icon-size: 1em" + << QFont() << QSize(defaultMetrics.height(), defaultMetrics.height()); + QTest::addRow("1em, small font") << "icon-size: 1em" + << smallFont << QSize(smallMetrics.height(), smallMetrics.height()); + QTest::addRow("1em, large font") << "icon-size: 1em" + << largeFont << QSize(largeMetrics.height(), largeMetrics.height()); + QTest::addRow("1.5em, lage font") << "icon-size: 1.5em" + << largeFont << QSize(largeMetrics.height(), largeMetrics.height()) * 1.5; + QTest::addRow("2em with styled font") << "font-size: 40pt; icon-size: 2em" + << QFont() << QSize(hugeMetrics.height(), hugeMetrics.height()) * 2; + + QTest::addRow("1ex, default font") << "icon-size: 1ex" + << QFont() << QSize(defaultMetrics.xHeight(), defaultMetrics.xHeight()); + QTest::addRow("1ex, small font") << "icon-size: 1ex" + << smallFont << QSize(smallMetrics.xHeight(), smallMetrics.xHeight()); + QTest::addRow("1ex, large font") << "icon-size: 1ex" + << largeFont << QSize(largeMetrics.xHeight(), largeMetrics.xHeight()); + QTest::addRow("1.5ex, lage font") << "icon-size: 1.5ex" + << largeFont << QSize(largeMetrics.xHeight(), largeMetrics.xHeight()) * 1.5; + QTest::addRow("2ex with styled font") << "font-size: 40pt; icon-size: 2ex" + << QFont() << QSize(hugeMetrics.xHeight(), hugeMetrics.xHeight()) * 2; +} + +void tst_QStyleSheetStyle::iconSizes() +{ + QFETCH(QString, styleSheet); + QFETCH(QFont, font); + QFETCH(QSize, iconSize); + + QPushButton button; + button.setFont(font); + button.setStyleSheet(styleSheet); + QCOMPARE(button.iconSize(), iconSize); +} + +void tst_QStyleSheetStyle::inheritWidgetPalette_data() +{ + QTest::addColumn<const QString>("styleSheet"); + QTest::addColumn<const QColor>("phColorPalette"); + + QTest::addRow("blueAndGreen") << "QLineEdit {color: rgb(0,0,255);}" << QColor(Qt::green); + QTest::addRow("emptyStyleSheet") << QString() << QColor(Qt::green); + +} + +void tst_QStyleSheetStyle::inheritWidgetPalette() +{ + QFETCH(const QString, styleSheet); + QFETCH(const QColor, phColorPalette); + + QLineEdit edit; + QPalette palette = edit.palette(); + palette.setBrush(QPalette::PlaceholderText, phColorPalette); + edit.setPalette(palette); + edit.setStyleSheet(styleSheet); + const QColor phColor = edit.palette().placeholderText().color(); + + QCOMPARE(phColor, phColorPalette); } QTEST_MAIN(tst_QStyleSheetStyle) diff --git a/tests/auto/widgets/util/CMakeLists.txt b/tests/auto/widgets/util/CMakeLists.txt index 20accb3150..31725573c7 100644 --- a/tests/auto/widgets/util/CMakeLists.txt +++ b/tests/auto/widgets/util/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from util.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause add_subdirectory(qcompleter) add_subdirectory(qscroller) diff --git a/tests/auto/widgets/util/qcompleter/BLACKLIST b/tests/auto/widgets/util/qcompleter/BLACKLIST index 367270fdf2..5487a1fe44 100644 --- a/tests/auto/widgets/util/qcompleter/BLACKLIST +++ b/tests/auto/widgets/util/qcompleter/BLACKLIST @@ -1,6 +1,2 @@ [QTBUG_14292_filesystem] -ubuntu-16.04 opensuse-leap -rhel-7.4 -rhel-6.6 -opensuse-42.3 diff --git a/tests/auto/widgets/util/qcompleter/CMakeLists.txt b/tests/auto/widgets/util/qcompleter/CMakeLists.txt index 5d4ff347a2..a96945771f 100644 --- a/tests/auto/widgets/util/qcompleter/CMakeLists.txt +++ b/tests/auto/widgets/util/qcompleter/CMakeLists.txt @@ -1,17 +1,22 @@ -# Generated from qcompleter.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qcompleter Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcompleter LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qcompleter SOURCES tst_qcompleter.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::TestPrivate Qt::Widgets + Qt::WidgetsPrivate ) - -#### Keys ignored in scope 1:.:.:qcompleter.pro:<TRUE>: -# TEMPLATE = "app" diff --git a/tests/auto/widgets/util/qcompleter/tst_qcompleter.cpp b/tests/auto/widgets/util/qcompleter/tst_qcompleter.cpp index 7de37875b2..b2e77bc935 100644 --- a/tests/auto/widgets/util/qcompleter/tst_qcompleter.cpp +++ b/tests/auto/widgets/util/qcompleter/tst_qcompleter.cpp @@ -1,36 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QtGui> #include <QtWidgets> #include <QtDebug> -#include <QPair> #include <QList> #include <QPointer> #include <QSignalSpy> @@ -41,6 +15,8 @@ #include <memory> +#include <QtWidgets/private/qapplication_p.h> + Q_DECLARE_METATYPE(QCompleter::CompletionMode) using namespace QTestPrivate; @@ -82,7 +58,7 @@ QString CsvCompleter::pathFromIndex(const QModelIndex &sourceIndex) const idx = parent.sibling(parent.row(), sourceIndex.column()); } while (idx.isValid()); - return list.count() == 1 ? list.constFirst() : list.join(QLatin1Char(',')); + return list.size() == 1 ? list.constFirst() : list.join(QLatin1Char(',')); } class tst_QCompleter : public QObject @@ -110,6 +86,8 @@ private slots: void fileSystemModel_data(); void fileSystemModel(); + void fileDialog_data(); + void fileDialog(); void changingModel_data(); void changingModel(); @@ -131,6 +109,8 @@ private slots: void dynamicSortOrder(); void disabledItems(); + void hideWidget(); + // task-specific tests below me void task178797_activatedOnReturn(); void task189564_omitNonSelectableItems(); @@ -143,6 +123,8 @@ private slots: void QTBUG_14292_filesystem(); void QTBUG_52028_tabAutoCompletes(); void QTBUG_51889_activatedSentTwice(); + void showPopupInGraphicsView(); + void inheritedEventFilter(); private: void filter(bool assync = false); @@ -173,6 +155,16 @@ tst_QCompleter::~tst_QCompleter() delete completer; } +#ifdef Q_OS_ANDROID +static QString androidHomePath() +{ + const auto homePaths = QStandardPaths::standardLocations(QStandardPaths::HomeLocation); + QDir dir = homePaths.isEmpty() ? QDir() : homePaths.first(); + dir.cdUp(); + return dir.path(); +} +#endif + void tst_QCompleter::setSourceModel(ModelType type) { QTreeWidgetItem *parent, *child; @@ -225,7 +217,13 @@ void tst_QCompleter::setSourceModel(ModelType type) completer->setCsvCompletion(false); { auto m = new QFileSystemModel(completer); +#ifdef Q_OS_ANDROID + // Android 11 and above doesn't allow accessing root filesystem as before, + // so let's opt int for the app's home. + m->setRootPath(androidHomePath()); +#else m->setRootPath("/"); +#endif completer->setModel(m); } completer->setCompletionColumn(0); @@ -612,7 +610,9 @@ void tst_QCompleter::fileSystemModel_data() // QTest::newRow("(/d)") << "/d" << "" << "Developer" << "/Developer"; #elif defined(Q_OS_ANDROID) QTest::newRow("()") << "" << "" << "/" << "/"; - QTest::newRow("(/et)") << "/et" << "" << "etc" << "/etc"; + const QString androidDir = androidHomePath(); + const QString tag = QStringLiteral("%1/fil").arg(androidDir); + QTest::newRow(tag.toUtf8().data()) << tag << "" << "files" << androidDir + "/files"; #else QTest::newRow("()") << "" << "" << "/" << "/"; #if !defined(Q_OS_AIX) && !defined(Q_OS_HPUX) && !defined(Q_OS_QNX) @@ -626,7 +626,23 @@ void tst_QCompleter::fileSystemModel_data() void tst_QCompleter::fileSystemModel() { - //QFileSystemModel is assync. + //QFileSystemModel is async. + filter(true); +} + +/*! + In the file dialog, the completer uses the EditRole. + See QTBUG-94799 +*/ +void tst_QCompleter::fileDialog_data() +{ + fileSystemModel_data(); + completer->setCompletionRole(Qt::EditRole); +} + +void tst_QCompleter::fileDialog() +{ + //QFileSystemModel is async. filter(true); } @@ -1046,7 +1062,6 @@ void tst_QCompleter::multipleWidgets() window.setWindowTitle(QLatin1String(QTest::currentTestFunction())); window.move(200, 200); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); QFocusEvent focusIn(QEvent::FocusIn); @@ -1058,7 +1073,6 @@ void tst_QCompleter::multipleWidgets() comboBox->setFocus(); comboBox->show(); window.activateWindow(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); QCOMPARE(QApplication::focusWidget(), comboBox); comboBox->lineEdit()->setText("it"); @@ -1093,7 +1107,6 @@ void tst_QCompleter::focusIn() window.move(200, 200); window.show(); window.activateWindow(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); auto comboBox = new QComboBox(&window); @@ -1164,13 +1177,39 @@ void tst_QCompleter::disabledItems() QAbstractItemView *view = lineEdit.completer()->popup(); QVERIFY(view->isVisible()); QTest::mouseClick(view->viewport(), Qt::LeftButton, {}, view->visualRect(view->model()->index(0, 0)).center()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QVERIFY(view->isVisible()); QTest::mouseClick(view->viewport(), Qt::LeftButton, {}, view->visualRect(view->model()->index(1, 0)).center()); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QVERIFY(!view->isVisible()); } +void tst_QCompleter::hideWidget() +{ + // hiding the widget should hide/close the popup + QWidget w; + w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); + w.setLayout(new QVBoxLayout); + + QLineEdit edit; + edit.setCompleter(new QCompleter({ "foo", "bar" })); + + w.layout()->addWidget(&edit); + + const auto pos = w.screen()->availableGeometry().topLeft() + QPoint(200, 200); + w.move(pos); + w.show(); + QApplicationPrivate::setActiveWindow(&w); + QVERIFY(QTest::qWaitForWindowActive(&w)); + + // activate the completer + QTest::keyClick(&edit, Qt::Key_F); + QVERIFY(edit.completer()->popup()); + QTRY_VERIFY(edit.completer()->popup()->isVisible()); + edit.hide(); + QVERIFY(!edit.completer()->popup()->isVisible()); +} + void tst_QCompleter::task178797_activatedOnReturn() { if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) @@ -1181,10 +1220,9 @@ void tst_QCompleter::task178797_activatedOnReturn() auto completer = new QCompleter({"foobar1", "foobar2"}, &ledit); ledit.setCompleter(completer); QSignalSpy spy(completer, QOverload<const QString &>::of(&QCompleter::activated)); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); ledit.move(200, 200); ledit.show(); - QApplication::setActiveWindow(&ledit); QVERIFY(QTest::qWaitForWindowActive(&ledit)); QTest::keyClick(&ledit, Qt::Key_F); QCoreApplication::processEvents(); @@ -1193,7 +1231,7 @@ void tst_QCompleter::task178797_activatedOnReturn() QCoreApplication::processEvents(); QTest::keyClick(QApplication::activePopupWidget(), Qt::Key_Return); QCoreApplication::processEvents(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } class task189564_StringListModel : public QStringListModel @@ -1268,14 +1306,13 @@ void tst_QCompleter::task246056_setCompletionPrefix() comboBox.addItem("a2"); comboBox.move(200, 200); comboBox.show(); - QApplication::setActiveWindow(&comboBox); QVERIFY(QTest::qWaitForWindowActive(&comboBox)); QSignalSpy spy(comboBox.completer(), QOverload<const QModelIndex &>::of(&QCompleter::activated)); QTest::keyPress(&comboBox, 'a'); QTest::keyPress(comboBox.completer()->popup(), Qt::Key_Down); QTest::keyPress(comboBox.completer()->popup(), Qt::Key_Down); QTest::keyPress(comboBox.completer()->popup(), Qt::Key_Enter); // don't crash! - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); const auto index = spy.at(0).constFirst().toModelIndex(); QVERIFY(!index.isValid()); } @@ -1334,7 +1371,6 @@ void tst_QCompleter::task250064_lostFocus() task250064_Widget widget; widget.setWindowTitle(QLatin1String(QTest::currentTestFunction())); widget.show(); - QApplication::setActiveWindow(&widget); QVERIFY(QTest::qWaitForWindowActive(&widget)); QTest::keyPress(widget.textEdit(), 'a'); Qt::FocusPolicy origPolicy = widget.textEdit()->focusPolicy(); @@ -1379,7 +1415,6 @@ void tst_QCompleter::task253125_lineEditCompletion() edit.move(200, 200); edit.show(); edit.setFocus(); - QApplication::setActiveWindow(&edit); QVERIFY(QTest::qWaitForWindowActive(&edit)); QTest::keyClick(&edit, 'i'); @@ -1542,7 +1577,6 @@ void tst_QCompleter::task247560_keyboardNavigation() edit.move(200, 200); edit.show(); edit.setFocus(); - QApplication::setActiveWindow(&edit); QVERIFY(QTest::qWaitForWindowActive(&edit)); QTest::keyClick(&edit, 'r'); @@ -1642,6 +1676,7 @@ void tst_QCompleter::QTBUG_14292_filesystem() // to pop up the completion list due to file changed signals. FileSystem fs; QFileSystemModel model; + QSignalSpy filesAddedSpy(&model, &QAbstractItemModel::rowsInserted); model.setRootPath(fs.path()); QVERIFY(fs.createDirectory(QLatin1String(testDir1))); @@ -1655,7 +1690,6 @@ void tst_QCompleter::QTBUG_14292_filesystem() edit.move(200, 200); edit.show(); - QApplication::setActiveWindow(&edit); QVERIFY(QTest::qWaitForWindowActive(&edit)); QCOMPARE(QApplication::activeWindow(), &edit); edit.setFocus(); @@ -1671,6 +1705,7 @@ void tst_QCompleter::QTBUG_14292_filesystem() QTRY_VERIFY(comp.popup()->isVisible()); QCOMPARE(comp.popup()->model()->rowCount(), 2); QApplication::processEvents(); + QCOMPARE(qApp->focusObject(), &edit); // for QTBUG_108522 QTest::keyClick(&edit, 'h'); QCOMPARE(comp.popup()->model()->rowCount(), 2); QTest::keyClick(&edit, 'e'); @@ -1678,13 +1713,16 @@ void tst_QCompleter::QTBUG_14292_filesystem() QTest::keyClick(&edit, 'r'); QTRY_VERIFY(!comp.popup()->isVisible()); QVERIFY(fs.createDirectory(QStringLiteral("hero"))); + if (!filesAddedSpy.wait()) + QSKIP("File system model didn't notify about new directory, skipping tests"); QTRY_VERIFY(comp.popup()->isVisible()); QCOMPARE(comp.popup()->model()->rowCount(), 1); QTest::keyClick(comp.popup(), Qt::Key_Escape); QTRY_VERIFY(!comp.popup()->isVisible()); QVERIFY(fs.createDirectory(QStringLiteral("nothingThere"))); //there is no reason creating a file should open a popup, it did in Qt 4.7.0 - QTest::qWait(60); + if (!filesAddedSpy.wait()) + QSKIP("File system model didn't notify about new file, skipping tests"); QVERIFY(!comp.popup()->isVisible()); QTest::keyClick(&edit, Qt::Key_Backspace); @@ -1696,13 +1734,13 @@ void tst_QCompleter::QTBUG_14292_filesystem() QWidget w; w.move(400, 200); w.show(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QVERIFY(!edit.hasFocus() && !comp.popup()->hasFocus()); QVERIFY(fs.createDirectory(QStringLiteral("hemo"))); //there is no reason creating a file should open a popup, it did in Qt 4.7.0 - QTest::qWait(60); + if (!filesAddedSpy.wait()) + QSKIP("File system model didn't notify about new file, skipping tests"); QVERIFY(!comp.popup()->isVisible()); } @@ -1732,7 +1770,7 @@ void tst_QCompleter::QTBUG_52028_tabAutoCompletes() const auto pos = w.screen()->availableGeometry().topLeft() + QPoint(200,200); w.move(pos); w.show(); - QApplication::setActiveWindow(&w); + QApplicationPrivate::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QSignalSpy activatedSpy(&cbox, &QComboBox::activated); @@ -1744,10 +1782,10 @@ void tst_QCompleter::QTBUG_52028_tabAutoCompletes() QTRY_VERIFY(cbox.completer()->popup()->isVisible()); QTest::keyClick(cbox.completer()->popup(), Qt::Key_Tab); QCOMPARE(cbox.completer()->currentCompletion(), QLatin1String("hux")); - QCOMPARE(activatedSpy.count(), 0); + QCOMPARE(activatedSpy.size(), 0); QEXPECT_FAIL("", "QTBUG-52028 will not be fixed today.", Abort); QCOMPARE(cbox.currentText(), QLatin1String("hux")); - QCOMPARE(activatedSpy.count(), 0); + QCOMPARE(activatedSpy.size(), 0); QVERIFY(!le->hasFocus()); } @@ -1776,7 +1814,6 @@ void tst_QCompleter::QTBUG_51889_activatedSentTwice() const auto pos = w.screen()->availableGeometry().topLeft() + QPoint(200,200); w.move(pos); w.show(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QSignalSpy activatedSpy(&cbox, &QComboBox::activated); @@ -1788,7 +1825,7 @@ void tst_QCompleter::QTBUG_51889_activatedSentTwice() QTRY_VERIFY(cbox.completer()->popup()->isVisible()); QTest::keyClick(cbox.completer()->popup(), Qt::Key_Down); QTest::keyClick(cbox.completer()->popup(), Qt::Key_Return); - QTRY_COMPARE(activatedSpy.count(), 1); + QTRY_COMPARE(activatedSpy.size(), 1); // Navigate + enter activates only once (non-first item) cbox.lineEdit()->clear(); @@ -1798,7 +1835,7 @@ void tst_QCompleter::QTBUG_51889_activatedSentTwice() QTRY_VERIFY(cbox.completer()->popup()->isVisible()); QTest::keyClick(cbox.completer()->popup(), Qt::Key_Down); QTest::keyClick(cbox.completer()->popup(), Qt::Key_Return); - QTRY_COMPARE(activatedSpy.count(), 1); + QTRY_COMPARE(activatedSpy.size(), 1); // Full text + enter activates only once cbox.lineEdit()->clear(); @@ -1807,7 +1844,78 @@ void tst_QCompleter::QTBUG_51889_activatedSentTwice() QVERIFY(cbox.completer()->popup()); QTRY_VERIFY(cbox.completer()->popup()->isVisible()); QTest::keyClick(&cbox, Qt::Key_Return); - QTRY_COMPARE(activatedSpy.count(), 1); + QTRY_COMPARE(activatedSpy.size(), 1); +} + +void tst_QCompleter::showPopupInGraphicsView() +{ + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Wayland: Skip this test, see also QTBUG-107186"); + + QGraphicsView view; + QGraphicsScene scene; + view.setScene(&scene); + + QLineEdit lineEdit; + lineEdit.setCompleter(new QCompleter({"alpha", "omega", "omicron", "zeta"})); + scene.addWidget(&lineEdit); + + view.move(view.screen()->availableGeometry().topLeft() + QPoint(10, 10)); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + // show popup under line edit + QTest::keyClick(&lineEdit, Qt::Key_A); + QVERIFY(lineEdit.completer()->popup()); + QVERIFY(lineEdit.completer()->popup()->isVisible()); + QCOMPARE(lineEdit.completer()->popup()->geometry().x(), lineEdit.mapToGlobal(QPoint(0, 0)).x()); + QVERIFY(lineEdit.completer()->popup()->geometry().top() >= (lineEdit.mapToGlobal(QPoint(0, lineEdit.height() - 1)).y() - 1)); + + // move widget to the bottom of screen + lineEdit.clear(); + int y = view.screen()->availableGeometry().height() - lineEdit.geometry().y(); + view.move(view.geometry().x(), y); + + // show popup above line edit + QTest::keyClick(&lineEdit, Qt::Key_A); + QVERIFY(lineEdit.completer()->popup()->geometry().bottom() < lineEdit.mapToGlobal(QPoint(0, 0)).y()); +} + +void tst_QCompleter::inheritedEventFilter() +{ + class Completer : public QCompleter + { + public: + explicit Completer(QWidget *parent) : QCompleter(parent) + { + Q_ASSERT(parent); + setPopup(new QListView()); + popup()->installEventFilter(this); + } + + bool m_popupChildAdded = false; + + protected: + bool eventFilter(QObject *watched, QEvent *event) override + { + if (watched == popup() && event->type() == QEvent::ChildAdded) + m_popupChildAdded = true; + + return QCompleter::eventFilter(watched, event); + } + }; + + QComboBox comboBox; + comboBox.setEditable(true); + Completer *completer = new Completer(&comboBox); + comboBox.setCompleter(completer); + + // comboBox.show() must not crash with an infinite loop in the event filter + comboBox.show(); + QVERIFY(QTest::qWaitForWindowExposed(&comboBox)); + + // Since event orders are platform dependent, only the the ChildAdded event is checked. + QVERIFY(QTest::qWaitFor([completer](){return completer->m_popupChildAdded; })); } QTEST_MAIN(tst_QCompleter) diff --git a/tests/auto/widgets/util/qscroller/CMakeLists.txt b/tests/auto/widgets/util/qscroller/CMakeLists.txt index 11e13cdd9f..2beee70109 100644 --- a/tests/auto/widgets/util/qscroller/CMakeLists.txt +++ b/tests/auto/widgets/util/qscroller/CMakeLists.txt @@ -1,14 +1,22 @@ -# Generated from qscroller.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qscroller Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qscroller LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qscroller SOURCES tst_qscroller.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::GuiPrivate Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/util/qscroller/tst_qscroller.cpp b/tests/auto/widgets/util/qscroller/tst_qscroller.cpp index 232b8084cd..101b502fc6 100644 --- a/tests/auto/widgets/util/qscroller/tst_qscroller.cpp +++ b/tests/auto/widgets/util/qscroller/tst_qscroller.cpp @@ -1,37 +1,15 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the $MODULE$ of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtGui> #include <QtWidgets> #include <QTest> #include <QtGui/private/qevent_p.h> +#include <QtGui/private/qeventpoint_p.h> #include <qpa/qwindowsysteminterface.h> +#include <QtWidgets/private/qapplication_p.h> + // #include <QDebug> class tst_QScrollerWidget : public QWidget @@ -127,6 +105,7 @@ private slots: void scroll(); void overshoot(); void multipleWindows(); + void mouseEventTimestamp(); private: QPointingDevice *m_touchScreen = QTest::createTouchDevice(); @@ -147,11 +126,11 @@ void tst_QScroller::kineticScroll(tst_QScrollerWidget *sw, QPointF from, QPoint QScrollerProperties sp1 = QScroller::scroller(sw)->scrollerProperties(); // send the touch begin event - QMutableEventPoint touchPoint(0); - touchPoint.setState(QEventPoint::State::Pressed); - touchPoint.setPosition(touchStart); - touchPoint.setScenePosition(touchStart); - touchPoint.setGlobalPosition(touchStart); + QEventPoint touchPoint(0); + QMutableEventPoint::setState(touchPoint, QEventPoint::State::Pressed); + QMutableEventPoint::setPosition(touchPoint, touchStart); + QMutableEventPoint::setScenePosition(touchPoint, touchStart); + QMutableEventPoint::setGlobalPosition(touchPoint, touchStart); QTouchEvent touchEvent1(QEvent::TouchBegin, m_touchScreen, @@ -164,10 +143,10 @@ void tst_QScroller::kineticScroll(tst_QScrollerWidget *sw, QPointF from, QPoint // send the touch update far enough to trigger a scroll QTest::qWait(200); // we need to wait a little or else the speed would be infinite. now we have around 500 pixel per second. - touchPoint.setPosition(touchUpdate); - touchPoint.setScenePosition(touchUpdate); - touchPoint.setGlobalPosition(touchUpdate); - touchPoint.setState(QEventPoint::State::Updated); + QMutableEventPoint::setPosition(touchPoint, touchUpdate); + QMutableEventPoint::setScenePosition(touchPoint, touchUpdate); + QMutableEventPoint::setGlobalPosition(touchPoint, touchUpdate); + QMutableEventPoint::setState(touchPoint, QEventPoint::State::Updated); QTouchEvent touchEvent2(QEvent::TouchUpdate, m_touchScreen, Qt::NoModifier, @@ -188,10 +167,10 @@ void tst_QScroller::kineticScroll(tst_QScrollerWidget *sw, QPointF from, QPoint QVERIFY(qAbs(sw->currentPos.y() - calculatedPos.y()) < 1.0); // send the touch end - touchPoint.setPosition(touchEnd); - touchPoint.setScenePosition(touchEnd); - touchPoint.setGlobalPosition(touchEnd); - touchPoint.setState(QEventPoint::State::Released); + QMutableEventPoint::setPosition(touchPoint, touchEnd); + QMutableEventPoint::setScenePosition(touchPoint, touchEnd); + QMutableEventPoint::setGlobalPosition(touchPoint, touchEnd); + QMutableEventPoint::setState(touchPoint, QEventPoint::State::Released); QTouchEvent touchEvent5(QEvent::TouchEnd, m_touchScreen, Qt::NoModifier, @@ -215,11 +194,11 @@ void tst_QScroller::kineticScrollNoTest(tst_QScrollerWidget *sw, QPointF from, Q int fps = 60; // send the touch begin event - QMutableEventPoint touchPoint(0); - touchPoint.setState(QEventPoint::State::Pressed); - touchPoint.setPosition(touchStart); - touchPoint.setScenePosition(touchStart); - touchPoint.setGlobalPosition(touchStart); + QEventPoint touchPoint(0); + QMutableEventPoint::setState(touchPoint, QEventPoint::State::Pressed); + QMutableEventPoint::setPosition(touchPoint, touchStart); + QMutableEventPoint::setScenePosition(touchPoint, touchStart); + QMutableEventPoint::setGlobalPosition(touchPoint, touchStart); QTouchEvent touchEvent1(QEvent::TouchBegin, m_touchScreen, Qt::NoModifier, @@ -228,10 +207,10 @@ void tst_QScroller::kineticScrollNoTest(tst_QScrollerWidget *sw, QPointF from, Q // send the touch update far enough to trigger a scroll QTest::qWait(200); // we need to wait a little or else the speed would be infinite. now we have around 500 pixel per second. - touchPoint.setState(QEventPoint::State::Updated); - touchPoint.setPosition(touchUpdate); - touchPoint.setScenePosition(touchUpdate); - touchPoint.setGlobalPosition(touchUpdate); + QMutableEventPoint::setState(touchPoint, QEventPoint::State::Updated); + QMutableEventPoint::setPosition(touchPoint, touchUpdate); + QMutableEventPoint::setScenePosition(touchPoint, touchUpdate); + QMutableEventPoint::setGlobalPosition(touchPoint, touchUpdate); QTouchEvent touchEvent2(QEvent::TouchUpdate, m_touchScreen, Qt::NoModifier, @@ -241,10 +220,10 @@ void tst_QScroller::kineticScrollNoTest(tst_QScrollerWidget *sw, QPointF from, Q QTest::qWait(1000 / fps * 2); // wait until the first scroll move // send the touch end - touchPoint.setState(QEventPoint::State::Released); - touchPoint.setPosition(touchEnd); - touchPoint.setScenePosition(touchEnd); - touchPoint.setGlobalPosition(touchEnd); + QMutableEventPoint::setState(touchPoint, QEventPoint::State::Released); + QMutableEventPoint::setPosition(touchPoint, touchEnd); + QMutableEventPoint::setScenePosition(touchPoint, touchEnd); + QMutableEventPoint::setGlobalPosition(touchPoint, touchEnd); QTouchEvent touchEvent5(QEvent::TouchEnd, m_touchScreen, Qt::NoModifier, @@ -345,7 +324,6 @@ void tst_QScroller::scrollTo() { QScopedPointer<tst_QScrollerWidget> sw(new tst_QScrollerWidget); sw->show(); - QApplication::setActiveWindow(sw.data()); if (!QTest::qWaitForWindowExposed(sw.data()) || !QTest::qWaitForWindowActive(sw.data())) QSKIP("Failed to show and activate window"); @@ -377,7 +355,6 @@ void tst_QScroller::scroll() QScroller::grabGesture(sw.data(), QScroller::TouchGesture); sw->setGeometry(100, 100, 400, 300); sw->show(); - QApplication::setActiveWindow(sw.data()); if (!QTest::qWaitForWindowExposed(sw.data()) || !QTest::qWaitForWindowActive(sw.data())) QSKIP("Failed to show and activate window"); @@ -418,7 +395,6 @@ void tst_QScroller::overshoot() QScroller::grabGesture(sw.data(), QScroller::TouchGesture); sw->setGeometry(100, 100, 400, 300); sw->show(); - QApplication::setActiveWindow(sw.data()); if (!QTest::qWaitForWindowExposed(sw.data()) || !QTest::qWaitForWindowActive(sw.data())) QSKIP("Failed to show and activate window"); @@ -540,6 +516,66 @@ void tst_QScroller::multipleWindows() #endif } +/*! + This test verifies that mouse events arrive at the target widget + with valid timestamp, even if there is a gesture filtering (and then + replaying a copy of) the event. QTBUG-102010 + + We cannot truly simulate the double click here, as simulated events don't + go through the exact same event machinery as real events, so double clicks + don't get generated by Qt here. But we can verify that the timestamps of + the eventually delivered events are maintained. +*/ +void tst_QScroller::mouseEventTimestamp() +{ +#if QT_CONFIG(gestures) && QT_CONFIG(scroller) + QScopedPointer<tst_QScrollerWidget> sw(new tst_QScrollerWidget()); + sw->scrollArea = QRectF(0, 0, 1000, 1000); + QScroller::grabGesture(sw.data(), QScroller::LeftMouseButtonGesture); + sw->setGeometry(100, 100, 400, 300); + sw->show(); + if (!QTest::qWaitForWindowExposed(sw.data()) || !QTest::qWaitForWindowActive(sw.data())) + QSKIP("Failed to show and activate window"); + + QScroller *s1 = QScroller::scroller(sw.data()); + + struct EventFilter : QObject + { + QList<int> timestamps; + protected: + bool eventFilter(QObject *o, QEvent *e) override + { + if (e->isInputEvent()) + timestamps << static_cast<QInputEvent *>(e)->timestamp(); + return QObject::eventFilter(o, e); + } + + } eventFilter; + sw->installEventFilter(&eventFilter); + + const int interval = QGuiApplication::styleHints()->mouseDoubleClickInterval() / 10; + const QPoint point = sw->geometry().center(); + // Simulate double by pressing twice within the double click interval. + // Presses are filtered and then delayed by the scroller/gesture machinery, + // so we first record all events, and then make sure that the relative timestamps + // are maintained also for the replayed or synthesized events. + QTest::mousePress(sw->windowHandle(), Qt::LeftButton, {}, point); + QCOMPARE(s1->state(), QScroller::Pressed); + QTest::mouseRelease(sw->windowHandle(), Qt::LeftButton, {}, point, interval); + QCOMPARE(s1->state(), QScroller::Inactive); + QTest::mousePress(sw->windowHandle(), Qt::LeftButton, {}, point, interval); + QCOMPARE(s1->state(), QScroller::Pressed); + // also filtered and delayed by the scroller + QTest::mouseRelease(sw->windowHandle(), Qt::LeftButton, {}, point, interval); + QCOMPARE(s1->state(), QScroller::Inactive); + int lastTimestamp = -1; + for (int timestamp : std::as_const(eventFilter.timestamps)) { + QCOMPARE_GE(timestamp, lastTimestamp); + lastTimestamp = timestamp + interval; + } +#endif +} + QTEST_MAIN(tst_QScroller) #include "tst_qscroller.moc" diff --git a/tests/auto/widgets/util/qsystemtrayicon/CMakeLists.txt b/tests/auto/widgets/util/qsystemtrayicon/CMakeLists.txt index d84f229c04..d4c517e88c 100644 --- a/tests/auto/widgets/util/qsystemtrayicon/CMakeLists.txt +++ b/tests/auto/widgets/util/qsystemtrayicon/CMakeLists.txt @@ -1,13 +1,26 @@ -# Generated from qsystemtrayicon.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qsystemtrayicon Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsystemtrayicon LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +set(resources_resource_files + "icons/icon.png" +) + qt_internal_add_test(tst_qsystemtrayicon SOURCES tst_qsystemtrayicon.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets + TESTDATA ${resources_resource_files} + BUILTIN_TESTDATA ) diff --git a/tests/auto/widgets/util/qsystemtrayicon/tst_qsystemtrayicon.cpp b/tests/auto/widgets/util/qsystemtrayicon/tst_qsystemtrayicon.cpp index 7511ea1f7c..9a7cd2e534 100644 --- a/tests/auto/widgets/util/qsystemtrayicon/tst_qsystemtrayicon.cpp +++ b/tests/auto/widgets/util/qsystemtrayicon/tst_qsystemtrayicon.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -64,9 +39,9 @@ tst_QSystemTrayIcon::~tst_QSystemTrayIcon() void tst_QSystemTrayIcon::showHide() { QSystemTrayIcon icon; - icon.setIcon(QIcon("icons/icon.png")); + icon.setIcon(QIcon(":/icons/icon.png")); icon.show(); - icon.setIcon(QIcon("icons/icon.png")); + icon.setIcon(QIcon(":/icons/icon.png")); icon.hide(); } @@ -74,7 +49,7 @@ void tst_QSystemTrayIcon::showHide() void tst_QSystemTrayIcon::showMessage() { QSystemTrayIcon icon; - icon.setIcon(QIcon("icons/icon.png")); + icon.setIcon(QIcon(":/icons/icon.png")); icon.showMessage("Title", "Messagecontents"); icon.showMessage("Title", "Messagecontents", QSystemTrayIcon::NoIcon); @@ -97,7 +72,7 @@ void tst_QSystemTrayIcon::getSetCheck() QCOMPARE(true, "testToolTip" == icon.toolTip()); QCOMPARE(true, icon.icon().isNull()); - icon.setIcon(QIcon("icons/icon.png")); + icon.setIcon(QIcon(":/icons/icon.png")); QCOMPARE(false, icon.icon().isNull()); QMenu menu; @@ -126,16 +101,16 @@ void tst_QSystemTrayIcon::supportsMessages() void tst_QSystemTrayIcon::lastWindowClosed() { - QSignalSpy spy(qApp, &QApplication::lastWindowClosed); + QSignalSpy spy(qApp, &QGuiApplication::lastWindowClosed); QWidget window; QSystemTrayIcon icon; - icon.setIcon(QIcon("whatever.png")); + icon.setIcon(QIcon(":/icons/icon.png")); icon.show(); window.show(); QTimer::singleShot(2500, &window, SLOT(close())); QTimer::singleShot(20000, qApp, SLOT(quit())); // in case the test fails qApp->exec(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } QTEST_MAIN(tst_QSystemTrayIcon) diff --git a/tests/auto/widgets/widgets/CMakeLists.txt b/tests/auto/widgets/widgets/CMakeLists.txt index c64b86b3de..eb44f3d103 100644 --- a/tests/auto/widgets/widgets/CMakeLists.txt +++ b/tests/auto/widgets/widgets/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from widgets.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause add_subdirectory(qabstractbutton) add_subdirectory(qabstractscrollarea) @@ -28,6 +29,7 @@ add_subdirectory(qscrollbar) add_subdirectory(qsizegrip) add_subdirectory(qslider) add_subdirectory(qspinbox) +add_subdirectory(qsplashscreen) add_subdirectory(qsplitter) add_subdirectory(qstackedwidget) add_subdirectory(qstatusbar) @@ -46,17 +48,15 @@ endif() if(QT_FEATURE_shortcut) add_subdirectory(qkeysequenceedit) endif() +add_subdirectory(qmenu) +add_subdirectory(qlineedit) if(NOT ANDROID) - # QTBUG-87417 # special case - add_subdirectory(qlineedit) - # QTBUG-87420 # special case + # QTBUG-87420 add_subdirectory(qmdiarea) - # QTBUG-87671 # special case - add_subdirectory(qmenu) - # QTBUG-87421 # special case + # QTBUG-87421 add_subdirectory(qmenubar) endif() -# QTBUG-87671 # special case -if(QT_FEATURE_opengl AND NOT ANDROID) +if(QT_FEATURE_opengl) add_subdirectory(qopenglwidget) endif() +add_subdirectory(qrhiwidget) diff --git a/tests/auto/widgets/widgets/qabstractbutton/BLACKLIST b/tests/auto/widgets/widgets/qabstractbutton/BLACKLIST deleted file mode 100644 index f12e580559..0000000000 --- a/tests/auto/widgets/widgets/qabstractbutton/BLACKLIST +++ /dev/null @@ -1,5 +0,0 @@ -# QTBUG-87411 -[shortcutEvents] -android -[stopRepeatTimer] -android diff --git a/tests/auto/widgets/widgets/qabstractbutton/CMakeLists.txt b/tests/auto/widgets/widgets/qabstractbutton/CMakeLists.txt index d192549f94..883614fc04 100644 --- a/tests/auto/widgets/widgets/qabstractbutton/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qabstractbutton/CMakeLists.txt @@ -1,14 +1,22 @@ -# Generated from qabstractbutton.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qabstractbutton Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qabstractbutton LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qabstractbutton SOURCES tst_qabstractbutton.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::GuiPrivate Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/widgets/qabstractbutton/tst_qabstractbutton.cpp b/tests/auto/widgets/widgets/qabstractbutton/tst_qabstractbutton.cpp index 878647f4a4..cb91f0c6e6 100644 --- a/tests/auto/widgets/widgets/qabstractbutton/tst_qabstractbutton.cpp +++ b/tests/auto/widgets/widgets/qabstractbutton/tst_qabstractbutton.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -42,6 +17,9 @@ #include <private/qguiapplication_p.h> #include <qpa/qplatformintegration.h> +#include <qpa/qplatformtheme.h> + +#include <QtWidgets/private/qapplication_p.h> class tst_QAbstractButton : public QObject { @@ -81,6 +59,8 @@ private slots: void keyNavigation(); #endif + void buttonPressKeys(); + protected slots: void onClicked(); void onToggled( bool on ); @@ -276,7 +256,13 @@ void tst_QAbstractButton::setAutoRepeat() QCOMPARE(press_count, click_count); QVERIFY(click_count > 1); break; - case 4: + case 4: { + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value<QList<Qt::Key>>(); + if (buttonPressKeys.contains(Qt::Key_Enter)) { + QSKIP("platform theme has Key_Enter in ButtonPressKeys"); + } // check that pressing ENTER has no effect when autorepeat is false testWidget->setDown( false ); testWidget->setAutoRepeat( false ); @@ -293,7 +279,14 @@ void tst_QAbstractButton::setAutoRepeat() QVERIFY( click_count == 0 ); break; - case 5: + } + case 5: { + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value<QList<Qt::Key>>(); + if (buttonPressKeys.contains(Qt::Key_Enter)) { + QSKIP("platform theme has Key_Enter in ButtonPressKeys"); + } // check that pressing ENTER has no effect when autorepeat is true testWidget->setDown( false ); testWidget->setAutoRepeat( true ); @@ -311,6 +304,7 @@ void tst_QAbstractButton::setAutoRepeat() QVERIFY( click_count == 0 ); break; + } case 6: // verify autorepeat is off by default. MyButton tmp( 0); @@ -486,7 +480,6 @@ void tst_QAbstractButton::setShortcut() QKeySequence seq( Qt::Key_A ); testWidget->setShortcut( seq ); - QApplication::setActiveWindow(testWidget); testWidget->activateWindow(); // must be active to get shortcuts QVERIFY(QTest::qWaitForWindowActive(testWidget)); @@ -514,11 +507,34 @@ void tst_QAbstractButton::setShortcut() void tst_QAbstractButton::animateClick() { - testWidget->animateClick(); - QVERIFY( testWidget->isDown() ); - qApp->processEvents(); - QVERIFY( testWidget->isDown() ); - QTRY_VERIFY( !testWidget->isDown() ); + MyButton button; + QSignalSpy pressedSpy(&button, &QAbstractButton::pressed); + QSignalSpy releasedSpy(&button, &QAbstractButton::released); + QSignalSpy clickedSpy(&button, &QAbstractButton::clicked); + + QElapsedTimer elapsed; + elapsed.start(); + button.animateClick(); + + QVERIFY(button.isDown()); + QCOMPARE(pressedSpy.size(), 1); + QCOMPARE(releasedSpy.size(), 0); + QCOMPARE(clickedSpy.size(), 0); + qApp->processEvents(QEventLoop::AllEvents, 10); + // QAbstractButton starts a 100ms timer which performs the click. If it + // took more than 100ms to get here, then the button might no longer be down. + if (elapsed.elapsed() < 100) { + QVERIFY(button.isDown()); + QCOMPARE(pressedSpy.size(), 1); + QCOMPARE(releasedSpy.size(), 0); + QCOMPARE(clickedSpy.size(), 0); + } + QTRY_VERIFY(!button.isDown()); + // but once the button has been clicked, it must have taken at least 100ms + QVERIFY(elapsed.elapsed() >= 100); + QCOMPARE(pressedSpy.size(), 1); + QCOMPARE(releasedSpy.size(), 1); + QCOMPARE(clickedSpy.size(), 1); } #if QT_CONFIG(shortcut) @@ -541,9 +557,9 @@ void tst_QAbstractButton::shortcutEvents() QTest::qWait(1000); // ensure animate timer is expired - QCOMPARE(pressedSpy.count(), 3); - QCOMPARE(releasedSpy.count(), 3); - QCOMPARE(clickedSpy.count(), 3); + QCOMPARE(pressedSpy.size(), 3); + QCOMPARE(releasedSpy.size(), 3); + QCOMPARE(clickedSpy.size(), 3); } #endif // QT_CONFIG(shortcut) @@ -585,26 +601,26 @@ void tst_QAbstractButton::mouseReleased() // QTBUG-53244 QSignalSpy spyRelease(&button, &QAbstractButton::released); QTest::mousePress(&button, Qt::LeftButton); - QCOMPARE(spyPress.count(), 1); + QCOMPARE(spyPress.size(), 1); QCOMPARE(button.isDown(), true); - QCOMPARE(spyRelease.count(), 0); + QCOMPARE(spyRelease.size(), 0); QTest::mouseClick(&button, Qt::RightButton); - QCOMPARE(spyPress.count(), 1); + QCOMPARE(spyPress.size(), 1); QCOMPARE(button.isDown(), true); - QCOMPARE(spyRelease.count(), 0); + QCOMPARE(spyRelease.size(), 0); QPointF posOutOfWidget = QPointF(30, 30); QMouseEvent me(QEvent::MouseMove, - posOutOfWidget, Qt::NoButton, - Qt::MouseButtons(Qt::LeftButton), - Qt::NoModifier); // mouse press and move + posOutOfWidget, button.mapToGlobal(posOutOfWidget), + Qt::NoButton, Qt::MouseButtons(Qt::LeftButton), + Qt::NoModifier); // mouse press and move qApp->sendEvent(&button, &me); // should emit released signal once mouse is dragging out of boundary - QCOMPARE(spyPress.count(), 1); + QCOMPARE(spyPress.size(), 1); QCOMPARE(button.isDown(), false); - QCOMPARE(spyRelease.count(), 1); + QCOMPARE(spyRelease.size(), 1); } #ifdef QT_KEYPAD_NAVIGATION @@ -624,7 +640,7 @@ void tst_QAbstractButton::keyNavigation() } widget.show(); - qApp->setActiveWindow(&widget); + QApplicationPrivate::setActiveWindow(&widget); widget.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&widget)); @@ -664,5 +680,16 @@ void tst_QAbstractButton::keyNavigation() } #endif +void tst_QAbstractButton::buttonPressKeys() +{ + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value<QList<Qt::Key>>(); + for (uint i = 0; i < buttonPressKeys.size(); ++i) { + QTest::keyClick(testWidget, buttonPressKeys[i]); + QCOMPARE(click_count, i + 1); + } +} + QTEST_MAIN(tst_QAbstractButton) #include "tst_qabstractbutton.moc" diff --git a/tests/auto/widgets/widgets/qabstractscrollarea/CMakeLists.txt b/tests/auto/widgets/widgets/qabstractscrollarea/CMakeLists.txt index d756d28952..ac1d8ad54a 100644 --- a/tests/auto/widgets/widgets/qabstractscrollarea/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qabstractscrollarea/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qabstractscrollarea.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qabstractscrollarea Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qabstractscrollarea LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qabstractscrollarea SOURCES tst_qabstractscrollarea.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/widgets/qabstractscrollarea/tst_qabstractscrollarea.cpp b/tests/auto/widgets/widgets/qabstractscrollarea/tst_qabstractscrollarea.cpp index de57769793..fa1f799855 100644 --- a/tests/auto/widgets/widgets/qabstractscrollarea/tst_qabstractscrollarea.cpp +++ b/tests/auto/widgets/widgets/qabstractscrollarea/tst_qabstractscrollarea.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -37,6 +12,8 @@ #include <qlabel.h> #include <qwidget.h> #include <qdialog.h> +#include <qscroller.h> +#include <qstyle.h> class tst_QAbstractScrollArea : public QObject { @@ -57,6 +34,8 @@ private slots: void task214488_layoutDirection(); void margins(); + void resizeWithOvershoot(); + void sizeHint(); }; tst_QAbstractScrollArea::tst_QAbstractScrollArea() @@ -394,5 +373,93 @@ void tst_QAbstractScrollArea::margins() QCOMPARE(area.viewportMargins(), margins); } +void tst_QAbstractScrollArea::resizeWithOvershoot() +{ + QWidget window; + + QScrollArea scrollArea(&window); + scrollArea.setWidget([]{ + QWidget *widget = new QWidget; + widget->setFixedSize(QSize(0, 200)); + return widget; + }()); + scrollArea.setGeometry(0, 20, 100, 100); + + QScroller::grabGesture(&scrollArea, QScroller::LeftMouseButtonGesture); + + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + const QPoint originAtRest = scrollArea.viewport()->pos(); + + QPoint center = scrollArea.viewport()->mapToGlobal(scrollArea.viewport()->rect().center()); + center = window.windowHandle()->mapFromGlobal(center); + QTest::mousePress(window.windowHandle(), Qt::LeftButton, {}, center); + QTest::mouseMove(window.windowHandle(), center + QPoint(0, 50)); + QTRY_COMPARE(scrollArea.viewport()->pos(), originAtRest + QPoint(0, 25)); + QPoint overshootPosition = scrollArea.viewport()->pos(); + + // trigger a layout of the scroll area while there's overshoot + scrollArea.setGeometry(0, 0, 100, 120); + QCOMPARE(scrollArea.viewport()->pos(), overshootPosition); + QTest::mouseRelease(window.windowHandle(), Qt::LeftButton, {}, center + QPoint(0, 50)); + QTRY_COMPARE(scrollArea.viewport()->pos(), originAtRest); + // Process a few more events and verify that the scroll area + // doesn't overcompensate for the overshoot. + QApplication::processEvents(); + QTRY_COMPARE(scrollArea.viewport()->pos(), originAtRest); +} + +void tst_QAbstractScrollArea::sizeHint() +{ + class ScrollArea : public QAbstractScrollArea + { + public: + QSize viewportSizeHint() const override { return {200, 200}; } + } scrollArea; + // We cannot reliable test the impact of the scrollbars on the size hint + // if the style uses transient scrollbars, so use the class Windows style. + const QString defaultStyle = QApplication::style()->name(); + QApplication::setStyle("Windows"); + auto resetStyle = qScopeGuard([defaultStyle]{ + QApplication::setStyle(defaultStyle); + }); + scrollArea.setFrameShape(QFrame::NoFrame); + scrollArea.setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); + scrollArea.show(); + + QSize sizeHint = scrollArea.sizeHint(); + QCOMPARE(sizeHint, scrollArea.viewportSizeHint()); + + scrollArea.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + const QSize sizeHintWithScrollBars = scrollArea.sizeHint(); + QTRY_COMPARE_GT(sizeHintWithScrollBars.width(), sizeHint.width()); + QTRY_COMPARE_GT(sizeHintWithScrollBars.height(), sizeHint.height()); + + sizeHint = scrollArea.sizeHint(); + + // whether the scroll area itself is visible or not should not influence + // the size hint + scrollArea.hide(); + QCOMPARE(scrollArea.sizeHint(), sizeHint); + scrollArea.show(); + QCOMPARE(scrollArea.sizeHint(), sizeHint); + + scrollArea.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + QCOMPARE(scrollArea.sizeHint(), scrollArea.viewportSizeHint()); + + scrollArea.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + + scrollArea.verticalScrollBar()->setRange(0, 1); + scrollArea.horizontalScrollBar()->setRange(0, 1); + scrollArea.resize(sizeHint / 2); + QApplication::processEvents(); // trigger lazy layout process + QCOMPARE(scrollArea.sizeHint(), sizeHintWithScrollBars); +} + QTEST_MAIN(tst_QAbstractScrollArea) #include "tst_qabstractscrollarea.moc" diff --git a/tests/auto/widgets/widgets/qabstractslider/CMakeLists.txt b/tests/auto/widgets/widgets/qabstractslider/CMakeLists.txt index 37c725c391..711c73931d 100644 --- a/tests/auto/widgets/widgets/qabstractslider/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qabstractslider/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qabstractslider.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qabstractslider Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qabstractslider LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qabstractslider SOURCES tst_qabstractslider.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::TestPrivate Qt::Widgets diff --git a/tests/auto/widgets/widgets/qabstractslider/tst_qabstractslider.cpp b/tests/auto/widgets/widgets/qabstractslider/tst_qabstractslider.cpp index 7ab609a7ca..9be41ad799 100644 --- a/tests/auto/widgets/widgets/qabstractslider/tst_qabstractslider.cpp +++ b/tests/auto/widgets/widgets/qabstractslider/tst_qabstractslider.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -1484,7 +1459,7 @@ void tst_QAbstractSlider::keyPressed() slider->setOrientation(Qt::Horizontal); slider->setInvertedAppearance(invertedAppearance); slider->setInvertedControls(invertedControls); - for (int i=0;i<keySequence.count();i++) { + for (int i=0;i<keySequence.size();i++) { QTest::keyClick(slider, keySequence.at(i)); } QCOMPARE(slider->sliderPosition(), expectedSliderPosition); @@ -1691,8 +1666,8 @@ void tst_QAbstractSlider::wheelEvent() #endif QCOMPARE(slider->sliderPosition(),expectedSliderPosition); int expectedSignalCount = (initialSliderPosition == expectedSliderPosition) ? 0 : 1; - QCOMPARE(spy1.count(), expectedSignalCount); - QCOMPARE(spy2.count(), expectedSignalCount); + QCOMPARE(spy1.size(), expectedSignalCount); + QCOMPARE(spy2.size(), expectedSignalCount); if (expectedSignalCount) QVERIFY(actionTriggeredTimeStamp < valueChangedTimeStamp); } @@ -1839,9 +1814,9 @@ void tst_QAbstractSlider::sliderPressedReleased() QTest::mousePress(slider, Qt::LeftButton, {}, QPoint(rect.center().x() + 2, rect.center().y() + 2)); - QCOMPARE(spy1.count(), expectedCount); + QCOMPARE(spy1.size(), expectedCount); QTest::mouseRelease(slider, Qt::LeftButton, {}, rect.center()); - QCOMPARE(spy2.count(), expectedCount); + QCOMPARE(spy2.size(), expectedCount); delete slider; } @@ -1910,7 +1885,7 @@ void tst_QAbstractSlider::sliderMoved() slider->setMaximum(maximum); slider->setSliderDown(sliderDown); slider->setSliderPosition(position); - QCOMPARE(spy.count(), expectedCount); + QCOMPARE(spy.size(), expectedCount); delete slider; } @@ -1982,7 +1957,7 @@ void tst_QAbstractSlider::rangeChanged() slider.setRange(minimum, maximum); QSignalSpy spy(&slider, SIGNAL(rangeChanged(int,int))); slider.setRange(newMin, newMax); - QCOMPARE(spy.count(), expectedCount); + QCOMPARE(spy.size(), expectedCount); } void tst_QAbstractSlider::setSliderPosition_data() @@ -2021,8 +1996,8 @@ void tst_QAbstractSlider::setSliderPosition() QSignalSpy spy2(slider, SIGNAL(valueChanged(int))); slider->setSliderPosition(targetPosition); QCOMPARE(slider->sliderPosition(), targetPosition); - QCOMPARE(spy1.count(), down ? 1 : 0); - QCOMPARE(spy2.count(), tracking ? 1 : 0); + QCOMPARE(spy1.size(), down ? 1 : 0); + QCOMPARE(spy2.size(), tracking ? 1 : 0); QCOMPARE(slider->value(), tracking ? targetPosition : initialValue); if (tracking && down) QVERIFY(sliderMovedTimeStamp < valueChangedTimeStamp); @@ -2046,17 +2021,21 @@ void tst_QAbstractSlider::setValue() slider->setRange(minimum, maximum); slider->setSliderDown(down); slider->setValue(49); // to force a valueChanged() below - QSignalSpy spy1(slider, SIGNAL(sliderMoved(int))); - QSignalSpy spy2(slider, SIGNAL(valueChanged(int))); - QSignalSpy spy3(slider, SIGNAL(actionTriggered(int))); + QSignalSpy spy1(slider, &QAbstractSlider::sliderMoved); + QSignalSpy spy2(slider, &QAbstractSlider::valueChanged); + QSignalSpy spy3(slider, &QAbstractSlider::actionTriggered); slider->setValue(50); - QCOMPARE(spy1.count(), down ? 1 : 0); - QCOMPARE(spy2.count(), 1); - QCOMPARE(spy3.count(), 0); + QCOMPARE(spy1.size(), down ? 1 : 0); + QCOMPARE(spy2.size(), 1); + QCOMPARE(spy3.size(), 0); QCOMPARE(slider->value(), reportedValue); QCOMPARE(slider->sliderPosition(), reportedSliderPosition); if (down) QVERIFY(sliderMovedTimeStamp < valueChangedTimeStamp); + + slider->setValue(50); + QApplication::processEvents(); + QCOMPARE(spy2.size(), 1); } void tst_QAbstractSlider::waitUntilTimeElapsed(const QElapsedTimer &t, int ms) @@ -2076,37 +2055,37 @@ void tst_QAbstractSlider::setRepeatAction() // Start repeat action with initial delay of 500 ms, and then repeating // every 250 ms. slider->setRepeatAction(QAbstractSlider::SliderPageStepAdd, 500, 250); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QCOMPARE(slider->value(), 55); QElapsedTimer t; t.start(); QTest::qWait(300); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QCOMPARE(slider->value(), 55); waitUntilTimeElapsed(t, 550); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(slider->value(), 65); QCOMPARE(spy.at(0).at(0).toUInt(), (uint)QAbstractSlider::SliderPageStepAdd); waitUntilTimeElapsed(t, 790); - QTRY_COMPARE(spy.count(), 2); + QTRY_COMPARE(spy.size(), 2); QCOMPARE(slider->value(), 75); QCOMPARE(spy.at(1).at(0).toUInt(), (uint)QAbstractSlider::SliderPageStepAdd); waitUntilTimeElapsed(t, 1790); - QTRY_COMPARE(spy.count(), 6); + QTRY_COMPARE(spy.size(), 6); QCOMPARE(slider->value(), 115); QCOMPARE(spy.at(4).at(0).toUInt(), (uint)QAbstractSlider::SliderPageStepAdd); QCOMPARE(spy.at(5).at(0).toUInt(), (uint)QAbstractSlider::SliderPageStepAdd); slider->setRepeatAction(QAbstractSlider::SliderNoAction); - QCOMPARE(spy.count(), 6); + QCOMPARE(spy.size(), 6); QCOMPARE(slider->value(), 115); QTest::qWait(300); - QCOMPARE(spy.count(), 6); + QCOMPARE(spy.size(), 6); QCOMPARE(slider->value(), 115); } diff --git a/tests/auto/widgets/widgets/qabstractspinbox/CMakeLists.txt b/tests/auto/widgets/widgets/qabstractspinbox/CMakeLists.txt index df077a0bbd..adaef01601 100644 --- a/tests/auto/widgets/widgets/qabstractspinbox/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qabstractspinbox/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qabstractspinbox.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qabstractspinbox Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qabstractspinbox LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qabstractspinbox SOURCES tst_qabstractspinbox.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/widgets/qabstractspinbox/tst_qabstractspinbox.cpp b/tests/auto/widgets/widgets/qabstractspinbox/tst_qabstractspinbox.cpp index 6898cf18dc..00d0fdaf94 100644 --- a/tests/auto/widgets/widgets/qabstractspinbox/tst_qabstractspinbox.cpp +++ b/tests/auto/widgets/widgets/qabstractspinbox/tst_qabstractspinbox.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -39,15 +14,12 @@ #include "../../../shared/platforminputcontext.h" #include <private/qinputmethod_p.h> +#include <memory> class tst_QAbstractSpinBox : public QObject { Q_OBJECT -public: - tst_QAbstractSpinBox(); - virtual ~tst_QAbstractSpinBox(); - private slots: void initTestCase(); void cleanupTestCase(); @@ -64,32 +36,25 @@ private: PlatformInputContext m_platformInputContext; }; -tst_QAbstractSpinBox::tst_QAbstractSpinBox() -{ -} - -tst_QAbstractSpinBox::~tst_QAbstractSpinBox() -{ -} - class MyAbstractSpinBox : public QAbstractSpinBox { public: - MyAbstractSpinBox() : QAbstractSpinBox() {} - QLineEdit *lineEdit() { return QAbstractSpinBox::lineEdit(); } - void setLineEdit(QLineEdit *le) { QAbstractSpinBox::setLineEdit(le); } + using QAbstractSpinBox::QAbstractSpinBox; + + using QAbstractSpinBox::lineEdit; + using QAbstractSpinBox::setLineEdit; }; void tst_QAbstractSpinBox::initTestCase() { - QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod()); + auto *inputMethodPrivate = QInputMethodPrivate::get(QGuiApplication::inputMethod()); inputMethodPrivate->testContext = &m_platformInputContext; } void tst_QAbstractSpinBox::cleanupTestCase() { - QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod()); - inputMethodPrivate->testContext = 0; + auto *inputMethodPrivate = QInputMethodPrivate::get(QGuiApplication::inputMethod()); + inputMethodPrivate->testContext = nullptr; } // Testing get/set functions @@ -112,11 +77,12 @@ void tst_QAbstractSpinBox::getSetCheck() // QLineEdit * QAbstractSpinBox::lineEdit() // void QAbstractSpinBox::setLineEdit(QLineEdit *) - QLineEdit *var3 = new QLineEdit(0); + auto *var3 = new QLineEdit(nullptr); obj1.setLineEdit(var3); QCOMPARE(var3, obj1.lineEdit()); -#ifndef QT_DEBUG - obj1.setLineEdit((QLineEdit *)0); // Will assert in debug, so only test in release + // Will assert in debug, so only test in release +#if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS) + obj1.setLineEdit(nullptr); QCOMPARE(var3, obj1.lineEdit()); // Setting 0 should keep the current editor #endif // delete var3; // No delete, since QAbstractSpinBox takes ownership @@ -124,45 +90,38 @@ void tst_QAbstractSpinBox::getSetCheck() void tst_QAbstractSpinBox::task183108_clear() { - QAbstractSpinBox *sbox; - - sbox = new QSpinBox; + auto sbox = std::make_unique<QSpinBox>(); sbox->clear(); sbox->show(); - qApp->processEvents(); + QCoreApplication::processEvents(); QVERIFY(sbox->text().isEmpty()); - delete sbox; - sbox = new QSpinBox; + sbox.reset(new QSpinBox); sbox->clear(); sbox->show(); sbox->hide(); - qApp->processEvents(); + QCoreApplication::processEvents(); QCOMPARE(sbox->text(), QString()); - delete sbox; - sbox = new QSpinBox; + sbox.reset(new QSpinBox); sbox->show(); sbox->clear(); - qApp->processEvents(); + QCoreApplication::processEvents(); QCOMPARE(sbox->text(), QString()); - delete sbox; - sbox = new QSpinBox; + sbox.reset(new QSpinBox); sbox->show(); sbox->clear(); sbox->hide(); - qApp->processEvents(); + QCoreApplication::processEvents(); QCOMPARE(sbox->text(), QString()); - - delete sbox; } void tst_QAbstractSpinBox::task228728_cssselector() { //QAbstractSpinBox does some call to stylehint into his constructor. //so while the stylesheet want to access property, it should not crash - qApp->setStyleSheet("[alignment=\"1\"], [text=\"foo\"] { color:black; }" ); + qApp->setStyleSheet(R"([alignment="1"], [text="foo"] { color:black; })"); QSpinBox box; } @@ -173,24 +132,23 @@ void tst_QAbstractSpinBox::inputMethodUpdate() QSpinBox box; - QSpinBox *testWidget = &box; - testWidget->setRange(0, 1); + box.setRange(0, 1); - QTestPrivate::centerOnScreen(testWidget); - testWidget->clear(); - testWidget->show(); - QVERIFY(QTest::qWaitForWindowExposed(testWidget)); + QTestPrivate::centerOnScreen(&box); + box.clear(); + box.show(); + QVERIFY(QTest::qWaitForWindowExposed(&box)); - testWidget->activateWindow(); - testWidget->setFocus(); - QTRY_VERIFY(testWidget->hasFocus()); - QTRY_COMPARE(qApp->focusObject(), testWidget); + box.activateWindow(); + box.setFocus(); + QTRY_VERIFY(box.hasFocus()); + QTRY_COMPARE(QGuiApplication::focusObject(), &box); m_platformInputContext.m_updateCallCount = 0; { QList<QInputMethodEvent::Attribute> attributes; QInputMethodEvent event("1", attributes); - QApplication::sendEvent(testWidget, &event); + QCoreApplication::sendEvent(&box, &event); } QVERIFY(m_platformInputContext.m_updateCallCount >= 1); @@ -199,7 +157,7 @@ void tst_QAbstractSpinBox::inputMethodUpdate() QList<QInputMethodEvent::Attribute> attributes; attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()); QInputMethodEvent event("1", attributes); - QApplication::sendEvent(testWidget, &event); + QCoreApplication::sendEvent(&box, &event); } QVERIFY(m_platformInputContext.m_updateCallCount >= 1); @@ -208,17 +166,17 @@ void tst_QAbstractSpinBox::inputMethodUpdate() QList<QInputMethodEvent::Attribute> attributes; QInputMethodEvent event("", attributes); event.setCommitString("1"); - QApplication::sendEvent(testWidget, &event); + QCoreApplication::sendEvent(&box, &event); } QVERIFY(m_platformInputContext.m_updateCallCount >= 1); - QCOMPARE(testWidget->value(), 1); + QCOMPARE(box.value(), 1); m_platformInputContext.m_updateCallCount = 0; { QList<QInputMethodEvent::Attribute> attributes; attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 0, QVariant()); QInputMethodEvent event("", attributes); - QApplication::sendEvent(testWidget, &event); + QCoreApplication::sendEvent(&box, &event); } QVERIFY(m_platformInputContext.m_updateCallCount >= 1); } diff --git a/tests/auto/widgets/widgets/qbuttongroup/CMakeLists.txt b/tests/auto/widgets/widgets/qbuttongroup/CMakeLists.txt index 1f876ec5d6..b58775d2ff 100644 --- a/tests/auto/widgets/widgets/qbuttongroup/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qbuttongroup/CMakeLists.txt @@ -1,13 +1,21 @@ -# Generated from qbuttongroup.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qbuttongroup Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qbuttongroup LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qbuttongroup SOURCES tst_qbuttongroup.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/widgets/qbuttongroup/tst_qbuttongroup.cpp b/tests/auto/widgets/widgets/qbuttongroup/tst_qbuttongroup.cpp index 8ab2faab43..3ff748ef27 100644 --- a/tests/auto/widgets/widgets/qbuttongroup/tst_qbuttongroup.cpp +++ b/tests/auto/widgets/widgets/qbuttongroup/tst_qbuttongroup.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -44,6 +19,8 @@ #include <qsettings.h> #endif +#include <QtWidgets/private/qapplication_p.h> + class SpecialRadioButton: public QRadioButton { public: @@ -145,7 +122,6 @@ void tst_QButtonGroup::arrowKeyNavigation() layout.addWidget(&g2); dlg.show(); - qApp->setActiveWindow(&dlg); QVERIFY(QTest::qWaitForWindowActive(&dlg)); bt1.setFocus(); @@ -227,7 +203,6 @@ void tst_QButtonGroup::keyNavigationPushButtons() buttonGroup->addButton(pb3); dlg.show(); - qApp->setActiveWindow(&dlg); if (!QTest::qWaitForWindowActive(&dlg)) QSKIP("Window activation failed, skipping test"); @@ -374,17 +349,17 @@ void tst_QButtonGroup::testSignals() pb1.animateClick(); QTestEventLoop::instance().enterLoop(1); - QCOMPARE(clickedSpy.count(), 1); - QCOMPARE(clickedIdSpy.count(), 1); + QCOMPARE(clickedSpy.size(), 1); + QCOMPARE(clickedIdSpy.size(), 1); int expectedId = -2; QCOMPARE(clickedIdSpy.takeFirst().at(0).toInt(), expectedId); - QCOMPARE(pressedSpy.count(), 1); - QCOMPARE(pressedIdSpy.count(), 1); + QCOMPARE(pressedSpy.size(), 1); + QCOMPARE(pressedIdSpy.size(), 1); QCOMPARE(pressedIdSpy.takeFirst().at(0).toInt(), expectedId); - QCOMPARE(releasedSpy.count(), 1); - QCOMPARE(releasedIdSpy.count(), 1); + QCOMPARE(releasedSpy.size(), 1); + QCOMPARE(releasedIdSpy.size(), 1); QCOMPARE(releasedIdSpy.takeFirst().at(0).toInt(), expectedId); clickedSpy.clear(); @@ -397,14 +372,14 @@ void tst_QButtonGroup::testSignals() pb2.animateClick(); QTestEventLoop::instance().enterLoop(1); - QCOMPARE(clickedSpy.count(), 1); - QCOMPARE(clickedIdSpy.count(), 1); + QCOMPARE(clickedSpy.size(), 1); + QCOMPARE(clickedIdSpy.size(), 1); QCOMPARE(clickedIdSpy.takeFirst().at(0).toInt(), 23); - QCOMPARE(pressedSpy.count(), 1); - QCOMPARE(pressedIdSpy.count(), 1); + QCOMPARE(pressedSpy.size(), 1); + QCOMPARE(pressedIdSpy.size(), 1); QCOMPARE(pressedIdSpy.takeFirst().at(0).toInt(), 23); - QCOMPARE(releasedSpy.count(), 1); - QCOMPARE(releasedIdSpy.count(), 1); + QCOMPARE(releasedSpy.size(), 1); + QCOMPARE(releasedIdSpy.size(), 1); QCOMPARE(releasedIdSpy.takeFirst().at(0).toInt(), 23); @@ -414,18 +389,18 @@ void tst_QButtonGroup::testSignals() pb1.setCheckable(true); pb2.setCheckable(true); pb1.toggle(); - QCOMPARE(toggledSpy.count(), 1); - QCOMPARE(toggledIdSpy.count(), 1); + QCOMPARE(toggledSpy.size(), 1); + QCOMPARE(toggledIdSpy.size(), 1); pb2.toggle(); - QCOMPARE(toggledSpy.count(), 3); // equals 3 since pb1 and pb2 are both toggled - QCOMPARE(toggledIdSpy.count(), 3); + QCOMPARE(toggledSpy.size(), 3); // equals 3 since pb1 and pb2 are both toggled + QCOMPARE(toggledIdSpy.size(), 3); pb1.setCheckable(false); pb2.setCheckable(false); pb1.toggle(); - QCOMPARE(toggledSpy.count(), 3); - QCOMPARE(toggledIdSpy.count(), 3); + QCOMPARE(toggledSpy.size(), 3); + QCOMPARE(toggledIdSpy.size(), 3); } void tst_QButtonGroup::task106609() @@ -458,14 +433,13 @@ void tst_QButtonGroup::task106609() qRegisterMetaType<QAbstractButton*>("QAbstractButton*"); QSignalSpy spy1(buttons, SIGNAL(buttonClicked(QAbstractButton*))); - QApplication::setActiveWindow(&dlg); QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&dlg)); radio1->setFocus(); radio1->setChecked(true); QTestEventLoop::instance().enterLoop(1); - QCOMPARE(spy1.count(), 2); + QCOMPARE(spy1.size(), 2); } void tst_QButtonGroup::checkedButton() @@ -550,7 +524,7 @@ void tst_QButtonGroup::task209485_removeFromGroupInEventHandler() // NOTE: Reintroducing the bug of this task will cause the following line to crash: QTest::mouseClick(button, Qt::LeftButton); - QCOMPARE(spy1.count(), signalCount); + QCOMPARE(spy1.size(), signalCount); } void tst_QButtonGroup::autoIncrementId() diff --git a/tests/auto/widgets/widgets/qcalendarwidget/CMakeLists.txt b/tests/auto/widgets/widgets/qcalendarwidget/CMakeLists.txt index 853ff95a03..5140c37e94 100644 --- a/tests/auto/widgets/widgets/qcalendarwidget/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qcalendarwidget/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qcalendarwidget.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qcalendarwidget Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcalendarwidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qcalendarwidget SOURCES tst_qcalendarwidget.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/widgets/qcalendarwidget/tst_qcalendarwidget.cpp b/tests/auto/widgets/widgets/qcalendarwidget/tst_qcalendarwidget.cpp index 91da5dc69b..ca5cc06e99 100644 --- a/tests/auto/widgets/widgets/qcalendarwidget/tst_qcalendarwidget.cpp +++ b/tests/auto/widgets/widgets/qcalendarwidget/tst_qcalendarwidget.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -109,7 +84,7 @@ void tst_QCalendarWidget::getSetCheck() QDate selectedDate(2005, 7, 3); QSignalSpy spy(&object, SIGNAL(selectionChanged())); object.setSelectedDate(selectedDate); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(selectedDate, object.selectedDate()); //month and year object.setCurrentPage(2004, 1); @@ -135,11 +110,19 @@ void tst_QCalendarWidget::getSetCheck() object.setSelectedDate(selectedDate); QCOMPARE(minDate, object.selectedDate()); QVERIFY(selectedDate != object.selectedDate()); + object.clearMinimumDate(); + object.setSelectedDate(selectedDate); + QCOMPARE(selectedDate, object.selectedDate()); + //date should not go beyond the maximum. selectedDate = maxDate.addDays(10); object.setSelectedDate(selectedDate); QCOMPARE(maxDate, object.selectedDate()); QVERIFY(selectedDate != object.selectedDate()); + object.clearMaximumDate(); + object.setSelectedDate(selectedDate); + QCOMPARE(selectedDate, object.selectedDate()); + //show today QDate today = QDate::currentDate(); object.showToday(); @@ -290,6 +273,9 @@ void tst_QCalendarWidget::showPrevNext() QFETCH(ShowFunc, function); QFETCH(QDate, dateOrigin); QFETCH(QDate, expectedDate); +#ifdef Q_OS_ANDROID + QSKIP("Crashes sometimes on Android emulator, figure out why (QTBUG-102258)"); +#endif QCalendarWidget calWidget; calWidget.show(); diff --git a/tests/auto/widgets/widgets/qcheckbox/CMakeLists.txt b/tests/auto/widgets/widgets/qcheckbox/CMakeLists.txt index df03bea079..9e3f0e9874 100644 --- a/tests/auto/widgets/widgets/qcheckbox/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qcheckbox/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qcheckbox.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qcheckbox Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcheckbox LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qcheckbox SOURCES tst_qcheckbox.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/widgets/qcheckbox/tst_qcheckbox.cpp b/tests/auto/widgets/widgets/qcheckbox/tst_qcheckbox.cpp index 33fb6b482a..42eb81d3c7 100644 --- a/tests/auto/widgets/widgets/qcheckbox/tst_qcheckbox.cpp +++ b/tests/auto/widgets/widgets/qcheckbox/tst_qcheckbox.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -41,6 +16,7 @@ private slots: void initTestCase(); void setChecked(); + void setCheckedSignal(); void setTriState(); void setText_data(); void setText(); @@ -50,7 +26,7 @@ private slots: void toggle(); void pressed(); void toggled(); - void stateChanged(); + void checkStateChanged(); void foregroundRole(); void minimumSizeHint(); }; @@ -84,6 +60,25 @@ void tst_QCheckBox::setChecked() QVERIFY(!testWidget.isChecked()); } +void tst_QCheckBox::setCheckedSignal() +{ + QCheckBox testWidget; + testWidget.setCheckState(Qt::Unchecked); + QSignalSpy checkStateChangedSpy(&testWidget, &QCheckBox::checkStateChanged); + testWidget.setCheckState(Qt::Checked); + testWidget.setCheckState(Qt::Checked); + QTRY_COMPARE(checkStateChangedSpy.size(), 1); // get signal only once + QCOMPARE(testWidget.checkState(), Qt::Checked); + testWidget.setCheckState(Qt::Unchecked); + testWidget.setCheckState(Qt::Unchecked); + QTRY_COMPARE(checkStateChangedSpy.size(), 2); // get signal only once + QCOMPARE(testWidget.checkState(), Qt::Unchecked); + testWidget.setCheckState(Qt::PartiallyChecked); + testWidget.setCheckState(Qt::PartiallyChecked); + QTRY_COMPARE(checkStateChangedSpy.size(), 3); // get signal only once + QCOMPARE(testWidget.checkState(), Qt::PartiallyChecked); +} + void tst_QCheckBox::setTriState() { QCheckBox testWidget; @@ -213,30 +208,49 @@ void tst_QCheckBox::toggled() QCOMPARE(click_count, 0); } -void tst_QCheckBox::stateChanged() +void tst_QCheckBox::checkStateChanged() { QCheckBox testWidget; - int cur_state = -1; + QCOMPARE(testWidget.checkState(), Qt::Unchecked); + + Qt::CheckState cur_state = Qt::Unchecked; + QSignalSpy checkStateChangedSpy(&testWidget, &QCheckBox::checkStateChanged); +#if QT_DEPRECATED_SINCE(6, 9) + QT_IGNORE_DEPRECATIONS( QSignalSpy stateChangedSpy(&testWidget, &QCheckBox::stateChanged); - connect(&testWidget, &QCheckBox::stateChanged, this, [&](int state) { ++cur_state = state; }); + ) +#endif + connect(&testWidget, &QCheckBox::checkStateChanged, this, [&](auto state) { cur_state = state; }); testWidget.setChecked(true); - QCoreApplication::processEvents(); - QCOMPARE(cur_state, 2); + QTRY_COMPARE(checkStateChangedSpy.size(), 1); +#if QT_DEPRECATED_SINCE(6, 9) + QCOMPARE(stateChangedSpy.size(), 1); +#endif + QCOMPARE(cur_state, Qt::Checked); + QCOMPARE(testWidget.checkState(), Qt::Checked); - cur_state = -1; testWidget.setChecked(false); - QCoreApplication::processEvents(); - QCOMPARE(cur_state, 0); + QTRY_COMPARE(checkStateChangedSpy.size(), 2); +#if QT_DEPRECATED_SINCE(6, 9) + QCOMPARE(stateChangedSpy.size(), 2); +#endif + QCOMPARE(cur_state, Qt::Unchecked); + QCOMPARE(testWidget.checkState(), Qt::Unchecked); - cur_state = -1; testWidget.setCheckState(Qt::PartiallyChecked); - QCoreApplication::processEvents(); - QCOMPARE(cur_state, 1); + QTRY_COMPARE(checkStateChangedSpy.size(), 3); +#if QT_DEPRECATED_SINCE(6, 9) + QCOMPARE(stateChangedSpy.size(), 3); +#endif + QCOMPARE(cur_state, Qt::PartiallyChecked); + QCOMPARE(testWidget.checkState(), Qt::PartiallyChecked); - QCOMPARE(stateChangedSpy.count(), 3); testWidget.setCheckState(Qt::PartiallyChecked); QCoreApplication::processEvents(); - QCOMPARE(stateChangedSpy.count(), 3); + QCOMPARE(checkStateChangedSpy.size(), 3); +#if QT_DEPRECATED_SINCE(6, 9) + QCOMPARE(stateChangedSpy.size(), 3); +#endif } void tst_QCheckBox::isToggleButton() diff --git a/tests/auto/widgets/widgets/qcombobox/CMakeLists.txt b/tests/auto/widgets/widgets/qcombobox/CMakeLists.txt index a1dded1384..87ac247b24 100644 --- a/tests/auto/widgets/widgets/qcombobox/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qcombobox/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qcombobox.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qcombobox Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcombobox LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data list(APPEND test_data "qtlogo.png") list(APPEND test_data "qtlogoinverted.png") @@ -13,7 +20,7 @@ qt_internal_add_test(tst_qcombobox tst_qcombobox.cpp DEFINES QTEST_QPA_MOUSE_HANDLING - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp index 909b0b4aaa..66c1359bd8 100644 --- a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp +++ b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSignalSpy> @@ -33,6 +8,7 @@ #include "qcombobox.h" #include <private/qcombobox_p.h> #include <private/qguiapplication_p.h> +#include <qpa/qplatformintegration.h> #include <qpa/qplatformtheme.h> #include <qfontcombobox.h> @@ -49,6 +25,7 @@ #include <qtablewidget.h> #include <qscrollbar.h> #include <qboxlayout.h> +#include <qshortcut.h> #include <qstackedwidget.h> #include <qstandarditemmodel.h> @@ -65,11 +42,15 @@ #include <qstandarditemmodel.h> #include <qproxystyle.h> #include <qfont.h> +#include <qstylehints.h> #include "../../../shared/platforminputcontext.h" #include <private/qinputmethod_p.h> #include <QtTest/private/qtesthelpers_p.h> +#include <QtTest/private/qemulationdetector_p.h> + +#include <QtWidgets/private/qapplication_p.h> using namespace QTestPrivate; @@ -130,6 +111,7 @@ private slots: #ifndef QT_NO_STYLE_FUSION void task190351_layout(); void task191329_size(); + void popupPositionAfterStyleChange(); #endif void task166349_setEditableOnReturn(); void task190205_setModelAdjustToContents(); @@ -169,6 +151,11 @@ private slots: void task_QTBUG_52027_mapCompleterIndex(); void checkMenuItemPosWhenStyleSheetIsSet(); void checkEmbeddedLineEditWhenStyleSheetIsSet(); + void propagateStyleChanges(); + void buttonPressKeys(); + void clearModel(); + void cancelClosesPopupNotDialog(); + void closePopupWithCheckableItems(); private: PlatformInputContext m_platformInputContext; @@ -471,11 +458,19 @@ void tst_QComboBox::setEditable() void tst_QComboBox::setPalette() { -#ifdef Q_OS_MAC - if (QApplication::style()->inherits("QMacStyle")) { - QSKIP("This test doesn't make sense for pixmap-based styles"); - } -#endif + // If (a) Palettes are pixmap based and/or (b) contain color groups/roles which the + // resolve mask prevents from being copied, the direct comparison of the inherited + // palette and the parent palette will fail. + // To prevent that, set a simple gray system palette for QComboBox and QLineEdit + const QPalette comboBoxPalette = qApp->palette("QComboBox"); + const QPalette lineEditPalette = qApp->palette("QLineEdit"); + auto guard = qScopeGuard([&]{ + qApp->setPalette(comboBoxPalette, "QComboBox"); + qApp->setPalette(lineEditPalette, "QLineEdit"); + }); + qApp->setPalette(QPalette(Qt::gray), "QComboBox"); + qApp->setPalette(QPalette(Qt::gray), "QLineEdit"); + TestWidget topLevel; topLevel.show(); QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); @@ -483,16 +478,15 @@ void tst_QComboBox::setPalette() QPalette pal = testWidget->palette(); pal.setColor(QPalette::Base, Qt::red); testWidget->setPalette(pal); - testWidget->setEditable(!testWidget->isEditable()); + testWidget->setEditable(true); pal.setColor(QPalette::Base, Qt::blue); testWidget->setPalette(pal); - const QObjectList comboChildren = testWidget->children(); - for (int i = 0; i < comboChildren.size(); ++i) { - QObject *o = comboChildren.at(i); - if (o->isWidgetType()) { - QCOMPARE(((QWidget*)o)->palette(), pal); + const QObjectList &comboChildren = testWidget->children(); + for (auto *child : comboChildren) { + if (auto *widget = qobject_cast<QWidget *>(child)) { + QCOMPARE(widget->palette(), pal); } } @@ -769,7 +763,7 @@ void tst_QComboBox::insertPolicy() testWidget->setInsertPolicy(insertPolicy); testWidget->addItems(initialEntries); testWidget->setEditable(true); - if (initialEntries.count() > 0) + if (initialEntries.size() > 0) testWidget->setCurrentIndex(currentIndex); // clear @@ -781,10 +775,10 @@ void tst_QComboBox::insertPolicy() // First check that there is the right number of entries, or // we may unwittingly pass - QCOMPARE((int)result.count(), testWidget->count()); + QCOMPARE((int)result.size(), testWidget->count()); // No need to compare if there are no strings to compare - if (result.count() > 0) { + if (result.size() > 0) { for (int i=0; i<testWidget->count(); ++i) { QCOMPARE(testWidget->itemText(i), result.at(i)); } @@ -855,7 +849,6 @@ void tst_QComboBox::autoCompletionCaseSensitivity() TestWidget topLevel; topLevel.show(); QComboBox *testWidget = topLevel.comboBox(); - qApp->setActiveWindow(&topLevel); testWidget->setFocus(); QVERIFY(QTest::qWaitForWindowActive(&topLevel)); QCOMPARE(qApp->focusWidget(), (QWidget *)testWidget); @@ -881,18 +874,18 @@ void tst_QComboBox::autoCompletionCaseSensitivity() QTest::keyClick(testWidget->lineEdit(), Qt::Key_A); qApp->processEvents(); QCOMPARE(testWidget->currentText(), QString("aww")); - QCOMPARE(spyReturn.count(), 0); + QCOMPARE(spyReturn.size(), 0); QTest::keyClick(testWidget->lineEdit(), Qt::Key_B); qApp->processEvents(); // autocompletions preserve userkey-case from 4.2 QCOMPARE(testWidget->currentText(), QString("abCDEF")); - QCOMPARE(spyReturn.count(), 0); + QCOMPARE(spyReturn.size(), 0); QTest::keyClick(testWidget->lineEdit(), Qt::Key_Enter); qApp->processEvents(); QCOMPARE(testWidget->currentText(), QString("aBCDEF")); // case restored to item's case - QCOMPARE(spyReturn.count(), 1); + QCOMPARE(spyReturn.size(), 1); testWidget->clearEditText(); QTest::keyClick(testWidget->lineEdit(), 'c'); @@ -1028,7 +1021,7 @@ void tst_QComboBox::currentIndex_data() expectedCurrentIndex = -1; expectedCurrentText = ""; expectedSignalCount = 2; - QTest::newRow("check that isetting the index to -1 works") + QTest::newRow("check that setting the index to -1 works") << initialItems << setCurrentIndex << removeIndex << insertIndex << insertText << expectedCurrentIndex << expectedCurrentText << expectedSignalCount; @@ -1171,66 +1164,71 @@ void tst_QComboBox::currentIndex() TestWidget topLevel; topLevel.show(); QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); - QComboBox *testWidget = topLevel.comboBox(); + QComboBox *comboBox = topLevel.comboBox(); // test both editable/non-editable combobox for (int edit = 0; edit < 2; ++edit) { - testWidget->clear(); - testWidget->setEditable(edit ? true : false); + comboBox->clear(); + comboBox->setEditable(edit ? true : false); if (edit) - QVERIFY(testWidget->lineEdit()); + QVERIFY(comboBox->lineEdit()); // verify it is empty, has no current index and no current text - QCOMPARE(testWidget->count(), 0); - QCOMPARE(testWidget->currentIndex(), -1); - QVERIFY(testWidget->currentText().isEmpty()); + QCOMPARE(comboBox->count(), 0); + QCOMPARE(comboBox->currentIndex(), -1); + QVERIFY(comboBox->currentText().isEmpty()); // spy on currentIndexChanged - QSignalSpy indexChangedInt(testWidget, SIGNAL(currentIndexChanged(int))); + QSignalSpy indexChangedSpy(comboBox, &QComboBox::currentIndexChanged); // stuff items into it - foreach(QString text, initialItems) { - testWidget->addItem(text); - } - QCOMPARE(testWidget->count(), initialItems.count()); + for (const QString &text : initialItems) + comboBox->addItem(text); + + QCOMPARE(comboBox->count(), initialItems.size()); // set current index, remove and/or insert if (setCurrentIndex >= -1) { - testWidget->setCurrentIndex(setCurrentIndex); - QCOMPARE(testWidget->currentIndex(), setCurrentIndex); + comboBox->setCurrentIndex(setCurrentIndex); + QCOMPARE(comboBox->currentIndex(), setCurrentIndex); } if (removeIndex >= 0) - testWidget->removeItem(removeIndex); + comboBox->removeItem(removeIndex); if (insertIndex >= 0) - testWidget->insertItem(insertIndex, insertText); + comboBox->insertItem(insertIndex, insertText); // compare with expected index and text - QCOMPARE(testWidget->currentIndex(), expectedCurrentIndex); - QCOMPARE(testWidget->currentText(), expectedCurrentText); + QCOMPARE(comboBox->currentIndex(), expectedCurrentIndex); + QCOMPARE(comboBox->currentText(), expectedCurrentText); // check that signal count is correct - QCOMPARE(indexChangedInt.count(), expectedSignalCount); + QCOMPARE(indexChangedSpy.size(), expectedSignalCount); // compare with last sent signal values - if (indexChangedInt.count()) - QCOMPARE(indexChangedInt.at(indexChangedInt.count() - 1).at(0).toInt(), - testWidget->currentIndex()); + if (indexChangedSpy.size()) + QCOMPARE(indexChangedSpy.at(indexChangedSpy.size() - 1).at(0).toInt(), + comboBox->currentIndex()); + + // Test a no-op index change + const int index = comboBox->currentIndex(); + comboBox->setCurrentIndex(index); + QCOMPARE(indexChangedSpy.size(), expectedSignalCount); if (edit) { - testWidget->setCurrentIndex(-1); - testWidget->setInsertPolicy(QComboBox::InsertAtBottom); - QTest::keyPress(testWidget, 'a'); - QTest::keyPress(testWidget, 'b'); - QCOMPARE(testWidget->currentText(), QString("ab")); - QCOMPARE(testWidget->currentIndex(), -1); - int numItems = testWidget->count(); - QTest::keyPress(testWidget, Qt::Key_Return); - QCOMPARE(testWidget->count(), numItems + 1); - QCOMPARE(testWidget->currentIndex(), numItems); - testWidget->setCurrentIndex(-1); - QTest::keyPress(testWidget, 'a'); - QTest::keyPress(testWidget, 'b'); - QCOMPARE(testWidget->currentIndex(), -1); + comboBox->setCurrentIndex(-1); + comboBox->setInsertPolicy(QComboBox::InsertAtBottom); + QTest::keyPress(comboBox, 'a'); + QTest::keyPress(comboBox, 'b'); + QCOMPARE(comboBox->currentText(), QString("ab")); + QCOMPARE(comboBox->currentIndex(), -1); + int numItems = comboBox->count(); + QTest::keyPress(comboBox, Qt::Key_Return); + QCOMPARE(comboBox->count(), numItems + 1); + QCOMPARE(comboBox->currentIndex(), numItems); + comboBox->setCurrentIndex(-1); + QTest::keyPress(comboBox, 'a'); + QTest::keyPress(comboBox, 'b'); + QCOMPARE(comboBox->currentIndex(), -1); } } } @@ -1250,8 +1248,8 @@ void tst_QComboBox::insertItems_data() QTest::newRow("prepend") << initialItems << insertedItems << 0 << 0; QTest::newRow("prepend with negative value") << initialItems << insertedItems << -1 << 0; - QTest::newRow("append") << initialItems << insertedItems << initialItems.count() << initialItems.count(); - QTest::newRow("append with too high value") << initialItems << insertedItems << 999 << initialItems.count(); + QTest::newRow("append") << initialItems << insertedItems << initialItems.size() << initialItems.size(); + QTest::newRow("append with too high value") << initialItems << insertedItems << 999 << initialItems.size(); QTest::newRow("insert") << initialItems << insertedItems << 1 << 1; } @@ -1267,12 +1265,12 @@ void tst_QComboBox::insertItems() QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); QComboBox *testWidget = topLevel.comboBox(); testWidget->insertItems(0, initialItems); - QCOMPARE(testWidget->count(), initialItems.count()); + QCOMPARE(testWidget->count(), initialItems.size()); testWidget->insertItems(insertIndex, insertedItems); - QCOMPARE(testWidget->count(), initialItems.count() + insertedItems.count()); - for (int i=0; i<insertedItems.count(); ++i) + QCOMPARE(testWidget->count(), initialItems.size() + insertedItems.size()); + for (int i=0; i<insertedItems.size(); ++i) QCOMPARE(testWidget->itemText(expectedIndex + i), insertedItems.at(i)); } @@ -1288,11 +1286,12 @@ void tst_QComboBox::insertItem_data() initialItems << "foo" << "bar"; for(int e = 0 ; e<2 ; e++) { bool editable = (e==0); - QTest::newRow("Insert less then 0") << initialItems << -1 << "inserted" << 0 << editable; - QTest::newRow("Insert at 0") << initialItems << 0 << "inserted" << 0 << editable; - QTest::newRow("Insert beyond count") << initialItems << 3 << "inserted" << 2 << editable; - QTest::newRow("Insert at count") << initialItems << 2 << "inserted" << 2 << editable; - QTest::newRow("Insert in the middle") << initialItems << 1 << "inserted" << 1 << editable; + const auto txt = editable ? QByteArray("editable: ") : QByteArray("non-editable: "); + QTest::newRow(txt + "Insert less then 0") << initialItems << -1 << "inserted" << 0 << editable; + QTest::newRow(txt + "Insert at 0") << initialItems << 0 << "inserted" << 0 << editable; + QTest::newRow(txt + "Insert beyond count") << initialItems << 3 << "inserted" << 2 << editable; + QTest::newRow(txt + "Insert at count") << initialItems << 2 << "inserted" << 2 << editable; + QTest::newRow(txt + "Insert in the middle") << initialItems << 1 << "inserted" << 1 << editable; } } @@ -1309,14 +1308,14 @@ void tst_QComboBox::insertItem() QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); QComboBox *testWidget = topLevel.comboBox(); testWidget->insertItems(0, initialItems); - QCOMPARE(testWidget->count(), initialItems.count()); + QCOMPARE(testWidget->count(), initialItems.size()); testWidget->setEditable(true); if (editable) testWidget->setEditText("FOO"); testWidget->insertItem(insertIndex, itemLabel); - QCOMPARE(testWidget->count(), initialItems.count() + 1); + QCOMPARE(testWidget->count(), initialItems.size() + 1); QCOMPARE(testWidget->itemText(expectedIndex), itemLabel); if (editable) @@ -1384,21 +1383,21 @@ void tst_QComboBox::textpixmapdata() QFETCH(IconList, icons); QFETCH(VariantList, variant); - QVERIFY(text.count() == icons.count() && text.count() == variant.count()); + QVERIFY(text.size() == icons.size() && text.size() == variant.size()); TestWidget topLevel; topLevel.show(); QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); QComboBox *testWidget = topLevel.comboBox(); - for (int i = 0; i<text.count(); ++i) { + for (int i = 0; i<text.size(); ++i) { testWidget->insertItem(i, text.at(i)); testWidget->setItemIcon(i, icons.at(i)); testWidget->setItemData(i, variant.at(i), Qt::UserRole); } - QCOMPARE(testWidget->count(), text.count()); + QCOMPARE(testWidget->count(), text.size()); - for (int i = 0; i<text.count(); ++i) { + for (int i = 0; i<text.size(); ++i) { QIcon icon = testWidget->itemIcon(i); QCOMPARE(icon.cacheKey(), icons.at(i).cacheKey()); QPixmap original = icons.at(i).pixmap(1024); @@ -1406,7 +1405,7 @@ void tst_QComboBox::textpixmapdata() QCOMPARE(pixmap.toImage(), original.toImage()); } - for (int i = 0; i<text.count(); ++i) { + for (int i = 0; i<text.size(); ++i) { QCOMPARE(testWidget->itemText(i), text.at(i)); // ### we should test icons/pixmap as well, but I need to fix the api mismatch first QCOMPARE(testWidget->itemData(i, Qt::UserRole), variant.at(i)); @@ -1480,14 +1479,12 @@ void tst_QComboBox::setCurrentText() else QCOMPARE(testWidget->currentText(), QString("foo")); -#ifndef QT_NO_PROPERTIES // verify WRITE for currentText property testWidget->setCurrentIndex(0); const QByteArray n("currentText"); QCOMPARE(testWidget->property(n).toString(), QString("foo")); testWidget->setProperty(n, QString("bar")); QCOMPARE(testWidget->property(n).toString(), QString("bar")); -#endif } void tst_QComboBox::currentTextChanged_data() @@ -1509,42 +1506,70 @@ void tst_QComboBox::currentTextChanged() testWidget->addItems(QStringList() << "foo" << "bar"); QCOMPARE(testWidget->count(), 2); - QSignalSpy spy(testWidget, SIGNAL(currentTextChanged(QString))); + QSignalSpy textChangedSpy(testWidget, &QComboBox::currentTextChanged); testWidget->setEditable(editable); // set text in list testWidget->setCurrentIndex(0); QCOMPARE(testWidget->currentIndex(), 0); - spy.clear(); + textChangedSpy.clear(); testWidget->setCurrentText(QString("bar")); - QCOMPARE(spy.count(), 1); - QCOMPARE(qvariant_cast<QString>(spy.at(0).at(0)), QString("bar")); + QCOMPARE(textChangedSpy.size(), 1); + QCOMPARE(qvariant_cast<QString>(textChangedSpy.at(0).at(0)), QString("bar")); // set text not in list testWidget->setCurrentIndex(0); QCOMPARE(testWidget->currentIndex(), 0); - spy.clear(); + textChangedSpy.clear(); testWidget->setCurrentText(QString("qt")); if (editable) { - QCOMPARE(spy.count(), 1); - QCOMPARE(qvariant_cast<QString>(spy.at(0).at(0)), QString("qt")); + QCOMPARE(textChangedSpy.size(), 1); + QCOMPARE(qvariant_cast<QString>(textChangedSpy.at(0).at(0)), QString("qt")); } else { - QCOMPARE(spy.count(), 0); + QCOMPARE(textChangedSpy.size(), 0); } // item changed testWidget->setCurrentIndex(0); QCOMPARE(testWidget->currentIndex(), 0); - spy.clear(); + textChangedSpy.clear(); testWidget->setItemText(0, QString("ape")); - QCOMPARE(spy.count(), 1); - QCOMPARE(qvariant_cast<QString>(spy.at(0).at(0)), QString("ape")); + QCOMPARE(textChangedSpy.size(), 1); + QCOMPARE(qvariant_cast<QString>(textChangedSpy.at(0).at(0)), QString("ape")); + // change it back - spy.clear(); + textChangedSpy.clear(); testWidget->setItemText(0, QString("foo")); - QCOMPARE(spy.count(), 1); - QCOMPARE(qvariant_cast<QString>(spy.at(0).at(0)), QString("foo")); + QCOMPARE(textChangedSpy.size(), 1); + QCOMPARE(qvariant_cast<QString>(textChangedSpy.at(0).at(0)), QString("foo")); + + // currentIndexChanged vs. currentTextChanged + testWidget->clear(); + testWidget->addItems(QStringList() << "first" << "second" << "third" << "fourth" << "fourth"); + testWidget->setCurrentIndex(4); + textChangedSpy.clear(); + QSignalSpy indexChangedSpy(testWidget, &QComboBox::currentIndexChanged); + + // Index change w/o text change + testWidget->removeItem(3); + QCOMPARE(textChangedSpy.count(), 0); + QCOMPARE(indexChangedSpy.count(), 1); + + // Index and text change + testWidget->setCurrentIndex(0); + QCOMPARE(textChangedSpy.count(), 1); + QCOMPARE(indexChangedSpy.count(), 2); + + // remove item above current index + testWidget->removeItem(2); + QCOMPARE(textChangedSpy.count(), 1); + QCOMPARE(indexChangedSpy.count(), 2); + + // Text change w/o index change + testWidget->setItemText(0, "first class"); + QCOMPARE(textChangedSpy.count(), 2); + QCOMPARE(indexChangedSpy.count(), 2); } void tst_QComboBox::editTextChanged() @@ -1568,13 +1593,13 @@ void tst_QComboBox::editTextChanged() QCOMPARE(testWidget->currentIndex(), 0); testWidget->setCurrentIndex(0); QCOMPARE(testWidget->currentIndex(), 0); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); // no signal should be sent when changing to other index because we are not editable QCOMPARE(testWidget->currentIndex(), 0); testWidget->setCurrentIndex(1); QCOMPARE(testWidget->currentIndex(), 1); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); // now set to editable and reset current index testWidget->setEditable(true); @@ -1586,20 +1611,20 @@ void tst_QComboBox::editTextChanged() QCOMPARE(testWidget->currentIndex(), 0); testWidget->setCurrentIndex(0); QCOMPARE(testWidget->currentIndex(), 0); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); // signal should be sent when changing to other index QCOMPARE(testWidget->currentIndex(), 0); testWidget->setCurrentIndex(1); QCOMPARE(testWidget->currentIndex(), 1); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(qvariant_cast<QString>(spy.at(0).at(0)), QString("bar")); // insert some keys and notice they are all signaled spy.clear(); QTest::keyClicks(testWidget, "bingo"); - QCOMPARE(spy.count(), 5); + QCOMPARE(spy.size(), 5); QCOMPARE(qvariant_cast<QString>(spy.at(4).at(0)), QString("barbingo")); } @@ -1652,6 +1677,16 @@ void tst_QComboBox::setModel() QCOMPARE(box.rootModelIndex(), rootModelIndex); box.setModel(box.model()); QCOMPARE(box.rootModelIndex(), rootModelIndex); + + // check that setting the same model as the completer's doesn't crash + QCompleter *completer = new QCompleter(&box); + box.setEditable(true); + box.setCompleter(completer); + auto *listModel = new QStringListModel({ "one", "two" }, completer); + completer->setModel(listModel); + QCOMPARE(listModel->rowCount(), 2); // make sure it wasn't deleted + box.setModel(listModel); + QCOMPARE(listModel->rowCount(), 2); // make sure it wasn't deleted } void tst_QComboBox::setCustomModelAndView() @@ -1756,7 +1791,7 @@ void tst_QComboBox::setMaxCount() // insert 5 items at pos 2. Make sure only two get inserted QSignalSpy spy(box.model(), SIGNAL(rowsInserted(QModelIndex,int,int))); box.insertItems(2, items); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(1).toInt(), 2); QCOMPARE(spy.at(0).at(2).toInt(), 3); @@ -1986,7 +2021,7 @@ void tst_QComboBox::flaggedItems_data() disableFlagList << 1; keyMovementList.clear(); keyMovementList << Qt::Key_T << Qt::Key_Enter; - QTest::newRow(testCase.toLatin1() + "disabled") << itemList << deselectFlagList << disableFlagList << keyMovementList << bool(editable) << 2; + QTest::newRow(testCase.toLatin1() + "disabled with key") << itemList << deselectFlagList << disableFlagList << keyMovementList << bool(editable) << 2; QTest::newRow(testCase.toLatin1() + "broken autocompletion") << itemList << deselectFlagList << disableFlagList << keyMovementList << bool(editable) << 2; } } @@ -1998,8 +2033,8 @@ void tst_QComboBox::flaggedItems() QSKIP("Wayland: This fails. Figure out why."); QFETCH(QStringList, itemList); - QFETCH(IntList, deselectFlagList); - QFETCH(IntList, disableFlagList); + QFETCH(const IntList, deselectFlagList); + QFETCH(const IntList, disableFlagList); QFETCH(KeyList, keyMovementList); QFETCH(bool, editable); QFETCH(int, expectedIndex); @@ -2010,17 +2045,16 @@ void tst_QComboBox::flaggedItems() listWidget.addItems(itemList); comboBox.setEditable(editable); - foreach (int index, deselectFlagList) + for (int index : deselectFlagList) listWidget.item(index)->setFlags(listWidget.item(index)->flags() & ~Qt::ItemIsSelectable); - foreach (int index, disableFlagList) + for (int index : disableFlagList) listWidget.item(index)->setFlags(listWidget.item(index)->flags() & ~Qt::ItemIsEnabled); comboBox.setModel(listWidget.model()); comboBox.setView(&listWidget); comboBox.move(200, 200); comboBox.show(); - QApplication::setActiveWindow(&comboBox); comboBox.activateWindow(); comboBox.setFocus(); QVERIFY(QTest::qWaitForWindowActive(&comboBox)); @@ -2030,7 +2064,7 @@ void tst_QComboBox::flaggedItems() if (editable) comboBox.lineEdit()->selectAll(); - for (int i = 0; i < keyMovementList.count(); ++i) { + for (int i = 0; i < keyMovementList.size(); ++i) { Qt::Key key = keyMovementList[i]; QTest::keyClick(&comboBox, key); } @@ -2096,7 +2130,7 @@ void tst_QComboBox::mouseWheel_data() void tst_QComboBox::mouseWheel() { - QFETCH(IntList, disabledItems); + QFETCH(const IntList, disabledItems); QFETCH(int, startIndex); QFETCH(int, wheelDirection); QFETCH(int, expectedIndex); @@ -2111,7 +2145,7 @@ void tst_QComboBox::mouseWheel() QListWidget listWidget; listWidget.addItems(list); - foreach (int index, disabledItems) + for (int index : disabledItems) listWidget.item(index)->setFlags(listWidget.item(index)->flags() & ~Qt::ItemIsEnabled); box.setModel(listWidget.model()); @@ -2245,13 +2279,13 @@ void tst_QComboBox::separatorItem_data() void tst_QComboBox::separatorItem() { QFETCH(QStringList, items); - QFETCH(IntList, separators); + QFETCH(const IntList, separators); QComboBox box; box.addItems(items); - foreach(int index, separators) + for (int index : separators) box.insertSeparator(index); - QCOMPARE(box.count(), (items.count() + separators.count())); + QCOMPARE(box.count(), (items.size() + separators.size())); for (int i = 0, s = 0; i < box.count(); ++i) { if (i == separators.at(s)) { QCOMPARE(box.itemText(i), QString()); @@ -2370,7 +2404,8 @@ void tst_QComboBox::task191329_size() QFrame *container = tableCombo.findChild<QComboBoxPrivateContainer *>(); QVERIFY(container); QCOMPARE(static_cast<QAbstractItemView *>(table), container->findChild<QAbstractItemView *>()); - foreach (QWidget *button, container->findChildren<QComboBoxPrivateScroller *>()) { + const auto buttons = container->findChildren<QComboBoxPrivateScroller *>(); + for (QWidget *button : buttons) { //the popup should be large enough to contains everithing so the top and left button are hidden QVERIFY(!button->isVisible()); } @@ -2449,7 +2484,6 @@ void tst_QComboBox::task247863_keyBoardSelection() combo.addItem( QLatin1String("111")); combo.addItem( QLatin1String("222")); combo.show(); - QApplication::setActiveWindow(&combo); QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&combo)); QSignalSpy spy(&combo, &QComboBox::activated); @@ -2459,7 +2493,7 @@ void tst_QComboBox::task247863_keyBoardSelection() QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_Down); QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_Enter); QCOMPARE(combo.currentText(), QLatin1String("222")); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } void tst_QComboBox::task220195_keyBoardSelection2() @@ -2475,7 +2509,6 @@ void tst_QComboBox::task220195_keyBoardSelection2() combo.addItem( QLatin1String("foo2")); combo.addItem( QLatin1String("foo3")); combo.show(); - QApplication::setActiveWindow(&combo); QVERIFY(QTest::qWaitForWindowActive(&combo)); combo.setCurrentIndex(-1); @@ -2734,16 +2767,16 @@ void tst_QComboBox::resetModel() QComboBox cb; StringListModel model({"1", "2"}); QSignalSpy spy(&cb, &QComboBox::currentIndexChanged); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QCOMPARE(cb.currentIndex(), -1); //no selection cb.setModel(&model); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(cb.currentIndex(), 0); //first item selected model.reset(); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); QCOMPARE(cb.currentIndex(), 0); //first item selected } @@ -2761,7 +2794,6 @@ void tst_QComboBox::keyBoardNavigationWithMouse() combo.move(200, 200); combo.showNormal(); - QApplication::setActiveWindow(&combo); QVERIFY(QTest::qWaitForWindowActive(&combo)); QCOMPARE(combo.currentText(), QLatin1String("0")); @@ -2817,17 +2849,16 @@ void tst_QComboBox::task_QTBUG_1071_changingFocusEmitsActivated() layout.addWidget(&edit); w.show(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); cb.clearEditText(); cb.setFocus(); QApplication::processEvents(); QTRY_VERIFY(cb.hasFocus()); QTest::keyClick(static_cast<QWidget *>(0), '1'); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); edit.setFocus(); QTRY_VERIFY(edit.hasFocus()); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); } void tst_QComboBox::maxVisibleItems_data() @@ -3169,31 +3200,55 @@ void tst_QComboBox::task_QTBUG_54191_slotOnEditTextChangedSetsComboBoxToReadOnly QCOMPARE(cb.currentIndex(), 1); } +class ComboBox : public QComboBox { +public: + using QComboBox::QComboBox; + + void keyPressEvent(QKeyEvent *e) override + { + QComboBox::keyPressEvent(e); + accepted = e->isAccepted(); + } + bool accepted = false; +}; + void tst_QComboBox::keyboardSelection() { - QComboBox comboBox; + ComboBox comboBox; const int keyboardInterval = QApplication::keyboardInputInterval(); - QStringList list; - list << "OA" << "OB" << "OC" << "OO" << "OP" << "PP"; + const QStringList list = {"OA", "OB", "OC", "OO", "OP", "PP"}; comboBox.addItems(list); // Clear any remaining keyboard input from previous tests. QTest::qWait(keyboardInterval); QTest::keyClicks(&comboBox, "oo", Qt::NoModifier, 50); QCOMPARE(comboBox.currentText(), list.at(3)); + QCOMPARE(comboBox.accepted, true); QTest::qWait(keyboardInterval); QTest::keyClicks(&comboBox, "op", Qt::NoModifier, 50); QCOMPARE(comboBox.currentText(), list.at(4)); + QCOMPARE(comboBox.accepted, true); QTest::keyClick(&comboBox, Qt::Key_P, Qt::NoModifier, keyboardInterval); QCOMPARE(comboBox.currentText(), list.at(5)); + QCOMPARE(comboBox.accepted, true); QTest::keyClick(&comboBox, Qt::Key_O, Qt::NoModifier, keyboardInterval); QCOMPARE(comboBox.currentText(), list.at(0)); + QCOMPARE(comboBox.accepted, true); QTest::keyClick(&comboBox, Qt::Key_O, Qt::NoModifier, keyboardInterval); QCOMPARE(comboBox.currentText(), list.at(1)); + QCOMPARE(comboBox.accepted, true); + + QTest::keyClick(&comboBox, Qt::Key_Tab, Qt::NoModifier, keyboardInterval); + QCOMPARE(comboBox.currentText(), list.at(1)); + QCOMPARE(comboBox.accepted, false); + + QTest::keyClick(&comboBox, Qt::Key_Tab, Qt::ControlModifier, keyboardInterval); + QCOMPARE(comboBox.currentText(), list.at(1)); + QCOMPARE(comboBox.accepted, false); } void tst_QComboBox::updateDelegateOnEditableChange() @@ -3245,11 +3300,11 @@ void tst_QComboBox::respectChangedOwnershipOfItemView() QTableView *v2 = new QTableView(&box1); box1.setView(v2); // Here we do not expect v1 to be deleted QApplication::processEvents(); - QCOMPARE(spy1.count(), 0); + QCOMPARE(spy1.size(), 0); QSignalSpy spy2(v2, SIGNAL(destroyed())); box1.setView(v1); - QCOMPARE(spy2.count(), 1); + QCOMPARE(spy2.size(), 1); } void tst_QComboBox::task_QTBUG_49831_scrollerNotActivated() @@ -3270,14 +3325,14 @@ void tst_QComboBox::task_QTBUG_49831_scrollerNotActivated() QVERIFY(container); QVERIFY(QTest::qWaitForWindowExposed(container)); - QList<QComboBoxPrivateScroller *> scrollers = container->findChildren<QComboBoxPrivateScroller *>(); + const QList<QComboBoxPrivateScroller *> scrollers = container->findChildren<QComboBoxPrivateScroller *>(); // Not all styles support scrollers. We rely only on those platforms that do to catch any regression. if (!scrollers.isEmpty()) { - Q_FOREACH (QComboBoxPrivateScroller *scroller, scrollers) { + for (QComboBoxPrivateScroller *scroller : scrollers) { if (scroller->isVisible()) { QSignalSpy doScrollSpy(scroller, SIGNAL(doScroll(int))); QTest::mouseMove(scroller, QPoint(5, 5), 500); - QTRY_VERIFY(doScrollSpy.count() > 0); + QTRY_VERIFY(doScrollSpy.size() > 0); } } } @@ -3355,6 +3410,65 @@ void tst_QComboBox::task_QTBUG_56693_itemFontFromModel() box.hidePopup(); } +#ifndef QT_NO_STYLE_FUSION +void tst_QComboBox::popupPositionAfterStyleChange() +{ +#ifdef Q_OS_QNX + QSKIP("Fails on QNX, QTBUG-123798"); +#endif + // Check that the popup opens up centered on top of the current + // index if the style has changed since the last time it was + // opened (QTBUG-113765). + QComboBox box; + QStyleOptionComboBox opt; + const bool usePopup = qApp->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, &box); + if (!usePopup) + QSKIP("This test is only relevant for styles that centers the popup on top of the combo!"); + if (QTestPrivate::isRunningArmOnX86()) + QSKIP("Flaky on QEMU, QTBUG-114760"); + + box.addItems({"first", "middle", "last"}); + box.show(); + QVERIFY(QTest::qWaitForWindowExposed(&box)); + box.showPopup(); + + QFrame *container = box.findChild<QComboBoxPrivateContainer *>(); + QVERIFY(container); + QVERIFY(QTest::qWaitForWindowExposed(container)); + + // Select the last menu item, which will close the popup. This item is then expected + // to be centered on top of the combobox the next time the popup opens. + const QRect lastItemRect = box.view()->visualRect(box.view()->model()->index(2, 0)); + QTest::mouseClick(box.view(), Qt::LeftButton, Qt::NoModifier, lastItemRect.center()); + + // Change style. This can make the popup smaller, which will result in up-and-down + // scroll widgets showing in the menu, directly underneath the mouse before the popup + // ends up hidden. This again will trigger the item view to scroll, which seems to be + // the root cause of QTBUG-113765. + qApp->setStyle(QStringLiteral("Fusion")); + + // Click on the combobox again to reopen it. But since both QComboBox + // (QComboBoxPrivateScroller) is using its own internal timer to do scrolling, we + // need to wait a bit until the scrolling is done before we can reopen it (since + // the scrolling is the sore spot that we want to test). + // But note, we expect, but don't require, the popup to scroll. And for that + // reason, we don't see it as a failure if the scrolling doesn't happen. + (void) QTest::qWaitFor([&box]{ return box.view()->verticalScrollBar()->value() > 0; }, 1000); + + // Verify that the popup is hidden before we click the button + QTRY_VERIFY(!container->isVisible()); + QTest::mouseClick(&box, Qt::LeftButton); + + // Click on item under mouse. But wait a bit, to avoid a double click + QTest::qWait(2 * QGuiApplication::styleHints()->mouseDoubleClickInterval()); + QTest::mouseClick(&box, Qt::LeftButton); + + // Ensure that the item that was centered on top of the combobox, and which + // we therefore clicked, was the same item we clicked on the first time. + QTRY_COMPARE(box.currentText(), QStringLiteral("last")); +} +#endif // QT_NO_STYLE_FUSION + void tst_QComboBox::inputMethodUpdate() { if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) @@ -3434,10 +3548,9 @@ void tst_QComboBox::task_QTBUG_52027_mapCompleterIndex() cbox.setCompleter(completer); QSignalSpy spy(&cbox, SIGNAL(activated(int))); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); cbox.move(200, 200); cbox.show(); - QApplication::setActiveWindow(&cbox); QVERIFY(QTest::qWaitForWindowActive(&cbox)); QTest::keyClicks(&cbox, "foobar2"); @@ -3459,11 +3572,10 @@ void tst_QComboBox::task_QTBUG_52027_mapCompleterIndex() completer->setModel(model); if (QGuiApplication::platformName() == "offscreen") { - QWARN("Offscreen platform requires explicit activateWindow()"); + qWarning("Offscreen platform requires explicit activateWindow()"); cbox.activateWindow(); } - QApplication::setActiveWindow(&cbox); QVERIFY(QTest::qWaitForWindowActive(&cbox)); QTest::keyClicks(&cbox, "foobar1"); @@ -3531,7 +3643,6 @@ void tst_QComboBox::checkEmbeddedLineEditWhenStyleSheetIsSet() layout->addWidget(comboBox); topLevel.show(); comboBox->setEditable(true); - QApplication::setActiveWindow(&topLevel); QVERIFY(QTest::qWaitForWindowActive(&topLevel)); QImage grab = comboBox->grab().toImage(); @@ -3544,5 +3655,189 @@ void tst_QComboBox::checkEmbeddedLineEditWhenStyleSheetIsSet() qApp->setStyleSheet(oldCss); } +/*! + Tests that the style-based frame style propagates to the internal container + widget of QComboBox when the style changes by verifying that the respective + styleHint is asked for when the style changes. + + See QTBUG-92488 +*/ +void tst_QComboBox::propagateStyleChanges() +{ + class FrameStyle : public QProxyStyle + { + public: + FrameStyle(int frameStyle, QStyle *style = nullptr) + : QProxyStyle(style), frameStyle(frameStyle) + {} + + int styleHint(QStyle::StyleHint hint, const QStyleOption *opt, + const QWidget *widget, QStyleHintReturn *returnData) const override + { + if (hint == QStyle::SH_ComboBox_PopupFrameStyle) { + inquired = true; + return frameStyle; + } + return QProxyStyle::styleHint(hint, opt, widget, returnData); + } + + int frameStyle; + mutable bool inquired = false; + }; + + FrameStyle framelessStyle(QFrame::NoFrame); + FrameStyle frameStyle(QFrame::Plain | QFrame::Sunken); + + QComboBox combo; + // container will be created and take settings from this style + combo.setStyle(&framelessStyle); + QVERIFY(framelessStyle.inquired); + combo.addItem(QLatin1String("Open")); + combo.addItem(QLatin1String("Close")); + // needed because of QComboBox's adjustSizeTimer not doing anything otherwise + combo.setSizeAdjustPolicy(QComboBox::AdjustToContents); + combo.setStyle(&frameStyle); + QVERIFY(frameStyle.inquired); +} + +void tst_QComboBox::buttonPressKeys() +{ + QComboBox comboBox; + comboBox.setEditable(false); + comboBox.addItem(QString::number(1)); + comboBox.addItem(QString::number(2)); + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value<QList<Qt::Key>>(); + for (int i = 0; i < buttonPressKeys.size(); ++i) { + QTest::keyClick(&comboBox, buttonPressKeys[i]); + // On some platforms, a window will not be immediately visible, + // but take some event-loop iterations to complete. + // Using QTRY_VERIFY to deal with that. + QTRY_VERIFY(comboBox.view()->isVisible()); + comboBox.hidePopup(); + } +} + +void tst_QComboBox::clearModel() +{ + using namespace Qt::StringLiterals; + QStringListModel model({ "one"_L1, "two"_L1, "three"_L1 }); + + QComboBox combo; + combo.setModel(&model); + combo.setCurrentIndex(1); + + QCOMPARE(combo.currentIndex(), 1); + QCOMPARE(combo.currentText(), model.index(1).data().toString()); + + QSignalSpy indexSpy(&combo, &QComboBox::currentIndexChanged); + QSignalSpy textSpy(&combo, &QComboBox::currentTextChanged); + + QVERIFY(indexSpy.isEmpty()); + QVERIFY(textSpy.isEmpty()); + + model.setStringList({}); + + QCOMPARE(indexSpy.size(), 1); + const int index = indexSpy.takeFirst().at(0).toInt(); + QCOMPARE(index, -1); + + QCOMPARE(textSpy.size(), 1); + const QString text = textSpy.takeFirst().at(0).toString(); + QCOMPARE(text, QString()); + + QCOMPARE(combo.currentIndex(), -1); + QCOMPARE(combo.currentText(), QString()); +} + +void tst_QComboBox::cancelClosesPopupNotDialog() +{ + if (QGuiApplication::platformName() == "offscreen") + QSKIP("The offscreen platform plugin doesn't activate popups."); + + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + + QDialog dialog; + QComboBox combobox; + combobox.addItems({"A", "B", "C"}); + + std::unique_ptr<QShortcut> shortcut(new QShortcut(QKeySequence::Cancel, &dialog)); + bool shortcutTriggered = false; + connect(shortcut.get(), &QShortcut::activated, [&shortcutTriggered]{ + shortcutTriggered = true; + }); + + QVBoxLayout vbox; + vbox.addWidget(&combobox); + dialog.setLayout(&vbox); + + dialog.show(); + QVERIFY(QTest::qWaitForWindowActive(&dialog)); + + // while the combobox is closed, escape key triggers the shortcut + QTest::keyClick(dialog.window()->windowHandle(), Qt::Key_Escape); + QVERIFY(shortcutTriggered); + shortcutTriggered = false; + + combobox.showPopup(); + QTRY_VERIFY(combobox.view()->isVisible()); + + // an open combobox overrides and accepts the escape key to close + QTest::keyClick(dialog.window()->windowHandle(), Qt::Key_Escape); + QVERIFY(!shortcutTriggered); + shortcutTriggered = false; + QTRY_VERIFY(!combobox.view()->isVisible()); + QVERIFY(dialog.isVisible()); + + // once closed, escape key triggers the shortcut again + QTest::keyClick(dialog.window()->windowHandle(), Qt::Key_Escape); + QVERIFY(shortcutTriggered); + shortcutTriggered = false; + QVERIFY(dialog.isVisible()); + + shortcut.reset(); + + // without shortcut, escape key propagates to the parent + QTest::keyClick(dialog.window()->windowHandle(), Qt::Key_Escape); + QVERIFY(!dialog.isVisible()); +} + +void tst_QComboBox::closePopupWithCheckableItems() +{ + QWidget widget; + + QVBoxLayout *vb = new QVBoxLayout(&widget); + + QLabel *dlgLabel = new QLabel("Click when combo expanded."); + vb->addWidget(dlgLabel); + + QComboBox *combo = new QComboBox(); + vb->addWidget(combo); + + QStandardItemModel model; + const int rowCount = 10; + for (int r = 0; r < rowCount; ++r) { + QString str = "Item: " + QString::number(r); + QStandardItem *item = new QStandardItem(str); + const bool isChecked = (r % 2); + + item->setData(isChecked ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole); + item->setFlags(Qt::ItemIsUserCheckable | (item->flags() & ~(Qt::ItemIsSelectable)) ); + model.appendRow(item); + } + + combo->setModel(&model); + + widget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&widget)); + + QTest::mouseClick(widget.windowHandle(), Qt::LeftButton, {}, combo->geometry().center()); + QVERIFY(QTest::qWaitForWindowExposed(combo->view())); + QTest::mouseClick(widget.windowHandle(), Qt::LeftButton, {}, dlgLabel->geometry().center()); + QTRY_VERIFY(!combo->view()->isVisible()); +} + QTEST_MAIN(tst_QComboBox) #include "tst_qcombobox.moc" diff --git a/tests/auto/widgets/widgets/qcommandlinkbutton/CMakeLists.txt b/tests/auto/widgets/widgets/qcommandlinkbutton/CMakeLists.txt index dfb555673a..b89c5aa1dd 100644 --- a/tests/auto/widgets/widgets/qcommandlinkbutton/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qcommandlinkbutton/CMakeLists.txt @@ -1,13 +1,21 @@ -# Generated from qcommandlinkbutton.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qcommandlinkbutton Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcommandlinkbutton LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qcommandlinkbutton SOURCES tst_qcommandlinkbutton.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui + Qt::GuiPrivate Qt::Widgets ) diff --git a/tests/auto/widgets/widgets/qcommandlinkbutton/tst_qcommandlinkbutton.cpp b/tests/auto/widgets/widgets/qcommandlinkbutton/tst_qcommandlinkbutton.cpp index 0d9e3a3198..e0c63e5ced 100644 --- a/tests/auto/widgets/widgets/qcommandlinkbutton/tst_qcommandlinkbutton.cpp +++ b/tests/auto/widgets/widgets/qcommandlinkbutton/tst_qcommandlinkbutton.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -40,6 +15,9 @@ #include <QGridLayout> #include <QPainter> +#include <private/qguiapplication_p.h> +#include <qpa/qplatformtheme.h> + class tst_QCommandLinkButton : public QObject { Q_OBJECT @@ -58,7 +36,6 @@ private slots: void setDown(); void popupCrash(); void isChecked(); - void animateClick(); void toggle(); void clicked(); void toggled(); @@ -227,6 +204,13 @@ void tst_QCommandLinkButton::setAutoRepeat() // check that pressing ENTER has no effect resetCounters(); testWidget->setDown( false ); + // Skip after reset if ButtonPressKeys has Key_Enter + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value<QList<Qt::Key>>(); + if (buttonPressKeys.contains(Qt::Key_Enter)) { + return; + } testWidget->setAutoRepeat( false ); QTest::keyPress( testWidget, Qt::Key_Enter ); @@ -259,6 +243,14 @@ void tst_QCommandLinkButton::pressed() QCOMPARE( press_count, (uint)1 ); QCOMPARE( release_count, (uint)1 ); + // Skip if ButtonPressKeys has Key_Enter + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value<QList<Qt::Key>>(); + if (buttonPressKeys.contains(Qt::Key_Enter)) { + return; + } + QTest::keyPress( testWidget,Qt::Key_Enter ); QCOMPARE( press_count, (uint)1 ); QCOMPARE( release_count, (uint)1 ); @@ -363,20 +355,6 @@ void tst_QCommandLinkButton::setAccel() #endif // QT_CONFIG(shortcut) -void tst_QCommandLinkButton::animateClick() -{ - QVERIFY( !testWidget->isDown() ); - testWidget->animateClick(); - QVERIFY( testWidget->isDown() ); - QTest::qWait( 200 ); - QVERIFY( !testWidget->isDown() ); - - QVERIFY( click_count == 1 ); - QVERIFY( press_count == 1 ); - QVERIFY( release_count == 1 ); - QVERIFY( toggle_count == 0 ); -} - void tst_QCommandLinkButton::clicked() { QTest::mousePress( testWidget, Qt::LeftButton ); diff --git a/tests/auto/widgets/widgets/qdatetimeedit/CMakeLists.txt b/tests/auto/widgets/widgets/qdatetimeedit/CMakeLists.txt index 8ddb0a3b0d..9c2e777380 100644 --- a/tests/auto/widgets/widgets/qdatetimeedit/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qdatetimeedit/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qdatetimeedit.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qdatetimeedit Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qdatetimeedit LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qdatetimeedit SOURCES tst_qdatetimeedit.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::Widgets diff --git a/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp b/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp index f9572b85e3..2d9689db41 100644 --- a/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp +++ b/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qapplication.h> #include <qgroupbox.h> @@ -72,10 +47,10 @@ #include <private/qdatetimeedit_p.h> +#include <QtWidgets/private/qapplication_p.h> + #ifdef Q_OS_WIN -# include <windows.h> -# undef min -# undef max +# include <qt_windows.h> #endif @@ -258,10 +233,12 @@ private slots: void nextPrevSection(); void dateEditTimeEditFormats(); +#if QT_DEPRECATED_SINCE(6, 10) void timeSpec_data(); void timeSpec(); - void timeSpecBug(); - void timeSpecInit(); +#endif + void timeZoneBug(); + void timeZoneInit(); void setDateTime_data(); void setDateTime(); @@ -277,7 +254,7 @@ private slots: void task196924(); void focusNextPrevChild(); - void taskQTBUG_12384_timeSpecShowTimeOnly(); + void taskQTBUG_12384_timeZoneShowTimeOnly(); void deleteCalendarWidget(); @@ -430,9 +407,9 @@ void tst_QDateTimeEdit::initTestCase() if (system.language() != QLocale::C && system.language() != QLocale::English) qWarning("Running under locale %s/%s -- this test may generate failures due to language differences", qPrintable(QLocale::languageToString(system.language())), - qPrintable(QLocale::countryToString(system.country()))); - testWidget = new EditorDateEdit(0); - testFocusWidget = new QWidget(0); + qPrintable(QLocale::territoryToString(system.territory()))); + testWidget = new EditorDateEdit; + testFocusWidget = new QWidget(nullptr); testFocusWidget->resize(200, 100); testFocusWidget->show(); } @@ -461,7 +438,7 @@ void tst_QDateTimeEdit::cleanup() { testWidget->clearMinimumDateTime(); testWidget->clearMaximumDateTime(); - testWidget->setTimeSpec(Qt::LocalTime); + testWidget->setTimeZone(QTimeZone::LocalTime); testWidget->setSpecialValueText(QString()); testWidget->setWrapping(false); // Restore the default. @@ -483,121 +460,104 @@ void tst_QDateTimeEdit::constructor_qwidget() void tst_QDateTimeEdit::constructor_qdatetime_data() { QTest::addColumn<QDateTime>("parameter"); - QTest::addColumn<QDateTime>("displayDateTime"); - QTest::addColumn<QDate>("minimumDate"); - QTest::addColumn<QTime>("minimumTime"); - QTest::addColumn<QDate>("maximumDate"); - QTest::addColumn<QTime>("maximumTime"); - - QTest::newRow("normal") << QDateTime(QDate(2004, 6, 16), QTime(13, 46, 32, 764)) - << QDateTime(QDate(2004, 6, 16), QTime(13, 46, 32, 764)) - << QDate(1752, 9, 14) << QTime(0, 0, 0, 0) - << QDate(9999, 12, 31) << QTime(23, 59, 59, 999); - QTest::newRow("invalid") << QDateTime(QDate(9999, 99, 99), QTime(13, 46, 32, 764)) - << QDateTime(QDate(2000, 1, 1), QTime(0, 0, 0, 0)) - << QDate(1752, 9, 14) << QTime(0, 0, 0, 0) - << QDate(9999, 12, 31) << QTime(23, 59, 59, 999); + QTest::newRow("normal") << QDateTime(QDate(2004, 6, 16), QTime(13, 46, 32, 764)); + QTest::newRow("invalid") << QDateTime(QDate(9999, 99, 99), QTime(13, 46, 32, 764)); } void tst_QDateTimeEdit::constructor_qdatetime() { QFETCH(QDateTime, parameter); - QFETCH(QDateTime, displayDateTime); - QFETCH(QDate, minimumDate); - QFETCH(QTime, minimumTime); - QFETCH(QDate, maximumDate); - QFETCH(QTime, maximumTime); - testWidget->hide(); QDateTimeEdit dte(parameter); dte.show(); - QCOMPARE(dte.dateTime(), displayDateTime); - QCOMPARE(dte.minimumDate(), minimumDate); - QCOMPARE(dte.minimumTime(), minimumTime); - QCOMPARE(dte.maximumDate(), maximumDate); - QCOMPARE(dte.maximumTime(), maximumTime); + if (QByteArrayView(QTest::currentDataTag()) == "invalid") + QCOMPARE(dte.dateTime(), QDateTime(QDate(2000, 1, 1), QTime(0, 0))); + else + QCOMPARE(dte.dateTime(), parameter); + QCOMPARE(dte.minimumDate(), QDate(1752, 9, 14)); + QCOMPARE(dte.minimumTime(), QTime(0, 0)); + QCOMPARE(dte.maximumDate(), QDate(9999, 12, 31)); + QCOMPARE(dte.maximumTime(), QTime(23, 59, 59, 999)); } void tst_QDateTimeEdit::constructor_qdate_data() { QTest::addColumn<QDate>("parameter"); - QTest::addColumn<QDateTime>("displayDateTime"); - QTest::addColumn<QDate>("minimumDate"); - QTest::addColumn<QTime>("minimumTime"); - QTest::addColumn<QDate>("maximumDate"); - QTest::addColumn<QTime>("maximumTime"); - - QTest::newRow("normal") << QDate(2004, 6, 16) - << QDateTime(QDate(2004, 6, 16), QTime(0, 0, 0, 0)) - << QDate(1752, 9, 14) << QTime(0, 0, 0, 0) - << QDate(9999, 12, 31) << QTime(23, 59, 59, 999); - QTest::newRow("invalid") << QDate(9999, 99, 99) - << QDateTime(QDate(2000, 1, 1), QTime(0, 0, 0, 0)) - << QDate(1752, 9, 14) << QTime(0, 0, 0, 0) - << QDate(9999, 12, 31) << QTime(23, 59, 59, 999); + QTest::newRow("normal") << QDate(2004, 6, 16); + QTest::newRow("invalid") << QDate(9999, 99, 99); } void tst_QDateTimeEdit::constructor_qdate() { QFETCH(QDate, parameter); - QFETCH(QDateTime, displayDateTime); - QFETCH(QDate, minimumDate); - QFETCH(QTime, minimumTime); - QFETCH(QDate, maximumDate); - QFETCH(QTime, maximumTime); - testWidget->hide(); - QDateTimeEdit dte(parameter); - dte.show(); - QCOMPARE(dte.dateTime(), displayDateTime); - QCOMPARE(dte.minimumDate(), minimumDate); - QCOMPARE(dte.minimumTime(), minimumTime); - QCOMPARE(dte.maximumDate(), maximumDate); - QCOMPARE(dte.maximumTime(), maximumTime); + { + QDateTimeEdit dte(parameter); + dte.show(); + if (QByteArrayView(QTest::currentDataTag()) == "invalid") + QCOMPARE(dte.dateTime(), QDateTime(QDate(2000, 1, 1), QTime(0, 0))); + else + QCOMPARE(dte.dateTime(), QDateTime(parameter, QTime(0, 0))); + QCOMPARE(dte.minimumDate(), QDate(1752, 9, 14)); + QCOMPARE(dte.minimumTime(), QTime(0, 0)); + QCOMPARE(dte.maximumDate(), QDate(9999, 12, 31)); + QCOMPARE(dte.maximumTime(), QTime(23, 59, 59, 999)); + } + { + QDateEdit dte(parameter); + dte.show(); + if (QByteArrayView(QTest::currentDataTag()) == "invalid") + QCOMPARE(dte.date(), QDate(2000, 1, 1)); + else + QCOMPARE(dte.date(), parameter); + QCOMPARE(dte.minimumDate(), QDate(1752, 9, 14)); + QCOMPARE(dte.minimumTime(), QTime(0, 0)); + QCOMPARE(dte.maximumDate(), QDate(9999, 12, 31)); + QCOMPARE(dte.maximumTime(), QTime(23, 59, 59, 999)); + } } void tst_QDateTimeEdit::constructor_qtime_data() { QTest::addColumn<QTime>("parameter"); - QTest::addColumn<QDateTime>("displayDateTime"); - QTest::addColumn<QDate>("minimumDate"); - QTest::addColumn<QTime>("minimumTime"); - QTest::addColumn<QDate>("maximumDate"); - QTest::addColumn<QTime>("maximumTime"); - QTest::newRow("normal") << QTime(13, 46, 32, 764) - << QDateTime(QDate(2000, 1, 1), QTime(13, 46, 32, 764)) - << QDate(2000, 1, 1) << QTime(0, 0, 0, 0) - << QDate(2000, 1, 1) << QTime(23, 59, 59, 999); - - QTest::newRow("invalid") << QTime(99, 99, 99, 5000) - << QDateTime(QDate(2000, 1, 1), QTime(0, 0, 0, 0)) - << QDate(2000, 1, 1) << QTime(0, 0, 0, 0) - << QDate(2000, 1, 1) << QTime(23, 59, 59, 999); + QTest::newRow("normal") << QTime(13, 46, 32, 764); + QTest::newRow("invalid") << QTime(99, 99, 99, 5000); } void tst_QDateTimeEdit::constructor_qtime() { QFETCH(QTime, parameter); - QFETCH(QDateTime, displayDateTime); - QFETCH(QDate, minimumDate); - QFETCH(QTime, minimumTime); - QFETCH(QDate, maximumDate); - QFETCH(QTime, maximumTime); - testWidget->hide(); - QDateTimeEdit dte(parameter); - dte.show(); - QCOMPARE(dte.dateTime(), displayDateTime); - QCOMPARE(dte.minimumDate(), minimumDate); - QCOMPARE(dte.minimumTime(), minimumTime); - QCOMPARE(dte.maximumDate(), maximumDate); - QCOMPARE(dte.maximumTime(), maximumTime); + { + QDateTimeEdit dte(parameter); + dte.show(); + if (QByteArrayView(QTest::currentDataTag()) == "invalid") + QCOMPARE(dte.dateTime(), QDateTime(QDate(2000, 1, 1), QTime(0, 0))); + else + QCOMPARE(dte.dateTime(), QDateTime(QDate(2000, 1, 1), parameter)); + QCOMPARE(dte.minimumDate(), QDate(2000, 1, 1)); + QCOMPARE(dte.minimumTime(), QTime(0, 0)); + QCOMPARE(dte.maximumDate(), QDate(2000, 1, 1)); + QCOMPARE(dte.maximumTime(), QTime(23, 59, 59, 999)); + } + { + QTimeEdit dte(parameter); + dte.show(); + if (QByteArrayView(QTest::currentDataTag()) == "invalid") + QCOMPARE(dte.time(), QTime(0, 0)); + else + QCOMPARE(dte.time(), parameter); + QCOMPARE(dte.minimumDate(), QDate(2000, 1, 1)); + QCOMPARE(dte.minimumTime(), QTime(0, 0)); + QCOMPARE(dte.maximumDate(), QDate(2000, 1, 1)); + QCOMPARE(dte.maximumTime(), QTime(23, 59, 59, 999)); + } } void tst_QDateTimeEdit::minimumDate_data() @@ -867,7 +827,6 @@ void tst_QDateTimeEdit::selectAndScrollWithKeys() return; #endif - qApp->setActiveWindow(testWidget); testWidget->setDate(QDate(2004, 05, 11)); testWidget->setDisplayFormat("dd/MM/yyyy"); testWidget->show(); @@ -969,7 +928,6 @@ void tst_QDateTimeEdit::selectAndScrollWithKeys() void tst_QDateTimeEdit::backspaceKey() { - qApp->setActiveWindow(testWidget); testWidget->setDate(QDate(2004, 05, 11)); testWidget->setDisplayFormat("d/MM/yyyy"); testWidget->show(); @@ -1035,7 +993,6 @@ void tst_QDateTimeEdit::backspaceKey() void tst_QDateTimeEdit::deleteKey() { - qApp->setActiveWindow(testWidget); testWidget->setDate(QDate(2004, 05, 11)); testWidget->setDisplayFormat("d/MM/yyyy"); #ifdef Q_OS_MAC @@ -1054,7 +1011,6 @@ void tst_QDateTimeEdit::deleteKey() void tst_QDateTimeEdit::tabKeyNavigation() { - qApp->setActiveWindow(testWidget); testWidget->setDate(QDate(2004, 05, 11)); testWidget->setDisplayFormat("dd/MM/yyyy"); testWidget->show(); @@ -1072,7 +1028,6 @@ void tst_QDateTimeEdit::tabKeyNavigation() void tst_QDateTimeEdit::tabKeyNavigationWithPrefix() { - qApp->setActiveWindow(testWidget); testWidget->setDate(QDate(2004, 05, 11)); testWidget->setDisplayFormat("prefix dd/MM/yyyy"); @@ -1090,7 +1045,6 @@ void tst_QDateTimeEdit::tabKeyNavigationWithPrefix() void tst_QDateTimeEdit::tabKeyNavigationWithSuffix() { - qApp->setActiveWindow(testWidget); testWidget->setDate(QDate(2004, 05, 11)); testWidget->setDisplayFormat("dd/MM/yyyy 'suffix'"); @@ -1106,7 +1060,6 @@ void tst_QDateTimeEdit::tabKeyNavigationWithSuffix() void tst_QDateTimeEdit::enterKey() { - qApp->setActiveWindow(testWidget); testWidget->setDate(QDate(2004, 5, 11)); testWidget->setDisplayFormat("prefix d/MM/yyyy 'suffix'"); testWidget->lineEdit()->setFocus(); @@ -1164,7 +1117,7 @@ void tst_QDateTimeEdit::enterKey() // we include this test so a change to the behaviour can't go unnoticed. QSignalSpy enterSpy(testWidget, SIGNAL(dateChanged(QDate))); QTest::keyClick(testWidget, Qt::Key_Enter); - QCOMPARE(enterSpy.count(), 1); + QCOMPARE(enterSpy.size(), 1); QVariantList list = enterSpy.takeFirst(); QCOMPARE(list.at(0).toDate(), QDate(2004, 5, 9)); } @@ -1358,7 +1311,7 @@ void tst_QDateTimeEdit::editingRanged_data() << QDate(2010, 12, 30) << QTime() << QDate(2011, 1, 2) << QTime() << QString::fromLatin1("01012011") - << QDateTime(QDate(2011, 1, 1), QTime()); + << QDateTime(QDate(2011, 1, 1), QTime(), QTimeZone::UTC); } void tst_QDateTimeEdit::editingRanged() @@ -1370,19 +1323,19 @@ void tst_QDateTimeEdit::editingRanged() QFETCH(QString, userInput); QFETCH(QDateTime, expected); - QDateTimeEdit *edit; + QScopedPointer<QDateTimeEdit> edit; if (minTime.isValid()) { - edit = new QDateTimeEdit; + edit.reset(new QDateTimeEdit); edit->setDisplayFormat("dd.MM.yyyy hh:mm"); edit->setDateTimeRange(QDateTime(minDate, minTime), QDateTime(maxDate, maxTime)); } else { - edit = new QDateEdit; + edit.reset(new QDateEdit); edit->setDisplayFormat("dd.MM.yyyy"); edit->setDateRange(minDate, maxDate); } int callCount = 0; - connect(edit, &QDateTimeEdit::dateTimeChanged, [&](const QDateTime &dateTime) { + connect(edit.get(), &QDateTimeEdit::dateTimeChanged, [&](const QDateTime &dateTime) { ++callCount; if (minTime.isValid()) { QVERIFY(dateTime >= QDateTime(minDate, minTime)); @@ -1394,15 +1347,14 @@ void tst_QDateTimeEdit::editingRanged() }); edit->show(); - QApplication::setActiveWindow(edit); - if (!QTest::qWaitForWindowActive(edit)) + if (!QTest::qWaitForWindowActive(edit.get())) QSKIP("Failed to make window active, aborting"); edit->setFocus(); // with keyboard tracking, never get a signal with an out-of-range value edit->setKeyboardTracking(true); - QTest::keyClicks(edit, userInput); - QTest::keyClick(edit, Qt::Key_Return); + QTest::keyClicks(edit.get(), userInput); + QTest::keyClick(edit.get(), Qt::Key_Return); QVERIFY(callCount > 0); // QDateTimeEdit blocks these dates from being entered - see QTBUG-65 @@ -1418,12 +1370,10 @@ void tst_QDateTimeEdit::editingRanged() callCount = 0; edit->setKeyboardTracking(false); - QTest::keyClicks(edit, userInput); - QTest::keyClick(edit, Qt::Key_Return); + QTest::keyClicks(edit.get(), userInput); + QTest::keyClick(edit.get(), Qt::Key_Return); QCOMPARE(edit->dateTime(), expected); QCOMPARE(callCount, 1); - - delete edit; } void tst_QDateTimeEdit::wrappingTime_data() @@ -2241,7 +2191,7 @@ void tst_QDateTimeEdit::dateSignalChecking() QSignalSpy timeSpy(testWidget, SIGNAL(timeChanged(QTime))); testWidget->setDate(newDate); - QCOMPARE(dateSpy.count(), timesEmitted); + QCOMPARE(dateSpy.size(), timesEmitted); if (timesEmitted > 0) { QList<QVariant> list = dateSpy.takeFirst(); @@ -2249,8 +2199,8 @@ void tst_QDateTimeEdit::dateSignalChecking() d = qvariant_cast<QDate>(list.at(0)); QCOMPARE(d, newDate); } - QCOMPARE(dateTimeSpy.count(), timesEmitted); - QCOMPARE(timeSpy.count(), 0); + QCOMPARE(dateTimeSpy.size(), timesEmitted); + QCOMPARE(timeSpy.size(), 0); } void tst_QDateTimeEdit::timeSignalChecking_data() @@ -2277,7 +2227,7 @@ void tst_QDateTimeEdit::timeSignalChecking() QSignalSpy timeSpy(testWidget, SIGNAL(timeChanged(QTime))); testWidget->setTime(newTime); - QCOMPARE(timeSpy.count(), timesEmitted); + QCOMPARE(timeSpy.size(), timesEmitted); if (timesEmitted > 0) { QList<QVariant> list = timeSpy.takeFirst(); @@ -2285,8 +2235,8 @@ void tst_QDateTimeEdit::timeSignalChecking() t = qvariant_cast<QTime>(list.at(0)); QCOMPARE(t, newTime); } - QCOMPARE(dateTimeSpy.count(), timesEmitted); - QCOMPARE(dateSpy.count(), 0); + QCOMPARE(dateTimeSpy.size(), timesEmitted); + QCOMPARE(dateSpy.size(), 0); } void tst_QDateTimeEdit::dateTimeSignalChecking_data() @@ -2327,7 +2277,7 @@ void tst_QDateTimeEdit::dateTimeSignalChecking() QSignalSpy dateTimeSpy(testWidget, SIGNAL(dateTimeChanged(QDateTime))); testWidget->setDateTime(newDateTime); - QCOMPARE(dateSpy.count(), timesDateEmitted); + QCOMPARE(dateSpy.size(), timesDateEmitted); if (timesDateEmitted > 0) { QCOMPARE(timesDateEmitted, 1); QList<QVariant> list = dateSpy.takeFirst(); @@ -2335,14 +2285,14 @@ void tst_QDateTimeEdit::dateTimeSignalChecking() d = qvariant_cast<QDate>(list.at(0)); QCOMPARE(d, newDateTime.date()); } - QCOMPARE(timeSpy.count(), timesTimeEmitted); + QCOMPARE(timeSpy.size(), timesTimeEmitted); if (timesTimeEmitted > 0) { QList<QVariant> list = timeSpy.takeFirst(); QTime t; t = qvariant_cast<QTime>(list.at(0)); QCOMPARE(t, newDateTime.time()); } - QCOMPARE(dateTimeSpy.count(), timesDateTimeEmitted); + QCOMPARE(dateTimeSpy.size(), timesDateTimeEmitted); if (timesDateTimeEmitted > 0) { QList<QVariant> list = dateTimeSpy.takeFirst(); QDateTime dt; @@ -3143,9 +3093,9 @@ void tst_QDateTimeEdit::hour12Test() void tst_QDateTimeEdit::yyTest() { testWidget->setDisplayFormat("dd-MMM-yy"); - testWidget->setTime(QTime(0, 0, 0)); testWidget->setDateRange(QDate(2005, 1, 1), QDate(2010, 12, 31)); testWidget->setDate(testWidget->minimumDate()); + testWidget->setTime(QTime(12, 0, 0)); // Mid-day to avoid DST artefacts. testWidget->setCurrentSection(QDateTimeEdit::YearSection); QString jan = QLocale::system().monthName(1, QLocale::ShortFormat); @@ -3206,22 +3156,22 @@ void tst_QDateTimeEdit::task149097() testWidget->setDisplayFormat("yyyy/MM/dd hh:mm:ss"); testWidget->setDateTime(QDateTime(QDate(2001, 02, 03), QTime(5, 1, 2))); // QTest::keyClick(testWidget, Qt::Key_Enter); - QCOMPARE(dtSpy.count(), 1); - QCOMPARE(dSpy.count(), 1); - QCOMPARE(tSpy.count(), 1); + QCOMPARE(dtSpy.size(), 1); + QCOMPARE(dSpy.size(), 1); + QCOMPARE(tSpy.size(), 1); testWidget->setCurrentSection(QDateTimeEdit::YearSection); testWidget->stepBy(1); - QCOMPARE(dtSpy.count(), 2); - QCOMPARE(dSpy.count(), 2); - QCOMPARE(tSpy.count(), 1); + QCOMPARE(dtSpy.size(), 2); + QCOMPARE(dSpy.size(), 2); + QCOMPARE(tSpy.size(), 1); testWidget->setCurrentSection(QDateTimeEdit::MinuteSection); testWidget->stepBy(1); - QCOMPARE(dtSpy.count(), 3); - QCOMPARE(dSpy.count(), 2); - QCOMPARE(tSpy.count(), 2); + QCOMPARE(dtSpy.size(), 3); + QCOMPARE(dSpy.size(), 2); + QCOMPARE(tSpy.size(), 2); } void tst_QDateTimeEdit::task148725() @@ -3409,7 +3359,7 @@ void tst_QDateTimeEdit::wheelEvent() QFETCH(QDate, startDate); QFETCH(DateList, expectedDates); - EditorDateEdit edit(0); + EditorDateEdit edit; edit.setDate(startDate); edit.setCurrentSection(section); @@ -3535,6 +3485,7 @@ void tst_QDateTimeEdit::dateEditTimeEditFormats() QCOMPARE(d.displayedSections(), QDateTimeEdit::YearSection); } +#if QT_DEPRECATED_SINCE(6, 10) void tst_QDateTimeEdit::timeSpec_data() { QTest::addColumn<bool>("useSetProperty"); @@ -3542,6 +3493,8 @@ void tst_QDateTimeEdit::timeSpec_data() QTest::newRow("setTimeSpec") << false; } +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED void tst_QDateTimeEdit::timeSpec() { QFETCH(bool, useSetProperty); @@ -3567,7 +3520,7 @@ void tst_QDateTimeEdit::timeSpec() QCOMPARE(edit.timeSpec(), Qt::LocalTime); const QDateTime utc = dt.toUTC(); if (dt.time() != utc.time()) { - const QDateTime min(QDate(1999, 1, 1), QTime(1, 0, 0), Qt::LocalTime); + const QDateTime min(QDate(1999, 1, 1), QTime(1, 0)); edit.setMinimumDateTime(min); QCOMPARE(edit.minimumTime(), min.time()); if (useSetProperty) { @@ -3582,10 +3535,12 @@ void tst_QDateTimeEdit::timeSpec() QSKIP("Not tested in the GMT timezone"); } } +QT_WARNING_POP +#endif // test deprecated timeSpec property -void tst_QDateTimeEdit::timeSpecBug() +void tst_QDateTimeEdit::timeZoneBug() { - testWidget->setTimeSpec(Qt::UTC); + testWidget->setTimeZone(QTimeZone::UTC); testWidget->setDisplayFormat("hh:mm"); testWidget->setTime(QTime(2, 2)); const QString oldText = testWidget->text(); @@ -3595,57 +3550,33 @@ void tst_QDateTimeEdit::timeSpecBug() QCOMPARE(oldText, testWidget->text()); } -void tst_QDateTimeEdit::timeSpecInit() +void tst_QDateTimeEdit::timeZoneInit() { - QDateTime utc(QDate(2000, 1, 1), QTime(12, 0, 0), Qt::UTC); + QDateTime utc(QDate(2000, 1, 1), QTime(12, 0), QTimeZone::UTC); QDateTimeEdit widget(utc); QCOMPARE(widget.dateTime(), utc); } void tst_QDateTimeEdit::setDateTime_data() { - QTest::addColumn<Qt::TimeSpec>("spec"); - QDateTime localNoon(QDate(2019, 12, 24), QTime(12, 0), Qt::LocalTime); -#if 0 // Not yet supported - QTest::addColumn<int>("offset"); - QTest::addColumn<QByteArray>("zoneName"); - - QTest::newRow("OffsetFromUTC/LocalTime") - << Qt::OffsetFromUTC << 7200 << "" - << localNoon << localNoon.toOffsetFromUtc(7200); -#if QT_CONFIG(timezone) - QTest::newRow("TimeZone/LocalTime") - << Qt::TimeZone << 0 << "Europe/Berlin" - << localNoon << localNoon.toTimeZone(QTimeZone("Europe/Berlin")); -#endif -#endif // unsupported + const QDateTime localNoon(QDate(2019, 12, 24), QTime(12, 0)); + const QTimeZone UTC(QTimeZone::UTC), local(QTimeZone::LocalTime); + QTest::addColumn<QTimeZone>("zone"); QTest::addColumn<QDateTime>("store"); QTest::addColumn<QDateTime>("expect"); - QTest::newRow("LocalTime/LocalTime") - << Qt::LocalTime // << 0 << "" - << localNoon << localNoon; - QTest::newRow("LocalTime/UTC") - << Qt::LocalTime // << 0 << "" - << localNoon.toUTC() << localNoon; - QTest::newRow("UTC/LocalTime") - << Qt::UTC // << 0 << "" - << localNoon << localNoon.toUTC(); - QTest::newRow("UTC/UTC") - << Qt::UTC // << 0 << "" - << localNoon.toUTC() << localNoon.toUTC(); + QTest::newRow("LocalTime/LocalTime") << local << localNoon << localNoon; + QTest::newRow("LocalTime/UTC") << local << localNoon.toUTC() << localNoon; + QTest::newRow("UTC/LocalTime") << UTC << localNoon << localNoon.toUTC(); + QTest::newRow("UTC/UTC") << UTC << localNoon.toUTC() << localNoon.toUTC(); } void tst_QDateTimeEdit::setDateTime() { - QFETCH(const Qt::TimeSpec, spec); -#if 0 // Not yet supported - QFETCH(const int, offset); - QFETCH(const QByteArray, zoneName); -#endif // configuring the spec, when OffsetFromUTC or TimeZone + QFETCH(const QTimeZone, zone); QFETCH(const QDateTime, store); QFETCH(const QDateTime, expect); QDateTimeEdit editor; - editor.setTimeSpec(spec); + editor.setTimeZone(zone); editor.setDateTime(store); QCOMPARE(editor.dateTime(), expect); } @@ -3839,14 +3770,14 @@ void tst_QDateTimeEdit::focusNextPrevChild() QCOMPARE(edit.currentSection(), QDateTimeEdit::MonthSection); } -void tst_QDateTimeEdit::taskQTBUG_12384_timeSpecShowTimeOnly() +void tst_QDateTimeEdit::taskQTBUG_12384_timeZoneShowTimeOnly() { QDateTime time = QDateTime::fromString("20100723 04:02:40", "yyyyMMdd hh:mm:ss"); - time.setTimeSpec(Qt::UTC); + time.setTimeZone(QTimeZone::UTC); EditorDateEdit edit; edit.setDisplayFormat("hh:mm:ss"); - edit.setTimeSpec(Qt::UTC); + edit.setTimeZone(QTimeZone::UTC); edit.setDateTime(time); QCOMPARE(edit.minimumTime(), QTime(0, 0, 0, 0)); @@ -3858,16 +3789,14 @@ void tst_QDateTimeEdit::deleteCalendarWidget() { { // setup - QCalendarWidget *cw = 0; QDateEdit edit; QVERIFY(!edit.calendarWidget()); edit.setCalendarPopup(true); QVERIFY(edit.calendarWidget()); - edit.calendarWidget()->setObjectName("cw1");; + edit.calendarWidget()->setObjectName("cw1"); // delete - cw = edit.calendarWidget(); - delete cw; + delete edit.calendarWidget(); // it should create a new widget QVERIFY(edit.calendarWidget()); @@ -3881,7 +3810,7 @@ void tst_QDateTimeEdit::setLocaleOnCalendarWidget() QList<QLocale> allLocales = QLocale::matchingLocales( QLocale::AnyLanguage, QLocale::AnyScript, - QLocale::AnyCountry); + QLocale::AnyTerritory); QLocale c = QLocale::c(); dateEdit.setCalendarPopup(true); dateEdit.setLocale(c); @@ -4013,20 +3942,23 @@ void tst_QDateTimeEdit::dateEditCorrectSectionSize_data() KeyPairList shortAndLongNameIssueKeypresses; shortAndLongNameIssueKeypresses << key(Qt::Key_Tab) << key(Qt::Key_3) << key(Qt::Key_1) << key(Qt::Key_Up); + // When day-of-week is specified, rather than day-of-month, changing month + // cares more about preserving day-of-week than day-of-month, so Jan/31 -> + // Feb picks 28th even in a leap year, as that's exactly four weeks later. QTest::newRow("no fixday, leap, yy/M/dddd") << ozzy << y2kStart << QString::fromLatin1("yy/M/dddd") << threeDigitDayIssueKeypresses_DayName - << QDate(2000, 2, 29) << QString::fromLatin1("00/2/Tuesday"); + << QDate(2000, 2, 28) << QString::fromLatin1("00/2/Monday"); QTest::newRow("no fixday, leap, yy/M/ddd") << ozzy << y2kStart << QString::fromLatin1("yy/M/ddd") << threeDigitDayIssueKeypresses_DayName - << QDate(2000, 2, 29) << QString::fromLatin1("00/2/Tue"); + << QDate(2000, 2, 28) << QString::fromLatin1("00/2/Mon"); QTest::newRow("no fixday, leap, yy/MM/dddd") << ozzy << y2kStart << QString::fromLatin1("yy/MM/dddd") << threeDigitDayIssueKeypresses_DayName - << QDate(2000, 2, 29) << QString::fromLatin1("00/02/Tuesday"); + << QDate(2000, 2, 28) << QString::fromLatin1("00/02/Monday"); QTest::newRow("fixday, leap, yy/MM/dd") << ozzy << y2kStart << QString::fromLatin1("yy/MM/dd") @@ -4076,12 +4008,12 @@ void tst_QDateTimeEdit::dateEditCorrectSectionSize_data() QTest::newRow("no fixday, leap, yyyy/M/dddd") << ozzy << y2kStart << QString::fromLatin1("yyyy/M/dddd") << threeDigitDayIssueKeypresses_DayName - << QDate(2000, 2, 29) << QString::fromLatin1("2000/2/Tuesday"); + << QDate(2000, 2, 28) << QString::fromLatin1("2000/2/Monday"); QTest::newRow("no fixday, leap, yyyy/MM/dddd") << ozzy << y2kStart << QString::fromLatin1("yyyy/MM/dddd") << threeDigitDayIssueKeypresses_DayName - << QDate(2000, 2, 29) << QString::fromLatin1("2000/02/Tuesday"); + << QDate(2000, 2, 28) << QString::fromLatin1("2000/02/Monday"); QTest::newRow("fixday, leap, yyyy/dd/MM") << ozzy << y2kStart << QString::fromLatin1("yyyy/dd/MM") @@ -4116,12 +4048,12 @@ void tst_QDateTimeEdit::dateEditCorrectSectionSize_data() QTest::newRow("fixday, leap, yyyy/dddd/M") << ozzy << y2kStart << QString::fromLatin1("yyyy/dddd/M") << threeDigitDayIssueKeypresses_DayName_YearDayMonth - << QDate(2000, 2, 29) << QString::fromLatin1("2000/Tuesday/2"); + << QDate(2000, 2, 28) << QString::fromLatin1("2000/Monday/2"); QTest::newRow("fixday, leap, yyyy/dddd/MM") << ozzy << y2kStart << QString::fromLatin1("yyyy/dddd/MM") << threeDigitDayIssueKeypresses_DayName_YearDayMonth - << QDate(2000, 2, 29) << QString::fromLatin1("2000/Tuesday/02"); + << QDate(2000, 2, 28) << QString::fromLatin1("2000/Monday/02"); QTest::newRow("fixday, leap, d/M/yyyy") << ozzy << y2kStart << QString::fromLatin1("d/M/yyyy") @@ -4141,7 +4073,7 @@ void tst_QDateTimeEdit::dateEditCorrectSectionSize_data() QTest::newRow("fixday, leap, dddd/MM/yyyy") << ozzy << y2kStart << QString::fromLatin1("dddd/MM/yyyy") << threeDigitDayIssueKeypresses_DayName_DayMonthYear - << QDate(2000, 2, 29) << QString::fromLatin1("Tuesday/02/2000"); + << QDate(2000, 2, 28) << QString::fromLatin1("Monday/02/2000"); QTest::newRow("fixday, leap, d/yy/M") << ozzy << y2kStart << QString::fromLatin1("d/yy/M") @@ -4176,12 +4108,12 @@ void tst_QDateTimeEdit::dateEditCorrectSectionSize_data() QTest::newRow("fixday, leap, dddd/yy/M") << ozzy << y2kStart << QString::fromLatin1("dddd/yy/M") << threeDigitDayIssueKeypresses_DayName_DayYearMonth - << QDate(2000, 2, 29) << QString::fromLatin1("Tuesday/00/2"); + << QDate(2000, 2, 28) << QString::fromLatin1("Monday/00/2"); QTest::newRow("fixday, leap, dddd/yy/MM") << ozzy << y2kStart << QString::fromLatin1("dddd/yy/MM") << threeDigitDayIssueKeypresses_DayName_DayYearMonth - << QDate(2000, 2, 29) << QString::fromLatin1("Tuesday/00/02"); + << QDate(2000, 2, 28) << QString::fromLatin1("Monday/00/02"); QTest::newRow("fixday, leap, M/d/yy") << ozzy << y2kStart << QString::fromLatin1("M/d/yy") @@ -4201,7 +4133,7 @@ void tst_QDateTimeEdit::dateEditCorrectSectionSize_data() QTest::newRow("fixday, leap, M/dddd/yyyy") << ozzy << y2kStart << QString::fromLatin1("M/dddd/yyyy") << threeDigitDayIssueKeypresses_DayName_MonthDayYear - << QDate(2000, 2, 29) << QString::fromLatin1("2/Tuesday/2000"); + << QDate(2000, 2, 28) << QString::fromLatin1("2/Monday/2000"); QTest::newRow("fixday, leap, MM/dd/yyyy") << ozzy << y2kStart << QString::fromLatin1("MM/dd/yyyy") @@ -4211,7 +4143,7 @@ void tst_QDateTimeEdit::dateEditCorrectSectionSize_data() QTest::newRow("fixday, leap, MM/dddd/yyyy") << ozzy << y2kStart << QString::fromLatin1("MM/dddd/yyyy") << threeDigitDayIssueKeypresses_DayName_MonthDayYear - << QDate(2000, 2, 29) << QString::fromLatin1("02/Tuesday/2000"); + << QDate(2000, 2, 28) << QString::fromLatin1("02/Monday/2000"); QTest::newRow("fixday, leap, M/yyyy/dd") << ozzy << y2kStart << QString::fromLatin1("M/yyyy/dd") @@ -4279,6 +4211,9 @@ void tst_QDateTimeEdit::dateEditCorrectSectionSize() edit.setDisplayFormat(displayFormat); edit.show(); edit.setFocus(); + // Day-of-week tests rely on advance through week advancing the + // day-of-month, so not stopping at the locale's first day of the week: + edit.setWrapping(true); // For some reason, we need to set the selected section for the dd/MM/yyyy tests, // otherwise the 3 is inserted at the front of 01/01/2000 (301/01/2000), instead of the // selected text being replaced. This is not an issue for the yyyy/MM/dd format though... @@ -4484,7 +4419,7 @@ void tst_QDateTimeEdit::stepModifierButtons() testWidget->hide(); - EditorDateEdit edit(0); + EditorDateEdit edit; edit.setTime(startTime); edit.show(); QVERIFY(QTest::qWaitForWindowActive(&edit)); @@ -4570,11 +4505,15 @@ void tst_QDateTimeEdit::stepModifierPressAndHold() QFETCH(Qt::KeyboardModifiers, modifiers); QFETCH(int, expectedStepModifier); - const QDate startDate(2000, 1, 1); + // Some west African zones (e.g. Niamey, Conakry) changed from 1 hour west + // of GMT to GMT at the start of 1960; and spy.size() can get as high as 4, + // causing the expectedDate below, when expectedStepModifier is -10, to land + // in a transition gap for these zones, if we use Jan 1st; so use Jan 2nd. + const QDate startDate(2000, 1, 2); testWidget->hide(); - EditorDateEdit edit(0); + EditorDateEdit edit; edit.setDate(startDate); QScopedPointer<StepModifierStyle, QScopedPointerDeleteLater> stepModifierStyle( @@ -4595,12 +4534,12 @@ void tst_QDateTimeEdit::stepModifierPressAndHold() QStyle::CC_SpinBox, &spinBoxStyleOption, subControl, &edit); QTest::mousePress(&edit, Qt::LeftButton, modifiers, buttonRect.center()); - QTRY_VERIFY(spy.length() >= 3); + QTRY_VERIFY(spy.size() >= 3); QTest::mouseRelease(&edit, Qt::LeftButton, modifiers, buttonRect.center()); const auto value = spy.last().at(0); QVERIFY(value.userType() == QMetaType::QDate); - const QDate expectedDate = startDate.addYears(spy.length() * + const QDate expectedDate = startDate.addYears(spy.size() * expectedStepModifier); QCOMPARE(value.toDate(), expectedDate); } @@ -4624,13 +4563,20 @@ static QDateTime findSpring(int year, const QTimeZone &timeZone) const QTimeZone::OffsetData transition = midSummer.isDaylightTime() ? timeZone.previousTransition(midSummer) : timeZone.nextTransition(midSummer); - const QDateTime spring = transition.atUtc.toLocalTime(); + const QDateTime spring = transition.atUtc.toTimeZone(timeZone); // there might have been DST at some point, but not in the year we care about if (spring.date().year() != year || !spring.isDaylightTime()) return QDateTime(); return spring; }; + +// Number of missing seconds between a day before and a day after when. +// If when is the time of a spring-forward transition, this is the width of its gap. +static int missingSecondsNear(const QDateTime &when) +{ + return 2 * 24 * 60 * 60 - when.addDays(-1).secsTo(when.addDays(1)); +} #endif /*! @@ -4654,36 +4600,40 @@ void tst_QDateTimeEdit::springForward_data() QSKIP("Failed to obtain valid spring forward datetime for 2019!"); const QDate springDate = springTransition.date(); - const int gapWidth = timeZone.daylightTimeOffset(springTransition.addDays(1)); + const int gapWidth = missingSecondsNear(springTransition); + if (gapWidth <= 0) + QSKIP("Spring forward transition did not actually skip any time!"); + const QTime springGap = springTransition.time().addSecs(-gapWidth); - const QByteArray springTime = springGap.toString("hh:mm").toLocal8Bit(); - const QTime springGapMiddle = springTransition.time().addSecs(-gapWidth/2); + const QTime springGapMiddle = springTransition.time().addSecs(-gapWidth / 2); + const QByteArray startGapTime = springGap.toString("hh:mm").toLocal8Bit(); + const QByteArray midGapTime = springGapMiddle.toString("hh:mm").toLocal8Bit(); - QTest::addRow("forward to %s, correct to previous", springTime.data()) + QTest::addRow("forward to %s, correct to previous", startGapTime.data()) << QDateTime(springDate, springGap.addSecs(-gapWidth)) << QAbstractSpinBox::CorrectToPreviousValue << springGap << QDateTime(springDate, springGap.addSecs(-gapWidth)); - QTest::addRow("back to %s, correct to previous", springTime.data()) + QTest::addRow("back to %s, correct to previous", startGapTime.data()) << springTransition << QAbstractSpinBox::CorrectToPreviousValue << springGap << springTransition; - QTest::addRow("forward to %s, correct to nearest", springTime.data()) + QTest::addRow("forward to %s, correct to nearest", midGapTime.data()) << QDateTime(springDate, springGap.addSecs(-gapWidth)) << QAbstractSpinBox::CorrectToNearestValue << springGapMiddle << springTransition; - QTest::addRow("back to %s, correct to nearest", springTime.data()) + QTest::addRow("back to %s, correct to nearest", midGapTime.data()) << springTransition << QAbstractSpinBox::CorrectToNearestValue << springGapMiddle << springTransition; - QTest::addRow("jump to %s, correct to nearest", qPrintable(springGapMiddle.toString("hh:mm"))) + QTest::addRow("jump to %s, correct to nearest", midGapTime.data()) << QDateTime(QDate(1980, 5, 10), springGap) << QAbstractSpinBox::CorrectToNearestValue << springGapMiddle @@ -4711,11 +4661,11 @@ void tst_QDateTimeEdit::springForward() edit.setSelectedSection(QDateTimeEdit::DaySection); const QDate date = expected.date(); - const QString day = QString::number(date.day()).rightJustified(2, QLatin1Char('0')); - const QString month = QString::number(date.month()).rightJustified(2, QLatin1Char('0')); + const QString day = QString::number(date.day()).rightJustified(2, u'0'); + const QString month = QString::number(date.month()).rightJustified(2, u'0'); const QString year = QString::number(date.year()); - const QString hour = QString::number(inputTime.hour()).rightJustified(2, QLatin1Char('0')); - const QString minute = QString::number(inputTime.minute()).rightJustified(2, QLatin1Char('0')); + const QString hour = QString::number(inputTime.hour()).rightJustified(2, u'0'); + const QString minute = QString::number(inputTime.minute()).rightJustified(2, u'0'); QTest::keyClicks(&edit, day); QTest::keyClicks(&edit, month); QTest::keyClicks(&edit, year); @@ -4742,7 +4692,7 @@ void tst_QDateTimeEdit::stepIntoDSTGap_data() QTest::addColumn<int>("steps"); QTest::addColumn<QDateTime>("end"); - const QTimeZone timeZone = QTimeZone("Europe/Oslo"); + const QTimeZone timeZone = QTimeZone::systemTimeZone(); if (!timeZone.hasDaylightTime()) QSKIP("This test needs to run in a timezone that observes DST!"); @@ -4751,11 +4701,14 @@ void tst_QDateTimeEdit::stepIntoDSTGap_data() QSKIP("Failed to obtain valid spring forward datetime for 2007!"); const QDate spring = springTransition.date(); - const int gapWidth = timeZone.daylightTimeOffset(springTransition.addDays(1)); + const int gapWidth = missingSecondsNear(springTransition); + if (gapWidth <= 0) + QSKIP("Spring forward transition did not actually skip any time!"); + const QTime springGap = springTransition.time().addSecs(-gapWidth); const QByteArray springTime = springGap.toString("hh:mm").toLocal8Bit(); - // change hour + // change hour (can't change day): if (springGap.hour() != 0) { QTest::addRow("hour up into %s gap", springTime.data()) << QDateTime(spring, springGap.addSecs(-3600)) @@ -4765,7 +4718,7 @@ void tst_QDateTimeEdit::stepIntoDSTGap_data() // 3:00:10 into 2:00:10 should get us to 1:00:10 QTest::addRow("hour down into %s gap", springTime.data()) - << QDateTime(spring, springGap.addSecs(3610)) + << QDateTime(spring, springGap.addSecs(gapWidth + 10)) << QDateTimeEdit::HourSection << -1 << QDateTime(spring, springGap.addSecs(-3590)); @@ -4790,28 +4743,31 @@ void tst_QDateTimeEdit::stepIntoDSTGap_data() } // change month - QTest::addRow("month up into %s gap", springTime.data()) - << QDateTime(spring.addMonths(-1), springGap) - << QDateTimeEdit::MonthSection - << +1 - << springTransition; - QTest::addRow("month down into %s gap", springTime.data()) - << QDateTime(spring.addMonths(1), springGap) - << QDateTimeEdit::MonthSection - << -1 - << springTransition; + // Previous month may well be February, so lack the day-of-month that + // matches spring (e.g. Asia/Jerusalem, March 30). + if (QDate prior = spring.addMonths(-1); prior.day() == spring.day()) { + QTest::addRow("month up into %s gap", springTime.data()) + << QDateTime(prior, springGap) << QDateTimeEdit::MonthSection << +1 << springTransition; + } + // America/{Jujuy,Cordoba,Catamarca} did a 2007 Dec 30th 00:00 spring + // forward; and QDTE month steps won't change the year. + if (QDate prior = spring.addMonths(1); + prior.year() == spring.year() && prior.day() == spring.day()) { + QTest::addRow("month down into %s gap", springTime.data()) + << QDateTime(prior, springGap) << QDateTimeEdit::MonthSection << -1 << springTransition; + } // change year - QTest::addRow("year up into %s gap", springTime.data()) - << QDateTime(spring.addYears(-1), springGap) - << QDateTimeEdit::YearSection - << +1 - << springTransition; - QTest::addRow("year down into %s gap", springTime.data()) - << QDateTime(spring.addYears(1), springGap) - << QDateTimeEdit::YearSection - << -1 - << springTransition; + // Some zones (e.g. Asia/Baghdad) do transitions on a fixed date; for these, + // the springGap moment is invalid every year, so skip this test. + if (QDateTime prior = QDateTime(spring.addYears(-1), springGap); prior.isValid()) { + QTest::addRow("year up into %s gap", springTime.data()) + << prior << QDateTimeEdit::YearSection << +1 << springTransition; + } + if (QDateTime later(spring.addYears(1), springGap); later.isValid()) { + QTest::addRow("year down into %s gap", springTime.data()) + << later << QDateTimeEdit::YearSection << -1 << springTransition; + } #else QSKIP("Needs timezone feature enabled"); #endif diff --git a/tests/auto/widgets/widgets/qdial/CMakeLists.txt b/tests/auto/widgets/widgets/qdial/CMakeLists.txt index e76cc4a7db..5e300eec94 100644 --- a/tests/auto/widgets/widgets/qdial/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qdial/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qdial.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qdial Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qdial LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qdial SOURCES tst_qdial.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/widgets/qdial/tst_qdial.cpp b/tests/auto/widgets/widgets/qdial/tst_qdial.cpp index 29aee1c397..d0274ace2a 100644 --- a/tests/auto/widgets/widgets/qdial/tst_qdial.cpp +++ b/tests/auto/widgets/widgets/qdial/tst_qdial.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -42,6 +17,10 @@ private slots: void valueChanged(); void sliderMoved(); void wrappingCheck(); + void minEqualMaxValueOutsideRange(); + + void notchSize_data(); + void notchSize(); }; // Testing get/set functions @@ -74,14 +53,14 @@ void tst_QDial::valueChanged() dial.setMaximum(100); QSignalSpy spy(&dial, SIGNAL(valueChanged(int))); dial.setValue(50); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); dial.setValue(25); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); // repeat! dial.setValue(25); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } void tst_QDial::sliderMoved() @@ -97,7 +76,7 @@ void tst_QDial::sliderMoved() QPoint init(dial.width()/4, dial.height()/2); - QMouseEvent pressevent(QEvent::MouseButtonPress, init, + QMouseEvent pressevent(QEvent::MouseButtonPress, init, dial.mapToGlobal(init), Qt::LeftButton, Qt::LeftButton, {}); qApp->sendEvent(&dial, &pressevent); @@ -107,27 +86,27 @@ void tst_QDial::sliderMoved() { //move on top of the slider init = QPoint(dial.width()/2, dial.height()/4); - QMouseEvent moveevent(QEvent::MouseMove, init, + QMouseEvent moveevent(QEvent::MouseMove, init, dial.mapToGlobal(init), Qt::LeftButton, Qt::LeftButton, {}); qApp->sendEvent(&dial, &moveevent); - QCOMPARE( sliderspy.count(), 1); - QCOMPARE( valuespy.count(), 0); + QCOMPARE( sliderspy.size(), 1); + QCOMPARE( valuespy.size(), 0); } { //move on the right of the slider init = QPoint(dial.width()*3/4, dial.height()/2); - QMouseEvent moveevent(QEvent::MouseMove, init, + QMouseEvent moveevent(QEvent::MouseMove, init, dial.mapToGlobal(init), Qt::LeftButton, Qt::LeftButton, {}); qApp->sendEvent(&dial, &moveevent); - QCOMPARE( sliderspy.count(), 2); - QCOMPARE( valuespy.count(), 0); + QCOMPARE( sliderspy.size(), 2); + QCOMPARE( valuespy.size(), 0); } - QMouseEvent releaseevent(QEvent::MouseButtonRelease, init, + QMouseEvent releaseevent(QEvent::MouseButtonRelease, init, dial.mapToGlobal(init), Qt::LeftButton, Qt::LeftButton, {}); qApp->sendEvent(&dial, &releaseevent); - QCOMPARE( valuespy.count(), 1); // valuechanged signal should be called at this point + QCOMPARE( valuespy.size(), 1); // valuechanged signal should be called at this point } @@ -194,5 +173,42 @@ void tst_QDial::wrappingCheck() } } +// QTBUG-104641 +void tst_QDial::minEqualMaxValueOutsideRange() +{ + QDial dial; + dial.setRange(30, 30); + dial.setWrapping(true); + dial.setValue(45); +} + +/* + Verify that the notchSizes calculated don't change compared + to Qt 5.15 results for dial sizes at the edge values of the + algorithm. +*/ +void tst_QDial::notchSize_data() +{ + QTest::addColumn<int>("diameter"); + QTest::addColumn<int>("notchSize"); + + QTest::newRow("data0") << 50 << 4; + QTest::newRow("data1") << 80 << 4; + QTest::newRow("data2") << 95 << 4; + QTest::newRow("data3") << 110 << 4; + QTest::newRow("data4") << 152 << 2; + QTest::newRow("data5") << 210 << 2; + QTest::newRow("data6") << 228 << 1; +} + +void tst_QDial::notchSize() +{ + QFETCH(int, diameter); + QFETCH(int, notchSize); + QDial dial; + dial.setFixedSize(QSize(diameter, diameter)); + QCOMPARE(dial.notchSize(), notchSize); +} + QTEST_MAIN(tst_QDial) #include "tst_qdial.moc" diff --git a/tests/auto/widgets/widgets/qdialogbuttonbox/CMakeLists.txt b/tests/auto/widgets/widgets/qdialogbuttonbox/CMakeLists.txt index c42fccd87f..098b129cbc 100644 --- a/tests/auto/widgets/widgets/qdialogbuttonbox/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qdialogbuttonbox/CMakeLists.txt @@ -1,13 +1,21 @@ -# Generated from qdialogbuttonbox.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qdialogbuttonbox Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qdialogbuttonbox LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qdialogbuttonbox SOURCES tst_qdialogbuttonbox.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/widgets/qdialogbuttonbox/tst_qdialogbuttonbox.cpp b/tests/auto/widgets/widgets/qdialogbuttonbox/tst_qdialogbuttonbox.cpp index 2e7e248db7..3e7dc92da1 100644 --- a/tests/auto/widgets/widgets/qdialogbuttonbox/tst_qdialogbuttonbox.cpp +++ b/tests/auto/widgets/widgets/qdialogbuttonbox/tst_qdialogbuttonbox.cpp @@ -1,38 +1,17 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSignalSpy> #include <QtWidgets/QPushButton> #include <QtWidgets/QStyle> #include <QtWidgets/QLayout> #include <QtWidgets/QDialog> +#include <QtWidgets/QLineEdit> #include <QtGui/QAction> +#include <QtGui/QStyleHints> #include <qdialogbuttonbox.h> +#include <QtWidgets/private/qdialogbuttonbox_p.h> +#include <QtWidgets/private/qabstractbutton_p.h> #include <limits.h> Q_DECLARE_METATYPE(QDialogButtonBox::ButtonRole) @@ -74,6 +53,10 @@ private slots: void clear(); void removeButton_data(); void removeButton(); +#ifdef QT_BUILD_INTERNAL + void hideAndShowButton(); +#endif + void hideAndShowStandardButton(); void buttonRole_data(); void buttonRole(); void setStandardButtons_data(); @@ -99,6 +82,9 @@ private slots: void task191642_default(); void testDeletedStandardButton(); + void automaticDefaultButton(); + void initialFocus_data(); + void initialFocus(); private: qint64 timeStamp; @@ -122,7 +108,7 @@ void tst_QDialogButtonBox::testConstructor1() QDialogButtonBox buttonbox; QCOMPARE(buttonbox.orientation(), Qt::Horizontal); - QCOMPARE(buttonbox.buttons().count(), 0); + QCOMPARE(buttonbox.buttons().size(), 0); } void tst_QDialogButtonBox::layoutReuse() @@ -155,7 +141,7 @@ void tst_QDialogButtonBox::testConstructor2() QDialogButtonBox buttonBox(orient); QCOMPARE(buttonBox.orientation(), orient); - QCOMPARE(buttonBox.buttons().count(), 0); + QCOMPARE(buttonBox.buttons().size(), 0); } void tst_QDialogButtonBox::testConstructor3_data() @@ -192,7 +178,7 @@ void tst_QDialogButtonBox::testConstructor3() QDialogButtonBox buttonBox(buttons, (Qt::Orientation)orientation); QCOMPARE(int(buttonBox.orientation()), orientation); - QTEST(int(buttonBox.buttons().count()), "buttonCount"); + QTEST(int(buttonBox.buttons().size()), "buttonCount"); } void tst_QDialogButtonBox::testConstructor4_data() @@ -227,7 +213,7 @@ void tst_QDialogButtonBox::testConstructor4() QDialogButtonBox buttonBox(buttons); QCOMPARE(buttonBox.orientation(), Qt::Horizontal); - QTEST(int(buttonBox.buttons().count()), "buttonCount"); + QTEST(int(buttonBox.buttons().size()), "buttonCount"); } void tst_QDialogButtonBox::setOrientation_data() @@ -290,12 +276,12 @@ void tst_QDialogButtonBox::addButton1() { QFETCH(QDialogButtonBox::ButtonRole, role); QDialogButtonBox buttonBox; - QCOMPARE(buttonBox.buttons().count(), 0); + QCOMPARE(buttonBox.buttons().size(), 0); QPushButton *button = new QPushButton(); buttonBox.addButton(button, role); - QTEST(int(buttonBox.buttons().count()), "totalCount"); + QTEST(int(buttonBox.buttons().size()), "totalCount"); QList<QAbstractButton *> children = buttonBox.findChildren<QAbstractButton *>(); - QTEST(int(children.count()), "totalCount"); + QTEST(int(children.size()), "totalCount"); delete button; } @@ -318,11 +304,11 @@ void tst_QDialogButtonBox::addButton2() QFETCH(QString, text); QFETCH(QDialogButtonBox::ButtonRole, role); QDialogButtonBox buttonBox; - QCOMPARE(buttonBox.buttons().count(), 0); + QCOMPARE(buttonBox.buttons().size(), 0); buttonBox.addButton(text, role); - QTEST(int(buttonBox.buttons().count()), "totalCount"); + QTEST(int(buttonBox.buttons().size()), "totalCount"); QList<QAbstractButton *> children = buttonBox.findChildren<QAbstractButton *>(); - QTEST(int(children.count()), "totalCount"); + QTEST(int(children.size()), "totalCount"); } void tst_QDialogButtonBox::addButton3_data() @@ -345,11 +331,11 @@ void tst_QDialogButtonBox::addButton3() { QFETCH(QDialogButtonBox::StandardButton, button); QDialogButtonBox buttonBox; - QCOMPARE(buttonBox.buttons().count(), 0); + QCOMPARE(buttonBox.buttons().size(), 0); buttonBox.addButton(button); - QTEST(int(buttonBox.buttons().count()), "totalCount"); + QTEST(int(buttonBox.buttons().size()), "totalCount"); QList<QAbstractButton *> children = buttonBox.findChildren<QAbstractButton *>(); - QTEST(int(children.count()), "totalCount"); + QTEST(int(children.size()), "totalCount"); } void tst_QDialogButtonBox::clear_data() @@ -369,9 +355,9 @@ void tst_QDialogButtonBox::clear() for (int i = 1; i < rolesToAdd; ++i) buttonBox.addButton("Happy", QDialogButtonBox::ButtonRole(i)); buttonBox.clear(); - QCOMPARE(buttonBox.buttons().count(), 0); + QCOMPARE(buttonBox.buttons().size(), 0); QList<QAbstractButton *> children = buttonBox.findChildren<QAbstractButton *>(); - QCOMPARE(children.count(), 0); + QCOMPARE(children.size(), 0); } void tst_QDialogButtonBox::removeButton_data() @@ -387,31 +373,95 @@ void tst_QDialogButtonBox::removeButton() QFETCH(QDialogButtonBox::ButtonRole, roleToAdd); QDialogButtonBox buttonBox; - QCOMPARE(buttonBox.buttons().count(), 0); + QCOMPARE(buttonBox.buttons().size(), 0); QPushButton *button = new QPushButton("RemoveButton test"); buttonBox.addButton(button, roleToAdd); - QTEST(int(buttonBox.buttons().count()), "expectedCount"); + QTEST(int(buttonBox.buttons().size()), "expectedCount"); buttonBox.removeButton(button); - QCOMPARE(buttonBox.buttons().count(), 0); + QCOMPARE(buttonBox.buttons().size(), 0); delete button; } +#ifdef QT_BUILD_INTERNAL +void tst_QDialogButtonBox::hideAndShowButton() +{ + if (QGuiApplication::styleHints()->tabFocusBehavior() == Qt::NoTabFocus) + QSKIP("Test skipped with Qt::NoTabFocus"); + + QDialogButtonBox buttonBox; + QDialogButtonBoxPrivate *d = static_cast<QDialogButtonBoxPrivate *>(QObjectPrivate::get(&buttonBox)); + auto *apply = buttonBox.addButton( "Apply", QDialogButtonBox::ApplyRole ); + auto *accept = buttonBox.addButton( "Accept", QDialogButtonBox::AcceptRole ); + buttonBox.addButton( "Reject", QDialogButtonBox::RejectRole ); + auto *widget = new QWidget(); + auto *layout = new QHBoxLayout(widget); + layout->addWidget(&buttonBox); + + // apply button shows first on macOS. accept button on all other OSes. + QAbstractButton *hideButton; +#ifdef Q_OS_MACOS + hideButton = apply; + Q_UNUSED(accept); +#else + hideButton = accept; + Q_UNUSED(apply); +#endif + + hideButton->hide(); + widget->show(); + QVERIFY(QTest::qWaitForWindowExposed(widget)); + QTRY_VERIFY(buttonBox.focusWidget()); // QTBUG-114377: Without fixing, focusWidget() == nullptr + QCOMPARE(d->visibleButtons().count(), 2); + QCOMPARE(d->hiddenButtons.count(), 1); + QVERIFY(d->hiddenButtons.contains(hideButton)); + QCOMPARE(buttonBox.buttons().count(), 3); + QSignalSpy spy(qApp, &QApplication::focusChanged); + QTest::sendKeyEvent(QTest::KeyAction::Click, &buttonBox, Qt::Key_Tab, QString(), Qt::KeyboardModifiers()); + QCOMPARE(spy.count(), 1); // QTBUG-114377: Without fixing, tabbing wouldn't work. + hideButton->show(); + QCOMPARE_GE(spy.count(), 1); + QTRY_COMPARE(QApplication::focusWidget(), hideButton); + QCOMPARE(d->visibleButtons().count(), 3); + QCOMPARE(d->hiddenButtons.count(), 0); + QCOMPARE(buttonBox.buttons().count(), 3); + spy.clear(); + QTest::sendKeyEvent(QTest::KeyAction::Click, &buttonBox, Qt::Key_Backtab, QString(), Qt::KeyboardModifiers()); + QCOMPARE(spy.count(), 1); +} +#endif + +void tst_QDialogButtonBox::hideAndShowStandardButton() +{ + QDialogButtonBox buttonBox; + buttonBox.setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + buttonBox.show(); + QVERIFY(QTest::qWaitForWindowExposed(&buttonBox)); + auto *button = buttonBox.button(QDialogButtonBox::Cancel); + QVERIFY(button); + button->hide(); + QVERIFY(QTest::qWaitFor([button](){ return !button->isVisible(); })); + QCOMPARE(button, buttonBox.button(QDialogButtonBox::Cancel)); + button->show(); + QVERIFY(QTest::qWaitForWindowExposed(button)); + QCOMPARE(button, buttonBox.button(QDialogButtonBox::Cancel)); +} + void tst_QDialogButtonBox::testDelete() { QDialogButtonBox buttonBox; - QCOMPARE(buttonBox.buttons().count(), 0); + QCOMPARE(buttonBox.buttons().size(), 0); QPushButton *deleteMe = new QPushButton("Happy"); buttonBox.addButton(deleteMe, QDialogButtonBox::HelpRole); - QCOMPARE(buttonBox.buttons().count(), 1); + QCOMPARE(buttonBox.buttons().size(), 1); QList<QAbstractButton *> children = buttonBox.findChildren<QAbstractButton *>(); - QCOMPARE(children.count(), 1); + QCOMPARE(children.size(), 1); delete deleteMe; children = buttonBox.findChildren<QAbstractButton *>(); - QCOMPARE(children.count(), 0); - QCOMPARE(buttonBox.buttons().count(), 0); + QCOMPARE(children.size(), 0); + QCOMPARE(buttonBox.buttons().size(), 0); } class ObjectDeleter : public QObject @@ -433,7 +483,7 @@ void tst_QDialogButtonBox::testSignalEmissionAfterDelete_QTBUG_45835() { { QDialogButtonBox buttonBox; - QCOMPARE(buttonBox.buttons().count(), 0); + QCOMPARE(buttonBox.buttons().size(), 0); QSignalSpy buttonClickedSpy(&buttonBox, &QDialogButtonBox::clicked); QVERIFY(buttonClickedSpy.isValid()); @@ -442,21 +492,21 @@ void tst_QDialogButtonBox::testSignalEmissionAfterDelete_QTBUG_45835() QVERIFY(buttonBoxAcceptedSpy.isValid()); QPushButton *button = buttonBox.addButton("Test", QDialogButtonBox::AcceptRole); - QCOMPARE(buttonBox.buttons().count(), 1); + QCOMPARE(buttonBox.buttons().size(), 1); ObjectDeleter objectDeleter; connect(&buttonBox, &QDialogButtonBox::clicked, &objectDeleter, &ObjectDeleter::deleteButton); button->click(); - QCOMPARE(buttonBox.buttons().count(), 0); - QCOMPARE(buttonClickedSpy.count(), 1); - QCOMPARE(buttonBoxAcceptedSpy.count(), 1); + QCOMPARE(buttonBox.buttons().size(), 0); + QCOMPARE(buttonClickedSpy.size(), 1); + QCOMPARE(buttonBoxAcceptedSpy.size(), 1); } { QPointer<QDialogButtonBox> buttonBox(new QDialogButtonBox); - QCOMPARE(buttonBox->buttons().count(), 0); + QCOMPARE(buttonBox->buttons().size(), 0); QSignalSpy buttonClickedSpy(buttonBox.data(), &QDialogButtonBox::clicked); QVERIFY(buttonClickedSpy.isValid()); @@ -465,7 +515,7 @@ void tst_QDialogButtonBox::testSignalEmissionAfterDelete_QTBUG_45835() QVERIFY(buttonBoxAcceptedSpy.isValid()); QPushButton *button = buttonBox->addButton("Test", QDialogButtonBox::AcceptRole); - QCOMPARE(buttonBox->buttons().count(), 1); + QCOMPARE(buttonBox->buttons().size(), 1); ObjectDeleter objectDeleter; connect(buttonBox.data(), &QDialogButtonBox::clicked, &objectDeleter, &ObjectDeleter::deleteSender); @@ -473,8 +523,8 @@ void tst_QDialogButtonBox::testSignalEmissionAfterDelete_QTBUG_45835() button->click(); QVERIFY(buttonBox.isNull()); - QCOMPARE(buttonClickedSpy.count(), 1); - QCOMPARE(buttonBoxAcceptedSpy.count(), 0); + QCOMPARE(buttonClickedSpy.size(), 1); + QCOMPARE(buttonBoxAcceptedSpy.size(), 0); } } @@ -482,24 +532,24 @@ void tst_QDialogButtonBox::testMultipleAdd() { // Add a button into the thing multiple times. QDialogButtonBox buttonBox; - QCOMPARE(buttonBox.buttons().count(), 0); + QCOMPARE(buttonBox.buttons().size(), 0); QPushButton *button = new QPushButton("Foo away"); buttonBox.addButton(button, QDialogButtonBox::AcceptRole); - QCOMPARE(buttonBox.buttons().count(), 1); + QCOMPARE(buttonBox.buttons().size(), 1); QCOMPARE(buttonBox.buttonRole(button), QDialogButtonBox::AcceptRole); buttonBox.addButton(button, QDialogButtonBox::AcceptRole); - QCOMPARE(buttonBox.buttons().count(), 1); + QCOMPARE(buttonBox.buttons().size(), 1); QCOMPARE(buttonBox.buttonRole(button), QDialogButtonBox::AcceptRole); // Add it again with a different role buttonBox.addButton(button, QDialogButtonBox::RejectRole); - QCOMPARE(buttonBox.buttons().count(), 1); + QCOMPARE(buttonBox.buttons().size(), 1); QCOMPARE(buttonBox.buttonRole(button), QDialogButtonBox::RejectRole); // Add it as an "invalid" role buttonBox.addButton(button, QDialogButtonBox::InvalidRole); - QCOMPARE(buttonBox.buttons().count(), 1); + QCOMPARE(buttonBox.buttons().size(), 1); QCOMPARE(buttonBox.buttonRole(button), QDialogButtonBox::RejectRole); } @@ -619,13 +669,13 @@ void tst_QDialogButtonBox::testSignals() if (clickMe) clickMe->click(); - QTRY_COMPARE(clicked2.count(), clicked2Count); - if (clicked2.count() > 0) + QTRY_COMPARE(clicked2.size(), clicked2Count); + if (clicked2.size() > 0) QCOMPARE(qvariant_cast<QAbstractButton *>(clicked2.at(0).at(0)), clickMe); - QTEST(int(accept.count()), "acceptCount"); - QTEST(int(reject.count()), "rejectCount"); - QTEST(int(helpRequested.count()), "helpRequestedCount"); + QTEST(int(accept.size()), "acceptCount"); + QTEST(int(reject.size()), "rejectCount"); + QTEST(int(helpRequested.size()), "helpRequestedCount"); } void tst_QDialogButtonBox::testSignalOrder() @@ -781,7 +831,7 @@ void tst_QDialogButtonBox::testRemove() button->click(); QTest::qWait(100); - QCOMPARE(clicked.count(), 0); + QCOMPARE(clicked.size(), 0); delete button; } @@ -843,7 +893,7 @@ void tst_QDialogButtonBox::task191642_default() QVERIFY(QTest::qWaitForWindowActive(&dlg)); QVERIFY(def->isDefault()); QTest::keyPress( &dlg, Qt::Key_Enter ); - QCOMPARE(clicked.count(), 1); + QCOMPARE(clicked.size(), 1); } void tst_QDialogButtonBox::testDeletedStandardButton() @@ -863,5 +913,87 @@ void tst_QDialogButtonBox::testDeletedStandardButton() QVERIFY(!buttonC); } +void tst_QDialogButtonBox::automaticDefaultButton() +{ + // Having a QDialogButtonBox inside a QDialog triggers Qt to + // enable autoDefault for QPushButtons inside the button box. + // Check that the logic for resolving a default button based + // on the Accept role is not overridden by the first button + // in the dialog (the focus proxy) taking focus, and hence + // stealing the default button state. + + { + QDialog dialog; + QDialogButtonBox *bb = new QDialogButtonBox(&dialog); + // Force horizontal orientation, where we know the order between + // Reset and Accept roles are always the same for all layouts. + bb->setOrientation(Qt::Horizontal); + auto *okButton = bb->addButton(QDialogButtonBox::Ok); + auto *resetButton = bb->addButton(QDialogButtonBox::Reset); + // Double check our assumption about Reset being first + QCOMPARE(bb->layout()->itemAt(0)->widget(), resetButton); + + dialog.show(); + QVERIFY(QTest::qWaitForWindowActive(&dialog)); + + QVERIFY(okButton->isDefault()); + QSignalSpy buttonClicked(okButton, &QPushButton::clicked); + QTest::keyPress(&dialog, Qt::Key_Enter); + QCOMPARE(buttonClicked.count(), 1); + } + + // However, if an explicit button has been focused, we respect that. + + { + QDialog dialog; + QDialogButtonBox *bb = new QDialogButtonBox(&dialog); + bb->setOrientation(Qt::Horizontal); + bb->addButton(QDialogButtonBox::Ok); + auto *resetButton = bb->addButton(QDialogButtonBox::Reset); + resetButton->setFocus(); + dialog.show(); + QVERIFY(QTest::qWaitForWindowActive(&dialog)); + + QVERIFY(resetButton->isDefault()); + QSignalSpy buttonClicked(resetButton, &QPushButton::clicked); + QTest::keyPress(&dialog, Qt::Key_Enter); + QCOMPARE(buttonClicked.count(), 1); + } +} + +void tst_QDialogButtonBox::initialFocus_data() +{ + QTest::addColumn<Qt::FocusPolicy>("focusPolicy"); + QTest::addColumn<bool>("lineEditHasFocus"); + + QTest::addRow("TabFocus") << Qt::FocusPolicy::TabFocus << false; + QTest::addRow("StrongFocus") << Qt::FocusPolicy::StrongFocus << true; + QTest::addRow("NoFocus") << Qt::FocusPolicy::NoFocus << false; + QTest::addRow("ClickFocus") << Qt::FocusPolicy::ClickFocus << false; + QTest::addRow("WheelFocus") << Qt::FocusPolicy::WheelFocus << false; +} + +void tst_QDialogButtonBox::initialFocus() +{ + QFETCH(const Qt::FocusPolicy, focusPolicy); + QFETCH(const bool, lineEditHasFocus); + QDialog dialog; + QVBoxLayout *layout = new QVBoxLayout(&dialog); + QLineEdit *lineEdit = new QLineEdit(&dialog); + lineEdit->setFocusPolicy(focusPolicy); + layout->addWidget(lineEdit); + QDialogButtonBox *dialogButtonBox = new QDialogButtonBox(&dialog); + layout->addWidget(dialogButtonBox); + dialogButtonBox->addButton(QDialogButtonBox::Reset); + const auto *firstAcceptButton = dialogButtonBox->addButton(QDialogButtonBox::Ok); + dialogButtonBox->addButton(QDialogButtonBox::Cancel); + dialog.show(); + dialog.activateWindow(); + if (lineEditHasFocus) + QTRY_VERIFY(lineEdit->hasFocus()); + else + QTRY_VERIFY(firstAcceptButton->hasFocus()); +} + QTEST_MAIN(tst_QDialogButtonBox) #include "tst_qdialogbuttonbox.moc" diff --git a/tests/auto/widgets/widgets/qdockwidget/BLACKLIST b/tests/auto/widgets/widgets/qdockwidget/BLACKLIST index 6a3b189939..8873589ff4 100644 --- a/tests/auto/widgets/widgets/qdockwidget/BLACKLIST +++ b/tests/auto/widgets/widgets/qdockwidget/BLACKLIST @@ -1,3 +1,36 @@ # QTBUG-87415 [task169808_setFloating] android +# +# QDockWidget::isFloating() is flaky after state change on these OS +[closeAndDelete] +macos +b2qt +arm +android + +# QTBUG-103091 +[floatingTabs] +arm +android +qnx +macos +b2qt + +# QTBUG-103091 +[hoverWithoutDrop] +arm +android +qnx +macos +b2qt + +# OSes are flaky because of unplugging and plugging requires +# precise calculation of the title bar area for mouse emulation +# That's not possible for floating dock widgets. +[deleteFloatingTabWithSingleDockWidget] +qnx +b2qt +arm +android +macos diff --git a/tests/auto/widgets/widgets/qdockwidget/CMakeLists.txt b/tests/auto/widgets/widgets/qdockwidget/CMakeLists.txt index adf48ddb11..21d8fb60cc 100644 --- a/tests/auto/widgets/widgets/qdockwidget/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qdockwidget/CMakeLists.txt @@ -1,13 +1,21 @@ -# Generated from qdockwidget.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qdockwidget Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qdockwidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qdockwidget SOURCES tst_qdockwidget.cpp - PUBLIC_LIBRARIES + LIBRARIES + Qt::Core Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp b/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp index 4aa8e9887e..af62d0d089 100644 --- a/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp +++ b/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp @@ -1,42 +1,25 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSignalSpy> - #include <qaction.h> #include <qdockwidget.h> #include <qmainwindow.h> +#include "private/qdockwidget_p.h" +#include "private/qmainwindowlayout_p.h" +#include <QAbstractButton> #include <qlineedit.h> +#include <QtGui/qpa/qplatformwindow.h> #include <qtabbar.h> #include <QScreen> +#include <QTimer> #include <QtGui/QPainter> -#include "private/qdockwidget_p.h" +#include <QLabel> + +Q_LOGGING_CATEGORY(lcTestDockWidget, "qt.widgets.tests.qdockwidget") + +#include <QtWidgets/private/qapplication_p.h> bool hasFeature(QDockWidget *dockwidget, QDockWidget::DockWidgetFeature feature) { return (dockwidget->features() & feature) == feature; } @@ -70,6 +53,7 @@ private slots: void restoreDockWidget(); void restoreStateWhileStillFloating(); void setWindowTitle(); + // task specific tests: void task165177_deleteFocusWidget(); void task169808_setFloating(); @@ -78,6 +62,98 @@ private slots: void task258459_visibilityChanged(); void taskQTBUG_1665_closableChanged(); void taskQTBUG_9758_undockedGeometry(); + + // Dock area permissions for DockWidgets and DockWidgetGroupWindows + void dockPermissions(); + + // test floating tabs, item_tree and window title consistency + void floatingTabs(); + void hoverWithoutDrop(); + + // floating tab gets removed, when last child goes away + void deleteFloatingTabWithSingleDockWidget_data(); + void deleteFloatingTabWithSingleDockWidget(); + + // test hide & show + void hideAndShow(); + + // test closing and deleting consistency + void closeAndDelete(); + void closeUnclosable(); + + // test save and restore consistency + void saveAndRestore(); + +private: + // helpers and consts for dockPermissions, hideAndShow, closeAndDelete +#ifdef QT_BUILD_INTERNAL + void createTestWidgets(QMainWindow* &MainWindow, QPointer<QWidget> ¢, + QPointer<QDockWidget> &d1, QPointer<QDockWidget> &d2) const; + + void unplugAndResize(QMainWindow* MainWindow, QDockWidget* dw, QPoint home, QSize size) const; + + void createFloatingTabs(QMainWindow* &MainWindow, QPointer<QWidget> ¢, + QPointer<QDockWidget> &d1, QPointer<QDockWidget> &d2, + QList<int> &path1, QList<int> &path2) const; + + static inline QPoint dragPoint(QDockWidget* dockWidget); + static inline QPoint home1(QMainWindow* MainWindow) + { return MainWindow->mapToGlobal(MainWindow->rect().topLeft() + QPoint(0.1 * MainWindow->width(), 0.1 * MainWindow->height())); } + + static inline QPoint home2(QMainWindow* MainWindow) + { return MainWindow->mapToGlobal(MainWindow->rect().topLeft() + QPoint(0.6 * MainWindow->width(), 0.15 * MainWindow->height())); } + + static inline QSize size1(QMainWindow* MainWindow) + { return QSize (0.2 * MainWindow->width(), 0.25 * MainWindow->height()); } + + static inline QSize size2(QMainWindow* MainWindow) + { return QSize (0.1 * MainWindow->width(), 0.15 * MainWindow->height()); } + + static inline QPoint dockPoint(QMainWindow* mw, Qt::DockWidgetArea area) + { return mw->mapToGlobal(qobject_cast<QMainWindowLayout*>(mw->layout())->dockWidgetAreaRect(area, QMainWindowLayout::Maximum).center()); } + + bool checkFloatingTabs(QMainWindow* MainWindow, QPointer<QDockWidgetGroupWindow> &ftabs, const QList<QDockWidget*> &dwList = {}) const; + + // move a dock widget + enum class MoveDockWidgetRule { + Drop, + Abort + }; + + void moveDockWidget(QDockWidget* dw, QPoint to, QPoint from, MoveDockWidgetRule rule) const; + +#ifdef QT_BUILD_INTERNAL + // Message handling for xcb error QTBUG 82059 + static void xcbMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg); + + enum class ChildRemovalReason { + Destroyed, + Closed, + Reparented + }; + +public: + bool xcbError = false; + bool platformSupportingRaise = true; +#endif +private: + +#ifdef QT_DEBUG + // Grace time between mouse events. Set to 400 for debugging. + const int waitingTime = 400; + + // Waiting time before closing widgets successful test. Set to 20000 for debugging. + const int waitBeforeClose = 0; + + // Enable logging + const bool dockWidgetLog = true; +#else + const int waitingTime = 15; + const int waitBeforeClose = 0; + const bool dockWidgetLog = false; +#endif // QT_DEBUG +#endif // QT_BUILD_INTERNAL + }; // Testing get/set functions @@ -219,12 +295,12 @@ void tst_QDockWidget::features() QVERIFY(!hasFeature(&dw, QDockWidget::DockWidgetClosable)); QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetMovable)); QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetFloatable)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE((int)*(static_cast<const QDockWidget::DockWidgetFeature *>(spy.at(0).value(0).constData())), (int)dw.features()); spy.clear(); dw.setFeatures(dw.features()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); spy.clear(); setFeature(&dw, QDockWidget::DockWidgetClosable); @@ -232,12 +308,12 @@ void tst_QDockWidget::features() QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetClosable)); QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetMovable)); QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetFloatable)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE((int)*static_cast<const QDockWidget::DockWidgetFeature *>(spy.at(0).value(0).constData()), (int)dw.features()); spy.clear(); dw.setFeatures(dw.features()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); spy.clear(); setFeature(&dw, QDockWidget::DockWidgetMovable, false); @@ -245,12 +321,12 @@ void tst_QDockWidget::features() QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetClosable)); QVERIFY(!hasFeature(&dw, QDockWidget::DockWidgetMovable)); QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetFloatable)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE((int)*static_cast<const QDockWidget::DockWidgetFeature *>(spy.at(0).value(0).constData()), (int)dw.features()); spy.clear(); dw.setFeatures(dw.features()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); spy.clear(); setFeature(&dw, QDockWidget::DockWidgetMovable); @@ -258,12 +334,12 @@ void tst_QDockWidget::features() QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetClosable)); QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetMovable)); QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetFloatable)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE((int)*static_cast<const QDockWidget::DockWidgetFeature *>(spy.at(0).value(0).constData()), (int)dw.features()); spy.clear(); dw.setFeatures(dw.features()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); spy.clear(); setFeature(&dw, QDockWidget::DockWidgetFloatable, false); @@ -271,12 +347,12 @@ void tst_QDockWidget::features() QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetClosable)); QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetMovable)); QVERIFY(!hasFeature(&dw, QDockWidget::DockWidgetFloatable)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE((int)*static_cast<const QDockWidget::DockWidgetFeature *>(spy.at(0).value(0).constData()), (int)dw.features()); spy.clear(); dw.setFeatures(dw.features()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); spy.clear(); setFeature(&dw, QDockWidget::DockWidgetFloatable); @@ -284,12 +360,12 @@ void tst_QDockWidget::features() QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetClosable)); QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetMovable)); QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetFloatable)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE((int)*static_cast<const QDockWidget::DockWidgetFeature *>(spy.at(0).value(0).constData()), (int)dw.features()); spy.clear(); dw.setFeatures(dw.features()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); spy.clear(); // set all at once @@ -298,12 +374,12 @@ void tst_QDockWidget::features() QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetClosable)); QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetMovable)); QVERIFY(!hasFeature(&dw, QDockWidget::DockWidgetFloatable)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE((int)*static_cast<const QDockWidget::DockWidgetFeature *>(spy.at(0).value(0).constData()), (int)dw.features()); spy.clear(); dw.setFeatures(dw.features()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); spy.clear(); dw.setFeatures(QDockWidget::DockWidgetClosable); @@ -311,12 +387,12 @@ void tst_QDockWidget::features() QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetClosable)); QVERIFY(!hasFeature(&dw, QDockWidget::DockWidgetMovable)); QVERIFY(!hasFeature(&dw, QDockWidget::DockWidgetFloatable)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE((int)*static_cast<const QDockWidget::DockWidgetFeature *>(spy.at(0).value(0).constData()), (int)dw.features()); spy.clear(); dw.setFeatures(dw.features()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); spy.clear(); dw.setFeatures(allDockWidgetFeatures); @@ -324,12 +400,12 @@ void tst_QDockWidget::features() QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetClosable)); QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetMovable)); QVERIFY(hasFeature(&dw, QDockWidget::DockWidgetFloatable)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE((int)*static_cast<const QDockWidget::DockWidgetFeature *>(spy.at(0).value(0).constData()), (int)dw.features()); spy.clear(); dw.setFeatures(dw.features()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); spy.clear(); } @@ -356,20 +432,20 @@ void tst_QDockWidget::setFloating() QVERIFY((dockedPosition - floatingPosition).manhattanLength() < 50); QVERIFY(dw.isFloating()); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).value(0).toBool(), dw.isFloating()); spy.clear(); dw.setFloating(dw.isFloating()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); spy.clear(); dw.setFloating(false); QVERIFY(!dw.isFloating()); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).value(0).toBool(), dw.isFloating()); spy.clear(); dw.setFloating(dw.isFloating()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); spy.clear(); } @@ -393,12 +469,12 @@ void tst_QDockWidget::allowedAreas() QVERIFY(!dw.isAreaAllowed(Qt::RightDockWidgetArea)); QVERIFY(!dw.isAreaAllowed(Qt::TopDockWidgetArea)); QVERIFY(!dw.isAreaAllowed(Qt::BottomDockWidgetArea)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::DockWidgetAreas *>(spy.at(0).value(0).constData()), dw.allowedAreas()); spy.clear(); dw.setAllowedAreas(dw.allowedAreas()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); dw.setAllowedAreas(Qt::RightDockWidgetArea); QCOMPARE(dw.allowedAreas(), Qt::RightDockWidgetArea); @@ -406,12 +482,12 @@ void tst_QDockWidget::allowedAreas() QVERIFY(dw.isAreaAllowed(Qt::RightDockWidgetArea)); QVERIFY(!dw.isAreaAllowed(Qt::TopDockWidgetArea)); QVERIFY(!dw.isAreaAllowed(Qt::BottomDockWidgetArea)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::DockWidgetAreas *>(spy.at(0).value(0).constData()), dw.allowedAreas()); spy.clear(); dw.setAllowedAreas(dw.allowedAreas()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); dw.setAllowedAreas(Qt::TopDockWidgetArea); QCOMPARE(dw.allowedAreas(), Qt::TopDockWidgetArea); @@ -419,12 +495,12 @@ void tst_QDockWidget::allowedAreas() QVERIFY(!dw.isAreaAllowed(Qt::RightDockWidgetArea)); QVERIFY(dw.isAreaAllowed(Qt::TopDockWidgetArea)); QVERIFY(!dw.isAreaAllowed(Qt::BottomDockWidgetArea)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::DockWidgetAreas *>(spy.at(0).value(0).constData()), dw.allowedAreas()); spy.clear(); dw.setAllowedAreas(dw.allowedAreas()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); dw.setAllowedAreas(Qt::BottomDockWidgetArea); QCOMPARE(dw.allowedAreas(), Qt::BottomDockWidgetArea); @@ -432,12 +508,12 @@ void tst_QDockWidget::allowedAreas() QVERIFY(!dw.isAreaAllowed(Qt::RightDockWidgetArea)); QVERIFY(!dw.isAreaAllowed(Qt::TopDockWidgetArea)); QVERIFY(dw.isAreaAllowed(Qt::BottomDockWidgetArea)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::DockWidgetAreas *>(spy.at(0).value(0).constData()), dw.allowedAreas()); spy.clear(); dw.setAllowedAreas(dw.allowedAreas()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); // multiple dock window areas dw.setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea); @@ -446,12 +522,13 @@ void tst_QDockWidget::allowedAreas() QVERIFY(!dw.isAreaAllowed(Qt::RightDockWidgetArea)); QVERIFY(dw.isAreaAllowed(Qt::TopDockWidgetArea)); QVERIFY(dw.isAreaAllowed(Qt::BottomDockWidgetArea)); - QCOMPARE(spy.count(), 1); + //QVERIFY(!dw.isAreaAllowed(Qt::FloatingDockWidgetArea)); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::DockWidgetAreas *>(spy.at(0).value(0).constData()), dw.allowedAreas()); spy.clear(); dw.setAllowedAreas(dw.allowedAreas()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); dw.setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); QCOMPARE(dw.allowedAreas(), Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); @@ -459,12 +536,13 @@ void tst_QDockWidget::allowedAreas() QVERIFY(dw.isAreaAllowed(Qt::RightDockWidgetArea)); QVERIFY(!dw.isAreaAllowed(Qt::TopDockWidgetArea)); QVERIFY(!dw.isAreaAllowed(Qt::BottomDockWidgetArea)); - QCOMPARE(spy.count(), 1); + //QVERIFY(!dw.isAreaAllowed(Qt::FloatingDockWidgetArea)); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::DockWidgetAreas *>(spy.at(0).value(0).constData()), dw.allowedAreas()); spy.clear(); dw.setAllowedAreas(dw.allowedAreas()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); dw.setAllowedAreas(Qt::TopDockWidgetArea | Qt::LeftDockWidgetArea); QCOMPARE(dw.allowedAreas(), Qt::TopDockWidgetArea | Qt::LeftDockWidgetArea); @@ -472,12 +550,28 @@ void tst_QDockWidget::allowedAreas() QVERIFY(!dw.isAreaAllowed(Qt::RightDockWidgetArea)); QVERIFY(dw.isAreaAllowed(Qt::TopDockWidgetArea)); QVERIFY(!dw.isAreaAllowed(Qt::BottomDockWidgetArea)); - QCOMPARE(spy.count(), 1); + //QVERIFY(!dw.isAreaAllowed(Qt::FloatingDockWidgetArea)); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::DockWidgetAreas *>(spy.at(0).value(0).constData()), dw.allowedAreas()); spy.clear(); dw.setAllowedAreas(dw.allowedAreas()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); + + //dw.setAllowedAreas(Qt::BottomDockWidgetArea | Qt::FloatingDockWidgetArea); + dw.setAllowedAreas(Qt::BottomDockWidgetArea); + //QCOMPARE(dw.allowedAreas(), Qt::BottomDockWidgetArea | Qt::FloatingDockWidgetArea); + QVERIFY(!dw.isAreaAllowed(Qt::LeftDockWidgetArea)); + QVERIFY(!dw.isAreaAllowed(Qt::RightDockWidgetArea)); + QVERIFY(!dw.isAreaAllowed(Qt::TopDockWidgetArea)); + QVERIFY(dw.isAreaAllowed(Qt::BottomDockWidgetArea)); + //QVERIFY(dw.isAreaAllowed(Qt::FloatingDockWidgetArea)); + QCOMPARE(spy.size(), 1); + QCOMPARE(*static_cast<const Qt::DockWidgetAreas *>(spy.at(0).value(0).constData()), + dw.allowedAreas()); + spy.clear(); + dw.setAllowedAreas(dw.allowedAreas()); + QCOMPARE(spy.size(), 0); dw.setAllowedAreas(Qt::BottomDockWidgetArea | Qt::RightDockWidgetArea); QCOMPARE(dw.allowedAreas(), Qt::BottomDockWidgetArea | Qt::RightDockWidgetArea); @@ -485,12 +579,13 @@ void tst_QDockWidget::allowedAreas() QVERIFY(dw.isAreaAllowed(Qt::RightDockWidgetArea)); QVERIFY(!dw.isAreaAllowed(Qt::TopDockWidgetArea)); QVERIFY(dw.isAreaAllowed(Qt::BottomDockWidgetArea)); - QCOMPARE(spy.count(), 1); + //QVERIFY(!dw.isAreaAllowed(Qt::FloatingDockWidgetArea)); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::DockWidgetAreas *>(spy.at(0).value(0).constData()), dw.allowedAreas()); spy.clear(); dw.setAllowedAreas(dw.allowedAreas()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } void tst_QDockWidget::toggleViewAction() @@ -518,65 +613,65 @@ void tst_QDockWidget::visibilityChanged() mw.addDockWidget(Qt::LeftDockWidgetArea, &dw); mw.show(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toBool(), true); spy.clear(); dw.hide(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toBool(), false); spy.clear(); dw.hide(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); dw.show(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toBool(), true); spy.clear(); dw.show(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QDockWidget dw2; mw.tabifyDockWidget(&dw, &dw2); dw2.show(); dw2.raise(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toBool(), false); spy.clear(); dw2.hide(); qApp->processEvents(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toBool(), true); spy.clear(); dw2.show(); dw2.raise(); qApp->processEvents(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toBool(), false); spy.clear(); dw.raise(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toBool(), true); spy.clear(); dw.raise(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); dw2.raise(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toBool(), false); spy.clear(); dw2.raise(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); mw.addDockWidget(Qt::RightDockWidgetArea, &dw2); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toBool(), true); } @@ -603,6 +698,9 @@ void tst_QDockWidget::updateTabBarOnVisibilityChanged() mw.tabifyDockWidget(dw1, dw2); mw.tabifyDockWidget(dw2, dw3); + const auto list1 = QList<QDockWidget *>{dw1, dw2, dw3}; + QCOMPARE(mw.tabifiedDockWidgets(dw0), list1); + QTabBar *tabBar = mw.findChild<QTabBar *>(); QVERIFY(tabBar); tabBar->setCurrentIndex(2); @@ -616,6 +714,11 @@ void tst_QDockWidget::updateTabBarOnVisibilityChanged() dw1->hide(); QTRY_COMPARE(tabBar->count(), 2); QCOMPARE(tabBar->currentIndex(), 0); + + QCOMPARE(mw.tabifiedDockWidgets(dw2), {dw3}); + + mw.removeDockWidget(dw3); + QCOMPARE(mw.tabifiedDockWidgets(dw2).count(), 0); } Q_DECLARE_METATYPE(Qt::DockWidgetArea) @@ -630,56 +733,56 @@ void tst_QDockWidget::dockLocationChanged() QSignalSpy spy(&dw, SIGNAL(dockLocationChanged(Qt::DockWidgetArea))); mw.addDockWidget(Qt::LeftDockWidgetArea, &dw); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(qvariant_cast<Qt::DockWidgetArea>(spy.at(0).at(0)), Qt::LeftDockWidgetArea); spy.clear(); mw.addDockWidget(Qt::LeftDockWidgetArea, &dw); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(qvariant_cast<Qt::DockWidgetArea>(spy.at(0).at(0)), Qt::LeftDockWidgetArea); spy.clear(); mw.addDockWidget(Qt::RightDockWidgetArea, &dw); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(qvariant_cast<Qt::DockWidgetArea>(spy.at(0).at(0)), Qt::RightDockWidgetArea); spy.clear(); mw.removeDockWidget(&dw); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QDockWidget dw2; dw2.setObjectName("dock2"); mw.addDockWidget(Qt::TopDockWidgetArea, &dw2); mw.tabifyDockWidget(&dw2, &dw); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(qvariant_cast<Qt::DockWidgetArea>(spy.at(0).at(0)), Qt::TopDockWidgetArea); spy.clear(); mw.splitDockWidget(&dw2, &dw, Qt::Horizontal); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(qvariant_cast<Qt::DockWidgetArea>(spy.at(0).at(0)), Qt::TopDockWidgetArea); spy.clear(); dw.setFloating(true); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(qvariant_cast<Qt::DockWidgetArea>(spy.at(0).at(0)), Qt::NoDockWidgetArea); spy.clear(); dw.setFloating(false); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(qvariant_cast<Qt::DockWidgetArea>(spy.at(0).at(0)), Qt::TopDockWidgetArea); spy.clear(); QByteArray ba = mw.saveState(); mw.restoreState(ba); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(qvariant_cast<Qt::DockWidgetArea>(spy.at(0).at(0)), Qt::TopDockWidgetArea); } @@ -779,6 +882,9 @@ void tst_QDockWidget::restoreStateWhileStillFloating() void tst_QDockWidget::restoreDockWidget() { + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Fails on Wayland: QTBUG-91483"); + QByteArray geometry; QByteArray state; @@ -947,9 +1053,9 @@ void tst_QDockWidget::task258459_visibilityChanged() QSignalSpy spy2(&dock2, SIGNAL(visibilityChanged(bool))); win.show(); QVERIFY(QTest::qWaitForWindowActive(&win)); - QCOMPARE(spy1.count(), 1); + QCOMPARE(spy1.size(), 1); QCOMPARE(spy1.first().first().toBool(), false); //dock1 is invisible - QCOMPARE(spy2.count(), 1); + QCOMPARE(spy2.size(), 1); QCOMPARE(spy2.first().first().toBool(), true); //dock1 is visible } @@ -973,6 +1079,9 @@ void tst_QDockWidget::taskQTBUG_1665_closableChanged() void tst_QDockWidget::taskQTBUG_9758_undockedGeometry() { + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Fails on Wayland: QTBUG-91483"); + QMainWindow window; QDockWidget dock1(&window); QDockWidget dock2(&window); @@ -996,9 +1105,10 @@ void tst_QDockWidget::setWindowTitle() QMainWindow window; QDockWidget dock1(&window); QDockWidget dock2(&window); - const QString dock1Title = QStringLiteral("&Window"); - const QString dock2Title = QStringLiteral("&Modifiable Window [*]"); + constexpr QLatin1StringView dock1Title("&Window"); + constexpr QLatin1StringView dock2Title("&Modifiable Window [*]"); + // Set title on docked dock widgets, before main window is shown dock1.setWindowTitle(dock1Title); dock2.setWindowTitle(dock2Title); window.addDockWidget(Qt::RightDockWidgetArea, &dock1); @@ -1009,6 +1119,7 @@ void tst_QDockWidget::setWindowTitle() QCOMPARE(dock1.windowTitle(), dock1Title); QCOMPARE(dock2.windowTitle(), dock2Title); + // Check if title remains unchanged when docking / undocking dock1.setFloating(true); dock1.show(); QVERIFY(QTest::qWaitForWindowExposed(&dock1)); @@ -1018,12 +1129,16 @@ void tst_QDockWidget::setWindowTitle() dock1.setFloating(true); dock1.show(); QVERIFY(QTest::qWaitForWindowExposed(&dock1)); - const QString changed = QStringLiteral("Changed "); + + // Change a floating dock widget's title and check remains unchanged when docking + constexpr QLatin1StringView changed("Changed "); dock1.setWindowTitle(QString(changed + dock1Title)); QCOMPARE(dock1.windowTitle(), QString(changed + dock1Title)); dock1.setFloating(false); + QVERIFY(QTest::qWaitFor([&dock1](){ return !dock1.windowHandle(); })); QCOMPARE(dock1.windowTitle(), QString(changed + dock1Title)); + // Test consistency after toggling modified and floating dock2.setWindowModified(true); QCOMPARE(dock2.windowTitle(), dock2Title); dock2.setFloating(true); @@ -1038,6 +1153,846 @@ void tst_QDockWidget::setWindowTitle() dock2.show(); QVERIFY(QTest::qWaitForWindowExposed(&dock2)); QCOMPARE(dock2.windowTitle(), dock2Title); + + // Test title change of a closed dock widget + static constexpr QLatin1StringView closedDock2("Closed D2"); + dock2.close(); + dock2.setWindowTitle(closedDock2); + QCOMPARE(dock2.windowTitle(), closedDock2); +} + +// helpers for dockPermissions, hideAndShow, closeAndDelete +#ifdef QT_BUILD_INTERNAL +void tst_QDockWidget::createTestWidgets(QMainWindow* &mainWindow, QPointer<QWidget> ¢, QPointer<QDockWidget> &d1, QPointer<QDockWidget> &d2) const +{ + // Enable logging if required + if (dockWidgetLog) + QLoggingCategory::setFilterRules("qt.widgets.dockwidgets=true"); + + // Derive sizes and positions from primary screen + const QRect screen = QApplication::primaryScreen()->availableGeometry(); + const QPoint m_topLeft = screen.topLeft(); + const QSize s_mwindow = QApplication::primaryScreen()->availableSize() * 0.7; + + mainWindow = new QMainWindow; + mainWindow->setMouseTracking(true); + mainWindow->setFixedSize(s_mwindow); + cent = new QWidget; + mainWindow->setCentralWidget(cent); + cent->setLayout(new QGridLayout); + cent->layout()->setContentsMargins(0, 0, 0, 0); + cent->setMinimumSize(0, 0); + mainWindow->setDockOptions(QMainWindow::AllowTabbedDocks | QMainWindow::GroupedDragging); + mainWindow->move(m_topLeft); + + const int minWidth = QApplication::style()->pixelMetric(QStyle::PM_TitleBarHeight); + const QSize minSize(minWidth, 2 * minWidth); + d1 = new QDockWidget(mainWindow); + d1->setMinimumSize(minSize); + d1->setWindowTitle("I am D1"); + d1->setObjectName("D1"); + d1->setFeatures(QDockWidget::DockWidgetFeatureMask); + d1->setAllowedAreas(Qt::DockWidgetArea::AllDockWidgetAreas); + + d2 = new QDockWidget(mainWindow); + d2->setMinimumSize(minSize); + d2->setWindowTitle("I am D2"); + d2->setObjectName("D2"); + d2->setFeatures(QDockWidget::DockWidgetFeatureMask); + d2->setAllowedAreas(Qt::DockWidgetArea::RightDockWidgetArea); + + mainWindow->addDockWidget(Qt::DockWidgetArea::LeftDockWidgetArea, d1); + mainWindow->addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, d2); + d1->show(); + d2->show(); + mainWindow->show(); + QApplicationPrivate::setActiveWindow(mainWindow); + +} + +QPoint tst_QDockWidget::dragPoint(QDockWidget* dockWidget) +{ + Q_ASSERT(dockWidget); + QDockWidgetLayout *dwlayout = qobject_cast<QDockWidgetLayout *>(dockWidget->layout()); + Q_ASSERT(dwlayout); + return dockWidget->mapToGlobal(dwlayout->titleArea().center()); +} + +void tst_QDockWidget::moveDockWidget(QDockWidget* dw, QPoint to, QPoint from, MoveDockWidgetRule rule) const +{ + Q_ASSERT(dw); + + // If no from point is given, use the drag point + if (from.isNull()) + from = dragPoint(dw); + + // move and log + const QPoint source = dw->mapFromGlobal(from); + const QPoint target = dw->mapFromGlobal(to); + qCDebug(lcTestDockWidget) << "Move" << dw->objectName() << "from" << source; + qCDebug(lcTestDockWidget) << "Move" << dw->objectName() << "from" << from; + QTest::mousePress(dw, Qt::LeftButton, Qt::KeyboardModifiers(), source); + QTest::mouseMove(dw, target); + qCDebug(lcTestDockWidget) << "Move" << dw->objectName() << "to" << target; + qCDebug(lcTestDockWidget) << "Move" << dw->objectName() << "to" << to; + if (rule == MoveDockWidgetRule::Drop) { + QTest::mouseRelease(dw, Qt::LeftButton, Qt::KeyboardModifiers(), target); + QTest::qWait(waitingTime); + + // Verify WindowActive only for floating dock widgets + if (dw->isFloating()) + QTRY_VERIFY(QTest::qWaitForWindowActive(dw)); + return; + } + qCDebug(lcTestDockWidget) << "Aborting move and dropping at origin"; + + // Give animations some time + QTest::qWait(waitingTime); + QTest::mouseMove(dw, from); + QTest::mouseRelease(dw, Qt::LeftButton, Qt::KeyboardModifiers(), from); + QTest::qWait(waitingTime); +} + +void tst_QDockWidget::unplugAndResize(QMainWindow* mainWindow, QDockWidget* dw, QPoint home, QSize size) const +{ + Q_ASSERT(mainWindow); + Q_ASSERT(dw); + + // Return if floating + if (dw->isFloating()) + return; + + QMainWindowLayout* layout = qobject_cast<QMainWindowLayout*>(mainWindow->layout()); + Qt::DockWidgetArea area = layout->dockWidgetArea(dw); + + // calculate minimum lateral move to unplug a dock widget + const int unplugMargin = 80; + int my = 0; + int mx = 0; + + switch (area) { + case Qt::LeftDockWidgetArea: + mx = unplugMargin; + break; + case Qt::TopDockWidgetArea: + my = unplugMargin; + break; + case Qt::RightDockWidgetArea: + mx = -unplugMargin; + break; + case Qt::BottomDockWidgetArea: + my = -unplugMargin; + break; + default: + return; + } + + // Remember size for comparison with unplugged object +#ifdef Q_OS_LINUX + const int pluggedWidth = dw->width(); + const int pluggedHeight = dw->height(); +#endif + + // unplug and resize a dock Widget + qCDebug(lcTestDockWidget) << "*** unplug and resize" << dw->objectName(); + QPoint pos1 = dw->mapToGlobal(dw->rect().center()); + pos1.rx() += mx; + pos1.ry() += my; + moveDockWidget(dw, pos1, dw->mapToGlobal(dw->rect().center()), MoveDockWidgetRule::Drop); + QTRY_VERIFY(dw->isFloating()); + + // Unplugged object's size may differ max. by 2x frame size +#ifdef Q_OS_LINUX + const int xMargin = 2 * dw->frameSize().width(); + const int yMargin = 2 * dw->frameSize().height(); + QVERIFY(dw->height() - pluggedHeight <= xMargin); + QVERIFY(dw->width() - pluggedWidth <= yMargin); +#endif + + qCDebug(lcTestDockWidget) << "Resizing" << dw->objectName() << "to" << size; + dw->setFixedSize(size); + QTest::qWait(waitingTime); + qCDebug(lcTestDockWidget) << "Move" << dw->objectName() << "to its home" << dw->mapFromGlobal(home); + dw->move(home); +} + +bool tst_QDockWidget::checkFloatingTabs(QMainWindow* mainWindow, QPointer<QDockWidgetGroupWindow> &ftabs, const QList<QDockWidget*> &dwList) const +{ + Q_ASSERT(mainWindow); + + // Check if mainWindow has a floatingTab child + ftabs = mainWindow->findChild<QDockWidgetGroupWindow*>(); + if (ftabs.isNull()) { + qCDebug(lcTestDockWidget) << "MainWindow has no DockWidgetGroupWindow" << mainWindow; + return false; + } + + QTabBar* tab = ftabs->findChild<QTabBar*>(); + if (!tab) { + qCDebug(lcTestDockWidget) << "DockWidgetGroupWindow has no tab bar" << ftabs; + return false; + } + + // both dock widgets must be direct children of the main window + const QList<QDockWidget*> children = ftabs->findChildren<QDockWidget*>(QString(), Qt::FindDirectChildrenOnly); + if (dwList.size() > 0) + { + if (dwList.size() != children.size()) { + qCDebug(lcTestDockWidget) << "Expected DockWidgetGroupWindow children:" << dwList.size() + << "Children found:" << children.size(); + + qCDebug(lcTestDockWidget) << "Expected:" << dwList; + qCDebug(lcTestDockWidget) << "Found in" << ftabs << ":" << children.size(); + return false; + } + + for (const QDockWidget* child : dwList) { + if (!children.contains(child)) { + qCDebug(lcTestDockWidget) << "Expected child" << child << "not found in" << children; + return false; + } + } + } + + // Always select first tab position + qCDebug(lcTestDockWidget) << "click on first tab"; + QTest::mouseClick(tab, Qt::LeftButton, Qt::KeyboardModifiers(), tab->tabRect(0).center()); + return true; +} + +#ifdef QT_BUILD_INTERNAL +// Statics for xcb error, raise() suppert / msg handler +static tst_QDockWidget *qThis = nullptr; +static void (*oldMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &); +#define QXCBVERIFY(cond) do { if (xcbError) QSKIP("Test skipped due to XCB error"); QVERIFY(cond); } while (0) + +// detect xcb error and missing raise() support +// qt.qpa.xcb: internal error: void QXcbWindow::setNetWmStateOnUnmappedWindow() called on mapped window +void tst_QDockWidget::xcbMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) +{ + Q_ASSERT(oldMessageHandler); + + if (type == QtWarningMsg) { + Q_ASSERT(qThis); + if (QString(context.category) == "qt.qpa.xcb" && msg.contains("internal error")) + qThis->xcbError = true; + if (msg.contains("does not support raise")) + qThis->platformSupportingRaise = false; + } + + return oldMessageHandler(type, context, msg); +} +#endif + +void tst_QDockWidget::createFloatingTabs(QMainWindow* &mainWindow, QPointer<QWidget> ¢, + QPointer<QDockWidget> &d1, QPointer<QDockWidget> &d2, + QList<int> &path1, QList<int> &path2) const +{ + createTestWidgets(mainWindow, cent, d1, d2); + +#ifdef QT_BUILD_INTERNAL + qThis = const_cast<tst_QDockWidget *>(this); + oldMessageHandler = qInstallMessageHandler(xcbMessageHandler); + auto resetMessageHandler = qScopeGuard([] { qInstallMessageHandler(oldMessageHandler); }); +#endif + + // Test will fail if platform doesn't support raise. + mainWindow->windowHandle()->handle()->raise(); + if (!platformSupportingRaise) + QSKIP("Platform not supporting raise(). Floating tab based tests will fail."); + + // remember paths to d1 and d2 + QMainWindowLayout* layout = qobject_cast<QMainWindowLayout *>(mainWindow->layout()); + path1 = layout->layoutState.indexOf(d1); + path2 = layout->layoutState.indexOf(d2); + + // unplug and resize both dock widgets + unplugAndResize(mainWindow, d1, home1(mainWindow), size1(mainWindow)); + unplugAndResize(mainWindow, d2, home2(mainWindow), size2(mainWindow)); + + // docks must be parented to the main window, no group window must exist + QCOMPARE(d1->parentWidget(), mainWindow); + QCOMPARE(d2->parentWidget(), mainWindow); + QVERIFY(mainWindow->findChildren<QDockWidgetGroupWindow *>().isEmpty()); + + // Test plugging + qCDebug(lcTestDockWidget) << "*** move d1 dock over d2 dock ***"; + qCDebug(lcTestDockWidget) << "**********(test plugging)*************"; + qCDebug(lcTestDockWidget) << "Move d1 over d2"; + moveDockWidget(d1, d2->mapToGlobal(d2->rect().center()), QPoint(), MoveDockWidgetRule::Drop); + + // Now MainWindow has to have a floatingTab child + QPointer<QDockWidgetGroupWindow> ftabs; + QTRY_VERIFY(checkFloatingTabs(mainWindow, ftabs, QList<QDockWidget *>() << d1 << d2)); +} +#endif // QT_BUILD_INTERNAL + +// test floating tabs and item_tree consistency +void tst_QDockWidget::floatingTabs() +{ + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Test skipped on Wayland."); +#ifdef Q_OS_WIN + QSKIP("Test skipped on Windows platforms"); +#endif // Q_OS_WIN +#ifdef QT_BUILD_INTERNAL + // Create a mainwindow with a central widget and two dock widgets + QPointer<QDockWidget> d1; + QPointer<QDockWidget> d2; + QPointer<QWidget> cent; + QMainWindow* mainWindow; + QList<int> path1; + QList<int> path2; + createFloatingTabs(mainWindow, cent, d1, d2, path1, path2); + std::unique_ptr<QMainWindow> up_mainWindow(mainWindow); + + QCOMPARE(mainWindow->tabifiedDockWidgets(d1), {d2}); + QCOMPARE(mainWindow->tabifiedDockWidgets(d2), {d1}); + + /* + * unplug both dockwidgets, resize them and plug them into a joint floating tab + * expected behavior: QDOckWidgetGroupWindow with both widgets is created + */ + + // disabled due to flakiness on macOS and Windows + if (d1->isFloating()) + qWarning("OS flakiness: D1 is docked and reports being floating"); + if (d2->isFloating()) + qWarning("OS flakiness: D2 is docked and reports being floating"); + + // Now MainWindow has to have a floatingTab child + QPointer<QDockWidgetGroupWindow> ftabs; + QTRY_VERIFY(checkFloatingTabs(mainWindow, ftabs, QList<QDockWidget *>() << d1 << d2)); + + // Hide both dock widgets. Verify that the group window is also hidden. + qCDebug(lcTestDockWidget) << "*** Hide and show tabbed dock widgets ***"; + d1->hide(); + d2->hide(); + QTRY_VERIFY(ftabs->isHidden()); + + // Show both dockwidgets again. Verify that the group window is visible. + d1->show(); + d2->show(); + QTRY_VERIFY(ftabs->isVisible()); + + /* + * replug both dock widgets into their initial position + * expected behavior: + - both docks are plugged + - both docks are no longer floating + - title changes have been propagated + */ + + + // limitation: QTest cannot handle drag to unplug. + // reason: Object under mouse mutates from QTabBar::tab to QDockWidget. QTest cannot handle that. + // => click float button to unplug + qCDebug(lcTestDockWidget) << "*** test unplugging from floating dock ***"; + + // QDockWidget must have a QAbstractButton with object name "qt_dockwidget_floatbutton" + QAbstractButton* floatButton = d1->findChild<QAbstractButton*>("qt_dockwidget_floatbutton", Qt::FindDirectChildrenOnly); + QTRY_VERIFY(floatButton != nullptr); + QPoint pos1 = floatButton->rect().center(); + qCDebug(lcTestDockWidget) << "unplug d1" << pos1; + QTest::mouseClick(floatButton, Qt::LeftButton, Qt::KeyboardModifiers(), pos1); + QTest::qWait(waitingTime); + + // d1 must be floating again, while d2 is still in its GroupWindow + QTRY_VERIFY(d1->isFloating()); + QTRY_VERIFY(!d2->isFloating()); + + // Plug back into dock areas + qCDebug(lcTestDockWidget) << "*** test plugging back to dock areas ***"; + qCDebug(lcTestDockWidget) << "Move d1 to left dock"; + moveDockWidget(d1, dockPoint(mainWindow, Qt::LeftDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop); + qCDebug(lcTestDockWidget) << "Move d2 to right dock"; + moveDockWidget(d2, dockPoint(mainWindow, Qt::RightDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop); + + qCDebug(lcTestDockWidget) << "Waiting" << waitBeforeClose << "ms before plugging back."; + QTest::qWait(waitBeforeClose); + + // Both dock widgets must no longer be floating + QTRY_VERIFY(!d1->isFloating()); + QTRY_VERIFY(!d2->isFloating()); + + // check if QDockWidgetGroupWindow has been removed from mainWindowLayout and properly deleted + QTRY_VERIFY(!mainWindow->findChild<QDockWidgetGroupWindow*>()); + QTRY_VERIFY(ftabs.isNull()); + + // Check if paths are consistent + QMainWindowLayout* layout = qobject_cast<QMainWindowLayout *>(mainWindow->layout()); + qCDebug(lcTestDockWidget) << "Checking path consistency" << layout->layoutState.indexOf(d1) << layout->layoutState.indexOf(d2); + + // Paths must be identical + QTRY_COMPARE(layout->layoutState.indexOf(d1), path1); + QTRY_COMPARE(layout->layoutState.indexOf(d2), path2); + + QCOMPARE(mainWindow->tabifiedDockWidgets(d1), {}); + QCOMPARE(mainWindow->tabifiedDockWidgets(d2), {}); +#else + QSKIP("test requires -developer-build option"); +#endif // QT_BUILD_INTERNAL +} + +void tst_QDockWidget::deleteFloatingTabWithSingleDockWidget_data() +{ +#ifdef QT_BUILD_INTERNAL + QTest::addColumn<int>("reason"); + QTest::addRow("Delete child") << static_cast<int>(ChildRemovalReason::Destroyed); + QTest::addRow("Close child") << static_cast<int>(ChildRemovalReason::Closed); + QTest::addRow("Reparent child") << static_cast<int>(ChildRemovalReason::Reparented); +#endif +} + +void tst_QDockWidget::deleteFloatingTabWithSingleDockWidget() +{ + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Test skipped on Wayland."); +#ifdef Q_OS_WIN + QSKIP("Test skipped on Windows platforms"); +#endif // Q_OS_WIN +#ifdef QT_BUILD_INTERNAL + + QFETCH(int, reason); + const ChildRemovalReason removalReason = static_cast<ChildRemovalReason>(reason); + + QPointer<QDockWidget> d1; + QPointer<QDockWidget> d2; + QPointer<QWidget> cent; + QMainWindow* mainWindow; + QList<int> path1; + QList<int> path2; + createFloatingTabs(mainWindow, cent, d1, d2, path1, path2); + std::unique_ptr<QMainWindow> up_mainWindow(mainWindow); + + switch (removalReason) { + case ChildRemovalReason::Destroyed: + delete d1; + break; + case ChildRemovalReason::Closed: + d1->close(); + break; + case ChildRemovalReason::Reparented: + // This will create an invalid state, because setParent() doesn't fix the item_list. + // Testing this case anyway, because setParent() includig item_list fixup is executed, + // when the 2nd last dock widget is dragged out of a floating tab. + // => despite of the broken state, the group window has to be gone. + d1->setParent(mainWindow); + break; + } + + QTRY_VERIFY(!qobject_cast<QDockWidgetGroupWindow *>(d2->parentWidget())); + QTRY_VERIFY(mainWindow->findChildren<QDockWidgetGroupWindow *>().isEmpty()); +#endif // QT_BUILD_INTERNAL +} + +void tst_QDockWidget::hoverWithoutDrop() +{ + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Test skipped on Wayland."); +#ifdef QT_BUILD_INTERNAL + + QPointer<QDockWidget> d1; + QPointer<QDockWidget> d2; + QPointer<QWidget> cent; + QMainWindow* mainWindow; + createTestWidgets(mainWindow, cent, d1, d2); + std::unique_ptr<QMainWindow> up_mainWindow(mainWindow); + + // unplug and resize both dock widgets + unplugAndResize(mainWindow, d1, home1(mainWindow), size1(mainWindow)); + unplugAndResize(mainWindow, d2, home2(mainWindow), size2(mainWindow)); + + // Test plugging + qCDebug(lcTestDockWidget) << "*** move d1 dock over d2 dock ***"; + qCDebug(lcTestDockWidget) << "*******(test hovering)***********"; + qCDebug(lcTestDockWidget) << "Move d1 over d2, wait and return to origin"; + const QPoint source = d1->mapToGlobal(d1->rect().center()); + const QPoint target = d2->mapToGlobal(d2->rect().center()); + moveDockWidget(d1, target, source, MoveDockWidgetRule::Abort); + auto *groupWindow = mainWindow->findChild<QDockWidgetGroupWindow *>(); + QCOMPARE(groupWindow, nullptr); +#else + QSKIP("test requires -developer-build option"); +#endif // QT_BUILD_INTERNAL +} + +// test hide & show +void tst_QDockWidget::hideAndShow() +{ + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Test skipped on Wayland."); +#ifdef QT_BUILD_INTERNAL + // Skip test if xcb error is launched + qThis = this; + oldMessageHandler = qInstallMessageHandler(xcbMessageHandler); + auto resetMessageHandler = qScopeGuard([] { qInstallMessageHandler(oldMessageHandler); }); + + // Create a mainwindow with a central widget and two dock widgets + QPointer<QDockWidget> d1; + QPointer<QDockWidget> d2; + QPointer<QWidget> cent; + QMainWindow* mainWindow; + createTestWidgets(mainWindow, cent, d1, d2); + std::unique_ptr<QMainWindow> up_mainWindow(mainWindow); + + // Check hiding of docked widgets + qCDebug(lcTestDockWidget) << "Hiding mainWindow with plugged dock widgets" << mainWindow; + mainWindow->hide(); + QXCBVERIFY(!mainWindow->isVisible()); + QXCBVERIFY(!d1->isVisible()); + QXCBVERIFY(!d2->isVisible()); + + // Check showing everything again + qCDebug(lcTestDockWidget) << "Showing mainWindow with plugged dock widgets" << mainWindow; + mainWindow->show(); + QXCBVERIFY(QTest::qWaitForWindowActive(mainWindow)); + QXCBVERIFY(QTest::qWaitForWindowExposed(mainWindow)); + QXCBVERIFY(mainWindow->isVisible()); + QXCBVERIFY(QTest::qWaitForWindowActive(d1)); + QXCBVERIFY(d1->isVisible()); + QXCBVERIFY(QTest::qWaitForWindowActive(d2)); + QXCBVERIFY(d2->isVisible()); + + // in case of XCB errors, unplugAndResize will block and cause the test to time out. + // => force skip + QTest::qWait(waitingTime); + if (xcbError) + QSKIP("Test skipped due to XCB error"); + + // unplug and resize both dock widgets + unplugAndResize(mainWindow, d1, home1(mainWindow), size1(mainWindow)); + unplugAndResize(mainWindow, d2, home2(mainWindow), size2(mainWindow)); + + // Check hiding of undocked widgets + qCDebug(lcTestDockWidget) << "Hiding mainWindow with unplugged dock widgets" << mainWindow; + mainWindow->hide(); + QTRY_VERIFY(!mainWindow->isVisible()); + QTRY_VERIFY(d1->isVisible()); + QTRY_VERIFY(d2->isVisible()); + d1->hide(); + d2->hide(); + QTRY_VERIFY(!d1->isVisible()); + QTRY_VERIFY(!d2->isVisible()); + + + // Check floating, hidden dock widgets remain hidden, when their state is restored + qCDebug(lcTestDockWidget) << "Restoring state of unplugged, hidden dock widgets" << mainWindow; + const QByteArray state = mainWindow->saveState(); + mainWindow->restoreState(state); + mainWindow->show(); + QVERIFY(QTest::qWaitForWindowExposed(mainWindow)); + QTRY_VERIFY(!d1->isVisible()); + QTRY_VERIFY(!d2->isVisible()); + + qCDebug(lcTestDockWidget) << "Waiting" << waitBeforeClose << "ms before closing."; + QTest::qWait(waitBeforeClose); +#else + QSKIP("test requires -developer-build option"); +#endif // QT_BUILD_INTERNAL +} + +// test closing and deleting consistency +void tst_QDockWidget::closeAndDelete() +{ + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Test skipped on Wayland."); +#ifdef QT_BUILD_INTERNAL + // Create a mainwindow with a central widget and two dock widgets + QPointer<QDockWidget> d1; + QPointer<QDockWidget> d2; + QPointer<QWidget> cent; + QMainWindow* mainWindow; + createTestWidgets(mainWindow, cent, d1, d2); + std::unique_ptr<QMainWindow> up_mainWindow(mainWindow); + + // unplug and resize both dock widgets + unplugAndResize(mainWindow, d1, home1(mainWindow), size1(mainWindow)); + unplugAndResize(mainWindow, d2, home2(mainWindow), size2(mainWindow)); + + // Create a floating tab and unplug it again + qCDebug(lcTestDockWidget) << "Move d1 over d2"; + moveDockWidget(d1, d2->mapToGlobal(d2->rect().center()), QPoint(), MoveDockWidgetRule::Drop); + + // Both dock widgets must no longer be floating + // disabled due to flakiness on macOS and Windows + //QTRY_VERIFY(!d1->isFloating()); + //QTRY_VERIFY(!d2->isFloating()); + if (d1->isFloating()) + qWarning("OS flakiness: D1 is docked and reports being floating"); + if (d2->isFloating()) + qWarning("OS flakiness: D2 is docked and reports being floating"); + + // Close everything with a single shot. Expected behavior: Event loop stops + QSignalSpy closeSpy(qApp, &QApplication::lastWindowClosed); + QObject localContext; + + QTimer::singleShot(0, &localContext, [&](){ + mainWindow->close(); + QTRY_VERIFY(!mainWindow->isVisible()); + QTRY_VERIFY(d1->isVisible()); + QTRY_VERIFY(d2->isVisible()); + d1->close(); + d2->close(); + QTRY_VERIFY(!d1->isVisible()); + QTRY_VERIFY(!d2->isVisible()); + QTRY_COMPARE(closeSpy.count(), 1); + QApplication::quit(); + }); + + QApplication::exec(); + + // Check heap cleanup + qCDebug(lcTestDockWidget) << "Deleting mainWindow"; + up_mainWindow.reset(); + QTRY_VERIFY(d1.isNull()); + QTRY_VERIFY(d2.isNull()); + QTRY_VERIFY(cent.isNull()); +#else + QSKIP("test requires -developer-build option"); +#endif // QT_BUILD_INTERNAL +} + +void tst_QDockWidget::closeUnclosable() +{ + QDockWidget *dockWidget = new QDockWidget("dock"); + dockWidget->setWidget(new QScrollArea); + dockWidget->setFeatures(QDockWidget::DockWidgetFloatable); + + QMainWindow mw; + mw.addDockWidget(Qt::TopDockWidgetArea, dockWidget); + mw.show(); + + QVERIFY(QTest::qWaitForWindowExposed(&mw)); + dockWidget->setFloating(true); + + QCOMPARE(dockWidget->close(), false); + mw.close(); + QCOMPARE(dockWidget->close(), true); +} + +// Test dock area permissions +void tst_QDockWidget::dockPermissions() +{ + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Test skipped on Wayland."); +#ifdef Q_OS_WIN + QSKIP("Test skipped on Windows platforms"); +#endif // Q_OS_WIN +#ifdef QT_BUILD_INTERNAL + // Create a mainwindow with a central widget and two dock widgets + QPointer<QDockWidget> d1; + QPointer<QDockWidget> d2; + QPointer<QWidget> cent; + QMainWindow* mainWindow; + createTestWidgets(mainWindow, cent, d1, d2); + std::unique_ptr<QMainWindow> up_mainWindow(mainWindow); + + /* + * Unplug both dock widgets from their dock areas and hover them over each other + * expected behavior: + * - d2 hovering over d1 does nothing as d2 can only use right dock + * - hovering d2 over top, left and bottom dock area will do nothing due to lacking permissions + * - d1 hovering over d2 will create floating tabs as d1 has permission for DockWidgetArea::FloatingDockWidgetArea + * - resizing and tab creation will add two gap items in the right dock (d2) + */ + + // unplug and resize both dock widgets + unplugAndResize(mainWindow, d1, home1(mainWindow), size1(mainWindow)); + unplugAndResize(mainWindow, d2, home2(mainWindow), size2(mainWindow)); + + // both dock widgets must be direct children of the main window + { + const QList<QDockWidget*> children = mainWindow->findChildren<QDockWidget*>(QString(), Qt::FindDirectChildrenOnly); + QTRY_VERIFY(children.size() == 2); + for (const QDockWidget* child : children) + QTRY_VERIFY(child == d1 || child == d2); + } + + // The main window must not contain floating tabs + QTRY_VERIFY(mainWindow->findChild<QDockWidgetGroupWindow*>() == nullptr); + + // Test unpermitted dock areas with d2 + qCDebug(lcTestDockWidget) << "*** move d2 to forbidden docks ***"; + + // Move d2 to non allowed dock areas and verify it remains floating + qCDebug(lcTestDockWidget) << "Move d2 to top dock"; + moveDockWidget(d2, dockPoint(mainWindow, Qt::TopDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop); + QTRY_VERIFY(d2->isFloating()); + + qCDebug(lcTestDockWidget) << "Move d2 to left dock"; + //moveDockWidget(d2, d2->mapFrom(MainWindow, dockPoint(MainWindow, Qt::LeftDockWidgetArea))); + moveDockWidget(d2, dockPoint(mainWindow, Qt::LeftDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop); + QTRY_VERIFY(d2->isFloating()); + + qCDebug(lcTestDockWidget) << "Move d2 to bottom dock"; + moveDockWidget(d2, dockPoint(mainWindow, Qt::BottomDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop); + QTRY_VERIFY(d2->isFloating()); + + qCDebug(lcTestDockWidget) << "Waiting" << waitBeforeClose << "ms before closing."; + QTest::qWait(waitBeforeClose); +#else + QSKIP("test requires -developer-build option"); +#endif // QT_BUILD_INTERNAL +} + +/*! + \internal + + This test checks consistency of QMainWindow::saveState() / QMainWindow::restoreState(). + These methods (de)serialize dock widget properties via a QDataStream into a QByteArray. + + If the logic of (de)serializing Qt datatypes and classes changes, old settings can fail + to restore properly without triggering warnings or assertions. + + The test consists of two parts: + \list 1 + \li Read properties from a hard coded byte array and check if it is deserialized correctly. + \li Serialize properties into a \a QByteArray and check if it is serialized correctly. + \endlist +*/ +void tst_QDockWidget::saveAndRestore() +{ + if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) + QSKIP("Test skipped on Wayland."); +#ifdef Q_OS_WIN + QSKIP("Test skipped on Windows platforms"); +#endif // Q_OS_WIN +#ifndef QT_BUILD_INTERNAL + QSKIP("test requires -developer-build option"); +#else + + // Hard coded byte array for test initialization + const QByteArray testArray = QByteArrayLiteral( + "\x00\x00\x00\xFF\x00\x00\x00\x00\xFD\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x13\x00\x00\x05\xE8\xFC\x02\x00\x00\x00\x01\xFB\x00\x00\x00\x04\x00" + "D\x00" + "1\x03\x00\x00\x01\f\x00\x00\x00\x97\x00\x00\x02\x19\x00\x00\x01z\x00\x00\x00\x01\x00\x00\x00\x13\x00\x00\x05\xE8\xFC\x02\x00\x00\x00\x01\xFB\x00\x00\x00\x04\x00" + "D\x00" + "2\x03\x00\x00\x06L\x00\x00\x00\xFF\x00\x00\x01\f\x00\x00\x00\xE2\x00\x00\n\x80\x00\x00\x05\xE8\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\b\x00\x00\x00\b\xFC\x00\x00\x00\x00" + ); + + QByteArray referenceArray; // Copy of testArray, corrected for current screen limits + QPoint topLeft1; // Top left point of dock widget d1 + QPoint topLeft2; // Top left point of dock widget d2 + QSize widgetSize1; // Size of dock widget d1 + QSize widgetSize2; // Size of dock widget d2 + bool isFloating1; // Floating status of dock widget d1 + bool isFloating2; // Floating status of dock widget d2 + + // Create a mainwindow with a central widget and two dock widgets. + // Import properties from hard coded byte array. + // Use a scope to delete objects from screen after test. + { + QPointer<QDockWidget> d1; + QPointer<QDockWidget> d2; + QPointer<QWidget> cent; + QMainWindow* mainWindow; + createTestWidgets(mainWindow, cent, d1, d2); + + // Failure to restore properties might lead to inconsistencies and crash. + // To leave a clean environment when the test inexpectedly goes out of scope, + // => store main window pointer in a std::unique_ptr + std::unique_ptr<QMainWindow> up_mainWindow(mainWindow); + + // Restore, wait for events to be processed + mainWindow->restoreState(testArray); + QVERIFY(QTest::qWaitForWindowExposed(d1)); + QVERIFY(QTest::qWaitForWindowExposed(d2)); + + // Serialized dock widget positions and sizes might be overridden due + // screen size limitations => do not check them here. + // If the test fails between here and scope end, serialization format/sequence have changed + QTRY_VERIFY(d1->isFloating()); + QTRY_VERIFY(d2->isFloating()); + + // Hide main window and save their floating status. + // Reason: + // - KDE window managers do not take control over dock widgets. + // => They always close with the main window. + // - Some non KDE window managers do take control over dock widgets. + // => They prevent them from closing with the main window (QTBUG-103474). + // If properties are restored correctly, closing behavior must be consistent + // throughout this test. + mainWindow->hide(); + // FIXME: No method exists in 6.5 to wait for a window to be hidden. + // => wait and hope the best, replace with qWaitForWindowHidden once implemented. + QTest::qWait(200); + isFloating1 = d1->isFloating(); + isFloating2 = d2->isFloating(); + } + + // Create a mainwindow with a central widget and two dock widgets. + // Assign different properties to each dock widgets. + // Write properties to a byte array. + // Remember position and size properties for comparison. + // Use a scope to delete objects from screen after test. + { + QPointer<QDockWidget> d1; + QPointer<QDockWidget> d2; + QPointer<QWidget> cent; + QMainWindow* mainWindow; + createTestWidgets(mainWindow, cent, d1, d2); + std::unique_ptr<QMainWindow> up_mainWindow(mainWindow); + + // unplug, position and resize both dock widgets relative to screen size + unplugAndResize(mainWindow, d1, home1(mainWindow), size1(mainWindow)); + unplugAndResize(mainWindow, d2, home2(mainWindow), size2(mainWindow)); + + topLeft1 = d1->pos(); + topLeft2 = d2->pos(); + widgetSize1 = d1->size(); + widgetSize2 = d2->size(); + + // save properties, potentially corrected for screen limits + referenceArray = mainWindow->saveState(); + + // Check closing behavior consistency + mainWindow->hide(); + QTRY_VERIFY(d1->isFloating()); + QTRY_VERIFY(d2->isFloating()); + QCOMPARE(d1->isFloating(), isFloating1); + QCOMPARE(d2->isFloating(), isFloating2); + } + + // Create a new main window, central window and two dock widgets. + QPointer<QDockWidget> d1; + QPointer<QDockWidget> d2; + QPointer<QWidget> cent; + QMainWindow* mainWindow; + createTestWidgets(mainWindow, cent, d1, d2); + + // Failure to restore properties might lead to inconsistencies and crash. + // To leave a clean environment when the test inexpectedly goes out of scope, + // - store main window pointer in a std::unique_ptr + std::unique_ptr<QMainWindow> up_mainWindow(mainWindow); + + // Restore properties and wait for events to be processed + mainWindow->restoreState(referenceArray); + QVERIFY(QTest::qWaitForWindowExposed(d1)); + QVERIFY(QTest::qWaitForWindowExposed(d2)); + + // Compare positions, sizes and floating status + // If the test fails in the following 12 lines, + // the de-serialization format/sequence have changed + QCOMPARE(topLeft1, d1->pos()); + QCOMPARE(topLeft2, d2->pos()); + QCOMPARE(widgetSize1, d1->size()); + QCOMPARE(widgetSize2, d2->size()); + QVERIFY(d1->isFloating()); + QVERIFY(d2->isFloating()); + + // Serialize again to compare all remaining properties + const QByteArray comparisonArray = mainWindow->saveState(); + QCOMPARE(comparisonArray, referenceArray); + + // Check closing behavior consistency + mainWindow->hide(); + QTRY_VERIFY(d1->isFloating()); + QTRY_VERIFY(d2->isFloating()); + QCOMPARE(d1->isFloating(), isFloating1); + QCOMPARE(d2->isFloating(), isFloating2); + +#endif // QT_BUILD_INTERNAL } QTEST_MAIN(tst_QDockWidget) diff --git a/tests/auto/widgets/widgets/qdoublespinbox/CMakeLists.txt b/tests/auto/widgets/widgets/qdoublespinbox/CMakeLists.txt index 6be779bb81..b023174dc9 100644 --- a/tests/auto/widgets/widgets/qdoublespinbox/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qdoublespinbox/CMakeLists.txt @@ -1,13 +1,21 @@ -# Generated from qdoublespinbox.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qdoublespinbox Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qdoublespinbox LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qdoublespinbox SOURCES tst_qdoublespinbox.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp b/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp index 6b0c353e3b..28752cd40d 100644 --- a/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp +++ b/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -47,6 +22,8 @@ #include <QStyle> #include <QProxyStyle> +#include <QtWidgets/private/qapplication_p.h> + class DoubleSpinBox : public QDoubleSpinBox { Q_OBJECT @@ -134,8 +111,8 @@ public: virtual ~tst_QDoubleSpinBox(); public slots: void initTestCase(); - void cleanupTestCase(); void init(); + void cleanup(); private slots: void germanTest(); @@ -213,7 +190,6 @@ public slots: private: QStringList actualTexts; QList<double> actualValues; - QWidget *testFocusWidget; }; typedef QList<double> DoubleList; @@ -257,25 +233,18 @@ tst_QDoubleSpinBox::~tst_QDoubleSpinBox() void tst_QDoubleSpinBox::initTestCase() { - testFocusWidget = new QWidget(0); - testFocusWidget->resize(200, 100); - testFocusWidget->show(); - if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) QSKIP("Wayland: This fails. Figure out why."); - - QVERIFY(QTest::qWaitForWindowActive(testFocusWidget)); } -void tst_QDoubleSpinBox::cleanupTestCase() +void tst_QDoubleSpinBox::init() { - delete testFocusWidget; - testFocusWidget = 0; + QLocale::setDefault(QLocale(QLocale::C)); } -void tst_QDoubleSpinBox::init() +void tst_QDoubleSpinBox::cleanup() { - QLocale::setDefault(QLocale(QLocale::C)); + QTRY_VERIFY(QApplication::topLevelWidgets().isEmpty()); } void tst_QDoubleSpinBox::setValue_data() @@ -878,17 +847,25 @@ void tst_QDoubleSpinBox::setReadOnly() void tst_QDoubleSpinBox::editingFinished() { - QVBoxLayout *layout = new QVBoxLayout(testFocusWidget); - QDoubleSpinBox *box = new QDoubleSpinBox(testFocusWidget); + QWidget testFocusWidget(nullptr); + QVBoxLayout *layout = new QVBoxLayout(&testFocusWidget); + QDoubleSpinBox *box = new QDoubleSpinBox(&testFocusWidget); layout->addWidget(box); - QDoubleSpinBox *box2 = new QDoubleSpinBox(testFocusWidget); + QDoubleSpinBox *box2 = new QDoubleSpinBox(&testFocusWidget); layout->addWidget(box2); - testFocusWidget->show(); - testFocusWidget->activateWindow(); - QVERIFY(QTest::qWaitForWindowActive(testFocusWidget)); + testFocusWidget.show(); + testFocusWidget.activateWindow(); + QVERIFY(QTest::qWaitForWindowActive(&testFocusWidget)); + + box->show(); + QVERIFY(QTest::qWaitForWindowExposed(box)); box->setFocus(); - QTRY_VERIFY(box->hasFocus()); + + // Box may fail to acquire focus due to a system popup + // it is fair in that case to skip the test + if (!QTest::qWaitForWindowActive(box)) + QSKIP("Focus acquisition failed."); QSignalSpy editingFinishedSpy1(box, SIGNAL(editingFinished())); QSignalSpy editingFinishedSpy2(box2, SIGNAL(editingFinished())); @@ -897,42 +874,37 @@ void tst_QDoubleSpinBox::editingFinished() QTest::keyClick(box, Qt::Key_Up); - QCOMPARE(editingFinishedSpy1.count(), 0); - QCOMPARE(editingFinishedSpy2.count(), 0); + QCOMPARE(editingFinishedSpy1.size(), 0); + QCOMPARE(editingFinishedSpy2.size(), 0); QTest::keyClick(box2, Qt::Key_Up); QTest::keyClick(box2, Qt::Key_Up); box2->setFocus(); - QCOMPARE(editingFinishedSpy1.count(), 1); + QCOMPARE(editingFinishedSpy1.size(), 1); box->setFocus(); - QCOMPARE(editingFinishedSpy1.count(), 1); - QCOMPARE(editingFinishedSpy2.count(), 1); + QCOMPARE(editingFinishedSpy1.size(), 1); + QCOMPARE(editingFinishedSpy2.size(), 1); QTest::keyClick(box, Qt::Key_Up); - QCOMPARE(editingFinishedSpy1.count(), 1); - QCOMPARE(editingFinishedSpy2.count(), 1); + QCOMPARE(editingFinishedSpy1.size(), 1); + QCOMPARE(editingFinishedSpy2.size(), 1); QTest::keyClick(box, Qt::Key_Enter); - QCOMPARE(editingFinishedSpy1.count(), 2); - QCOMPARE(editingFinishedSpy2.count(), 1); + QCOMPARE(editingFinishedSpy1.size(), 2); + QCOMPARE(editingFinishedSpy2.size(), 1); QTest::keyClick(box, Qt::Key_Return); - QCOMPARE(editingFinishedSpy1.count(), 3); - QCOMPARE(editingFinishedSpy2.count(), 1); + QCOMPARE(editingFinishedSpy1.size(), 3); + QCOMPARE(editingFinishedSpy2.size(), 1); box2->setFocus(); - QCOMPARE(editingFinishedSpy1.count(), 4); - QCOMPARE(editingFinishedSpy2.count(), 1); + QCOMPARE(editingFinishedSpy1.size(), 4); + QCOMPARE(editingFinishedSpy2.size(), 1); QTest::keyClick(box2, Qt::Key_Enter); - QCOMPARE(editingFinishedSpy1.count(), 4); - QCOMPARE(editingFinishedSpy2.count(), 2); + QCOMPARE(editingFinishedSpy1.size(), 4); + QCOMPARE(editingFinishedSpy2.size(), 2); QTest::keyClick(box2, Qt::Key_Return); - QCOMPARE(editingFinishedSpy1.count(), 4); - QCOMPARE(editingFinishedSpy2.count(), 3); - testFocusWidget->hide(); - QCOMPARE(editingFinishedSpy1.count(), 4); - QCOMPARE(editingFinishedSpy2.count(), 4); - - // On some platforms this is our root window - // we need to show it again otherwise subsequent - // tests will fail - testFocusWidget->show(); + QCOMPARE(editingFinishedSpy1.size(), 4); + QCOMPARE(editingFinishedSpy2.size(), 3); + testFocusWidget.hide(); + QCOMPARE(editingFinishedSpy1.size(), 4); + QCOMPARE(editingFinishedSpy2.size(), 4); } void tst_QDoubleSpinBox::removeAll() @@ -1057,7 +1029,7 @@ void tst_QDoubleSpinBox::undoRedo() QVERIFY(!spin.lineEdit()->isUndoAvailable()); QVERIFY(spin.lineEdit()->isRedoAvailable()); } else { - QWARN("Undo not tested because no key sequence associated to QKeySequence::Redo"); + qWarning("Undo not tested because no key sequence associated to QKeySequence::Redo"); } @@ -1070,7 +1042,7 @@ void tst_QDoubleSpinBox::undoRedo() QVERIFY(!spin.lineEdit()->isRedoAvailable()); QVERIFY(spin.lineEdit()->isUndoAvailable()); } else { - QWARN("Redo not tested because no key sequence associated to QKeySequence::Redo"); + qWarning("Redo not tested because no key sequence associated to QKeySequence::Redo"); } @@ -1111,15 +1083,15 @@ public: void tst_QDoubleSpinBox::task224497_fltMax() { - task224497_fltMax_DoubleSpinBox *dspin = new task224497_fltMax_DoubleSpinBox; - dspin->setMinimum(3); - dspin->setMaximum(FLT_MAX); - dspin->show(); - QVERIFY(QTest::qWaitForWindowActive(dspin)); - dspin->lineEdit()->selectAll(); - QTest::keyClick(dspin->lineEdit(), Qt::Key_Delete); - QTest::keyClick(dspin->lineEdit(), Qt::Key_1); - QCOMPARE(dspin->cleanText(), QLatin1String("1")); + task224497_fltMax_DoubleSpinBox dspin; + dspin.setMinimum(3); + dspin.setMaximum(FLT_MAX); + dspin.show(); + QVERIFY(QTest::qWaitForWindowActive(&dspin)); + dspin.lineEdit()->selectAll(); + QTest::keyClick(dspin.lineEdit(), Qt::Key_Delete); + QTest::keyClick(dspin.lineEdit(), Qt::Key_1); + QCOMPARE(dspin.cleanText(), QLatin1String("1")); } void tst_QDoubleSpinBox::task221221() @@ -1182,7 +1154,7 @@ void tst_QDoubleSpinBox::taskQTBUG_5008_textFromValueAndValidate() spinbox.show(); spinbox.activateWindow(); spinbox.setFocus(); - QApplication::setActiveWindow(&spinbox); + QApplicationPrivate::setActiveWindow(&spinbox); QVERIFY(QTest::qWaitForWindowActive(&spinbox)); QCOMPARE(static_cast<QWidget *>(&spinbox), QApplication::activeWindow()); QTRY_VERIFY(spinbox.hasFocus()); @@ -1220,7 +1192,7 @@ void tst_QDoubleSpinBox::taskQTBUG_6496_fiddlingWithPrecision() void tst_QDoubleSpinBox::setGroupSeparatorShown_data() { QTest::addColumn<QLocale::Language>("lang"); - QTest::addColumn<QLocale::Country>("country"); + QTest::addColumn<QLocale::Territory>("country"); QTest::newRow("data0") << QLocale::English << QLocale::UnitedStates; QTest::newRow("data1") << QLocale::Swedish << QLocale::Sweden; @@ -1232,7 +1204,7 @@ void tst_QDoubleSpinBox::setGroupSeparatorShown_data() void tst_QDoubleSpinBox::setGroupSeparatorShown() { QFETCH(QLocale::Language, lang); - QFETCH(QLocale::Country, country); + QFETCH(QLocale::Territory, country); QLocale loc(lang, country); QLocale::setDefault(loc); @@ -1788,12 +1760,12 @@ void tst_QDoubleSpinBox::stepModifierPressAndHold() QStyle::CC_SpinBox, &spinBoxStyleOption, subControl, &spin); QTest::mousePress(&spin, Qt::LeftButton, modifiers, buttonRect.center()); - QTRY_VERIFY(spy.length() >= 3); + QTRY_VERIFY(spy.size() >= 3); QTest::mouseRelease(&spin, Qt::LeftButton, modifiers, buttonRect.center()); const auto value = spy.last().at(0); QVERIFY(value.userType() == QMetaType::Double); - QCOMPARE(value.toDouble(), spy.length() * expectedStepModifier); + QCOMPARE(value.toDouble(), spy.size() * expectedStepModifier); } QTEST_MAIN(tst_QDoubleSpinBox) diff --git a/tests/auto/widgets/widgets/qfocusframe/CMakeLists.txt b/tests/auto/widgets/widgets/qfocusframe/CMakeLists.txt index d5e32e9337..5e2f3bd955 100644 --- a/tests/auto/widgets/widgets/qfocusframe/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qfocusframe/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qfocusframe.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qfocusframe Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qfocusframe LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qfocusframe SOURCES tst_qfocusframe.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/widgets/qfocusframe/tst_qfocusframe.cpp b/tests/auto/widgets/widgets/qfocusframe/tst_qfocusframe.cpp index d273d77e35..560ba686cc 100644 --- a/tests/auto/widgets/widgets/qfocusframe/tst_qfocusframe.cpp +++ b/tests/auto/widgets/widgets/qfocusframe/tst_qfocusframe.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -95,7 +70,7 @@ void tst_QFocusFrame::focusFrameInsideScrollview() window.show(); QFocusFrame *focusFrame = nullptr; - QTRY_VERIFY(focusFrame = window.findChild<QFocusFrame *>()); + QTRY_VERIFY((focusFrame = window.findChild<QFocusFrame *>())); const QPoint initialOffset = focusFrame->widget()->mapToGlobal(QPoint()) - focusFrame->mapToGlobal(QPoint()); tableView.scrollTo(itemModel->index(40, 0)); diff --git a/tests/auto/widgets/widgets/qfontcombobox/CMakeLists.txt b/tests/auto/widgets/widgets/qfontcombobox/CMakeLists.txt index 869666cb19..15dad99b9c 100644 --- a/tests/auto/widgets/widgets/qfontcombobox/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qfontcombobox/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qfontcombobox.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qfontcombobox Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qfontcombobox LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qfontcombobox SOURCES tst_qfontcombobox.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/widgets/qfontcombobox/tst_qfontcombobox.cpp b/tests/auto/widgets/widgets/qfontcombobox/tst_qfontcombobox.cpp index 3b5a9387ab..abb9262288 100644 --- a/tests/auto/widgets/widgets/qfontcombobox/tst_qfontcombobox.cpp +++ b/tests/auto/widgets/widgets/qfontcombobox/tst_qfontcombobox.cpp @@ -1,34 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSignalSpy> +#include <QFontDatabase> #include <qfontcombobox.h> @@ -49,6 +25,7 @@ private slots: void writingSystem_data(); void writingSystem(); void currentFontChanged(); + void emptyFont(); }; // Subclass that exposes the protected functions. @@ -100,7 +77,7 @@ void tst_QFontComboBox::currentFont_data() if (!QFontDatabase::isPrivateFamily(defaultFont.family())) QTest::newRow("default2") << defaultFont; QStringList list = QFontDatabase::families(); - for (int i = 0; i < list.count(); ++i) { + for (int i = 0; i < list.size(); ++i) { QFont f = QFont(QStringList{QFontInfo(QFont(list.at(i))).family()}); if (!QFontDatabase::isPrivateFamily(f.families().first())) QTest::newRow(qPrintable(list.at(i))) << f; @@ -128,7 +105,7 @@ void tst_QFontComboBox::currentFont() if (oldCurrentFont != box.currentFont()) { //the signal may be emit twice if there is a foundry into brackets - QCOMPARE(spy0.count(),1); + QCOMPARE(spy0.size(),1); } } @@ -178,7 +155,7 @@ void tst_QFontComboBox::fontFilters() if((fontFilters & spacingMask) == spacingMask) fontFilters &= ~spacingMask; - for (int i = 0; i < list.count(); ++i) { + for (int i = 0; i < list.size(); ++i) { if (QFontDatabase::isPrivateFamily(list[i])) continue; if (fontFilters & QFontComboBox::ScalableFonts) { @@ -203,7 +180,7 @@ void tst_QFontComboBox::fontFilters() if (c == 0) QCOMPARE(box.currentFont(), QFont()); - QCOMPARE(spy0.count(), (currentFont != box.currentFont()) ? 1 : 0); + QCOMPARE(spy0.size(), (currentFont != box.currentFont()) ? 1 : 0); } // public QSize sizeHint() const @@ -244,36 +221,69 @@ void tst_QFontComboBox::writingSystem() QCOMPARE(box.writingSystem(), writingSystem); QStringList list = QFontDatabase::families(writingSystem); - int c = list.count(); - for (int i = 0; i < list.count(); ++i) { + int c = list.size(); + for (int i = 0; i < list.size(); ++i) { if (QFontDatabase::isPrivateFamily(list[i])) c--; } QCOMPARE(box.model()->rowCount(), c); - if (list.count() == 0) + if (list.size() == 0) QCOMPARE(box.currentFont(), QFont()); - QCOMPARE(spy0.count(), (currentFont != box.currentFont()) ? 1 : 0); + QCOMPARE(spy0.size(), (currentFont != box.currentFont()) ? 1 : 0); } // protected void currentFontChanged(QFont const& f) void tst_QFontComboBox::currentFontChanged() { - SubQFontComboBox box; - QSignalSpy spy0(&box, SIGNAL(currentFontChanged(QFont))); + // The absence of this file does not affect the test results + QFontDatabase::addApplicationFont("ArianaVioleta-dz2K.ttf"); - if (box.model()->rowCount() > 2) { - QTest::keyPress(&box, Qt::Key_Down); - QCOMPARE(spy0.count(), 1); + SubQFontComboBox *box = new SubQFontComboBox; + QSignalSpy spy0(box, SIGNAL(currentFontChanged(QFont))); + + if (box->model()->rowCount() > 2) { + QTest::keyPress(box, Qt::Key_Down); + QCOMPARE(spy0.size(), 1); QFont f( "Sans Serif" ); - box.setCurrentFont(f); - QCOMPARE(spy0.count(), 2); + box->setCurrentFont(f); + QCOMPARE(spy0.size(), 2); } else qWarning("Not enough fonts installed on test system. Consider adding some"); } +void tst_QFontComboBox::emptyFont() +{ + QFontComboBox fontCB; + if (fontCB.count() < 2) + QSKIP("Not enough fonts on system to run test."); + + QFont font; + font.setFamilies(QStringList()); + + // Due to QTBUG-98341, we need to find an index in the family list + // which does not match the default index for the empty font, otherwise + // the font selection will not be properly updated. + { + QFontInfo fi(font); + QStringList families = QFontDatabase::families(); + int index = families.indexOf(fi.family()); + if (index < 0) + index = 0; + if (index > 0) + index--; + else + index++; + + fontCB.setCurrentIndex(index); + } + + fontCB.setCurrentFont(font); + QVERIFY(!fontCB.currentFont().families().isEmpty()); +} + QTEST_MAIN(tst_QFontComboBox) #include "tst_qfontcombobox.moc" diff --git a/tests/auto/widgets/widgets/qframe/BLACKLIST b/tests/auto/widgets/widgets/qframe/BLACKLIST deleted file mode 100644 index 3a28dd1239..0000000000 --- a/tests/auto/widgets/widgets/qframe/BLACKLIST +++ /dev/null @@ -1,3 +0,0 @@ -# QTBUG-69064 -[testPainting] -android diff --git a/tests/auto/widgets/widgets/qframe/CMakeLists.txt b/tests/auto/widgets/widgets/qframe/CMakeLists.txt index 968f9ac3f3..2213f4a7d9 100644 --- a/tests/auto/widgets/widgets/qframe/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qframe/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qframe.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qframe Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qframe LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -11,10 +18,10 @@ file(GLOB_RECURSE test_data_glob list(APPEND test_data ${test_data_glob}) qt_internal_add_test(tst_qframe - LOWDPI # special case + LOWDPI SOURCES tst_qframe.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets TESTDATA ${test_data} diff --git a/tests/auto/widgets/widgets/qframe/tst_qframe.cpp b/tests/auto/widgets/widgets/qframe/tst_qframe.cpp index b5272f6b1a..324c512219 100644 --- a/tests/auto/widgets/widgets/qframe/tst_qframe.cpp +++ b/tests/auto/widgets/widgets/qframe/tst_qframe.cpp @@ -1,31 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QFrame> @@ -173,7 +148,16 @@ void tst_QFrame::testPainting() frame.setMidLineWidth(midLineWidth); frame.resize(16, 16); - const QPixmap pixmap = frame.grab(); + QPixmap pixmap = frame.grab(); +#ifdef Q_OS_ANDROID + // QPixmap is created with system's default format, which is + // ARGB32_Premultiplied for Android. For desktop systems the format is + // RGB32, so that's also the format of the images in resources. So on + // Android we need to explicitly convert the pixmap to a proper format. + QImage img = pixmap.toImage(); + QVERIFY(img.reinterpretAsFormat(QImage::Format_RGB32)); + pixmap = QPixmap::fromImage(img); +#endif const QString fileName = QLatin1String("images/") + basename + QLatin1Char('_') + QString::number(lineWidth) + QLatin1Char('_') + QString::number(midLineWidth) diff --git a/tests/auto/widgets/widgets/qgroupbox/BLACKLIST b/tests/auto/widgets/widgets/qgroupbox/BLACKLIST deleted file mode 100644 index 62ba05797e..0000000000 --- a/tests/auto/widgets/widgets/qgroupbox/BLACKLIST +++ /dev/null @@ -1,13 +0,0 @@ -# QTBUG-69083 -[clicked:hit nothing, checkable] -android -[clicked:hit frame, checkable] -android -[clicked:hit nothing, checkable, but unchecked] -android -[clicked:hit frame, checkable, but unchecked] -android - -# QTBUG-69084 -[task_QTBUG_15519_propagateMouseEvents] -android diff --git a/tests/auto/widgets/widgets/qgroupbox/CMakeLists.txt b/tests/auto/widgets/widgets/qgroupbox/CMakeLists.txt index ed08cce5f2..0414ce0cf5 100644 --- a/tests/auto/widgets/widgets/qgroupbox/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qgroupbox/CMakeLists.txt @@ -1,13 +1,22 @@ -# Generated from qgroupbox.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qgroupbox Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qgroupbox LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qgroupbox SOURCES tst_qgroupbox.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui + Qt::GuiPrivate Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp b/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp index b9def66f7c..0d716cce97 100644 --- a/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp +++ b/tests/auto/widgets/widgets/qgroupbox/tst_qgroupbox.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -36,6 +11,11 @@ #include <QDialog> #include <QSignalSpy> +#include <private/qguiapplication_p.h> +#include <qpa/qplatformtheme.h> + +#include <QtWidgets/private/qapplication_p.h> + #include "qgroupbox.h" class tst_QGroupBox : public QObject @@ -70,6 +50,7 @@ private slots: void propagateFocus(); void task_QTBUG_19170_ignoreMouseReleaseEvent(); void task_QTBUG_15519_propagateMouseEvents(); + void buttonPressKeys(); private: bool checked; @@ -392,8 +373,8 @@ void tst_QGroupBox::clicked() else QTest::mouseClick(&testWidget, Qt::LeftButton); - QTEST(int(spy.count()), "clickedCount"); - if (spy.count() > 0) + QTEST(int(spy.size()), "clickedCount"); + if (spy.size() > 0) QTEST(spy.at(0).at(0).toBool(), "finalCheck"); QTEST(testWidget.isChecked(), "finalCheck"); } @@ -407,9 +388,9 @@ void tst_QGroupBox::toggledVsClicked() QSignalSpy clickSpy(&groupBox, SIGNAL(clicked(bool))); groupBox.setChecked(!groupBox.isChecked()); - QCOMPARE(clickSpy.count(), 0); - QCOMPARE(toggleSpy.count(), 1); - if (toggleSpy.count() > 0) + QCOMPARE(clickSpy.size(), 0); + QCOMPARE(toggleSpy.size(), 1); + if (toggleSpy.size() > 0) QCOMPARE(toggleSpy.at(0).at(0).toBool(), groupBox.isChecked()); connect(&groupBox, SIGNAL(clicked(bool)), this, SLOT(clickTimestampSlot())); @@ -422,8 +403,8 @@ void tst_QGroupBox::toggledVsClicked() QStyle::SC_GroupBoxCheckBox, &groupBox); QTest::mouseClick(&groupBox, Qt::LeftButton, {}, rect.center()); - QCOMPARE(clickSpy.count(), 1); - QCOMPARE(toggleSpy.count(), 2); + QCOMPARE(clickSpy.size(), 1); + QCOMPARE(toggleSpy.size(), 2); QVERIFY(toggleTimeStamp < clickTimeStamp); } @@ -449,7 +430,7 @@ void tst_QGroupBox::childrenAreDisabled() layout->addWidget(new QRadioButton); box.setLayout(layout); - foreach (QObject *object, box.children()) { + for (QObject *object : box.children()) { if (QWidget *widget = qobject_cast<QWidget *>(object)) { QVERIFY(!widget->isEnabled()); QVERIFY(!widget->testAttribute(Qt::WA_ForceDisabled)); @@ -457,7 +438,7 @@ void tst_QGroupBox::childrenAreDisabled() } box.setChecked(true); - foreach (QObject *object, box.children()) { + for (QObject *object : box.children()) { if (QWidget *widget = qobject_cast<QWidget *>(object)) { QVERIFY(widget->isEnabled()); QVERIFY(!widget->testAttribute(Qt::WA_ForceDisabled)); @@ -465,7 +446,7 @@ void tst_QGroupBox::childrenAreDisabled() } box.setChecked(false); - foreach (QObject *object, box.children()) { + for (QObject *object : box.children()) { if (QWidget *widget = qobject_cast<QWidget *>(object)) { QVERIFY(!widget->isEnabled()); QVERIFY(!widget->testAttribute(Qt::WA_ForceDisabled)); @@ -481,7 +462,7 @@ void tst_QGroupBox::propagateFocus() QGroupBox box; QLineEdit lineEdit(&box); box.show(); - QApplication::setActiveWindow(&box); + QApplicationPrivate::setActiveWindow(&box); QVERIFY(QTest::qWaitForWindowActive(&box)); box.setFocus(); QTRY_COMPARE(qApp->focusWidget(), static_cast<QWidget*>(&lineEdit)); @@ -611,10 +592,25 @@ void tst_QGroupBox::task_QTBUG_15519_propagateMouseEvents() QCOMPARE(parent.mouseMoved, true); } +void tst_QGroupBox::buttonPressKeys() +{ + QGroupBox groupBox; + groupBox.setCheckable(true); + QSignalSpy clickedSpy(&groupBox, &QGroupBox::clicked); + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value<QList<Qt::Key>>(); + for (int i = 0; i < buttonPressKeys.size(); ++i) { + QTest::keyClick(&groupBox, buttonPressKeys[i]); + QCOMPARE(clickedSpy.size(), i + 1); + } +} + void tst_QGroupBox::sendMouseMoveEvent(QWidget *widget, const QPoint &localPos) { // Send a MouseMove event without actually moving the pointer - QMouseEvent event(QEvent::MouseMove, localPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier); + QMouseEvent event(QEvent::MouseMove, localPos, widget->mapToGlobal(localPos), + Qt::NoButton, Qt::NoButton, Qt::NoModifier); QApplication::sendEvent(widget, &event); } diff --git a/tests/auto/widgets/widgets/qkeysequenceedit/CMakeLists.txt b/tests/auto/widgets/widgets/qkeysequenceedit/CMakeLists.txt index a016280b5d..0cf0b1bdd3 100644 --- a/tests/auto/widgets/widgets/qkeysequenceedit/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qkeysequenceedit/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qkeysequenceedit.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qkeysequenceedit Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qkeysequenceedit LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qkeysequenceedit SOURCES tst_qkeysequenceedit.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/widgets/qkeysequenceedit/tst_qkeysequenceedit.cpp b/tests/auto/widgets/widgets/qkeysequenceedit/tst_qkeysequenceedit.cpp index 1a0532ffa4..301be319bf 100644 --- a/tests/auto/widgets/widgets/qkeysequenceedit/tst_qkeysequenceedit.cpp +++ b/tests/auto/widgets/widgets/qkeysequenceedit/tst_qkeysequenceedit.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -45,6 +20,9 @@ private slots: void testKeys_data(); void testKeys(); void testLineEditContents(); + void testMaximumSequenceLength(); + void testFinishingKeyCombinations_data(); + void testFinishingKeyCombinations(); }; void tst_QKeySequenceEdit::testSetters() @@ -59,7 +37,7 @@ void tst_QKeySequenceEdit::testSetters() edit.clear(); QCOMPARE(edit.keySequence(), QKeySequence()); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); } void tst_QKeySequenceEdit::testKeys_data() @@ -74,6 +52,42 @@ void tst_QKeySequenceEdit::testKeys_data() QTest::newRow("4") << Qt::Key_N << Qt::KeyboardModifiers(Qt::ControlModifier | Qt::ShiftModifier) << QKeySequence("Ctrl+Shift+N"); } +void tst_QKeySequenceEdit::testMaximumSequenceLength() +{ + // + // GIVEN: + // - A QKeySequenceEdit with maxKeyCount == 1 + // - A QKeySequence with more than one key + // + QKeySequenceEdit edit; + edit.setMaximumSequenceLength(1); + QCOMPARE(edit.maximumSequenceLength(), 1); + + QKeySequence multi("Ctrl+X, S"); + QCOMPARE(multi.count(), 2); + + // + // WHEN: setting the key sequence on the edit + // + QTest::ignoreMessage(QtWarningMsg, + "QKeySequenceEdit: setting a key sequence of length 2 when " + "maximumSequenceLength is 1, truncating."); + edit.setKeySequence(multi); + + // + // THEN: + // - the maxKeyCount property doesn't change + // - the key sequence is truncated to maxKeyCount + // - and won't un-truncate by increasing maxKeyCount + // + QCOMPARE(edit.maximumSequenceLength(), 1); + const auto edited = edit.keySequence(); + QCOMPARE(edited.count(), 1); + QCOMPARE(edited, QKeySequence("Ctrl+X")); + edit.setMaximumSequenceLength(2); + QCOMPARE(edit.keySequence(), edited); +} + void tst_QKeySequenceEdit::testKeys() { QFETCH(Qt::Key, key); @@ -85,9 +99,9 @@ void tst_QKeySequenceEdit::testKeys() QTest::keyPress(&edit, key, modifiers); QTest::keyRelease(&edit, key, modifiers); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QCOMPARE(edit.keySequence(), keySequence); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); } void tst_QKeySequenceEdit::testLineEditContents() @@ -111,5 +125,43 @@ void tst_QKeySequenceEdit::testLineEditContents() QCOMPARE(le->text(), QString()); } +void tst_QKeySequenceEdit::testFinishingKeyCombinations_data() +{ + QTest::addColumn<Qt::Key>("key"); + QTest::addColumn<Qt::KeyboardModifiers>("modifiers"); + QTest::addColumn<QKeySequence>("keySequence"); + + QTest::newRow("1") << Qt::Key_Backtab << Qt::KeyboardModifiers(Qt::NoModifier) << QKeySequence("Backtab"); + QTest::newRow("2") << Qt::Key_Tab << Qt::KeyboardModifiers(Qt::NoModifier) << QKeySequence("Tab"); + QTest::newRow("3") << Qt::Key_Return << Qt::KeyboardModifiers(Qt::NoModifier) << QKeySequence("Return"); + QTest::newRow("4") << Qt::Key_Enter << Qt::KeyboardModifiers(Qt::NoModifier) << QKeySequence("Enter"); + QTest::newRow("5") << Qt::Key_Enter << Qt::KeyboardModifiers(Qt::ShiftModifier) << QKeySequence("Shift+Enter"); +} + +void tst_QKeySequenceEdit::testFinishingKeyCombinations() +{ + QFETCH(Qt::Key, key); + QFETCH(Qt::KeyboardModifiers, modifiers); + QFETCH(QKeySequence, keySequence); + QKeySequenceEdit edit; + + QSignalSpy spy(&edit, SIGNAL(editingFinished())); + QCOMPARE(spy.size(), 0); + + edit.setFinishingKeyCombinations({QKeyCombination(modifiers, key)}); + QTest::keyPress(&edit, key, modifiers); + QTest::keyRelease(&edit, key, modifiers); + + QCOMPARE(edit.keySequence(), QKeySequence()); + QTRY_COMPARE(spy.size(), 1); + + edit.setFinishingKeyCombinations({}); + QTest::keyPress(&edit, key, modifiers); + QTest::keyRelease(&edit, key, modifiers); + + QCOMPARE(edit.keySequence(), keySequence); + QTRY_COMPARE(spy.size(), 2); +} + QTEST_MAIN(tst_QKeySequenceEdit) #include "tst_qkeysequenceedit.moc" diff --git a/tests/auto/widgets/widgets/qlabel/CMakeLists.txt b/tests/auto/widgets/widgets/qlabel/CMakeLists.txt index bbd51a206c..e29000a6ca 100644 --- a/tests/auto/widgets/widgets/qlabel/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qlabel/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qlabel.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qlabel Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qlabel LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -17,7 +24,7 @@ list(APPEND test_data ${test_data_glob}) qt_internal_add_test(tst_qlabel SOURCES tst_qlabel.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/widgets/qlabel/testdata/acc_01/res_Windows_data0.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/acc_01/res_Windows_data0.qsnap Binary files differdeleted file mode 100644 index 522c173ac4..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/acc_01/res_Windows_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/acc_01/res_Windows_win32_data0.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/acc_01/res_Windows_win32_data0.qsnap Binary files differdeleted file mode 100644 index acd881d29a..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/acc_01/res_Windows_win32_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data0.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data0.qsnap Binary files differdeleted file mode 100644 index 9e2c1764d3..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data1.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data1.qsnap Binary files differdeleted file mode 100644 index dcd708fdbf..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data1.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data10.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data10.qsnap Binary files differdeleted file mode 100644 index 2131f59fe9..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data10.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data2.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data2.qsnap Binary files differdeleted file mode 100644 index 2edd976830..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data2.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data3.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data3.qsnap Binary files differdeleted file mode 100644 index 2ce28d9816..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data3.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data4.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data4.qsnap Binary files differdeleted file mode 100644 index 6476f6c26b..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data4.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data5.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data5.qsnap Binary files differdeleted file mode 100644 index 6039742962..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data5.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data6.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data6.qsnap Binary files differdeleted file mode 100644 index 477d203960..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data6.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data7.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data7.qsnap Binary files differdeleted file mode 100644 index c673f4099e..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data7.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data8.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data8.qsnap Binary files differdeleted file mode 100644 index a490f77118..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data8.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data9.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data9.qsnap Binary files differdeleted file mode 100644 index 33342d3616..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Motif_data9.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data0.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data0.qsnap Binary files differdeleted file mode 100644 index 3fe9a82c2f..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data1.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data1.qsnap Binary files differdeleted file mode 100644 index 175235dc38..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data1.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data10.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data10.qsnap Binary files differdeleted file mode 100644 index b1ac74b531..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data10.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data2.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data2.qsnap Binary files differdeleted file mode 100644 index fdd3c7c701..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data2.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data3.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data3.qsnap Binary files differdeleted file mode 100644 index caa47f7292..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data3.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data4.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data4.qsnap Binary files differdeleted file mode 100644 index a0d2498e76..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data4.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data5.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data5.qsnap Binary files differdeleted file mode 100644 index 756d9fe827..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data5.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data6.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data6.qsnap Binary files differdeleted file mode 100644 index f973d14c38..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data6.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data7.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data7.qsnap Binary files differdeleted file mode 100644 index 720d807db2..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data7.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data8.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data8.qsnap Binary files differdeleted file mode 100644 index 20fd48e7cc..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data8.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data9.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data9.qsnap Binary files differdeleted file mode 100644 index 7db7c97a14..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_data9.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data0.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data0.qsnap Binary files differdeleted file mode 100644 index d9912d8c92..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data1.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data1.qsnap Binary files differdeleted file mode 100644 index bfc3a6488e..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data1.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data10.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data10.qsnap Binary files differdeleted file mode 100644 index 09a35ef761..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data10.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data2.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data2.qsnap Binary files differdeleted file mode 100644 index 14e11232f0..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data2.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data3.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data3.qsnap Binary files differdeleted file mode 100644 index 6ef864e635..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data3.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data4.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data4.qsnap Binary files differdeleted file mode 100644 index eb029008e3..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data4.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data5.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data5.qsnap Binary files differdeleted file mode 100644 index 439e196b57..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data5.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data6.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data6.qsnap Binary files differdeleted file mode 100644 index 9637d1741a..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data6.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data7.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data7.qsnap Binary files differdeleted file mode 100644 index 9a553465c3..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data7.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data8.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data8.qsnap Binary files differdeleted file mode 100644 index 0d9184c316..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data8.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data9.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data9.qsnap Binary files differdeleted file mode 100644 index f2873b1c78..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setAlignment/alignRes_Windows_win32_data9.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Motif_data0.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Motif_data0.qsnap Binary files differdeleted file mode 100644 index 1385a50d38..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Motif_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Motif_data1.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Motif_data1.qsnap Binary files differdeleted file mode 100644 index 38223cfba2..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Motif_data1.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Motif_data2.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Motif_data2.qsnap Binary files differdeleted file mode 100644 index 0b946a4968..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Motif_data2.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Windows_data0.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Windows_data0.qsnap Binary files differdeleted file mode 100644 index e1d2c41d88..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Windows_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Windows_data1.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Windows_data1.qsnap Binary files differdeleted file mode 100644 index b7bdee551b..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Windows_data1.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Windows_data2.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Windows_data2.qsnap Binary files differdeleted file mode 100644 index a20492ee70..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Windows_data2.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Windows_win32_data0.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Windows_win32_data0.qsnap Binary files differdeleted file mode 100644 index 85e3306d0c..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Windows_win32_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Windows_win32_data1.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Windows_win32_data1.qsnap Binary files differdeleted file mode 100644 index 3bf991f674..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Windows_win32_data1.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Windows_win32_data2.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Windows_win32_data2.qsnap Binary files differdeleted file mode 100644 index f05a9dcebd..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setIndent/indentRes_Windows_win32_data2.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/Vpix_Motif_data0.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/Vpix_Motif_data0.qsnap Binary files differdeleted file mode 100644 index d7428df5ef..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/Vpix_Motif_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/Vpix_Windows_data0.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/Vpix_Windows_data0.qsnap Binary files differdeleted file mode 100644 index 905acd1283..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/Vpix_Windows_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/Vpix_Windows_win32_data0.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/Vpix_Windows_win32_data0.qsnap Binary files differdeleted file mode 100644 index e1dea4b76b..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/Vpix_Windows_win32_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/empty_Motif_data0.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/empty_Motif_data0.qsnap Binary files differdeleted file mode 100644 index 055ccda47e..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/empty_Motif_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/empty_Windows_data0.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/empty_Windows_data0.qsnap Binary files differdeleted file mode 100644 index 64b70763d4..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/empty_Windows_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/empty_Windows_win32_data0.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/empty_Windows_win32_data0.qsnap Binary files differdeleted file mode 100644 index fb0ea227ad..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/empty_Windows_win32_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/scaledVpix_Motif_data0.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/scaledVpix_Motif_data0.qsnap Binary files differdeleted file mode 100644 index ae9cda541f..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/scaledVpix_Motif_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/scaledVpix_Windows_data0.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/scaledVpix_Windows_data0.qsnap Binary files differdeleted file mode 100644 index e21af3223a..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/scaledVpix_Windows_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/scaledVpix_Windows_win32_data0.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/scaledVpix_Windows_win32_data0.qsnap Binary files differdeleted file mode 100644 index 32cc40652b..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setPixmap/scaledVpix_Windows_win32_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Motif_data0.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Motif_data0.qsnap Binary files differdeleted file mode 100644 index 7191b517d2..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Motif_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Motif_data1.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Motif_data1.qsnap Binary files differdeleted file mode 100644 index d943ce960a..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Motif_data1.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Motif_data2.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Motif_data2.qsnap Binary files differdeleted file mode 100644 index 946432e66d..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Motif_data2.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Motif_data3.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Motif_data3.qsnap Binary files differdeleted file mode 100644 index dc5ac0c56d..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Motif_data3.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_data0.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_data0.qsnap Binary files differdeleted file mode 100644 index 2ab392e50e..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_data1.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_data1.qsnap Binary files differdeleted file mode 100644 index 5769459ab6..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_data1.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_data2.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_data2.qsnap Binary files differdeleted file mode 100644 index b4206c2a57..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_data2.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_data3.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_data3.qsnap Binary files differdeleted file mode 100644 index f120ac2e76..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_data3.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_win32_data0.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_win32_data0.qsnap Binary files differdeleted file mode 100644 index 7644f53726..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_win32_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_win32_data1.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_win32_data1.qsnap Binary files differdeleted file mode 100644 index 7902b1b087..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_win32_data1.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_win32_data2.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_win32_data2.qsnap Binary files differdeleted file mode 100644 index 60bd075c78..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_win32_data2.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_win32_data3.qsnap b/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_win32_data3.qsnap Binary files differdeleted file mode 100644 index c1dcb272b3..0000000000 --- a/tests/auto/widgets/widgets/qlabel/testdata/setText/res_Windows_win32_data3.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qlabel/tst_qlabel.cpp b/tests/auto/widgets/widgets/qlabel/tst_qlabel.cpp index 5eed823a34..325e188091 100644 --- a/tests/auto/widgets/widgets/qlabel/tst_qlabel.cpp +++ b/tests/auto/widgets/widgets/qlabel/tst_qlabel.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -38,11 +13,13 @@ #include <qmovie.h> #include <qpicture.h> #include <qmessagebox.h> +#include <qsizepolicy.h> #include <qfontmetrics.h> #include <qmath.h> -#include <qurlresourceprovider.h> #include <private/qlabel_p.h> +#include <QtWidgets/private/qapplication_p.h> + class Widget : public QWidget { Q_OBJECT @@ -76,6 +53,7 @@ private Q_SLOTS: #endif void setNum(); void clear(); + void wordWrap_data(); void wordWrap(); void eventPropagation_data(); void eventPropagation(); @@ -99,12 +77,16 @@ private Q_SLOTS: #ifndef QT_NO_CONTEXTMENU void taskQTBUG_7902_contextMenuCrash(); + void contextMenu_data(); + void contextMenu(); #endif void taskQTBUG_48157_dprPixmap(); void taskQTBUG_48157_dprMovie(); void resourceProvider(); + void mouseEventPropagation_data(); + void mouseEventPropagation(); private: QLabel *testWidget; @@ -116,16 +98,12 @@ private: void tst_QLabel::getSetCheck() { QLabel obj1; - // bool QLabel::wordWrap() - // void QLabel::setWordWrap(bool) obj1.setWordWrap(false); - QCOMPARE(false, obj1.wordWrap()); + QVERIFY(!obj1.wordWrap()); obj1.setWordWrap(true); - QCOMPARE(true, obj1.wordWrap()); + QVERIFY(obj1.wordWrap()); #if QT_CONFIG(shortcut) - // QWidget * QLabel::buddy() - // void QLabel::setBuddy(QWidget *) QWidget *var2 = new QWidget(); obj1.setBuddy(var2); QCOMPARE(var2, obj1.buddy()); @@ -134,8 +112,6 @@ void tst_QLabel::getSetCheck() delete var2; #endif // QT_CONFIG(shortcut) - // QMovie * QLabel::movie() - // void QLabel::setMovie(QMovie *) QMovie *var3 = new QMovie; obj1.setMovie(var3); QCOMPARE(var3, obj1.movie()); @@ -195,7 +171,7 @@ void tst_QLabel::setBuddy() layout->addWidget(test_edit); layout->addWidget(test_edit2); test_box->show(); - qApp->setActiveWindow(test_box); + QApplicationPrivate::setActiveWindow(test_box); QVERIFY(test_box->isActiveWindow()); test_label->setBuddy( test_edit ); @@ -275,21 +251,36 @@ void tst_QLabel::clear() QVERIFY(testWidget->text().isEmpty()); } +void tst_QLabel::wordWrap_data() +{ + QTest::addColumn<QString>("text"); + + QTest::newRow("Plain text") << "Plain text1"; + QTest::newRow("Rich text") << "<b>Rich text</b>"; + QTest::newRow("Long text") + << "This is a very long text to check that QLabel " + "does not wrap, even if the text would require wrapping to be fully displayed"; +} + void tst_QLabel::wordWrap() { - QLabel label; + QFETCH(QString, text); + QLabel label; + label.setText(text); QVERIFY(!label.wordWrap()); + QVERIFY(!label.sizePolicy().hasHeightForWidth()); - label.setText("Plain Text"); - QVERIFY(!label.wordWrap()); + const QSize unWrappedSizeHint = label.sizeHint(); - label.setText("<b>rich text</b>"); - QVERIFY(!label.wordWrap()); + label.setWordWrap(true); + QVERIFY(label.sizePolicy().hasHeightForWidth()); + + if (text.size() > 1 && text.contains(" ")) { + const int wrappedHeight = label.heightForWidth(unWrappedSizeHint.width() / 2); + QVERIFY(wrappedHeight > unWrappedSizeHint.height()); + } - label.setWordWrap(false); - label.setText("<b>rich text</b>"); - QVERIFY(!label.wordWrap()); } void tst_QLabel::eventPropagation_data() @@ -440,7 +431,6 @@ void tst_QLabel::emptyPixmap() void tst_QLabel::unicodeText_data() { QTest::addColumn<QString>("text"); - QTest::addColumn<QString>("languageName"); /* The "glass" phrase in Thai was the initial report for bug QTBUG-4848, was @@ -457,25 +447,21 @@ void tst_QLabel::unicodeText_data() speech, also translated using http://translate.google.com. */ - QTest::newRow("english") << QString::fromUtf8("I can eat glass and it doesn't hurt me.") << QString("english"); - QTest::newRow("thai") << QString::fromUtf8("ฉันจะกินแก้วและไม่เจ็บฉัน") << QString("thai"); - QTest::newRow("chinese") << QString::fromUtf8("我可以吃玻璃,并没有伤害我。") << QString("chinese"); - QTest::newRow("arabic") << QString::fromUtf8("أستطيع أكل الزجاج ، وأنه لا يؤذيني.") << QString("arabic"); - QTest::newRow("russian") << QString::fromUtf8("Я могу есть стекло, и не больно.") << QString("russian"); - QTest::newRow("korean") << QString::fromUtf8("유리를 먹을 수있는, 그리고 그게 날 다치게하지 않습니다.") << QString("korean"); - QTest::newRow("greek") << QString::fromUtf8("Μπορώ να φάτε γυαλί και δεν μου κάνει κακό.") << QString("greek"); - QTest::newRow("german") << QString::fromUtf8("Ich kann Glas essen und es macht mich nicht heiß.") << QString("german"); - + QTest::newRow("english") << QString::fromUtf8("I can eat glass and it doesn't hurt me."); + QTest::newRow("thai") << QString::fromUtf8("ฉันจะกินแก้วและไม่เจ็บฉัน"); + QTest::newRow("chinese") << QString::fromUtf8("我可以吃玻璃,并没有伤害我。"); + QTest::newRow("arabic") << QString::fromUtf8("أستطيع أكل الزجاج ، وأنه لا يؤذيني."); + QTest::newRow("russian") << QString::fromUtf8("Я могу есть стекло, и не больно."); + QTest::newRow("korean") << QString::fromUtf8("유리를 먹을 수있는, 그리고 그게 날 다치게하지 않습니다."); + QTest::newRow("greek") << QString::fromUtf8("Μπορώ να φάτε γυαλί και δεν μου κάνει κακό."); + QTest::newRow("german") << QString::fromUtf8("Ich kann Glas essen und es macht mich nicht heiß."); QTest::newRow("thai_long") << QString::fromUtf8("เราจะต่อสู้ในทะเลและมหาสมุทร. เราจะต่อสู้ด้วยความมั่นใจเติบโตและความเจริญเติบโตในอากาศเราจะปกป้องเกาะของเราค่าใช้จ่ายใดๆอาจ." - "เราจะต่อสู้บนชายหาดเราจะต่อสู้ในบริเวณเชื่อมโยงไปถึงเราจะต่อสู้ในช่องและในถนนที่เราจะต่อสู้ในภูเขานั้นเราจะไม่ยอม.") - << QString("thai_long"); + "เราจะต่อสู้บนชายหาดเราจะต่อสู้ในบริเวณเชื่อมโยงไปถึงเราจะต่อสู้ในช่องและในถนนที่เราจะต่อสู้ในภูเขานั้นเราจะไม่ยอม."); } void tst_QLabel::unicodeText() { - const QString testDataPath("testdata/unicodeText"); QFETCH(QString, text); - QFETCH(QString, languageName); QFrame frame; QVBoxLayout *layout = new QVBoxLayout(); QLabel *label = new QLabel(text, &frame); @@ -570,12 +556,50 @@ void tst_QLabel::taskQTBUG_7902_contextMenuCrash() w->connect(&ti, SIGNAL(timeout()), w, SLOT(deleteLater())); ti.start(300); - QContextMenuEvent *cme = new QContextMenuEvent(QContextMenuEvent::Mouse, w->rect().center()); + QContextMenuEvent *cme = new QContextMenuEvent(QContextMenuEvent::Mouse, w->rect().center(), + w->mapToGlobal(w->rect().center())); qApp->postEvent(w, cme); QTest::qWait(350); // No crash, it's allright. } + +void tst_QLabel::contextMenu_data() +{ + QTest::addColumn<QString>("text"); + QTest::addColumn<Qt::TextInteractionFlag>("interactionFlags"); + QTest::addColumn<bool>("showsContextMenu"); + + QTest::addRow("Read-only") << "Plain Text" + << Qt::NoTextInteraction + << false; + QTest::addRow("Selectable") << "Plain Text" + << Qt::TextEditorInteraction + << true; + QTest::addRow("Link") << "<a href=\"nowhere\">Rich text with link</a>" + << Qt::TextBrowserInteraction + << true; + QTest::addRow("Rich text") << "<b>Rich text without link</b>" + << Qt::TextBrowserInteraction + << true; +} + +void tst_QLabel::contextMenu() +{ + QFETCH(QString, text); + QFETCH(Qt::TextInteractionFlag, interactionFlags); + QFETCH(bool, showsContextMenu); + + QLabel label(text); + label.setTextInteractionFlags(interactionFlags); + label.show(); + QVERIFY(QTest::qWaitForWindowExposed(&label)); + + const QPoint menuPosition = label.rect().center(); + QContextMenuEvent cme(QContextMenuEvent::Mouse, menuPosition, label.mapToGlobal(menuPosition)); + QApplication::sendEvent(&label, &cme); + QCOMPARE(cme.isAccepted(), showsContextMenu); +} #endif void tst_QLabel::taskQTBUG_48157_dprPixmap() @@ -585,7 +609,7 @@ void tst_QLabel::taskQTBUG_48157_dprPixmap() pixmap.load(QFINDTESTDATA(QStringLiteral("red@2x.png"))); QCOMPARE(pixmap.devicePixelRatio(), 2.0); label.setPixmap(pixmap); - QCOMPARE(label.sizeHint(), pixmap.rect().size() / pixmap.devicePixelRatio()); + QCOMPARE(label.sizeHint(), pixmap.deviceIndependentSize().toSize()); } void tst_QLabel::taskQTBUG_48157_dprMovie() @@ -596,30 +620,114 @@ void tst_QLabel::taskQTBUG_48157_dprMovie() movie.start(); QCOMPARE(movie.currentPixmap().devicePixelRatio(), 2.0); label.setMovie(&movie); - QCOMPARE(label.sizeHint(), movie.currentPixmap().size() / movie.currentPixmap().devicePixelRatio()); + QCOMPARE(label.sizeHint(), movie.currentPixmap().deviceIndependentSize().toSize()); } -class UrlResourceProvider : public QUrlResourceProvider -{ -public: - QVariant resource(const QUrl &url) override - { - resourseUrl = url; - return QVariant(); - } - - QUrl resourseUrl; -}; - void tst_QLabel::resourceProvider() { QLabel label; - UrlResourceProvider resourceProvider; - label.setResourceProvider(&resourceProvider); - QUrl url("test://img"); + int providerCalled = 0; + QUrl providerUrl; + label.setResourceProvider([&](const QUrl &url){ + providerUrl = url; + ++providerCalled; + return QVariant(); + }); + + const QUrl url("test://img"); label.setText(QStringLiteral("<img src='%1'/>").arg(url.toString())); label.show(); - QCOMPARE(url, resourceProvider.resourseUrl); + QCOMPARE(providerUrl, url); + QVERIFY(providerCalled > 0); +} + +// Test if mouse events are correctly propagated to the parent widget, +// even if a label contains rich text (QTBUG-110055) +void tst_QLabel::mouseEventPropagation_data() +{ + QTest::addColumn<const QString>("text"); + QTest::addColumn<const Qt::TextInteractionFlag>("interaction"); + QTest::addColumn<const QList<Qt::MouseButton>>("buttons"); + QTest::addColumn<const bool>("expectPropagation"); + + + QTest::newRow("RichText") + << QString("<b>This is a rich text propagating mouse events</b>") + << Qt::LinksAccessibleByMouse + << QList<Qt::MouseButton>{Qt::LeftButton, Qt::RightButton, Qt::MiddleButton} + << true; + QTest::newRow("PlainText") + << QString("This is a plain text propagating mouse events") + << Qt::LinksAccessibleByMouse + << QList<Qt::MouseButton>{Qt::LeftButton, Qt::RightButton, Qt::MiddleButton} + << true; + QTest::newRow("PlainTextConsume") + << QString("This is a plain text consuming mouse events") + << Qt::TextSelectableByMouse + << QList<Qt::MouseButton>{Qt::LeftButton} + << false; + QTest::newRow("RichTextConsume") + << QString("<b>This is a rich text consuming mouse events</b>") + << Qt::TextSelectableByMouse + << QList<Qt::MouseButton>{Qt::LeftButton} + << false; + QTest::newRow("PlainTextNoInteraction") + << QString("This is a text not interacting with mouse") + << Qt::NoTextInteraction + << QList<Qt::MouseButton>{Qt::LeftButton, Qt::RightButton, Qt::MiddleButton} + << true; + QTest::newRow("RichTextNoInteraction") + << QString("<b>This is a rich text not interacting with mouse</b>") + << Qt::NoTextInteraction + << QList<Qt::MouseButton>{Qt::LeftButton, Qt::RightButton, Qt::MiddleButton} + << true; +} + +void tst_QLabel::mouseEventPropagation() +{ + class MouseEventWidget : public QWidget + { + public: + uint pressed() const { return m_pressed; } + uint released() const { return m_released; } + + private: + uint m_pressed = 0; + uint m_released = 0; + void mousePressEvent(QMouseEvent *event) override + { + ++m_pressed; + return QWidget::mousePressEvent(event); + } + + void mouseReleaseEvent(QMouseEvent *event) override + { + ++m_released; + return QWidget::mouseReleaseEvent(event); + } + }; + + QFETCH(const QString, text); + QFETCH(const Qt::TextInteractionFlag, interaction); + QFETCH(const QList<Qt::MouseButton>, buttons); + QFETCH(const bool, expectPropagation); + + MouseEventWidget widget; + auto *layout = new QVBoxLayout(&widget); + auto *label = new QLabel(text); + label->setTextInteractionFlags(interaction); + + layout->addWidget(label); + widget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&widget)); + + const QPoint labelCenter = label->rect().center(); + for (Qt::MouseButton mouseButton : buttons) + QTest::mouseClick(label, mouseButton, Qt::KeyboardModifiers(), labelCenter); + + const uint count = expectPropagation ? buttons.count() : 0; + QTRY_COMPARE(widget.pressed(), count); + QTRY_COMPARE(widget.released(), count); } QTEST_MAIN(tst_QLabel) diff --git a/tests/auto/widgets/widgets/qlcdnumber/CMakeLists.txt b/tests/auto/widgets/widgets/qlcdnumber/CMakeLists.txt index 4d6a8ff77a..954ec095e5 100644 --- a/tests/auto/widgets/widgets/qlcdnumber/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qlcdnumber/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qlcdnumber.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qlcdnumber Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qlcdnumber LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qlcdnumber SOURCES tst_qlcdnumber.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/widgets/qlcdnumber/tst_qlcdnumber.cpp b/tests/auto/widgets/widgets/qlcdnumber/tst_qlcdnumber.cpp index a97a090ebd..8fcf9c49fe 100644 --- a/tests/auto/widgets/widgets/qlcdnumber/tst_qlcdnumber.cpp +++ b/tests/auto/widgets/widgets/qlcdnumber/tst_qlcdnumber.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qlineedit/BLACKLIST b/tests/auto/widgets/widgets/qlineedit/BLACKLIST index 9f87a6d921..a459495d1a 100644 --- a/tests/auto/widgets/widgets/qlineedit/BLACKLIST +++ b/tests/auto/widgets/widgets/qlineedit/BLACKLIST @@ -3,13 +3,7 @@ android [leftKeyOnSelectedText] android -[cutWithoutSelection] -android -[inlineCompletion] -android [textMargin] android -[task210502_caseInsensitiveInlineCompletion] -android [testQuickSelectionWithMouse] android diff --git a/tests/auto/widgets/widgets/qlineedit/CMakeLists.txt b/tests/auto/widgets/widgets/qlineedit/CMakeLists.txt index 8cdee2a111..22ecf40aed 100644 --- a/tests/auto/widgets/widgets/qlineedit/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qlineedit/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qlineedit.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qlineedit Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qlineedit LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qlineedit SOURCES tst_qlineedit.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate @@ -20,6 +27,6 @@ qt_internal_add_test(tst_qlineedit ##################################################################### qt_internal_extend_target(tst_qlineedit CONDITION MACOS - PUBLIC_LIBRARIES + LIBRARIES ${FWAppKit} ) diff --git a/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp b/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp index 8668dfb8e2..7a63c156f3 100644 --- a/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp +++ b/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -292,6 +267,8 @@ private slots: void inputMethodQueryImHints_data(); void inputMethodQueryImHints(); + void inputMethodQueryEnterKeyType(); + void inputMethodUpdate(); void undoRedoAndEchoModes_data(); @@ -301,6 +278,7 @@ private slots: void clearButtonVisibleAfterSettingText_QTBUG_45518(); void sideWidgets(); void sideWidgetsActionEvents(); + void sideWidgetsEffectiveMargins(); void shouldShowPlaceholderText_data(); void shouldShowPlaceholderText(); @@ -314,6 +292,13 @@ private slots: void QTBUG_60319_setInputMaskCheckImSurroundingText(); void testQuickSelectionWithMouse(); void inputRejected(); + void keyReleasePropagates(); + +#if QT_CONFIG(shortcut) + void deleteWordByKeySequence_data(); + void deleteWordByKeySequence(); +#endif + protected slots: void editingFinished(); @@ -676,7 +661,7 @@ void tst_QLineEdit::setInputMask() testWidget->insert(input); } else { psKeyClick(testWidget, Qt::Key_Home); - for (int i=0; i<input.length(); i++) + for (int i=0; i<input.size(); i++) QTest::keyClick(testWidget, input.at(i).toLatin1()); } @@ -943,8 +928,8 @@ void tst_QLineEdit::hasAcceptableInputValidator() qApp->sendEvent(testWidget, &lostFocus); QVERIFY(testWidget->hasAcceptableInput()); - QCOMPARE(spyChanged.count(), 2); - QCOMPARE(spyEdited.count(), 0); + QCOMPARE(spyChanged.size(), 2); + QCOMPARE(spyEdited.size(), 0); } @@ -1616,7 +1601,7 @@ void tst_QLineEdit::setText() QSignalSpy editedSpy(testWidget, SIGNAL(textEdited(QString))); QSignalSpy changedSpy(testWidget, SIGNAL(textChanged(QString))); testWidget->setText("hello"); - QCOMPARE(editedSpy.count(), 0); + QCOMPARE(editedSpy.size(), 0); QCOMPARE(changedSpy.value(0).value(0).toString(), QString("hello")); } @@ -1688,7 +1673,7 @@ void tst_QLineEdit::displayText_data() QString input; QString pass; input = "Hello World"; - pass.resize(input.length()); + pass.resize(input.size()); pass.fill(passChar); QTest::newRow(QString(s + " text0").toLatin1()) << input << pass << m << bool(use_setText); QTest::newRow(QString(s + " text1").toLatin1()) << QString("") << @@ -1696,14 +1681,14 @@ void tst_QLineEdit::displayText_data() m << bool(use_setText); QTest::newRow(QString(s + " text2").toLatin1()) << QString("A") << QString(passChar) << m << bool(use_setText); input = QString("ryyryryryryryryryryryryryryryryryryryryryryryryryryryrryryryryryryryryryryryryryryryryryryryryryryryryryryryryryryryryryrryryryryryryryryryryryryry"); - pass.resize(input.length()); + pass.resize(input.size()); pass.fill(passChar); QTest::newRow(QString(s + " text3").toLatin1()) << input << pass << m << bool(use_setText); input = QString("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890`~!@#$%^&*()_-+={[}]|\\:;'?/>.<,\""); - pass.fill(passChar, input.length()); + pass.fill(passChar, input.size()); QTest::newRow(QString(s + " text4").toLatin1()) << input << pass << m << bool(use_setText); input = QString("Hello") + QChar(0xa0) + "World"; - pass.resize(input.length()); + pass.resize(input.size()); pass.fill(passChar); QTest::newRow(QString(s + " text with nbsp").toLatin1()) << input << pass << m << bool(use_setText); } @@ -2249,6 +2234,11 @@ void tst_QLineEdit::cursorPositionChanged() input.simulate(testWidget); QCOMPARE(lastCursorPos, lastPos); QCOMPARE(newCursorPos, newPos); + + QInputMethodQueryEvent queryEvent(Qt::ImCursorPosition | Qt::ImAbsolutePosition); + QApplication::sendEvent(testWidget, &queryEvent); + QCOMPARE(queryEvent.value(Qt::ImCursorPosition).toInt(), newPos); + QCOMPARE(queryEvent.value(Qt::ImAbsolutePosition).toInt(), newPos); } void tst_QLineEdit::selectedText() @@ -2311,7 +2301,7 @@ void tst_QLineEdit::deleteSelectedText() #ifndef QT_NO_CONTEXTMENU QMenu *menu = edit.createStandardContextMenu(); - for (int i = 0; i < menu->actions().count(); ++i) { + for (int i = 0; i < menu->actions().size(); ++i) { QAction *current = menu->actions().at(i); if (current->text() == QLineEdit::tr("Delete")) { current->trigger(); //this will delete the whole text selected @@ -2444,7 +2434,7 @@ class QIntFixValidator : public QIntValidator { public: QIntFixValidator(int min, int max, QObject *parent) : QIntValidator(min, max, parent) {} void fixup (QString &input) const override { - for (int i=0; i<input.length(); ++i) + for (int i=0; i<input.size(); ++i) if (!input.at(i).isNumber()) { input[(int)i] = QChar('0'); } @@ -3010,7 +3000,7 @@ void tst_QLineEdit::setSelection_data() QTest::newRow(selectionTestName(start, length).constData()) << text << start << length << pos << QString("Abc ") << true; - start = -1; length = 0; pos = text.length(); + start = -1; length = 0; pos = text.size(); QTest::newRow(selectionTestName(start, length).constData()) << text << start << length << pos << QString() << false; @@ -3030,7 +3020,7 @@ void tst_QLineEdit::setSelection_data() QTest::newRow(selectionTestName(start, length).constData()) << text << start << length << pos << QString("A") << true; - start = -1; length = -1; pos = text.length(); + start = -1; length = -1; pos = text.size(); QTest::newRow(selectionTestName(start, length).constData()) << text << start << length << pos << QString() << false; } @@ -3322,12 +3312,12 @@ void tst_QLineEdit::validateOnFocusOut() QLineEdit *testWidget = ensureTestWidget(); QSignalSpy editingFinishedSpy(testWidget, SIGNAL(editingFinished())); - testWidget->setValidator(new QIntValidator(100, 999, 0)); + testWidget->setValidator(new QIntValidator(100, 999, testWidget)); QTest::keyPress(testWidget, '1'); QTest::keyPress(testWidget, '0'); QCOMPARE(testWidget->text(), QString("10")); testWidget->clearFocus(); - QCOMPARE(editingFinishedSpy.count(), 0); + QCOMPARE(editingFinishedSpy.size(), 0); testWidget->setFocus(); centerOnScreen(testWidget); @@ -3340,14 +3330,14 @@ void tst_QLineEdit::validateOnFocusOut() QTRY_COMPARE(testWidget->text(), QString("100")); testWidget->clearFocus(); - QCOMPARE(editingFinishedSpy.count(), 1); + QCOMPARE(editingFinishedSpy.size(), 1); } void tst_QLineEdit::editInvalidText() { QLineEdit *testWidget = ensureTestWidget(); testWidget->clear(); - testWidget->setValidator(new QIntValidator(0, 12, 0)); + testWidget->setValidator(new QIntValidator(0, 12, testWidget)); testWidget->setText("1234"); QVERIFY(!testWidget->hasAcceptableInput()); @@ -3424,6 +3414,9 @@ void tst_QLineEdit::leftKeyOnSelectedText() void tst_QLineEdit::inlineCompletion() { +#ifdef Q_OS_ANDROID + QSKIP("QCompleter does not work on Android, see QTBUG-77174"); +#endif if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) QSKIP("Wayland: This fails. Figure out why."); @@ -3515,7 +3508,7 @@ void tst_QLineEdit::noTextEditedOnClear() testWidget->setText("Test"); QSignalSpy textEditedSpy(testWidget, SIGNAL(textEdited(QString))); testWidget->clear(); - QCOMPARE(textEditedSpy.count(), 0); + QCOMPARE(textEditedSpy.size(), 0); } void tst_QLineEdit::textMargin_data() @@ -3531,11 +3524,12 @@ void tst_QLineEdit::textMargin_data() QLineEdit testWidget; QFontMetrics metrics(testWidget.font()); const QString s = QLatin1String("MMM MMM MMM"); + const int windows11StyleHorizontalOffset = qApp->style()->inherits("QWindows11Style") ? 8 : 0; // Different styles generate different offsets, so // calculate the width rather than hardcode it. - const int pixelWidthOfM = metrics.horizontalAdvance(s, 1); - const int pixelWidthOfMMM_MM = metrics.horizontalAdvance(s, 6); + const int pixelWidthOfM = windows11StyleHorizontalOffset + metrics.horizontalAdvance(s, 1); + const int pixelWidthOfMMM_MM = windows11StyleHorizontalOffset + metrics.horizontalAdvance(s, 6); QTest::newRow("default-0") << 0 << 0 << 0 << 0 << QPoint(pixelWidthOfMMM_MM, 0) << 6; QTest::newRow("default-1") << 0 << 0 << 0 << 0 << QPoint(1, 1) << 0; @@ -3619,7 +3613,7 @@ void tst_QLineEdit::returnKeyClearsEditedFlag() // Focus drop with no edits shouldn't emit signal, edited flag == false testWidget.clearFocus(); // Signal not emitted QVERIFY(!testWidget.hasFocus()); - QCOMPARE(leSpy.count(), 0); + QCOMPARE(leSpy.size(), 0); // Focus drop after edits should emit signal, edited flag == true testWidget.setFocus(); @@ -3627,7 +3621,7 @@ void tst_QLineEdit::returnKeyClearsEditedFlag() QTest::keyClicks(&testWidget, "edit1 "); // edited flag set testWidget.clearFocus(); // edited flag cleared, signal emitted QVERIFY(!testWidget.hasFocus()); - QCOMPARE(leSpy.count(), 1); + QCOMPARE(leSpy.size(), 1); // Only text related keys should set edited flag testWidget.setFocus(); @@ -3637,7 +3631,7 @@ void tst_QLineEdit::returnKeyClearsEditedFlag() QTest::keyClick(&testWidget, Qt::Key_PageUp); testWidget.clearFocus(); // Signal not emitted QVERIFY(!testWidget.hasFocus()); - QCOMPARE(leSpy.count(), 1); // No change + QCOMPARE(leSpy.size(), 1); // No change // Return should always emit signal testWidget.setFocus(); @@ -3645,12 +3639,12 @@ void tst_QLineEdit::returnKeyClearsEditedFlag() QTest::keyClick(&testWidget, Qt::Key_Return); /* Without edits, signal emitted, edited flag cleared */ - QCOMPARE(leSpy.count(), 2); + QCOMPARE(leSpy.size(), 2); QTest::keyClicks(&testWidget, "edit2 "); // edited flag set QTest::keyClick(&testWidget, Qt::Key_Return); /* With edits, signal emitted, edited flag cleared */ - QCOMPARE(leSpy.count(), 3); + QCOMPARE(leSpy.size(), 3); /* After editing the line edit following a Return key press with a focus drop should not emit signal a second time since Return now @@ -3658,10 +3652,10 @@ void tst_QLineEdit::returnKeyClearsEditedFlag() QTest::keyClicks(&testWidget, "edit3 "); // edited flag set QTest::keyClick(&testWidget, Qt::Key_Return); /* signal emitted, edited flag cleared */ - QCOMPARE(leSpy.count(), 4); + QCOMPARE(leSpy.size(), 4); testWidget.clearFocus(); // Signal not emitted since edited == false QVERIFY(!testWidget.hasFocus()); - QCOMPARE(leSpy.count(), 4); // No change + QCOMPARE(leSpy.size(), 4); // No change } #ifndef QT_NO_CURSOR @@ -3728,7 +3722,6 @@ void tst_QLineEdit::task174640_editingFinished() layout->addWidget(le2); mw.show(); - QApplication::setActiveWindow(&mw); mw.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&mw)); QCOMPARE(&mw, QApplication::activeWindow()); @@ -3737,19 +3730,19 @@ void tst_QLineEdit::task174640_editingFinished() le1->setFocus(); QTRY_VERIFY(le1->hasFocus()); - QCOMPARE(editingFinishedSpy.count(), 0); + QCOMPARE(editingFinishedSpy.size(), 0); le2->setFocus(); QTRY_VERIFY(le2->hasFocus()); // editingFinished will not be emitted anew because no editing happened - QCOMPARE(editingFinishedSpy.count(), 0); + QCOMPARE(editingFinishedSpy.size(), 0); le1->setFocus(); QTRY_VERIFY(le1->hasFocus()); QTest::keyPress(le1, Qt::Key_Plus); le2->setFocus(); QTRY_VERIFY(le2->hasFocus()); - QCOMPARE(editingFinishedSpy.count(), 1); + QCOMPARE(editingFinishedSpy.size(), 1); editingFinishedSpy.clear(); le1->setFocus(); @@ -3764,7 +3757,7 @@ void tst_QLineEdit::task174640_editingFinished() mw.activateWindow(); delete testMenu1; - QCOMPARE(editingFinishedSpy.count(), 0); + QCOMPARE(editingFinishedSpy.size(), 0); QTRY_VERIFY(le1->hasFocus()); // Ensure le1 has been edited QTest::keyPress(le1, Qt::Key_Plus); @@ -3777,7 +3770,7 @@ void tst_QLineEdit::task174640_editingFinished() QTest::qWait(20); mw.activateWindow(); delete testMenu2; - QCOMPARE(editingFinishedSpy.count(), 1); + QCOMPARE(editingFinishedSpy.size(), 1); } #if QT_CONFIG(completer) @@ -3821,6 +3814,9 @@ void tst_QLineEdit::task198789_currentCompletion() void tst_QLineEdit::task210502_caseInsensitiveInlineCompletion() { +#ifdef Q_OS_ANDROID + QSKIP("QCompleter does not work on Android, see QTBUG-77174"); +#endif if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) QSKIP("Wayland: This fails. Figure out why."); @@ -3833,7 +3829,6 @@ void tst_QLineEdit::task210502_caseInsensitiveInlineCompletion() completer.setCompletionMode(QCompleter::InlineCompletion); lineEdit.setCompleter(&completer); lineEdit.show(); - QApplication::setActiveWindow(&lineEdit); QVERIFY(QTest::qWaitForWindowActive(&lineEdit)); lineEdit.setFocus(); QTRY_VERIFY(lineEdit.hasFocus()); @@ -3859,7 +3854,7 @@ void tst_QLineEdit::task229938_dontEmitChangedWhenTextIsNotChanged() QTest::keyPress(&lineEdit, 'd'); QTest::keyPress(&lineEdit, 'e'); QTest::keyPress(&lineEdit, 'f'); - QCOMPARE(changedSpy.count(), 5); + QCOMPARE(changedSpy.size(), 5); } void tst_QLineEdit::task233101_cursorPosAfterInputMethod_data() @@ -3934,7 +3929,6 @@ void tst_QLineEdit::task241436_passwordEchoOnEditRestoreEchoMode() testWidget->setFocus(); centerOnScreen(testWidget); testWidget->show(); - QApplication::setActiveWindow(testWidget); QVERIFY(QTest::qWaitForWindowActive(testWidget)); QVERIFY(testWidget->hasFocus()); @@ -3985,7 +3979,6 @@ void tst_QLineEdit::taskQTBUG_4401_enterKeyClearsPassword() testWidget->selectAll(); centerOnScreen(testWidget); testWidget->show(); - QApplication::setActiveWindow(testWidget); QVERIFY(QTest::qWaitForWindowActive(testWidget)); QTest::keyPress(testWidget, Qt::Key_Enter); @@ -4041,7 +4034,8 @@ void tst_QLineEdit::taskQTBUG_7902_contextMenuCrash() w->connect(&ti, SIGNAL(timeout()), w, SLOT(deleteLater())); ti.start(200); - QContextMenuEvent *cme = new QContextMenuEvent(QContextMenuEvent::Mouse, w->rect().center()); + QContextMenuEvent *cme = new QContextMenuEvent(QContextMenuEvent::Mouse, w->rect().center(), + w->mapToGlobal(w->rect().center())); qApp->postEvent(w, cme); QTest::qWait(300); @@ -4067,13 +4061,12 @@ void tst_QLineEdit::taskQTBUG_7395_readOnlyShortcut() le.show(); QVERIFY(QTest::qWaitForWindowExposed(&le)); - QApplication::setActiveWindow(&le); QVERIFY(QTest::qWaitForWindowActive(&le)); le.setFocus(); QTRY_VERIFY(le.hasFocus()); QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_P); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } void tst_QLineEdit::QTBUG697_paletteCurrentColorGroup() @@ -4089,7 +4082,6 @@ void tst_QLineEdit::QTBUG697_paletteCurrentColorGroup() le.setPalette(p); le.show(); - QApplication::setActiveWindow(&le); QVERIFY(QTest::qWaitForWindowActive(&le)); le.setFocus(); QTRY_VERIFY(le.hasFocus()); @@ -4117,7 +4109,7 @@ void tst_QLineEdit::QTBUG13520_textNotVisible() QString sometext("01-ST16-01SIL-MPL001wfgsdfgsdgsdfgsdfgsdfgsdfgsdfg"); le.setText(sometext); le.setCursorPosition(0); - QTest::qWait(100); //just make sure we get he lineedit correcly painted + QTest::qWait(100); //just make sure we get he lineedit correctly painted auto expectedCursorCoordinate = le.width() - le.fontMetrics().horizontalAdvance(sometext); // cursor does not leave widget to the left: @@ -4360,14 +4352,14 @@ void tst_QLineEdit::inputMethodSelection() testWidget->setSelection(0,0); QSignalSpy selectionSpy(testWidget, SIGNAL(selectionChanged())); - QCOMPARE(selectionSpy.count(), 0); + QCOMPARE(selectionSpy.size(), 0); QCOMPARE(testWidget->selectionStart(), -1); QCOMPARE(testWidget->selectionEnd(), -1); QCOMPARE(testWidget->selectionLength(), 0); testWidget->setSelection(0,5); - QCOMPARE(selectionSpy.count(), 1); + QCOMPARE(selectionSpy.size(), 1); QCOMPARE(testWidget->selectionStart(), 0); QCOMPARE(testWidget->selectionEnd(), 5); QCOMPARE(testWidget->selectionLength(), 5); @@ -4381,7 +4373,7 @@ void tst_QLineEdit::inputMethodSelection() QApplication::sendEvent(testWidget, &event); } - QCOMPARE(selectionSpy.count(), 2); + QCOMPARE(selectionSpy.size(), 2); QCOMPARE(testWidget->selectionStart(), 12); QCOMPARE(testWidget->selectionEnd(), 17); QCOMPARE(testWidget->selectionLength(), 5); @@ -4394,7 +4386,7 @@ void tst_QLineEdit::inputMethodSelection() QApplication::sendEvent(testWidget, &event); } - QCOMPARE(selectionSpy.count(), 3); + QCOMPARE(selectionSpy.size(), 3); QCOMPARE(testWidget->selectionStart(), -1); QCOMPARE(testWidget->selectionEnd(), -1); QCOMPARE(testWidget->selectionLength(), 0); @@ -4421,6 +4413,33 @@ void tst_QLineEdit::inputMethodQueryImHints() QCOMPARE(static_cast<Qt::InputMethodHints>(value.toInt()), hints); } +void tst_QLineEdit::inputMethodQueryEnterKeyType() +{ + QWidget mw; + QVBoxLayout layout(&mw); + QLineEdit le1(&mw); + layout.addWidget(&le1); + mw.show(); + QVariant enterType = le1.inputMethodQuery(Qt::ImEnterKeyType); + QCOMPARE(enterType.value<Qt::EnterKeyType>(), Qt::EnterKeyDefault); + + mw.hide(); + QLineEdit le2(&mw); + layout.addWidget(&le2); + mw.show(); + + enterType = le1.inputMethodQuery(Qt::ImEnterKeyType); +#ifdef Q_OS_ANDROID + // QTBUG-61652 + // EnterKey is changed to EnterKeyNext if the focus can be moved to widget below + QCOMPARE(enterType.value<Qt::EnterKeyType>(), Qt::EnterKeyNext); +#else + QCOMPARE(enterType.value<Qt::EnterKeyType>(), Qt::EnterKeyDefault); +#endif + enterType = le2.inputMethodQuery(Qt::ImEnterKeyType); + QCOMPARE(enterType.value<Qt::EnterKeyType>(), Qt::EnterKeyDefault); +} + void tst_QLineEdit::inputMethodUpdate() { if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) @@ -4557,7 +4576,6 @@ void tst_QLineEdit::clearButton() l->addWidget(listView); testWidget.move(300, 300); testWidget.show(); - qApp->setActiveWindow(&testWidget); QVERIFY(QTest::qWaitForWindowActive(&testWidget)); // Flip the clear button on,off, trying to detect crashes. filterLineEdit->setClearButtonEnabled(true); @@ -4582,11 +4600,11 @@ void tst_QLineEdit::clearButton() QSignalSpy spyEdited(filterLineEdit, &QLineEdit::textEdited); const QPoint clearButtonCenterPos = QRect(QPoint(0, 0), clearButton->size()).center(); QTest::mouseClick(clearButton, Qt::LeftButton, {}, clearButtonCenterPos); - QCOMPARE(spyEdited.count(), 1); + QCOMPARE(spyEdited.size(), 1); QTRY_COMPARE(clearButton->cursor().shape(), filterLineEdit->cursor().shape()); QTRY_COMPARE(filterModel->rowCount(), 3); QCoreApplication::processEvents(); - QCOMPARE(spyEdited.count(), 1); + QCOMPARE(spyEdited.size(), 1); filterLineEdit->setReadOnly(true); // QTBUG-34315 QVERIFY(!clearButton->isEnabled()); @@ -4668,7 +4686,8 @@ void tst_QLineEdit::sideWidgets() testWidget.move(300, 300); testWidget.show(); QVERIFY(QTest::qWaitForWindowExposed(&testWidget)); - foreach (QToolButton *button, lineEdit->findChildren<QToolButton *>()) + const auto buttons = lineEdit->findChildren<QToolButton *>(); + for (QToolButton *button : buttons) QCOMPARE(button->cursor().shape(), Qt::ArrowCursor); // Arbitrarily add/remove actions, trying to detect crashes. Add QTRY_VERIFY(false) to view the result. delete label3Action; @@ -4683,7 +4702,8 @@ void tst_QLineEdit::sideWidgets() template <class T> T *findAssociatedWidget(const QAction *a) { - foreach (QWidget *w, a->associatedWidgets()) { + const auto associatedObjects = a->associatedObjects(); + for (QObject *w : associatedObjects) { if (T *result = qobject_cast<T *>(w)) return result; } @@ -4737,6 +4757,71 @@ void tst_QLineEdit::sideWidgetsActionEvents() QCOMPARE(toolButton2->x(), toolButton1X); } +/*! + Verify that side widgets are positioned correctly and result in + correct effective text margins. +*/ +void tst_QLineEdit::sideWidgetsEffectiveMargins() +{ +#ifndef QT_BUILD_INTERNAL + QSKIP("This test requires a developer build."); +#else + QLineEdit edit; + edit.setPlaceholderText("placeholder"); + edit.setClearButtonEnabled(true); + edit.show(); + QLineEditPrivate *priv = QLineEditPrivate::get(&edit); + const auto sideWidgetParameters = priv->sideWidgetParameters(); + const int sideWidgetWidth = sideWidgetParameters.widgetWidth + sideWidgetParameters.margin; + QVERIFY(QTest::qWaitForWindowExposed(&edit)); + + QCOMPARE(priv->effectiveTextMargins().left(), 0); + QCOMPARE(priv->effectiveTextMargins().right(), 0); + + edit.setText("Left to right"); // clear button fades in on the right + QCOMPARE(priv->effectiveTextMargins().left(), 0); + QCOMPARE(priv->effectiveTextMargins().right(), sideWidgetWidth); + edit.clear(); + QCOMPARE(priv->effectiveTextMargins().left(), 0); + QCOMPARE(priv->effectiveTextMargins().right(), 0); + + edit.setLayoutDirection(Qt::RightToLeft); + edit.setText("ئۇيغۇر تىلى"); // clear button fades in on the left + QCOMPARE(priv->effectiveTextMargins().left(), sideWidgetWidth); + QCOMPARE(priv->effectiveTextMargins().right(), 0); + edit.clear(); + QCOMPARE(priv->effectiveTextMargins().left(), 0); + QCOMPARE(priv->effectiveTextMargins().right(), 0); + + edit.setLayoutDirection(Qt::LeftToRight); + + const QIcon leftIcon = edit.style()->standardIcon(QStyle::SP_FileIcon); + const QIcon rightIcon = edit.style()->standardIcon(QStyle::SP_DirIcon); + edit.addAction(leftIcon, QLineEdit::ActionPosition::LeadingPosition); + QCOMPARE(priv->effectiveTextMargins().left(), sideWidgetWidth); + QCOMPARE(priv->effectiveTextMargins().right(), 0); + + edit.addAction(rightIcon, QLineEdit::ActionPosition::TrailingPosition); + QCOMPARE(priv->effectiveTextMargins().left(), sideWidgetWidth); + QCOMPARE(priv->effectiveTextMargins().right(), sideWidgetWidth); + + edit.setText("Left to right"); // clear button on the right + QCOMPARE(priv->effectiveTextMargins().left(), sideWidgetWidth); + QCOMPARE(priv->effectiveTextMargins().right(), 2 * sideWidgetWidth); + edit.clear(); + QCOMPARE(priv->effectiveTextMargins().left(), sideWidgetWidth); + QCOMPARE(priv->effectiveTextMargins().right(), sideWidgetWidth); + + edit.setLayoutDirection(Qt::RightToLeft); + edit.setText("ئۇيغۇر تىلى"); // clear button fades in on the left + QCOMPARE(priv->effectiveTextMargins().left(), 2 * sideWidgetWidth); + QCOMPARE(priv->effectiveTextMargins().right(), sideWidgetWidth); + edit.clear(); + QCOMPARE(priv->effectiveTextMargins().left(), sideWidgetWidth); + QCOMPARE(priv->effectiveTextMargins().right(), sideWidgetWidth); +#endif +} + Q_DECLARE_METATYPE(Qt::AlignmentFlag) void tst_QLineEdit::shouldShowPlaceholderText_data() { @@ -4798,7 +4883,7 @@ void tst_QLineEdit::QTBUG1266_setInputMaskEmittingTextEdited() QSignalSpy spy(&lineEdit, SIGNAL(textEdited(QString))); lineEdit.setInputMask("AAAA"); lineEdit.setInputMask(QString()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } #if QT_CONFIG(shortcut) @@ -4859,7 +4944,7 @@ void tst_QLineEdit::shortcutOverrideOnReadonlyLineEdit() } const int activationCount = shouldBeHandledByQLineEdit ? 0 : 1; - QCOMPARE(spy.count(), activationCount); + QCOMPARE(spy.size(), activationCount); } #endif // QT_CONFIG(shortcut) @@ -4892,7 +4977,7 @@ void tst_QLineEdit::QTBUG59957_clearButtonLeftmostAction() bool tst_QLineEdit::unselectingWithLeftOrRightChangesCursorPosition() { -#if defined Q_OS_WIN || defined Q_OS_QNX //Windows and QNX do not jump to the beginning of the selection +#if defined Q_OS_WIN || defined Q_OS_QNX || defined Q_OS_VXWORKS //Windows, QNX and VxWorks do not jump to the beginning of the selection return true; #endif // Platforms minimal/offscreen also need left after unselecting with right @@ -4912,10 +4997,10 @@ void tst_QLineEdit::QTBUG_60319_setInputMaskCheckImSurroundingText() QLineEdit *testWidget = ensureTestWidget(); QString mask("+000(000)-000-00-00"); testWidget->setInputMask(mask); - testWidget->setCursorPosition(mask.length()); + testWidget->setCursorPosition(mask.size()); QString surroundingText = testWidget->inputMethodQuery(Qt::ImSurroundingText).toString(); int cursorPosition = testWidget->inputMethodQuery(Qt::ImCursorPosition).toInt(); - QCOMPARE(surroundingText.length(), cursorPosition); + QCOMPARE(surroundingText.size(), cursorPosition); } void tst_QLineEdit::testQuickSelectionWithMouse() @@ -5012,18 +5097,18 @@ void tst_QLineEdit::inputRejected() QSignalSpy spyInputRejected(testWidget, SIGNAL(inputRejected())); QTest::keyClicks(testWidget, "abcde"); - QCOMPARE(spyInputRejected.count(), 0); + QCOMPARE(spyInputRejected.size(), 0); testWidget->setText("fghij"); - QCOMPARE(spyInputRejected.count(), 0); + QCOMPARE(spyInputRejected.size(), 0); testWidget->insert("k"); - QCOMPARE(spyInputRejected.count(), 0); + QCOMPARE(spyInputRejected.size(), 0); testWidget->clear(); testWidget->setMaxLength(5); QTest::keyClicks(testWidget, "abcde"); - QCOMPARE(spyInputRejected.count(), 0); + QCOMPARE(spyInputRejected.size(), 0); QTest::keyClicks(testWidget, "fgh"); - QCOMPARE(spyInputRejected.count(), 3); + QCOMPARE(spyInputRejected.size(), 3); #if QT_CONFIG(clipboard) testWidget->clear(); spyInputRejected.clear(); @@ -5031,7 +5116,7 @@ void tst_QLineEdit::inputRejected() testWidget->paste(); // The first 5 characters are accepted, but // the last 2 are not. - QCOMPARE(spyInputRejected.count(), 1); + QCOMPARE(spyInputRejected.size(), 1); #endif testWidget->setMaxLength(INT_MAX); @@ -5040,15 +5125,15 @@ void tst_QLineEdit::inputRejected() QIntValidator intValidator(1, 100); testWidget->setValidator(&intValidator); QTest::keyClicks(testWidget, "11"); - QCOMPARE(spyInputRejected.count(), 0); + QCOMPARE(spyInputRejected.size(), 0); QTest::keyClicks(testWidget, "a#"); - QCOMPARE(spyInputRejected.count(), 2); + QCOMPARE(spyInputRejected.size(), 2); #if QT_CONFIG(clipboard) testWidget->clear(); spyInputRejected.clear(); QApplication::clipboard()->setText("a#"); testWidget->paste(); - QCOMPARE(spyInputRejected.count(), 1); + QCOMPARE(spyInputRejected.size(), 1); #endif testWidget->clear(); @@ -5056,10 +5141,154 @@ void tst_QLineEdit::inputRejected() spyInputRejected.clear(); testWidget->setInputMask("999.999.999.999;_"); QTest::keyClicks(testWidget, "11"); - QCOMPARE(spyInputRejected.count(), 0); + QCOMPARE(spyInputRejected.size(), 0); QTest::keyClicks(testWidget, "a#"); - QCOMPARE(spyInputRejected.count(), 2); + QCOMPARE(spyInputRejected.size(), 2); +} + +void tst_QLineEdit::keyReleasePropagates() +{ + struct Dialog : QWidget + { + QLineEdit *lineEdit; + int releasedKey = {}; + + Dialog() + { + lineEdit = new QLineEdit; + QHBoxLayout *hbox = new QHBoxLayout; + + hbox->addWidget(lineEdit); + setLayout(hbox); + } + + protected: + void keyReleaseEvent(QKeyEvent *e) override + { + releasedKey = e->key(); + } + } dialog; + + dialog.show(); + QVERIFY(QTest::qWaitForWindowExposed(&dialog)); + + QTest::keyPress(dialog.lineEdit, Qt::Key_A); + QTest::keyRelease(dialog.lineEdit, Qt::Key_A); + + QCOMPARE(dialog.releasedKey, Qt::Key_A); + + QTest::keyPress(dialog.lineEdit, Qt::Key_Alt); + QTest::keyRelease(dialog.lineEdit, Qt::Key_Alt); + + QCOMPARE(dialog.releasedKey, Qt::Key_Alt); } +#if QT_CONFIG(shortcut) + +void tst_QLineEdit::deleteWordByKeySequence_data() +{ + QTest::addColumn<QString>("startText"); + QTest::addColumn<int>("selectionStart"); + QTest::addColumn<int>("selectionEnd"); + QTest::addColumn<int>("cursorPosition"); + QTest::addColumn<QKeySequence::StandardKey>("key"); + QTest::addColumn<QString>("expectedText"); + QTest::addColumn<int>("expectedCursorPosition"); + + QTest::newRow("Delete start, no selection") + << QStringLiteral("Some Text") << 0 << 0 << 9 << QKeySequence::DeleteStartOfWord + << QStringLiteral("Some ") << 5; + QTest::newRow("Delete end, no selection") + << QStringLiteral("Some Text") << 0 << 0 << 5 << QKeySequence::DeleteEndOfWord + << QStringLiteral("Some ") << 5; + QTest::newRow("Delete start from middle, no selection") + << QStringLiteral("Some Text") << 0 << 0 << 7 << QKeySequence::DeleteStartOfWord + << QStringLiteral("Some xt") << 5; + QTest::newRow("Delete end from middle, no selection") + << QStringLiteral("Some Text") << 0 << 0 << 7 << QKeySequence::DeleteEndOfWord + << QStringLiteral("Some Te") << 7; + QTest::newRow("Delete end from first, no selection") + << QStringLiteral("Some Text") << 0 << 0 << 0 << QKeySequence::DeleteEndOfWord + << QStringLiteral("Text") << 0; + + QTest::newRow("Delete start, full selection") + << QStringLiteral("Some Text") << 0 << 9 << 0 << QKeySequence::DeleteStartOfWord + << QStringLiteral("") << 0; + QTest::newRow("Delete end, full selection") + << QStringLiteral("Some Text") << 0 << 9 << 0 << QKeySequence::DeleteEndOfWord + << QStringLiteral("") << 0; + QTest::newRow("Delete start, full selection, single word") + << QStringLiteral("Some") << 0 << 4 << 0 << QKeySequence::DeleteStartOfWord + << QStringLiteral("") << 0; + QTest::newRow("Delete end, full selection, single word") + << QStringLiteral("Some") << 0 << 4 << 0 << QKeySequence::DeleteEndOfWord + << QStringLiteral("") << 0; + + QTest::newRow("Delete start, word selection") + << QStringLiteral("Some Text") << 5 << 9 << 0 << QKeySequence::DeleteStartOfWord + << QStringLiteral("Some ") << 5; + QTest::newRow("Delete end, word selection") + << QStringLiteral("Some Text") << 5 << 9 << 0 << QKeySequence::DeleteEndOfWord + << QStringLiteral("Some ") << 5; + QTest::newRow("Delete start, partial word selection") + << QStringLiteral("Some Text") << 5 << 7 << 0 << QKeySequence::DeleteStartOfWord + << QStringLiteral("Some xt") << 5; + QTest::newRow("Delete end, partial word selection") + << QStringLiteral("Some Text") << 5 << 7 << 0 << QKeySequence::DeleteEndOfWord + << QStringLiteral("Some xt") << 5; + QTest::newRow("Delete start, partial inner word selection") + << QStringLiteral("Some Text") << 6 << 8 << 0 << QKeySequence::DeleteStartOfWord + << QStringLiteral("Some Tt") << 6; + QTest::newRow("Delete end, partial inner word selection") + << QStringLiteral("Some Text") << 6 << 8 << 0 << QKeySequence::DeleteEndOfWord + << QStringLiteral("Some Tt") << 6; + QTest::newRow("Delete start, selection with space") + << QStringLiteral("Some Text") << 3 << 9 << 0 << QKeySequence::DeleteStartOfWord + << QStringLiteral("Som") << 3; + QTest::newRow("Delete end, selection with space") + << QStringLiteral("Some Text") << 3 << 9 << 0 << QKeySequence::DeleteEndOfWord + << QStringLiteral("Som") << 3; + QTest::newRow("Delete start, partial word selection with space") + << QStringLiteral("Some Text") << 3 << 7 << 0 << QKeySequence::DeleteStartOfWord + << QStringLiteral("Somxt") << 3; + QTest::newRow("Delete end, partial selection with space") + << QStringLiteral("Some Text") << 3 << 7 << 0 << QKeySequence::DeleteEndOfWord + << QStringLiteral("Somxt") << 3; +} + +void tst_QLineEdit::deleteWordByKeySequence() +{ + QFETCH(QString, startText); + QFETCH(int, selectionStart); + QFETCH(int, selectionEnd); + QFETCH(int, cursorPosition); + QFETCH(QKeySequence::StandardKey, key); + QFETCH(QString, expectedText); + QFETCH(int, expectedCursorPosition); + + QWidget widget; + + QLineEdit *lineEdit = new QLineEdit(startText, &widget); + lineEdit->setFocus(); + lineEdit->setCursorPosition(cursorPosition); + if (selectionStart != selectionEnd) + lineEdit->setSelection(selectionStart, selectionEnd - selectionStart); + + widget.show(); + + QVERIFY(QTest::qWaitForWindowActive(&widget)); + + QTestEventList keys; + addKeySequenceStandardKey(keys, key); + keys.simulate(lineEdit); + + QCOMPARE(lineEdit->text(), expectedText); + QCOMPARE(lineEdit->selectionStart(), -1); + QCOMPARE(lineEdit->selectionEnd(), -1); + QCOMPARE(lineEdit->cursorPosition(), expectedCursorPosition); +} + +#endif // QT_CONFIG(shortcut) + QTEST_MAIN(tst_QLineEdit) #include "tst_qlineedit.moc" diff --git a/tests/auto/widgets/widgets/qmainwindow/BLACKLIST b/tests/auto/widgets/widgets/qmainwindow/BLACKLIST new file mode 100644 index 0000000000..28b08026d8 --- /dev/null +++ b/tests/auto/widgets/widgets/qmainwindow/BLACKLIST @@ -0,0 +1,2 @@ +[QTBUG52175_tabifiedDockWidgetActivated] +macos arm ci diff --git a/tests/auto/widgets/widgets/qmainwindow/CMakeLists.txt b/tests/auto/widgets/widgets/qmainwindow/CMakeLists.txt index 85ce87f764..93f94f78c7 100644 --- a/tests/auto/widgets/widgets/qmainwindow/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qmainwindow/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qmainwindow.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qmainwindow Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmainwindow LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qmainwindow SOURCES tst_qmainwindow.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets Qt::WidgetsPrivate diff --git a/tests/auto/widgets/widgets/qmainwindow/tst_qmainwindow.cpp b/tests/auto/widgets/widgets/qmainwindow/tst_qmainwindow.cpp index b31acb2b7a..093af90d1c 100644 --- a/tests/auto/widgets/widgets/qmainwindow/tst_qmainwindow.cpp +++ b/tests/auto/widgets/widgets/qmainwindow/tst_qmainwindow.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -68,13 +43,13 @@ public: } void timerEvent(QTimerEvent*) override { - QCoreApplication::postEvent(m_tb, new QMouseEvent(QEvent::MouseButtonPress, QPoint(6, 7), Qt::LeftButton, {}, {})); - QCoreApplication::postEvent(m_tb, new QMouseEvent(QEvent::MouseMove, QPoint(7, 8), Qt::LeftButton, Qt::LeftButton, {})); - QCoreApplication::postEvent(m_tb, new QMouseEvent(QEvent::MouseMove, QPoint(27, 23), Qt::LeftButton, Qt::LeftButton, {})); - QCoreApplication::postEvent(m_tb, new QMouseEvent(QEvent::MouseMove, QPoint(30, 27), Qt::LeftButton, Qt::LeftButton, {})); - QCoreApplication::postEvent(m_tb, new QMouseEvent(QEvent::MouseMove, QPoint(162, 109), Qt::LeftButton, Qt::LeftButton, {})); - QCoreApplication::postEvent(m_tb, new QMouseEvent(QEvent::MouseMove, QPoint(10, 4), Qt::LeftButton, Qt::LeftButton, {})); - QCoreApplication::postEvent(m_tb, new QMouseEvent(QEvent::MouseButtonRelease, QPoint(9, 4), Qt::LeftButton, {}, {})); + QCoreApplication::postEvent(m_tb, new QMouseEvent(QEvent::MouseButtonPress, QPoint(6, 7), m_tb->mapToGlobal(QPoint(6, 7)), Qt::LeftButton, {}, {})); + QCoreApplication::postEvent(m_tb, new QMouseEvent(QEvent::MouseMove, QPoint(7, 8), m_tb->mapToGlobal(QPoint(7, 8)), Qt::LeftButton, Qt::LeftButton, {})); + QCoreApplication::postEvent(m_tb, new QMouseEvent(QEvent::MouseMove, QPoint(27, 23), m_tb->mapToGlobal(QPoint(27, 23)), Qt::LeftButton, Qt::LeftButton, {})); + QCoreApplication::postEvent(m_tb, new QMouseEvent(QEvent::MouseMove, QPoint(30, 27), m_tb->mapToGlobal(QPoint(30, 27)), Qt::LeftButton, Qt::LeftButton, {})); + QCoreApplication::postEvent(m_tb, new QMouseEvent(QEvent::MouseMove, QPoint(162, 109), m_tb->mapToGlobal(QPoint(162, 109)), Qt::LeftButton, Qt::LeftButton, {})); + QCoreApplication::postEvent(m_tb, new QMouseEvent(QEvent::MouseMove, QPoint(10, 4), m_tb->mapToGlobal(QPoint(10, 4)), Qt::LeftButton, Qt::LeftButton, {})); + QCoreApplication::postEvent(m_tb, new QMouseEvent(QEvent::MouseButtonRelease, QPoint(9, 4), m_tb->mapToGlobal(QPoint(6, 7)), Qt::LeftButton, {}, {})); } }; @@ -92,8 +67,8 @@ public: void timerEvent(QTimerEvent*) override { - QCoreApplication::postEvent(m_w, new QMouseEvent(QEvent::MouseButtonPress, QPoint(230, 370), Qt::LeftButton, {}, {})); - QCoreApplication::postEvent(m_w, new QMouseEvent(QEvent::MouseButtonRelease, QPoint(230, 370), Qt::LeftButton, {}, {})); + QCoreApplication::postEvent(m_w, new QMouseEvent(QEvent::MouseButtonPress, QPoint(230, 370), m_w->mapToGlobal(QPoint(230, 370)), Qt::LeftButton, {}, {})); + QCoreApplication::postEvent(m_w, new QMouseEvent(QEvent::MouseButtonRelease, QPoint(230, 370), m_w->mapToGlobal(QPoint(230, 370)), Qt::LeftButton, {}, {})); } }; @@ -111,6 +86,7 @@ private slots: void iconSize(); void toolButtonStyle(); void menuBar(); + void customMenuBar(); void centralWidget(); void takeCentralWidget(); void corner(); @@ -126,6 +102,9 @@ private slots: void dockWidgetArea(); void restoreState(); void restoreStateFromPreviousVersion(); + void restoreStateSizeChanged_data(); + void restoreStateSizeChanged(); + void restoreAndModify(); void createPopupMenu(); void hideBeforeLayout(); #ifdef QT_BUILD_INTERNAL @@ -672,6 +651,18 @@ void tst_QMainWindow::menuBar() } } +// QTBUG-98247 +void tst_QMainWindow::customMenuBar() +{ + QMainWindow w; + std::unique_ptr<QWidget> menuWidget(new QWidget); + w.setMenuWidget(menuWidget.get()); + QVERIFY(menuWidget->parentWidget()); + QVERIFY(w.menuBar()); // implicitly calls setMenuBar + QVERIFY(!menuWidget->parentWidget()); + menuWidget.reset(); +} + #ifdef QT_BUILD_INTERNAL void tst_QMainWindow::statusBar() { @@ -1350,25 +1341,31 @@ void tst_QMainWindow::restoreState() dw.setObjectName(QLatin1String("dock")); mw.addDockWidget(Qt::LeftDockWidgetArea, &dw); + QWidgetPrivate *tbp = QWidgetPrivate::get(&tb); + QVERIFY(tbp->widgetItem); + QByteArray state; state = mw.saveState(); QVERIFY(mw.restoreState(state)); + QVERIFY(tbp->widgetItem); state = mw.saveState(1); QVERIFY(!mw.restoreState(state)); QVERIFY(mw.restoreState(state, 1)); + QVERIFY(tbp->widgetItem); } //tests the restoration of the previous versions of window settings void tst_QMainWindow::restoreStateFromPreviousVersion() { - QList<QByteArray> restoreData; - restoreData << QByteArray((char*)restoreData41, sizeof(restoreData41)) - << QByteArray((char*)restoreData42, sizeof(restoreData42)) - << QByteArray((char*)restoreData43, sizeof(restoreData43)); + const QByteArray restoreData[] = { + QByteArray((char*)restoreData41, sizeof(restoreData41)), + QByteArray((char*)restoreData42, sizeof(restoreData42)), + QByteArray((char*)restoreData43, sizeof(restoreData43)), + }; - foreach(QByteArray ba, restoreData) { + for (const QByteArray &ba : restoreData) { QMainWindow win; win.setCentralWidget(new QTextEdit); @@ -1391,6 +1388,158 @@ void tst_QMainWindow::restoreStateFromPreviousVersion() } +void tst_QMainWindow::restoreStateSizeChanged_data() +{ + QTest::addColumn<Qt::WindowState>("saveState"); + QTest::addColumn<Qt::WindowState>("showState"); + QTest::addColumn<bool>("sameSize"); + + QTest::addRow("fullscreen") << Qt::WindowFullScreen << Qt::WindowFullScreen << true; + QTest::addRow("maximized") << Qt::WindowMaximized << Qt::WindowMaximized << true; + QTest::addRow("maximized->normal") << Qt::WindowMaximized << Qt::WindowNoState << false; + QTest::addRow("fullscreen->normal") << Qt::WindowFullScreen << Qt::WindowNoState << false; + QTest::addRow("fullscreen->maximized") << Qt::WindowFullScreen << Qt::WindowMaximized << false; + QTest::addRow("maximized->fullscreen") << Qt::WindowMaximized << Qt::WindowFullScreen << true; +} + +void tst_QMainWindow::restoreStateSizeChanged() +{ + QFETCH(Qt::WindowState, saveState); + QFETCH(Qt::WindowState, showState); + QFETCH(bool, sameSize); + + auto createMainWindow = []{ + QMainWindow *mainWindow = new QMainWindow; + mainWindow->move(QGuiApplication::primaryScreen()->availableGeometry().topLeft()); + mainWindow->setCentralWidget(new QLabel("X")); + QDockWidget *dockWidget = new QDockWidget; + dockWidget->setObjectName("Dock Widget"); + mainWindow->addDockWidget(Qt::LeftDockWidgetArea, dockWidget); + return mainWindow; + }; + + QByteArray geometryData; + QByteArray stateData; + int dockWidgetWidth = 0; + QRect normalGeometry; + + { + auto mainWindow = QScopedPointer<QMainWindow>(createMainWindow()); + mainWindow->setWindowState(saveState); + mainWindow->show(); + QVERIFY(QTest::qWaitForWindowExposed(mainWindow.data())); + dockWidgetWidth = mainWindow->width() - 100; + QDockWidget *dockWidget = mainWindow->findChild<QDockWidget*>("Dock Widget"); + mainWindow->resizeDocks({dockWidget}, {dockWidgetWidth}, Qt::Horizontal); + geometryData = mainWindow->saveGeometry(); + stateData = mainWindow->saveState(); + normalGeometry = mainWindow->normalGeometry(); + } + + auto mainWindow = QScopedPointer<QMainWindow>(createMainWindow()); + mainWindow->restoreGeometry(geometryData); + QElapsedTimer timer; + timer.start(); + mainWindow->restoreState(stateData); + mainWindow->setWindowState(showState); + mainWindow->show(); + QVERIFY(QTest::qWaitForWindowExposed(mainWindow.data())); + + QDockWidget *dockWidget = mainWindow->findChild<QDockWidget*>("Dock Widget"); + QVERIFY(dockWidget); + QCOMPARE(mainWindow->normalGeometry().size(), normalGeometry.size()); + if (sameSize) { + // The implementation discards the restored state 150ms after a resize + // event. If it takes too long to get here, then the test cannot pass anymore + // and we want to XFAIL rather then skip it with some information that might + // help us adjust the timeout in QMainWindowLayout. + bool expectFail = false; + const auto waitForLastResize = [&]() -> bool { + if (dockWidget->width() == dockWidgetWidth) + return true; + if (timer.elapsed() > 150) { + QMainWindowLayout *l = mainWindow->findChild<QMainWindowLayout *>(); + Q_ASSERT(l); + if (!l->restoredState) { + qWarning("Restored state discarded after %lld", timer.elapsed()); + expectFail = true; + return true; + } + } + return false; + }; + QTRY_VERIFY_WITH_TIMEOUT(waitForLastResize(), 500); + if (expectFail) { + QEXPECT_FAIL("fullscreen", "Restored state probably discarded too early", Continue); + QEXPECT_FAIL("maximized->fullscreen", "Restored state probably discarded too early", Continue); + } + QCOMPARE(dockWidget->width(), dockWidgetWidth); + } +} + +/*! + If a main window's state is restored but also modified, then we + might have to forget the restored state to avoid dangling pointers. + See comment in QMainWindowLayout::applyRestoredState() and QTBUG-120025. +*/ +void tst_QMainWindow::restoreAndModify() +{ + class MainWindow : public QMainWindow + { + public: + MainWindow() + { + setCentralWidget(new QTextEdit); + + customers = new QDockWidget(tr("Customers"), this); + customers->setObjectName("Customers"); + customers->setAllowedAreas(Qt::LeftDockWidgetArea | + Qt::RightDockWidgetArea); + customers->setWidget(new QTextEdit); + addDockWidget(Qt::RightDockWidgetArea, customers); + + paragraphs = new QDockWidget(tr("Paragraphs"), this); + paragraphs->setObjectName("Paragraphs"); + paragraphs->setWidget(new QTextEdit); + addDockWidget(Qt::RightDockWidgetArea, paragraphs); + } + + void restore() + { + if (!savedGeometry.isEmpty()) + restoreGeometry(savedGeometry); + setWindowState(Qt::WindowMaximized); + if (!savedState.isEmpty()) + restoreState(savedState); + + tabifyDockWidget(customers, paragraphs); + } + protected: + void closeEvent(QCloseEvent *event) override + { + savedGeometry = saveGeometry(); + savedState = saveState(); + + return QMainWindow::closeEvent(event); + } + private: + QByteArray savedGeometry; + QByteArray savedState; + + QDockWidget *customers; + QDockWidget *paragraphs; + + } mainWindow; + + mainWindow.restore(); + mainWindow.show(); + QVERIFY(QTest::qWaitForWindowExposed(&mainWindow)); + mainWindow.close(); + + mainWindow.restore(); + mainWindow.show(); + QVERIFY(QTest::qWaitForWindowExposed(&mainWindow)); +} void tst_QMainWindow::createPopupMenu() { @@ -1588,25 +1737,25 @@ void MoveSeparator::apply(QMainWindow *mw) const QMap<QString, QRect> dockWidgetGeometries(QMainWindow *mw) { QMap<QString, QRect> result; - QList<QDockWidget*> dockWidgets = mw->findChildren<QDockWidget*>(); - foreach (QDockWidget *dw, dockWidgets) + const QList<QDockWidget*> dockWidgets = mw->findChildren<QDockWidget*>(); + for (QDockWidget *dw : dockWidgets) result.insert(dw->objectName(), dw->geometry()); return result; } #define COMPARE_DOCK_WIDGET_GEOS(_oldGeos, _newGeos) \ { \ - QMap<QString, QRect> __oldGeos = _oldGeos; \ - QMap<QString, QRect> __newGeos = _newGeos; \ - QCOMPARE(__newGeos.keys(), __oldGeos.keys()); \ - QStringList __keys = __newGeos.keys(); \ - foreach (const QString &key, __keys) { \ - QRect __r1 = __oldGeos[key]; \ - QRect __r2 = __newGeos[key]; \ - if (__r1 != __r2) \ - qWarning() << key << __r1 << __r2; \ + QMap<QString, QRect> _v_oldGeos = _oldGeos; \ + QMap<QString, QRect> _v_newGeos = _newGeos; \ + QCOMPARE(_v_newGeos.keys(), _v_oldGeos.keys()); \ + const QStringList _v_keys = _v_newGeos.keys(); \ + for (const QString &key : _v_keys) { \ + QRect _v_r1 = _v_oldGeos[key]; \ + QRect _v_r2 = _v_newGeos[key]; \ + if (_v_r1 != _v_r2) \ + qWarning() << key << _v_r1 << _v_r2; \ } \ - QCOMPARE(__newGeos, __oldGeos); \ + QCOMPARE(_v_newGeos, _v_oldGeos); \ } #ifdef QT_BUILD_INTERNAL @@ -1652,8 +1801,8 @@ void tst_QMainWindow::saveRestore_data() #ifdef QT_BUILD_INTERNAL void tst_QMainWindow::saveRestore() { - QFETCH(AddList, addList); - QFETCH(MoveList, moveList); + QFETCH(const AddList, addList); + QFETCH(const MoveList, moveList); QByteArray stateData; QMap<QString, QRect> dockWidgetGeos; @@ -1665,12 +1814,12 @@ void tst_QMainWindow::saveRestore() QTextEdit centralWidget("The rain in Spain falls mainly on the plains"); mainWindow.setCentralWidget(¢ralWidget); - foreach (const AddDockWidget &adw, addList) + for (const AddDockWidget &adw : addList) adw.apply(&mainWindow); mainWindow.show(); - foreach (const MoveSeparator &ms, moveList) + for (const MoveSeparator &ms : moveList) ms.apply(&mainWindow); dockWidgetGeos = dockWidgetGeometries(&mainWindow); @@ -1688,10 +1837,11 @@ void tst_QMainWindow::saveRestore() QTextEdit centralWidget("The rain in Spain falls mainly on the plains"); mainWindow.setCentralWidget(¢ralWidget); - foreach (const AddDockWidget &adw, addList) + for (const AddDockWidget &adw : addList) adw.apply(&mainWindow); mainWindow.show(); + mainWindow.restoreState(stateData); COMPARE_DOCK_WIDGET_GEOS(dockWidgetGeos, dockWidgetGeometries(&mainWindow)); @@ -1704,12 +1854,13 @@ void tst_QMainWindow::saveRestore() QTextEdit centralWidget("The rain in Spain falls mainly on the plains"); mainWindow.setCentralWidget(¢ralWidget); - foreach (const AddDockWidget &adw, addList) + for (const AddDockWidget &adw : addList) adw.apply(&mainWindow); mainWindow.resize(size); mainWindow.restoreState(stateData); mainWindow.show(); + QVERIFY(QTest::qWaitForWindowExposed(&mainWindow)); COMPARE_DOCK_WIDGET_GEOS(dockWidgetGeos, dockWidgetGeometries(&mainWindow)); } } @@ -1754,11 +1905,11 @@ void tst_QMainWindow::setCursor() QVERIFY(QTest::qWaitForWindowActive(&mw)); QCOMPARE(cur.shape(), mw.cursor().shape()); - QHoverEvent enterE(QEvent::HoverEnter, QPoint(10,10), QPoint()); + QHoverEvent enterE(QEvent::HoverEnter, QPoint(10,10), QPoint(), QPoint()); mw.event(&enterE); QCOMPARE(cur.shape(), mw.cursor().shape()); - QHoverEvent leaveE(QEvent::HoverLeave, QPoint(), QPoint()); + QHoverEvent leaveE(QEvent::HoverLeave, QPoint(), QPoint(), QPoint()); mw.event(&leaveE); QCOMPARE(cur.shape(), mw.cursor().shape()); } @@ -1779,7 +1930,7 @@ void tst_QMainWindow::addToolbarAfterShow() void tst_QMainWindow::centralWidgetSize() { if (qGuiApp->styleHints()->showIsFullScreen()) - QSKIP("The platform is auto maximizing, so the test makes no sense");; + QSKIP("The platform is auto maximizing, so the test makes no sense"); QMainWindow mainWindow; mainWindow.menuBar()->addMenu("menu"); @@ -2011,10 +2162,11 @@ void tst_QMainWindow::resizeDocks() mw.setDockNestingEnabled(true); mw.resize(1800, 600); - foreach (const AddDockWidget &i, addList) + for (const AddDockWidget &i : std::as_const(addList)) i.apply(&mw); - foreach (QDockWidget *dw, mw.findChildren<QDockWidget *>()) + const auto dockWidgets = mw.findChildren<QDockWidget *>(); + for (QDockWidget *dw : dockWidgets) dw->setStyleSheet( "* { background-color: " + dw->objectName() +" }"); mw.setCentralWidget(new QTextEdit); @@ -2023,11 +2175,11 @@ void tst_QMainWindow::resizeDocks() QVERIFY(QTest::qWaitForWindowExposed(&mw)); QFETCH(Qt::Orientation, orientation); - QFETCH(QStringList, docks); + QFETCH(const QStringList, docks); QFETCH(QList<int>, sizes); QList<QDockWidget *> list; - foreach (const QString &name, docks) { + for (const QString &name : docks) { QDockWidget *d = mw.findChild<QDockWidget *>(name); QVERIFY(d); list << d; @@ -2039,14 +2191,14 @@ void tst_QMainWindow::resizeDocks() int totalFromList = 0; int actualTotal = 0; - for (int i = 0; i < docks.count(); ++i) { + for (int i = 0; i < docks.size(); ++i) { totalFromList += sizes[i]; QSize s = list[i]->size(); actualTotal += (orientation == Qt::Horizontal) ? s.width() : s.height(); // qDebug() << list[i] << list[i]->size() << sizes[i]; } - for (int i = 0; i < docks.count(); ++i) { + for (int i = 0; i < docks.size(); ++i) { QSize s = list[i]->size(); int value = (orientation == Qt::Horizontal) ? s.width() : s.height(); QCOMPARE(value, qRound(sizes[i]*actualTotal/double(totalFromList))); diff --git a/tests/auto/widgets/widgets/qmdiarea/BLACKLIST b/tests/auto/widgets/widgets/qmdiarea/BLACKLIST index 3091b73269..c3234bf56c 100644 --- a/tests/auto/widgets/widgets/qmdiarea/BLACKLIST +++ b/tests/auto/widgets/widgets/qmdiarea/BLACKLIST @@ -1,15 +1,7 @@ [tileSubWindows] -ubuntu-16.04 -rhel-7.6 centos opensuse-leap macos -ubuntu-18.04 -ubuntu-20.04 -macos -rhel-7.4 -macos -opensuse-42.3 [resizeTimer] macos diff --git a/tests/auto/widgets/widgets/qmdiarea/CMakeLists.txt b/tests/auto/widgets/widgets/qmdiarea/CMakeLists.txt index 9586d69352..5f61dc8664 100644 --- a/tests/auto/widgets/widgets/qmdiarea/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qmdiarea/CMakeLists.txt @@ -1,32 +1,38 @@ -# Generated from qmdiarea.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qmdiarea Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmdiarea LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qmdiarea SOURCES tst_qmdiarea.cpp DEFINES QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII - INCLUDE_DIRECTORIES - . - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::GuiPrivate Qt::Widgets + Qt::WidgetsPrivate ) ## Scopes: ##################################################################### qt_internal_extend_target(tst_qmdiarea CONDITION TARGET Qt::OpenGL - PUBLIC_LIBRARIES + LIBRARIES Qt::OpenGL ) qt_internal_extend_target(tst_qmdiarea CONDITION APPLE - PUBLIC_LIBRARIES + LIBRARIES ${FWSecurity} ) diff --git a/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp b/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp index 6df7c74bdc..ef00dcaac0 100644 --- a/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp +++ b/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -49,6 +24,8 @@ #endif #include <QStyleHints> +#include <QtWidgets/private/qapplication_p.h> + static const Qt::WindowFlags DefaultWindowFlags = Qt::SubWindow | Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint; @@ -163,7 +140,7 @@ static bool verifyArrangement(QMdiArea *mdiArea, Arrangement arrangement, const case Tiled: { // Calculate the number of rows and columns. - const int n = subWindows.count(); + const int n = subWindows.size(); const int numColumns = qMax(qCeil(qSqrt(qreal(n))), 1); const int numRows = qMax((n % numColumns) ? (n / numColumns + 1) : (n / numColumns), 1); @@ -199,7 +176,7 @@ static bool verifyArrangement(QMdiArea *mdiArea, Arrangement arrangement, const // QWidget::childAt with the position of the first one and subsequently adding // dx and dy. QPoint subWindowPos(20, 5); - foreach (int expectedIndex, expectedIndices) { + for (int expectedIndex : expectedIndices) { QMdiSubWindow *expected = subWindows.at(expectedIndex); expected->raise(); if (mdiArea->viewport()->childAt(subWindowPos) != expected) @@ -210,7 +187,7 @@ static bool verifyArrangement(QMdiArea *mdiArea, Arrangement arrangement, const } // Restore stacking order. - foreach (QMdiSubWindow *subWindow, activationOrderList) { + for (QMdiSubWindow *subWindow : activationOrderList) { mdiArea->setActiveSubWindow(subWindow); qApp->processEvents(); } @@ -281,6 +258,12 @@ private slots: void nativeSubWindows(); void task_209615(); void task_236750(); + void qtbug92240_title_data(); + void qtbug92240_title(); + void tabbedview_singleSubWindow(); + void tabbedview_activefirst(); + void tabbedview_activesecond(); + void tabbedview_activethird(); private: QMdiSubWindow *activeWindow; @@ -324,7 +307,7 @@ void tst_QMdiArea::subWindowActivated() QSignalSpy spy(workspace, SIGNAL(subWindowActivated(QMdiSubWindow*))); connect( workspace, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(activeChanged(QMdiSubWindow*))); mw.show(); - qApp->setActiveWindow(&mw); + QApplicationPrivate::setActiveWindow(&mw); QFETCH( int, count ); int i; @@ -337,12 +320,12 @@ void tst_QMdiArea::subWindowActivated() widget->show(); qApp->processEvents(); QVERIFY( activeWindow == workspace->activeSubWindow() ); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); } QList<QMdiSubWindow *> windows = workspace->subWindowList(); - QCOMPARE( (int)windows.count(), count ); + QCOMPARE( (int)windows.size(), count ); for ( i = 0; i < count; ++i ) { QMdiSubWindow *window = windows.at(i); @@ -366,13 +349,13 @@ void tst_QMdiArea::subWindowActivated() workspace->activeSubWindow()->close(); qApp->processEvents(); QCOMPARE(activeWindow, workspace->activeSubWindow()); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); } QVERIFY(!activeWindow); QVERIFY(!workspace->activeSubWindow()); - QCOMPARE(workspace->subWindowList().count(), 0); + QCOMPARE(workspace->subWindowList().size(), 0); { workspace->hide(); @@ -380,14 +363,14 @@ void tst_QMdiArea::subWindowActivated() widget->setAttribute(Qt::WA_DeleteOnClose); QMdiSubWindow *window = workspace->addSubWindow(widget); widget->show(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); workspace->show(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); QVERIFY( activeWindow == window ); window->close(); qApp->processEvents(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); QVERIFY( activeWindow == 0 ); } @@ -399,15 +382,15 @@ void tst_QMdiArea::subWindowActivated() QMdiSubWindow *window = workspace->addSubWindow(widget); widget->showMaximized(); qApp->sendPostedEvents(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); spy.clear(); workspace->show(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); QVERIFY( activeWindow == window ); window->close(); qApp->processEvents(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); QVERIFY( activeWindow == 0 ); } @@ -417,13 +400,13 @@ void tst_QMdiArea::subWindowActivated() widget->setAttribute(Qt::WA_DeleteOnClose); QMdiSubWindow *window = workspace->addSubWindow(widget); widget->showMinimized(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); QVERIFY( activeWindow == window ); QCOMPARE(workspace->activeSubWindow(), window); window->close(); qApp->processEvents(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); QVERIFY(!workspace->activeSubWindow()); QVERIFY(!activeWindow); @@ -451,12 +434,12 @@ void tst_QMdiArea::subWindowActivated2() QSignalSpy spy(&mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*))); for (int i = 0; i < 5; ++i) mdiArea.addSubWindow(new QWidget); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); mdiArea.show(); mdiArea.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&mdiArea)); - QTRY_COMPARE(spy.count(), 5); + QTRY_COMPARE(spy.size(), 5); QCOMPARE(mdiArea.activeSubWindow(), mdiArea.subWindowList().back()); spy.clear(); @@ -465,13 +448,13 @@ void tst_QMdiArea::subWindowActivated2() QMdiSubWindow *staysOnTopWindow = mdiArea.subWindowList().at(3); staysOnTopWindow->setWindowFlags(Qt::WindowStaysOnTopHint); mdiArea.setActiveSubWindow(staysOnTopWindow); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(mdiArea.activeSubWindow(), staysOnTopWindow); spy.clear(); QMdiSubWindow *activeSubWindow = mdiArea.subWindowList().at(2); mdiArea.setActiveSubWindow(activeSubWindow); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(mdiArea.activeSubWindow(), activeSubWindow); spy.clear(); @@ -479,7 +462,7 @@ void tst_QMdiArea::subWindowActivated2() // is unchanged after hide/show. mdiArea.hide(); QTest::qWait(100); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QVERIFY(!mdiArea.activeSubWindow()); QCOMPARE(mdiArea.currentSubWindow(), activeSubWindow); spy.clear(); @@ -507,7 +490,7 @@ void tst_QMdiArea::subWindowActivated2() #endif if (!QGuiApplication::platformName().compare(QLatin1String("xcb"), Qt::CaseInsensitive)) QSKIP("QTBUG-25298: Unstable on some X11 window managers"); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QVERIFY(!mdiArea.activeSubWindow()); QCOMPARE(mdiArea.currentSubWindow(), activeSubWindow); spy.clear(); @@ -517,7 +500,7 @@ void tst_QMdiArea::subWindowActivated2() mdiArea.showNormal(); mdiArea.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&mdiArea)); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(mdiArea.activeSubWindow(), activeSubWindow); spy.clear(); } @@ -532,7 +515,6 @@ void tst_QMdiArea::subWindowActivatedWithMinimize() QSignalSpy spy(workspace, SIGNAL(subWindowActivated(QMdiSubWindow*))); connect( workspace, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(activeChanged(QMdiSubWindow*)) ); mw.show(); - qApp->setActiveWindow(&mw); QWidget *widget = new QWidget(workspace); widget->setAttribute(Qt::WA_DeleteOnClose); QMdiSubWindow *window1 = workspace->addSubWindow(widget); @@ -554,7 +536,7 @@ void tst_QMdiArea::subWindowActivatedWithMinimize() QVERIFY(!workspace->activeSubWindow()); QVERIFY(!activeWindow); - QVERIFY( workspace->subWindowList().count() == 0 ); + QVERIFY( workspace->subWindowList().size() == 0 ); } void tst_QMdiArea::showWindows() @@ -677,7 +659,6 @@ void tst_QMdiArea::changeWindowTitle() #endif mw->show(); - qApp->setActiveWindow(mw); #ifdef USE_SHOW mw->showFullScreen(); @@ -833,14 +814,14 @@ void tst_QMdiArea::fixedSize() } QList<QMdiSubWindow *> windows = ws->subWindowList(); - for (i = 0; i < (int)windows.count(); ++i) { + for (i = 0; i < (int)windows.size(); ++i) { QMdiSubWindow *child = windows.at(i); QCOMPARE(child->size(), fixed); } ws->cascadeSubWindows(); ws->resize(800, 800); - for (i = 0; i < (int)windows.count(); ++i) { + for (i = 0; i < (int)windows.size(); ++i) { QMdiSubWindow *child = windows.at(i); QCOMPARE(child->size(), fixed); } @@ -848,13 +829,13 @@ void tst_QMdiArea::fixedSize() ws->tileSubWindows(); ws->resize(800, 800); - for (i = 0; i < (int)windows.count(); ++i) { + for (i = 0; i < (int)windows.size(); ++i) { QMdiSubWindow *child = windows.at(i); QCOMPARE(child->size(), fixed); } ws->resize(500, 500); - for (i = 0; i < (int)windows.count(); ++i) { + for (i = 0; i < (int)windows.size(); ++i) { QMdiSubWindow *child = windows.at(i); delete child; } @@ -922,7 +903,7 @@ void tst_QMdiArea::setActiveSubWindow() QSignalSpy spy(&workspace, SIGNAL(subWindowActivated(QMdiSubWindow*))); connect(&workspace, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(activeChanged(QMdiSubWindow*))); - qApp->setActiveWindow(&workspace); + QApplicationPrivate::setActiveWindow(&workspace); // Activate hidden windows const int windowCount = 10; @@ -933,7 +914,7 @@ void tst_QMdiArea::setActiveSubWindow() QVERIFY(windows[i]->isHidden()); workspace.setActiveSubWindow(windows[i]); } - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QVERIFY(!activeWindow); spy.clear(); @@ -943,7 +924,7 @@ void tst_QMdiArea::setActiveSubWindow() QVERIFY(!windows[i]->isHidden()); workspace.setActiveSubWindow(windows[i]); qApp->processEvents(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(activeWindow, windows[i]); spy.clear(); } @@ -951,7 +932,7 @@ void tst_QMdiArea::setActiveSubWindow() // Deactivate active window QCOMPARE(workspace.activeSubWindow(), windows[windowCount - 1]); workspace.setActiveSubWindow(0); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QVERIFY(!activeWindow); QVERIFY(!workspace.activeSubWindow()); @@ -981,7 +962,6 @@ void tst_QMdiArea::activeSubWindow() mainWindow.addDockWidget(Qt::LeftDockWidgetArea, dockWidget); mainWindow.show(); - qApp->setActiveWindow(&mainWindow); QVERIFY(QTest::qWaitForWindowActive(&mainWindow)); QCOMPARE(mdiArea->activeSubWindow(), subWindow); QCOMPARE(qApp->focusWidget(), (QWidget *)subWindowLineEdit); @@ -1004,15 +984,13 @@ void tst_QMdiArea::activeSubWindow() dummyTopLevel.show(); QVERIFY(QTest::qWaitForWindowExposed(&dummyTopLevel)); - qApp->setActiveWindow(&dummyTopLevel); QCOMPARE(mdiArea->activeSubWindow(), subWindow); - qApp->setActiveWindow(&mainWindow); QCOMPARE(mdiArea->activeSubWindow(), subWindow); //task 202657 dockWidgetLineEdit->setFocus(); - qApp->setActiveWindow(&mainWindow); + QApplicationPrivate::setActiveWindow(&mainWindow); QVERIFY(dockWidgetLineEdit->hasFocus()); } @@ -1025,7 +1003,7 @@ void tst_QMdiArea::currentSubWindow() for (int i = 0; i < 5; ++i) mdiArea.addSubWindow(new QLineEdit)->show(); - qApp->setActiveWindow(&mdiArea); + QApplicationPrivate::setActiveWindow(&mdiArea); QCOMPARE(qApp->activeWindow(), (QWidget *)&mdiArea); // Check that the last added window is the active and the current. @@ -1040,7 +1018,6 @@ void tst_QMdiArea::currentSubWindow() // Move focus to another top-level and check that we still // have an active window. - qApp->setActiveWindow(&dummyTopLevel); QCOMPARE(qApp->activeWindow(), (QWidget *)&dummyTopLevel); QVERIFY(mdiArea.activeSubWindow()); @@ -1053,7 +1030,7 @@ void tst_QMdiArea::currentSubWindow() QCOMPARE(mdiArea.currentSubWindow(), mdiArea.subWindowList().front()); // Activate mdi area and check that active == current. - qApp->setActiveWindow(&mdiArea); + QApplicationPrivate::setActiveWindow(&mdiArea); active = mdiArea.activeSubWindow(); QVERIFY(active); QCOMPARE(mdiArea.activeSubWindow(), mdiArea.subWindowList().front()); @@ -1062,11 +1039,9 @@ void tst_QMdiArea::currentSubWindow() QCOMPARE(mdiArea.activeSubWindow(), active); QCOMPARE(mdiArea.currentSubWindow(), active); - qApp->setActiveWindow(&dummyTopLevel); QVERIFY(mdiArea.activeSubWindow()); QCOMPARE(mdiArea.currentSubWindow(), active); - qApp->setActiveWindow(&mdiArea); active->show(); QCOMPARE(mdiArea.activeSubWindow(), active); @@ -1096,11 +1071,11 @@ void tst_QMdiArea::addAndRemoveWindows() QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); { // addSubWindow with large widget - QCOMPARE(workspace.subWindowList().count(), 0); + QCOMPARE(workspace.subWindowList().size(), 0); QWidget *window = workspace.addSubWindow(new LargeWidget); QVERIFY(window); qApp->processEvents(); - QCOMPARE(workspace.subWindowList().count(), 1); + QCOMPARE(workspace.subWindowList().size(), 1); QCOMPARE(window->windowFlags(), DefaultWindowFlags); QCOMPARE(window->size(), workspace.viewport()->size()); } @@ -1111,7 +1086,7 @@ void tst_QMdiArea::addAndRemoveWindows() workspace.addSubWindow(window); QVERIFY(window); qApp->processEvents(); - QCOMPARE(workspace.subWindowList().count(), 2); + QCOMPARE(workspace.subWindowList().size(), 2); QCOMPARE(window->windowFlags(), DefaultWindowFlags); QCOMPARE(window->size(), window->minimumSize()); } @@ -1123,7 +1098,7 @@ void tst_QMdiArea::addAndRemoveWindows() workspace.addSubWindow(window); QVERIFY(window); qApp->processEvents(); - QCOMPARE(workspace.subWindowList().count(), 3); + QCOMPARE(workspace.subWindowList().size(), 3); QCOMPARE(window->windowFlags(), DefaultWindowFlags); QCOMPARE(window->size(), QSize(1500, 1500)); } @@ -1132,7 +1107,7 @@ void tst_QMdiArea::addAndRemoveWindows() QTest::ignoreMessage(QtWarningMsg, "QMdiArea::addSubWindow: null pointer to widget"); QWidget *window = workspace.addSubWindow(0); QVERIFY(!window); - QCOMPARE(workspace.subWindowList().count(), 3); + QCOMPARE(workspace.subWindowList().size(), 3); } { // addChildWindow @@ -1141,7 +1116,7 @@ void tst_QMdiArea::addAndRemoveWindows() qApp->processEvents(); QCOMPARE(window->windowFlags(), DefaultWindowFlags); window->setWidget(new QWidget); - QCOMPARE(workspace.subWindowList().count(), 4); + QCOMPARE(workspace.subWindowList().size(), 4); QTest::ignoreMessage(QtWarningMsg, "QMdiArea::addSubWindow: window is already added"); workspace.addSubWindow(window); } @@ -1149,15 +1124,16 @@ void tst_QMdiArea::addAndRemoveWindows() { // addChildWindow with 0 pointer QTest::ignoreMessage(QtWarningMsg, "QMdiArea::addSubWindow: null pointer to widget"); workspace.addSubWindow(0); - QCOMPARE(workspace.subWindowList().count(), 4); + QCOMPARE(workspace.subWindowList().size(), 4); } // removeSubWindow - foreach (QWidget *window, workspace.subWindowList()) { + const auto subWindows = workspace.subWindowList(); + for (QWidget *window : subWindows) { workspace.removeSubWindow(window); delete window; } - QCOMPARE(workspace.subWindowList().count(), 0); + QCOMPARE(workspace.subWindowList().size(), 0); // removeSubWindow with 0 pointer QTest::ignoreMessage(QtWarningMsg, "QMdiArea::removeSubWindow: null pointer to widget"); @@ -1165,7 +1141,7 @@ void tst_QMdiArea::addAndRemoveWindows() workspace.addSubWindow(new QPushButton(QLatin1String("Dummy to make workspace non-empty"))); qApp->processEvents(); - QCOMPARE(workspace.subWindowList().count(), 1); + QCOMPARE(workspace.subWindowList().size(), 1); // removeSubWindow with window not inside workspace QTest::ignoreMessage(QtWarningMsg,"QMdiArea::removeSubWindow: window is not inside workspace"); @@ -1207,20 +1183,20 @@ void tst_QMdiArea::addAndRemoveWindowsWithReparenting() // 0 because the window list contains widgets and not actual // windows. Silly, but that's the behavior. - QCOMPARE(workspace.subWindowList().count(), 0); + QCOMPARE(workspace.subWindowList().size(), 0); window.setWidget(new QWidget); qApp->processEvents(); - QCOMPARE(workspace.subWindowList().count(), 1); + QCOMPARE(workspace.subWindowList().size(), 1); window.setParent(0); // Will also reset window flags - QCOMPARE(workspace.subWindowList().count(), 0); + QCOMPARE(workspace.subWindowList().size(), 0); window.setParent(&workspace); - QCOMPARE(workspace.subWindowList().count(), 1); + QCOMPARE(workspace.subWindowList().size(), 1); QCOMPARE(window.windowFlags(), DefaultWindowFlags); QTest::ignoreMessage(QtWarningMsg, "QMdiArea::addSubWindow: window is already added"); workspace.addSubWindow(&window); - QCOMPARE(workspace.subWindowList().count(), 1); + QCOMPARE(workspace.subWindowList().size(), 1); } class MySubWindow : public QMdiSubWindow @@ -1271,23 +1247,22 @@ void tst_QMdiArea::closeWindows() { QMdiArea workspace; workspace.show(); - qApp->setActiveWindow(&workspace); // Close widget QWidget *widget = new QWidget; QMdiSubWindow *subWindow = workspace.addSubWindow(widget); qApp->processEvents(); - QCOMPARE(workspace.subWindowList().count(), 1); + QCOMPARE(workspace.subWindowList().size(), 1); subWindow->close(); - QCOMPARE(workspace.subWindowList().count(), 0); + QCOMPARE(workspace.subWindowList().size(), 0); // Close window QWidget *window = workspace.addSubWindow(new QWidget); qApp->processEvents(); - QCOMPARE(workspace.subWindowList().count(), 1); + QCOMPARE(workspace.subWindowList().size(), 1); window->close(); qApp->processEvents(); - QCOMPARE(workspace.subWindowList().count(), 0); + QCOMPARE(workspace.subWindowList().size(), 0); const int windowCount = 10; @@ -1295,7 +1270,7 @@ void tst_QMdiArea::closeWindows() for (int i = 0; i < windowCount; ++i) workspace.addSubWindow(new QWidget)->show(); qApp->processEvents(); - QCOMPARE(workspace.subWindowList().count(), windowCount); + QCOMPARE(workspace.subWindowList().size(), windowCount); int activeSubWindowCount = 0; while (workspace.activeSubWindow()) { workspace.activeSubWindow()->close(); @@ -1303,19 +1278,19 @@ void tst_QMdiArea::closeWindows() ++activeSubWindowCount; } QCOMPARE(activeSubWindowCount, windowCount); - QCOMPARE(workspace.subWindowList().count(), 0); + QCOMPARE(workspace.subWindowList().size(), 0); // Close all windows for (int i = 0; i < windowCount; ++i) workspace.addSubWindow(new QWidget)->show(); qApp->processEvents(); - QCOMPARE(workspace.subWindowList().count(), windowCount); + QCOMPARE(workspace.subWindowList().size(), windowCount); QSignalSpy spy(&workspace, SIGNAL(subWindowActivated(QMdiSubWindow*))); connect(&workspace, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(activeChanged(QMdiSubWindow*))); workspace.closeAllSubWindows(); qApp->processEvents(); - QCOMPARE(workspace.subWindowList().count(), 0); - QCOMPARE(spy.count(), 1); + QCOMPARE(workspace.subWindowList().size(), 0); + QCOMPARE(spy.size(), 1); QVERIFY(!activeWindow); } @@ -1323,7 +1298,6 @@ void tst_QMdiArea::activateNextAndPreviousWindow() { QMdiArea workspace; workspace.show(); - qApp->setActiveWindow(&workspace); const int windowCount = 10; QMdiSubWindow *windows[windowCount]; @@ -1341,7 +1315,7 @@ void tst_QMdiArea::activateNextAndPreviousWindow() workspace.activateNextSubWindow(); qApp->processEvents(); QCOMPARE(workspace.activeSubWindow(), windows[i]); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); } QVERIFY(activeWindow); @@ -1353,7 +1327,7 @@ void tst_QMdiArea::activateNextAndPreviousWindow() workspace.activatePreviousSubWindow(); qApp->processEvents(); QCOMPARE(workspace.activeSubWindow(), windows[i]); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); if (i % 2 == 0) windows[i]->hide(); // 10, 8, 6, 4, 2, 0 @@ -1365,7 +1339,7 @@ void tst_QMdiArea::activateNextAndPreviousWindow() // activateNextSubWindow with every 2nd window hidden for (int i = 0; i < windowCount / 2; ++i) { workspace.activateNextSubWindow(); // 1, 3, 5, 7, 9 - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); } QCOMPARE(workspace.activeSubWindow(), windows[windowCount - 1]); @@ -1373,7 +1347,7 @@ void tst_QMdiArea::activateNextAndPreviousWindow() // activatePreviousSubWindow with every 2nd window hidden for (int i = 0; i < windowCount / 2; ++i) { workspace.activatePreviousSubWindow(); // 7, 5, 3, 1, 9 - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); } QCOMPARE(workspace.activeSubWindow(), windows[windowCount - 1]); @@ -1407,7 +1381,6 @@ void tst_QMdiArea::subWindowList() QMdiArea workspace; workspace.show(); - qApp->setActiveWindow(&workspace); QVERIFY(QTest::qWaitForWindowActive(&workspace)); QList<QMdiSubWindow *> activationOrder; @@ -1420,8 +1393,8 @@ void tst_QMdiArea::subWindowList() { QList<QMdiSubWindow *> widgets = workspace.subWindowList(windowOrder); - QCOMPARE(widgets.count(), windowCount); - for (int i = 0; i < widgets.count(); ++i) + QCOMPARE(widgets.size(), windowCount); + for (int i = 0; i < widgets.size(); ++i) QCOMPARE(widgets.at(i), windows[i]); } @@ -1440,9 +1413,9 @@ void tst_QMdiArea::subWindowList() } if (windowOrder == QMdiArea::StackingOrder) { - QCOMPARE(subWindows.at(subWindows.count() - 1), windows[staysOnTop1]); - QCOMPARE(subWindows.at(subWindows.count() - 2), windows[activeSubWindow]); - QCOMPARE(subWindows.count(), windowCount); + QCOMPARE(subWindows.at(subWindows.size() - 1), windows[staysOnTop1]); + QCOMPARE(subWindows.at(subWindows.size() - 2), windows[activeSubWindow]); + QCOMPARE(subWindows.size(), windowCount); } else { // ActivationHistoryOrder QCOMPARE(subWindows, activationOrder); } @@ -1457,11 +1430,11 @@ void tst_QMdiArea::subWindowList() activationOrder.move(activationOrder.indexOf(windows[activeSubWindow]), windowCount - 1); QList<QMdiSubWindow *> widgets = workspace.subWindowList(windowOrder); - QCOMPARE(widgets.count(), windowCount); + QCOMPARE(widgets.size(), windowCount); if (windowOrder == QMdiArea::StackingOrder) { - QCOMPARE(widgets.at(widgets.count() - 1), windows[staysOnTop2]); - QCOMPARE(widgets.at(widgets.count() - 2), windows[staysOnTop1]); - QCOMPARE(widgets.at(widgets.count() - 3), windows[activeSubWindow]); + QCOMPARE(widgets.at(widgets.size() - 1), windows[staysOnTop2]); + QCOMPARE(widgets.at(widgets.size() - 2), windows[staysOnTop1]); + QCOMPARE(widgets.at(widgets.size() - 3), windows[activeSubWindow]); } else { // ActivationHistory QCOMPARE(widgets, activationOrder); } @@ -1471,8 +1444,8 @@ void tst_QMdiArea::subWindowList() widgets = workspace.subWindowList(windowOrder); if (windowOrder == QMdiArea::StackingOrder) { - QCOMPARE(widgets.at(widgets.count() - 1), windows[activeSubWindow]); - QCOMPARE(widgets.at(widgets.count() - 2), windows[staysOnTop1]); + QCOMPARE(widgets.at(widgets.size() - 1), windows[activeSubWindow]); + QCOMPARE(widgets.at(widgets.size() - 2), windows[staysOnTop1]); QCOMPARE(widgets.at(0), windows[staysOnTop2]); } else { // ActivationHistoryOrder QCOMPARE(widgets, activationOrder); @@ -1483,9 +1456,9 @@ void tst_QMdiArea::subWindowList() widgets = workspace.subWindowList(windowOrder); if (windowOrder == QMdiArea::StackingOrder) { - QCOMPARE(widgets.at(widgets.count() - 1), windows[staysOnTop2]); - QCOMPARE(widgets.at(widgets.count() - 2), windows[staysOnTop1]); - QCOMPARE(widgets.at(widgets.count() - 3), windows[activeSubWindow]); + QCOMPARE(widgets.at(widgets.size() - 1), windows[staysOnTop2]); + QCOMPARE(widgets.at(widgets.size() - 2), windows[staysOnTop1]); + QCOMPARE(widgets.at(widgets.size() - 3), windows[activeSubWindow]); } else { // ActivationHistoryOrder QCOMPARE(widgets, activationOrder); } @@ -1495,9 +1468,9 @@ void tst_QMdiArea::subWindowList() widgets = workspace.subWindowList(windowOrder); if (windowOrder == QMdiArea::StackingOrder) { - QCOMPARE(widgets.at(widgets.count() - 1), windows[staysOnTop1]); - QCOMPARE(widgets.at(widgets.count() - 2), windows[staysOnTop2]); - QCOMPARE(widgets.at(widgets.count() - 3), windows[activeSubWindow]); + QCOMPARE(widgets.at(widgets.size() - 1), windows[staysOnTop1]); + QCOMPARE(widgets.at(widgets.size() - 2), windows[staysOnTop2]); + QCOMPARE(widgets.at(widgets.size() - 3), windows[activeSubWindow]); } else { // ActivationHistoryOrder QCOMPARE(widgets, activationOrder); } @@ -1538,14 +1511,14 @@ void tst_QMdiArea::setViewport() qApp->processEvents(); QList<QMdiSubWindow *> windowsBeforeViewportChange = workspace.subWindowList(); - QCOMPARE(windowsBeforeViewportChange.count(), windowCount); + QCOMPARE(windowsBeforeViewportChange.size(), windowCount); workspace.setViewport(new QWidget); qApp->processEvents(); QVERIFY(workspace.viewport() != firstViewport); QList<QMdiSubWindow *> windowsAfterViewportChange = workspace.subWindowList(); - QCOMPARE(windowsAfterViewportChange.count(), windowCount); + QCOMPARE(windowsAfterViewportChange.size(), windowCount); QCOMPARE(windowsAfterViewportChange, windowsBeforeViewportChange); // for (int i = 0; i < windowCount; ++i) { @@ -1561,7 +1534,7 @@ void tst_QMdiArea::setViewport() delete workspace.viewport(); qApp->processEvents(); - QCOMPARE(workspace.subWindowList().count(), 0); + QCOMPARE(workspace.subWindowList().size(), 0); QVERIFY(!workspace.activeSubWindow()); } @@ -1703,7 +1676,8 @@ void tst_QMdiArea::tileSubWindows() QTRY_COMPARE(workspace.size(), QSize(350, 150)); const QSize minSize(600, 130); - foreach (QMdiSubWindow *subWindow, workspace.subWindowList()) + const auto subWindows = workspace.subWindowList(); + for (QMdiSubWindow *subWindow : subWindows) subWindow->setMinimumSize(minSize); QCOMPARE(workspace.size(), QSize(350, 150)); @@ -1801,7 +1775,7 @@ void tst_QMdiArea::cascadeAndTileSubWindows() #endif QCOMPARE(windows.at(2)->geometry().top() - windows.at(1)->geometry().top(), dy); - for (int i = 0; i < windows.count(); ++i) { + for (int i = 0; i < windows.size(); ++i) { QMdiSubWindow *window = windows.at(i); if (i % 3 == 0) { QVERIFY(window->isMinimized()); @@ -1861,7 +1835,7 @@ void tst_QMdiArea::resizeMaximizedChildWindows() int newSize = startSize + increment * windowCount; QCOMPARE(workspaceSize, QSize(newSize, newSize)); - foreach (QWidget *window, windows) + for (QWidget *window : std::as_const(windows)) QCOMPARE(window->rect(), workspace.contentsRect()); } @@ -1886,7 +1860,7 @@ void tst_QMdiArea::focusWidgetAfterAddSubWindow() mdiArea.show(); view->show(); - qApp->setActiveWindow(&mdiArea); + QApplicationPrivate::setActiveWindow(&mdiArea); QCOMPARE(qApp->focusWidget(), static_cast<QWidget *>(lineEdit2)); } @@ -1895,7 +1869,6 @@ void tst_QMdiArea::dontMaximizeSubWindowOnActivation() QMdiArea mdiArea; mdiArea.show(); QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); - qApp->setActiveWindow(&mdiArea); // Add one maximized window. mdiArea.addSubWindow(new QWidget)->showMaximized(); @@ -1906,16 +1879,13 @@ void tst_QMdiArea::dontMaximizeSubWindowOnActivation() for (int i = 0; i < 5; ++i) { QMdiSubWindow *window = mdiArea.addSubWindow(new QWidget); window->show(); -#if defined Q_OS_QNX - QEXPECT_FAIL("", "QTBUG-38231", Abort); -#endif QVERIFY(window->isMaximized()); qApp->processEvents(); } // Verify that activated windows still are maximized on activation. - QList<QMdiSubWindow *> subWindows = mdiArea.subWindowList(); - for (int i = 0; i < subWindows.count(); ++i) { + const QList<QMdiSubWindow *> subWindows = mdiArea.subWindowList(); + for (int i = 0; i < subWindows.size(); ++i) { mdiArea.activateNextSubWindow(); QMdiSubWindow *window = subWindows.at(i); QCOMPARE(mdiArea.activeSubWindow(), window); @@ -1926,7 +1896,7 @@ void tst_QMdiArea::dontMaximizeSubWindowOnActivation() // Restore active window and verify that other windows aren't // maximized on activation. mdiArea.activeSubWindow()->showNormal(); - for (int i = 0; i < subWindows.count(); ++i) { + for (int i = 0; i < subWindows.size(); ++i) { mdiArea.activateNextSubWindow(); QMdiSubWindow *window = subWindows.at(i); QCOMPARE(mdiArea.activeSubWindow(), window); @@ -1940,7 +1910,7 @@ void tst_QMdiArea::dontMaximizeSubWindowOnActivation() int indexOfMaximized = subWindows.indexOf(mdiArea.activeSubWindow()); // Verify that windows are not maximized on activation. - for (int i = 0; i < subWindows.count(); ++i) { + for (int i = 0; i < subWindows.size(); ++i) { mdiArea.activateNextSubWindow(); QMdiSubWindow *window = subWindows.at(i); QCOMPARE(mdiArea.activeSubWindow(), window); @@ -1951,7 +1921,7 @@ void tst_QMdiArea::dontMaximizeSubWindowOnActivation() QVERIFY(mdiArea.activeSubWindow()->isMaximized()); // Minimize all windows. - foreach (QMdiSubWindow *window, subWindows) { + for (QMdiSubWindow *window : subWindows) { window->showMinimized(); QVERIFY(window->isMinimized()); qApp->processEvents(); @@ -1962,7 +1932,7 @@ void tst_QMdiArea::dontMaximizeSubWindowOnActivation() mdiArea.activeSubWindow()->showMaximized(); // Verify that minimized windows are maximized on activation. - for (int i = 0; i < subWindows.count(); ++i) { + for (int i = 0; i < subWindows.size(); ++i) { mdiArea.activateNextSubWindow(); QMdiSubWindow *window = subWindows.at(i); QCOMPARE(mdiArea.activeSubWindow(), window); @@ -1972,7 +1942,7 @@ void tst_QMdiArea::dontMaximizeSubWindowOnActivation() // Verify that activated windows are maximized after closing // the active window - for (int i = 0; i < subWindows.count(); ++i) { + for (int i = 0; i < subWindows.size(); ++i) { QVERIFY(mdiArea.activeSubWindow()); QVERIFY(mdiArea.activeSubWindow()->isMaximized()); mdiArea.activeSubWindow()->close(); @@ -2224,7 +2194,7 @@ void tst_QMdiArea::setActivationOrder() mdiArea.show(); QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); - for (int i = 0; i < subWindows.count(); ++i) { + for (int i = 0; i < subWindows.size(); ++i) { mdiArea.activateNextSubWindow(); QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(i)); qApp->processEvents(); @@ -2283,12 +2253,11 @@ void tst_QMdiArea::tabBetweenSubWindows() mdiArea.show(); QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); - qApp->setActiveWindow(&mdiArea); QWidget *focusWidget = subWindows.back()->widget(); QCOMPARE(qApp->focusWidget(), focusWidget); QSignalSpy spy(&mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*))); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); // Walk through the entire list of sub windows. #ifdef Q_OS_MAC @@ -2296,7 +2265,7 @@ void tst_QMdiArea::tabBetweenSubWindows() #endif QVERIFY(tabBetweenSubWindowsIn(&mdiArea)); QCOMPARE(mdiArea.activeSubWindow(), subWindows.back()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); mdiArea.setActiveSubWindow(subWindows.front()); QCOMPARE(mdiArea.activeSubWindow(), subWindows.front()); @@ -2305,12 +2274,12 @@ void tst_QMdiArea::tabBetweenSubWindows() // Walk through the entire list of sub windows in the opposite direction (Ctrl-Shift-Tab). QVERIFY(tabBetweenSubWindowsIn(&mdiArea, -1, true)); QCOMPARE(mdiArea.activeSubWindow(), subWindows.front()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); // Ctrl-Tab-Tab-Tab QVERIFY(tabBetweenSubWindowsIn(&mdiArea, 3)); QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(3)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); mdiArea.setActiveSubWindow(subWindows.at(1)); QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(1)); @@ -2319,7 +2288,7 @@ void tst_QMdiArea::tabBetweenSubWindows() // Quick switch (Ctrl-Tab once) -> switch back to the previously active sub-window. QVERIFY(tabBetweenSubWindowsIn(&mdiArea, 1)); QCOMPARE(mdiArea.activeSubWindow(), subWindows.at(3)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } void tst_QMdiArea::setViewMode() @@ -2338,7 +2307,7 @@ void tst_QMdiArea::setViewMode() QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); QMdiSubWindow *activeSubWindow = mdiArea.activeSubWindow(); - QList<QMdiSubWindow *> subWindows = mdiArea.subWindowList(); + const QList<QMdiSubWindow *> subWindows = mdiArea.subWindowList(); // Default. QVERIFY(!activeSubWindow->isMaximized()); @@ -2353,7 +2322,7 @@ void tst_QMdiArea::setViewMode() QVERIFY(tabBar); QVERIFY(tabBar->isVisible()); - QCOMPARE(tabBar->count(), subWindows.count()); + QCOMPARE(tabBar->count(), subWindows.size()); QVERIFY(activeSubWindow->isMaximized()); QCOMPARE(tabBar->currentIndex(), subWindows.indexOf(activeSubWindow)); @@ -2407,13 +2376,12 @@ void tst_QMdiArea::setViewMode() QVERIFY(tabBar->isTabEnabled(tabIndex)); // Remove sub-windows and make sure the tab is removed. - foreach (QMdiSubWindow *subWindow, subWindows) { + for (QMdiSubWindow *subWindow : subWindows) { if (subWindow != activeSubWindow) { mdiArea.removeSubWindow(subWindow); delete subWindow; } } - subWindows.clear(); QCOMPARE(tabBar->count(), 1); // Go back to default (QMdiArea::SubWindowView). @@ -2614,8 +2582,11 @@ void tst_QMdiArea::nativeSubWindows() // No native widgets. QVERIFY(!mdiArea.viewport()->internalWinId()); - foreach (QMdiSubWindow *subWindow, mdiArea.subWindowList()) - QVERIFY(!subWindow->internalWinId()); + { + const auto subWindows = mdiArea.subWindowList(); + for (QMdiSubWindow *subWindow : subWindows) + QVERIFY(!subWindow->internalWinId()); + } QWidget *nativeWidget = new QWidget; QVERIFY(nativeWidget->winId()); // enforce native window. @@ -2624,8 +2595,11 @@ void tst_QMdiArea::nativeSubWindows() // The viewport and all the sub-windows must be native. QVERIFY(mdiArea.viewport()->internalWinId()); - foreach (QMdiSubWindow *subWindow, mdiArea.subWindowList()) - QVERIFY(subWindow->internalWinId()); + { + const auto subWindows = mdiArea.subWindowList(); + for (QMdiSubWindow *subWindow : subWindows) + QVERIFY(subWindow->internalWinId()); + } // Add a non-native widget. This should become native. QMdiSubWindow *subWindow = new QMdiSubWindow; @@ -2646,8 +2620,11 @@ void tst_QMdiArea::nativeSubWindows() // The viewport and all the sub-windows must be native. QVERIFY(mdiArea.viewport()->internalWinId()); - foreach (QMdiSubWindow *subWindow, mdiArea.subWindowList()) - QVERIFY(subWindow->internalWinId()); + { + const auto subWindows = mdiArea.subWindowList(); + for (QMdiSubWindow *subWindow : subWindows) + QVERIFY(subWindow->internalWinId()); + } } { // Make a sub-window native *after* it's added to the area. @@ -2663,9 +2640,12 @@ void tst_QMdiArea::nativeSubWindows() // All the sub-windows should be native at this point QVERIFY(mdiArea.viewport()->internalWinId()); - foreach (QMdiSubWindow *subWindow, mdiArea.subWindowList()) + { + const auto subWindows = mdiArea.subWindowList(); + for (QMdiSubWindow *subWindow : subWindows) QVERIFY(subWindow->internalWinId()); } + } } void tst_QMdiArea::task_209615() @@ -2697,6 +2677,114 @@ void tst_QMdiArea::task_236750() subWindow->showMinimized(); } +// QTBUG-92240: When subwindows are maximized, their title is supposed to +// appear on the main window. When DontMaximizeSubWindowOnActivation was set, +// titles of previously created maximized windows interfered, resulting in +// "QTBUG-92240 - [1] - [2]". +void tst_QMdiArea::qtbug92240_title_data() +{ + QTest::addColumn<bool>("dontMaximize"); + QTest::newRow("default") << false; + QTest::newRow("dontMaximize") << true; +} + +void tst_QMdiArea::qtbug92240_title() +{ + QFETCH(bool, dontMaximize); + +#ifdef Q_OS_MACOS + QSKIP("Not supported on macOS"); +#endif + + QMainWindow w; + const QString title = QStringLiteral("QTBUG-92240"); + w.setWindowTitle(title); + w.menuBar()->addMenu(QStringLiteral("File")); + w.show(); + + auto *mdiArea = new QMdiArea; + w.setCentralWidget(mdiArea); + if (dontMaximize) + mdiArea->setOption(QMdiArea::DontMaximizeSubWindowOnActivation); + auto *sw1 = mdiArea->addSubWindow(new QWidget); + sw1->setWindowTitle(QStringLiteral("1")); + sw1->showMaximized(); + QTRY_COMPARE(w.windowTitle(), QLatin1String("QTBUG-92240 - [1]")); + auto *sw2 = mdiArea->addSubWindow(new QWidget); + sw2->setWindowTitle(QStringLiteral("2")); + sw2->showMaximized(); + QTRY_COMPARE(w.windowTitle(), QLatin1String("QTBUG-92240 - [2]")); +} + +void tst_QMdiArea::tabbedview_singleSubWindow() +{ + // With only one sub-window, setViewMode() before addSubWindow(); and addSubWindow() + // before show(), ensure the sub-window is properly activated. + QMdiArea mdiArea; + mdiArea.setViewMode(QMdiArea::TabbedView); + auto *w = new QWidget(&mdiArea); + mdiArea.addSubWindow(w); + mdiArea.show(); + QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); + auto *sub = mdiArea.subWindowList().at(0); + QCOMPARE(mdiArea.activeSubWindow(), sub); + QVERIFY(sub->isMaximized()); +} + +static void setupMdiAreaWithTabbedView(QMdiArea &mdiArea) +{ + mdiArea.setViewMode(QMdiArea::TabbedView); + + auto *mdiWin1 = new QWidget(&mdiArea); + mdiWin1->setWindowTitle(QLatin1String("Sub1")); + mdiArea.addSubWindow(mdiWin1); + + auto *mdiWin2 = new QWidget(&mdiArea); + mdiWin2->setWindowTitle(QLatin1String("Sub2")); + mdiArea.addSubWindow(mdiWin2); + + auto *mdiWin3 = new QWidget(&mdiArea); + mdiWin3->setWindowTitle(QLatin1String("Sub3")); + mdiArea.addSubWindow(mdiWin3); +} + +void tst_QMdiArea::tabbedview_activefirst() +{ + QMdiArea mdiArea; + setupMdiAreaWithTabbedView(mdiArea); + + auto sub0 = mdiArea.subWindowList().at(0); + mdiArea.setActiveSubWindow(sub0); + mdiArea.show(); + QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); + QCOMPARE(mdiArea.activeSubWindow(), sub0); +} + +void tst_QMdiArea::tabbedview_activesecond() +{ + QMdiArea mdiArea; + setupMdiAreaWithTabbedView(mdiArea); + + auto sub1 = mdiArea.subWindowList().at(1); + mdiArea.setActiveSubWindow(sub1); + mdiArea.show(); + QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); + QCOMPARE(mdiArea.activeSubWindow(), sub1); +} + +void tst_QMdiArea::tabbedview_activethird() +{ + QMdiArea mdiArea; + setupMdiAreaWithTabbedView(mdiArea); + + auto sub2 = mdiArea.subWindowList().at(2); + mdiArea.setActiveSubWindow(sub2); + mdiArea.show(); + QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); + QCOMPARE(mdiArea.activeSubWindow(), sub2); +} + + QTEST_MAIN(tst_QMdiArea) #include "tst_qmdiarea.moc" diff --git a/tests/auto/widgets/widgets/qmdisubwindow/CMakeLists.txt b/tests/auto/widgets/widgets/qmdisubwindow/CMakeLists.txt index 2c72f7d4e1..45b7b74ac4 100644 --- a/tests/auto/widgets/widgets/qmdisubwindow/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qmdisubwindow/CMakeLists.txt @@ -1,18 +1,23 @@ -# Generated from qmdisubwindow.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qmdisubwindow Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmdisubwindow LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qmdisubwindow SOURCES tst_qmdisubwindow.cpp DEFINES QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII - INCLUDE_DIRECTORIES - . - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets Qt::WidgetsPrivate diff --git a/tests/auto/widgets/widgets/qmdisubwindow/tst_qmdisubwindow.cpp b/tests/auto/widgets/widgets/qmdisubwindow/tst_qmdisubwindow.cpp index a4283de59e..60b675b18a 100644 --- a/tests/auto/widgets/widgets/qmdisubwindow/tst_qmdisubwindow.cpp +++ b/tests/auto/widgets/widgets/qmdisubwindow/tst_qmdisubwindow.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qmdisubwindow.h" #include "private/qmdisubwindow_p.h" @@ -45,10 +20,13 @@ #include <QStyleOptionTitleBar> #include <QPushButton> #include <QScreen> +#include <QScrollBar> #include <QSizeGrip> #include <QSignalSpy> #include <QList> +#include <QtWidgets/private/qapplication_p.h> + QT_BEGIN_NAMESPACE extern bool qt_tab_all_widgets(); QT_END_NAMESPACE @@ -221,6 +199,7 @@ private slots: void styleChange(); void testFullScreenState(); void testRemoveBaseWidget(); + void testRespectMinimumSize(); }; void tst_QMdiSubWindow::initTestCase() @@ -405,7 +384,6 @@ void tst_QMdiSubWindow::mainWindowSupport() mainWindow.setCentralWidget(workspace); mainWindow.show(); mainWindow.menuBar()->setVisible(true); - QApplication::setActiveWindow(&mainWindow); bool nativeMenuBar = mainWindow.menuBar()->isNativeMenuBar(); // QMainWindow's window title is empty, so on a platform which does NOT have a native menubar, @@ -494,7 +472,7 @@ void tst_QMdiSubWindow::mainWindowSupport() workspace->activateNextSubWindow(); QCoreApplication::processEvents(); - for (QMdiSubWindow *window : qAsConst(windows)) { + for (QMdiSubWindow *window : std::as_const(windows)) { QCOMPARE(workspace->activeSubWindow(), window); QVERIFY(window->isMaximized()); QVERIFY(window->maximizedButtonsWidget()); @@ -531,7 +509,6 @@ void tst_QMdiSubWindow::emittingOfSignals() workspace.setWindowTitle(QLatin1String(QTest::currentTestFunction())); workspace.show(); QCoreApplication::processEvents(); - QApplication::setActiveWindow(&workspace); QMdiSubWindow *window = qobject_cast<QMdiSubWindow *>(workspace.addSubWindow(new QWidget)); QCoreApplication::processEvents(); window->show(); @@ -551,9 +528,9 @@ void tst_QMdiSubWindow::emittingOfSignals() int count = 0; if (signal == SIGNAL(aboutToActivate())) { - count += spy.count(); + count += spy.size(); } else { - for (int i = 0; i < spy.count(); ++i) { + for (int i = 0; i < spy.size(); ++i) { Qt::WindowStates oldState = qvariant_cast<Qt::WindowStates>(spy.at(i).at(0)); Qt::WindowStates newState = qvariant_cast<Qt::WindowStates>(spy.at(i).at(1)); if (watchedState != Qt::WindowNoState) { @@ -576,7 +553,7 @@ void tst_QMdiSubWindow::emittingOfSignals() spy.clear(); triggerSignal(window, &workspace, signal); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); delete window; window = nullptr; @@ -975,6 +952,8 @@ void tst_QMdiSubWindow::mouseDoubleClick() if (!window->style()->styleHint(QStyle::SH_TitleBar_NoBorder, &options, window)) height += window->isMinimized() ? 8 : 4; QPoint mousePosition(window->width() / 2, height - 1); + if (window->style()->inherits("QWindows11Style")) + mousePosition = QPoint(8, height - 1); sendMouseMove(window, mousePosition, Qt::NoButton); // Without Qt::WindowShadeButtonHint flag set @@ -1002,8 +981,10 @@ void tst_QMdiSubWindow::mouseDoubleClick() window->showMinimized(); QVERIFY(window->isMinimized()); + //Process QEvent::WindowStateChange + QCoreApplication::processEvents(); sendMouseDoubleClick(window, mousePosition); - QVERIFY(!window->isMinimized()); + QTRY_VERIFY(!window->isMinimized()); QCOMPARE(window->geometry(), originalGeometry); } @@ -1053,7 +1034,7 @@ void tst_QMdiSubWindow::setSystemMenu() subWindow->setSystemMenu(systemMenu); QCOMPARE(subWindow->systemMenu(), qobject_cast<QMenu *>(systemMenu)); QCOMPARE(subWindow->systemMenu()->parentWidget(), static_cast<QWidget *>(subWindow)); - QCOMPARE(subWindow->systemMenu()->actions().count(), 1); + QCOMPARE(subWindow->systemMenu()->actions().size(), 1); // Show the new system menu QVERIFY(!QApplication::activePopupWidget()); @@ -1157,7 +1138,7 @@ void tst_QMdiSubWindow::restoreFocus() topArea.show(); box->show(); - QApplication::setActiveWindow(&topArea); + QApplicationPrivate::setActiveWindow(&topArea); QMdiSubWindow *expectedFocusWindow = nestedWorkspace->subWindowList().last(); QVERIFY(expectedFocusWindow); QVERIFY(expectedFocusWindow->widget()); @@ -1249,7 +1230,6 @@ void tst_QMdiSubWindow::restoreFocusOverCreation() subWidget1->m_lineEdit2->setFocus(); subWindow1->show(); mdiArea.show(); - QApplication::setActiveWindow(&mdiArea); QVERIFY(QTest::qWaitForWindowActive(&mdiArea)); QCOMPARE(QApplication::focusWidget(), subWidget1->m_lineEdit2); @@ -1277,9 +1257,9 @@ void tst_QMdiSubWindow::changeFocusWithTab() mdiArea.setWindowTitle(QLatin1String(QTest::currentTestFunction())); mdiArea.addSubWindow(widget); mdiArea.show(); - QCOMPARE(mdiArea.subWindowList().count(), 1); + QCOMPARE(mdiArea.subWindowList().size(), 1); - QApplication::setActiveWindow(&mdiArea); + QApplicationPrivate::setActiveWindow(&mdiArea); QCOMPARE(QApplication::focusWidget(), static_cast<QWidget *>(firstLineEdit)); // Next @@ -1359,7 +1339,7 @@ void tst_QMdiSubWindow::closeEvent() QVERIFY(window->close()); QCOMPARE(closeSpy.count(), 3); - QCOMPARE(mdiArea.subWindowList().count(), 0); + QCOMPARE(mdiArea.subWindowList().size(), 0); } // There exists more tests in QMdiArea which covers window title support @@ -1394,7 +1374,7 @@ void tst_QMdiSubWindow::setWindowTitle() // other widgets which are not real top-level widgets). QCOMPARE(window->windowTitle(), expectedWindowTitle); - textEdit->setWindowModified(true);; + textEdit->setWindowModified(true); expectedWindowTitle = QLatin1String("Override child title"); window->setWindowTitle(expectedWindowTitle); QVERIFY(window->isWindowModified()); @@ -1567,9 +1547,6 @@ void tst_QMdiSubWindow::hideAndShow() #if !defined (Q_OS_DARWIN) QVERIFY(menuBar->cornerWidget(Qt::TopRightCorner)); -#if defined Q_OS_QNX - QEXPECT_FAIL("", "QTBUG-38231", Abort); -#endif QVERIFY(subWindow->maximizedButtonsWidget()); QVERIFY(subWindow->maximizedSystemMenuIconWidget()); QCOMPARE(menuBar->cornerWidget(Qt::TopRightCorner), subWindow->maximizedButtonsWidget()); @@ -1800,9 +1777,6 @@ void tst_QMdiSubWindow::replaceMenuBarWhileMaximized() QCoreApplication::processEvents(); -#if defined Q_OS_QNX - QEXPECT_FAIL("", "QTBUG-38231", Abort); -#endif QVERIFY(subWindow->maximizedButtonsWidget()); QVERIFY(subWindow->maximizedSystemMenuIconWidget()); QCOMPARE(menuBar1->cornerWidget(Qt::TopLeftCorner), subWindow->maximizedSystemMenuIconWidget()); @@ -1894,7 +1868,6 @@ void tst_QMdiSubWindow::closeOnDoubleClick() void tst_QMdiSubWindow::setFont() { - QSKIP("This test function is unstable in CI, please see QTBUG-22544"); QMdiArea mdiArea; mdiArea.setWindowTitle(QLatin1String(QTest::currentTestFunction())); QMdiSubWindow *subWindow = mdiArea.addSubWindow(new TestPushButton(QLatin1String("test"))); @@ -1980,7 +1953,6 @@ void tst_QMdiSubWindow::task_182852() mainWindow.setCentralWidget(workspace); mainWindow.show(); mainWindow.menuBar()->setVisible(true); - QApplication::setActiveWindow(&mainWindow); if (mainWindow.menuBar()->isNativeMenuBar()) return; // The main window's title is not overwritten if we have a native menubar (macOS, Unity etc.) @@ -2130,7 +2102,7 @@ void tst_QMdiSubWindow::styleChange() // subWindowActivated should NOT be activated by a style change, // even if internally QMdiSubWindow un-minimizes subwindows temporarily. - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } void tst_QMdiSubWindow::testFullScreenState() @@ -2169,6 +2141,36 @@ void tst_QMdiSubWindow::testRemoveBaseWidget() delete widget1; } +void tst_QMdiSubWindow::testRespectMinimumSize() // QTBUG-100494 +{ + QMdiArea mdiArea; + mdiArea.resize(400, 400); + mdiArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + mdiArea.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + + auto vlay = new QVBoxLayout; + vlay->addWidget(new QPushButton(QLatin1String("btn1-1"))); + vlay->addSpacerItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding)); + vlay->addWidget(new QPushButton(QLatin1String("btn1-2"))); + auto w1 = new QWidget; + w1->setLayout(vlay); + w1->resize(300, 200); + w1->setMinimumSize(200, 150); + auto sw = new QMdiSubWindow; + sw->setWidget(w1); + sw->resize(w1->size()); + mdiArea.addSubWindow(sw); + sw->showMaximized(); + + mdiArea.show(); + QVERIFY(QTest::qWaitForWindowExposed(&mdiArea)); + QVERIFY(!mdiArea.horizontalScrollBar()->isVisible()); + QVERIFY(!mdiArea.verticalScrollBar()->isVisible()); + mdiArea.resize(150, 100); + QTRY_VERIFY(mdiArea.horizontalScrollBar()->isVisible()); + QTRY_VERIFY(mdiArea.verticalScrollBar()->isVisible()); +} + QTEST_MAIN(tst_QMdiSubWindow) #include "tst_qmdisubwindow.moc" diff --git a/tests/auto/widgets/widgets/qmenu/BLACKLIST b/tests/auto/widgets/widgets/qmenu/BLACKLIST index 837c785141..88484ddffa 100644 --- a/tests/auto/widgets/widgets/qmenu/BLACKLIST +++ b/tests/auto/widgets/widgets/qmenu/BLACKLIST @@ -1,10 +1,6 @@ [task258920_mouseBorder] macos -[layoutDirection] -macos # Fails when enabling synchronous expose events QTBUG-62092 -[pushButtonPopulateOnAboutToShow] -macos [tearOff] macos [submenuTearOffDontClose] @@ -20,6 +16,7 @@ android android [pushButtonPopulateOnAboutToShow] android +wayland [QTBUG8122_widgetActionCrashOnClose] android [click_while_dismissing_submenu] @@ -28,3 +25,7 @@ android android [QTBUG_89082_actionTipsHide] macos ci # Can't move cursor (QTBUG-76312) +windows-10 ci +# QTBUG-87424 +[transientParent] +android diff --git a/tests/auto/widgets/widgets/qmenu/CMakeLists.txt b/tests/auto/widgets/widgets/qmenu/CMakeLists.txt index 8e596a6b5b..1716ab85da 100644 --- a/tests/auto/widgets/widgets/qmenu/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qmenu/CMakeLists.txt @@ -1,17 +1,25 @@ -# Generated from qmenu.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qmenu Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmenu LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qmenu SOURCES tst_qmenu.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::GuiPrivate Qt::TestPrivate Qt::Widgets + Qt::WidgetsPrivate ) ## Scopes: @@ -20,7 +28,7 @@ qt_internal_add_test(tst_qmenu qt_internal_extend_target(tst_qmenu CONDITION MACOS SOURCES tst_qmenu_mac.mm - PUBLIC_LIBRARIES + LIBRARIES objc ) diff --git a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp index 9b92c27390..57cc404fc7 100644 --- a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp +++ b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QtTest/private/qtesthelpers_p.h> @@ -52,6 +27,9 @@ #include <qpa/qplatformtheme.h> #include <qpa/qplatformintegration.h> +#include <QtWidgets/private/qapplication_p.h> +#include <QtWidgets/private/qmenu_p.h> + using namespace QTestPrivate; Q_DECLARE_METATYPE(Qt::Key); @@ -118,6 +96,7 @@ private slots: void QTBUG_89082_actionTipsHide(); void QTBUG8122_widgetActionCrashOnClose(); void widgetActionTriggerClosesMenu(); + void transientParent(); void QTBUG_10735_crashWithDialog(); #ifdef Q_OS_MAC @@ -133,6 +112,10 @@ private slots: void menuSize_Scrolling(); void tearOffMenuNotDisplayed(); void QTBUG_61039_menu_shortcuts(); + void screenOrientationChangedCloseMenu(); + void deleteWhenTriggered(); + + void nestedTearOffDetached(); protected slots: void onActivated(QAction*); @@ -175,7 +158,9 @@ void tst_QMenu::getSetCheck() tst_QMenu::tst_QMenu() : m_onStatusTipTimerExecuted(false) { + QApplication::setEffectEnabled(Qt::UI_FadeTooltip, false); QApplication::setEffectEnabled(Qt::UI_AnimateMenu, false); + QApplication::setEffectEnabled(Qt::UI_AnimateTooltip, false); } void tst_QMenu::initTestCase() @@ -278,38 +263,93 @@ void tst_QMenu::onStatusMessageChanged(const QString &s) void tst_QMenu::addActionsAndClear() { - QCOMPARE(menus[0]->actions().count(), 0); + QCOMPARE(menus[0]->actions().size(), 0); createActions(); - QCOMPARE(menus[0]->actions().count(), 8); + QCOMPARE(menus[0]->actions().size(), 8); menus[0]->clear(); - QCOMPARE(menus[0]->actions().count(), 0); + QCOMPARE(menus[0]->actions().size(), 0); } -static void testFunction() { } +static void testFunction0() {} +static void testFunction1(bool) {} + +template <typename T> +struct ImplicitlyConvertibleTo { + T t; + operator const T() const { return t; } + operator T() { return t; } +}; void tst_QMenu::addActionsConnect() { + // almost exhaustive check of addAction() overloads: + // (text), (icon, text), (icon, text, shortcut), (text, shortcut) + // each with a good sample of ways to QObject::connect() to + // QAction::triggered(bool) QMenu menu; - const QString text = QLatin1String("bla"); - const QIcon icon; - menu.addAction(text, &menu, SLOT(deleteLater())); - menu.addAction(text, &menu, &QMenu::deleteLater); - menu.addAction(text, testFunction); - menu.addAction(text, &menu, testFunction); - menu.addAction(icon, text, &menu, SLOT(deleteLater())); - menu.addAction(icon, text, &menu, &QMenu::deleteLater); - menu.addAction(icon, text, testFunction); - menu.addAction(icon, text, &menu, testFunction); + + // don't just pass QString etc - that'd be too easy (think QStringBuilder) + ImplicitlyConvertibleTo<QString> text = {QLatin1String("bla")}; + ImplicitlyConvertibleTo<QIcon> icon; + + const auto check = [&](auto &...args) { // don't need to perfectly-forward, only lvalues passed + menu.addAction(args...); + + menu.addAction(args..., &menu, SLOT(deleteLater())); + menu.addAction(args..., &menu, &QMenu::deleteLater); + menu.addAction(args..., testFunction0); + menu.addAction(args..., &menu, testFunction0); + menu.addAction(args..., testFunction1); + menu.addAction(args..., &menu, testFunction1); + menu.addAction(args..., [&](bool b) { menu.setEnabled(b); }); + menu.addAction(args..., &menu, [&](bool b) { menu.setEnabled(b); }); + + menu.addAction(args..., &menu, SLOT(deleteLater()), Qt::QueuedConnection); + menu.addAction(args..., &menu, &QMenu::deleteLater, Qt::QueuedConnection); + // doesn't exist: menu.addAction(args..., testFunction0, Qt::QueuedConnection); + menu.addAction(args..., &menu, testFunction0, Qt::QueuedConnection); + // doesn't exist: menu.addAction(args..., testFunction1, Qt::QueuedConnection); + menu.addAction(args..., &menu, testFunction1, Qt::QueuedConnection); + // doesn't exist: menu.addAction(args..., [&](bool b) { menu.setEnabled(b); }, Qt::QueuedConnection); + menu.addAction(args..., &menu, [&](bool b) { menu.setEnabled(b); }, Qt::QueuedConnection); + }; + const auto check1 = [&](auto &arg, auto &...args) { + check(arg, args...); + check(std::as_const(arg), args...); + }; + const auto check2 = [&](auto &arg1, auto &arg2, auto &...args) { + check1(arg1, arg2, args...); + check1(arg1, std::as_const(arg2), args...); + }; + [[maybe_unused]] + const auto check3 = [&](auto &arg1, auto &arg2, auto &arg3) { + check2(arg1, arg2, arg3); + check2(arg1, arg2, std::as_const(arg3)); + }; + + check1(text); + check2(icon, text); #ifndef QT_NO_SHORTCUT - const QKeySequence keySequence(Qt::CTRL | Qt::Key_C); + ImplicitlyConvertibleTo<QKeySequence> keySequence = {Qt::CTRL | Qt::Key_C}; + check2(text, keySequence); + check3(icon, text, keySequence); +#if QT_DEPRECATED_SINCE(6, 4) + QT_WARNING_PUSH + QT_WARNING_DISABLE_DEPRECATED menu.addAction(text, &menu, SLOT(deleteLater()), keySequence); menu.addAction(text, &menu, &QMenu::deleteLater, keySequence); - menu.addAction(text, testFunction, keySequence); - menu.addAction(text, &menu, testFunction, keySequence); + menu.addAction(text, testFunction0, keySequence); + menu.addAction(text, &menu, testFunction0, keySequence); + menu.addAction(text, testFunction1, keySequence); + menu.addAction(text, &menu, testFunction1, keySequence); menu.addAction(icon, text, &menu, SLOT(deleteLater()), keySequence); menu.addAction(icon, text, &menu, &QMenu::deleteLater, keySequence); - menu.addAction(icon, text, testFunction, keySequence); - menu.addAction(icon, text, &menu, testFunction, keySequence); + menu.addAction(icon, text, testFunction0, keySequence); + menu.addAction(icon, text, &menu, testFunction0, keySequence); + menu.addAction(icon, text, testFunction1, keySequence); + menu.addAction(icon, text, &menu, testFunction1, keySequence); + QT_WARNING_POP +#endif #endif // !QT_NO_SHORTCUT } @@ -447,7 +487,7 @@ void tst_QMenu::focus() QPushButton button("Push me", &window); centerOnScreen(&window); window.show(); - qApp->setActiveWindow(&window); + QApplicationPrivate::setActiveWindow(&window); QVERIFY(button.hasFocus()); QCOMPARE(QApplication::focusWidget(), (QWidget *)&button); @@ -491,7 +531,6 @@ void tst_QMenu::overrideMenuAction() m->addAction(aQuit); w.show(); - QApplication::setActiveWindow(&w); w.setFocus(); QVERIFY(QTest::qWaitForWindowActive(&w)); QVERIFY(w.hasFocus()); @@ -859,13 +898,20 @@ private: void tst_QMenu::activeSubMenuPositionExec() { - +#ifdef Q_OS_ANDROID + // QTBUG-87424 + QSKIP("Android: This hangs. Figure out why."); +#endif SubMenuPositionExecMenu menu; menu.exec(QGuiApplication::primaryScreen()->availableGeometry().center()); } void tst_QMenu::task242454_sizeHint() { +#ifdef Q_OS_ANDROID + // QTBUG-87424 + QSKIP("Android: This hangs. Figure out why."); +#endif QMenu menu; QString s = QLatin1String("foo\nfoo\nfoo\nfoo"); menu.addAction(s); @@ -994,12 +1040,16 @@ public: // Mouse move related signals for Windows Mobile unavailable void tst_QMenu::task258920_mouseBorder() { + const QRect screenGeometry = QGuiApplication::primaryScreen()->availableGeometry(); Menu258920 menu; + QCursor::setPos(screenGeometry.topLeft()); + if (!QTest::qWaitFor([screenGeometry]{ return QCursor::pos() == screenGeometry.topLeft(); })) + QSKIP("Can't move cursor out of the way"); // For styles which inherit from QWindowsStyle, styleHint(QStyle::SH_Menu_MouseTracking) is true. menu.setMouseTracking(true); QAction *action = menu.addAction("test"); - const QPoint center = QGuiApplication::primaryScreen()->availableGeometry().center(); + const QPoint center = screenGeometry.center(); menu.popup(center); QVERIFY(QTest::qWaitForWindowExposed(&menu)); QRect actionRect = menu.actionGeometry(action); @@ -1095,14 +1145,18 @@ void tst_QMenu::pushButtonPopulateOnAboutToShow() QSKIP("Your window manager won't allow a window against the bottom of the screen"); } - QTimer::singleShot(300, buttonMenu, SLOT(hide())); QTest::mouseClick(&b, Qt::LeftButton, Qt::NoModifier, b.rect().center()); + QVERIFY(QTest::qWaitForWindowExposed(buttonMenu)); + QTest::qWait(300); + buttonMenu->hide(); QVERIFY2(!buttonMenu->geometry().intersects(b.geometry()), msgGeometryIntersects(buttonMenu->geometry(), b.geometry())); // note: we're assuming that, if we previously got the desired geometry, we'll get it here too b.move(10, screen.bottom()-buttonMenu->height()-5); - QTimer::singleShot(300, buttonMenu, SLOT(hide())); QTest::mouseClick(&b, Qt::LeftButton, Qt::NoModifier, b.rect().center()); + QVERIFY(QTest::qWaitForWindowExposed(buttonMenu)); + QTest::qWait(300); + buttonMenu->hide(); QVERIFY2(!buttonMenu->geometry().intersects(b.geometry()), msgGeometryIntersects(buttonMenu->geometry(), b.geometry())); } @@ -1217,7 +1271,7 @@ void tst_QMenu::click_while_dismissing_submenu() //the submenu must have been hidden for the bug to be triggered QVERIFY(!sub.isVisible()); QTest::mouseRelease(menuWindow, Qt::LeftButton, {}, menu.rect().center() - QPoint(0, 2), 300); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } #endif @@ -1369,10 +1423,15 @@ void tst_QMenu::QTBUG_89082_actionTipsHide() widget.resize(300, 200); centerOnScreen(&widget); widget.show(); +#if QT_CONFIG(cursor) + QCursor::setPos(widget.screen()->availableGeometry().topLeft() + QPoint(10, 10)); +#endif widget.activateWindow(); QVERIFY(QTest::qWaitForWindowExposed(&widget)); menu->popup(widget.geometry().topRight() + QPoint(50, 0)); QVERIFY(QTest::qWaitForWindowExposed(menu)); + auto menuWindow = menu->windowHandle(); + QVERIFY(menuWindow != nullptr); auto actionZero = menu->actions().at(0); auto actionOne = menu->actions().at(1); @@ -1386,12 +1445,12 @@ void tst_QMenu::QTBUG_89082_actionTipsHide() const QRect submenuRect5 = menu->actionGeometry(actionFive); const QPoint submenuPos5(submenuRect5.topLeft() + QPoint(10, 3)); - QTest::mouseMove(menu, submenuPos1); - QTest::mouseMove(menu, submenuPos0); //show the tip + QTest::mouseMove(menuWindow, submenuPos1); + QTest::mouseMove(menuWindow, submenuPos0); //show the tip QTRY_COMPARE_WITH_TIMEOUT(QToolTip::text(), tipFullName, 1000); //Move to the fifth action without prompting - QTest::mouseMove(menu, submenuPos5); + QTest::mouseMove(menuWindow, submenuPos5); //The previous tip was hidden, but now is a new tip to get text, So there should be no content QTRY_COMPARE_WITH_TIMEOUT(QToolTip::text(), QString(), 1000); } @@ -1525,6 +1584,53 @@ void tst_QMenu::widgetActionTriggerClosesMenu() QCOMPARE(actionTriggered, &widgetAction); } +void tst_QMenu::transientParent() +{ + QMainWindow window; + window.resize(480, 320); + window.menuBar()->setNativeMenuBar(false); + centerOnScreen(&window); + + QMenu *fileMenu = new QMenu("&File"); + QAction *exitAct = new QAction("Exit"); + fileMenu->addAction(exitAct); + + QMenu *editMenu = new QMenu("&Edit"); + QAction *undoAct = new QAction("Undo"); + editMenu->addAction(undoAct); + + QMenuBar *menuBar = new QMenuBar; + menuBar->addMenu(fileMenu); + menuBar->addMenu(editMenu); + window.setMenuBar(menuBar); + + // On Mac, we need to create native key events to test menu + // action activation, so skip this part of the test. +#if QT_CONFIG(shortcut) && !defined(Q_OS_DARWIN) + window.show(); + QVERIFY(QTest::qWaitForWindowActive(&window)); + QWindow *topLevel = window.windowHandle(); + QVERIFY(topLevel); + + window.setFocus(); + QVERIFY(QTest::qWaitForWindowActive(&window)); + QVERIFY(window.hasFocus()); + + QTest::keyPress(&window, Qt::Key_F, Qt::AltModifier); + QTRY_VERIFY(QTest::qWaitForWindowExposed(fileMenu)); + if (fileMenu->isWindow() && fileMenu->window() && fileMenu->window()->windowHandle()) + QVERIFY(fileMenu->window()->windowHandle()->transientParent()); + QTest::keyRelease(fileMenu, Qt::Key_F, Qt::AltModifier); + + QTest::keyPress(fileMenu, Qt::Key_E, Qt::AltModifier); + QTRY_VERIFY(QTest::qWaitForWindowExposed(editMenu)); + if (editMenu->isWindow() && editMenu->window() && editMenu->window()->windowHandle()) + QVERIFY(editMenu->window()->windowHandle()->transientParent()); + QTest::keyRelease(editMenu, Qt::Key_E, Qt::AltModifier); +#endif // QT_CONFIG(shortcut) && !Q_OS_DARWIN + +} + class MyMenu : public QMenu { Q_OBJECT @@ -1740,16 +1846,16 @@ void tst_QMenu::menuSize_Scrolling() private: void showEvent(QShowEvent *e) override { - QVERIFY(actions().length() == m_numItems); + QVERIFY(actions().size() == m_numItems); int hmargin = style()->pixelMetric(QStyle::PM_MenuHMargin, nullptr, this); int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, nullptr, this); const QMargins cm = contentsMargins(); - QRect lastItem = actionGeometry(actions().at(actions().length() - 1)); + QRect lastItem = actionGeometry(actions().at(actions().size() - 1)); QSize s = size(); if (!QGuiApplication::platformName().compare(QLatin1String("minimal"), Qt::CaseInsensitive) || !QGuiApplication::platformName().compare(QLatin1String("offscreen"), Qt::CaseInsensitive)) { - QWARN("Skipping test on minimal/offscreen platforms - QTBUG-73522"); + qWarning("Skipping test on minimal/offscreen platforms - QTBUG-73522"); QMenu::showEvent(e); return; } @@ -1811,7 +1917,7 @@ void tst_QMenu::menuSize_Scrolling() QVERIFY(QTest::qWaitForWindowExposed(&menu)); QList<QAction *> actions = menu.actions(); - QCOMPARE(actions.length(), numItems); + QCOMPARE(actions.size(), numItems); MenuMetrics mm(&menu); QTest::keyClick(&menu, Qt::Key_Home); @@ -1891,11 +1997,120 @@ void tst_QMenu::QTBUG_61039_menu_shortcuts() QSignalSpy actionKamenSpy(actionKamen, &QAction::triggered); QTest::keyClick(&widget, Qt::Key_K); - QTRY_COMPARE(actionKamenSpy.count(), 1); + QTRY_COMPARE(actionKamenSpy.size(), 1); QSignalSpy actionJoeSpy(actionJoe, &QAction::triggered); QTest::keyClick(&widget, Qt::Key_J, Qt::ControlModifier); - QTRY_COMPARE(actionJoeSpy.count(), 1); + QTRY_COMPARE(actionJoeSpy.size(), 1); +} + +void tst_QMenu::screenOrientationChangedCloseMenu() +{ + QMenu menu; + menu.addAction("action1"); + menu.show(); + + QTRY_COMPARE(menu.isVisible(),true); + + Qt::ScreenOrientation orientation = menu.screen()->orientation() == Qt::PortraitOrientation ? Qt::LandscapeOrientation : Qt::PortraitOrientation; + QScreenOrientationChangeEvent event(menu.screen(), orientation); + QCoreApplication::sendEvent(QCoreApplication::instance(), &event); + + QTRY_COMPARE(menu.isVisible(),false); +} + +/* + Verify that deleting the menu in a slot connected to an + action's triggered signal doesn't crash. + QTBUG-106718 +*/ +void tst_QMenu::deleteWhenTriggered() +{ + QPointer<QMenu> menu = new QMenu; + QAction *action = menu->addAction("Action", [&menu]{ + delete menu; + }); + menu->popup(QGuiApplication::primaryScreen()->availableGeometry().center()); + menu->setActiveAction(action); + QTest::keyClick(menu, Qt::Key_Return); + QTRY_VERIFY(!menu); +} + +/* + QMenu uses the caused-stack to create the parent/child relationship + for tear-off menus. Since QTornOffMenu set the DeleteOnClose flag, closing a + tear-off in the parent chain will result in a null-pointer in the caused-stack. + Verify that we don't crash when traversing the chain, as reported in QTBUG-112217. + + The test has to open the submenus by hovering of the menu action, otherwise + the caused-stack remains empty and the issue doesn't reproduce. Due to QMenu's + timing and "sloppiness", we need to move the mouse within the action, with some + waiting and event processing in between to trigger the opening of the submenu. + If this fails we skip, as we then can't test what we are trying to test. +*/ +void tst_QMenu::nestedTearOffDetached() +{ + // Since QTornOffMenu is not declared in qmenuprivate.h we can't access the + // object even through QMenuPrivate. So use an event filter to watch out for + // a QTornOffMenu showing. + class TearOffWatcher : public QObject + { + public: + QMenu *tornOffMenu = nullptr; + protected: + bool eventFilter(QObject *receiver, QEvent *event) override + { + if (event->type() == QEvent::Show && receiver->inherits("QTornOffMenu")) + tornOffMenu = qobject_cast<QMenu *>(receiver); + return QObject::eventFilter(receiver, event); + } + } watcher; + qApp->installEventFilter(&watcher); + + QWidget widget; + QMenu *menu = new QMenu("Context", &widget); + + MenuMetrics mm(menu); + const int tearOffOffset = mm.fw + mm.vmargin + mm.tearOffHeight / 2; + + QMenu *subMenu = menu->addMenu("SubMenu"); + menu->setTearOffEnabled(true); + QMenu *subSubMenu = subMenu->addMenu("SubSubMenu"); + subMenu->setTearOffEnabled(true); + subSubMenu->addAction("Action!"); + subSubMenu->setTearOffEnabled(true); + + widget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&widget)); + + // open and tear off context menu + menu->popup(widget.geometry().center()); + QTest::mouseClick(menu, Qt::LeftButton, {}, QPoint(menu->width() / 2, tearOffOffset)); + + QMenu *menuTorn = watcher.tornOffMenu; + watcher.tornOffMenu = nullptr; + QVERIFY(menuTorn); + QVERIFY(QTest::qWaitForWindowExposed(menuTorn)); + + // open second menu and tear-off + QTest::mouseMove(menuTorn, menuTorn->actionGeometry(subMenu->menuAction()).topLeft()); + QTest::qWait(100); + QTest::mouseMove(menuTorn, menuTorn->actionGeometry(subMenu->menuAction()).center()); + if (!QTest::qWaitFor([subMenu]{ return subMenu->isVisible(); })) + QSKIP("Menu failed to show, skipping test"); + + QTest::mouseClick(subMenu, Qt::LeftButton, {}, QPoint(subMenu->width() / 2, tearOffOffset)); + menuTorn = watcher.tornOffMenu; + QVERIFY(menuTorn); + QVERIFY(QTest::qWaitForWindowExposed(menuTorn)); + // close the top level tear off + menu->hideTearOffMenu(); + // open third menu and tear-off + QTest::mouseMove(menuTorn, menuTorn->actionGeometry(subSubMenu->menuAction()).topLeft()); + QTest::qWait(100); + QTest::mouseMove(menuTorn, menuTorn->actionGeometry(subSubMenu->menuAction()).center()); + QTRY_VERIFY(subSubMenu->isVisible()); + QTest::mouseClick(subSubMenu, Qt::LeftButton, {}, QPoint(subSubMenu->width() / 2, tearOffOffset)); } QTEST_MAIN(tst_QMenu) diff --git a/tests/auto/widgets/widgets/qmenu/tst_qmenu_mac.mm b/tests/auto/widgets/widgets/qmenu/tst_qmenu_mac.mm index f90af47c19..ba76c28fd4 100644 --- a/tests/auto/widgets/widgets/qmenu/tst_qmenu_mac.mm +++ b/tests/auto/widgets/widgets/qmenu/tst_qmenu_mac.mm @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #import <AppKit/AppKit.h> diff --git a/tests/auto/widgets/widgets/qmenubar/BLACKLIST b/tests/auto/widgets/widgets/qmenubar/BLACKLIST index 633bda332f..516fd39f86 100644 --- a/tests/auto/widgets/widgets/qmenubar/BLACKLIST +++ b/tests/auto/widgets/widgets/qmenubar/BLACKLIST @@ -1,10 +1,5 @@ [check_menuPosition] -ubuntu-16.04 -#QTBUG-66255 -ubuntu-18.04 -ubuntu-20.04 -[activatedCount] -opensuse-42.3 +ubuntu-22.04 # QTBUG-87421 [cornerWidgets] android diff --git a/tests/auto/widgets/widgets/qmenubar/CMakeLists.txt b/tests/auto/widgets/widgets/qmenubar/CMakeLists.txt index 63f690a2ed..f2b1c1bec6 100644 --- a/tests/auto/widgets/widgets/qmenubar/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qmenubar/CMakeLists.txt @@ -1,16 +1,24 @@ -# Generated from qmenubar.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qmenubar Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmenubar LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qmenubar SOURCES tst_qmenubar.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::TestPrivate Qt::Widgets + Qt::WidgetsPrivate ) ## Scopes: @@ -19,6 +27,6 @@ qt_internal_add_test(tst_qmenubar qt_internal_extend_target(tst_qmenubar CONDITION MACOS SOURCES tst_qmenubar_mac.mm - PUBLIC_LIBRARIES + LIBRARIES ${FWAppKit} ) diff --git a/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp b/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp index 0e6689b413..7c1fb97b97 100644 --- a/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp +++ b/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -40,10 +15,13 @@ #include <QVBoxLayout> #include <QLabel> #include <QPlainTextEdit> +#include <QTranslator> #include <qscreen.h> #include <qobject.h> +#include <QtWidgets/private/qapplication_p.h> + QT_FORWARD_DECLARE_CLASS(QMainWindow) #include <qmenubar.h> @@ -115,13 +93,6 @@ private slots: #endif void allowActiveAndDisabled(); void taskQTBUG56860_focus(); - void check_endKey(); - void check_homeKey(); - -// void check_mouse1_data(); -// void check_mouse1(); -// void check_mouse2_data(); -// void check_mouse2(); void check_altPress(); void check_altClosePress(); @@ -144,6 +115,8 @@ private slots: #ifdef Q_OS_MACOS void taskQTBUG56275_reinsertMenuInParentlessQMenuBar(); void QTBUG_57404_existingMenuItemException(); + void defaultEditMenuItems(); + #endif void QTBUG_25669_menubarActionDoubleTriggered(); void taskQTBUG55966_subMenuRemoved(); @@ -356,7 +329,6 @@ void tst_QMenuBar::accel() QMainWindow w; const TestMenu menu = initWindowWithSimpleMenuBar(w); w.show(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); // shortcuts won't work unless the window is active QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_A, Qt::ControlModifier ); @@ -378,7 +350,6 @@ void tst_QMenuBar::activatedCount() QFETCH( bool, forceNonNative ); initWindowWithSimpleMenuBar(w, forceNonNative); w.show(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_A, Qt::ControlModifier ); @@ -578,7 +549,6 @@ void tst_QMenuBar::check_accelKeys() QMainWindow w; initWindowWithComplexMenuBar(w); w.show(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); // start with a bogus key that shouldn't trigger anything @@ -657,7 +627,6 @@ void tst_QMenuBar::check_cursorKeys1() QMainWindow w; initWindowWithComplexMenuBar(w); w.show(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); // start with a ALT + 1 that activates the first popupmenu @@ -697,7 +666,6 @@ void tst_QMenuBar::check_cursorKeys2() QMainWindow w; initWindowWithComplexMenuBar(w); w.show(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); // select popupmenu2 @@ -736,7 +704,6 @@ void tst_QMenuBar::check_cursorKeys3() QMainWindow w; initWindowWithComplexMenuBar(w); w.show(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); // select Popupmenu 2 @@ -778,7 +745,6 @@ void tst_QMenuBar::taskQTBUG56860_focus() w.setCentralWidget(e); w.show(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QTRY_COMPARE(QApplication::focusWidget(), e); @@ -805,85 +771,6 @@ void tst_QMenuBar::taskQTBUG56860_focus() } /*! - If a popupmenu is active you can use home to go quickly to the first item in the menu. -*/ -void tst_QMenuBar::check_homeKey() -{ - // I'm temporarily shutting up this testcase. - // Seems like the behaviour i'm expecting isn't ok. - QSKIP("This test has been \"temporarily\" disabled at least since 2009 :)"); - - QEXPECT_FAIL( "0", "Popupmenu should respond to a Home key", Abort ); - - QMainWindow w; - initWindowWithComplexMenuBar(w); - w.show(); - QApplication::setActiveWindow(&w); - QVERIFY(QTest::qWaitForWindowActive(&w)); - - // select Popupmenu 2 - QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_2, Qt::AltModifier ); - - // Simulate some keys - QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_Down ); - QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_Down ); - QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_Down ); - QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_Home ); - // and press ENTER - QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_Enter ); - // Let's see if the correct slot is called... -// QVERIFY2( m_complexActionTriggerCount[int('c')] == 1, "Popupmenu should respond to a Home key" ); - QCOMPARE(m_complexTriggerCount[int('c')], 1); - QCOMPARE(m_complexTriggerCount[3], 0); - QCOMPARE(m_complexTriggerCount[4], 0); - QCOMPARE(m_complexTriggerCount[int('a')], 0); - QCOMPARE(m_complexTriggerCount[int('b')], 0); - QCOMPARE(m_complexTriggerCount[int('d')], 0); - QCOMPARE(m_complexTriggerCount[int('e')], 0); - QCOMPARE(m_complexTriggerCount[int('f')], 0); - QCOMPARE(m_complexTriggerCount[int('g')], 0); - QCOMPARE(m_complexTriggerCount[int('h')], 0); -} - -/*! - If a popupmenu is active you can use end to go quickly to the last item in the menu. -*/ -void tst_QMenuBar::check_endKey() -{ - // I'm temporarily silenting this testcase. - // Seems like the behaviour i'm expecting isn't ok. - QSKIP("This test has been \"temporarily\" disabled at least since 2009 :)"); - - QEXPECT_FAIL( "0", "Popupmenu should respond to an End key", Abort ); - - QMainWindow w; - initWindowWithComplexMenuBar(w); - w.show(); - QApplication::setActiveWindow(&w); - QVERIFY(QTest::qWaitForWindowActive(&w)); - - // select Popupmenu 2 - QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_2, Qt::AltModifier ); - - // Simulate some keys - QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_End ); - // and press ENTER - QTest::keyClick(static_cast<QWidget *>(0), Qt::Key_Enter ); - // Let's see if the correct slot is called... -// QVERIFY2( m_complexActionTriggerCount[int('h')] == 1, "Popupmenu should respond to an End key" ); - QCOMPARE(m_complexTriggerCount[int('h')], 1);//, "Popupmenu should respond to an End key"); - QCOMPARE(m_complexTriggerCount[3], 0); - QCOMPARE(m_complexTriggerCount[4], 0); - QCOMPARE(m_complexTriggerCount[int('a')], 0); - QCOMPARE(m_complexTriggerCount[int('b')], 0); - QCOMPARE(m_complexTriggerCount[int('c')], 0); - QCOMPARE(m_complexTriggerCount[int('d')], 0); - QCOMPARE(m_complexTriggerCount[int('e')], 0); - QCOMPARE(m_complexTriggerCount[int('f')], 0); - QCOMPARE(m_complexTriggerCount[int('g')], 0); -} - -/*! If a popupmenu is active you can use esc to hide the menu and then the menubar should become active. If Down is pressed next the popup is activated again. @@ -900,7 +787,6 @@ void tst_QMenuBar::check_escKey() const TestMenu menu = initWindowWithComplexMenuBar(w); w.show(); w.setFocus(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QVERIFY( !menu.menus.at(0)->isActiveWindow() ); @@ -921,7 +807,7 @@ void tst_QMenuBar::check_escKey() if (!QGuiApplication::platformName().compare(QLatin1String("minimal"), Qt::CaseInsensitive) || !QGuiApplication::platformName().compare(QLatin1String("offscreen"), Qt::CaseInsensitive)) { - QWARN("Skipping menu button test on minimal/offscreen platforms"); + qWarning("Skipping menu button test on minimal/offscreen platforms"); return; } @@ -938,112 +824,6 @@ void tst_QMenuBar::check_escKey() #endif -// void tst_QMenuBar::check_mouse1_data() -// { -// QTest::addColumn<QString>("popup_item"); -// QTest::addColumn<int>("itemA_count"); -// QTest::addColumn<int>("itemB_count"); - -// QTest::newRow( "A" ) << QString( "Item A Ctrl+A" ) << 1 << 0; -// QTest::newRow( "B" ) << QString( "Item B Ctrl+B" ) << 0 << 1; -// } - -// /*! -// Check if the correct signals are emitted if we select a popupmenu. -// */ -// void tst_QMenuBar::check_mouse1() -// { -// if (QSystem::curStyle() == "Motif") -// QSKIP("This fails in Motif due to a bug in the testing framework"); -// QFETCH( QString, popup_item ); -// QFETCH( int, itemA_count ); -// QFETCH( int, itemB_count ); - -// // initComplexMenubar(); -// QVERIFY( !pm1->isActiveWindow() ); -// QVERIFY( !pm2->isActiveWindow() ); - -// QTest::qWait(1000); -// QtTestMouse mouse; -// mouse.mouseEvent( QtTestMouse::MouseClick, mb, "Menu &1", Qt::LeftButton ); - -// QVERIFY( pm1->isActiveWindow() ); -// QVERIFY( !pm2->isActiveWindow() ); - -// QTest::qWait(1000); -// mouse.mouseEvent( QtTestMouse::MouseClick, pm1, popup_item, Qt::LeftButton ); - -// QCOMPARE(m_complexActionTriggerCount[3], 0); -// QCOMPARE(m_complexActionTriggerCount[4], 0); -// QCOMPARE(m_complexActionTriggerCount['a'], (uint)itemA_count); // this option should have fired -// QCOMPARE(m_complexActionTriggerCount['b'], (uint)itemB_count); -// QCOMPARE(m_complexActionTriggerCount['c'], 0); -// QCOMPARE(m_complexActionTriggerCount['d'], 0); -// QCOMPARE(m_complexActionTriggerCount['e'], 0); -// QCOMPARE(m_complexActionTriggerCount['f'], 0); -// QCOMPARE(m_complexActionTriggerCount['g'], 0); -// } - -// void tst_QMenuBar::check_mouse2_data() -// { -// QTest::addColumn<QString>("label"); -// QTest::addColumn<int>("itemA_count"); -// QTest::addColumn<int>("itemB_count"); -// QTest::addColumn<int>("itemC_count"); -// QTest::addColumn<int>("itemD_count"); -// QTest::addColumn<int>("itemE_count"); -// QTest::addColumn<int>("itemF_count"); -// QTest::addColumn<int>("itemG_count"); -// QTest::addColumn<int>("itemH_count"); -// QTest::addColumn<int>("menu3_count"); - -// QTest::newRow( "A" ) << QString( "Menu &1/Item A Ctrl+A" ) << 1 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; -// QTest::newRow( "B" ) << QString( "Menu &1/Item B Ctrl+B" ) << 0 << 1 << 0 << 0 << 0 << 0 << 0 << 0 << 0; -// QTest::newRow( "C" ) << QString( "Menu &2/Item C Ctrl+C" ) << 0 << 0 << 1 << 0 << 0 << 0 << 0 << 0 << 0; -// QTest::newRow( "D" ) << QString( "Menu &2/Item D Ctrl+D" ) << 0 << 0 << 0 << 1 << 0 << 0 << 0 << 0 << 0; -// QTest::newRow( "E" ) << QString( "Menu &2/Item E Ctrl+E" ) << 0 << 0 << 0 << 0 << 1 << 0 << 0 << 0 << 0; -// QTest::newRow( "F" ) << QString( "Menu &2/Item F Ctrl+F" ) << 0 << 0 << 0 << 0 << 0 << 1 << 0 << 0 << 0; -// QTest::newRow( "G" ) << QString( "Menu &2/Item G Ctrl+G" ) << 0 << 0 << 0 << 0 << 0 << 0 << 1 << 0 << 0; -// QTest::newRow( "H" ) << QString( "Menu &2/Item H Ctrl+H" ) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 1 << 0; -// QTest::newRow( "menu 3" ) << QString( "M&enu 3" ) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 1; -// } - -// /*! -// Check if the correct signals are emitted if we select a popupmenu. -// This time, we use a little bit more magic from the testframework. -// */ -// void tst_QMenuBar::check_mouse2() -// { -// if (QSystem::curStyle() == "Motif") -// QSKIP("This fails in Motif due to a bug in the testing framework"); -// QFETCH( QString, label ); -// QFETCH( int, itemA_count ); -// QFETCH( int, itemB_count ); -// QFETCH( int, itemC_count ); -// QFETCH( int, itemD_count ); -// QFETCH( int, itemE_count ); -// QFETCH( int, itemF_count ); -// QFETCH( int, itemG_count ); -// QFETCH( int, itemH_count ); -// QFETCH( int, menu3_count ); - -// // initComplexMenubar(); -// QtTestMouse mouse; -// mouse.click( QtTestMouse::Menu, label, Qt::LeftButton ); - -// // check if the correct signals have fired -// QCOMPARE(m_complexActionTriggerCount[3], (uint)menu3_count); -// QCOMPARE(m_complexActionTriggerCount[4], 0); -// QCOMPARE(m_complexActionTriggerCount['a'], (uint)itemA_count); -// QCOMPARE(m_complexActionTriggerCount['b'], (uint)itemB_count); -// QCOMPARE(m_complexActionTriggerCount['c'], (uint)itemC_count); -// QCOMPARE(m_complexActionTriggerCount['d'], (uint)itemD_count); -// QCOMPARE(m_complexActionTriggerCount['e'], (uint)itemE_count); -// QCOMPARE(m_complexActionTriggerCount['f'], (uint)itemF_count); -// QCOMPARE(m_complexActionTriggerCount['g'], (uint)itemG_count); -// QCOMPARE(m_complexActionTriggerCount['h'], (uint)itemH_count); -// } - void tst_QMenuBar::allowActiveAndDisabled() { QMenuBar menuBar; @@ -1098,7 +878,6 @@ void tst_QMenuBar::check_altPress() initWindowWithSimpleMenuBar(w); w.show(); w.setFocus(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QTest::keyClick( &w, Qt::Key_Alt ); @@ -1128,7 +907,6 @@ void tst_QMenuBar::check_altClosePress() w.show(); w.move(QGuiApplication::primaryScreen()->availableGeometry().center()); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QTest::keyClick(&w, Qt::Key_F, Qt::AltModifier); @@ -1149,7 +927,6 @@ void tst_QMenuBar::check_shortcutPress() const TestMenu menu = initWindowWithComplexMenuBar(w); w.show(); w.setFocus(); - QApplication::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QCOMPARE(m_complexTriggerCount[3], 0); @@ -1206,7 +983,6 @@ void tst_QMenuBar::check_menuPosition() QAction *menu_action = w.menuBar()->addMenu(&menu); centerOnScreen(&w); w.show(); - qApp->setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); //the menu should be below the menubar item @@ -1290,9 +1066,9 @@ void tst_QMenuBar::task223138_triggered() //let's trigger the first action top->trigger(); - QCOMPARE(menubarSpy.count(), 1); - QCOMPARE(menuSpy.count(), 1); - QCOMPARE(submenuSpy.count(), 0); + QCOMPARE(menubarSpy.size(), 1); + QCOMPARE(menuSpy.size(), 1); + QCOMPARE(submenuSpy.size(), 0); menubarSpy.clear(); menuSpy.clear(); @@ -1300,9 +1076,9 @@ void tst_QMenuBar::task223138_triggered() //let's trigger the sub action action->trigger(); - QCOMPARE(menubarSpy.count(), 1); - QCOMPARE(menuSpy.count(), 1); - QCOMPARE(submenuSpy.count(), 1); + QCOMPARE(menubarSpy.size(), 1); + QCOMPARE(menuSpy.size(), 1); + QCOMPARE(submenuSpy.size(), 1); } void tst_QMenuBar::task256322_highlight() @@ -1325,7 +1101,6 @@ void tst_QMenuBar::task256322_highlight() centerOnScreen(&win); win.show(); - QApplication::setActiveWindow(&win); QVERIFY(QTest::qWaitForWindowActive(&win)); const QPoint filePos = menuBarActionWindowPos(win.menuBar(), file); @@ -1356,7 +1131,8 @@ void tst_QMenuBar::menubarSizeHint() { MyStyle() : QProxyStyle(QStyleFactory::create("windows")) { } - virtual int pixelMetric(PixelMetric metric, const QStyleOption * option = 0, const QWidget * widget = 0 ) const + int pixelMetric(PixelMetric metric, const QStyleOption *option = 0, + const QWidget *widget = 0) const override { // I chose strange values (prime numbers to be more sure that the size of the menubar is correct) switch (metric) @@ -1380,8 +1156,8 @@ void tst_QMenuBar::menubarSizeHint() mb.setStyle(&style); //this is a list of arbitrary strings so that we check the geometry - QStringList list = QStringList() << "trer" << "ezrfgtgvqd" << "sdgzgzerzerzer" << "eerzertz" << "er"; - foreach(QString str, list) + const auto list = QStringList{"trer", "ezrfgtgvqd", "sdgzgzerzerzer", "eerzertz", "er"}; + for (const QString &str : list) mb.addAction(str); const int panelWidth = style.pixelMetric(QStyle::PM_MenuBarPanelWidth); @@ -1392,7 +1168,8 @@ void tst_QMenuBar::menubarSizeHint() centerOnScreen(&mb); mb.show(); QRect result; - foreach(QAction *action, mb.actions()) { + const auto actions = mb.actions(); + for (QAction *action : actions) { const QRect actionRect = mb.actionGeometry(action); if (!result.isNull()) //this is the first item QCOMPARE(actionRect.left() - result.right() - 1, spacing); @@ -1428,11 +1205,11 @@ void tst_QMenuBar::taskQTBUG4965_escapeEaten() QMenu menu("menu1"); QAction *first = menubar.addMenu(&menu); #if QT_CONFIG(shortcut) - menu.addAction("quit", &menubar, SLOT(close()), QKeySequence("ESC")); + menu.addAction("quit", QKeySequence("ESC"), &menubar, SLOT(close())); #endif centerOnScreen(&menubar); menubar.show(); - QApplication::setActiveWindow(&menubar); + QApplicationPrivate::setActiveWindow(&menubar); QVERIFY(QTest::qWaitForWindowExposed(&menubar)); menubar.setActiveAction(first); QTRY_VERIFY(menu.isVisible()); @@ -1463,7 +1240,6 @@ void tst_QMenuBar::taskQTBUG11823_crashwithInvisibleActions() centerOnScreen(&menubar); menubar.show(); - QApplication::setActiveWindow(&menubar); QVERIFY(QTest::qWaitForWindowActive(&menubar)); menubar.setActiveAction(m); QCOMPARE(menubar.activeAction(), m); @@ -1494,7 +1270,6 @@ void tst_QMenuBar::closeOnSecondClickAndOpenOnThirdClick() // QTBUG-32807, menu QMenu *fileMenu = menuBar->addMenu(QStringLiteral("OpenCloseOpen")); fileMenu->addAction(QStringLiteral("Quit")); mainWindow.show(); - QApplication::setActiveWindow(&mainWindow); QVERIFY(QTest::qWaitForWindowActive(&mainWindow)); const QPoint center = menuBarActionWindowPos(mainWindow.menuBar(), fileMenu->menuAction()); @@ -1589,7 +1364,6 @@ void tst_QMenuBar::taskQTBUG53205_crashReparentNested() mainWindow.resize(300, 200); centerOnScreen(&mainWindow); const TestMenu testMenus = initWindowWithComplexMenuBar(mainWindow); - QApplication::setActiveWindow(&mainWindow); // they can't be windows QWidget hiddenParent(&mainWindow, {}); @@ -1639,12 +1413,11 @@ void tst_QMenuBar::QTBUG_65488_hiddenActionTriggered() // resize to action's size to make Action1 hidden win.resize(actRect.width() - 10, win.size().height()); win.show(); - QApplication::setActiveWindow(&win); QVERIFY(QTest::qWaitForWindowExposed(&win)); // click center of the blank area on the menubar where Action1 resided QTest::mouseClick(win.windowHandle(), Qt::LeftButton, Qt::NoModifier, win.menuBar()->geometry().center()); QCoreApplication::sendPostedEvents(); // make sure all queued events also dispatched - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } // QTBUG-56526 @@ -1711,20 +1484,19 @@ void tst_QMenuBar::QTBUG_25669_menubarActionDoubleTriggered() QSignalSpy spy(win.menuBar(), &QMenuBar::triggered); win.show(); - QApplication::setActiveWindow(&win); QVERIFY(QTest::qWaitForWindowExposed(&win)); QPoint posAct1 = menuBarActionWindowPos(win.menuBar(), act1); QPoint posAct2 = menuBarActionWindowPos(win.menuBar(), act2); QTest::mouseClick(win.windowHandle(), Qt::LeftButton, Qt::NoModifier, posAct1); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QTest::mouseClick(win.windowHandle(), Qt::LeftButton, Qt::NoModifier, posAct2); - QTRY_COMPARE(spy.count(), 2); + QTRY_COMPARE(spy.size(), 2); QTest::mouseClick(win.windowHandle(), Qt::LeftButton, Qt::NoModifier, posAct2); - QTRY_COMPARE(spy.count(), 3); + QTRY_COMPARE(spy.size(), 3); } void tst_QMenuBar::slotForTaskQTBUG53205() @@ -1748,7 +1520,6 @@ void tst_QMenuBar::taskQTBUG46812_doNotLeaveMenubarHighlighted() initWindowWithSimpleMenuBar(mainWindow); mainWindow.show(); - QApplication::setActiveWindow(&mainWindow); QVERIFY(QTest::qWaitForWindowActive(&mainWindow)); QVERIFY(!mainWindow.menuBar()->hasFocus()); @@ -1817,6 +1588,32 @@ void tst_QMenuBar::QTBUG_57404_existingMenuItemException() QTest::qWait(100); // No crash, all fine. Ideally, there should be only one warning. } + +void tst_QMenuBar::defaultEditMenuItems() +{ + class TestTranslator : public QTranslator + { + public: + QString translate(const char *context, const char *sourceText, + const char *disambiguation = nullptr, int n = -1) const override + { + if (QByteArrayView(context) == "QCocoaMenu" && QByteArrayView(sourceText) == "Edit") + return QString("Editieren"); + return QTranslator::translate(context, sourceText, disambiguation, n); + } + } testTranslator; + qApp->installTranslator(&testTranslator); + + QMainWindow mw; + mw.show(); + QVERIFY(QTest::qWaitForWindowActive(&mw)); + + mw.menuBar()->addMenu("Editieren")->addAction("Undo"); + + mw.hide(); + mw.show(); + // this should not crash with infinite recursion +} #endif // Q_OS_MACOS void tst_QMenuBar::taskQTBUG55966_subMenuRemoved() @@ -1834,7 +1631,6 @@ void tst_QMenuBar::taskQTBUG55966_subMenuRemoved() delete subMenu; window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); QTest::qWait(500); } diff --git a/tests/auto/widgets/widgets/qmenubar/tst_qmenubar_mac.mm b/tests/auto/widgets/widgets/qmenubar/tst_qmenubar_mac.mm index 823ca7edfa..55e983a4d1 100644 --- a/tests/auto/widgets/widgets/qmenubar/tst_qmenubar_mac.mm +++ b/tests/auto/widgets/widgets/qmenubar/tst_qmenubar_mac.mm @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #import <Cocoa/Cocoa.h> diff --git a/tests/auto/widgets/widgets/qopenglwidget/BLACKLIST b/tests/auto/widgets/widgets/qopenglwidget/BLACKLIST index ecb962d8ca..6b96499889 100644 --- a/tests/auto/widgets/widgets/qopenglwidget/BLACKLIST +++ b/tests/auto/widgets/widgets/qopenglwidget/BLACKLIST @@ -1,5 +1,7 @@ -[stackWidgetOpaqueChildIsVisible] -windows-10 msvc-2017 +# Temporary solution COIN-966 +ubuntu +rhel +opensuse-leap # QTBUG-87436 [clearAndGrab] diff --git a/tests/auto/widgets/widgets/qopenglwidget/CMakeLists.txt b/tests/auto/widgets/widgets/qopenglwidget/CMakeLists.txt index f32b547329..78cef5300a 100644 --- a/tests/auto/widgets/widgets/qopenglwidget/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qopenglwidget/CMakeLists.txt @@ -1,14 +1,21 @@ -# Generated from qopenglwidget.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qopenglwidget Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qopenglwidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qopenglwidget - LOWDPI # special case + LOWDPI SOURCES tst_qopenglwidget.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp index 3d7bb36f24..773ccd894c 100644 --- a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp +++ b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp @@ -1,34 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtOpenGLWidgets/QOpenGLWidget> #include <QtGui/QOpenGLFunctions> #include <QtGui/QPainter> +#include <QtGui/QBackingStore> #include <QtGui/QScreen> #include <QtGui/QStaticText> #include <QtWidgets/QGraphicsView> @@ -37,12 +13,18 @@ #include <QtWidgets/QVBoxLayout> #include <QtWidgets/QPushButton> #include <QtWidgets/QStackedWidget> +#include <QtWidgets/QTabWidget> +#include <QtWidgets/QLabel> #include <QTest> #include <QSignalSpy> #include <private/qguiapplication_p.h> #include <private/qstatictext_p.h> #include <private/qopengltextureglyphcache_p.h> #include <qpa/qplatformintegration.h> +#include <private/qguiapplication_p.h> +#include <qpa/qplatformbackingstore.h> +#include <qpa/qplatformintegration.h> +#include <rhi/qrhi.h> class tst_QOpenGLWidget : public QObject { @@ -54,10 +36,14 @@ private slots: void clearAndGrab(); void clearAndResizeAndGrab(); void createNonTopLevel(); +#if QT_CONFIG(egl) + void deviceLoss(); +#endif void painter(); void reparentToAlreadyCreated(); void reparentToNotYetCreated(); void reparentHidden(); + void reparentTopLevel(); void asViewport(); void requestUpdate(); void fboRedirect(); @@ -66,6 +52,9 @@ private slots: void stackWidgetOpaqueChildIsVisible(); void offscreen(); void offscreenThenOnscreen(); + void paintWhileHidden(); + void widgetWindowColorFormat_data(); + void widgetWindowColorFormat(); #ifdef QT_BUILD_INTERNAL void staticTextDanglingPointer(); @@ -87,7 +76,7 @@ void tst_QOpenGLWidget::create() QSignalSpy frameSwappedSpy(w.data(), SIGNAL(frameSwapped())); w->show(); QVERIFY(QTest::qWaitForWindowExposed(w.data())); - QVERIFY(frameSwappedSpy.count() > 0); + QVERIFY(frameSwappedSpy.size() > 0); QVERIFY(w->isValid()); QVERIFY(w->context()); @@ -152,6 +141,9 @@ void tst_QOpenGLWidget::clearAndGrab() void tst_QOpenGLWidget::clearAndResizeAndGrab() { +#ifdef Q_OS_ANDROID + QSKIP("Crashes on Android, figure out why (QTBUG-102043)"); +#endif QScopedPointer<QOpenGLWidget> w(new ClearWidget(0, 640, 480)); w->resize(640, 480); w->show(); @@ -179,7 +171,7 @@ void tst_QOpenGLWidget::createNonTopLevel() w.resize(400, 400); w.show(); QVERIFY(QTest::qWaitForWindowExposed(&w)); - QVERIFY(frameSwappedSpy.count() > 0); + QVERIFY(frameSwappedSpy.size() > 0); QVERIFY(glw->m_resizeCalled); glw->m_resizeCalled = false; @@ -203,10 +195,49 @@ void tst_QOpenGLWidget::createNonTopLevel() QVERIFY(QOpenGLContext::currentContext() == glw->context() && glw->context()); } +#if QT_CONFIG(egl) +void tst_QOpenGLWidget::deviceLoss() +{ + QScopedPointer<QOpenGLWidget> w(new ClearWidget(0, 640, 480)); + + w->resize(640, 480); + w->show(); + + auto rhi = w->backingStore()->handle()->rhi(); + QNativeInterface::QEGLContext *rhiContext = nullptr; + if (rhi->backend() == QRhi::OpenGLES2) { + auto rhiHandles = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles()); + rhiContext = rhiHandles->context->nativeInterface<QNativeInterface::QEGLContext>(); + } + if (!rhiContext) + QSKIP("deviceLoss needs EGL"); + + QVERIFY(QTest::qWaitForWindowExposed(w.data())); + + QImage image = w->grabFramebuffer(); + QVERIFY(!image.isNull()); + QCOMPARE(image.width(), w->width()); + QCOMPARE(image.height(), w->height()); + QVERIFY(image.pixel(30, 40) == qRgb(255, 0, 0)); + + rhiContext->invalidateContext(); + + w->resize(600, 600); + QSignalSpy frameSwappedSpy(w.get(), &QOpenGLWidget::resized); + QTRY_VERIFY(frameSwappedSpy.size() > 0); + + image = w->grabFramebuffer(); + QVERIFY(!image.isNull()); + QCOMPARE(image.width(), w->width()); + QCOMPARE(image.height(), w->height()); + QVERIFY(image.pixel(30, 40) == qRgb(255, 0, 0)); +} +#endif + class PainterWidget : public QOpenGLWidget, protected QOpenGLFunctions { public: - PainterWidget(QWidget *parent) + PainterWidget(QWidget *parent = nullptr) : QOpenGLWidget(parent), m_clear(false) { } void initializeGL() override { @@ -272,6 +303,9 @@ void tst_QOpenGLWidget::reparentToAlreadyCreated() void tst_QOpenGLWidget::reparentToNotYetCreated() { +#ifdef Q_OS_ANDROID + QSKIP("Crashes on Android, figure out why (QTBUG-102043)"); +#endif QWidget w1; PainterWidget *glw = new PainterWidget(&w1); w1.resize(640, 480); @@ -320,6 +354,70 @@ void tst_QOpenGLWidget::reparentHidden() QVERIFY(originalContext != newContext); } +void tst_QOpenGLWidget::reparentTopLevel() +{ +#ifdef Q_OS_ANDROID + QSKIP("Crashes on Android, figure out why (QTBUG-102043)"); +#endif + // no GL content yet, just an ordinary tab widget, top-level + QTabWidget tabWidget; + tabWidget.resize(640, 480); + tabWidget.addTab(new QLabel("Dummy page"), "Page 1"); + tabWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tabWidget)); + + PainterWidget *glw1 = new PainterWidget; + // add child GL widget as a tab page + { + QSignalSpy frameSwappedSpy(glw1, &QOpenGLWidget::frameSwapped); + tabWidget.setCurrentIndex(tabWidget.addTab(glw1, "OpenGL widget 1")); + QTRY_VERIFY(frameSwappedSpy.size() > 0); + } + + PainterWidget *glw2 = new PainterWidget; + // add child GL widget #2 as a tab page + { + QSignalSpy frameSwappedSpy(glw2, &QOpenGLWidget::frameSwapped); + tabWidget.setCurrentIndex(tabWidget.addTab(glw2, "OpenGL widget 2")); + QTRY_VERIFY(frameSwappedSpy.size() > 0); + } + + QImage image = glw2->grabFramebuffer(); + QVERIFY(image.pixel(20, 10) == qRgb(0, 0, 255)); + + // now delete GL widget #2 + { + QSignalSpy frameSwappedSpy(glw1, &QOpenGLWidget::frameSwapped); + delete glw2; + QTRY_VERIFY(frameSwappedSpy.size() > 0); + } + + image = glw1->grabFramebuffer(); + QVERIFY(image.pixel(20, 10) == qRgb(0, 0, 255)); + + // make the GL widget top-level + { + QSignalSpy frameSwappedSpy(glw1, &QOpenGLWidget::frameSwapped); + glw1->setParent(nullptr); + glw1->show(); + QVERIFY(QTest::qWaitForWindowExposed(glw1)); + QTRY_VERIFY(frameSwappedSpy.size() > 0); + } + + image = glw1->grabFramebuffer(); + QVERIFY(image.pixel(20, 10) == qRgb(0, 0, 255)); + + // back to a child widget by readding to the tab widget + { + QSignalSpy frameSwappedSpy(glw1, &QOpenGLWidget::frameSwapped); + tabWidget.setCurrentIndex(tabWidget.addTab(glw1, "Re-added OpenGL widget 1")); + QTRY_VERIFY(frameSwappedSpy.size() > 0); + } + + image = glw1->grabFramebuffer(); + QVERIFY(image.pixel(20, 10) == qRgb(0, 0, 255)); +} + class CountingGraphicsView : public QGraphicsView { public: @@ -346,6 +444,9 @@ void CountingGraphicsView::drawForeground(QPainter *, const QRectF &) void tst_QOpenGLWidget::asViewport() { +#ifdef Q_OS_ANDROID + QSKIP("Crashes on Android, figure out why (QTBUG-102043)"); +#endif // Have a QGraphicsView with a QOpenGLWidget as its viewport. QGraphicsScene scene; scene.addItem(new QGraphicsRectItem(10, 10, 100, 100)); @@ -367,6 +468,7 @@ void tst_QOpenGLWidget::asViewport() // repainted when going from Inactive to Active. So wait for the window to be // active before we continue, so the activation doesn't happen at a random // time below. And call processEvents to have the paint events delivered right away. + widget.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&widget)); qApp->processEvents(); } @@ -394,8 +496,9 @@ public: void tst_QOpenGLWidget::requestUpdate() { - if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) - QSKIP("Wayland: This fails. Figure out why."); +#ifdef Q_OS_ANDROID + QSKIP("Crashes on Android, figure out why (QTBUG-102043)"); +#endif PaintCountWidget w; w.resize(640, 480); @@ -421,6 +524,9 @@ public: void tst_QOpenGLWidget::fboRedirect() { +#ifdef Q_OS_ANDROID + QSKIP("Crashes on Android, figure out why (QTBUG-102043)"); +#endif FboCheckWidget w; w.resize(640, 480); w.show(); @@ -436,6 +542,9 @@ void tst_QOpenGLWidget::fboRedirect() void tst_QOpenGLWidget::showHide() { +#ifdef Q_OS_ANDROID + QSKIP("Crashes on Android, figure out why (QTBUG-102043)"); +#endif QScopedPointer<ClearWidget> w(new ClearWidget(0, 800, 600)); w->resize(800, 600); w->show(); @@ -460,41 +569,187 @@ void tst_QOpenGLWidget::showHide() QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); } +QtMessageHandler oldHandler = nullptr; + +void nativeWindowMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) +{ + if (oldHandler) + oldHandler(type, context, msg); + + if (type == QtWarningMsg + && (msg.contains("QOpenGLContext::makeCurrent() called with non-opengl surface") + || msg.contains("Failed to make context current"))) + { + QFAIL("Unexpected warning got printed"); + } +} + void tst_QOpenGLWidget::nativeWindow() { - QScopedPointer<ClearWidget> w(new ClearWidget(0, 800, 600)); - w->resize(800, 600); - w->show(); - w->winId(); - QVERIFY(QTest::qWaitForWindowExposed(w.data())); +#ifdef Q_OS_ANDROID + QSKIP("Crashes on Android, figure out why (QTBUG-102043)"); +#endif - QImage image = w->grabFramebuffer(); - QVERIFY(!image.isNull()); - QCOMPARE(image.width(), w->width()); - QCOMPARE(image.height(), w->height()); - QVERIFY(image.pixel(30, 40) == qRgb(255, 0, 0)); - QVERIFY(w->internalWinId()); - - // Now as a native child. - QWidget nativeParent; - nativeParent.resize(800, 600); - nativeParent.setAttribute(Qt::WA_NativeWindow); - ClearWidget *child = new ClearWidget(0, 800, 600); - child->setClearColor(0, 1, 0); - child->setParent(&nativeParent); - child->resize(400, 400); - child->move(23, 34); - nativeParent.show(); - QVERIFY(QTest::qWaitForWindowExposed(&nativeParent)); - - QVERIFY(nativeParent.internalWinId()); - QVERIFY(!child->internalWinId()); - - image = child->grabFramebuffer(); - QVERIFY(!image.isNull()); - QCOMPARE(image.width(), child->width()); - QCOMPARE(image.height(), child->height()); - QVERIFY(image.pixel(30, 40) == qRgb(0, 255, 0)); + // NB these tests do not fully verify that native child widgets are fully + // functional since there is no guarantee that the content is composed and + // presented correctly as we can only do verification with + // grabFramebuffer() here which only exercises a part of the pipeline. + + // Install a message handler that looks for some typical warnings from + // QRhi/QOpenGLConext that occur when the RHI-related logic in widgets goes wrong. + oldHandler = qInstallMessageHandler(nativeWindowMessageHandler); + + { + QScopedPointer<ClearWidget> w(new ClearWidget(nullptr, 800, 600)); + w->resize(800, 600); + w->show(); + w->winId(); + QVERIFY(QTest::qWaitForWindowExposed(w.data())); + + QImage image = w->grabFramebuffer(); + QVERIFY(!image.isNull()); + QCOMPARE(image.width(), w->width()); + QCOMPARE(image.height(), w->height()); + QVERIFY(image.pixel(30, 40) == qRgb(255, 0, 0)); + QVERIFY(w->internalWinId()); + } + + // QTBUG-113557: a plain _raster_ QWidget that is a _native_ child in a toplevel + // combined with a RHI-based (non-native) widget (QOpenGLWidget in this case) + // in the same toplevel. + { + QWidget topLevel; + topLevel.resize(800, 600); + + ClearWidget *child = new ClearWidget(&topLevel, 800, 600); + child->setClearColor(1, 0, 0); + child->resize(400, 400); + child->move(23, 34); + + QWidget *raster = new QWidget(&topLevel); + raster->setGeometry(23, 240, 120, 120); + raster->setStyleSheet("QWidget { background-color: yellow; }"); + + raster->winId(); + + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + // Do not bother checking the output, i.e. if the yellow raster native child + // shows up as it should, but rather rely on the message handler catching the + // qWarnings if they occur. + } + + // Now with the QOpenGLWidget being a native child + { + QWidget topLevel; + topLevel.resize(800, 600); + + ClearWidget *child = new ClearWidget(nullptr, 800, 600); + child->setParent(&topLevel); + + // make it a native child (native window, but not top-level -> no backingstore) + child->winId(); + + child->setClearColor(0, 1, 0); + child->resize(400, 400); + child->move(23, 34); + + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + QVERIFY(topLevel.internalWinId()); + QVERIFY(child->internalWinId()); + + QImage image = child->grabFramebuffer(); + QVERIFY(!image.isNull()); + QCOMPARE(image.width(), child->width()); + QCOMPARE(image.height(), child->height()); + QVERIFY(image.pixel(30, 40) == qRgb(0, 255, 0)); + } + + // Now the same with WA_NativeWindow instead + { + QWidget topLevel; + topLevel.resize(800, 600); + + ClearWidget *child = new ClearWidget(nullptr, 800, 600); + child->setParent(&topLevel); + + // make it a native child (native window, but not top-level -> no backingstore) + child->setAttribute(Qt::WA_NativeWindow); + + child->setClearColor(0, 1, 0); + child->resize(400, 400); + child->move(23, 34); + + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + QVERIFY(child->internalWinId()); + + QImage image = child->grabFramebuffer(); + QCOMPARE(image.width(), child->width()); + QCOMPARE(image.height(), child->height()); + QVERIFY(image.pixel(30, 40) == qRgb(0, 255, 0)); + } + + // Now as a child of a native child + { + QWidget topLevel; + topLevel.resize(800, 600); + + QWidget *container = new QWidget(&topLevel); + // make it a native child (native window, but not top-level -> no backingstore) + container->winId(); + + ClearWidget *child = new ClearWidget(nullptr, 800, 600); + // set the parent separately, this is important, see next test case + child->setParent(container); + child->setClearColor(0, 0, 1); + child->resize(400, 400); + child->move(23, 34); + + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + + QVERIFY(topLevel.internalWinId()); + QVERIFY(container->internalWinId()); + QVERIFY(!child->internalWinId()); + + QImage image = child->grabFramebuffer(); + QCOMPARE(image.width(), child->width()); + QCOMPARE(image.height(), child->height()); + QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); + } + + // Again as a child of a native child, but this time specifying the parent + // upon construction, not with an explicit setParent() call afterwards. + { + QWidget topLevel; + topLevel.resize(800, 600); + QWidget *container = new QWidget(&topLevel); + container->winId(); + // parent it right away + ClearWidget *child = new ClearWidget(container, 800, 600); + child->setClearColor(0, 0, 1); + child->resize(400, 400); + child->move(23, 34); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + QVERIFY(topLevel.internalWinId()); + QVERIFY(container->internalWinId()); + QVERIFY(!child->internalWinId()); + QImage image = child->grabFramebuffer(); + QCOMPARE(image.width(), child->width()); + QCOMPARE(image.height(), child->height()); + QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); + } + + if (oldHandler) { + qInstallMessageHandler(oldHandler); + oldHandler = nullptr; + } } static inline QString msgRgbMismatch(unsigned actual, unsigned expected) @@ -533,50 +788,59 @@ static QPixmap grabWidgetWithoutRepaint(const QWidget *widget, QRect clipArea) bool verifyColor(const QWidget *widget, const QRect &clipArea, const QColor &color, int callerLine) { - for (int t = 0; t < 6; t++) { - const QPixmap pixmap = grabWidgetWithoutRepaint(widget, clipArea); - if (!QTest::qCompare(pixmap.size(), - clipArea.size(), - "pixmap.size()", - "rect.size()", - __FILE__, - callerLine)) - return false; - - - const QImage image = pixmap.toImage(); - QPixmap expectedPixmap(pixmap); /* ensure equal formats */ - expectedPixmap.detach(); - expectedPixmap.fill(color); - + // Create a comparison target image + QPixmap expectedPixmap(grabWidgetWithoutRepaint(widget, clipArea)); /* ensure equal formats */ + expectedPixmap.detach(); + expectedPixmap.fill(color); + const QImage expectedImage = expectedPixmap.toImage(); + + // test image size + QPixmap pixmap; + auto testSize = [&](){ + pixmap = grabWidgetWithoutRepaint(widget, clipArea); + return pixmap.size() == clipArea.size(); + }; + + // test the first pixel's color + uint firstPixel; + auto testPixel = [&](){ + const QImage image = grabWidgetWithoutRepaint(widget, clipArea).toImage(); uint alphaCorrection = image.format() == QImage::Format_RGB32 ? 0xff000000 : 0; - uint firstPixel = image.pixel(0,0) | alphaCorrection; - - // Retry a couple of times. Some window managers have transparency animation, or are - // just slow to render. - if (t < 5) { - if (firstPixel == QColor(color).rgb() - && image == expectedPixmap.toImage()) - return true; - else - QTest::qWait(200); - } else { - if (!QTest::qVerify(firstPixel == QColor(color).rgb(), - "firstPixel == QColor(color).rgb()", - qPrintable(msgRgbMismatch(firstPixel, QColor(color).rgb())), - __FILE__, callerLine)) { - return false; - } - if (!QTest::qVerify(image == expectedPixmap.toImage(), - "image == expectedPixmap.toImage()", - "grabbed pixmap differs from expected pixmap", - __FILE__, callerLine)) { - return false; - } - } + firstPixel = image.pixel(0,0) | alphaCorrection; + return firstPixel == QColor(color).rgb(); + }; + + // test the rendered image + QImage image; + auto testImage = [&](){ + image = grabWidgetWithoutRepaint(widget, clipArea).toImage(); + return image == expectedImage; + }; + + // Perform checks and make test case fail if unsuccessful + if (!QTest::qWaitFor(testSize)) + return QTest::qCompare(pixmap.size(), + clipArea.size(), + "pixmap.size()", + "rect.size()", + __FILE__, + callerLine); + + if (!QTest::qWaitFor(testPixel)) { + return QTest::qVerify(firstPixel == QColor(color).rgb(), + "firstPixel == QColor(color).rgb()", + qPrintable(msgRgbMismatch(firstPixel, QColor(color).rgb())), + __FILE__, callerLine); } - return false; + if (!QTest::qWaitFor(testImage)) { + return QTest::qVerify(image == expectedImage, + "image == expectedPixmap.toImage()", + "grabbed pixmap differs from expected pixmap", + __FILE__, callerLine); + } + + return true; } void tst_QOpenGLWidget::stackWidgetOpaqueChildIsVisible() @@ -585,8 +849,11 @@ void tst_QOpenGLWidget::stackWidgetOpaqueChildIsVisible() QSKIP("QScreen::grabWindow() doesn't work properly on OSX HighDPI screen: QTBUG-46803"); return; #endif - if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) - QSKIP("Wayland: This fails. Figure out why."); +#ifdef Q_OS_ANDROID + QSKIP("Crashes on Android, figure out why (QTBUG-102043)"); +#endif + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("Platform does not support window activation"); if (QGuiApplication::platformName().startsWith(QLatin1String("offscreen"), Qt::CaseInsensitive)) QSKIP("Offscreen: This fails."); @@ -606,11 +873,12 @@ void tst_QOpenGLWidget::stackWidgetOpaqueChildIsVisible() stack.resize(dimensionSize, dimensionSize); stack.show(); QVERIFY(QTest::qWaitForWindowExposed(&stack)); + stack.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&stack)); // Switch to the QOpenGLWidget. stack.setCurrentIndex(1); - QTRY_COMPARE(clearWidget->m_paintCalled, true); + QTRY_VERIFY(clearWidget->m_paintCalled); // Resize the tested region to be half size in the middle, because some OSes make the widget // have rounded corners (e.g. OSX), and the grabbed window pixmap will not coincide perfectly @@ -671,6 +939,9 @@ void tst_QOpenGLWidget::offscreen() void tst_QOpenGLWidget::offscreenThenOnscreen() { +#ifdef Q_OS_ANDROID + QSKIP("Crashes on Android, figure out why (QTBUG-102043)"); +#endif QScopedPointer<ClearWidget> w(new ClearWidget(0, 800, 600)); w->resize(800, 600); @@ -694,6 +965,57 @@ void tst_QOpenGLWidget::offscreenThenOnscreen() QVERIFY(image.pixel(30, 40) == qRgb(0, 0, 255)); } +void tst_QOpenGLWidget::paintWhileHidden() +{ +#ifdef Q_OS_ANDROID + QSKIP("Crashes on Android, figure out why (QTBUG-102043)"); +#endif + QScopedPointer<QWidget> tlw(new QWidget); + tlw->resize(640, 480); + + ClearWidget *w = new ClearWidget(0, 640, 480); + w->setParent(tlw.data()); + w->setClearColor(0, 0, 1); + + tlw->show(); + QVERIFY(QTest::qWaitForWindowExposed(tlw.data())); + + // QTBUG-101620: Now make visible=false and call update and see if we get to + // paintEvent/paintGL eventually, to ensure the updating of the texture is + // not optimized permanently away even though there is no composition + // on-screen at the point when update() is called. + + w->setVisible(false); + w->m_paintCalled = false; + w->update(); + w->setVisible(true); + QTRY_VERIFY(w->m_paintCalled); +} + +void tst_QOpenGLWidget::widgetWindowColorFormat_data() +{ + QTest::addColumn<bool>("translucent"); + QTest::newRow("Translucent background disabled") << false; + QTest::newRow("Translucent background enabled") << true; +} + +void tst_QOpenGLWidget::widgetWindowColorFormat() +{ + QFETCH(bool, translucent); + + QOpenGLWidget w; + w.setAttribute(Qt::WA_TranslucentBackground, translucent); + w.setWindowTitle(QLatin1String(QTest::currentTestFunction())); + w.setFixedSize(16, 16); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + QCOMPARE(w.format().redBufferSize(), ctx->format().redBufferSize()); + QCOMPARE(w.format().greenBufferSize(), ctx->format().greenBufferSize()); + QCOMPARE(w.format().blueBufferSize(), ctx->format().blueBufferSize()); +} + class StaticTextPainterWidget : public QOpenGLWidget { public: diff --git a/tests/auto/widgets/widgets/qplaintextedit/BLACKLIST b/tests/auto/widgets/widgets/qplaintextedit/BLACKLIST deleted file mode 100644 index b71f2fcc0a..0000000000 --- a/tests/auto/widgets/widgets/qplaintextedit/BLACKLIST +++ /dev/null @@ -1,15 +0,0 @@ -# QTBUG-87423 -[copyAvailable] -android -[adjustScrollbars] -android -# QTBUG-89402 -[undoAvailableAfterPaste] -android -[copyAndSelectAllInReadonly] -android -[canPaste] -android -[updateCursorPositionAfterEdit] -android - diff --git a/tests/auto/widgets/widgets/qplaintextedit/CMakeLists.txt b/tests/auto/widgets/widgets/qplaintextedit/CMakeLists.txt index a339b158c2..b7528498cb 100644 --- a/tests/auto/widgets/widgets/qplaintextedit/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qplaintextedit/CMakeLists.txt @@ -1,15 +1,22 @@ -# Generated from qplaintextedit.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qplaintextedit Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qplaintextedit LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qplaintextedit SOURCES tst_qplaintextedit.cpp INCLUDE_DIRECTORIES .. - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::GuiPrivate Qt::Widgets @@ -20,6 +27,6 @@ qt_internal_add_test(tst_qplaintextedit ##################################################################### qt_internal_extend_target(tst_qplaintextedit CONDITION MACOS - PUBLIC_LIBRARIES + LIBRARIES ${FWAppKit} ) diff --git a/tests/auto/widgets/widgets/qplaintextedit/tst_qplaintextedit.cpp b/tests/auto/widgets/widgets/qplaintextedit/tst_qplaintextedit.cpp index 297eb5ec51..ca7cc6d4b4 100644 --- a/tests/auto/widgets/widgets/qplaintextedit/tst_qplaintextedit.cpp +++ b/tests/auto/widgets/widgets/qplaintextedit/tst_qplaintextedit.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -38,6 +13,7 @@ #include <qclipboard.h> #include <qtextbrowser.h> #include <private/qwidgettextcontrol_p.h> +#include <private/qplaintextedit_p.h> #include <qscrollbar.h> #include <qtextobject.h> #include <qmenu.h> @@ -143,7 +119,7 @@ private slots: void layoutAfterMultiLineRemove(); void undoCommandRemovesAndReinsertsBlock(); void taskQTBUG_43562_lineCountCrash(); -#ifndef QT_NO_CONTEXTMENU +#if !defined(QT_NO_CONTEXTMENU) && !defined(QT_NO_CLIPBOARD) void contextMenu(); #endif void inputMethodCursorRect(); @@ -154,6 +130,10 @@ private slots: void updateCursorPositionAfterEdit(); #endif void appendTextWhenInvisible(); + void placeholderVisibility_data(); + void placeholderVisibility(); + void scrollBarSignals(); + void dontCrashWithCss(); private: void createSelection(); @@ -415,7 +395,7 @@ void tst_QPlainTextEdit::cursorPositionChanged() spy.clear(); QTest::keyClick(ed, Qt::Key_A); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QTextCursor cursor = ed->textCursor(); cursor.movePosition(QTextCursor::Start); @@ -423,23 +403,23 @@ void tst_QPlainTextEdit::cursorPositionChanged() cursor.movePosition(QTextCursor::End); spy.clear(); cursor.insertText("Test"); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); cursor.movePosition(QTextCursor::End); ed->setTextCursor(cursor); cursor.movePosition(QTextCursor::Start); spy.clear(); cursor.insertText("Test"); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); QTest::keyClick(ed, Qt::Key_Left); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); CursorPositionChangedRecorder spy2(ed); QVERIFY(ed->textCursor().position() > 0); ed->setPlainText("Hello World"); - QCOMPARE(spy2.cursorPositions.count(), 1); + QCOMPARE(spy2.cursorPositions.size(), 1); QCOMPARE(spy2.cursorPositions.at(0), 0); QCOMPARE(ed->textCursor().position(), 0); } @@ -456,7 +436,7 @@ void tst_QPlainTextEdit::setTextCursor() spy.clear(); ed->setTextCursor(cursor); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } #ifndef QT_NO_CLIPBOARD @@ -473,7 +453,7 @@ void tst_QPlainTextEdit::undoAvailableAfterPaste() const QString txt("Test"); QApplication::clipboard()->setText(txt); ed->paste(); - QVERIFY(spy.count() >= 1); + QVERIFY(spy.size() >= 1); QCOMPARE(ed->toPlainText(), txt); } #endif @@ -727,16 +707,16 @@ void tst_QPlainTextEdit::noPropertiesOnDefaultTextEditCharFormat() // on a text edit. Font properties instead should be taken from the // widget's font (in sync with defaultFont property in document) and the // foreground color should be taken from the palette. - QCOMPARE(ed->textCursor().charFormat().properties().count(), 0); + QCOMPARE(ed->textCursor().charFormat().properties().size(), 0); } void tst_QPlainTextEdit::setPlainTextShouldEmitTextChangedOnce() { QSignalSpy spy(ed, SIGNAL(textChanged())); ed->setPlainText("Yankee Doodle"); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); ed->setPlainText(""); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); } void tst_QPlainTextEdit::overwriteMode() @@ -1016,7 +996,7 @@ void tst_QPlainTextEdit::copyAvailable_data() //Tests the copyAvailable slot for several cases void tst_QPlainTextEdit::copyAvailable() { - QFETCH(pairListType,keystrokes); + QFETCH(const pairListType, keystrokes); QFETCH(QList<bool>, copyAvailable); QFETCH(QString, function); @@ -1029,9 +1009,8 @@ void tst_QPlainTextEdit::copyAvailable() QSignalSpy spyCopyAvailabe(ed, SIGNAL(copyAvailable(bool))); //Execute Keystrokes - foreach(keyPairType keyPair, keystrokes) { + for (keyPairType keyPair : keystrokes) QTest::keyClick(ed, keyPair.first, keyPair.second ); - } //Execute ed->"function" if (function == "cut") @@ -1048,8 +1027,8 @@ void tst_QPlainTextEdit::copyAvailable() //Compare spied signals QEXPECT_FAIL("Case7 T,A,A, <- + shift, <- + shift, <- + shift, ctrl + x, undo() | signals: true, false, true", "Wrong undo selection behaviour. Should be fixed in some future release. (See task: 132482)", Abort); - QCOMPARE(spyCopyAvailabe.count(), copyAvailable.count()); - for (int i=0;i<spyCopyAvailabe.count(); i++) { + QCOMPARE(spyCopyAvailabe.size(), copyAvailable.size()); + for (int i=0;i<spyCopyAvailabe.size(); i++) { QVariant variantSpyCopyAvailable = spyCopyAvailabe.at(i).at(0); QVERIFY2(variantSpyCopyAvailable.toBool() == copyAvailable.at(i), QString("Spied singnal: %1").arg(i).toLatin1()); } @@ -1085,10 +1064,10 @@ void tst_QPlainTextEdit::moveCursor() QCOMPARE(ed->textCursor().position(), 0); ed->moveCursor(QTextCursor::NextCharacter); QCOMPARE(ed->textCursor().position(), 1); - QCOMPARE(cursorMovedSpy.count(), 1); + QCOMPARE(cursorMovedSpy.size(), 1); ed->moveCursor(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); QCOMPARE(ed->textCursor().position(), 2); - QCOMPARE(cursorMovedSpy.count(), 2); + QCOMPARE(cursorMovedSpy.size(), 2); QCOMPARE(ed->textCursor().selectedText(), QString("e")); } @@ -1300,7 +1279,7 @@ void tst_QPlainTextEdit::ensureVisibleWithRtl() ed->setLayoutDirection(Qt::RightToLeft); ed->setLineWrapMode(QPlainTextEdit::NoWrap); QString txt(500, QChar(QLatin1Char('a'))); - QCOMPARE(txt.length(), 500); + QCOMPARE(txt.size(), 500); ed->setPlainText(txt); ed->resize(100, 100); ed->show(); @@ -1353,7 +1332,7 @@ void tst_QPlainTextEdit::extraSelections() ed->setExtraSelections(QList<QTextEdit::ExtraSelection>() << sel); QList<QTextEdit::ExtraSelection> selections = ed->extraSelections(); - QCOMPARE(selections.count(), 1); + QCOMPARE(selections.size(), 1); QCOMPARE(selections.at(0).cursor.position(), endPos); QCOMPARE(selections.at(0).cursor.anchor(), wordPos); } @@ -1368,7 +1347,9 @@ void tst_QPlainTextEdit::adjustScrollbars() ed->setFont(ff); ed->setMinimumSize(140, 100); ed->setMaximumSize(140, 100); - ed->show(); + // We use showNormal() here, because otherwise on Android the widget will + // be shown fullscreen, and the scrollbar will not appear. + ed->showNormal(); QLatin1String txt("\nabc def ghi jkl mno pqr stu vwx"); ed->setPlainText(txt + txt + txt + txt); @@ -1481,44 +1462,44 @@ void tst_QPlainTextEdit::selectionChanged() QTest::keyClick(ed, Qt::Key_Right); QCOMPARE(ed->textCursor().position(), 1); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); QTest::keyClick(ed, Qt::Key_Right, Qt::ShiftModifier); QCOMPARE(ed->textCursor().position(), 2); - QCOMPARE(selectionChangedSpy.count(), 1); + QCOMPARE(selectionChangedSpy.size(), 1); QTest::keyClick(ed, Qt::Key_Right, Qt::ShiftModifier); QCOMPARE(ed->textCursor().position(), 3); - QCOMPARE(selectionChangedSpy.count(), 2); + QCOMPARE(selectionChangedSpy.size(), 2); QTest::keyClick(ed, Qt::Key_Right, Qt::ShiftModifier); QCOMPARE(ed->textCursor().position(), 4); - QCOMPARE(selectionChangedSpy.count(), 3); + QCOMPARE(selectionChangedSpy.size(), 3); QTest::keyClick(ed, Qt::Key_Right); QCOMPARE(ed->textCursor().position(), 4); - QCOMPARE(selectionChangedSpy.count(), 4); + QCOMPARE(selectionChangedSpy.size(), 4); QTest::keyClick(ed, Qt::Key_Right); QCOMPARE(ed->textCursor().position(), 5); - QCOMPARE(selectionChangedSpy.count(), 4); + QCOMPARE(selectionChangedSpy.size(), 4); } void tst_QPlainTextEdit::blockCountChanged() { QSignalSpy blockCountCpangedSpy(ed, SIGNAL(blockCountChanged(int))); ed->setPlainText("Hello"); - QCOMPARE(blockCountCpangedSpy.count(), 0); + QCOMPARE(blockCountCpangedSpy.size(), 0); ed->setPlainText("Hello World"); - QCOMPARE(blockCountCpangedSpy.count(), 0); + QCOMPARE(blockCountCpangedSpy.size(), 0); ed->setPlainText("Hello \n World \n this \n has \n more \n blocks \n than \n just \n one"); - QCOMPARE(blockCountCpangedSpy.count(), 1); + QCOMPARE(blockCountCpangedSpy.size(), 1); ed->setPlainText("One"); - QCOMPARE(blockCountCpangedSpy.count(), 2); + QCOMPARE(blockCountCpangedSpy.size(), 2); ed->setPlainText("One \n Two"); - QCOMPARE(blockCountCpangedSpy.count(), 3); + QCOMPARE(blockCountCpangedSpy.size(), 3); ed->setPlainText("Three \n Four"); - QCOMPARE(blockCountCpangedSpy.count(), 3); + QCOMPARE(blockCountCpangedSpy.size(), 3); } @@ -1696,7 +1677,7 @@ void tst_QPlainTextEdit::taskQTBUG_43562_lineCountCrash() disconnect(ed->document(), SIGNAL(contentsChange(int, int, int)), 0, 0); } -#ifndef QT_NO_CONTEXTMENU +#if !defined(QT_NO_CONTEXTMENU) && !defined(QT_NO_CLIPBOARD) void tst_QPlainTextEdit::contextMenu() { ed->appendHtml(QStringLiteral("Hello <a href='http://www.qt.io'>Qt</a>")); @@ -1719,7 +1700,7 @@ void tst_QPlainTextEdit::contextMenu() QVERIFY(!ed->findChild<QAction *>(QStringLiteral("link-copy"))); QTextCursor cursor = ed->textCursor(); - cursor.setPosition(ed->toPlainText().length() - 2); + cursor.setPosition(ed->toPlainText().size() - 2); ed->setTextCursor(cursor); menu = ed->createStandardContextMenu(ed->cursorRect().center()); @@ -1730,7 +1711,7 @@ void tst_QPlainTextEdit::contextMenu() delete menu; QVERIFY(!ed->findChild<QAction *>(QStringLiteral("link-copy"))); } -#endif // QT_NO_CONTEXTMENU +#endif // QT_NO_CONTEXTMENU && QT_NO_CLIPBOARD // QTBUG-51923: Verify that the cursor rectangle returned by the input // method query correctly reflects the viewport offset. @@ -1802,7 +1783,7 @@ void tst_QPlainTextEdit::updateCursorPositionAfterEdit() QTest::keyClick(&plaintextEdit, Qt::Key_Up); // The curser should move back to the end of the copied text - QCOMPARE(plaintextEdit.textCursor().position(), initialPosition + txt.length()); + QCOMPARE(plaintextEdit.textCursor().position(), initialPosition + txt.size()); } #endif @@ -1837,5 +1818,141 @@ void tst_QPlainTextEdit::appendTextWhenInvisible() QCOMPARE(maxAfterAppend, maxAfterSet); } +enum SetupCommand { + ClearPlaceHolder, // set empty placeholder text + SetPlaceHolder, // set a non-empty placeholder text + ClearContent, // set empty text as content + SetContent // set non-empty text as content +}; + +void tst_QPlainTextEdit::placeholderVisibility_data() +{ + QTest::addColumn<QList<SetupCommand>>("setupCommands"); + QTest::addColumn<bool>("placeholderVisible"); + QTest::addRow("no placeholder set + no text set") + << QList<SetupCommand>{} << false; + QTest::addRow("no placeholder set + text set or text set + no placeholder set") + << QList<SetupCommand>{ SetContent } << false; + QTest::addRow("no placeholder set + text set + empty text set") + << QList<SetupCommand>{ SetContent , ClearContent } + << false; + QTest::addRow("no placeholder set + empty text set + text set") + << QList<SetupCommand>{ ClearContent, SetContent } + << false; + QTest::addRow("empty placeholder set + no text set") + << QList<SetupCommand>{ ClearPlaceHolder } << false; + QTest::addRow("empty placeholder set + text set") + << QList<SetupCommand>{ ClearPlaceHolder, SetContent } + << false; + QTest::addRow("empty placeholder set + text set + empty text set") + << QList<SetupCommand>{ ClearPlaceHolder, SetContent, ClearContent } + << false; + QTest::addRow("empty placeholder set + empty text set + text set") + << QList<SetupCommand>{ ClearPlaceHolder, ClearContent, SetContent } + << false; + QTest::addRow("placeholder set + no text set") + << QList<SetupCommand>{ SetPlaceHolder, ClearContent } + << true; + QTest::addRow("placeholder set + text set") + << QList<SetupCommand>{ SetPlaceHolder, SetContent } + << false; + QTest::addRow("placeholder set + text set + empty text set") + << QList<SetupCommand>{ SetPlaceHolder, SetContent, ClearContent } + << true; + QTest::addRow("placeholder set + empty text set + text set") + << QList<SetupCommand>{ SetPlaceHolder, ClearContent, SetContent } + << false; + QTest::addRow("placeholder set + text set + empty placeholder set") + << QList<SetupCommand>{ SetPlaceHolder, SetContent, ClearPlaceHolder} + << false; + QTest::addRow("placeholder set + empty placeholder set + text set") + << QList<SetupCommand>{ SetPlaceHolder, ClearPlaceHolder, SetContent } + << false; + QTest::addRow("placeholder set + empty placeholder set + empty text set") + << QList<SetupCommand>{ SetPlaceHolder, ClearPlaceHolder, ClearContent } + << false; + QTest::addRow("placeholder set + empty text set + empty placeholder set") + << QList<SetupCommand>{ SetPlaceHolder, ClearContent, ClearPlaceHolder } + << false; + QTest::addRow("text set + no placeholder set + empty text set") + << QList<SetupCommand>{ SetContent, ClearContent } + << false; + QTest::addRow("text set + empty placeholder set") + << QList<SetupCommand>{ SetContent, ClearPlaceHolder } + << false; + QTest::addRow("text set + placeholder set") + << QList<SetupCommand>{ SetContent, SetPlaceHolder } + << false; + QTest::addRow("text set + placeholder set + empty text set") + << QList<SetupCommand>{ SetContent, SetPlaceHolder, ClearContent } + << true; + QTest::addRow("text set + placeholder set + empty placeholder set") + << QList<SetupCommand>{ SetContent, SetPlaceHolder, ClearPlaceHolder } + << false; +} + +void tst_QPlainTextEdit::placeholderVisibility() +{ + QFETCH(QList<SetupCommand>, setupCommands); + QFETCH(bool, placeholderVisible); + + QPlainTextEdit plainTextEdit; + for (auto command : setupCommands) { + switch (command) { + case ClearPlaceHolder: + plainTextEdit.setPlaceholderText(""); + break; + case SetPlaceHolder: + plainTextEdit.setPlaceholderText("Qt is awesome !"); + break; + case ClearContent: + plainTextEdit.setPlainText(""); + break; + case SetContent: + plainTextEdit.setPlainText("PlainText..."); + break; + } + } + auto *plainTextEdit_d = static_cast<QPlainTextEditPrivate *>(qt_widget_private(&plainTextEdit)); + + plainTextEdit.show(); + QVERIFY(QTest::qWaitForWindowExposed(&plainTextEdit)); + QTRY_COMPARE(plainTextEdit_d->placeholderTextShown, placeholderVisible); +} + + +void tst_QPlainTextEdit::scrollBarSignals() +{ + QPlainTextEdit plainTextEdit; + QString longText; + for (uint i = 0; i < 500; ++i) + longText += "This is going to be a very long text for scroll signal testing.\n"; + plainTextEdit.setPlainText(longText); + QScrollBar *vbar = plainTextEdit.verticalScrollBar(); + plainTextEdit.show(); + QVERIFY(QTest::qWaitForWindowExposed(&plainTextEdit)); + QSignalSpy spy(vbar, &QScrollBar::valueChanged); + + QTest::keyClick(vbar, Qt::Key_Down); + QTRY_COMPARE(spy.count(), 1); + QTest::keyClick(vbar, Qt::Key_PageDown); + QTRY_COMPARE(spy.count(), 2); + QTest::keyClick(vbar, Qt::Key_PageDown); + QTRY_COMPARE(spy.count(), 3); + QTest::keyClick(vbar, Qt::Key_Up); + QTRY_COMPARE(spy.count(), 4); + QTest::keyClick(vbar, Qt::Key_PageUp); + QTRY_COMPARE(spy.count(), 5); +} + +void tst_QPlainTextEdit::dontCrashWithCss() +{ + qApp->setStyleSheet("QWidget { font: 10pt; }"); + QPlainTextEdit edit; + edit.show(); + qApp->setStyleSheet(QString()); +} + + QTEST_MAIN(tst_QPlainTextEdit) #include "tst_qplaintextedit.moc" diff --git a/tests/auto/widgets/widgets/qprogressbar/CMakeLists.txt b/tests/auto/widgets/widgets/qprogressbar/CMakeLists.txt index 699ba0a2f1..61ce6c2692 100644 --- a/tests/auto/widgets/widgets/qprogressbar/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qprogressbar/CMakeLists.txt @@ -1,13 +1,21 @@ -# Generated from qprogressbar.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qprogressbar Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qprogressbar LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qprogressbar SOURCES tst_qprogressbar.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/widgets/qprogressbar/tst_qprogressbar.cpp b/tests/auto/widgets/widgets/qprogressbar/tst_qprogressbar.cpp index 5c08ac4e3e..4a90ed6667 100644 --- a/tests/auto/widgets/widgets/qprogressbar/tst_qprogressbar.cpp +++ b/tests/auto/widgets/widgets/qprogressbar/tst_qprogressbar.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -36,6 +11,8 @@ #include <qtimer.h> #include <QStyleFactory> +#include <QtWidgets/private/qapplication_p.h> + class tst_QProgressBar : public QObject { Q_OBJECT @@ -248,7 +225,7 @@ void tst_QProgressBar::setMinMaxRepaint() pbar.setFormat("%v"); pbar.move(300, 300); pbar.show(); - qApp->setActiveWindow(&pbar); + QApplicationPrivate::setActiveWindow(&pbar); QVERIFY(QTest::qWaitForWindowActive(&pbar)); // No repaint when setting minimum to the current minimum diff --git a/tests/auto/widgets/widgets/qpushbutton/BLACKLIST b/tests/auto/widgets/widgets/qpushbutton/BLACKLIST deleted file mode 100644 index 9fddf5abda..0000000000 --- a/tests/auto/widgets/widgets/qpushbutton/BLACKLIST +++ /dev/null @@ -1,3 +0,0 @@ -# QTBUG-87426 -[taskQTBUG_20191_shortcutWithKeypadModifer] -android diff --git a/tests/auto/widgets/widgets/qpushbutton/CMakeLists.txt b/tests/auto/widgets/widgets/qpushbutton/CMakeLists.txt index 0ec5c1fe09..afd052fa17 100644 --- a/tests/auto/widgets/widgets/qpushbutton/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qpushbutton/CMakeLists.txt @@ -1,13 +1,22 @@ -# Generated from qpushbutton.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qpushbutton Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qpushbutton LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qpushbutton SOURCES tst_qpushbutton.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui + Qt::GuiPrivate Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/widgets/qpushbutton/testdata/setEnabled/disabled_Windows_win32_data0.qsnap b/tests/auto/widgets/widgets/qpushbutton/testdata/setEnabled/disabled_Windows_win32_data0.qsnap Binary files differdeleted file mode 100644 index 8c2c08aee9..0000000000 --- a/tests/auto/widgets/widgets/qpushbutton/testdata/setEnabled/disabled_Windows_win32_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qpushbutton/testdata/setEnabled/enabled_Motif_data0.qsnap b/tests/auto/widgets/widgets/qpushbutton/testdata/setEnabled/enabled_Motif_data0.qsnap Binary files differdeleted file mode 100644 index 3c455887da..0000000000 --- a/tests/auto/widgets/widgets/qpushbutton/testdata/setEnabled/enabled_Motif_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qpushbutton/testdata/setEnabled/enabled_Windows_data0.qsnap b/tests/auto/widgets/widgets/qpushbutton/testdata/setEnabled/enabled_Windows_data0.qsnap Binary files differdeleted file mode 100644 index e655b09d29..0000000000 --- a/tests/auto/widgets/widgets/qpushbutton/testdata/setEnabled/enabled_Windows_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qpushbutton/testdata/setEnabled/enabled_Windows_win32_data0.qsnap b/tests/auto/widgets/widgets/qpushbutton/testdata/setEnabled/enabled_Windows_win32_data0.qsnap Binary files differdeleted file mode 100644 index 8f59499d72..0000000000 --- a/tests/auto/widgets/widgets/qpushbutton/testdata/setEnabled/enabled_Windows_win32_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qpushbutton/testdata/setPixmap/Vpix_Motif_data0.qsnap b/tests/auto/widgets/widgets/qpushbutton/testdata/setPixmap/Vpix_Motif_data0.qsnap Binary files differdeleted file mode 100644 index a6967a17f7..0000000000 --- a/tests/auto/widgets/widgets/qpushbutton/testdata/setPixmap/Vpix_Motif_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qpushbutton/testdata/setPixmap/Vpix_Windows_data0.qsnap b/tests/auto/widgets/widgets/qpushbutton/testdata/setPixmap/Vpix_Windows_data0.qsnap Binary files differdeleted file mode 100644 index d7c721c960..0000000000 --- a/tests/auto/widgets/widgets/qpushbutton/testdata/setPixmap/Vpix_Windows_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qpushbutton/testdata/setPixmap/Vpix_Windows_win32_data0.qsnap b/tests/auto/widgets/widgets/qpushbutton/testdata/setPixmap/Vpix_Windows_win32_data0.qsnap Binary files differdeleted file mode 100644 index ae0261a22c..0000000000 --- a/tests/auto/widgets/widgets/qpushbutton/testdata/setPixmap/Vpix_Windows_win32_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qpushbutton/testdata/setText/simple_Motif_data0.qsnap b/tests/auto/widgets/widgets/qpushbutton/testdata/setText/simple_Motif_data0.qsnap Binary files differdeleted file mode 100644 index 039bdce748..0000000000 --- a/tests/auto/widgets/widgets/qpushbutton/testdata/setText/simple_Motif_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qpushbutton/testdata/setText/simple_Windows_data0.qsnap b/tests/auto/widgets/widgets/qpushbutton/testdata/setText/simple_Windows_data0.qsnap Binary files differdeleted file mode 100644 index db40dc4726..0000000000 --- a/tests/auto/widgets/widgets/qpushbutton/testdata/setText/simple_Windows_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qpushbutton/testdata/setText/simple_Windows_win32_data0.qsnap b/tests/auto/widgets/widgets/qpushbutton/testdata/setText/simple_Windows_win32_data0.qsnap Binary files differdeleted file mode 100644 index c0e1279f46..0000000000 --- a/tests/auto/widgets/widgets/qpushbutton/testdata/setText/simple_Windows_win32_data0.qsnap +++ /dev/null diff --git a/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp b/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp index f8464e2383..92ce1f5419 100644 --- a/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp +++ b/tests/auto/widgets/widgets/qpushbutton/tst_qpushbutton.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -40,6 +15,12 @@ #include <QGridLayout> #include <QStyleFactory> #include <QTabWidget> +#include <QStyleOption> + +#include <private/qguiapplication_p.h> +#include <qpa/qplatformtheme.h> + +#include <QtWidgets/private/qapplication_p.h> class tst_QPushButton : public QObject { @@ -60,7 +41,6 @@ private slots: void setDown(); void popupCrash(); void isChecked(); - void animateClick(); void toggle(); void clicked(); void touchTap(); @@ -73,20 +53,22 @@ private slots: #endif void emitReleasedAfterChange(); void hitButton(); + void iconOnlyStyleSheet(); + void mousePressAndMove(); + void reactToMenuClosed(); protected slots: void resetCounters(); void onClicked(); - void onToggled( bool on ); + void onToggled(bool on); void onPressed(); void onReleased(); - void helperSlotDelete(); private: - uint click_count; - uint toggle_count; - uint press_count; - uint release_count; + int click_count; + int toggle_count; + int press_count; + int release_count; QPushButton *testWidget; QPointingDevice *m_touchScreen = QTest::createTouchDevice(); @@ -101,40 +83,40 @@ void tst_QPushButton::getSetCheck() QMenu *var1 = new QMenu; obj1.setMenu(var1); QCOMPARE(var1, obj1.menu()); - obj1.setMenu((QMenu *)0); - QCOMPARE((QMenu *)0, obj1.menu()); + obj1.setMenu(nullptr); + QCOMPARE(obj1.menu(), nullptr); delete var1; } void tst_QPushButton::initTestCase() { // Create the test class - testWidget = new QPushButton( "&Start", 0 ); + testWidget = new QPushButton("&Start", 0); testWidget->setObjectName("testWidget"); - testWidget->resize( 200, 200 ); + testWidget->resize(200, 200); testWidget->show(); - connect( testWidget, SIGNAL(clicked()), this, SLOT(onClicked()) ); - connect( testWidget, SIGNAL(pressed()), this, SLOT(onPressed()) ); - connect( testWidget, SIGNAL(released()), this, SLOT(onReleased()) ); - connect( testWidget, SIGNAL(toggled(bool)), this, SLOT(onToggled(bool)) ); + connect(testWidget, SIGNAL(clicked()), this, SLOT(onClicked())); + connect(testWidget, SIGNAL(pressed()), this, SLOT(onPressed())); + connect(testWidget, SIGNAL(released()), this, SLOT(onReleased())); + connect(testWidget, SIGNAL(toggled(bool)), this, SLOT(onToggled(bool))); } void tst_QPushButton::cleanupTestCase() { delete testWidget; - testWidget = 0; + testWidget = nullptr; } void tst_QPushButton::init() { - testWidget->setAutoRepeat( false ); - testWidget->setDown( false ); + testWidget->setAutoRepeat(false); + testWidget->setDown(false); testWidget->setText("Test"); - testWidget->setEnabled( true ); + testWidget->setEnabled(true); #if QT_CONFIG(shortcut) QKeySequence seq; - testWidget->setShortcut( seq ); + testWidget->setShortcut(seq); #endif resetCounters(); @@ -153,7 +135,7 @@ void tst_QPushButton::onClicked() click_count++; } -void tst_QPushButton::onToggled( bool /*on*/ ) +void tst_QPushButton::onToggled(bool /*on*/) { toggle_count++; } @@ -171,46 +153,46 @@ void tst_QPushButton::onReleased() void tst_QPushButton::autoRepeat() { // If this changes, this test must be completely revised. - QVERIFY( !testWidget->isCheckable() ); + QVERIFY(!testWidget->isCheckable()); // verify autorepeat is off by default. - QPushButton tmp( 0 ); + QPushButton tmp; tmp.setObjectName("tmp"); - QVERIFY( !tmp.autoRepeat() ); + QVERIFY(!tmp.autoRepeat()); // check if we can toggle the mode - testWidget->setAutoRepeat( true ); - QVERIFY( testWidget->autoRepeat() ); + testWidget->setAutoRepeat(true); + QVERIFY(testWidget->autoRepeat()); - testWidget->setAutoRepeat( false ); - QVERIFY( !testWidget->autoRepeat() ); + testWidget->setAutoRepeat(false); + QVERIFY(!testWidget->autoRepeat()); resetCounters(); // check that the button is down if we press space and not in autorepeat - testWidget->setDown( false ); - testWidget->setAutoRepeat( false ); - QTest::keyPress( testWidget, Qt::Key_Space ); + testWidget->setDown(false); + testWidget->setAutoRepeat(false); + QTest::keyPress(testWidget, Qt::Key_Space); - QTRY_VERIFY( testWidget->isDown() ); - QVERIFY( toggle_count == 0 ); - QVERIFY( press_count == 1 ); - QVERIFY( release_count == 0 ); - QVERIFY( click_count == 0 ); + QTRY_VERIFY(testWidget->isDown()); + QCOMPARE(toggle_count, 0); + QCOMPARE(press_count, 1); + QCOMPARE(release_count, 0); + QCOMPARE(click_count, 0); - QTest::keyRelease( testWidget, Qt::Key_Space ); + QTest::keyRelease(testWidget, Qt::Key_Space); resetCounters(); // check that the button is down if we press space while in autorepeat // we can't actually confirm how many times it is fired, more than 1 is enough. - testWidget->setDown( false ); - testWidget->setAutoRepeat( true ); - QTest::keyPress( testWidget, Qt::Key_Space ); + testWidget->setDown(false); + testWidget->setAutoRepeat(true); + QTest::keyPress(testWidget, Qt::Key_Space); QTRY_VERIFY(press_count > 3); - QVERIFY( testWidget->isDown() ); - QVERIFY( toggle_count == 0 ); - QTest::keyRelease( testWidget, Qt::Key_Space ); + QVERIFY(testWidget->isDown()); + QCOMPARE(toggle_count, 0); + QTest::keyRelease(testWidget, Qt::Key_Space); QCOMPARE(press_count, release_count); QCOMPARE(release_count, click_count); @@ -218,113 +200,128 @@ void tst_QPushButton::autoRepeat() // check that pressing ENTER has no effect resetCounters(); - testWidget->setDown( false ); - testWidget->setAutoRepeat( false ); - QTest::keyPress( testWidget, Qt::Key_Enter ); + testWidget->setDown(false); + // Skip after reset if ButtonPressKeys has Key_Enter + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value<QList<Qt::Key>>(); + if (buttonPressKeys.contains(Qt::Key_Enter)) { + return; + } + testWidget->setAutoRepeat(false); + QTest::keyPress(testWidget, Qt::Key_Enter); - QTest::qWait( 300 ); + QTest::qWait(300); - QVERIFY( !testWidget->isDown() ); - QVERIFY( toggle_count == 0 ); - QVERIFY( press_count == 0 ); - QVERIFY( release_count == 0 ); - QVERIFY( click_count == 0 ); - QTest::keyRelease( testWidget, Qt::Key_Enter ); + QVERIFY(!testWidget->isDown()); + QCOMPARE(toggle_count, 0); + QCOMPARE(press_count, 0); + QCOMPARE(release_count, 0); + QCOMPARE(click_count, 0); + QTest::keyRelease(testWidget, Qt::Key_Enter); // check that pressing ENTER has no effect resetCounters(); - testWidget->setDown( false ); - testWidget->setAutoRepeat( true ); - QTest::keyClick( testWidget, Qt::Key_Enter ); - QTest::qWait( 300 ); - QVERIFY( !testWidget->isDown() ); - QVERIFY( toggle_count == 0 ); - QVERIFY( press_count == 0 ); - QVERIFY( release_count == 0 ); - QVERIFY( click_count == 0 ); + testWidget->setDown(false); + testWidget->setAutoRepeat(true); + QTest::keyClick(testWidget, Qt::Key_Enter); + QTest::qWait(300); + QVERIFY(!testWidget->isDown()); + QCOMPARE(toggle_count, 0); + QCOMPARE(press_count, 0); + QCOMPARE(release_count, 0); + QCOMPARE(click_count, 0); } void tst_QPushButton::pressed() { - QTest::keyPress( testWidget, ' ' ); - QCOMPARE( press_count, (uint)1 ); - QCOMPARE( release_count, (uint)0 ); - - QTest::keyRelease( testWidget, ' ' ); - QCOMPARE( press_count, (uint)1 ); - QCOMPARE( release_count, (uint)1 ); + QTest::keyPress(testWidget, ' '); + QCOMPARE(press_count, 1); + QCOMPARE(release_count, 0); + + QTest::keyRelease(testWidget, ' '); + QCOMPARE(press_count, 1); + QCOMPARE(release_count, 1); + + // Skip if ButtonPressKeys has Key_Enter + const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme() + ->themeHint(QPlatformTheme::ButtonPressKeys) + .value<QList<Qt::Key>>(); + if (buttonPressKeys.contains(Qt::Key_Enter)) { + return; + } - QTest::keyPress( testWidget,Qt::Key_Enter ); - QCOMPARE( press_count, (uint)1 ); - QCOMPARE( release_count, (uint)1 ); + QTest::keyPress(testWidget,Qt::Key_Enter); + QCOMPARE(press_count, 1); + QCOMPARE(release_count, 1); testWidget->setAutoDefault(true); - QTest::keyPress( testWidget,Qt::Key_Enter ); - QCOMPARE( press_count, (uint)2 ); - QCOMPARE( release_count, (uint)2 ); + QTest::keyPress(testWidget,Qt::Key_Enter); + QCOMPARE(press_count, 2); + QCOMPARE(release_count, 2); testWidget->setAutoDefault(false); } void tst_QPushButton::isCheckable() { - QVERIFY( !testWidget->isCheckable() ); + QVERIFY(!testWidget->isCheckable()); } void tst_QPushButton::setDown() { - testWidget->setDown( false ); - QVERIFY( !testWidget->isDown() ); + testWidget->setDown(false); + QVERIFY(!testWidget->isDown()); - testWidget->setDown( true ); - QVERIFY( testWidget->isDown() ); + testWidget->setDown(true); + QVERIFY(testWidget->isDown()); - testWidget->setDown( true ); - QTest::keyClick( testWidget, Qt::Key_Escape ); - QVERIFY( !testWidget->isDown() ); + testWidget->setDown(true); + QTest::keyClick(testWidget, Qt::Key_Escape); + QVERIFY(!testWidget->isDown()); } void tst_QPushButton::isChecked() { - testWidget->setDown( false ); - QVERIFY( !testWidget->isChecked() ); + testWidget->setDown(false); + QVERIFY(!testWidget->isChecked()); - testWidget->setDown( true ); - QVERIFY( !testWidget->isChecked() ); + testWidget->setDown(true); + QVERIFY(!testWidget->isChecked()); - testWidget->setDown( false ); + testWidget->setDown(false); testWidget->toggle(); - QVERIFY( testWidget->isChecked() == testWidget->isCheckable() ); + QCOMPARE(testWidget->isChecked(), testWidget->isCheckable()); } void tst_QPushButton::toggle() { // the pushbutton shouldn't toggle the button. testWidget->toggle(); - QVERIFY( testWidget->isChecked() == false ); + QCOMPARE(testWidget->isChecked(), false); } void tst_QPushButton::toggled() { // the pushbutton shouldn't send a toggled signal when we call the toggle slot. - QVERIFY( !testWidget->isCheckable() ); + QVERIFY(!testWidget->isCheckable()); testWidget->toggle(); - QVERIFY( toggle_count == 0 ); + QCOMPARE(toggle_count, 0); // do it again, just to be sure resetCounters(); testWidget->toggle(); - QVERIFY( toggle_count == 0 ); + QCOMPARE(toggle_count, 0); // finally check that we can toggle using the mouse resetCounters(); - QTest::mousePress( testWidget, Qt::LeftButton ); - QVERIFY( toggle_count == 0 ); - QVERIFY( click_count == 0 ); + QTest::mousePress(testWidget, Qt::LeftButton); + QCOMPARE(toggle_count, 0); + QCOMPARE(click_count, 0); - QTest::mouseRelease( testWidget, Qt::LeftButton ); - QVERIFY( click_count == 1 ); + QTest::mouseRelease(testWidget, Qt::LeftButton); + QCOMPARE(click_count, 1); } #if QT_CONFIG(shortcut) @@ -336,74 +333,57 @@ void tst_QPushButton::toggled() void tst_QPushButton::setAccel() { - if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) - QSKIP("Wayland: This fails. Figure out why."); - testWidget->setText("&AccelTest"); - QKeySequence seq( Qt::ALT | Qt::Key_A ); - testWidget->setShortcut( seq ); + QKeySequence seq(Qt::ALT | Qt::Key_A); + testWidget->setShortcut(seq); // The shortcut will not be activated unless the button is in a active // window and has focus - QApplication::setActiveWindow(testWidget); + QApplicationPrivate::setActiveWindow(testWidget); testWidget->setFocus(); QVERIFY(QTest::qWaitForWindowActive(testWidget)); - QTest::keyClick( testWidget, 'A', Qt::AltModifier ); - QTRY_VERIFY( click_count == 1 ); - QVERIFY( press_count == 1 ); - QVERIFY( release_count == 1 ); - QVERIFY( toggle_count == 0 ); + QTest::keyClick(testWidget, 'A', Qt::AltModifier); + QTRY_VERIFY(click_count == 1); + QCOMPARE(press_count, 1); + QCOMPARE(release_count, 1); + QCOMPARE(toggle_count, 0); // wait 200 ms because setAccel uses animateClick. // if we don't wait this may screw up a next test. QTest::qWait(200); - QTRY_VERIFY( !testWidget->isDown() ); + QTRY_VERIFY(!testWidget->isDown()); } #endif // QT_CONFIG(shortcut) -void tst_QPushButton::animateClick() -{ - QVERIFY( !testWidget->isDown() ); - testWidget->animateClick(); - QVERIFY( testWidget->isDown() ); - QTest::qWait( 200 ); - QVERIFY( !testWidget->isDown() ); - - QVERIFY( click_count == 1 ); - QVERIFY( press_count == 1 ); - QVERIFY( release_count == 1 ); - QVERIFY( toggle_count == 0 ); -} - void tst_QPushButton::clicked() { - QTest::mousePress( testWidget, Qt::LeftButton ); - QVERIFY( press_count == 1 ); - QVERIFY( release_count == 0 ); + QTest::mousePress(testWidget, Qt::LeftButton); + QCOMPARE(press_count, 1); + QCOMPARE(release_count, 0); - QTest::mouseRelease( testWidget, Qt::LeftButton ); - QCOMPARE( press_count, (uint)1 ); - QCOMPARE( release_count, (uint)1 ); + QTest::mouseRelease(testWidget, Qt::LeftButton); + QCOMPARE(press_count, 1); + QCOMPARE(release_count, 1); press_count = 0; release_count = 0; testWidget->setDown(false); for (uint i=0; i<10; i++) - QTest::mouseClick( testWidget, Qt::LeftButton ); - QCOMPARE( press_count, (uint)10 ); - QCOMPARE( release_count, (uint)10 ); + QTest::mouseClick(testWidget, Qt::LeftButton); + QCOMPARE(press_count, 10); + QCOMPARE(release_count, 10); } void tst_QPushButton::touchTap() { QTest::touchEvent(testWidget, m_touchScreen).press(0, QPoint(10, 10)); - QVERIFY( press_count == 1 ); - QVERIFY( release_count == 0 ); + QCOMPARE(press_count, 1); + QCOMPARE(release_count, 0); QTest::touchEvent(testWidget, m_touchScreen).release(0, QPoint(10, 10)); - QCOMPARE( press_count, (uint)1 ); - QCOMPARE( release_count, (uint)1 ); - QCOMPARE( click_count, (uint)1 ); + QCOMPARE(press_count, 1); + QCOMPARE(release_count, 1); + QCOMPARE(click_count, 1); press_count = 0; release_count = 0; @@ -413,26 +393,23 @@ void tst_QPushButton::touchTap() QTest::touchEvent(testWidget, m_touchScreen).press(0, QPoint(10, 10)); QTest::touchEvent(testWidget, m_touchScreen).release(0, QPoint(10, 10)); } - QCOMPARE( press_count, (uint)10 ); - QCOMPARE( release_count, (uint)10 ); - QCOMPARE( click_count, (uint)10 ); -} - -QPushButton *pb = 0; -void tst_QPushButton::helperSlotDelete() -{ - delete pb; - pb = 0; + QCOMPARE(press_count, 10); + QCOMPARE(release_count, 10); + QCOMPARE(click_count, 10); } void tst_QPushButton::popupCrash() { - pb = new QPushButton("foo"); + QPushButton *pb = new QPushButton("foo"); QMenu *menu = new QMenu("bar", pb); pb->setMenu(menu); - QTimer::singleShot(1000, this, SLOT(helperSlotDelete())); + QTimer::singleShot(1000, this, [&pb]{ + delete pb; + pb = nullptr; + }); pb->show(); pb->click(); + QTRY_COMPARE(pb, nullptr); } void tst_QPushButton::defaultAndAutoDefault() @@ -522,14 +499,14 @@ void tst_QPushButton::defaultAndAutoDefault() // Reparenting QVERIFY(button2.autoDefault()); - button2.setParent(0); + button2.setParent(nullptr); QVERIFY(!button2.autoDefault()); button2.setAutoDefault(false); button2.setParent(&dialog); QVERIFY(!button2.autoDefault()); button1.setAutoDefault(true); - button1.setParent(0); + button1.setParent(nullptr); QVERIFY(button1.autoDefault()); } } @@ -570,11 +547,13 @@ void tst_QPushButton::sizeHint() button->setParent(widget); button->sizeHint(); - widget->setParent(0); + widget->setParent(nullptr); delete dialog; button->setDefault(false); QCOMPARE(button->sizeHint(), initSizeHint); delete button; + + delete widget; } // Test 2 @@ -604,6 +583,8 @@ void tst_QPushButton::sizeHint() tabWidget->setCurrentWidget(tab1); QTRY_COMPARE(button1_2->size(), button2_2->size()); + + delete dialog; } } @@ -621,7 +602,7 @@ void tst_QPushButton::taskQTBUG_20191_shortcutWithKeypadModifer() dialog.setLayout(layout); dialog.show(); QVERIFY(QTest::qWaitForWindowExposed(&dialog)); - QApplication::setActiveWindow(&dialog); + QApplicationPrivate::setActiveWindow(&dialog); // add shortcut '5' to button1 and test with keyboard and keypad '5' keys QSignalSpy spy1(button1, SIGNAL(clicked())); @@ -630,7 +611,7 @@ void tst_QPushButton::taskQTBUG_20191_shortcutWithKeypadModifer() QTest::qWait(300); QTest::keyClick(&dialog, Qt::Key_5, Qt::KeypadModifier); QTest::qWait(300); - QCOMPARE(spy1.count(), 2); + QCOMPARE(spy1.size(), 2); // add shortcut 'keypad 5' to button2 spy1.clear(); @@ -640,8 +621,8 @@ void tst_QPushButton::taskQTBUG_20191_shortcutWithKeypadModifer() QTest::qWait(300); QTest::keyClick(&dialog, Qt::Key_5, Qt::KeypadModifier); QTest::qWait(300); - QCOMPARE(spy1.count(), 1); - QCOMPARE(spy2.count(), 1); + QCOMPARE(spy1.size(), 1); + QCOMPARE(spy2.size(), 1); // remove shortcut from button1 spy1.clear(); @@ -651,8 +632,8 @@ void tst_QPushButton::taskQTBUG_20191_shortcutWithKeypadModifer() QTest::qWait(300); QTest::keyClick(&dialog, Qt::Key_5, Qt::KeypadModifier); QTest::qWait(300); - QCOMPARE(spy1.count(), 0); - QCOMPARE(spy2.count(), 1); + QCOMPARE(spy1.size(), 0); + QCOMPARE(spy2.size(), 1); } #endif // QT_CONFIG(shortcut) @@ -668,7 +649,7 @@ void tst_QPushButton::emitReleasedAfterChange() dialog.setLayout(layout); dialog.show(); QVERIFY(QTest::qWaitForWindowExposed(&dialog)); - QApplication::setActiveWindow(&dialog); + QApplicationPrivate::setActiveWindow(&dialog); button1->setFocus(); QSignalSpy spy(button1, SIGNAL(released())); @@ -676,16 +657,16 @@ void tst_QPushButton::emitReleasedAfterChange() QVERIFY(button1->isDown()); QTest::keyClick(&dialog, Qt::Key_Tab); QVERIFY(!button1->isDown()); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); button1->setFocus(); QTest::mousePress(button1, Qt::LeftButton); QVERIFY(button1->isDown()); button1->setEnabled(false); QVERIFY(!button1->isDown()); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } /* @@ -734,5 +715,98 @@ void tst_QPushButton::hitButton() QVERIFY(!button2->hitButton(QPoint(2, 2))); } +/* + Test that a style sheet with only icon doesn't crash. + QTBUG-91735 +*/ +void tst_QPushButton::iconOnlyStyleSheet() +{ + QIcon icon(":/qt-project.org/styles/commonstyle/images/dvd-32.png"); + QVERIFY(!icon.isNull()); + QPushButton pb; + pb.setStyleSheet("QPushButton {" + "icon: url(:/qt-project.org/styles/commonstyle/images/dvd-32.png);" + "border: red;" + "}"); + pb.show(); + QVERIFY(QTest::qWaitForWindowExposed(&pb)); +} + +/* + Test that mouse has been pressed,the signal is sent when moving the mouse. + QTBUG-97937 +*/ +void tst_QPushButton::mousePressAndMove() +{ + QPushButton button; + button.setGeometry(0, 0, 20, 20); + QSignalSpy pressSpy(&button, &QAbstractButton::pressed); + QSignalSpy releaseSpy(&button, &QAbstractButton::released); + + QTest::mousePress(&button, Qt::LeftButton); + QCOMPARE(pressSpy.size(), 1); + QCOMPARE(releaseSpy.size(), 0); + + // mouse pressed and moving out + QTest::mouseMove(&button, QPoint(100, 100)); + + // should emit released signal when the mouse is dragged out of boundary + QCOMPARE(pressSpy.size(), 1); + QCOMPARE(releaseSpy.size(), 1); + + // mouse pressed and moving into + QTest::mouseMove(&button, QPoint(10, 10)); + + // should emit pressed signal when the mouse is dragged into of boundary + QCOMPARE(pressSpy.size(), 2); + QCOMPARE(releaseSpy.size(), 1); +} + +/* + Test checking that a QPushButton with a QMenu has a sunken style only + when the menu is open + QTBUG-120976 +*/ +void tst_QPushButton::reactToMenuClosed() +{ + // create a subclass of QPushButton to expose the initStyleOption method + class PushButton : public QPushButton { + public: + virtual void initStyleOption(QStyleOptionButton *option) const override + { + QPushButton::initStyleOption(option); + } + }; + + PushButton button; + QStyleOptionButton opt; + QMenu menu; + + // add a menu to the button + menu.addAction(tr("string")); + button.setMenu(&menu); + + // give the button a size and show it + button.setGeometry(0, 0, 50, 50); + button.show(); + QVERIFY(QTest::qWaitForWindowExposed(&button)); + + // click the button to open the menu + QTest::mouseClick(&button, Qt::LeftButton); + + // check the menu is visible and the button style is sunken + QTRY_VERIFY(menu.isVisible()); + button.initStyleOption(&opt); + QVERIFY(opt.state.testFlag(QStyle::StateFlag::State_Sunken)); + + // close the menu + menu.close(); + + // check the menu isn't visible and the style isn't sunken + QTRY_VERIFY(!menu.isVisible()); + button.initStyleOption(&opt); + QVERIFY(!opt.state.testFlag(QStyle::StateFlag::State_Sunken)); +} + QTEST_MAIN(tst_QPushButton) #include "tst_qpushbutton.moc" diff --git a/tests/auto/widgets/widgets/qradiobutton/CMakeLists.txt b/tests/auto/widgets/widgets/qradiobutton/CMakeLists.txt index 8ce8aadea8..ce016975c8 100644 --- a/tests/auto/widgets/widgets/qradiobutton/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qradiobutton/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qradiobutton.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qradiobutton Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qradiobutton LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qradiobutton SOURCES tst_qradiobutton.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/widgets/qradiobutton/tst_qradiobutton.cpp b/tests/auto/widgets/widgets/qradiobutton/tst_qradiobutton.cpp index 8b1670ae06..144d91e9f2 100644 --- a/tests/auto/widgets/widgets/qradiobutton/tst_qradiobutton.cpp +++ b/tests/auto/widgets/widgets/qradiobutton/tst_qradiobutton.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qrhiwidget/CMakeLists.txt b/tests/auto/widgets/widgets/qrhiwidget/CMakeLists.txt new file mode 100644 index 0000000000..f8d18bcf53 --- /dev/null +++ b/tests/auto/widgets/widgets/qrhiwidget/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qrhiwidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +file(GLOB_RECURSE qrhiwidget_resource_files + RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + data/* +) + +qt_internal_add_test(tst_qrhiwidget + SOURCES + tst_qrhiwidget.cpp + LIBRARIES + Qt::CorePrivate + Qt::Gui + Qt::GuiPrivate + Qt::Widgets + TESTDATA ${qrhiwidget_resource_files} + BUILTIN_TESTDATA +) diff --git a/tests/auto/widgets/widgets/qrhiwidget/data/simple.frag b/tests/auto/widgets/widgets/qrhiwidget/data/simple.frag new file mode 100644 index 0000000000..2aa500e09a --- /dev/null +++ b/tests/auto/widgets/widgets/qrhiwidget/data/simple.frag @@ -0,0 +1,8 @@ +#version 440 + +layout(location = 0) out vec4 fragColor; + +void main() +{ + fragColor = vec4(1.0, 0.0, 0.0, 1.0); +} diff --git a/tests/auto/widgets/widgets/qrhiwidget/data/simple.frag.qsb b/tests/auto/widgets/widgets/qrhiwidget/data/simple.frag.qsb Binary files differnew file mode 100644 index 0000000000..40d0a296ac --- /dev/null +++ b/tests/auto/widgets/widgets/qrhiwidget/data/simple.frag.qsb diff --git a/tests/auto/widgets/widgets/qrhiwidget/data/simple.vert b/tests/auto/widgets/widgets/qrhiwidget/data/simple.vert new file mode 100644 index 0000000000..6b954cdaec --- /dev/null +++ b/tests/auto/widgets/widgets/qrhiwidget/data/simple.vert @@ -0,0 +1,8 @@ +#version 440 + +layout(location = 0) in vec4 position; + +void main() +{ + gl_Position = position; +} diff --git a/tests/auto/widgets/widgets/qrhiwidget/data/simple.vert.qsb b/tests/auto/widgets/widgets/qrhiwidget/data/simple.vert.qsb Binary files differnew file mode 100644 index 0000000000..5b7fd39668 --- /dev/null +++ b/tests/auto/widgets/widgets/qrhiwidget/data/simple.vert.qsb diff --git a/tests/auto/widgets/widgets/qrhiwidget/tst_qrhiwidget.cpp b/tests/auto/widgets/widgets/qrhiwidget/tst_qrhiwidget.cpp new file mode 100644 index 0000000000..7a102180e7 --- /dev/null +++ b/tests/auto/widgets/widgets/qrhiwidget/tst_qrhiwidget.cpp @@ -0,0 +1,834 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtWidgets/QRhiWidget> +#include <QtGui/QPainter> +#include <QTest> +#include <QSignalSpy> +#include <private/qguiapplication_p.h> +#include <qpa/qplatformintegration.h> +#include <rhi/qrhi.h> + +#include <QApplication> +#include <QFile> +#include <QVBoxLayout> +#include <QScrollArea> + +#if QT_CONFIG(vulkan) +#include <private/qvulkandefaultinstance_p.h> +#endif + +class tst_QRhiWidget : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void create_data(); + void create(); + void noCreate(); + void simple_data(); + void simple(); + void msaa_data(); + void msaa(); + void fixedSize_data(); + void fixedSize(); + void autoRt_data(); + void autoRt(); + void reparent_data(); + void reparent(); + void grabFramebufferWhileStillInvisible_data(); + void grabFramebufferWhileStillInvisible(); + void grabViaQWidgetGrab_data(); + void grabViaQWidgetGrab(); + void mirror_data(); + void mirror(); + +private: + void testData(); +}; + +void tst_QRhiWidget::initTestCase() +{ + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RhiBasedRendering)) + QSKIP("RhiBasedRendering capability is reported as unsupported on this platform."); + + qputenv("QT_RHI_LEAK_CHECK", "1"); +} + +void tst_QRhiWidget::testData() +{ + QTest::addColumn<QRhiWidget::Api>("api"); + +#ifndef Q_OS_WEBOS + QTest::newRow("Null") << QRhiWidget::Api::Null; +#endif + +#if QT_CONFIG(opengl) + if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) + QTest::newRow("OpenGL") << QRhiWidget::Api::OpenGL; +#endif + +#if QT_CONFIG(vulkan) + // Have to probe to be sure Vulkan is actually working (the test cases + // themselves will assume QRhi init succeeds). + if (QVulkanDefaultInstance::instance()) { + QRhiVulkanInitParams vulkanInitParams; + vulkanInitParams.inst = QVulkanDefaultInstance::instance(); + if (QRhi::probe(QRhi::Vulkan, &vulkanInitParams)) + QTest::newRow("Vulkan") << QRhiWidget::Api::Vulkan; + } +#endif + +#if QT_CONFIG(metal) + QRhiMetalInitParams metalInitParams; + if (QRhi::probe(QRhi::Metal, &metalInitParams)) + QTest::newRow("Metal") << QRhiWidget::Api::Metal; +#endif + +#ifdef Q_OS_WIN + QTest::newRow("D3D11") << QRhiWidget::Api::Direct3D11; + // D3D12 needs to be probed too due to being disabled if the SDK headers + // are too old (clang, mingw). + QRhiD3D12InitParams d3d12InitParams; + if (QRhi::probe(QRhi::D3D12, &d3d12InitParams)) + QTest::newRow("D3D12") << QRhiWidget::Api::Direct3D12; +#endif +} + +void tst_QRhiWidget::create_data() +{ + testData(); +} + +void tst_QRhiWidget::create() +{ + QFETCH(QRhiWidget::Api, api); + + { + QRhiWidget w; + w.setApi(api); + w.resize(320, 240); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + } + + { + QWidget topLevel; + topLevel.resize(320, 240); + QRhiWidget *w = new QRhiWidget(&topLevel); + w->setApi(api); + w->resize(100, 100); + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + } +} + +void tst_QRhiWidget::noCreate() +{ + // Now try something that is guaranteed to fail. + // E.g. try using Metal on Windows. + // The error signal should be emitted. The frame signal should not. +#ifdef Q_OS_WIN + qDebug("Warnings will be printed below, this is as expected"); + QRhiWidget rhiWidget; + rhiWidget.setApi(QRhiWidget::Api::Metal); + QSignalSpy frameSpy(&rhiWidget, &QRhiWidget::frameSubmitted); + QSignalSpy errorSpy(&rhiWidget, &QRhiWidget::renderFailed); + rhiWidget.resize(320, 240); + rhiWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&rhiWidget)); + QTRY_VERIFY(errorSpy.count() > 0); + QCOMPARE(frameSpy.count(), 0); +#endif +} + +static QShader getShader(const QString &name) +{ + QFile f(name); + return f.open(QIODevice::ReadOnly) ? QShader::fromSerialized(f.readAll()) : QShader(); +} + +static bool submitResourceUpdates(QRhi *rhi, QRhiResourceUpdateBatch *batch) +{ + QRhiCommandBuffer *cb = nullptr; + QRhi::FrameOpResult result = rhi->beginOffscreenFrame(&cb); + if (result != QRhi::FrameOpSuccess) { + qWarning("beginOffscreenFrame returned %d", result); + return false; + } + if (!cb) { + qWarning("No command buffer from beginOffscreenFrame"); + return false; + } + cb->resourceUpdate(batch); + rhi->endOffscreenFrame(); + return true; +} + +inline bool imageRGBAEquals(const QImage &a, const QImage &b, int maxFuzz = 1) +{ + if (a.size() != b.size()) + return false; + + const QImage image0 = a.convertToFormat(QImage::Format_RGBA8888_Premultiplied); + const QImage image1 = b.convertToFormat(QImage::Format_RGBA8888_Premultiplied); + + const int width = image0.width(); + const int height = image0.height(); + for (int y = 0; y < height; ++y) { + const quint32 *p0 = reinterpret_cast<const quint32 *>(image0.constScanLine(y)); + const quint32 *p1 = reinterpret_cast<const quint32 *>(image1.constScanLine(y)); + int x = width - 1; + while (x-- >= 0) { + const QRgb c0(*p0++); + const QRgb c1(*p1++); + const int red = qAbs(qRed(c0) - qRed(c1)); + const int green = qAbs(qGreen(c0) - qGreen(c1)); + const int blue = qAbs(qBlue(c0) - qBlue(c1)); + const int alpha = qAbs(qAlpha(c0) - qAlpha(c1)); + if (red > maxFuzz || green > maxFuzz || blue > maxFuzz || alpha > maxFuzz) + return false; + } + } + + return true; +} + +class SimpleRhiWidget : public QRhiWidget +{ +public: + SimpleRhiWidget(int sampleCount = 1, QWidget *parent = nullptr) + : QRhiWidget(parent), + m_sampleCount(sampleCount) + { } + + ~SimpleRhiWidget() + { + delete m_rt; + delete m_rp; + } + + void initialize(QRhiCommandBuffer *cb) override; + void render(QRhiCommandBuffer *cb) override; + void releaseResources() override; + + int m_sampleCount; + QRhi *m_rhi = nullptr; + std::unique_ptr<QRhiBuffer> m_vbuf; + std::unique_ptr<QRhiBuffer> m_ubuf; + std::unique_ptr<QRhiShaderResourceBindings> m_srb; + std::unique_ptr<QRhiGraphicsPipeline> m_pipeline; + QRhiTextureRenderTarget *m_rt = nullptr; // used when autoRenderTarget is off + QRhiRenderPassDescriptor *m_rp = nullptr; // used when autoRenderTarget is off + + friend class tst_QRhiWidget; +}; + +void SimpleRhiWidget::initialize(QRhiCommandBuffer *cb) +{ + if (m_rhi != rhi()) { + m_pipeline.reset(); + m_rhi = rhi(); + } + + if (!m_pipeline) { + if (!isAutoRenderTargetEnabled()) { + delete m_rt; + delete m_rp; + QRhiTextureRenderTargetDescription rtDesc; + if (colorTexture()) { + rtDesc.setColorAttachments({ colorTexture() }); + } else if (msaaColorBuffer()) { + QRhiColorAttachment att; + att.setRenderBuffer(msaaColorBuffer()); + rtDesc.setColorAttachments({ att }); + } + m_rt = m_rhi->newTextureRenderTarget(rtDesc); + m_rp = m_rt->newCompatibleRenderPassDescriptor(); + m_rt->setRenderPassDescriptor(m_rp); + m_rt->create(); + } + + static float vertexData[] = { + 0, 1, + -1, -1, + 1, -1 + }; + + m_vbuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData))); + m_vbuf->create(); + + m_ubuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64)); + m_ubuf->create(); + + m_srb.reset(m_rhi->newShaderResourceBindings()); + m_srb->create(); + + m_pipeline.reset(m_rhi->newGraphicsPipeline()); + m_pipeline->setShaderStages({ + { QRhiShaderStage::Vertex, getShader(QLatin1String(":/data/simple.vert.qsb")) }, + { QRhiShaderStage::Fragment, getShader(QLatin1String(":/data/simple.frag.qsb")) } + }); + QRhiVertexInputLayout inputLayout; + inputLayout.setBindings({ + { 2 * sizeof(float) } + }); + inputLayout.setAttributes({ + { 0, 0, QRhiVertexInputAttribute::Float2, 0 } + }); + m_pipeline->setSampleCount(m_sampleCount); + m_pipeline->setVertexInputLayout(inputLayout); + m_pipeline->setShaderResourceBindings(m_srb.get()); + m_pipeline->setRenderPassDescriptor(renderTarget() ? renderTarget()->renderPassDescriptor() : m_rp); + m_pipeline->create(); + + QRhiResourceUpdateBatch *resourceUpdates = m_rhi->nextResourceUpdateBatch(); + resourceUpdates->uploadStaticBuffer(m_vbuf.get(), vertexData); + cb->resourceUpdate(resourceUpdates); + } +} + +void SimpleRhiWidget::render(QRhiCommandBuffer *cb) +{ + const QSize outputSize = colorTexture() ? colorTexture()->pixelSize() : msaaColorBuffer()->pixelSize(); + if (renderTarget()) { + QCOMPARE(outputSize, renderTarget()->pixelSize()); + if (rhi()->backend() != QRhi::Null && rhi()->supportedSampleCounts().contains(m_sampleCount)) + QCOMPARE(m_sampleCount, renderTarget()->sampleCount()); + } + + const QColor clearColor = QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f); + cb->beginPass(renderTarget() ? renderTarget() : m_rt, clearColor, { 1.0f, 0 }); + cb->setGraphicsPipeline(m_pipeline.get()); + cb->setViewport(QRhiViewport(0, 0, outputSize.width(), outputSize.height())); + cb->setShaderResources(); + const QRhiCommandBuffer::VertexInput vbufBinding(m_vbuf.get(), 0); + cb->setVertexInput(0, 1, &vbufBinding); + cb->draw(3); + cb->endPass(); +} + +void SimpleRhiWidget::releaseResources() +{ + m_pipeline.reset(); + m_srb.reset(); + m_ubuf.reset(); + m_vbuf.reset(); + +} + +void tst_QRhiWidget::simple_data() +{ + testData(); +} + +void tst_QRhiWidget::simple() +{ + QFETCH(QRhiWidget::Api, api); + + SimpleRhiWidget *rhiWidget = new SimpleRhiWidget; + rhiWidget->setApi(api); + QSignalSpy frameSpy(rhiWidget, &QRhiWidget::frameSubmitted); + QSignalSpy errorSpy(rhiWidget, &QRhiWidget::renderFailed); + + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(rhiWidget); + + QWidget w; + w.setLayout(layout); + w.resize(1280, 720); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + + QCOMPARE(rhiWidget->sampleCount(), 1); + QCOMPARE(rhiWidget->colorBufferFormat(), QRhiWidget::TextureFormat::RGBA8); + QVERIFY(rhiWidget->isAutoRenderTargetEnabled()); + + // Pull out the QRhiTexture (we know colorTexture() and rhi() and friends + // are all there even outside initialize() and render(), even though this + // is not quite documented), and read it back. + QRhiTexture *backingTexture = rhiWidget->colorTexture(); + QVERIFY(backingTexture); + QCOMPARE(backingTexture->format(), QRhiTexture::RGBA8); + QVERIFY(rhiWidget->depthStencilBuffer()); + QVERIFY(rhiWidget->renderTarget()); + QVERIFY(!rhiWidget->resolveTexture()); + QRhi *rhi = rhiWidget->rhi(); + QVERIFY(rhi); + + switch (api) { + case QRhiWidget::Api::OpenGL: + QCOMPARE(rhi->backend(), QRhi::OpenGLES2); + break; + case QRhiWidget::Api::Metal: + QCOMPARE(rhi->backend(), QRhi::Metal); + break; + case QRhiWidget::Api::Vulkan: + QCOMPARE(rhi->backend(), QRhi::Vulkan); + break; + case QRhiWidget::Api::Direct3D11: + QCOMPARE(rhi->backend(), QRhi::D3D11); + break; + case QRhiWidget::Api::Direct3D12: + QCOMPARE(rhi->backend(), QRhi::D3D12); + break; + case QRhiWidget::Api::Null: + QCOMPARE(rhi->backend(), QRhi::Null); + break; + default: + break; + } + + const int maxFuzz = 1; + QImage resultOne; + if (rhi->backend() != QRhi::Null) { + QRhiReadbackResult readResult; + bool readCompleted = false; + readResult.completed = [&readCompleted] { readCompleted = true; }; + QRhiResourceUpdateBatch *rub = rhi->nextResourceUpdateBatch(); + rub->readBackTexture(backingTexture, &readResult); + QVERIFY(submitResourceUpdates(rhi, rub)); + QVERIFY(readCompleted); + + QImage wrapperImage(reinterpret_cast<const uchar *>(readResult.data.constData()), + readResult.pixelSize.width(), readResult.pixelSize.height(), + QImage::Format_RGBA8888); + if (rhi->isYUpInFramebuffer()) + resultOne = wrapperImage.mirrored(); + else + resultOne = wrapperImage.copy(); + + // result is now a red triangle upon greenish background, where the + // triangle's edges are (0, 1), (-1, -1), and (1, -1). + // It's upside down with Vulkan (Y is not corrected, clipSpaceCorrMatrix() is not used), + // but that won't matter for the test. + + // Check that the center is a red pixel. + QRgb c = resultOne.pixel(resultOne.width() / 2, resultOne.height() / 2); + QVERIFY(qRed(c) >= 255 - maxFuzz); + QVERIFY(qGreen(c) <= maxFuzz); + QVERIFY(qBlue(c) <= maxFuzz); + } + + // Now through grabFramebuffer(). + QImage resultTwo; + if (rhi->backend() != QRhi::Null) { + resultTwo = rhiWidget->grabFramebuffer(); + QCOMPARE(errorSpy.count(), 0); + QVERIFY(!resultTwo.isNull()); + QRgb c = resultTwo.pixel(resultTwo.width() / 2, resultTwo.height() / 2); + QVERIFY(qRed(c) >= 255 - maxFuzz); + QVERIFY(qGreen(c) <= maxFuzz); + QVERIFY(qBlue(c) <= maxFuzz); + } + + // Check we got the same result from our manual readback and when the + // texture was rendered to again and grabFramebuffer() was called. + QVERIFY(imageRGBAEquals(resultOne, resultTwo, maxFuzz)); +} + +void tst_QRhiWidget::msaa_data() +{ + testData(); +} + +void tst_QRhiWidget::msaa() +{ + QFETCH(QRhiWidget::Api, api); + + const int SAMPLE_COUNT = 4; + SimpleRhiWidget *rhiWidget = new SimpleRhiWidget(SAMPLE_COUNT); + rhiWidget->setApi(api); + rhiWidget->setSampleCount(SAMPLE_COUNT); + QSignalSpy frameSpy(rhiWidget, &QRhiWidget::frameSubmitted); + QSignalSpy errorSpy(rhiWidget, &QRhiWidget::renderFailed); + + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(rhiWidget); + + QWidget w; + w.setLayout(layout); + w.resize(1280, 720); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + + QCOMPARE(rhiWidget->sampleCount(), 4); + QCOMPARE(rhiWidget->colorBufferFormat(), QRhiWidget::TextureFormat::RGBA8); + QVERIFY(!rhiWidget->colorTexture()); + QVERIFY(rhiWidget->msaaColorBuffer()); + QVERIFY(rhiWidget->depthStencilBuffer()); + QVERIFY(rhiWidget->renderTarget()); + QVERIFY(rhiWidget->resolveTexture()); + QCOMPARE(rhiWidget->resolveTexture()->format(), QRhiTexture::RGBA8); + QRhi *rhi = rhiWidget->rhi(); + QVERIFY(rhi); + + if (rhi->backend() != QRhi::Null) { + QRhiReadbackResult readResult; + QRhiResourceUpdateBatch *rub = rhi->nextResourceUpdateBatch(); + rub->readBackTexture(rhiWidget->resolveTexture(), &readResult); + QVERIFY(submitResourceUpdates(rhi, rub)); + + QImage wrapperImage(reinterpret_cast<const uchar *>(readResult.data.constData()), + readResult.pixelSize.width(), readResult.pixelSize.height(), + QImage::Format_RGBA8888); + QImage result; + if (rhi->isYUpInFramebuffer()) + result = wrapperImage.mirrored(); + else + result = wrapperImage.copy(); + + // Check that the center is a red pixel. + const int maxFuzz = 1; + QRgb c = result.pixel(result.width() / 2, result.height() / 2); + QVERIFY(qRed(c) >= 255 - maxFuzz); + QVERIFY(qGreen(c) <= maxFuzz); + QVERIFY(qBlue(c) <= maxFuzz); + } + + // See if switching back and forth works. + frameSpy.clear(); + rhiWidget->m_pipeline.reset(); + rhiWidget->m_sampleCount = 1; + rhiWidget->setSampleCount(1); + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + QVERIFY(rhiWidget->colorTexture()); + QVERIFY(!rhiWidget->msaaColorBuffer()); + + frameSpy.clear(); + rhiWidget->m_pipeline.reset(); + rhiWidget->m_sampleCount = SAMPLE_COUNT; + rhiWidget->setSampleCount(SAMPLE_COUNT); + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + QVERIFY(!rhiWidget->colorTexture()); + QVERIFY(rhiWidget->msaaColorBuffer()); +} + +void tst_QRhiWidget::fixedSize_data() +{ + testData(); +} + +void tst_QRhiWidget::fixedSize() +{ + QFETCH(QRhiWidget::Api, api); + + SimpleRhiWidget *rhiWidget = new SimpleRhiWidget; + rhiWidget->setApi(api); + QSignalSpy frameSpy(rhiWidget, &QRhiWidget::frameSubmitted); + QSignalSpy errorSpy(rhiWidget, &QRhiWidget::renderFailed); + + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(rhiWidget); + + rhiWidget->setFixedColorBufferSize(QSize(320, 200)); + + QWidget w; + w.setLayout(layout); + w.resize(1280, 720); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + + QVERIFY(rhiWidget->rhi()); + QVERIFY(rhiWidget->colorTexture()); + QCOMPARE(rhiWidget->colorTexture()->pixelSize(), QSize(320, 200)); + QVERIFY(rhiWidget->depthStencilBuffer()); + QCOMPARE(rhiWidget->depthStencilBuffer()->pixelSize(), QSize(320, 200)); + QVERIFY(rhiWidget->renderTarget()); + QVERIFY(!rhiWidget->resolveTexture()); + + frameSpy.clear(); + rhiWidget->setFixedColorBufferSize(640, 480); // should also trigger update() + QTRY_VERIFY(frameSpy.count() > 0); + + QVERIFY(rhiWidget->colorTexture()); + QCOMPARE(rhiWidget->colorTexture()->pixelSize(), QSize(640, 480)); + QVERIFY(rhiWidget->depthStencilBuffer()); + QCOMPARE(rhiWidget->depthStencilBuffer()->pixelSize(), QSize(640, 480)); + + frameSpy.clear(); + rhiWidget->setFixedColorBufferSize(QSize()); + QTRY_VERIFY(frameSpy.count() > 0); + + QVERIFY(rhiWidget->colorTexture()); + QVERIFY(rhiWidget->colorTexture()->pixelSize() != QSize(640, 480)); + QVERIFY(rhiWidget->depthStencilBuffer()); + QVERIFY(rhiWidget->depthStencilBuffer()->pixelSize() != QSize(640, 480)); +} + +void tst_QRhiWidget::autoRt_data() +{ + testData(); +} + +void tst_QRhiWidget::autoRt() +{ + QFETCH(QRhiWidget::Api, api); + + SimpleRhiWidget *rhiWidget = new SimpleRhiWidget; + rhiWidget->setApi(api); + QVERIFY(rhiWidget->isAutoRenderTargetEnabled()); + rhiWidget->setAutoRenderTarget(false); + QVERIFY(!rhiWidget->isAutoRenderTargetEnabled()); + QSignalSpy frameSpy(rhiWidget, &QRhiWidget::frameSubmitted); + QSignalSpy errorSpy(rhiWidget, &QRhiWidget::renderFailed); + + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(rhiWidget); + + QWidget w; + w.setLayout(layout); + w.resize(1280, 720); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + + QVERIFY(rhiWidget->rhi()); + QVERIFY(rhiWidget->colorTexture()); + QVERIFY(!rhiWidget->depthStencilBuffer()); + QVERIFY(!rhiWidget->renderTarget()); + QVERIFY(!rhiWidget->resolveTexture()); + + QVERIFY(rhiWidget->m_rt); + QVERIFY(rhiWidget->m_rp); + QCOMPARE(rhiWidget->m_rt->description().cbeginColorAttachments()->texture(), rhiWidget->colorTexture()); + + frameSpy.clear(); + // do something that triggers creating a new backing texture + rhiWidget->setFixedColorBufferSize(QSize(320, 200)); + QTRY_VERIFY(frameSpy.count() > 0); + + QVERIFY(rhiWidget->colorTexture()); + QCOMPARE(rhiWidget->m_rt->description().cbeginColorAttachments()->texture(), rhiWidget->colorTexture()); +} + +void tst_QRhiWidget::reparent_data() +{ + testData(); +} + +void tst_QRhiWidget::reparent() +{ + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::MultipleWindows)) + QSKIP("MultipleWindows capability is reported as unsupported, skipping reparenting test."); + + QFETCH(QRhiWidget::Api, api); + + QWidget *windowOne = new QWidget; + windowOne->resize(1280, 720); + + SimpleRhiWidget *rhiWidget = new SimpleRhiWidget(1); + rhiWidget->setApi(api); + rhiWidget->resize(800, 600); + QSignalSpy frameSpy(rhiWidget, &QRhiWidget::frameSubmitted); + QSignalSpy errorSpy(rhiWidget, &QRhiWidget::renderFailed); + + rhiWidget->show(); + QVERIFY(QTest::qWaitForWindowExposed(rhiWidget)); + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + + frameSpy.clear(); + rhiWidget->setParent(windowOne); + windowOne->show(); + QVERIFY(QTest::qWaitForWindowExposed(windowOne)); + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + + frameSpy.clear(); + QWidget windowTwo; + windowTwo.resize(1280, 720); + + rhiWidget->setParent(&windowTwo); + + // There's nothing saying the old top-level parent is going to be around, + // which is interesting wrt to its QRhi and resources created with that; + // exercise this. + delete windowOne; + + windowTwo.show(); + QVERIFY(QTest::qWaitForWindowExposed(&windowTwo)); + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + + // now reparent after show() has already been called + frameSpy.clear(); + QWidget windowThree; + windowThree.resize(1280, 720); + windowThree.show(); + QVERIFY(QTest::qWaitForWindowExposed(&windowThree)); + + rhiWidget->setParent(&windowThree); + // this case needs a show() on rhiWidget + rhiWidget->show(); + + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); +} + +void tst_QRhiWidget::grabFramebufferWhileStillInvisible_data() +{ + testData(); +} + +void tst_QRhiWidget::grabFramebufferWhileStillInvisible() +{ + QFETCH(QRhiWidget::Api, api); + + const int maxFuzz = 1; + + SimpleRhiWidget w; + w.setApi(api); + w.resize(1280, 720); + QSignalSpy errorSpy(&w, &QRhiWidget::renderFailed); + + QImage image = w.grabFramebuffer(); // creates its own QRhi just to render offscreen + QVERIFY(!image.isNull()); + QVERIFY(w.rhi()); + QVERIFY(w.colorTexture()); + QCOMPARE(errorSpy.count(), 0); + if (api != QRhiWidget::Api::Null) { + QRgb c = image.pixel(image.width() / 2, image.height() / 2); + QVERIFY(qRed(c) >= 255 - maxFuzz); + QVERIFY(qGreen(c) <= maxFuzz); + QVERIFY(qBlue(c) <= maxFuzz); + } + + // Make the window visible, this under the hood drops the QRhiWidget's + // own QRhi and attaches to the backingstore's. + QSignalSpy frameSpy(&w, &QRhiWidget::frameSubmitted); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + QTRY_VERIFY(frameSpy.count() > 0); + + QCOMPARE(errorSpy.count(), 0); + + if (api != QRhiWidget::Api::Null) { + QRhiReadbackResult readResult; + QRhiResourceUpdateBatch *rub = w.rhi()->nextResourceUpdateBatch(); + rub->readBackTexture(w.colorTexture(), &readResult); + QVERIFY(submitResourceUpdates(w.rhi(), rub)); + QImage wrapperImage(reinterpret_cast<const uchar *>(readResult.data.constData()), + readResult.pixelSize.width(), readResult.pixelSize.height(), + QImage::Format_RGBA8888); + if (w.rhi()->isYUpInFramebuffer()) + image = wrapperImage.mirrored(); + else + image = wrapperImage.copy(); + QRgb c = image.pixel(image.width() / 2, image.height() / 2); + QVERIFY(qRed(c) >= 255 - maxFuzz); + QVERIFY(qGreen(c) <= maxFuzz); + QVERIFY(qBlue(c) <= maxFuzz); + } +} + +void tst_QRhiWidget::grabViaQWidgetGrab_data() +{ + testData(); +} + +void tst_QRhiWidget::grabViaQWidgetGrab() +{ + QFETCH(QRhiWidget::Api, api); + + SimpleRhiWidget w; + w.setApi(api); + w.resize(1280, 720); + QSignalSpy frameSpy(&w, &QRhiWidget::frameSubmitted); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + QTRY_VERIFY(frameSpy.count() > 0); + + QImage image = w.grab().toImage(); + + if (w.rhi()->backend() != QRhi::Null) { + // It's upside down with Vulkan (Y is not corrected, clipSpaceCorrMatrix() is not used), + // but that won't matter for the test. + QRgb c = image.pixel(image.width() / 2, image.height() / 2); + const int maxFuzz = 1; + QVERIFY(qRed(c) >= 255 - maxFuzz); + QVERIFY(qGreen(c) <= maxFuzz); + QVERIFY(qBlue(c) <= maxFuzz); + } +} + +void tst_QRhiWidget::mirror_data() +{ + testData(); +} + +void tst_QRhiWidget::mirror() +{ + QFETCH(QRhiWidget::Api, api); + + SimpleRhiWidget *rhiWidget = new SimpleRhiWidget; + rhiWidget->setApi(api); + QVERIFY(!rhiWidget->isMirrorVerticallyEnabled()); + + QSignalSpy frameSpy(rhiWidget, &QRhiWidget::frameSubmitted); + QSignalSpy errorSpy(rhiWidget, &QRhiWidget::renderFailed); + + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(rhiWidget); + QWidget w; + w.setLayout(layout); + w.resize(1280, 720); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + + frameSpy.clear(); + rhiWidget->setMirrorVertically(true); + QVERIFY(rhiWidget->isMirrorVerticallyEnabled()); + QTRY_VERIFY(frameSpy.count() > 0); + QCOMPARE(errorSpy.count(), 0); + + if (api != QRhiWidget::Api::Null) { + QRhi *rhi = rhiWidget->rhi(); + QRhiReadbackResult readResult; + QRhiResourceUpdateBatch *rub = rhi->nextResourceUpdateBatch(); + rub->readBackTexture(rhiWidget->colorTexture(), &readResult); + QVERIFY(submitResourceUpdates(rhi, rub)); + QImage wrapperImage(reinterpret_cast<const uchar *>(readResult.data.constData()), + readResult.pixelSize.width(), readResult.pixelSize.height(), + QImage::Format_RGBA8888); + QImage image; + if (rhi->isYUpInFramebuffer()) + image = wrapperImage.mirrored(); + else + image = wrapperImage.copy(); + + const int maxFuzz = 1; + QRgb c = image.pixel(50, 5); + if (api != QRhiWidget::Api::Vulkan) { + // this should be the background (greenish), not the red triangle + QVERIFY(qGreen(c) > qRed(c)); + } else { + // remember that Vulkan is upside down due to not correcting for Y down in NDC + // hence this is red + QVERIFY(qRed(c) >= 255 - maxFuzz); + QVERIFY(qGreen(c) <= maxFuzz); + } + QVERIFY(qBlue(c) <= maxFuzz); + } +} + +QTEST_MAIN(tst_QRhiWidget) + +#include "tst_qrhiwidget.moc" diff --git a/tests/auto/widgets/widgets/qscrollarea/CMakeLists.txt b/tests/auto/widgets/widgets/qscrollarea/CMakeLists.txt index a10c911286..5e84614407 100644 --- a/tests/auto/widgets/widgets/qscrollarea/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qscrollarea/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qscrollarea.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qscrollarea Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qscrollarea LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qscrollarea SOURCES tst_qscrollarea.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/widgets/qscrollarea/tst_qscrollarea.cpp b/tests/auto/widgets/widgets/qscrollarea/tst_qscrollarea.cpp index 8a9200f406..87a623b223 100644 --- a/tests/auto/widgets/widgets/qscrollarea/tst_qscrollarea.cpp +++ b/tests/auto/widgets/widgets/qscrollarea/tst_qscrollarea.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -33,6 +8,7 @@ #include <qdebug.h> #include <qscrollarea.h> #include <qlayout.h> +#include <qscrollbar.h> class tst_QScrollArea : public QObject { @@ -46,6 +22,7 @@ private slots: void getSetCheck(); void ensureMicroFocusVisible_Task_167838(); void checkHFW_Task_197736(); + void stableHeightForWidth(); }; tst_QScrollArea::tst_QScrollArea() @@ -165,5 +142,61 @@ void tst_QScrollArea::checkHFW_Task_197736() QCOMPARE(w->height(), 200); } + +/* + If the scroll area rides the size where, due to the height-for-width + implementation of the widget, the vertical scrollbar is needed only + if the vertical scrollbar is visible, then we don't want it to flip + back and forth, but rather constrain the width of the widget. + See QTBUG-92958. +*/ +void tst_QScrollArea::stableHeightForWidth() +{ + struct HeightForWidthWidget : public QWidget + { + HeightForWidthWidget() + { + QSizePolicy policy = sizePolicy(); + policy.setHeightForWidth(true); + setSizePolicy(policy); + } + // Aspect ratio 1:1 + int heightForWidth(int width) const override { return width; } + }; + + class HeightForWidthArea : public QScrollArea + { + public: + HeightForWidthArea() + { + this->verticalScrollBar()->installEventFilter(this); + } + protected: + bool eventFilter(QObject *obj, QEvent *e) override + { + if (obj == verticalScrollBar() && e->type() == QEvent::Hide) + ++m_hideCount; + return QScrollArea::eventFilter(obj,e); + } + public: + int m_hideCount = 0; + }; + + HeightForWidthArea area; + HeightForWidthWidget equalWHWidget; + area.setWidget(&equalWHWidget); + area.setWidgetResizable(true); + // at this size, the widget wants to be 501 pixels high, + // requiring a vertical scrollbar in a 499 pixel high area. + // but the width resulting from showing the scrollbar would + // be less than 499, so no scrollbars would be needed anymore. + area.resize(501, 499); + area.show(); + QTest::qWait(500); + // if the scrollbar got hidden more than once, then the layout + // isn't stable. + QVERIFY(area.m_hideCount <= 1); +} + QTEST_MAIN(tst_QScrollArea) #include "tst_qscrollarea.moc" diff --git a/tests/auto/widgets/widgets/qscrollbar/CMakeLists.txt b/tests/auto/widgets/widgets/qscrollbar/CMakeLists.txt index fbaba594cf..23e31327e1 100644 --- a/tests/auto/widgets/widgets/qscrollbar/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qscrollbar/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qscrollbar.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qscrollbar Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qscrollbar LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qscrollbar SOURCES tst_qscrollbar.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::TestPrivate Qt::Widgets diff --git a/tests/auto/widgets/widgets/qscrollbar/tst_qscrollbar.cpp b/tests/auto/widgets/widgets/qscrollbar/tst_qscrollbar.cpp index a08e51cafd..fc836dec4a 100644 --- a/tests/auto/widgets/widgets/qscrollbar/tst_qscrollbar.cpp +++ b/tests/auto/widgets/widgets/qscrollbar/tst_qscrollbar.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -115,11 +90,11 @@ void tst_QScrollBar::task_209492() QSignalSpy spy(verticalScrollBar, SIGNAL(actionTriggered(int))); QCOMPARE(scrollArea.scrollCount, 0); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); // Simulate a mouse click on the "scroll down button". const QPoint pressPoint(verticalScrollBar->width() / 2, verticalScrollBar->height() - 10); - const QPoint globalPressPoint = verticalScrollBar->mapToGlobal(globalPressPoint); + const QPoint globalPressPoint = verticalScrollBar->mapToGlobal(pressPoint); QMouseEvent mousePressEvent(QEvent::MouseButtonPress, pressPoint, globalPressPoint, Qt::LeftButton, Qt::LeftButton, {}); QApplication::sendEvent(verticalScrollBar, &mousePressEvent); @@ -134,7 +109,7 @@ void tst_QScrollBar::task_209492() QSKIP("The result depends on system setting and is not relevant on Mac"); #endif QCOMPARE(scrollArea.scrollCount, 1); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } #if QT_CONFIG(wheelevent) @@ -184,7 +159,7 @@ void tst_QScrollBar::QTBUG_42871() QSignalSpy spy(&scrollBarWidget, SIGNAL(actionTriggered(int))); QVERIFY(spy.isValid()); QCOMPARE(myHandler.updatesCount, 0); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); // Simulate a mouse click on the "scroll down button". const QPoint pressPoint(scrollBarWidget.width() / 2, scrollBarWidget.height() - 10); @@ -192,13 +167,20 @@ void tst_QScrollBar::QTBUG_42871() QMouseEvent mousePressEvent(QEvent::MouseButtonPress, pressPoint, globalPressPoint, Qt::LeftButton, Qt::LeftButton, {}); QApplication::sendEvent(&scrollBarWidget, &mousePressEvent); + QElapsedTimer timer; + timer.start(); QTest::qWait(1); QMouseEvent mouseReleaseEvent(QEvent::MouseButtonRelease, pressPoint, globalPressPoint, Qt::LeftButton, Qt::LeftButton, {}); QApplication::sendEvent(&scrollBarWidget, &mouseReleaseEvent); + if (timer.elapsed() > 40) { + // took too long, we need to tolerate auto-repeat + if (myHandler.updatesCount > 1) + QEXPECT_FAIL("", "Took too long to process events, repeat timer fired", Continue); + } // Check that the action was triggered once. QCOMPARE(myHandler.updatesCount, 1); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), myHandler.updatesCount); } QTEST_MAIN(tst_QScrollBar) diff --git a/tests/auto/widgets/widgets/qsizegrip/CMakeLists.txt b/tests/auto/widgets/widgets/qsizegrip/CMakeLists.txt index c05b601d87..2de1583233 100644 --- a/tests/auto/widgets/widgets/qsizegrip/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qsizegrip/CMakeLists.txt @@ -1,15 +1,20 @@ -# Generated from qsizegrip.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qsizegrip Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsizegrip LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qsizegrip SOURCES tst_qsizegrip.cpp - INCLUDE_DIRECTORIES - . - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/widgets/qsizegrip/tst_qsizegrip.cpp b/tests/auto/widgets/widgets/qsizegrip/tst_qsizegrip.cpp index 1c69a1c8bd..a543efe44e 100644 --- a/tests/auto/widgets/widgets/qsizegrip/tst_qsizegrip.cpp +++ b/tests/auto/widgets/widgets/qsizegrip/tst_qsizegrip.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qslider/CMakeLists.txt b/tests/auto/widgets/widgets/qslider/CMakeLists.txt index 4fe495b3c5..664e9a52af 100644 --- a/tests/auto/widgets/widgets/qslider/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qslider/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qslider.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qslider Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qslider LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qslider SOURCES tst_qslider.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/widgets/qslider/tst_qslider.cpp b/tests/auto/widgets/widgets/qslider/tst_qslider.cpp index 3722025782..a70c8c484e 100644 --- a/tests/auto/widgets/widgets/qslider/tst_qslider.cpp +++ b/tests/auto/widgets/widgets/qslider/tst_qslider.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qspinbox/BLACKLIST b/tests/auto/widgets/widgets/qspinbox/BLACKLIST deleted file mode 100644 index 96a7732165..0000000000 --- a/tests/auto/widgets/widgets/qspinbox/BLACKLIST +++ /dev/null @@ -1,2 +0,0 @@ -[stepModifierPressAndHold] -opensuse-42.3 diff --git a/tests/auto/widgets/widgets/qspinbox/CMakeLists.txt b/tests/auto/widgets/widgets/qspinbox/CMakeLists.txt index adc0ee093f..826ce16d64 100644 --- a/tests/auto/widgets/widgets/qspinbox/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qspinbox/CMakeLists.txt @@ -1,13 +1,21 @@ -# Generated from qspinbox.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qspinbox Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qspinbox LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qspinbox SOURCES tst_qspinbox.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp b/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp index 724eff608f..dfb0f71139 100644 --- a/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp +++ b/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qdebug.h> #include <qapplication.h> @@ -54,6 +29,8 @@ #include <QProxyStyle> #include <QScreen> +#include <QtWidgets/private/qapplication_p.h> + #if QT_CONFIG(shortcut) # include <QKeySequence> #endif @@ -130,6 +107,28 @@ public: Qt::KeyboardModifier stepModifier = Qt::ControlModifier; }; +class SelectAllOnStepStyle : public QProxyStyle +{ +public: + SelectAllOnStepStyle(bool selectAll) + : selectAll(selectAll) + {} + + int styleHint(QStyle::StyleHint hint, const QStyleOption *option, + const QWidget *widget, QStyleHintReturn *returnData = nullptr) const override + { + switch (hint) { + case QStyle::SH_SpinBox_SelectOnStep: + return selectAll; + default: + return QProxyStyle::styleHint(hint, option, widget, returnData); + } + } + +private: + const bool selectAll; +}; + class tst_QSpinBox : public QObject { Q_OBJECT @@ -210,6 +209,10 @@ private slots: void stepModifierPressAndHold_data(); void stepModifierPressAndHold(); + + void stepSelectAll_data(); + void stepSelectAll(); + public slots: void textChangedHelper(const QString &); void valueChangedHelper(int); @@ -924,7 +927,7 @@ void tst_QSpinBox::editingFinished() layout->addWidget(box2); testFocusWidget.show(); - QApplication::setActiveWindow(&testFocusWidget); + QApplicationPrivate::setActiveWindow(&testFocusWidget); QVERIFY(QTest::qWaitForWindowActive(&testFocusWidget)); box->activateWindow(); box->setFocus(); @@ -938,45 +941,45 @@ void tst_QSpinBox::editingFinished() QTest::keyClick(box, Qt::Key_Up); QTest::keyClick(box, Qt::Key_Up); - QCOMPARE(editingFinishedSpy1.count(), 0); - QCOMPARE(editingFinishedSpy2.count(), 0); + QCOMPARE(editingFinishedSpy1.size(), 0); + QCOMPARE(editingFinishedSpy2.size(), 0); QTest::keyClick(box2, Qt::Key_Up); QTest::keyClick(box2, Qt::Key_Up); box2->setFocus(); - QCOMPARE(editingFinishedSpy1.count(), 1); + QCOMPARE(editingFinishedSpy1.size(), 1); box->setFocus(); - QCOMPARE(editingFinishedSpy1.count(), 1); - QCOMPARE(editingFinishedSpy2.count(), 1); + QCOMPARE(editingFinishedSpy1.size(), 1); + QCOMPARE(editingFinishedSpy2.size(), 1); QTest::keyClick(box, Qt::Key_Up); - QCOMPARE(editingFinishedSpy1.count(), 1); - QCOMPARE(editingFinishedSpy2.count(), 1); + QCOMPARE(editingFinishedSpy1.size(), 1); + QCOMPARE(editingFinishedSpy2.size(), 1); QTest::keyClick(box, Qt::Key_Enter); - QCOMPARE(editingFinishedSpy1.count(), 2); - QCOMPARE(editingFinishedSpy2.count(), 1); + QCOMPARE(editingFinishedSpy1.size(), 2); + QCOMPARE(editingFinishedSpy2.size(), 1); QTest::keyClick(box, Qt::Key_Return); - QCOMPARE(editingFinishedSpy1.count(), 3); - QCOMPARE(editingFinishedSpy2.count(), 1); + QCOMPARE(editingFinishedSpy1.size(), 3); + QCOMPARE(editingFinishedSpy2.size(), 1); box2->setFocus(); - QCOMPARE(editingFinishedSpy1.count(), 4); - QCOMPARE(editingFinishedSpy2.count(), 1); + QCOMPARE(editingFinishedSpy1.size(), 4); + QCOMPARE(editingFinishedSpy2.size(), 1); QTest::keyClick(box2, Qt::Key_Enter); - QCOMPARE(editingFinishedSpy1.count(), 4); - QCOMPARE(editingFinishedSpy2.count(), 2); + QCOMPARE(editingFinishedSpy1.size(), 4); + QCOMPARE(editingFinishedSpy2.size(), 2); QTest::keyClick(box2, Qt::Key_Return); - QCOMPARE(editingFinishedSpy1.count(), 4); - QCOMPARE(editingFinishedSpy2.count(), 3); + QCOMPARE(editingFinishedSpy1.size(), 4); + QCOMPARE(editingFinishedSpy2.size(), 3); testFocusWidget.hide(); - QCOMPARE(editingFinishedSpy1.count(), 4); - QCOMPARE(editingFinishedSpy2.count(), 4); + QCOMPARE(editingFinishedSpy1.size(), 4); + QCOMPARE(editingFinishedSpy2.size(), 4); //task203285 editingFinishedSpy1.clear(); testFocusWidget.show(); QVERIFY(QTest::qWaitForWindowActive(&testFocusWidget)); box->setKeyboardTracking(false); - qApp->setActiveWindow(&testFocusWidget); + QApplicationPrivate::setActiveWindow(&testFocusWidget); testFocusWidget.activateWindow(); box->setFocus(); QTRY_VERIFY(box->hasFocus()); @@ -986,7 +989,7 @@ void tst_QSpinBox::editingFinished() box2->setFocus(); QTRY_VERIFY(qApp->focusWidget() != box); QCOMPARE(box->text(), QLatin1String("20")); - QCOMPARE(editingFinishedSpy1.count(), 1); + QCOMPARE(editingFinishedSpy1.size(), 1); } void tst_QSpinBox::removeAll() @@ -1103,7 +1106,7 @@ void tst_QSpinBox::specialValue() spin.setValue(50); topWidget.show(); //make sure we have the focus (even if editingFinished fails) - qApp->setActiveWindow(&topWidget); + QApplicationPrivate::setActiveWindow(&topWidget); topWidget.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&topWidget)); spin.setFocus(); @@ -1149,33 +1152,32 @@ public: void tst_QSpinBox::sizeHint() { - QWidget *widget = new QWidget; - QHBoxLayout *layout = new QHBoxLayout(widget); + QWidget widget; + QHBoxLayout *layout = new QHBoxLayout(&widget); + sizeHint_SpinBox *spinBox = new sizeHint_SpinBox; layout->addWidget(spinBox); - widget->show(); - QVERIFY(QTest::qWaitForWindowExposed(widget)); + // Make sure all layout requests posted by the QHBoxLayout constructor and addWidget + // are processed before the widget is shown + QCoreApplication::sendPostedEvents(&widget, QEvent::LayoutRequest); + widget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&widget)); // Prefix spinBox->sizeHintRequests = 0; spinBox->setPrefix(QLatin1String("abcdefghij")); - qApp->processEvents(); QTRY_VERIFY(spinBox->sizeHintRequests > 0); // Suffix spinBox->sizeHintRequests = 0; spinBox->setSuffix(QLatin1String("abcdefghij")); - qApp->processEvents(); QTRY_VERIFY(spinBox->sizeHintRequests > 0); // Range spinBox->sizeHintRequests = 0; spinBox->setRange(0, 1234567890); spinBox->setValue(spinBox->maximum()); - qApp->processEvents(); QTRY_VERIFY(spinBox->sizeHintRequests > 0); - - delete widget; } void tst_QSpinBox::taskQTBUG_5008_textFromValueAndValidate() @@ -1208,7 +1210,7 @@ void tst_QSpinBox::taskQTBUG_5008_textFromValueAndValidate() spinbox.show(); spinbox.activateWindow(); spinbox.setFocus(); - QApplication::setActiveWindow(&spinbox); + QApplicationPrivate::setActiveWindow(&spinbox); QVERIFY(QTest::qWaitForWindowActive(&spinbox)); QVERIFY(spinbox.hasFocus()); QTRY_COMPARE(static_cast<QWidget *>(&spinbox), QApplication::activeWindow()); @@ -1256,7 +1258,7 @@ void tst_QSpinBox::lineEditReturnPressed() QSignalSpy spyCurrentChanged(spinBox.lineEdit(), SIGNAL(returnPressed())); spinBox.show(); QTest::keyClick(&spinBox, Qt::Key_Return); - QCOMPARE(spyCurrentChanged.count(), 1); + QCOMPARE(spyCurrentChanged.size(), 1); } void tst_QSpinBox::positiveSign() @@ -1310,7 +1312,7 @@ void tst_QSpinBox::interpretOnLosingFocus() void tst_QSpinBox::setGroupSeparatorShown_data() { QTest::addColumn<QLocale::Language>("lang"); - QTest::addColumn<QLocale::Country>("country"); + QTest::addColumn<QLocale::Territory>("country"); QTest::newRow("data0") << QLocale::English << QLocale::UnitedStates; QTest::newRow("data1") << QLocale::Swedish << QLocale::Sweden; @@ -1322,7 +1324,7 @@ void tst_QSpinBox::setGroupSeparatorShown_data() void tst_QSpinBox::setGroupSeparatorShown() { QFETCH(QLocale::Language, lang); - QFETCH(QLocale::Country, country); + QFETCH(QLocale::Territory, country); QLocale loc(lang, country); QLocale::setDefault(loc); @@ -1828,10 +1830,6 @@ void tst_QSpinBox::stepModifierPressAndHold() spin.setStyle(stepModifierStyle.data()); QSignalSpy spy(&spin, &SpinBox::valueChanged); - // TODO: remove debug output when QTBUG-69492 is fixed - connect(&spin, &SpinBox::valueChanged, [=]() { - qDebug() << QTime::currentTime() << "valueChanged emitted"; - }); spin.show(); QVERIFY(QTest::qWaitForWindowActive(&spin)); @@ -1846,13 +1844,50 @@ void tst_QSpinBox::stepModifierPressAndHold() qDebug() << "QGuiApplication::focusWindow():" << QGuiApplication::focusWindow(); qDebug() << "QGuiApplication::topLevelWindows():" << QGuiApplication::topLevelWindows(); QTest::mousePress(&spin, Qt::LeftButton, modifiers, buttonRect.center()); - QTRY_VERIFY2(spy.length() >= 3, qPrintable(QString::fromLatin1( - "Expected valueChanged() to be emitted 3 or more times, but it was only emitted %1 times").arg(spy.length()))); + QTRY_VERIFY2(spy.size() >= 3, qPrintable(QString::fromLatin1( + "Expected valueChanged() to be emitted 3 or more times, but it was only emitted %1 times").arg(spy.size()))); QTest::mouseRelease(&spin, Qt::LeftButton, modifiers, buttonRect.center()); const auto value = spy.last().at(0); QVERIFY(value.metaType().id() == QMetaType::Int); - QCOMPARE(value.toInt(), spy.length() * expectedStepModifier); + QCOMPARE(value.toInt(), spy.size() * expectedStepModifier); +} + +void tst_QSpinBox::stepSelectAll_data() +{ + QTest::addColumn<bool>("stepShouldSelectAll"); + QTest::addColumn<QStringList>("selectedText"); + + QTest::addRow("select all") << true << QStringList{"1", "0", "5", "4", "9"}; + QTest::addRow("don't select all") << false << QStringList{{}, {}, {}, {}, "94"}; +} + +void tst_QSpinBox::stepSelectAll() +{ + QFETCH(bool, stepShouldSelectAll); + QFETCH(QStringList, selectedText); + SelectAllOnStepStyle style(stepShouldSelectAll); + + SpinBox spinBox; + spinBox.setStyle(&style); + + QCOMPARE(spinBox.lineEdit()->selectedText(), QString()); + + auto it = selectedText.cbegin(); + spinBox.stepUp(); + QCOMPARE(spinBox.lineEdit()->selectedText(), *(it++)); + spinBox.lineEdit()->deselect(); + spinBox.stepDown(); + QCOMPARE(spinBox.lineEdit()->selectedText(), *(it++)); + spinBox.lineEdit()->deselect(); + spinBox.stepBy(5); + QCOMPARE(spinBox.lineEdit()->selectedText(), *(it++)); + spinBox.lineEdit()->deselect(); + QTest::keyClick(&spinBox, Qt::Key_Down); + QCOMPARE(spinBox.lineEdit()->selectedText(), *(it++)); + QTest::keyClicks(&spinBox, "9"); + QCOMPARE(spinBox.lineEdit()->selectedText(), QString()); + QCOMPARE(spinBox.lineEdit()->text(), *(it++)); } QTEST_MAIN(tst_QSpinBox) diff --git a/tests/auto/widgets/widgets/qsplashscreen/CMakeLists.txt b/tests/auto/widgets/widgets/qsplashscreen/CMakeLists.txt index 7c13db39a2..12602328c3 100644 --- a/tests/auto/widgets/widgets/qsplashscreen/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qsplashscreen/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qsplashscreen.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qsplashscreen Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsplashscreen LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qsplashscreen SOURCES tst_qsplashscreen.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/widgets/qsplashscreen/tst_qsplashscreen.cpp b/tests/auto/widgets/widgets/qsplashscreen/tst_qsplashscreen.cpp index c038682788..f57634152a 100644 --- a/tests/auto/widgets/widgets/qsplashscreen/tst_qsplashscreen.cpp +++ b/tests/auto/widgets/widgets/qsplashscreen/tst_qsplashscreen.cpp @@ -1,34 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSplashScreen> +#include <QTimer> class tst_QSplashScreen : public QObject { @@ -36,7 +12,7 @@ class tst_QSplashScreen : public QObject private slots: void checkCloseTime(); - void checkScreenConstructor(); + void checkConstructorAndShow(); }; class CloseEventSplash : public QSplashScreen @@ -45,7 +21,7 @@ public: CloseEventSplash(const QPixmap &pix) : QSplashScreen(pix), receivedCloseEvent(false) {} bool receivedCloseEvent; protected: - void closeEvent(QCloseEvent *event) + void closeEvent(QCloseEvent *event) override { receivedCloseEvent = true; QSplashScreen::closeEvent(event); @@ -60,23 +36,26 @@ void tst_QSplashScreen::checkCloseTime() QVERIFY(!splash.receivedCloseEvent); QWidget w; splash.show(); - QTimer::singleShot(500, &w, SLOT(show())); + QTimer::singleShot(10, &w, &QWidget::show); QVERIFY(!splash.receivedCloseEvent); splash.finish(&w); QVERIFY(splash.receivedCloseEvent); // We check the window handle because if this is not valid, then // it can't have been exposed QVERIFY(w.windowHandle()); - QVERIFY(w.windowHandle()->isExposed()); + QVERIFY(w.windowHandle()->isVisible()); } -void tst_QSplashScreen::checkScreenConstructor() +void tst_QSplashScreen::checkConstructorAndShow() { - for (const auto screen : QGuiApplication::screens()) { - QSplashScreen splash(screen); + QPixmap pix(100, 100); + pix.fill(Qt::red); + for (auto *screen : QGuiApplication::screens()) { + QSplashScreen splash(screen, pix); splash.show(); QCOMPARE(splash.screen(), screen); QVERIFY(splash.windowHandle()); + QVERIFY(splash.windowHandle()->isVisible()); QCOMPARE(splash.windowHandle()->screen(), screen); } } diff --git a/tests/auto/widgets/widgets/qsplitter/CMakeLists.txt b/tests/auto/widgets/widgets/qsplitter/CMakeLists.txt index 6cbbfd3792..16244c8834 100644 --- a/tests/auto/widgets/widgets/qsplitter/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qsplitter/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qsplitter.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qsplitter Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsplitter LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data list(APPEND test_data "extradata.txt") list(APPEND test_data "setSizes3.dat") @@ -11,7 +18,7 @@ list(APPEND test_data "setSizes3.dat") qt_internal_add_test(tst_qsplitter SOURCES tst_qsplitter.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets TESTDATA ${test_data} diff --git a/tests/auto/widgets/widgets/qsplitter/tst_qsplitter.cpp b/tests/auto/widgets/widgets/qsplitter/tst_qsplitter.cpp index ce7ac0a186..071e6d4cbc 100644 --- a/tests/auto/widgets/widgets/qsplitter/tst_qsplitter.cpp +++ b/tests/auto/widgets/widgets/qsplitter/tst_qsplitter.cpp @@ -1,33 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> +#include <QSignalSpy> + #include <qapplication.h> #include <qsplitter.h> #include <qstyle.h> @@ -81,6 +58,8 @@ private slots: void replaceWidget(); void replaceWidgetWithSplitterChild_data(); void replaceWidgetWithSplitterChild(); + void replaceWidgetWhileHidden_data(); + void replaceWidgetWhileHidden(); void handleMinimumWidth(); // task-specific tests below me: @@ -89,6 +68,7 @@ private slots: void task169702_sizes(); void taskQTBUG_4101_ensureOneNonCollapsedWidget_data(); void taskQTBUG_4101_ensureOneNonCollapsedWidget(); + void taskQTBUG_102249_moveNonPressed(); void setLayout(); void autoAdd(); @@ -851,6 +831,47 @@ void tst_QSplitter::replaceWidgetWithSplitterChild() } } +void tst_QSplitter::replaceWidgetWhileHidden_data() +{ + QTest::addColumn<bool>("splitterVisible"); + QTest::addColumn<bool>("widgetVisible"); + + QTest::addRow("visibleToVisible") << true << true; + QTest::addRow("hiddenToVisible") << true << false; + QTest::addRow("visibleToHidden") << false << true; + QTest::addRow("hiddenToHidden") << false << false; +} + +void tst_QSplitter::replaceWidgetWhileHidden() +{ + QFETCH(bool, splitterVisible); + QFETCH(bool, widgetVisible); + + MyFriendlySplitter splitter; + + splitter.addWidget(new QLabel("One")); + splitter.addWidget(new QLabel("Two")); + + if (splitterVisible) { + splitter.show(); + QVERIFY(QTest::qWaitForWindowExposed(&splitter)); + } + QWidget *newWidget = new QLabel("Three"); + if (!widgetVisible) + newWidget->hide(); + + const bool wasExplicitHide = !widgetVisible && newWidget->testAttribute(Qt::WA_WState_ExplicitShowHide); + splitter.replaceWidget(1, newWidget); + + QCOMPARE(!widgetVisible && newWidget->testAttribute(Qt::WA_WState_ExplicitShowHide), wasExplicitHide); + + if (!splitterVisible) { + splitter.show(); + QVERIFY(QTest::qWaitForWindowExposed(&splitter)); + } + QCOMPARE(widgetVisible, newWidget->isVisible()); +} + void tst_QSplitter::handleMinimumWidth() { MyFriendlySplitter split; @@ -887,55 +908,56 @@ void tst_QSplitter::rubberBandNotInSplitter() void tst_QSplitter::task187373_addAbstractScrollAreas_data() { - QTest::addColumn<QString>("className"); + QTest::addColumn<QByteArray>("className"); QTest::addColumn<bool>("addInConstructor"); QTest::addColumn<bool>("addOutsideConstructor"); - QStringList classNames; - classNames << QLatin1String("QGraphicsView"); - classNames << QLatin1String("QMdiArea"); - classNames << QLatin1String("QScrollArea"); - classNames << QLatin1String("QTextEdit"); - classNames << QLatin1String("QTreeView"); - - foreach (QString className, classNames) { - QTest::newRow(qPrintable(className + QLatin1String(" 1"))) << className << false << true; - QTest::newRow(qPrintable(className + QLatin1String(" 2"))) << className << true << false; - QTest::newRow(qPrintable(className + QLatin1String(" 3"))) << className << true << true; + QList<QByteArray> classNames{ + "QGraphicsView", + "QMdiArea", + "QScrollArea", + "QTextEdit", + "QTreeView" + }; + + for (const auto &className : std::as_const(classNames)) { + QTest::newRow(qPrintable(className + " 1")) << className << false << true; + QTest::newRow(qPrintable(className + " 2")) << className << true << false; + QTest::newRow(qPrintable(className + " 3")) << className << true << true; } } static QAbstractScrollArea *task187373_createScrollArea( - QSplitter *splitter, const QString &className, bool addInConstructor) + QSplitter *splitter, const QByteArray &className, bool addInConstructor) { - if (className == QLatin1String("QGraphicsView")) + if (className == "QGraphicsView") return new QGraphicsView(addInConstructor ? splitter : 0); - if (className == QLatin1String("QMdiArea")) + if (className == "QMdiArea") return new QMdiArea(addInConstructor ? splitter : 0); - if (className == QLatin1String("QScrollArea")) + if (className == "QScrollArea") return new QScrollArea(addInConstructor ? splitter : 0); - if (className == QLatin1String("QTextEdit")) + if (className == "QTextEdit") return new QTextEdit(addInConstructor ? splitter : 0); - if (className == QLatin1String("QTreeView")) + if (className == "QTreeView") return new QTreeView(addInConstructor ? splitter : 0); return 0; } void tst_QSplitter::task187373_addAbstractScrollAreas() { - QFETCH(QString, className); + QFETCH(QByteArray, className); QFETCH(bool, addInConstructor); QFETCH(bool, addOutsideConstructor); QVERIFY(addInConstructor || addOutsideConstructor); - QSplitter *splitter = new QSplitter; - splitter->show(); - QVERIFY(splitter->isVisible()); + QSplitter splitter; + splitter.show(); + QVERIFY(splitter.isVisible()); - QAbstractScrollArea *w = task187373_createScrollArea(splitter, className, addInConstructor); + QAbstractScrollArea *w = task187373_createScrollArea(&splitter, className, addInConstructor); QVERIFY(w); if (addOutsideConstructor) - splitter->addWidget(w); + splitter.addWidget(w); QTRY_VERIFY(w->isVisible()); QVERIFY(!w->isHidden()); @@ -1010,7 +1032,7 @@ void tst_QSplitter::taskQTBUG_4101_ensureOneNonCollapsedWidget() QFETCH(bool, testingHide); MyFriendlySplitter s; - QLabel *l; + QLabel *l = nullptr; for (int i = 0; i < 5; ++i) { l = new QLabel(QString("Label ") + QChar('A' + i)); l->setAlignment(Qt::AlignCenter); @@ -1026,6 +1048,24 @@ void tst_QSplitter::taskQTBUG_4101_ensureOneNonCollapsedWidget() QTRY_VERIFY(s.sizes().at(0) > 0); } +void tst_QSplitter::taskQTBUG_102249_moveNonPressed() +{ + QSplitter s; + s.setOpaqueResize(true); + s.addWidget(new QWidget()); + s.addWidget(new QWidget()); + s.show(); + + QSignalSpy spyMove(&s, &QSplitter::splitterMoved); + QPointF posOutOfWidget = QPointF(30, 30); + QMouseEvent me(QEvent::MouseMove, + posOutOfWidget, s.mapToGlobal(posOutOfWidget), + Qt::NoButton, Qt::MouseButtons(Qt::LeftButton), + Qt::NoModifier); + qApp->sendEvent(s.handle(0), &me); + QCOMPARE(spyMove.size(), 0); +} + void tst_QSplitter::setLayout() { QSplitter splitter; diff --git a/tests/auto/widgets/widgets/qstackedwidget/CMakeLists.txt b/tests/auto/widgets/widgets/qstackedwidget/CMakeLists.txt index 7c99df28a4..0c79cf29f4 100644 --- a/tests/auto/widgets/widgets/qstackedwidget/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qstackedwidget/CMakeLists.txt @@ -1,13 +1,21 @@ -# Generated from qstackedwidget.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qstackedwidget Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qstackedwidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qstackedwidget SOURCES tst_qstackedwidget.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/widgets/qstackedwidget/tst_qstackedwidget.cpp b/tests/auto/widgets/widgets/qstackedwidget/tst_qstackedwidget.cpp index df3b943e05..0a1b140867 100644 --- a/tests/auto/widgets/widgets/qstackedwidget/tst_qstackedwidget.cpp +++ b/tests/auto/widgets/widgets/qstackedwidget/tst_qstackedwidget.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -36,6 +11,8 @@ #include <QHBoxLayout> #include <qlineedit.h> +#include <QtWidgets/private/qapplication_p.h> + class tst_QStackedWidget : public QObject { Q_OBJECT @@ -182,7 +159,7 @@ void tst_QStackedWidget::dynamicPages() le11->setFocus(); // set focus to second widget in the page sw->resize(200, 200); sw->show(); - qApp->setActiveWindow(sw); + QApplicationPrivate::setActiveWindow(sw); QVERIFY(QTest::qWaitForWindowActive(sw)); QTRY_COMPARE(QApplication::focusWidget(), le11); diff --git a/tests/auto/widgets/widgets/qstatusbar/CMakeLists.txt b/tests/auto/widgets/widgets/qstatusbar/CMakeLists.txt index e4e19e230e..3bda170936 100644 --- a/tests/auto/widgets/widgets/qstatusbar/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qstatusbar/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qstatusbar.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qstatusbar Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qstatusbar LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qstatusbar SOURCES tst_qstatusbar.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/widgets/qstatusbar/tst_qstatusbar.cpp b/tests/auto/widgets/widgets/qstatusbar/tst_qstatusbar.cpp index d7079197ce..b0a53ba01a 100644 --- a/tests/auto/widgets/widgets/qstatusbar/tst_qstatusbar.cpp +++ b/tests/auto/widgets/widgets/qstatusbar/tst_qstatusbar.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -48,6 +23,7 @@ private slots: void tempMessage(); void insertWidget(); void insertPermanentWidget(); + void removeWidget(); void setSizeGripEnabled(); void task194017_hiddenWidget(); void QTBUG4334_hiddenOnMaximizedWindow(); @@ -129,6 +105,49 @@ void tst_QStatusBar::insertPermanentWidget() QCOMPARE(sb.insertPermanentWidget(1, new QLabel("foo")), 6); } +void tst_QStatusBar::removeWidget() +{ + QStatusBar sb; + std::vector<std::unique_ptr<QLabel>> widgets; + std::vector<bool> states; + for (int i = 0; i < 10; ++i) { + const QString text = i > 5 ? QString("p_%1").arg(i) : QString::number(i); + widgets.push_back(std::make_unique<QLabel>(text)); + states.push_back(true); + } + + for (auto &&widget : widgets) { + if (widget->text().startsWith("p_")) + sb.addPermanentWidget(widget.get()); + else + sb.addWidget(widget.get()); + } + sb.show(); + QVERIFY(QTest::qWaitForWindowExposed(&sb)); + + auto checkStates = [&]{ + for (size_t index = 0; index < std::size(widgets); ++index) { + if (widgets.at(index)->isVisible() != states.at(index)) { + qCritical("Mismatch for widget at index %zu\n" + "\tActual : %s\n" + "\tExpected: %s", + index, widgets.at(index)->isVisible() ? "true" : "false", + states.at(index) ? "true" : "false"); + return false; + } + } + return true; + }; + + QVERIFY(checkStates()); + // remove every widget except the first to trigger unstable reference + for (size_t i = 2; i < std::size(widgets); ++i) { + sb.removeWidget(widgets[i].get()); + states[i] = false; + QVERIFY2(checkStates(), qPrintable(QString("Failure at index %1").arg(i))); + } +} + void tst_QStatusBar::setSizeGripEnabled() { if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) @@ -297,18 +316,18 @@ void tst_QStatusBar::messageChangedSignal() testWidget->showMessage("Ready", 0); QCOMPARE(testWidget->currentMessage(), QString("Ready")); QCOMPARE(testWidget->currentMessage(), currentMessage); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().at(0).toString(), currentMessage); testWidget->clearMessage(); QCOMPARE(testWidget->currentMessage(), QString()); QCOMPARE(testWidget->currentMessage(), currentMessage); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().at(0).toString(), currentMessage); testWidget->showMessage("Ready", 0); testWidget->showMessage("Ready", 0); QCOMPARE(testWidget->currentMessage(), QString("Ready")); QCOMPARE(testWidget->currentMessage(), currentMessage); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().at(0).toString(), currentMessage); } diff --git a/tests/auto/widgets/widgets/qtabbar/BLACKLIST b/tests/auto/widgets/widgets/qtabbar/BLACKLIST deleted file mode 100644 index 735b044d8b..0000000000 --- a/tests/auto/widgets/widgets/qtabbar/BLACKLIST +++ /dev/null @@ -1,2 +0,0 @@ -[sizeHints] -redhatenterpriselinuxworkstation-6.6 diff --git a/tests/auto/widgets/widgets/qtabbar/CMakeLists.txt b/tests/auto/widgets/widgets/qtabbar/CMakeLists.txt index edc90d3138..b79e763819 100644 --- a/tests/auto/widgets/widgets/qtabbar/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qtabbar/CMakeLists.txt @@ -1,13 +1,21 @@ -# Generated from qtabbar.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtabbar Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtabbar LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qtabbar SOURCES tst_qtabbar.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/widgets/qtabbar/tst_qtabbar.cpp b/tests/auto/widgets/widgets/qtabbar/tst_qtabbar.cpp index 7f713ebe6d..03131cebe4 100644 --- a/tests/auto/widgets/widgets/qtabbar/tst_qtabbar.cpp +++ b/tests/auto/widgets/widgets/qtabbar/tst_qtabbar.cpp @@ -1,40 +1,22 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSignalSpy> #include <QApplication> #include <QTabBar> #include <QPushButton> +#include <QLabel> #include <QStyle> #include <QStyleOptionTab> #include <QProxyStyle> #include <QTimer> +#include <QScreen> +#include <QWindow> + +#include <QtWidgets/private/qtabbar_p.h> + +using namespace Qt::StringLiterals; class TabBar; @@ -68,6 +50,7 @@ private slots: void hideTab_data(); void hideTab(); void hideAllTabs(); + void checkHiddenTab(); void setElideMode_data(); void setElideMode(); @@ -77,6 +60,7 @@ private slots: void setUsesScrollButtons(); void removeLastTab(); + void removeLastVisibleTab(); void closeButton(); @@ -100,6 +84,25 @@ private slots: void mouseReleaseOutsideTabBar(); void mouseWheel(); + void kineticWheel_data(); + void kineticWheel(); + void highResolutionWheel_data(); + void highResolutionWheel(); + + void scrollButtons_data(); + void scrollButtons(); + + void currentTabLargeFont(); + + void hoverTab_data(); + void hoverTab(); + + void resizeKeepsScroll_data(); + void resizeKeepsScroll(); + void changeTabTextKeepsScroll(); + void settingCurrentTabBeforeShowDoesntScroll(); + void checkPositionsAfterShapeChange(); + void checkScrollOffsetAfterTabRemoval(); private: void checkPositions(const TabBar &tabbar, const QList<int> &positions); @@ -216,7 +219,7 @@ void tst_QTabBar::testCurrentChanged() QCOMPARE(tabBar.currentIndex(), 0); tabBar.setCurrentIndex(tabToSet); QCOMPARE(tabBar.currentIndex(), tabToSet); - QCOMPARE(spy.count(), expectedCount); + QCOMPARE(spy.size(), expectedCount); } class TabBar : public QTabBar @@ -225,6 +228,7 @@ public: using QTabBar::initStyleOption; using QTabBar::moveTab; using QTabBar::QTabBar; + using QTabBar::tabSizeHint; }; void tst_QTabBar::insertAtCurrentIndex() @@ -289,7 +293,7 @@ void tst_QTabBar::removeTab() tabbar.setCurrentIndex(currentIndex); QSignalSpy spy(&tabbar, SIGNAL(currentChanged(int))); tabbar.removeTab(deleteIndex); - QTEST(int(spy.count()), "spyCount"); + QTEST(int(spy.size()), "spyCount"); QTEST(tabbar.currentIndex(), "finalIndex"); } @@ -320,7 +324,7 @@ void tst_QTabBar::hideTab() tabbar.setCurrentIndex(currentIndex); QSignalSpy spy(&tabbar, &QTabBar::currentChanged); tabbar.setTabVisible(hideIndex, false); - QTEST(int(spy.count()), "spyCount"); + QTEST(int(spy.size()), "spyCount"); QTEST(tabbar.currentIndex(), "finalIndex"); } @@ -366,6 +370,25 @@ void tst_QTabBar::hideAllTabs() QVERIFY(sizeHint.width() < prevSizeHint.width()); } +void tst_QTabBar::checkHiddenTab() +{ + QTabBar tabbar; + + tabbar.addTab("foo"); + tabbar.addTab("bar"); + tabbar.addTab("baz"); + tabbar.setCurrentIndex(0); + tabbar.setTabVisible(1, false); + + QKeyEvent keyRight(QKeyEvent::KeyPress, Qt::Key_Right, Qt::NoModifier); + QVERIFY(QApplication::sendEvent(&tabbar, &keyRight)); + QCOMPARE(tabbar.currentIndex(), 2); + + QKeyEvent keyLeft(QKeyEvent::KeyPress, Qt::Key_Left, Qt::NoModifier); + QVERIFY(QApplication::sendEvent(&tabbar, &keyLeft)); + QCOMPARE(tabbar.currentIndex(), 0); +} + void tst_QTabBar::setElideMode_data() { QTest::addColumn<int>("tabElideMode"); @@ -464,16 +487,49 @@ void tst_QTabBar::removeLastTab() QTabBar tabbar; QSignalSpy spy(&tabbar, SIGNAL(currentChanged(int))); int index = tabbar.addTab("foo"); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toInt(), index); spy.clear(); tabbar.removeTab(index); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toInt(), -1); spy.clear(); } +void tst_QTabBar::removeLastVisibleTab() +{ + QTabBar tabbar; + tabbar.setSelectionBehaviorOnRemove(QTabBar::SelectionBehavior::SelectRightTab); + + int invisible = tabbar.addTab("invisible"); + int visible = tabbar.addTab("visible"); + tabbar.setCurrentIndex(visible); + tabbar.adjustSize(); + + tabbar.setTabVisible(invisible, false); + { + QSignalSpy spy(&tabbar, SIGNAL(currentChanged(int))); + tabbar.removeTab(visible); + QCOMPARE(spy.size(), 1); + QCOMPARE(spy.at(0).at(0).toInt(), -1); + QCOMPARE(tabbar.currentIndex(), -1); + } + + tabbar.setSelectionBehaviorOnRemove(QTabBar::SelectionBehavior::SelectLeftTab); + visible = tabbar.insertTab(0, "visible"); + ++invisible; + QVERIFY(!tabbar.isTabVisible(invisible)); + tabbar.setCurrentIndex(visible); + { + QSignalSpy spy(&tabbar, SIGNAL(currentChanged(int))); + tabbar.removeTab(visible); + QCOMPARE(spy.size(), 1); + QCOMPARE(spy.at(0).at(0).toInt(), -1); + QCOMPARE(tabbar.currentIndex(), -1); + } +} + void tst_QTabBar::closeButton() { QTabBar tabbar; @@ -492,7 +548,7 @@ void tst_QTabBar::closeButton() QSignalSpy spy(&tabbar, SIGNAL(tabCloseRequested(int))); button->click(); QCOMPARE(tabbar.count(), 1); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } Q_DECLARE_METATYPE(QTabBar::ButtonPosition) @@ -598,6 +654,8 @@ void tst_QTabBar::selectionBehaviorOnRemove_data() // every other one QTest::newRow("previous-10") << QTabBar::SelectPreviousTab << 7 << (IntList() << 0 << 2 << 4 << 6) << (IntList() << 6 << 4) << 2; + // QTBUG-94352 + QTest::newRow("QTBUG-94352") << QTabBar::SelectPreviousTab << 4 << (IntList() << 3) << (IntList() << 2 << 2) << 0; } @@ -762,36 +820,38 @@ void tst_QTabBar::tabBarClicked() QSignalSpy clickSpy(&tabBar, SIGNAL(tabBarClicked(int))); QSignalSpy doubleClickSpy(&tabBar, SIGNAL(tabBarDoubleClicked(int))); - QCOMPARE(clickSpy.count(), 0); - QCOMPARE(doubleClickSpy.count(), 0); + QCOMPARE(clickSpy.size(), 0); + QCOMPARE(doubleClickSpy.size(), 0); Qt::MouseButton button = Qt::LeftButton; while (button <= Qt::MaxMouseButton) { const QPoint tabPos = tabBar.tabRect(0).center(); QTest::mouseClick(&tabBar, button, {}, tabPos); - QCOMPARE(clickSpy.count(), 1); + QCOMPARE(clickSpy.size(), 1); QCOMPARE(clickSpy.takeFirst().takeFirst().toInt(), 0); - QCOMPARE(doubleClickSpy.count(), 0); + QCOMPARE(doubleClickSpy.size(), 0); QTest::mouseDClick(&tabBar, button, {}, tabPos); - QCOMPARE(clickSpy.count(), 1); + QCOMPARE(clickSpy.size(), 1); QCOMPARE(clickSpy.takeFirst().takeFirst().toInt(), 0); - QCOMPARE(doubleClickSpy.count(), 1); + QCOMPARE(doubleClickSpy.size(), 1); QCOMPARE(doubleClickSpy.takeFirst().takeFirst().toInt(), 0); + QTest::mouseRelease(&tabBar, button, {}, tabPos); const QPoint barPos(tabBar.tabRect(0).right() + 5, tabBar.tabRect(0).center().y()); QTest::mouseClick(&tabBar, button, {}, barPos); - QCOMPARE(clickSpy.count(), 1); + QCOMPARE(clickSpy.size(), 1); QCOMPARE(clickSpy.takeFirst().takeFirst().toInt(), -1); - QCOMPARE(doubleClickSpy.count(), 0); + QCOMPARE(doubleClickSpy.size(), 0); QTest::mouseDClick(&tabBar, button, {}, barPos); - QCOMPARE(clickSpy.count(), 1); + QCOMPARE(clickSpy.size(), 1); QCOMPARE(clickSpy.takeFirst().takeFirst().toInt(), -1); - QCOMPARE(doubleClickSpy.count(), 1); + QCOMPARE(doubleClickSpy.size(), 1); QCOMPARE(doubleClickSpy.takeFirst().takeFirst().toInt(), -1); + QTest::mouseRelease(&tabBar, button, {}, barPos); button = Qt::MouseButton(button << 1); } @@ -833,9 +893,10 @@ void tst_QTabBar::mouseReleaseOutsideTabBar() QRect rectToBeRepainted; bool eventFilter(QObject *, QEvent *event) override { - if (event->type() == QEvent::Paint - && rectToBeRepainted.contains(static_cast<QPaintEvent *>(event)->rect())) + if (event->type() == QEvent::Paint && + static_cast<QPaintEvent *>(event)->rect().contains(rectToBeRepainted)) { repainted = true; + } return false; } } repaintChecker; @@ -850,14 +911,15 @@ void tst_QTabBar::mouseReleaseOutsideTabBar() QRect tabRect = tabBar.tabRect(1); QPoint tabCenter = tabRect.center(); + repaintChecker.rectToBeRepainted = tabRect; + // if a press repaints the tab... QTest::mousePress(&tabBar, Qt::LeftButton, {}, tabCenter); - QTest::mouseEvent(QTest::MouseMove, &tabBar, Qt::LeftButton, {}, tabCenter + QPoint(tabCenter.x(), tabCenter.y() + tabRect.height())); + const bool pressRepainted = QTest::qWaitFor([&]{ return repaintChecker.repainted; }, 250); - // make sure the holding tab is repainted after releasing the mouse + // ... then releasing the mouse outside the tabbar should repaint it as well repaintChecker.repainted = false; - repaintChecker.rectToBeRepainted = tabRect; QTest::mouseRelease(&tabBar, Qt::LeftButton, {}, tabCenter + QPoint(tabCenter.x(), tabCenter.y() + tabRect.height())); - QTRY_VERIFY(repaintChecker.repainted); + QTRY_COMPARE(repaintChecker.repainted, pressRepainted); } void tst_QTabBar::checkPositions(const TabBar &tabbar, const QList<int> &positions) @@ -873,20 +935,25 @@ void tst_QTabBar::checkPositions(const TabBar &tabbar, const QList<int> &positio } #if QT_CONFIG(wheelevent) -// defined to be 120 by the wheel mouse vendors according to the docs -#define WHEEL_DELTA 120 class TabBarScrollingProxyStyle : public QProxyStyle { public: - TabBarScrollingProxyStyle() : QProxyStyle(), scrolling(true) + TabBarScrollingProxyStyle(const QString &defStyle = {}) + : QProxyStyle(defStyle), scrolling(true) { } int styleHint(StyleHint hint, const QStyleOption *option = 0, const QWidget *widget = 0, QStyleHintReturn *returnData = 0) const override { - if (hint == QStyle::SH_TabBar_AllowWheelScrolling) + switch (hint) { + case QStyle::SH_TabBar_AllowWheelScrolling: return scrolling; + case SH_TabBar_ElideMode: + return Qt::ElideNone; + default: + break; + } return QProxyStyle::styleHint(hint, option, widget, returnData); } @@ -896,38 +963,638 @@ public: void tst_QTabBar::mouseWheel() { + TabBar tabbar; - // apply custom style to app, which can toggle tabbar scrolling behavior - QCoreApplication *applicationInstance = QApplication::instance(); - QVERIFY(applicationInstance != 0); - auto *proxyStyle = new TabBarScrollingProxyStyle; - QApplication::setStyle(proxyStyle); + // apply custom style to the tabbar, which can toggle tabbar scrolling behavior + TabBarScrollingProxyStyle proxyStyle; + tabbar.setStyle(&proxyStyle); // make tabbar with three tabs, select the middle one - TabBar tabbar; tabbar.addTab("one"); tabbar.addTab("two"); tabbar.addTab("three"); int startIndex = 1; tabbar.setCurrentIndex(startIndex); + const auto systemId = QPointingDevice::primaryPointingDevice()->systemId() + 1; + QPointingDevice clickyWheel("test clicky wheel", systemId, QInputDevice::DeviceType::Mouse, + QPointingDevice::PointerType::Generic, + QInputDevice::Capability::Position | QInputDevice::Capability::Scroll, + 1, 3); + // define scroll event const QPoint wheelPoint = tabbar.rect().bottomRight(); - QWheelEvent event(wheelPoint, tabbar.mapToGlobal(wheelPoint), QPoint(), QPoint(0, WHEEL_DELTA), - Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false); + QWheelEvent event(wheelPoint, tabbar.mapToGlobal(wheelPoint), + QPoint(), QPoint(0, QWheelEvent::DefaultDeltasPerStep), + Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false, + Qt::MouseEventSynthesizedByApplication, &clickyWheel); // disable scrolling, send scroll event, confirm that tab did not change - proxyStyle->scrolling = false; - QVERIFY(applicationInstance->sendEvent(&tabbar, &event)); + proxyStyle.scrolling = false; + QVERIFY(QApplication::sendEvent(&tabbar, &event)); QVERIFY(tabbar.currentIndex() == startIndex); // enable scrolling, send scroll event, confirm that tab changed - proxyStyle->scrolling = true; - QVERIFY(applicationInstance->sendEvent(&tabbar, &event)); + proxyStyle.scrolling = true; + QVERIFY(QApplication::sendEvent(&tabbar, &event)); + QVERIFY(tabbar.currentIndex() != startIndex); +} + +void tst_QTabBar::kineticWheel_data() +{ + QTest::addColumn<QTabBar::Shape>("tabShape"); + + QTest::addRow("North") << QTabBar::RoundedNorth; + QTest::addRow("East") << QTabBar::RoundedEast; + QTest::addRow("South") << QTabBar::RoundedSouth; + QTest::addRow("West") << QTabBar::RoundedWest; +} + +void tst_QTabBar::kineticWheel() +{ + const auto systemId = QPointingDevice::primaryPointingDevice()->systemId() + 1; + QPointingDevice pixelPad("test pixel pad", systemId, QInputDevice::DeviceType::TouchPad, + QPointingDevice::PointerType::Generic, + QInputDevice::Capability::Position | QInputDevice::Capability::PixelScroll, + 1, 3); + + QFETCH(QTabBar::Shape, tabShape); + QWidget window; + TabBar tabbar(&window); + // Since the macOS style makes sure that all tabs are always visible, we + // replace it with the windows style for this test, and use the proxy that + // makes sure that scrolling is enabled and that tab texts are not elided. + QString defaultStyle; + if (QApplication::style()->name() == QStringLiteral("macos")) + defaultStyle = "windows"; + TabBarScrollingProxyStyle proxyStyle(defaultStyle); + tabbar.setStyle(&proxyStyle); + + tabbar.addTab("long tab text 1"); + tabbar.addTab("long tab text 2"); + tabbar.addTab("long tab text 3"); + + // Make sure we don't have enough space for the tabs and need to scroll + const int tabbarLength = tabbar.tabRect(0).width() * 2; + + tabbar.setShape(tabShape); + const bool horizontal = tabShape == QTabBar::RoundedNorth + || tabShape == QTabBar::RoundedSouth; + if (horizontal) + tabbar.setFixedWidth(tabbarLength); + else + tabbar.setFixedHeight(tabbarLength); + + // start with the middle tab, QTabBar will scroll to make it visible + const int startIndex = 1; + tabbar.setCurrentIndex(startIndex); + + window.setMinimumSize(tabbarLength, tabbarLength); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + const auto *leftButton = tabbar.findChild<QAbstractButton*>(u"ScrollLeftButton"_s); + const auto *rightButton = tabbar.findChild<QAbstractButton*>(u"ScrollRightButton"_s); + QVERIFY(leftButton && rightButton); + QVERIFY(leftButton->isEnabled() && rightButton->isEnabled()); + + // Figure out if any of the buttons is laid out to be in front of the tabs. + // We can't use setUsesScrollButtons(false), as then several styles will enforce + // a minimum size for the tab bar. + const bool leftInFront = ((horizontal && leftButton->pos().x() < tabbar.rect().center().x()) + || (!horizontal && leftButton->pos().y() < tabbar.rect().center().y())); + const bool rightInFront = ((horizontal && rightButton->pos().x() < tabbar.rect().center().x()) + || (!horizontal && rightButton->pos().y() < tabbar.rect().center().y())); + QPoint leftEdge; + QPoint rightEdge; + if (leftInFront && rightInFront) { // both on the left + leftEdge = rightButton->geometry().bottomRight(); + rightEdge = tabbar.rect().bottomRight(); + } else if (leftInFront && !rightInFront) { + leftEdge = leftButton->geometry().bottomRight(); + rightEdge = rightButton->geometry().topLeft(); + } else { // both on the right + leftEdge = QPoint(0, 0); + rightEdge = leftButton->geometry().topLeft(); + } + // avoid border lines + leftEdge += QPoint(2, 2); + if (horizontal) { + rightEdge += QPoint(-2, 2); + } else { + rightEdge += QPoint(2, -2); + } + + QCOMPARE(tabbar.tabAt(leftEdge), 0); + QCOMPARE(tabbar.tabAt(rightEdge), 1); + + const QPoint delta = horizontal ? QPoint(10, 0) : QPoint(0, 10); + const QPoint wheelPoint = tabbar.rect().center(); + + bool accepted = true; + Qt::ScrollPhase phase = Qt::ScrollBegin; + // scroll all the way to the end + while (accepted) { + QWheelEvent event(wheelPoint, tabbar.mapToGlobal(wheelPoint), -delta, -delta, + Qt::NoButton, Qt::NoModifier, phase, false, + Qt::MouseEventSynthesizedByApplication, &pixelPad); + if (phase == Qt::ScrollBegin) + phase = Qt::ScrollUpdate; + QApplication::sendEvent(&tabbar, &event); + accepted = event.isAccepted(); + } + QCOMPARE(tabbar.tabAt(leftEdge), 1); + QCOMPARE(tabbar.tabAt(rightEdge), 2); + QVERIFY(leftButton->isEnabled()); + QVERIFY(!rightButton->isEnabled()); + // kinetic wheel events don't change the current index + QVERIFY(tabbar.currentIndex() == startIndex); + + // scroll all the way to the beginning + accepted = true; + while (accepted) { + QWheelEvent event(wheelPoint, tabbar.mapToGlobal(wheelPoint), delta, delta, + Qt::NoButton, Qt::NoModifier, phase, false, + Qt::MouseEventSynthesizedByApplication, &pixelPad); + QApplication::sendEvent(&tabbar, &event); + accepted = event.isAccepted(); + } + + QCOMPARE(tabbar.tabAt(leftEdge), 0); + QCOMPARE(tabbar.tabAt(rightEdge), 1); + QVERIFY(!leftButton->isEnabled()); + QVERIFY(rightButton->isEnabled()); + // kinetic wheel events don't change the current index + QVERIFY(tabbar.currentIndex() == startIndex); + + // make tabs small so that we have enough space, and verify sure we can't scroll + tabbar.setTabText(0, "A"); + tabbar.setTabText(1, "B"); + tabbar.setTabText(2, "C"); + QVERIFY(tabbar.sizeHint().width() <= tabbar.width() && tabbar.sizeHint().height() <= tabbar.height()); + + { + QWheelEvent event(wheelPoint, tabbar.mapToGlobal(wheelPoint), -delta, -delta, + Qt::NoButton, Qt::NoModifier, phase, false, + Qt::MouseEventSynthesizedByApplication, &pixelPad); + QApplication::sendEvent(&tabbar, &event); + QVERIFY(!event.isAccepted()); + } + + { + QWheelEvent event(wheelPoint, tabbar.mapToGlobal(wheelPoint), delta, delta, + Qt::NoButton, Qt::NoModifier, phase, false, + Qt::MouseEventSynthesizedByApplication, &pixelPad); + QApplication::sendEvent(&tabbar, &event); + QVERIFY(!event.isAccepted()); + } +} + +void tst_QTabBar::highResolutionWheel_data() +{ + QTest::addColumn<int>("angleDelta"); + // Smallest angleDelta for a Logitech MX Master 3 with Linux/X11/Libinput + QTest::addRow("increment index") << -16; + QTest::addRow("decrement index") << 16; +} + +void tst_QTabBar::highResolutionWheel() +{ + TabBar tabbar; + TabBarScrollingProxyStyle proxyStyle; + tabbar.setStyle(&proxyStyle); + + tabbar.addTab("tab1"); + tabbar.addTab("tab2"); + QFETCH(int, angleDelta); + // Negative values increment, positive values decrement + int startIndex = angleDelta < 0 ? 0 : 1; + tabbar.setCurrentIndex(startIndex); + + const auto systemId = QPointingDevice::primaryPointingDevice()->systemId() + 1; + QPointingDevice hiResWheel( + "test high resolution wheel", systemId, QInputDevice::DeviceType::Mouse, + QPointingDevice::PointerType::Generic, + QInputDevice::Capability::Position | QInputDevice::Capability::Scroll, 1, 3); + + const QPoint wheelPoint = tabbar.rect().bottomRight(); + QWheelEvent event(wheelPoint, tabbar.mapToGlobal(wheelPoint), QPoint(), + QPoint(angleDelta, angleDelta), Qt::NoButton, Qt::NoModifier, + Qt::NoScrollPhase, false, Qt::MouseEventSynthesizedByApplication, + &hiResWheel); + + proxyStyle.scrolling = true; + for (int accumulated = 0; accumulated < QWheelEvent::DefaultDeltasPerStep; + accumulated += qAbs(angleDelta)) { + // verify that nothing has changed until the threshold has been reached + QVERIFY(tabbar.currentIndex() == startIndex); + QVERIFY(QApplication::sendEvent(&tabbar, &event)); + } QVERIFY(tabbar.currentIndex() != startIndex); } #endif // QT_CONFIG(wheelevent) +void tst_QTabBar::scrollButtons_data() +{ + QTest::addColumn<QTabWidget::TabPosition>("tabPosition"); + QTest::addColumn<Qt::LayoutDirection>("layoutDirection"); + + for (auto ld : {Qt::LeftToRight, Qt::RightToLeft}) { + const char *ldStr = ld == Qt::LeftToRight ? "LTR" : "RTL"; + QTest::addRow("North, %s", ldStr) << QTabWidget::North << ld; + QTest::addRow("South, %s", ldStr) << QTabWidget::South << ld; + QTest::addRow("West, %s", ldStr) << QTabWidget::West << ld; + QTest::addRow("East, %s", ldStr) << QTabWidget::East << ld; + } +} + +void tst_QTabBar::scrollButtons() +{ + QFETCH(QTabWidget::TabPosition, tabPosition); + QFETCH(Qt::LayoutDirection, layoutDirection); + + QWidget window; + QTabWidget tabWidget(&window); + tabWidget.setLayoutDirection(layoutDirection); + tabWidget.setTabPosition(tabPosition); + tabWidget.setElideMode(Qt::ElideNone); + tabWidget.setUsesScrollButtons(true); + + const int tabCount = 5; + for (int i = 0; i < tabCount; ++i) + { + const QString num = QString::number(i); + tabWidget.addTab(new QLabel(num), num + " - Really long tab name to force arrows"); + } + tabWidget.move(0, 0); + tabWidget.resize(tabWidget.minimumSizeHint()); + window.show(); + QVERIFY(QTest::qWaitForWindowActive(&window)); + + auto *leftB = tabWidget.tabBar()->findChild<QAbstractButton*>(u"ScrollLeftButton"_s); + auto *rightB = tabWidget.tabBar()->findChild<QAbstractButton*>(u"ScrollRightButton"_s); + + QVERIFY(leftB->isVisible()); + QVERIFY(!leftB->isEnabled()); + QVERIFY(rightB->isVisible()); + QVERIFY(rightB->isEnabled()); + QVERIFY(!tabWidget.tabBar()->tabRect(1).intersects(tabWidget.tabBar()->rect())); + + int index = 0; + for (; index < tabWidget.count(); ++index) { + QCOMPARE(leftB->isEnabled(), index > 0); + QCOMPARE(rightB->isEnabled(), index < tabWidget.count() - 1); + QVERIFY(tabWidget.tabBar()->tabRect(index).intersects(tabWidget.tabBar()->rect())); + QCOMPARE(tabWidget.tabBar()->tabAt(tabWidget.tabBar()->rect().center()), index); + if (rightB->isEnabled()) + rightB->click(); + } + for (--index; index >= 0; --index) { + QCOMPARE(leftB->isEnabled(), index >= 0); + QCOMPARE(rightB->isEnabled(), index < tabWidget.count() - 1); + + QVERIFY(tabWidget.tabBar()->tabRect(index).intersects(tabWidget.tabBar()->rect())); + if (leftB->isEnabled()) + leftB->click(); + } + QVERIFY(!leftB->isEnabled()); +} + +void tst_QTabBar::currentTabLargeFont() +{ + TabBar tabBar; + tabBar.setStyleSheet(R"( + QTabBar::tab::selected { + font-size: 24pt; + } + )"); + + tabBar.addTab("Tab Item 1"); + tabBar.addTab("Tab Item 2"); + tabBar.addTab("Tab Item 3"); + + tabBar.setCurrentIndex(0); + tabBar.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tabBar)); + + QList<QRect> oldTabRects; + oldTabRects << tabBar.tabRect(0) << tabBar.tabRect(1) << tabBar.tabRect(2); + tabBar.setCurrentIndex(1); + QList<QRect> newTabRects; + newTabRects << tabBar.tabRect(0) << tabBar.tabRect(1) << tabBar.tabRect(2); + QVERIFY(oldTabRects != newTabRects); +} + +void tst_QTabBar::hoverTab_data() +{ + // Move the cursor away from the widget as not to interfere. + // skip this test if we can't + const QPoint topLeft = QGuiApplication::primaryScreen()->availableGeometry().topLeft(); + const QPoint cursorPos = topLeft + QPoint(10, 10); + QCursor::setPos(cursorPos); + if (!QTest::qWaitFor([cursorPos]{ return QCursor::pos() == cursorPos; }, 500)) + QSKIP("Can't move mouse"); + + QTest::addColumn<bool>("documentMode"); + QTest::addRow("normal mode") << true; + QTest::addRow("document mode") << true; +} + +void tst_QTabBar::hoverTab() +{ + QFETCH(bool, documentMode); + QWidget topLevel; + class TabBar : public QTabBar + { + public: + using QTabBar::QTabBar; + void initStyleOption(QStyleOptionTab *option, int tabIndex) const override + { + QTabBar::initStyleOption(option, tabIndex); + styleOptions[tabIndex] = *option; + } + mutable QHash<int, QStyleOptionTab> styleOptions; + } tabbar(&topLevel); + + tabbar.setDocumentMode(documentMode); + tabbar.addTab("A"); + tabbar.addTab("B"); + tabbar.addTab("C"); + tabbar.addTab("D"); + + tabbar.move(0,0); + const QSize size = tabbar.sizeHint(); + const auto center = topLevel.screen()->availableGeometry().center(); + topLevel.move(center - QPoint{size.width(), size.height()} / 2); + topLevel.setMinimumSize(size); + tabbar.ensurePolished(); + + // some styles set those flags, some don't. If not, use a style sheet + if (!(tabbar.testAttribute(Qt::WA_Hover) || tabbar.hasMouseTracking())) { + tabbar.setStyleSheet(R"( + QTabBar::tab { background: blue; } + QTabBar::tab::hover { background: yellow; } + QTabBar::tab::selected { background: red; } + )"); + } + + topLevel.show(); + QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); + auto *window = topLevel.windowHandle(); + + auto pos = tabbar.mapToParent(tabbar.tabRect(0).center()); + QTest::mouseMove(window, pos); + QTRY_VERIFY(tabbar.styleOptions[0].state & QStyle::State_Selected); + QTRY_COMPARE(tabbar.styleOptions[1].state & QStyle::State_MouseOver, QStyle::State_None); + QTRY_COMPARE(tabbar.styleOptions[2].state & QStyle::State_MouseOver, QStyle::State_None); + QTRY_COMPARE(tabbar.styleOptions[3].state & QStyle::State_MouseOver, QStyle::State_None); + + pos = tabbar.mapToParent(tabbar.tabRect(1).center()); + QTest::mouseMove(window, pos); + QTRY_COMPARE(tabbar.styleOptions[1].state & QStyle::State_MouseOver, QStyle::State_MouseOver); + QCOMPARE(tabbar.styleOptions[2].state & QStyle::State_MouseOver, QStyle::State_None); + QCOMPARE(tabbar.styleOptions[3].state & QStyle::State_MouseOver, QStyle::State_None); + + pos = tabbar.mapToParent(tabbar.tabRect(2).center()); + QTest::mouseMove(window, pos); + QTRY_COMPARE(tabbar.styleOptions[2].state & QStyle::State_MouseOver, QStyle::State_MouseOver); + QCOMPARE(tabbar.styleOptions[1].state & QStyle::State_MouseOver, QStyle::State_None); + QCOMPARE(tabbar.styleOptions[3].state & QStyle::State_MouseOver, QStyle::State_None); + + // removing tab 2 lays the tabs out so that they stretch across the + // tab bar; tab 1 is now where the cursor was. What matters is that a + // different tab is now hovered (rather than none). + tabbar.removeTab(2); + QTRY_COMPARE(tabbar.styleOptions[1].state & QStyle::State_MouseOver, QStyle::State_MouseOver); + QCOMPARE(tabbar.styleOptions[2].state & QStyle::State_MouseOver, QStyle::State_None); + + // inserting a tab at index 2 again should paint the new tab hovered + tabbar.insertTab(2, "C2"); + QTRY_COMPARE(tabbar.styleOptions[2].state & QStyle::State_MouseOver, QStyle::State_MouseOver); + QCOMPARE(tabbar.styleOptions[1].state & QStyle::State_MouseOver, QStyle::State_None); +} + + +void tst_QTabBar::resizeKeepsScroll_data() +{ + QTest::addColumn<QTabBar::Shape>("tabShape"); + QTest::addColumn<bool>("expanding"); + + QTest::addRow("North, expanding") << QTabBar::RoundedNorth << true; + QTest::addRow("East, expanding") << QTabBar::RoundedEast << true; + QTest::addRow("South, expanding") << QTabBar::RoundedSouth << true; + QTest::addRow("West, expanding") << QTabBar::RoundedWest << true; + + QTest::addRow("North, not expanding") << QTabBar::RoundedNorth << false; + QTest::addRow("South, not expanding") << QTabBar::RoundedSouth << false; +} + +void tst_QTabBar::resizeKeepsScroll() +{ + QFETCH(QTabBar::Shape, tabShape); + QFETCH(const bool, expanding); + + QTabBar tabBar; + TabBarScrollingProxyStyle proxyStyle; + tabBar.setStyle(&proxyStyle); + + for (int i = 0; i < 10; ++i) + tabBar.addTab(u"Tab Number %1"_s.arg(i)); + + tabBar.setShape(tabShape); + tabBar.setUsesScrollButtons(true); + tabBar.setExpanding(expanding); + + // resize to half + const QSize fullSize = tabBar.sizeHint(); + const bool horizontal = fullSize.width() > fullSize.height(); + if (horizontal) + tabBar.resize(fullSize.width() / 2, fullSize.height()); + else + tabBar.resize(fullSize.width(), fullSize.height() / 2); + + tabBar.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tabBar)); + + const auto getScrollOffset = [&]() -> int { + return static_cast<QTabBarPrivate *>(QObjectPrivate::get(&tabBar))->scrollOffset; + }; + + // select a tab outside, this will scroll + tabBar.setCurrentIndex(6); + // the first tab is now scrolled out + const int scrollOffset = getScrollOffset(); + QCOMPARE_GT(scrollOffset, 0); + // the current index is now fully visible, with margin on both sides + tabBar.setCurrentIndex(5); + + // make the tab bar a bit larger, by the width of a tab + if (horizontal) + tabBar.resize(tabBar.width() + tabBar.tabRect(5).width(), tabBar.height()); + else + tabBar.resize(tabBar.width(), tabBar.height() + tabBar.tabRect(5).height()); + + // this should not change the scroll + QCOMPARE(getScrollOffset(), scrollOffset); + + // make the tab bar large enough to fit everything with extra space + tabBar.resize(fullSize + QSize(50, 50)); + + // there should be no scroll + QCOMPARE(getScrollOffset(), 0); + + for (int i = 0; i < tabBar.count(); ++i) { + tabBar.setCurrentIndex(i); + QCOMPARE(getScrollOffset(), 0); + } +} + +void tst_QTabBar::changeTabTextKeepsScroll() +{ + QTabBar tabBar; + TabBarScrollingProxyStyle proxyStyle; + tabBar.setStyle(&proxyStyle); + + for (int i = 0; i < 6; ++i) + tabBar.addTab(u"Tab Number %1"_s.arg(i)); + + const QSize fullSize = tabBar.sizeHint(); + tabBar.resize(fullSize.width() / 2, fullSize.height()); + + tabBar.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tabBar)); + + const auto getScrollOffset = [&]() -> int { + return static_cast<QTabBarPrivate *>(QObjectPrivate::get(&tabBar))->scrollOffset; + }; + + tabBar.setCurrentIndex(3); + const int scrollOffset = getScrollOffset(); + tabBar.setTabText(3, "New title"); + QCOMPARE(getScrollOffset(), scrollOffset); +} + +void tst_QTabBar::settingCurrentTabBeforeShowDoesntScroll() +{ + QTabBar tabBar; + TabBarScrollingProxyStyle proxyStyle; + tabBar.setStyle(&proxyStyle); + + for (int i = 0; i < 6; ++i) + tabBar.addTab(u"Tab Number %1"_s.arg(i)); + + const auto getScrollOffset = [&]() -> int { + return static_cast<QTabBarPrivate *>(QObjectPrivate::get(&tabBar))->scrollOffset; + }; + + tabBar.setCurrentIndex(5); + + // changing the current index while the tab bar isn't visible shouldn't scroll yet + QCOMPARE(getScrollOffset(), 0); + + // now show the tab bar with a size that's too small to fit the current index + const QSize fullSize = tabBar.sizeHint(); + tabBar.resize(fullSize.width() / 2, fullSize.height()); + + tabBar.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tabBar)); + + // this should scroll + QCOMPARE_GT(getScrollOffset(), 0); +} + +void tst_QTabBar::checkPositionsAfterShapeChange() +{ + class TabWidget : public QTabWidget + { + public: + using QTabWidget::QTabWidget; + using QTabWidget::setTabBar; + }; + + class TabBar : public QTabBar + { + public: + using QTabBar::initStyleOption; + void resizeEvent(QResizeEvent *e) override + { + QTabBar::resizeEvent(e); + resized = true; + } + bool resized = false; + }; + + TabWidget tabWidget; + auto *tabBar = new TabBar; + tabWidget.setTabBar(tabBar); + for (int i = 0; i < 3; ++i) + tabWidget.addTab(new QWidget, u"Tab %1"_s.arg(i)); + tabWidget.setTabPosition(QTabWidget::North); + tabWidget.setCurrentIndex(2); + tabWidget.resize(300, 300); + tabWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tabWidget)); + + tabBar->resized = false; + tabWidget.setTabPosition(QTabWidget::East); + QVERIFY(QTest::qWaitFor([&]() { return tabBar->resized; })); + QStyleOptionTab opt; + tabBar->initStyleOption(&opt, 2); + QVERIFY(opt.rect.top() > 0); +} + +void tst_QTabBar::checkScrollOffsetAfterTabRemoval() +{ + QTabWidget tabWidget; + QTabBar *tabBar = tabWidget.tabBar(); + for (int i = 0; i < 10; ++i) + tabWidget.addTab(new QWidget, u"Tab %1"_s.arg(i)); + tabWidget.setTabPosition(QTabWidget::North); + tabWidget.resize(300, 300); + tabWidget.setCurrentIndex(0); + tabWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tabWidget)); + + auto *rightButton = tabBar->findChild<QAbstractButton *>(u"ScrollRightButton"_s); + auto *leftButton = tabBar->findChild<QAbstractButton *>(u"ScrollLeftButton"_s); + QVERIFY(leftButton); + QVERIFY(rightButton); + QVERIFY(rightButton->isEnabled()); + QVERIFY(!leftButton->isEnabled()); + // scroll to the right + tabBar->setCurrentIndex(9); + QVERIFY(!rightButton->isEnabled()); + QVERIFY(leftButton->isEnabled()); + // scroll to the center + tabBar->setCurrentIndex(2); + QVERIFY(rightButton->isEnabled()); + QVERIFY(leftButton->isEnabled()); + + const auto getScrollOffset = [&]() -> int { + return static_cast<QTabBarPrivate *>(QObjectPrivate::get(tabBar))->scrollOffset; + }; + // the scroll offset should not change when a tab right outside + // the scroll rect is removed + auto oldOffset = getScrollOffset(); + tabWidget.removeTab(9); + QCOMPARE(getScrollOffset(), oldOffset); + // the scroll offset must change when a tab left outside + // the scroll rect is removed + oldOffset = getScrollOffset(); + tabWidget.removeTab(0); + QVERIFY(getScrollOffset() < oldOffset); + + // the scroll offset must change when there is empty + // place in the right after tab removal + oldOffset = getScrollOffset(); + QVERIFY(oldOffset > 0); + for (int i : { 7, 6, 5, 4, 3 }) + tabWidget.removeTab(i); + QCOMPARE(getScrollOffset(), 0); + QVERIFY(!rightButton->isVisible()); + QVERIFY(!leftButton->isVisible()); +} + QTEST_MAIN(tst_QTabBar) #include "tst_qtabbar.moc" diff --git a/tests/auto/widgets/widgets/qtabwidget/CMakeLists.txt b/tests/auto/widgets/widgets/qtabwidget/CMakeLists.txt index d0689d0b66..a8b48e925c 100644 --- a/tests/auto/widgets/widgets/qtabwidget/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qtabwidget/CMakeLists.txt @@ -1,15 +1,22 @@ -# Generated from qtabwidget.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtabwidget Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtabwidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qtabwidget SOURCES tst_qtabwidget.cpp INCLUDE_DIRECTORIES .. - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets Qt::WidgetsPrivate @@ -19,6 +26,6 @@ qt_internal_add_test(tst_qtabwidget ##################################################################### qt_internal_extend_target(tst_qtabwidget CONDITION WIN32 - PUBLIC_LIBRARIES + LIBRARIES user32 ) diff --git a/tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp b/tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp index 0e7012482d..d7bfdfaad2 100644 --- a/tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp +++ b/tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -85,6 +60,7 @@ private slots: void tabPosition(); void tabEnabled(); void tabHidden(); + void checkHiddenTab(); void tabText(); void tabShape(); void tabTooltip(); @@ -104,6 +80,9 @@ private slots: void moveCurrentTab(); void autoHide(); + void setCurrentBeforeShow_data(); + void setCurrentBeforeShow(); + private: int addPage(); void removePage(int index); @@ -274,6 +253,32 @@ void tst_QTabWidget::tabHidden() } } +void tst_QTabWidget::checkHiddenTab() +{ + tw->addTab(new QWidget(), "foo"); + tw->addTab(new QWidget(), "bar"); + tw->addTab(new QWidget(), "baz"); + QCOMPARE(tw->count(), 3); + tw->setCurrentIndex(0); + tw->setTabVisible(1, false); + + QKeyEvent keyTab(QKeyEvent::KeyPress, Qt::Key_Tab, Qt::ControlModifier); + QVERIFY(QApplication::sendEvent(tw, &keyTab)); + QCOMPARE(tw->currentIndex(), 2); + QVERIFY(QApplication::sendEvent(tw, &keyTab)); + QCOMPARE(tw->currentIndex(), 0); + QVERIFY(QApplication::sendEvent(tw, &keyTab)); + QCOMPARE(tw->currentIndex(), 2); + + QKeyEvent keyBacktab(QKeyEvent::KeyPress, Qt::Key_Backtab, Qt::ControlModifier); + QVERIFY(QApplication::sendEvent(tw, &keyBacktab)); + QCOMPARE(tw->currentIndex(), 0); + QVERIFY(QApplication::sendEvent(tw, &keyBacktab)); + QCOMPARE(tw->currentIndex(), 2); + QVERIFY(QApplication::sendEvent(tw, &keyBacktab)); + QCOMPARE(tw->currentIndex(), 0); +} + void tst_QTabWidget::tabText() { // Test bad arguments @@ -383,12 +388,12 @@ void tst_QTabWidget::currentIndex() QCOMPARE(tw->currentIndex(), -1); tw->setCurrentIndex(-1); QCOMPARE(tw->currentIndex(), -1); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); int firstIndex = addPage(); tw->setCurrentIndex(firstIndex); QCOMPARE(tw->currentIndex(), firstIndex); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QList<QVariant> arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), firstIndex); @@ -396,19 +401,19 @@ void tst_QTabWidget::currentIndex() QCOMPARE(tw->currentIndex(), firstIndex); tw->setCurrentIndex(index); QCOMPARE(tw->currentIndex(), index); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), index); removePage(index); QCOMPARE(tw->currentIndex(), firstIndex); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), firstIndex); removePage(firstIndex); QCOMPARE(tw->currentIndex(), -1); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); arguments = spy.takeFirst(); QCOMPARE(arguments.at(0).toInt(), -1); } @@ -689,8 +694,8 @@ void tst_QTabWidget::tabBarClicked() QSignalSpy clickSpy(&tabWidget, SIGNAL(tabBarClicked(int))); QSignalSpy doubleClickSpy(&tabWidget, SIGNAL(tabBarDoubleClicked(int))); - QCOMPARE(clickSpy.count(), 0); - QCOMPARE(doubleClickSpy.count(), 0); + QCOMPARE(clickSpy.size(), 0); + QCOMPARE(doubleClickSpy.size(), 0); QTabBar &tabBar = *tabWidget.tabBar(); Qt::MouseButton button = Qt::LeftButton; @@ -698,27 +703,27 @@ void tst_QTabWidget::tabBarClicked() const QPoint tabPos = tabBar.tabRect(0).center(); QTest::mouseClick(&tabBar, button, {}, tabPos); - QCOMPARE(clickSpy.count(), 1); + QCOMPARE(clickSpy.size(), 1); QCOMPARE(clickSpy.takeFirst().takeFirst().toInt(), 0); - QCOMPARE(doubleClickSpy.count(), 0); + QCOMPARE(doubleClickSpy.size(), 0); QTest::mouseDClick(&tabBar, button, {}, tabPos); - QCOMPARE(clickSpy.count(), 1); + QCOMPARE(clickSpy.size(), 1); QCOMPARE(clickSpy.takeFirst().takeFirst().toInt(), 0); - QCOMPARE(doubleClickSpy.count(), 1); + QCOMPARE(doubleClickSpy.size(), 1); QCOMPARE(doubleClickSpy.takeFirst().takeFirst().toInt(), 0); const QPoint barPos(tabBar.tabRect(0).right() + 5, tabBar.tabRect(0).center().y()); QTest::mouseClick(&tabBar, button, {}, barPos); - QCOMPARE(clickSpy.count(), 1); + QCOMPARE(clickSpy.size(), 1); QCOMPARE(clickSpy.takeFirst().takeFirst().toInt(), -1); - QCOMPARE(doubleClickSpy.count(), 0); + QCOMPARE(doubleClickSpy.size(), 0); QTest::mouseDClick(&tabBar, button, {}, barPos); - QCOMPARE(clickSpy.count(), 1); + QCOMPARE(clickSpy.size(), 1); QCOMPARE(clickSpy.takeFirst().takeFirst().toInt(), -1); - QCOMPARE(doubleClickSpy.count(), 1); + QCOMPARE(doubleClickSpy.size(), 1); QCOMPARE(doubleClickSpy.takeFirst().takeFirst().toInt(), -1); button = Qt::MouseButton(button << 1); @@ -775,5 +780,39 @@ void tst_QTabWidget::autoHide() QVERIFY(heightForWidth1 > tabWidget.heightForWidth(20)); } +void tst_QTabWidget::setCurrentBeforeShow_data() +{ + QTest::addColumn<QTabWidget::TabPosition>("tabPosition"); + QTest::newRow("West") << QTabWidget::West; + QTest::newRow("North") << QTabWidget::North; + QTest::newRow("East") << QTabWidget::East; + QTest::newRow("South") << QTabWidget::South; +} + +void tst_QTabWidget::setCurrentBeforeShow() +{ + QFETCH(QTabWidget::TabPosition, tabPosition); + + QTabWidget tabWidget; + tabWidget.setTabPosition(tabPosition); + + QPixmap pm(50, 50); + pm.fill(Qt::red); + const QIcon icon(pm); + for (int i = 0; i < 4; ++i) + tabWidget.addTab(new QWidget, icon, QString("Tab %1").arg(i)); + + // the tab widget has space for the entire tab bar + tabWidget.resize(tabWidget.tabBar()->sizeHint() + QSize(50, 50)); + tabWidget.setCurrentIndex(2); + tabWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tabWidget)); + + QCOMPARE_GE(tabWidget.tabBar()->tabRect(0).x(), 0); + QCOMPARE_GE(tabWidget.tabBar()->tabRect(0).y(), 0); + + QTest::qWait(2000); +} + QTEST_MAIN(tst_QTabWidget) #include "tst_qtabwidget.moc" diff --git a/tests/auto/widgets/widgets/qtextbrowser/CMakeLists.txt b/tests/auto/widgets/widgets/qtextbrowser/CMakeLists.txt index 170ac1e139..4a80068d75 100644 --- a/tests/auto/widgets/widgets/qtextbrowser/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qtextbrowser/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qtextbrowser.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtextbrowser Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtextbrowser LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} @@ -22,9 +29,10 @@ list(APPEND test_data ${test_data_glob}) qt_internal_add_test(tst_qtextbrowser SOURCES tst_qtextbrowser.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets + Qt::WidgetsPrivate TESTDATA ${test_data} ) diff --git a/tests/auto/widgets/widgets/qtextbrowser/tst_qtextbrowser.cpp b/tests/auto/widgets/widgets/qtextbrowser/tst_qtextbrowser.cpp index 73266d77f3..5d66f5922a 100644 --- a/tests/auto/widgets/widgets/qtextbrowser/tst_qtextbrowser.cpp +++ b/tests/auto/widgets/widgets/qtextbrowser/tst_qtextbrowser.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -37,12 +12,14 @@ #include <qtextbrowser.h> #include <qtextobject.h> +#include <QtWidgets/private/qapplication_p.h> + class TestBrowser : public QTextBrowser { public: inline TestBrowser() { show(); - QApplication::setActiveWindow(this); + QApplicationPrivate::setActiveWindow(this); activateWindow(); setFocus(); QVERIFY(QTest::qWaitForWindowActive(this)); @@ -245,29 +222,29 @@ void tst_QTextBrowser::relativeLinks() QSignalSpy sourceChangedSpy(browser, SIGNAL(sourceChanged(QUrl))); browser->setSource(QUrl("subdir/../qtextbrowser.html")); QVERIFY(!browser->document()->isEmpty()); - QCOMPARE(sourceChangedSpy.count(), 1); + QCOMPARE(sourceChangedSpy.size(), 1); QCOMPARE(sourceChangedSpy.takeFirst()[0].toUrl(), QUrl("subdir/../qtextbrowser.html")); browser->setSource(QUrl("subdir/index.html")); QVERIFY(!browser->document()->isEmpty()); - QCOMPARE(sourceChangedSpy.count(), 1); + QCOMPARE(sourceChangedSpy.size(), 1); QCOMPARE(sourceChangedSpy.takeFirst()[0].toUrl(), QUrl("subdir/index.html")); browser->setSource(QUrl("anchor.html")); QVERIFY(!browser->document()->isEmpty()); - QCOMPARE(sourceChangedSpy.count(), 1); + QCOMPARE(sourceChangedSpy.size(), 1); QCOMPARE(sourceChangedSpy.takeFirst()[0].toUrl(), QUrl("anchor.html")); browser->setSource(QUrl("subdir/index.html")); QVERIFY(!browser->document()->isEmpty()); - QCOMPARE(sourceChangedSpy.count(), 1); + QCOMPARE(sourceChangedSpy.size(), 1); QCOMPARE(sourceChangedSpy.takeFirst()[0].toUrl(), QUrl("subdir/index.html")); // using QUrl::fromLocalFile() browser->setSource(QUrl::fromLocalFile("anchor.html")); QVERIFY(!browser->document()->isEmpty()); - QCOMPARE(sourceChangedSpy.count(), 1); + QCOMPARE(sourceChangedSpy.size(), 1); QCOMPARE(sourceChangedSpy.takeFirst()[0].toUrl(), QUrl("file:anchor.html")); browser->setSource(QUrl("subdir/../qtextbrowser.html")); QVERIFY(!browser->document()->isEmpty()); - QCOMPARE(sourceChangedSpy.count(), 1); + QCOMPARE(sourceChangedSpy.size(), 1); QCOMPARE(sourceChangedSpy.takeFirst()[0].toUrl(), QUrl("subdir/../qtextbrowser.html")); } @@ -299,9 +276,9 @@ void tst_QTextBrowser::forwardBackwardAvailable() browser->setSource(QUrl::fromLocalFile("anchor.html")); QVERIFY(!browser->isBackwardAvailable()); QVERIFY(!browser->isForwardAvailable()); - QCOMPARE(backwardSpy.count(), 1); + QCOMPARE(backwardSpy.size(), 1); QVERIFY(!backwardSpy.at(0).at(0).toBool()); - QCOMPARE(forwardSpy.count(), 1); + QCOMPARE(forwardSpy.size(), 1); QVERIFY(!forwardSpy.at(0).at(0).toBool()); backwardSpy.clear(); @@ -310,9 +287,9 @@ void tst_QTextBrowser::forwardBackwardAvailable() browser->setSource(QUrl::fromLocalFile("bigpage.html")); QVERIFY(browser->isBackwardAvailable()); QVERIFY(!browser->isForwardAvailable()); - QCOMPARE(backwardSpy.count(), 1); + QCOMPARE(backwardSpy.size(), 1); QVERIFY(backwardSpy.at(0).at(0).toBool()); - QCOMPARE(forwardSpy.count(), 1); + QCOMPARE(forwardSpy.size(), 1); QVERIFY(!forwardSpy.at(0).at(0).toBool()); backwardSpy.clear(); @@ -321,9 +298,9 @@ void tst_QTextBrowser::forwardBackwardAvailable() browser->setSource(QUrl::fromLocalFile("pagewithbg.html")); QVERIFY(browser->isBackwardAvailable()); QVERIFY(!browser->isForwardAvailable()); - QCOMPARE(backwardSpy.count(), 1); + QCOMPARE(backwardSpy.size(), 1); QVERIFY(backwardSpy.at(0).at(0).toBool()); - QCOMPARE(forwardSpy.count(), 1); + QCOMPARE(forwardSpy.size(), 1); QVERIFY(!forwardSpy.at(0).at(0).toBool()); backwardSpy.clear(); @@ -332,9 +309,9 @@ void tst_QTextBrowser::forwardBackwardAvailable() browser->backward(); QVERIFY(browser->isBackwardAvailable()); QVERIFY(browser->isForwardAvailable()); - QCOMPARE(backwardSpy.count(), 1); + QCOMPARE(backwardSpy.size(), 1); QVERIFY(backwardSpy.at(0).at(0).toBool()); - QCOMPARE(forwardSpy.count(), 1); + QCOMPARE(forwardSpy.size(), 1); QVERIFY(forwardSpy.at(0).at(0).toBool()); backwardSpy.clear(); @@ -343,9 +320,9 @@ void tst_QTextBrowser::forwardBackwardAvailable() browser->backward(); QVERIFY(!browser->isBackwardAvailable()); QVERIFY(browser->isForwardAvailable()); - QCOMPARE(backwardSpy.count(), 1); + QCOMPARE(backwardSpy.size(), 1); QVERIFY(!backwardSpy.at(0).at(0).toBool()); - QCOMPARE(forwardSpy.count(), 1); + QCOMPARE(forwardSpy.size(), 1); QVERIFY(forwardSpy.at(0).at(0).toBool()); backwardSpy.clear(); @@ -354,9 +331,9 @@ void tst_QTextBrowser::forwardBackwardAvailable() browser->forward(); QVERIFY(browser->isBackwardAvailable()); QVERIFY(browser->isForwardAvailable()); - QCOMPARE(backwardSpy.count(), 1); + QCOMPARE(backwardSpy.size(), 1); QVERIFY(backwardSpy.at(0).at(0).toBool()); - QCOMPARE(forwardSpy.count(), 1); + QCOMPARE(forwardSpy.size(), 1); QVERIFY(forwardSpy.at(0).at(0).toBool()); backwardSpy.clear(); @@ -365,9 +342,9 @@ void tst_QTextBrowser::forwardBackwardAvailable() browser->forward(); QVERIFY(browser->isBackwardAvailable()); QVERIFY(!browser->isForwardAvailable()); - QCOMPARE(backwardSpy.count(), 1); + QCOMPARE(backwardSpy.size(), 1); QVERIFY(backwardSpy.at(0).at(0).toBool()); - QCOMPARE(forwardSpy.count(), 1); + QCOMPARE(forwardSpy.size(), 1); QVERIFY(!forwardSpy.at(0).at(0).toBool()); backwardSpy.clear(); @@ -385,9 +362,9 @@ void tst_QTextBrowser::clearHistory() browser->clearHistory(); QVERIFY(!browser->isBackwardAvailable()); QVERIFY(!browser->isForwardAvailable()); - QCOMPARE(backwardSpy.count(), 1); + QCOMPARE(backwardSpy.size(), 1); QVERIFY(!backwardSpy.at(0).at(0).toBool()); - QCOMPARE(forwardSpy.count(), 1); + QCOMPARE(forwardSpy.size(), 1); QVERIFY(!forwardSpy.at(0).at(0).toBool()); QVERIFY(browser->historyTitle(-1).isEmpty()); QVERIFY(browser->historyTitle(0).isEmpty()); @@ -399,9 +376,9 @@ void tst_QTextBrowser::clearHistory() browser->setSource(QUrl::fromLocalFile("anchor.html")); QVERIFY(!browser->isBackwardAvailable()); QVERIFY(!browser->isForwardAvailable()); - QCOMPARE(backwardSpy.count(), 1); + QCOMPARE(backwardSpy.size(), 1); QVERIFY(!backwardSpy.at(0).at(0).toBool()); - QCOMPARE(forwardSpy.count(), 1); + QCOMPARE(forwardSpy.size(), 1); QVERIFY(!forwardSpy.at(0).at(0).toBool()); backwardSpy.clear(); @@ -410,9 +387,9 @@ void tst_QTextBrowser::clearHistory() browser->setSource(QUrl::fromLocalFile("bigpage.html")); QVERIFY(browser->isBackwardAvailable()); QVERIFY(!browser->isForwardAvailable()); - QCOMPARE(backwardSpy.count(), 1); + QCOMPARE(backwardSpy.size(), 1); QVERIFY(backwardSpy.at(0).at(0).toBool()); - QCOMPARE(forwardSpy.count(), 1); + QCOMPARE(forwardSpy.size(), 1); QVERIFY(!forwardSpy.at(0).at(0).toBool()); backwardSpy.clear(); @@ -421,9 +398,9 @@ void tst_QTextBrowser::clearHistory() browser->clearHistory(); QVERIFY(!browser->isBackwardAvailable()); QVERIFY(!browser->isForwardAvailable()); - QCOMPARE(backwardSpy.count(), 1); + QCOMPARE(backwardSpy.size(), 1); QVERIFY(!backwardSpy.at(0).at(0).toBool()); - QCOMPARE(forwardSpy.count(), 1); + QCOMPARE(forwardSpy.size(), 1); QVERIFY(!forwardSpy.at(0).at(0).toBool()); QVERIFY(browser->historyTitle(-1).isEmpty()); QVERIFY(browser->historyTitle(1).isEmpty()); @@ -466,11 +443,29 @@ void tst_QTextBrowser::textInteractionFlags_vs_readOnly() void tst_QTextBrowser::inputMethodAttribute_vs_readOnly() { QVERIFY(browser->isReadOnly()); +#if defined(Q_OS_ANDROID) + QInputMethodQueryEvent query(Qt::ImReadOnly); + QCoreApplication::sendEvent(browser, &query); + QVERIFY(query.value(Qt::ImReadOnly).toBool()); +#else QVERIFY(!browser->testAttribute(Qt::WA_InputMethodEnabled)); +#endif + browser->setReadOnly(false); +#if defined(Q_OS_ANDROID) + QCoreApplication::sendEvent(browser, &query); + QVERIFY(!query.value(Qt::ImReadOnly).toBool()); +#else QVERIFY(browser->testAttribute(Qt::WA_InputMethodEnabled)); +#endif + browser->setReadOnly(true); +#if defined(Q_OS_ANDROID) + QCoreApplication::sendEvent(browser, &query); + QVERIFY(query.value(Qt::ImReadOnly).toBool()); +#else QVERIFY(!browser->testAttribute(Qt::WA_InputMethodEnabled)); +#endif } void tst_QTextBrowser::anchorsWithSelfBuiltHtml() @@ -678,7 +673,7 @@ void tst_QTextBrowser::urlEncoding() browser->setEditFocus(true); #endif QTest::keyClick(browser, Qt::Key_Enter); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QUrl url = spy.at(0).at(0).toUrl(); QCOMPARE(url.toEncoded(), QByteArray("http://www.google.com/q=%22")); diff --git a/tests/auto/widgets/widgets/qtextedit/CMakeLists.txt b/tests/auto/widgets/widgets/qtextedit/CMakeLists.txt index e90b59ccb8..e406e088ca 100644 --- a/tests/auto/widgets/widgets/qtextedit/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qtextedit/CMakeLists.txt @@ -1,16 +1,23 @@ -# Generated from qtextedit.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtextedit Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtextedit LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data list(APPEND test_data "fullWidthSelection") qt_internal_add_test(tst_qtextedit SOURCES tst_qtextedit.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate @@ -23,6 +30,6 @@ qt_internal_add_test(tst_qtextedit ##################################################################### qt_internal_extend_target(tst_qtextedit CONDITION MACOS - PUBLIC_LIBRARIES + LIBRARIES ${FWAppKit} ) diff --git a/tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp b/tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp index 8d5716c129..0136e5b5de 100644 --- a/tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp +++ b/tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -58,6 +33,10 @@ #include "../../../shared/platforminputcontext.h" #include <private/qinputmethod_p.h> +#include <QtWidgets/private/qapplication_p.h> + +Q_LOGGING_CATEGORY(lcTests, "qt.widgets.tests") + //Used in copyAvailable typedef QPair<Qt::Key, Qt::KeyboardModifier> keyPairType; typedef QList<keyPairType> pairListType; @@ -137,7 +116,13 @@ private slots: void moveCursor(); #ifndef QT_NO_CLIPBOARD void mimeDataReimplementations(); +#ifndef QT_NO_TEXTHTMLPARSER + void mimeTypesAvailableFromRichText(); #endif +#if QT_CONFIG(textmarkdownreader) + void mimeTypesAvailableFromMarkdown(); +#endif +#endif // QT_NO_CLIPBOARD void ctrlEnterShouldInsertLineSeparator_NOT(); void shiftEnterShouldInsertLineSeparator(); void selectWordsFromStringsContainingSeparators_data(); @@ -173,6 +158,7 @@ private slots: void setDocumentPreservesPalette(); #endif void pasteFromQt3RichText(); + void pasteFromMarkdown(); void noWrapBackgrounds(); void preserveCharFormatAfterUnchangingSetPosition(); void twoSameInputMethodEvents(); @@ -209,10 +195,16 @@ private slots: void preeditCharFormat_data(); void preeditCharFormat(); + void nextFormatAfterEnterPressed_data(); + void nextFormatAfterEnterPressed(); + + void dontCrashWithCss(); + private: void createSelection(); int blockCount() const; void compareWidgetAndImage(QTextEdit &widget, const QString &imageFileName); + bool isMainFontFixed(); QTextEdit *ed; qreal rootFrameMargin; @@ -529,9 +521,9 @@ void tst_QTextEdit::clearShouldClearExtraSelections() sel.cursor = ed->textCursor(); sel.format.setProperty(QTextFormat::FullWidthSelection, true); ed->setExtraSelections(QList<QTextEdit::ExtraSelection>() << sel); - QCOMPARE(ed->extraSelections().count(), 1); + QCOMPARE(ed->extraSelections().size(), 1); ed->clear(); - QCOMPARE(ed->extraSelections().count(), 0); + QCOMPARE(ed->extraSelections().size(), 0); } void tst_QTextEdit::paragSeparatorOnPlaintextAppend() @@ -731,7 +723,7 @@ void tst_QTextEdit::cursorPositionChanged() spy.clear(); QTest::keyClick(ed, Qt::Key_A); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QTextCursor cursor = ed->textCursor(); cursor.movePosition(QTextCursor::Start); @@ -739,18 +731,18 @@ void tst_QTextEdit::cursorPositionChanged() cursor.movePosition(QTextCursor::End); spy.clear(); cursor.insertText("Test"); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); cursor.movePosition(QTextCursor::End); ed->setTextCursor(cursor); cursor.movePosition(QTextCursor::Start); spy.clear(); cursor.insertText("Test"); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); QTest::keyClick(ed, Qt::Key_Left); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); cursor.movePosition(QTextCursor::Start); ed->setTextCursor(cursor); @@ -759,14 +751,19 @@ void tst_QTextEdit::cursorPositionChanged() QTest::mouseDClick(ed->viewport(), Qt::LeftButton, {}, ed->cursorRect().center()); QVERIFY(ed->textCursor().hasSelection()); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); CursorPositionChangedRecorder spy2(ed); QVERIFY(ed->textCursor().position() > 0); ed->setPlainText("Hello World"); - QCOMPARE(spy2.cursorPositions.count(), 1); + QCOMPARE(spy2.cursorPositions.size(), 1); QCOMPARE(spy2.cursorPositions.at(0), 0); QCOMPARE(ed->textCursor().position(), 0); + + ed->selectAll(); + QCOMPARE(spy2.cursorPositions.size(), 2); + QCOMPARE(spy2.cursorPositions.at(1), 11); + QCOMPARE(ed->textCursor().position(), 11); } void tst_QTextEdit::setTextCursor() @@ -781,7 +778,7 @@ void tst_QTextEdit::setTextCursor() spy.clear(); ed->setTextCursor(cursor); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } #ifndef QT_NO_CLIPBOARD @@ -798,7 +795,7 @@ void tst_QTextEdit::undoAvailableAfterPaste() const QString txt("Test"); QApplication::clipboard()->setText(txt); ed->paste(); - QVERIFY(spy.count() >= 1); + QVERIFY(spy.size() >= 1); QCOMPARE(ed->toPlainText(), txt); } #endif @@ -1051,7 +1048,7 @@ void tst_QTextEdit::noPropertiesOnDefaultTextEditCharFormat() // on a text edit. Font properties instead should be taken from the // widget's font (in sync with defaultFont property in document) and the // foreground color should be taken from the palette. - QCOMPARE(ed->currentCharFormat().properties().count(), 0); + QCOMPARE(ed->currentCharFormat().properties().size(), 0); } void tst_QTextEdit::setPlainTextShouldUseCurrentCharFormat() @@ -1073,9 +1070,9 @@ void tst_QTextEdit::setPlainTextShouldEmitTextChangedOnce() { QSignalSpy spy(ed, SIGNAL(textChanged())); ed->setPlainText("Yankee Doodle"); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); ed->setPlainText(""); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); } void tst_QTextEdit::overwriteMode() @@ -1357,7 +1354,7 @@ void tst_QTextEdit::copyAvailable_data() //Tests the copyAvailable slot for several cases void tst_QTextEdit::copyAvailable() { - QFETCH(pairListType,keystrokes); + QFETCH(const pairListType, keystrokes); QFETCH(QList<bool>, copyAvailable); QFETCH(QString, function); @@ -1370,9 +1367,8 @@ void tst_QTextEdit::copyAvailable() QSignalSpy spyCopyAvailabe(ed, SIGNAL(copyAvailable(bool))); //Execute Keystrokes - foreach(keyPairType keyPair, keystrokes) { + for (keyPairType keyPair : keystrokes) QTest::keyClick(ed, keyPair.first, keyPair.second ); - } //Execute ed->"function" if (function == "cut") @@ -1389,8 +1385,8 @@ void tst_QTextEdit::copyAvailable() //Compare spied signals QEXPECT_FAIL("Case7 T,A,A, <- + shift, <- + shift, <- + shift, ctrl + x, undo() | signals: true, false, true", "Wrong undo selection behaviour. Should be fixed in some future release. (See task: 132482)", Abort); - QCOMPARE(spyCopyAvailabe.count(), copyAvailable.count()); - for (int i=0;i<spyCopyAvailabe.count(); i++) { + QCOMPARE(spyCopyAvailabe.size(), copyAvailable.size()); + for (int i=0;i<spyCopyAvailabe.size(); i++) { QVariant variantSpyCopyAvailable = spyCopyAvailabe.at(i).at(0); QVERIFY2(variantSpyCopyAvailable.toBool() == copyAvailable.at(i), QString("Spied singnal: %1").arg(i).toLatin1()); } @@ -1426,10 +1422,10 @@ void tst_QTextEdit::moveCursor() QCOMPARE(ed->textCursor().position(), 0); ed->moveCursor(QTextCursor::NextCharacter); QCOMPARE(ed->textCursor().position(), 1); - QCOMPARE(cursorMovedSpy.count(), 1); + QCOMPARE(cursorMovedSpy.size(), 1); ed->moveCursor(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); QCOMPARE(ed->textCursor().position(), 2); - QCOMPARE(cursorMovedSpy.count(), 2); + QCOMPARE(cursorMovedSpy.size(), 2); QCOMPARE(ed->textCursor().selectedText(), QString("e")); } @@ -1500,7 +1496,63 @@ void tst_QTextEdit::mimeDataReimplementations() QCOMPARE(ed.insertCallCount, 1); #endif } + +#ifndef QT_NO_TEXTHTMLPARSER +void tst_QTextEdit::mimeTypesAvailableFromRichText() +{ + MyTextEdit ed; + ed.setHtml("<i>Hello <b>World</b></i>"); + ed.selectAll(); + ed.copy(); + const auto *mimeData = QApplication::clipboard()->mimeData(); + qCDebug(lcTests) << "available mime types" << mimeData->formats(); + QVERIFY(mimeData->formats().contains("text/plain")); +#if QT_CONFIG(textmarkdownwriter) + QVERIFY(mimeData->formats().contains("text/markdown")); + const QByteArray expectedMarkdown = "*Hello **World***\n\n"; + if (mimeData->data("text/markdown") != expectedMarkdown && isMainFontFixed()) + QEXPECT_FAIL("", "fixed-pitch main font (QTBUG-103484)", Continue); + QCOMPARE(mimeData->data("text/markdown"), expectedMarkdown); #endif +#ifndef QT_NO_TEXTHTMLPARSER + QVERIFY(mimeData->formats().contains("text/html")); + QVERIFY(mimeData->hasHtml()); +#endif +#ifndef QT_NO_TEXTODFWRITER + QVERIFY(mimeData->formats().contains("application/vnd.oasis.opendocument.text")); +#endif +} +#endif // QT_NO_TEXTHTMLPARSER + +#if QT_CONFIG(textmarkdownreader) +void tst_QTextEdit::mimeTypesAvailableFromMarkdown() +{ + MyTextEdit ed; + const QString md("# TODO\n\n- [x] Fix bugs\n- [ ] Have a beer\n"); + ed.setMarkdown(md); + ed.selectAll(); + ed.copy(); + const auto *mimeData = QApplication::clipboard()->mimeData(); + qCDebug(lcTests) << "available mime types" << mimeData->formats(); + QVERIFY(mimeData->formats().contains("text/plain")); +#if QT_CONFIG(textmarkdownwriter) + QVERIFY(mimeData->formats().contains("text/markdown")); + if (mimeData->data("text/markdown") != md && isMainFontFixed()) + QEXPECT_FAIL("", "fixed-pitch main font (QTBUG-103484)", Continue); + QCOMPARE(mimeData->data("text/markdown"), md); +#endif +#ifndef QT_NO_TEXTHTMLPARSER + QVERIFY(mimeData->formats().contains("text/html")); + QVERIFY(mimeData->hasHtml()); + QVERIFY(mimeData->html().contains("checked")); // <li class=\"checked\" ... +#endif +#ifndef QT_NO_TEXTODFWRITER + QVERIFY(mimeData->formats().contains("application/vnd.oasis.opendocument.text")); +#endif +} +#endif // textmarkdownreader + +#endif // QT_NO_CLIPBOARD void tst_QTextEdit::ctrlEnterShouldInsertLineSeparator_NOT() { @@ -1648,7 +1700,7 @@ void tst_QTextEdit::ensureVisibleWithRtl() ed->setLayoutDirection(Qt::RightToLeft); ed->setLineWrapMode(QTextEdit::NoWrap); QString txt(500, QChar(QLatin1Char('a'))); - QCOMPARE(txt.length(), 500); + QCOMPARE(txt.size(), 500); ed->setPlainText(txt); ed->resize(100, 100); ed->show(); @@ -1699,7 +1751,7 @@ void tst_QTextEdit::extraSelections() ed->setExtraSelections(QList<QTextEdit::ExtraSelection>() << sel); QList<QTextEdit::ExtraSelection> selections = ed->extraSelections(); - QCOMPARE(selections.count(), 1); + QCOMPARE(selections.size(), 1); QCOMPARE(selections.at(0).cursor.position(), endPos); QCOMPARE(selections.at(0).cursor.anchor(), wordPos); } @@ -1841,27 +1893,27 @@ void tst_QTextEdit::selectionChanged() QTest::keyClick(ed, Qt::Key_Right); QCOMPARE(ed->textCursor().position(), 1); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); QTest::keyClick(ed, Qt::Key_Right, Qt::ShiftModifier); QCOMPARE(ed->textCursor().position(), 2); - QCOMPARE(selectionChangedSpy.count(), 1); + QCOMPARE(selectionChangedSpy.size(), 1); QTest::keyClick(ed, Qt::Key_Right, Qt::ShiftModifier); QCOMPARE(ed->textCursor().position(), 3); - QCOMPARE(selectionChangedSpy.count(), 2); + QCOMPARE(selectionChangedSpy.size(), 2); QTest::keyClick(ed, Qt::Key_Right, Qt::ShiftModifier); QCOMPARE(ed->textCursor().position(), 4); - QCOMPARE(selectionChangedSpy.count(), 3); + QCOMPARE(selectionChangedSpy.size(), 3); QTest::keyClick(ed, Qt::Key_Right); QCOMPARE(ed->textCursor().position(), 4); - QCOMPARE(selectionChangedSpy.count(), 4); + QCOMPARE(selectionChangedSpy.size(), 4); QTest::keyClick(ed, Qt::Key_Right); QCOMPARE(ed->textCursor().position(), 5); - QCOMPARE(selectionChangedSpy.count(), 4); + QCOMPARE(selectionChangedSpy.size(), 4); } #ifndef QT_NO_CLIPBOARD @@ -2153,6 +2205,18 @@ void tst_QTextEdit::compareWidgetAndImage(QTextEdit &widget, const QString &imag } } +bool tst_QTextEdit::isMainFontFixed() +{ + bool ret = QFontInfo(QGuiApplication::font()).fixedPitch(); + if (ret) { + qCWarning(lcTests) << "QFontDatabase::GeneralFont is monospaced: markdown writing is likely to use too many backticks"; + qCWarning(lcTests) << "system fonts: fixed" << QFontDatabase::systemFont(QFontDatabase::FixedFont) + << "fixed?" << QFontInfo(QFontDatabase::systemFont(QFontDatabase::FixedFont)).fixedPitch() + << "general" << QFontDatabase::systemFont(QFontDatabase::GeneralFont); + } + return ret; +} + void tst_QTextEdit::cursorRect() { ed->show(); @@ -2173,9 +2237,8 @@ void tst_QTextEdit::setDocumentPreservesPalette() QWidgetTextControl *control = ed->findChild<QWidgetTextControl *>(); QVERIFY(control); - QPalette defaultPal = ed->palette(); QPalette whitePal = ed->palette(); - whitePal.setColor(QPalette::Active, QPalette::Text, "white"); + whitePal.setColor(QPalette::Active, QPalette::Text, Qt::white); QVERIFY(whitePal != ed->palette()); @@ -2220,6 +2283,24 @@ void tst_QTextEdit::pasteFromQt3RichText() QCOMPARE(ed->toPlainText(), QString::fromLatin1(" QTextEdit is an ")); } +void tst_QTextEdit::pasteFromMarkdown() +{ + QByteArray richtext("*This* text is **rich**"); + + QMimeData mimeData; + mimeData.setData("text/markdown", richtext); + + static_cast<PublicTextEdit *>(ed)->publicInsertFromMimeData(&mimeData); + + QCOMPARE(ed->toPlainText(), "This text is rich"); +#if QT_CONFIG(textmarkdownwriter) + const auto expectedMarkdown = QString::fromLatin1(richtext + "\n\n"); + if (ed->toMarkdown() != expectedMarkdown && isMainFontFixed()) + QEXPECT_FAIL("", "fixed-pitch main font (QTBUG-103484)", Continue); + QCOMPARE(ed->toMarkdown(), expectedMarkdown); +#endif +} + void tst_QTextEdit::noWrapBackgrounds() { QWidget topLevel; @@ -2292,7 +2373,8 @@ void tst_QTextEdit::taskQTBUG_7902_contextMenuCrash() w->connect(&ti, SIGNAL(timeout()), w, SLOT(deleteLater())); ti.start(200); - QContextMenuEvent *cme = new QContextMenuEvent(QContextMenuEvent::Mouse, w->rect().center()); + QContextMenuEvent *cme = new QContextMenuEvent(QContextMenuEvent::Mouse, w->rect().center(), + w->viewport()->mapToGlobal(w->rect().center())); qApp->postEvent(w->viewport(), cme); QTest::qWait(300); @@ -2458,12 +2540,12 @@ void tst_QTextEdit::inputMethodEvent() QInputMethodEvent event; event.setCommitString("text"); QApplication::sendEvent(ed, &event); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(ed->toPlainText(), QString("text")); // test that input method gets chance to commit preedit when removing focus ed->setText(""); - QApplication::setActiveWindow(ed); + QApplicationPrivate::setActiveWindow(ed); QTRY_VERIFY(QApplication::focusWindow()); QCOMPARE(qApp->focusObject(), ed); @@ -2488,7 +2570,7 @@ void tst_QTextEdit::inputMethodSelection() cursor.setPosition(5, QTextCursor::KeepAnchor); ed->setTextCursor(cursor); - QCOMPARE(selectionSpy.count(), 1); + QCOMPARE(selectionSpy.size(), 1); QCOMPARE(ed->textCursor().selectionStart(), 0); QCOMPARE(ed->textCursor().selectionEnd(), 5); @@ -2497,7 +2579,7 @@ void tst_QTextEdit::inputMethodSelection() QInputMethodEvent event("", attributes); QApplication::sendEvent(ed, &event); - QCOMPARE(selectionSpy.count(), 2); + QCOMPARE(selectionSpy.size(), 2); QCOMPARE(ed->textCursor().selectionStart(), 12); QCOMPARE(ed->textCursor().selectionEnd(), 17); } @@ -2512,7 +2594,7 @@ void tst_QTextEdit::inputMethodQuery() QGuiApplication::sendEvent(ed, &event); int anchor = event.value(Qt::ImAnchorPosition).toInt(); int position = event.value(Qt::ImCursorPosition).toInt(); - QCOMPARE(qAbs(position - anchor), text.length()); + QCOMPARE(qAbs(position - anchor), text.size()); QCOMPARE(event.value(Qt::ImEnabled).toBool(), true); ed->setEnabled(false); @@ -2597,7 +2679,7 @@ void tst_QTextEdit::countTextChangedOnRemove() QKeyEvent event(QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier); QCoreApplication::instance()->notify(&edit, &event); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } #if QT_CONFIG(regularexpression) @@ -2895,5 +2977,103 @@ void tst_QTextEdit::preeditCharFormat() delete w; } +void tst_QTextEdit::nextFormatAfterEnterPressed_data() +{ + typedef QMap<int, QVariant> pmap; + QTest::addColumn<QString>("html"); + QTest::addColumn<int>("enterKeyCount"); + QTest::addColumn<pmap>("expectedPrevBlockProps"); + QTest::addColumn<pmap>("expectedPrevCharProps"); + QTest::addColumn<pmap>("expectedNewBlockProps"); + QTest::addColumn<pmap>("expectedNewCharProps"); + + // the BlockBottomMargin on "two" will be removed: property() returns invalid QVariant + QTest::newRow("bottom margin after ordered list") << "<ol><li>one</li><li>two</li></ol>" << 1 + << pmap{{QTextFormat::BlockBottomMargin, {}}} << pmap{} + << pmap{{QTextFormat::BlockBottomMargin, 12}} << pmap{}; + QTest::newRow("double enter after list: default format") << "<ol><li>one</li><li>two</li></ol>" << 2 + << pmap{{QTextFormat::BlockBottomMargin, {}}} << pmap{} + << pmap{} << pmap{}; + QTest::newRow("continue block quote") << "<blockquote>I'll be back</blockquote>" << 1 + << pmap{{QTextFormat::BlockLeftMargin, 40}} << pmap{} + << pmap{{QTextFormat::BlockLeftMargin, 40}} << pmap{}; + QTest::newRow("double enter after block quote") << "<blockquote>I'll be back</blockquote>" << 2 + << pmap{{QTextFormat::BlockLeftMargin, 40}} << pmap{} + << pmap{{QTextFormat::BlockLeftMargin, {}}} << pmap{}; + QTest::newRow("bottom margin after bullet list") << "<ul><li>one</li><li>two</li></ul>" << 1 + << pmap{{QTextFormat::BlockBottomMargin, {}}} << pmap{} + << pmap{{QTextFormat::BlockBottomMargin, 12}} << pmap{}; + QTest::newRow("paragraph after heading") << "<h1>so big!</h1>" << 1 + << pmap{{QTextFormat::HeadingLevel, 1}} << pmap{} + << pmap{{QTextFormat::HeadingLevel, {}}} << pmap{}; + QTest::newRow("paragraph after hrule") << "<p style='font-size:18px;'>blah blah<hr/></p>" << 1 + << pmap{} << pmap{} + << pmap{{QTextFormat::BlockTrailingHorizontalRulerWidth, {}}} << pmap{}; +} + +void tst_QTextEdit::nextFormatAfterEnterPressed() +{ + typedef QMap<int, QVariant> pmap; + QFETCH(QString, html); + QFETCH(int, enterKeyCount); + QFETCH(pmap, expectedPrevBlockProps); + QFETCH(pmap, expectedPrevCharProps); + QFETCH(pmap, expectedNewBlockProps); + QFETCH(pmap, expectedNewCharProps); + + ed->setHtml(html); + QTextCursor cursor = ed->textCursor(); + cursor.movePosition(QTextCursor::End); + ed->setTextCursor(cursor); + + if (lcTests().isDebugEnabled()) { + ed->show(); + QTest::qWait(500); + } + + for (int i = 0; i < enterKeyCount; ++i) + QTest::keyClick(ed, Qt::Key_Enter); + QTest::keyClicks(ed, "foo"); + + if (lcTests().isDebugEnabled()) { + // visually see what happened when debug is enabled + QTest::qWait(500); + qCDebug(lcTests) << "new block" << Qt::hex << ed->textCursor().blockFormat().properties(); + qCDebug(lcTests) << "new char" << Qt::hex << ed->textCursor().charFormat().properties(); + } + + // if expectedNewBlockProps is empty, we expect the current block format to be the default format + if (expectedNewBlockProps.isEmpty()) + QCOMPARE(ed->textCursor().blockFormat(), QTextBlockFormat()); + // otherwise we expect to find certain property values in the current block format + else for (auto it = expectedNewBlockProps.constBegin(); it != expectedNewBlockProps.constEnd(); ++it) + QCOMPARE(ed->textCursor().blockFormat().property(it.key()), it.value()); + + // if expectedNewCharProps is empty, we expect the current char format to be the default format + if (expectedNewCharProps.isEmpty()) + QCOMPARE(ed->textCursor().charFormat(), QTextCharFormat()); + // otherwise we expect to find certain property values in the current char format + else for (auto it = expectedNewCharProps.constBegin(); it != expectedNewCharProps.constEnd(); ++it) + QCOMPARE(ed->textCursor().charFormat().property(it.key()), it.value()); + + // check the cases where QWidgetTextControlPrivate::insertParagraphSeparator() should modify + // the previous block's block format and/or char format + auto prevBlockCursor = ed->textCursor(); + prevBlockCursor.movePosition(QTextCursor::PreviousBlock); + for (auto it = expectedPrevBlockProps.constBegin(); it != expectedPrevBlockProps.constEnd(); ++it) + QCOMPARE(prevBlockCursor.blockFormat().property(it.key()), it.value()); + for (auto it = expectedPrevCharProps.constBegin(); it != expectedPrevCharProps.constEnd(); ++it) + QCOMPARE(prevBlockCursor.charFormat().property(it.key()), it.value()); +} + +void tst_QTextEdit::dontCrashWithCss() +{ + qApp->setStyleSheet("QWidget { font: 10pt; }"); + QTextEdit edit; + edit.show(); + qApp->setStyleSheet(QString()); +} + + QTEST_MAIN(tst_QTextEdit) #include "tst_qtextedit.moc" diff --git a/tests/auto/widgets/widgets/qtoolbar/CMakeLists.txt b/tests/auto/widgets/widgets/qtoolbar/CMakeLists.txt index 79b4816d53..e9fb01c3e8 100644 --- a/tests/auto/widgets/widgets/qtoolbar/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qtoolbar/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qtoolbar.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtoolbar Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtoolbar LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qtoolbar SOURCES tst_qtoolbar.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::GuiPrivate Qt::Widgets diff --git a/tests/auto/widgets/widgets/qtoolbar/tst_qtoolbar.cpp b/tests/auto/widgets/widgets/qtoolbar/tst_qtoolbar.cpp index 8fea0dcd67..8b8c74b1e7 100644 --- a/tests/auto/widgets/widgets/qtoolbar/tst_qtoolbar.cpp +++ b/tests/auto/widgets/widgets/qtoolbar/tst_qtoolbar.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -47,6 +22,8 @@ #include <qlabel.h> #include <private/qtoolbarextension_p.h> +#include <QtWidgets/private/qapplication_p.h> + QT_FORWARD_DECLARE_CLASS(QAction) class tst_QToolBar : public QObject @@ -162,12 +139,12 @@ void tst_QToolBar::allowedAreas() QVERIFY(!tb.isAreaAllowed(Qt::RightToolBarArea)); QVERIFY(!tb.isAreaAllowed(Qt::TopToolBarArea)); QVERIFY(!tb.isAreaAllowed(Qt::BottomToolBarArea)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::ToolBarAreas *>(spy.at(0).value(0).constData()), tb.allowedAreas()); spy.clear(); tb.setAllowedAreas(tb.allowedAreas()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); tb.setAllowedAreas(Qt::RightToolBarArea); QCOMPARE((int)tb.allowedAreas(), (int)Qt::RightToolBarArea); @@ -175,12 +152,12 @@ void tst_QToolBar::allowedAreas() QVERIFY(tb.isAreaAllowed(Qt::RightToolBarArea)); QVERIFY(!tb.isAreaAllowed(Qt::TopToolBarArea)); QVERIFY(!tb.isAreaAllowed(Qt::BottomToolBarArea)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::ToolBarAreas *>(spy.at(0).value(0).constData()), tb.allowedAreas()); spy.clear(); tb.setAllowedAreas(tb.allowedAreas()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); tb.setAllowedAreas(Qt::TopToolBarArea); QCOMPARE((int)tb.allowedAreas(), (int)Qt::TopToolBarArea); @@ -188,12 +165,12 @@ void tst_QToolBar::allowedAreas() QVERIFY(!tb.isAreaAllowed(Qt::RightToolBarArea)); QVERIFY(tb.isAreaAllowed(Qt::TopToolBarArea)); QVERIFY(!tb.isAreaAllowed(Qt::BottomToolBarArea)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::ToolBarAreas *>(spy.at(0).value(0).constData()), tb.allowedAreas()); spy.clear(); tb.setAllowedAreas(tb.allowedAreas()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); tb.setAllowedAreas(Qt::BottomToolBarArea); QCOMPARE((int)tb.allowedAreas(), (int)Qt::BottomToolBarArea); @@ -201,12 +178,12 @@ void tst_QToolBar::allowedAreas() QVERIFY(!tb.isAreaAllowed(Qt::RightToolBarArea)); QVERIFY(!tb.isAreaAllowed(Qt::TopToolBarArea)); QVERIFY(tb.isAreaAllowed(Qt::BottomToolBarArea)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::ToolBarAreas *>(spy.at(0).value(0).constData()), tb.allowedAreas()); spy.clear(); tb.setAllowedAreas(tb.allowedAreas()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); // multiple dock window areas tb.setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea); @@ -215,12 +192,12 @@ void tst_QToolBar::allowedAreas() QVERIFY(!tb.isAreaAllowed(Qt::RightToolBarArea)); QVERIFY(tb.isAreaAllowed(Qt::TopToolBarArea)); QVERIFY(tb.isAreaAllowed(Qt::BottomToolBarArea)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::ToolBarAreas *>(spy.at(0).value(0).constData()), tb.allowedAreas()); spy.clear(); tb.setAllowedAreas(tb.allowedAreas()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); tb.setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea); QCOMPARE(tb.allowedAreas(), Qt::LeftToolBarArea | Qt::RightToolBarArea); @@ -228,12 +205,12 @@ void tst_QToolBar::allowedAreas() QVERIFY(tb.isAreaAllowed(Qt::RightToolBarArea)); QVERIFY(!tb.isAreaAllowed(Qt::TopToolBarArea)); QVERIFY(!tb.isAreaAllowed(Qt::BottomToolBarArea)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::ToolBarAreas *>(spy.at(0).value(0).constData()), tb.allowedAreas()); spy.clear(); tb.setAllowedAreas(tb.allowedAreas()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); tb.setAllowedAreas(Qt::TopToolBarArea | Qt::LeftToolBarArea); QCOMPARE(tb.allowedAreas(), Qt::TopToolBarArea | Qt::LeftToolBarArea); @@ -241,12 +218,12 @@ void tst_QToolBar::allowedAreas() QVERIFY(!tb.isAreaAllowed(Qt::RightToolBarArea)); QVERIFY(tb.isAreaAllowed(Qt::TopToolBarArea)); QVERIFY(!tb.isAreaAllowed(Qt::BottomToolBarArea)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::ToolBarAreas *>(spy.at(0).value(0).constData()), tb.allowedAreas()); spy.clear(); tb.setAllowedAreas(tb.allowedAreas()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); tb.setAllowedAreas(Qt::BottomToolBarArea | Qt::RightToolBarArea); QCOMPARE(tb.allowedAreas(), Qt::BottomToolBarArea | Qt::RightToolBarArea); @@ -254,12 +231,12 @@ void tst_QToolBar::allowedAreas() QVERIFY(tb.isAreaAllowed(Qt::RightToolBarArea)); QVERIFY(!tb.isAreaAllowed(Qt::TopToolBarArea)); QVERIFY(tb.isAreaAllowed(Qt::BottomToolBarArea)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::ToolBarAreas *>(spy.at(0).value(0).constData()), tb.allowedAreas()); spy.clear(); tb.setAllowedAreas(tb.allowedAreas()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } void tst_QToolBar::orientation() @@ -271,48 +248,48 @@ void tst_QToolBar::orientation() tb.setOrientation(Qt::Vertical); QCOMPARE(tb.orientation(), Qt::Vertical); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::Orientation *>(spy.at(0).value(0).constData()), tb.orientation()); spy.clear(); tb.setOrientation(tb.orientation()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); tb.setOrientation(Qt::Horizontal); QCOMPARE(tb.orientation(), Qt::Horizontal); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::Orientation *>(spy.at(0).value(0).constData()), tb.orientation()); spy.clear(); tb.setOrientation(tb.orientation()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); tb.setOrientation(Qt::Vertical); QCOMPARE(tb.orientation(), Qt::Vertical); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::Orientation *>(spy.at(0).value(0).constData()), tb.orientation()); spy.clear(); tb.setOrientation(tb.orientation()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); tb.setOrientation(Qt::Horizontal); QCOMPARE(tb.orientation(), Qt::Horizontal); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::Orientation *>(spy.at(0).value(0).constData()), tb.orientation()); spy.clear(); tb.setOrientation(tb.orientation()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); tb.setOrientation(Qt::Vertical); QCOMPARE(tb.orientation(), Qt::Vertical); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(*static_cast<const Qt::Orientation *>(spy.at(0).value(0).constData()), tb.orientation()); spy.clear(); tb.setOrientation(tb.orientation()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } void tst_QToolBar::addAction() @@ -322,13 +299,13 @@ void tst_QToolBar::addAction() { QAction action(0); - QCOMPARE(tb.actions().count(), 0); + QCOMPARE(tb.actions().size(), 0); tb.addAction(&action); - QCOMPARE(tb.actions().count(), 1); + QCOMPARE(tb.actions().size(), 1); QCOMPARE(tb.actions()[0], &action); tb.clear(); - QCOMPARE(tb.actions().count(), 0); + QCOMPARE(tb.actions().size(), 0); } { @@ -351,14 +328,14 @@ void tst_QToolBar::addAction() QCOMPARE(icon, action4->icon()); QCOMPARE(text, action4->text()); - QCOMPARE(tb.actions().count(), 4); + QCOMPARE(tb.actions().size(), 4); QCOMPARE(tb.actions()[0], action1); QCOMPARE(tb.actions()[1], action2); QCOMPARE(tb.actions()[2], action3); QCOMPARE(tb.actions()[3], action4); tb.clear(); - QCOMPARE(tb.actions().count(), 0); + QCOMPARE(tb.actions().size(), 0); } } @@ -387,19 +364,19 @@ void tst_QToolBar::insertAction() QAction action3(0); QAction action4(0); - QCOMPARE(tb.actions().count(), 0); + QCOMPARE(tb.actions().size(), 0); tb.insertAction(0, &action1); tb.insertAction(&action1, &action2); tb.insertAction(&action2, &action3); tb.insertAction(&action3, &action4); - QCOMPARE(tb.actions().count(), 4); + QCOMPARE(tb.actions().size(), 4); QCOMPARE(tb.actions()[0], &action4); QCOMPARE(tb.actions()[1], &action3); QCOMPARE(tb.actions()[2], &action2); QCOMPARE(tb.actions()[3], &action1); tb.clear(); - QCOMPARE(tb.actions().count(), 0); + QCOMPARE(tb.actions().size(), 0); } void tst_QToolBar::addSeparator() @@ -413,13 +390,13 @@ void tst_QToolBar::addSeparator() QAction *sep = tb.addSeparator(); tb.addAction(&action2); - QCOMPARE(tb.actions().count(), 3); + QCOMPARE(tb.actions().size(), 3); QCOMPARE(tb.actions()[0], &action1); QCOMPARE(tb.actions()[1], sep); QCOMPARE(tb.actions()[2], &action2); tb.clear(); - QCOMPARE(tb.actions().count(), 0); + QCOMPARE(tb.actions().size(), 0); } void tst_QToolBar::insertSeparator() @@ -433,13 +410,13 @@ void tst_QToolBar::insertSeparator() tb.addAction(&action2); QAction *sep = tb.insertSeparator(&action2); - QCOMPARE(tb.actions().count(), 3); + QCOMPARE(tb.actions().size(), 3); QCOMPARE(tb.actions()[0], &action1); QCOMPARE(tb.actions()[1], sep); QCOMPARE(tb.actions()[2], &action2); tb.clear(); - QCOMPARE(tb.actions().count(), 0); + QCOMPARE(tb.actions().size(), 0); } void tst_QToolBar::addWidget() @@ -454,7 +431,7 @@ void tst_QToolBar::addWidget() QAction *widget = tb.addWidget(&w); tb.addAction(&action2); - QCOMPARE(tb.actions().count(), 3); + QCOMPARE(tb.actions().size(), 3); QCOMPARE(tb.actions()[0], &action1); QCOMPARE(tb.actions()[1], widget); QCOMPARE(tb.actions()[2], &action2); @@ -462,18 +439,18 @@ void tst_QToolBar::addWidget() // it should be possible to reuse the action returned by // addWidget() to place the widget somewhere else in the toolbar tb.removeAction(widget); - QCOMPARE(tb.actions().count(), 2); + QCOMPARE(tb.actions().size(), 2); QCOMPARE(tb.actions()[0], &action1); QCOMPARE(tb.actions()[1], &action2); tb.addAction(widget); - QCOMPARE(tb.actions().count(), 3); + QCOMPARE(tb.actions().size(), 3); QCOMPARE(tb.actions()[0], &action1); QCOMPARE(tb.actions()[1], &action2); QCOMPARE(tb.actions()[2], widget); tb.clear(); - QCOMPARE(tb.actions().count(), 0); + QCOMPARE(tb.actions().size(), 0); } void tst_QToolBar::insertWidget() @@ -488,7 +465,7 @@ void tst_QToolBar::insertWidget() tb.addAction(&action2); QAction *widget = tb.insertWidget(&action2, &w); - QCOMPARE(tb.actions().count(), 3); + QCOMPARE(tb.actions().size(), 3); QCOMPARE(tb.actions()[0], &action1); QCOMPARE(tb.actions()[1], widget); QCOMPARE(tb.actions()[2], &action2); @@ -496,18 +473,18 @@ void tst_QToolBar::insertWidget() // it should be possible to reuse the action returned by // addWidget() to place the widget somewhere else in the toolbar tb.removeAction(widget); - QCOMPARE(tb.actions().count(), 2); + QCOMPARE(tb.actions().size(), 2); QCOMPARE(tb.actions()[0], &action1); QCOMPARE(tb.actions()[1], &action2); tb.insertAction(&action1, widget); - QCOMPARE(tb.actions().count(), 3); + QCOMPARE(tb.actions().size(), 3); QCOMPARE(tb.actions()[0], widget); QCOMPARE(tb.actions()[1], &action1); QCOMPARE(tb.actions()[2], &action2); tb.clear(); - QCOMPARE(tb.actions().count(), 0); + QCOMPARE(tb.actions().size(), 0); { QToolBar tb; @@ -649,43 +626,43 @@ void tst_QToolBar::iconSize() QCOMPARE(tb.iconSize(), defaultIconSize); tb.setIconSize(defaultIconSize); QCOMPARE(tb.iconSize(), defaultIconSize); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); spy.clear(); tb.setIconSize(largeIconSize); QCOMPARE(tb.iconSize(), largeIconSize); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.first().first().toSize(), largeIconSize); // no-op spy.clear(); tb.setIconSize(largeIconSize); QCOMPARE(tb.iconSize(), largeIconSize); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); spy.clear(); tb.setIconSize(defaultIconSize); QCOMPARE(tb.iconSize(), defaultIconSize); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.first().first().toSize(), defaultIconSize); // no-op spy.clear(); tb.setIconSize(defaultIconSize); QCOMPARE(tb.iconSize(), defaultIconSize); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); spy.clear(); tb.setIconSize(smallIconSize); QCOMPARE(tb.iconSize(), smallIconSize); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.first().first().toSize(), smallIconSize); // no-op spy.clear(); tb.setIconSize(smallIconSize); QCOMPARE(tb.iconSize(), smallIconSize); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); // setting the icon size to an invalid QSize will reset the // iconSize property to the default @@ -713,28 +690,28 @@ void tst_QToolBar::iconSize() // explicitly set it to the default tb.setIconSize(defaultIconSize); QCOMPARE(tb.iconSize(), defaultIconSize); - QCOMPARE(tbSpy.count(), 0); + QCOMPARE(tbSpy.size(), 0); mw.addToolBar(&tb); // tb icon size should not change since it has been explicitly set QCOMPARE(tb.iconSize(), defaultIconSize); - QCOMPARE(tbSpy.count(), 0); + QCOMPARE(tbSpy.size(), 0); mw.setIconSize(largeIconSize); QCOMPARE(tb.iconSize(), defaultIconSize); - QCOMPARE(tbSpy.count(), 0); + QCOMPARE(tbSpy.size(), 0); mw.setIconSize(defaultIconSize); QCOMPARE(tb.iconSize(), defaultIconSize); - QCOMPARE(tbSpy.count(), 0); + QCOMPARE(tbSpy.size(), 0); mw.setIconSize(smallIconSize); QCOMPARE(tb.iconSize(), defaultIconSize); - QCOMPARE(tbSpy.count(), 0); + QCOMPARE(tbSpy.size(), 0); // resetting to the default should cause the toolbar to take // on the mainwindow's icon size @@ -757,51 +734,51 @@ void tst_QToolBar::toolButtonStyle() QCOMPARE(tb.toolButtonStyle(), Qt::ToolButtonIconOnly); tb.setToolButtonStyle(Qt::ToolButtonIconOnly); QCOMPARE(tb.toolButtonStyle(), Qt::ToolButtonIconOnly); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); tb.setToolButtonStyle(Qt::ToolButtonTextOnly); QCOMPARE(tb.toolButtonStyle(), Qt::ToolButtonTextOnly); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); // no-op tb.setToolButtonStyle(Qt::ToolButtonTextOnly); QCOMPARE(tb.toolButtonStyle(), Qt::ToolButtonTextOnly); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); tb.setToolButtonStyle(Qt::ToolButtonIconOnly); QCOMPARE(tb.toolButtonStyle(), Qt::ToolButtonIconOnly); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); // no-op tb.setToolButtonStyle(Qt::ToolButtonIconOnly); QCOMPARE(tb.toolButtonStyle(), Qt::ToolButtonIconOnly); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); tb.setToolButtonStyle(Qt::ToolButtonTextBesideIcon); QCOMPARE(tb.toolButtonStyle(), Qt::ToolButtonTextBesideIcon); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); // no-op tb.setToolButtonStyle(Qt::ToolButtonTextBesideIcon); QCOMPARE(tb.toolButtonStyle(), Qt::ToolButtonTextBesideIcon); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); tb.setToolButtonStyle(Qt::ToolButtonTextUnderIcon); QCOMPARE(tb.toolButtonStyle(), Qt::ToolButtonTextUnderIcon); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); spy.clear(); // no-op tb.setToolButtonStyle(Qt::ToolButtonTextUnderIcon); QCOMPARE(tb.toolButtonStyle(), Qt::ToolButtonTextUnderIcon); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); tb.setToolButtonStyle(Qt::ToolButtonFollowStyle); QCOMPARE(tb.toolButtonStyle(), Qt::ToolButtonFollowStyle); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } { @@ -815,28 +792,28 @@ void tst_QToolBar::toolButtonStyle() // explicitly set the tb to the default tb.setToolButtonStyle(Qt::ToolButtonIconOnly); QCOMPARE(tb.toolButtonStyle(), Qt::ToolButtonIconOnly); - QCOMPARE(tbSpy.count(), 0); + QCOMPARE(tbSpy.size(), 0); mw.addToolBar(&tb); // tb icon size should not change since it has been explicitly set QCOMPARE(tb.toolButtonStyle(), Qt::ToolButtonIconOnly); - QCOMPARE(tbSpy.count(), 0); + QCOMPARE(tbSpy.size(), 0); mw.setToolButtonStyle(Qt::ToolButtonIconOnly); QCOMPARE(tb.toolButtonStyle(), Qt::ToolButtonIconOnly); - QCOMPARE(tbSpy.count(), 0); + QCOMPARE(tbSpy.size(), 0); mw.setToolButtonStyle(Qt::ToolButtonTextOnly); QCOMPARE(tb.toolButtonStyle(), Qt::ToolButtonIconOnly); - QCOMPARE(tbSpy.count(), 0); + QCOMPARE(tbSpy.size(), 0); mw.setToolButtonStyle(Qt::ToolButtonTextUnderIcon); QCOMPARE(tb.toolButtonStyle(), Qt::ToolButtonIconOnly); - QCOMPARE(tbSpy.count(), 0); + QCOMPARE(tbSpy.size(), 0); // note: there is no way to clear the explicitly set tool // button style... once you explicitly set it, the toolbar @@ -950,25 +927,25 @@ void tst_QToolBar::visibilityChanged() mw.addToolBar(&tb); mw.show(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toBool(), true); spy.clear(); tb.hide(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toBool(), false); spy.clear(); tb.hide(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); tb.show(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toBool(), true); spy.clear(); tb.show(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } void tst_QToolBar::actionOwnership() @@ -1045,12 +1022,11 @@ void tst_QToolBar::accel() QSignalSpy spy(action, SIGNAL(triggered(bool))); mw.show(); - QApplication::setActiveWindow(&mw); QVERIFY(QTest::qWaitForWindowActive(&mw)); QTest::keyClick(&mw, Qt::Key_T, Qt::AltModifier); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); #ifdef Q_OS_MAC qt_set_sequence_auto_mnemonic(false); #endif diff --git a/tests/auto/widgets/widgets/qtoolbox/CMakeLists.txt b/tests/auto/widgets/widgets/qtoolbox/CMakeLists.txt index bb50233c6e..362fba25a4 100644 --- a/tests/auto/widgets/widgets/qtoolbox/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qtoolbox/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qtoolbox.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtoolbox Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtoolbox LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qtoolbox SOURCES tst_qtoolbox.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/widgets/qtoolbox/tst_qtoolbox.cpp b/tests/auto/widgets/widgets/qtoolbox/tst_qtoolbox.cpp index fc7eade9f4..fb14ceb79c 100644 --- a/tests/auto/widgets/widgets/qtoolbox/tst_qtoolbox.cpp +++ b/tests/auto/widgets/widgets/qtoolbox/tst_qtoolbox.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/widgets/widgets/qtoolbutton/CMakeLists.txt b/tests/auto/widgets/widgets/qtoolbutton/CMakeLists.txt index b27d88f503..7c8b41b5e6 100644 --- a/tests/auto/widgets/widgets/qtoolbutton/CMakeLists.txt +++ b/tests/auto/widgets/widgets/qtoolbutton/CMakeLists.txt @@ -1,13 +1,21 @@ -# Generated from qtoolbutton.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtoolbutton Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtoolbutton LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qtoolbutton SOURCES tst_qtoolbutton.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/widgets/qtoolbutton/tst_qtoolbutton.cpp b/tests/auto/widgets/widgets/qtoolbutton/tst_qtoolbutton.cpp index d6142dfcdc..20472861d9 100644 --- a/tests/auto/widgets/widgets/qtoolbutton/tst_qtoolbutton.cpp +++ b/tests/auto/widgets/widgets/qtoolbutton/tst_qtoolbutton.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -40,6 +15,8 @@ #include <qscreen.h> #include <qlabel.h> +#include <QtWidgets/private/qapplication_p.h> + class tst_QToolButton : public QObject { Q_OBJECT @@ -56,6 +33,8 @@ private slots: void task176137_autoRepeatOfAction(); void qtbug_26956_popupTimerDone(); void qtbug_34759_sizeHintResetWhenSettingMenu(); + void defaultActionSynced(); + void deleteInHandler(); protected slots: void sendMouseClick(); @@ -142,11 +121,11 @@ void tst_QToolButton::triggered() toolButton->setDefaultAction(defaultAction); mainWidget.show(); - QApplication::setActiveWindow(&mainWidget); + QApplicationPrivate::setActiveWindow(&mainWidget); QVERIFY(QTest::qWaitForWindowActive(&mainWidget)); defaultAction->trigger(); - QCOMPARE(spy.count(),1); + QCOMPARE(spy.size(),1); QCOMPARE(qvariant_cast<QAction *>(spy.at(0).at(0)), defaultAction); m_menu = menu.data(); @@ -157,7 +136,7 @@ void tst_QToolButton::triggered() timer->start(); QTimer::singleShot(10000, &mainWidget, SLOT(close())); // Emergency bail-out toolButton->showMenu(); - QTRY_COMPARE(spy.count(),2); + QTRY_COMPARE(spy.size(),2); QCOMPARE(qvariant_cast<QAction *>(spy.at(1).at(0)), one); } @@ -203,14 +182,14 @@ void tst_QToolButton::task176137_autoRepeatOfAction() label->move(0, 50); mainWidget.show(); - QApplication::setActiveWindow(&mainWidget); + QApplicationPrivate::setActiveWindow(&mainWidget); QVERIFY(QTest::qWaitForWindowActive(&mainWidget)); QSignalSpy spy(&action,SIGNAL(triggered())); QTest::mousePress (toolButton, Qt::LeftButton); QTest::qWait(2000); QTest::mouseRelease (toolButton, Qt::LeftButton, {}, {}); - QCOMPARE(spy.count(),1); + QCOMPARE(spy.size(),1); // try again with auto repeat toolButton->setAutoRepeat (true); @@ -220,11 +199,11 @@ void tst_QToolButton::task176137_autoRepeatOfAction() QTest::mouseRelease (toolButton, Qt::LeftButton, {}, {}); const qreal expected = (3000 - toolButton->autoRepeatDelay()) / toolButton->autoRepeatInterval() + 1; //we check that the difference is small (on some systems timers are not super accurate) - qreal diff = (expected - repeatSpy.count()) / expected; + qreal diff = (expected - repeatSpy.size()) / expected; QVERIFY2(qAbs(diff) < 0.2, qPrintable( QString("expected: %1, actual: %2, diff (fraction): %3") .arg(expected) - .arg(repeatSpy.count()) + .arg(repeatSpy.size()) .arg(diff))); } @@ -280,5 +259,80 @@ void tst_QToolButton::qtbug_34759_sizeHintResetWhenSettingMenu() QTRY_COMPARE(button1.sizeHint(), button2.sizeHint()); } +void tst_QToolButton::defaultActionSynced() +{ + QAction a; + a.setCheckable(true); + + QToolButton tb; + tb.setDefaultAction(&a); + QVERIFY(tb.isCheckable()); + + QSignalSpy tbSpy(&tb, SIGNAL(toggled(bool))); + QSignalSpy aSpy(&a, SIGNAL(toggled(bool))); + + int tbToggledCount = 0; + int aToggledCount = 0; + + tb.setChecked(true); + QVERIFY(a.isChecked()); + QCOMPARE(tbSpy.size(), ++tbToggledCount); + QCOMPARE(aSpy.size(), ++aToggledCount); + tb.setChecked(false); + QVERIFY(!a.isChecked()); + QCOMPARE(tbSpy.size(), ++tbToggledCount); + QCOMPARE(aSpy.size(), ++aToggledCount); + + a.setChecked(true); + QVERIFY(tb.isChecked()); + QCOMPARE(tbSpy.size(), ++tbToggledCount); + QCOMPARE(aSpy.size(), ++aToggledCount); + a.setChecked(false); + QVERIFY(!tb.isChecked()); + QCOMPARE(tbSpy.size(), ++tbToggledCount); + QCOMPARE(aSpy.size(), ++aToggledCount); + + QAction b; + QSignalSpy bSpy(&b, SIGNAL(toggled(bool))); + int bToggledCount = 0; + tb.setDefaultAction(&b); + QVERIFY(!tb.isCheckable()); + b.setCheckable(true); + QVERIFY(tb.isCheckable()); + + tb.setChecked(true); + QVERIFY(!a.isChecked()); + QVERIFY(b.isChecked()); + + QCOMPARE(tbSpy.size(), ++tbToggledCount); + QCOMPARE(aSpy.size(), aToggledCount); + QCOMPARE(bSpy.size(), ++bToggledCount); + + tb.click(); + QVERIFY(!a.isChecked()); + QVERIFY(!tb.isChecked()); + QVERIFY(!b.isChecked()); + QCOMPARE(tbSpy.size(), ++tbToggledCount); + QCOMPARE(aSpy.size(), aToggledCount); + QCOMPARE(bSpy.size(), ++bToggledCount); +} + +void tst_QToolButton::deleteInHandler() +{ + // Tests that if something deletes the button + // while its event handler is still on the callstack, we don't crash + + QPointer<QToolButton> tb = new QToolButton(); + tb->show(); + QVERIFY(QTest::qWaitForWindowActive(tb)); + + connect(tb, &QToolButton::clicked, this, [tb] { + delete tb; + }); + + QTest::mouseClick(tb, Qt::LeftButton); + QVERIFY(!tb); +} + QTEST_MAIN(tst_QToolButton) #include "tst_qtoolbutton.moc" |