diff options
Diffstat (limited to 'tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp')
-rw-r--r-- | tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp | 366 |
1 files changed, 285 insertions, 81 deletions
diff --git a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp index 5b9f8f4f43..66c1359bd8 100644 --- a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp +++ b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSignalSpy> @@ -8,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> @@ -24,6 +25,7 @@ #include <qtablewidget.h> #include <qscrollbar.h> #include <qboxlayout.h> +#include <qshortcut.h> #include <qstackedwidget.h> #include <qstandarditemmodel.h> @@ -40,11 +42,13 @@ #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> @@ -107,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(); @@ -149,6 +154,8 @@ private slots: void propagateStyleChanges(); void buttonPressKeys(); void clearModel(); + void cancelClosesPopupNotDialog(); + void closePopupWithCheckableItems(); private: PlatformInputContext m_platformInputContext; @@ -842,7 +849,6 @@ void tst_QComboBox::autoCompletionCaseSensitivity() TestWidget topLevel; topLevel.show(); QComboBox *testWidget = topLevel.comboBox(); - QApplicationPrivate::setActiveWindow(&topLevel); testWidget->setFocus(); QVERIFY(QTest::qWaitForWindowActive(&topLevel)); QCOMPARE(qApp->focusWidget(), (QWidget *)testWidget); @@ -1015,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; @@ -1158,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.size()); + 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.size(), expectedSignalCount); + QCOMPARE(indexChangedSpy.size(), expectedSignalCount); // compare with last sent signal values - if (indexChangedInt.size()) - QCOMPARE(indexChangedInt.at(indexChangedInt.size() - 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); } } } @@ -1275,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; } } @@ -1494,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.size(), 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.size(), 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.size(), 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.size(), 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.size(), 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() @@ -1981,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; } } @@ -1993,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); @@ -2005,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(); - QApplicationPrivate::setActiveWindow(&comboBox); comboBox.activateWindow(); comboBox.setFocus(); QVERIFY(QTest::qWaitForWindowActive(&comboBox)); @@ -2091,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); @@ -2106,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()); @@ -2240,11 +2279,11 @@ 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.size() + separators.size())); for (int i = 0, s = 0; i < box.count(); ++i) { @@ -2365,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()); } @@ -2444,7 +2484,6 @@ void tst_QComboBox::task247863_keyBoardSelection() combo.addItem( QLatin1String("111")); combo.addItem( QLatin1String("222")); combo.show(); - QApplicationPrivate::setActiveWindow(&combo); QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&combo)); QSignalSpy spy(&combo, &QComboBox::activated); @@ -2470,7 +2509,6 @@ void tst_QComboBox::task220195_keyBoardSelection2() combo.addItem( QLatin1String("foo2")); combo.addItem( QLatin1String("foo3")); combo.show(); - QApplicationPrivate::setActiveWindow(&combo); QVERIFY(QTest::qWaitForWindowActive(&combo)); combo.setCurrentIndex(-1); @@ -2756,7 +2794,6 @@ void tst_QComboBox::keyBoardNavigationWithMouse() combo.move(200, 200); combo.showNormal(); - QApplicationPrivate::setActiveWindow(&combo); QVERIFY(QTest::qWaitForWindowActive(&combo)); QCOMPARE(combo.currentText(), QLatin1String("0")); @@ -2812,7 +2849,6 @@ void tst_QComboBox::task_QTBUG_1071_changingFocusEmitsActivated() layout.addWidget(&edit); w.show(); - QApplicationPrivate::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); cb.clearEditText(); cb.setFocus(); @@ -3164,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() @@ -3265,10 +3325,10 @@ 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); @@ -3350,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)) @@ -3432,7 +3551,6 @@ void tst_QComboBox::task_QTBUG_52027_mapCompleterIndex() QCOMPARE(spy.size(), 0); cbox.move(200, 200); cbox.show(); - QApplicationPrivate::setActiveWindow(&cbox); QVERIFY(QTest::qWaitForWindowActive(&cbox)); QTest::keyClicks(&cbox, "foobar2"); @@ -3458,7 +3576,6 @@ void tst_QComboBox::task_QTBUG_52027_mapCompleterIndex() cbox.activateWindow(); } - QApplicationPrivate::setActiveWindow(&cbox); QVERIFY(QTest::qWaitForWindowActive(&cbox)); QTest::keyClicks(&cbox, "foobar1"); @@ -3526,7 +3643,6 @@ void tst_QComboBox::checkEmbeddedLineEditWhenStyleSheetIsSet() layout->addWidget(comboBox); topLevel.show(); comboBox->setEditable(true); - QApplicationPrivate::setActiveWindow(&topLevel); QVERIFY(QTest::qWaitForWindowActive(&topLevel)); QImage grab = comboBox->grab().toImage(); @@ -3635,5 +3751,93 @@ void tst_QComboBox::clearModel() 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" |