diff options
Diffstat (limited to 'tests/auto/widgets')
24 files changed, 1793 insertions, 196 deletions
diff --git a/tests/auto/widgets/dialogs/qfiledialog/tst_qfiledialog.cpp b/tests/auto/widgets/dialogs/qfiledialog/tst_qfiledialog.cpp index c3bdf3701f..eee227fc2f 100644 --- a/tests/auto/widgets/dialogs/qfiledialog/tst_qfiledialog.cpp +++ b/tests/auto/widgets/dialogs/qfiledialog/tst_qfiledialog.cpp @@ -933,7 +933,6 @@ void tst_QFiledialog::selectFiles() QSignalSpy spyDirectoryEntered(&fd, SIGNAL(directoryEntered(QString))); QSignalSpy spyFilesSelected(&fd, SIGNAL(filesSelected(QStringList))); QSignalSpy spyFilterSelected(&fd, SIGNAL(filterSelected(QString))); - fd.show(); fd.setFileMode(QFileDialog::ExistingFiles); QString filesPath = fd.directory().absolutePath(); @@ -1517,7 +1516,8 @@ public: const QWindow *window = QGuiApplication::topLevelWindows().constFirst(); const QFileDialog *fileDialog = qobject_cast<QFileDialog*>(QApplication::activeModalWidget()); - QVERIFY(fileDialog); + if (!fileDialog) + return; // The problem in QTBUG-57193 was from a platform input context plugin that was // connected to QWindow::focusObjectChanged(), and consequently accessed the focus diff --git a/tests/auto/widgets/dialogs/qfiledialog2/tst_qfiledialog2.cpp b/tests/auto/widgets/dialogs/qfiledialog2/tst_qfiledialog2.cpp index fca1f17a4d..8f9a8c11a7 100644 --- a/tests/auto/widgets/dialogs/qfiledialog2/tst_qfiledialog2.cpp +++ b/tests/auto/widgets/dialogs/qfiledialog2/tst_qfiledialog2.cpp @@ -1176,7 +1176,7 @@ void tst_QFileDialog2::QTBUG4419_lineEditSelectAll() fd.setFileMode(QFileDialog::AnyFile); fd.show(); - QApplication::setActiveWindow(&fd); + fd.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&fd)); QCOMPARE(fd.isVisible(), true); QCOMPARE(QApplication::activeWindow(), static_cast<QWidget*>(&fd)); diff --git a/tests/auto/widgets/effects/qgraphicseffect/tst_qgraphicseffect.cpp b/tests/auto/widgets/effects/qgraphicseffect/tst_qgraphicseffect.cpp index 5b142048b5..95662a49a0 100644 --- a/tests/auto/widgets/effects/qgraphicseffect/tst_qgraphicseffect.cpp +++ b/tests/auto/widgets/effects/qgraphicseffect/tst_qgraphicseffect.cpp @@ -731,12 +731,12 @@ void tst_QGraphicsEffect::itemHasNoContents() CustomEffect *effect = new CustomEffect; parent->setGraphicsEffect(effect); - QTRY_COMPARE(effect->numRepaints, 1); + QTRY_VERIFY(effect->numRepaints >= 1); for (int i = 0; i < 3; ++i) { effect->reset(); effect->update(); - QTRY_COMPARE(effect->numRepaints, 1); + QTRY_VERIFY(effect->numRepaints >= 1); } } diff --git a/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp index 6c1abaedb3..6ff95c0304 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsitem/tst_qgraphicsitem.cpp @@ -986,6 +986,7 @@ void tst_QGraphicsItem::inputMethodHints() QGraphicsView view(&scene); QApplication::setActiveWindow(&view); view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); item->setFocus(); QTRY_VERIFY(item->hasFocus()); @@ -1036,6 +1037,7 @@ void tst_QGraphicsItem::toolTip() view.setFixedSize(200, 200); view.show(); QApplication::setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); { QHelpEvent helpEvent(QEvent::ToolTip, view.viewport()->rect().topLeft(), @@ -3226,6 +3228,7 @@ void tst_QGraphicsItem::hoverEventsGenerateRepaints() QGraphicsScene scene; QGraphicsView view(&scene); view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); EventTester *tester = new EventTester; @@ -4938,6 +4941,7 @@ void tst_QGraphicsItem::sceneEventFilter() QGraphicsView view(&scene); view.show(); QApplication::setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QGraphicsTextItem *text1 = scene.addText(QLatin1String("Text1")); @@ -6802,6 +6806,7 @@ void tst_QGraphicsItem::opacity2() MyGraphicsView view(&scene); view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_VERIFY(view.repaints >= 1); @@ -6874,6 +6879,7 @@ void tst_QGraphicsItem::opacityZeroUpdates() MyGraphicsView view(&scene); view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_VERIFY(view.repaints > 0); @@ -7262,6 +7268,7 @@ void tst_QGraphicsItem::cacheMode() view.resize(150, 150); view.show(); QApplication::setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); EventTester *tester = new EventTester; @@ -7440,6 +7447,7 @@ void tst_QGraphicsItem::cacheMode2() view.resize(150, 150); view.show(); QApplication::setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); EventTester *tester = new EventTester; @@ -8093,6 +8101,7 @@ void tst_QGraphicsItem::moveLineItem() MyGraphicsView view(&scene); view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); view.reset(); @@ -8161,6 +8170,7 @@ void tst_QGraphicsItem::sorting() view.setFrameStyle(0); view.show(); qApp->setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_VERIFY(_paintedItems.count() > 0); @@ -8197,6 +8207,7 @@ void tst_QGraphicsItem::itemHasNoContents() QGraphicsView view(&scene); view.show(); qApp->setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_VERIFY(!_paintedItems.isEmpty()); @@ -9204,6 +9215,7 @@ void tst_QGraphicsItem::ensureDirtySceneTransform() QGraphicsView view(&scene); view.show(); QApplication::setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view)); @@ -9590,6 +9602,7 @@ void tst_QGraphicsItem::QTBUG_4233_updateCachedWithSceneRect() QGraphicsView view(&scene); view.show(); QApplication::setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), (QWidget *)&view); @@ -10720,6 +10733,7 @@ void tst_QGraphicsItem::scroll() MyGraphicsView view(&scene); view.setFrameStyle(0); view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_VERIFY(view.repaints > 0); @@ -11242,6 +11256,7 @@ void tst_QGraphicsItem::QTBUG_6738_missingUpdateWithSetParent() MyGraphicsView view(&scene); view.show(); qApp->setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_VERIFY(view.repaints > 0); @@ -11290,6 +11305,7 @@ void tst_QGraphicsItem::QT_2653_fullUpdateDiscardingOpacityUpdate() parentGreen->setFlag(QGraphicsItem::ItemIgnoresTransformations); view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); view.reset(); @@ -11336,6 +11352,7 @@ void tst_QGraphicsItem::QTBUG_7714_fullUpdateDiscardingOpacityUpdate2() view.show(); qApp->setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); view.reset(); origView.reset(); @@ -11475,6 +11492,7 @@ void tst_QGraphicsItem::doNotMarkFullUpdateIfNotInScene() item2->setParentItem(item); scene.addItem(item); view.show(); + QVERIFY(QTest::qWaitForWindowExposed(view.windowHandle())); QVERIFY(QTest::qWaitForWindowActive(view.windowHandle())); view.activateWindow(); QTRY_VERIFY(view.isActiveWindow()); @@ -11506,6 +11524,7 @@ void tst_QGraphicsItem::itemDiesDuringDraggingOperation() scene.addItem(item); view.show(); QApplication::setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), (QWidget *)&view); QGraphicsSceneDragDropEvent dragEnter(QEvent::GraphicsSceneDragEnter); @@ -11533,6 +11552,7 @@ void tst_QGraphicsItem::QTBUG_12112_focusItem() view.show(); QApplication::setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), (QWidget *)&view); diff --git a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp index f52bbce53a..87a05d02d9 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp @@ -431,6 +431,7 @@ void tst_QGraphicsView::interactive() QCOMPARE(item->events.size(), 0); view.show(); view.activateWindow(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_COMPARE(item->events.size(), 1); // activate @@ -1646,6 +1647,7 @@ void tst_QGraphicsView::itemsInRect_cosmeticAdjust() view.setFrameStyle(0); view.resize(300, 300); view.showNormal(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_VERIFY(rect->numPaints > 0); @@ -2140,6 +2142,7 @@ 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)); @@ -2207,6 +2210,7 @@ void tst_QGraphicsView::wheelEvent() QGraphicsView view(&scene); view.show(); QApplication::setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view)); @@ -2443,6 +2447,7 @@ 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()); view.lastUpdateRegions.clear(); @@ -2526,6 +2531,7 @@ void tst_QGraphicsView::viewportUpdateMode2() view.resize(200 + left + right, 200 + top + bottom); toplevel.show(); qApp->setActiveWindow(&toplevel); + QVERIFY(QTest::qWaitForWindowExposed(&toplevel)); QVERIFY(QTest::qWaitForWindowActive(&toplevel)); QTRY_VERIFY(view.painted); const QRect viewportRect = view.viewport()->rect(); @@ -3170,6 +3176,7 @@ 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)); @@ -3969,6 +3976,7 @@ void tst_QGraphicsView::exposeRegion() view.setScene(&scene); view.show(); qApp->setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_VERIFY(item->paints > 0); @@ -4123,6 +4131,7 @@ void tst_QGraphicsView::update2() view.resize(200, 200); view.show(); qApp->setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_VERIFY(rect->numPaints > 0); @@ -4192,6 +4201,7 @@ void tst_QGraphicsView::update_ancestorClipsChildrenToShape() CustomView view(&scene); view.show(); qApp->setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_VERIFY(view.painted); @@ -4245,6 +4255,7 @@ void tst_QGraphicsView::update_ancestorClipsChildrenToShape2() CustomView view(&scene); view.show(); qApp->setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QTRY_VERIFY(view.painted); @@ -4305,6 +4316,7 @@ void tst_QGraphicsView::inputMethodSensitivity() QGraphicsView view(&scene); view.show(); QApplication::setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view)); @@ -4399,6 +4411,7 @@ void tst_QGraphicsView::inputContextReset() view.show(); QApplication::setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view)); @@ -4546,6 +4559,7 @@ void tst_QGraphicsView::task255529_transformationAnchorMouseAndViewportMargins() view.setWindowFlags(Qt::X11BypassWindowManagerHint); view.show(); qApp->setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); // This is highly unstable (observed to pass on Windows and some Linux configurations). #ifndef Q_OS_MAC @@ -4673,6 +4687,7 @@ void tst_QGraphicsView::QTBUG_4151_clipAndIgnore() view.resize(75, 75); view.show(); view.activateWindow(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(QApplication::activeWindow(), (QWidget *)&view); @@ -4710,6 +4725,7 @@ void tst_QGraphicsView::QTBUG_5859_exposedRect() view.scale(4.15, 4.15); view.showNormal(); qApp->setActiveWindow(&view); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); view.viewport()->repaint(10,10,20,20); @@ -4834,6 +4850,7 @@ void tst_QGraphicsView::QTBUG_16063_microFocusRect() view.setFixedSize(40, 40); view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); scene.setFocusItem(item); diff --git a/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp b/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp index fd83228c8b..57dbb98049 100644 --- a/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp +++ b/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp @@ -243,6 +243,7 @@ private slots: void testMinMaxSectionSize_data(); void testMinMaxSectionSize(); void sizeHintCrash(); + void testResetCachedSizeHint(); protected: void setupTestData(bool use_reset_model = false); @@ -265,13 +266,21 @@ Q_OBJECT public: QtTestModel(QObject *parent = 0): QAbstractTableModel(parent), - cols(0), rows(0), wrongIndex(false) {} - int rowCount(const QModelIndex&) const { return rows; } - int columnCount(const QModelIndex&) const { return cols; } + cols(0), rows(0), wrongIndex(false), m_bMultiLine(false) {} + int rowCount(const QModelIndex&) const override { return rows; } + int columnCount(const QModelIndex&) const override { return cols; } bool isEditable(const QModelIndex &) const { return true; } - QVariant data(const QModelIndex &idx, int) const + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override { + if (role == Qt::DisplayRole) + return m_bMultiLine ? QString("%1\n%1").arg(section) : QString::number(section); + return QAbstractTableModel::headerData(section, orientation, role); + } + QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override + { + if (role != Qt::DisplayRole) + return QVariant(); if (idx.row() < 0 || idx.column() < 0 || idx.column() >= cols || idx.row() >= rows) { wrongIndex = true; qWarning("Invalid modelIndex [%d,%d,%p]", idx.row(), idx.column(), idx.internalPointer()); @@ -354,8 +363,16 @@ public: emit layoutChanged(); } + void setMultiLineHeader(bool bEnable) + { + beginResetModel(); + m_bMultiLine = bEnable; + endResetModel(); + } + int cols, rows; mutable bool wrongIndex; + bool m_bMultiLine; }; // Testing get/set functions @@ -817,11 +834,9 @@ void tst_QHeaderView::offset() void tst_QHeaderView::sectionSizeHint() { - // Test bad arguments - view->sectionSizeHint(-1); - view->sectionSizeHint(99999); - - // TODO how to test the return value? + QCOMPARE(view->sectionSizeHint(-1), -1); + QCOMPARE(view->sectionSizeHint(99999), -1); + QVERIFY(view->sectionSizeHint(0) >= 0); } void tst_QHeaderView::logicalIndex() @@ -2268,6 +2283,7 @@ void tst_QHeaderView::QTBUG6058_reset() QHeaderView view(Qt::Vertical); view.setModel(&proxy); view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QVERIFY(QTest::qWaitForWindowActive(&view)); proxy.setSourceModel(&model1); @@ -3325,6 +3341,25 @@ void tst_QHeaderView::testMinMaxSectionSize() QTRY_COMPARE(header.sectionSize(0), defaultSectionSize); } +void tst_QHeaderView::testResetCachedSizeHint() +{ + QtTestModel model; + model.rows = model.cols = 10; + + QTableView tv; + tv.setModel(&model); + tv.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tv)); + + QSize s1 = tv.horizontalHeader()->sizeHint(); + model.setMultiLineHeader(true); + QSize s2 = tv.horizontalHeader()->sizeHint(); + model.setMultiLineHeader(false); + QSize s3 = tv.horizontalHeader()->sizeHint(); + QCOMPARE(s1, s3); + QVERIFY(s1 != s2); +} + QTEST_MAIN(tst_QHeaderView) #include "tst_qheaderview.moc" diff --git a/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp b/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp index d0c9dae313..237b950935 100644 --- a/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp +++ b/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp @@ -2419,6 +2419,7 @@ void tst_QListView::taskQTBUG_39902_mutualScrollBars() void tst_QListView::horizontalScrollingByVerticalWheelEvents() { +#if QT_CONFIG(wheelevent) QListView lv; lv.setWrapping(true); @@ -2460,6 +2461,9 @@ void tst_QListView::horizontalScrollingByVerticalWheelEvents() int vValue = lv.verticalScrollBar()->value(); QApplication::sendEvent(lv.viewport(), &wheelDownEvent); QVERIFY(lv.verticalScrollBar()->value() > vValue); +#else + QSKIP("Built with --no-feature-wheelevent"); +#endif } void tst_QListView::taskQTBUG_7232_AllowUserToControlSingleStep() diff --git a/tests/auto/widgets/itemviews/qtablewidget/tst_qtablewidget.cpp b/tests/auto/widgets/itemviews/qtablewidget/tst_qtablewidget.cpp index 4d4a95b3f5..012ffb1a36 100644 --- a/tests/auto/widgets/itemviews/qtablewidget/tst_qtablewidget.cpp +++ b/tests/auto/widgets/itemviews/qtablewidget/tst_qtablewidget.cpp @@ -1343,27 +1343,45 @@ void tst_QTableWidget::setItemWithSorting() } } +class QTableWidgetDataChanged : public QTableWidget +{ + Q_OBJECT +public: + using QTableWidget::QTableWidget; + + void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) override + { + QTableWidget::dataChanged(topLeft, bottomRight, roles); + currentRoles = roles; + } + QVector<int> currentRoles; +}; + void tst_QTableWidget::itemData() { - QTableWidget widget(2, 2); + QTableWidgetDataChanged widget(2, 2); widget.setItem(0, 0, new QTableWidgetItem()); QTableWidgetItem *item = widget.item(0, 0); QVERIFY(item); item->setFlags(item->flags() | Qt::ItemIsEditable); item->setData(Qt::DisplayRole, QString("0")); + QCOMPARE(widget.currentRoles, QVector<int>({Qt::DisplayRole, Qt::EditRole})); item->setData(Qt::CheckStateRole, Qt::PartiallyChecked); - item->setData(Qt::UserRole + 0, QString("1")); - item->setData(Qt::UserRole + 1, QString("2")); - item->setData(Qt::UserRole + 2, QString("3")); - item->setData(Qt::UserRole + 3, QString("4")); + QCOMPARE(widget.currentRoles, {Qt::CheckStateRole}); + for (int i = 0; i < 4; ++i) + { + item->setData(Qt::UserRole + i, QString::number(i + 1)); + QCOMPARE(widget.currentRoles, {Qt::UserRole + i}); + } QMap<int, QVariant> flags = widget.model()->itemData(widget.model()->index(0, 0)); QCOMPARE(flags.count(), 6); - QCOMPARE(flags[(Qt::UserRole + 0)].toString(), QString("1")); + for (int i = 0; i < 4; ++i) + QCOMPARE(flags[Qt::UserRole + i].toString(), QString::number(i + 1)); } void tst_QTableWidget::setItemData() { - QTableWidget table(10, 10); + QTableWidgetDataChanged table(10, 10); table.setSortingEnabled(false); QSignalSpy dataChangedSpy(table.model(), SIGNAL(dataChanged(QModelIndex,QModelIndex))); @@ -1376,6 +1394,7 @@ void tst_QTableWidget::setItemData() data.insert(Qt::DisplayRole, QLatin1String("Display")); data.insert(Qt::ToolTipRole, QLatin1String("ToolTip")); table.model()->setItemData(idx, data); + QCOMPARE(table.currentRoles, QVector<int>({Qt::DisplayRole, Qt::EditRole, Qt::ToolTipRole})); QCOMPARE(table.model()->data(idx, Qt::DisplayRole).toString(), QLatin1String("Display")); QCOMPARE(table.model()->data(idx, Qt::ToolTipRole).toString(), QLatin1String("ToolTip")); diff --git a/tests/auto/widgets/itemviews/qtreeview/BLACKLIST b/tests/auto/widgets/itemviews/qtreeview/BLACKLIST deleted file mode 100644 index 5eb80007c4..0000000000 --- a/tests/auto/widgets/itemviews/qtreeview/BLACKLIST +++ /dev/null @@ -1,2 +0,0 @@ -[setSortingEnabledChild] -windows diff --git a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp index 4c637573d0..a58202e636 100644 --- a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp +++ b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp @@ -2600,8 +2600,10 @@ void tst_QTreeView::setSortingEnabledChild() { QMainWindow win; QTreeView view; - QStandardItemModel model(1,1); + // two columns to not get in trouble with stretchLastSection + QStandardItemModel model(1,2); view.setModel(&model); + view.header()->setDefaultSectionSize(92); win.setCentralWidget(&view); const int size = view.header()->sectionSize(0); view.setSortingEnabled(true); diff --git a/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp b/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp index 321e4bcd0e..1f438f59eb 100644 --- a/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp +++ b/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp @@ -1938,23 +1938,38 @@ void tst_QTreeWidget::setData() } } +class QTreeWidgetDataChanged : public QTreeWidget +{ + Q_OBJECT +public: + using QTreeWidget::QTreeWidget; + + void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) override + { + QTreeWidget::dataChanged(topLeft, bottomRight, roles); + currentRoles = roles; + } + QVector<int> currentRoles; +}; + void tst_QTreeWidget::itemData() { - QTreeWidget widget; + QTreeWidgetDataChanged widget; QTreeWidgetItem item(&widget); widget.setColumnCount(2); item.setFlags(item.flags() | Qt::ItemIsEditable); item.setData(0, Qt::DisplayRole, QString("0")); + QCOMPARE(widget.currentRoles, QVector<int>({Qt::DisplayRole, Qt::EditRole})); item.setData(0, Qt::CheckStateRole, Qt::PartiallyChecked); - item.setData(0, Qt::UserRole + 0, QString("1")); - item.setData(0, Qt::UserRole + 1, QString("2")); - item.setData(0, Qt::UserRole + 2, QString("3")); - item.setData(0, Qt::UserRole + 3, QString("4")); - + QCOMPARE(widget.currentRoles, {Qt::CheckStateRole}); + for (int i = 0; i < 4; ++i) { + item.setData(0, Qt::UserRole + i, QString::number(i + 1)); + QCOMPARE(widget.currentRoles, {Qt::UserRole + i}); + } QMap<int, QVariant> flags = widget.model()->itemData(widget.model()->index(0, 0)); QCOMPARE(flags.count(), 6); - QCOMPARE(flags[Qt::UserRole + 0].toString(), QString("1")); - + 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); } diff --git a/tests/auto/widgets/kernel/qboxlayout/tst_qboxlayout.cpp b/tests/auto/widgets/kernel/qboxlayout/tst_qboxlayout.cpp index 8314dbedf5..b2650d1f32 100644 --- a/tests/auto/widgets/kernel/qboxlayout/tst_qboxlayout.cpp +++ b/tests/auto/widgets/kernel/qboxlayout/tst_qboxlayout.cpp @@ -56,6 +56,7 @@ private slots: void taskQTBUG_40609_addingWidgetToItsOwnLayout(); void taskQTBUG_40609_addingLayoutToItself(); void replaceWidget(); + void indexOf(); }; class CustomLayoutStyle : public QProxyStyle @@ -512,5 +513,24 @@ void tst_QBoxLayout::replaceWidget() QCOMPARE(boxLayout->indexOf(replaceTo), 1); } +void tst_QBoxLayout::indexOf() +{ + QWidget w; + auto outer = new QVBoxLayout(&w); + auto inner = new QHBoxLayout(); + outer->addLayout(inner); + auto widget1 = new QWidget(); + QWidget widget2; + inner->addWidget(widget1); + + QCOMPARE(inner->indexOf(widget1), 0); + QCOMPARE(inner->indexOf(&widget2), -1); + QCOMPARE(outer->indexOf(widget1), -1); + QCOMPARE(outer->indexOf(&widget2), -1); + QCOMPARE(outer->indexOf(outer), -1); + QCOMPARE(outer->indexOf(inner), 0); + QCOMPARE(inner->indexOf(inner->itemAt(0)), 0); +} + QTEST_MAIN(tst_QBoxLayout) #include "tst_qboxlayout.moc" diff --git a/tests/auto/widgets/kernel/qwidget/BLACKLIST b/tests/auto/widgets/kernel/qwidget/BLACKLIST index 6cebce26e8..ae2534a8c1 100644 --- a/tests/auto/widgets/kernel/qwidget/BLACKLIST +++ b/tests/auto/widgets/kernel/qwidget/BLACKLIST @@ -15,22 +15,15 @@ rhel-7.4 osx [focusProxyAndInputMethods] linux -[showMaximized] -osx -[setGeometry] -osx [raise] # QTBUG-68175 opensuse -osx -[resizeEvent] -osx [setWindowGeometry] osx [windowMoveResize] osx [childEvents] -osx +osx ci [renderInvisible] osx [optimizedResizeMove] @@ -42,27 +35,8 @@ osx [showMinimizedKeepsFocus] osx-10.11 ci osx-10.12 ci -[moveWindowInShowEvent:1] -osx -[moveWindowInShowEvent:2] -osx -[taskQTBUG_4055_sendSyntheticEnterLeave] -osx -[syntheticEnterLeave] -osx [maskedUpdate] -osx opensuse-42.3 -[hideOpaqueChildWhileHidden] -osx -[resizeStaticContentsChildWidget_QTBUG35282] -osx -[lower] -osx -[setClearAndResizeMask] -osx -[setToolTip] -osx [moveInResizeEvent] ubuntu-16.04 [moveChild:right] diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index 47ffee1501..3d94c7bcbd 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -3798,9 +3798,10 @@ void tst_QWidget::optimizedResize_topLevel() // a native function call works (it basically has to go through // WM_RESIZE in QApplication). This is a corner case, though. // See task 243708 - const QRect frame = topLevel.frameGeometry(); - MoveWindow(winHandleOf(&topLevel), frame.x(), frame.y(), - frame.width() + 10, frame.height() + 10, + RECT rect; + GetWindowRect(winHandleOf(&topLevel), &rect); + MoveWindow(winHandleOf(&topLevel), rect.left, rect.top, + rect.right - rect.left + 10, rect.bottom - rect.top + 10, true); QTest::qWait(100); #endif @@ -9122,7 +9123,8 @@ void tst_QWidget::taskQTBUG_4055_sendSyntheticEnterLeave() void mouseMoveEvent(QMouseEvent *event) { QCOMPARE(event->button(), Qt::NoButton); - QCOMPARE(event->buttons(), Qt::MouseButtons(Qt::NoButton)); + QCOMPARE(event->buttons(), QApplication::mouseButtons()); + QCOMPARE(event->modifiers(), QApplication::keyboardModifiers()); ++numMouseMoveEvents; } void reset() { numEnterEvents = numMouseMoveEvents = 0; } @@ -9156,11 +9158,11 @@ void tst_QWidget::taskQTBUG_4055_sendSyntheticEnterLeave() child.setMouseTracking(true); child.show(); - // Make sure the child gets enter event and mouse move event. + // 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, 1); + QCOMPARE(child.numMouseMoveEvents, 0); // Sending synthetic enter/leave trough the parent's mousePressEvent handler. parent.child = &child; @@ -9169,10 +9171,19 @@ void tst_QWidget::taskQTBUG_4055_sendSyntheticEnterLeave() child.reset(); QTest::mouseClick(&parent, Qt::LeftButton); - // Make sure the child gets enter event and one mouse move event. + // Make sure the child gets enter event. QTRY_COMPARE(child.numEnterEvents, 1); - QCOMPARE(child.numMouseMoveEvents, 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); diff --git a/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp b/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp index 28a099de83..6a4d972baf 100644 --- a/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp +++ b/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp @@ -98,6 +98,8 @@ private slots: void widgetStyle(); void appStyle(); void QTBUG11658_cachecrash(); + void styleSheetTargetAttribute(); + private: QColor COLOR(const QWidget& w) { w.ensurePolished(); @@ -1999,6 +2001,59 @@ void tst_QStyleSheetStyle::widgetStylePropagation() QCOMPARE(COLOR(childLabel), childExpectedColor); } +void tst_QStyleSheetStyle::styleSheetTargetAttribute() +{ + QGroupBox gb; + QLabel lb(&gb); + QPushButton pb(&lb); + + gb.ensurePolished(); lb.ensurePolished(); pb.ensurePolished(); + QCOMPARE(gb.testAttribute(Qt::WA_StyleSheetTarget), false); + QCOMPARE(lb.testAttribute(Qt::WA_StyleSheetTarget), false); + QCOMPARE(pb.testAttribute(Qt::WA_StyleSheetTarget), false); + + qApp->setStyleSheet("QPushButton { background-color: blue; }"); + + gb.ensurePolished(); lb.ensurePolished(); pb.ensurePolished(); + QCOMPARE(gb.testAttribute(Qt::WA_StyleSheetTarget), false); + QCOMPARE(lb.testAttribute(Qt::WA_StyleSheetTarget), false); + QCOMPARE(pb.testAttribute(Qt::WA_StyleSheetTarget), true); + + qApp->setStyleSheet("QGroupBox { background-color: blue; }"); + + gb.ensurePolished(); lb.ensurePolished(); pb.ensurePolished(); + QCOMPARE(gb.testAttribute(Qt::WA_StyleSheetTarget), true); + QCOMPARE(lb.testAttribute(Qt::WA_StyleSheetTarget), false); + QCOMPARE(pb.testAttribute(Qt::WA_StyleSheetTarget), false); + + qApp->setStyleSheet("QGroupBox * { background-color: blue; }"); + + gb.ensurePolished(); lb.ensurePolished(); pb.ensurePolished(); + QCOMPARE(gb.testAttribute(Qt::WA_StyleSheetTarget), false); + QCOMPARE(lb.testAttribute(Qt::WA_StyleSheetTarget), true); + QCOMPARE(pb.testAttribute(Qt::WA_StyleSheetTarget), true); + + qApp->setStyleSheet("* { background-color: blue; }"); + gb.ensurePolished(); lb.ensurePolished(); pb.ensurePolished(); + QCOMPARE(gb.testAttribute(Qt::WA_StyleSheetTarget), true); + QCOMPARE(lb.testAttribute(Qt::WA_StyleSheetTarget), true); + QCOMPARE(pb.testAttribute(Qt::WA_StyleSheetTarget), true); + + qApp->setStyleSheet("QLabel { font-size: 32pt; }"); + + gb.ensurePolished(); lb.ensurePolished(); pb.ensurePolished(); + QCOMPARE(gb.testAttribute(Qt::WA_StyleSheetTarget), false); + QCOMPARE(lb.testAttribute(Qt::WA_StyleSheetTarget), true); + QCOMPARE(pb.testAttribute(Qt::WA_StyleSheetTarget), false); + + qApp->setStyleSheet(""); + + gb.ensurePolished(); lb.ensurePolished(); pb.ensurePolished(); + QCOMPARE(gb.testAttribute(Qt::WA_StyleSheetTarget), false); + QCOMPARE(lb.testAttribute(Qt::WA_StyleSheetTarget), false); + QCOMPARE(pb.testAttribute(Qt::WA_StyleSheetTarget), false); +} + QTEST_MAIN(tst_QStyleSheetStyle) #include "tst_qstylesheetstyle.moc" diff --git a/tests/auto/widgets/widgets/qcombobox/qcombobox.pro b/tests/auto/widgets/widgets/qcombobox/qcombobox.pro index 939153dc88..d118833ae3 100644 --- a/tests/auto/widgets/widgets/qcombobox/qcombobox.pro +++ b/tests/auto/widgets/widgets/qcombobox/qcombobox.pro @@ -1,4 +1,5 @@ CONFIG += testcase TARGET = tst_qcombobox QT += widgets widgets-private gui-private core-private testlib testlib-private +DEFINES += QTEST_QPA_MOUSE_HANDLING SOURCES += tst_qcombobox.cpp diff --git a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp index 7c1deb8fff..80844715ef 100644 --- a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp +++ b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp @@ -2719,32 +2719,18 @@ void tst_QComboBox::resetModel() } -static inline void centerCursor(const QWidget *w) -{ -#ifndef QT_NO_CURSOR - // Force cursor movement to prevent QCursor::setPos() from returning prematurely on QPA: - const QPoint target(w->mapToGlobal(w->rect().center())); - QCursor::setPos(QPoint(target.x() + 1, target.y())); - QCursor::setPos(target); -#else // !QT_NO_CURSOR - Q_UNUSED(w) -#endif -} - void tst_QComboBox::keyBoardNavigationWithMouse() { QComboBox combo; combo.setEditable(false); setFrameless(&combo); - combo.move(200, 200); - for (int i = 0; i < 80; i++) - combo.addItem( QString::number(i)); + for (int i = 0; i < 200; i++) + combo.addItem(QString::number(i)); + combo.move(200, 200); combo.showNormal(); - centerCursor(&combo); // QTBUG-33973, cursor needs to be within view from start on Mac. QApplication::setActiveWindow(&combo); QVERIFY(QTest::qWaitForWindowActive(&combo)); - QCOMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&combo)); QCOMPARE(combo.currentText(), QLatin1String("0")); @@ -2752,18 +2738,12 @@ void tst_QComboBox::keyBoardNavigationWithMouse() QTRY_VERIFY(combo.hasFocus()); QTest::keyClick(combo.lineEdit(), Qt::Key_Space); - QTest::qWait(30); QTRY_VERIFY(combo.view()); QTRY_VERIFY(combo.view()->isVisible()); - QTest::qWait(130); QCOMPARE(combo.currentText(), QLatin1String("0")); - // When calling cursor function, Windows CE responds with: This function is not supported on this system. -#if !defined Q_OS_QNX - // Force cursor movement to prevent QCursor::setPos() from returning prematurely on QPA: - centerCursor(combo.view()); - QTest::qWait(200); + QTest::mouseMove(&combo, combo.rect().center()); #define GET_SELECTION(SEL) \ QCOMPARE(combo.view()->selectionModel()->selection().count(), 1); \ @@ -2771,23 +2751,19 @@ void tst_QComboBox::keyBoardNavigationWithMouse() SEL = combo.view()->selectionModel()->selection().indexes().first().row() int selection; - GET_SELECTION(selection); - - //since we moved the mouse is in the middle it should even be around 5; - QVERIFY2(selection > 3, (QByteArrayLiteral("selection=") + QByteArray::number(selection)).constData()); + GET_SELECTION(selection); // get initial selection - static const int final = 40; + const int final = 40; for (int i = selection + 1; i <= final; i++) { QTest::keyClick(combo.view(), Qt::Key_Down); - QTest::qWait(20); GET_SELECTION(selection); QCOMPARE(selection, i); } QTest::keyClick(combo.view(), Qt::Key_Enter); QTRY_COMPARE(combo.currentText(), QString::number(final)); -#endif +#undef GET_SELECTION } void tst_QComboBox::task_QTBUG_1071_changingFocusEmitsActivated() diff --git a/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp b/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp index fa28ec2575..0fe5b48341 100644 --- a/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp +++ b/tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp @@ -70,6 +70,7 @@ #include <QSignalSpy> #include <QTestEventList> #include <QDateEdit> +#include <QProxyStyle> #include <private/qdatetimeedit_p.h> @@ -93,6 +94,26 @@ public: friend class tst_QDateTimeEdit; }; +class PressAndHoldStyle : public QProxyStyle +{ + Q_OBJECT +public: + using QProxyStyle::QProxyStyle; + + int styleHint(QStyle::StyleHint hint, const QStyleOption *option = nullptr, + const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const override + { + switch (hint) { + case QStyle::SH_SpinBox_ClickAutoRepeatRate: + return 5; + case QStyle::SH_SpinBox_ClickAutoRepeatThreshold: + return 10; + default: + return QProxyStyle::styleHint(hint, option, widget, returnData); + } + } +}; + class tst_QDateTimeEdit : public QObject { Q_OBJECT @@ -205,9 +226,8 @@ private slots: void reverseTest(); void ddMMMMyyyy(); -#if QT_CONFIG(wheelevent) + void wheelEvent_data(); void wheelEvent(); -#endif void specialValueCornerCase(); void cursorPositionOnInit(); @@ -245,14 +265,99 @@ private slots: void dateEditCorrectSectionSize_data(); void dateEditCorrectSectionSize(); #endif + + void stepModifierKeys_data(); + void stepModifierKeys(); + + void stepModifierButtons_data(); + void stepModifierButtons(); + + void stepModifierPressAndHold_data(); + void stepModifierPressAndHold(); private: EditorDateEdit* testWidget; QWidget *testFocusWidget; }; +typedef QList<QDate> DateList; typedef QList<QTime> TimeList; typedef QList<Qt::Key> KeyList; +static QLatin1String modifierToName(Qt::KeyboardModifier modifier) +{ + switch (modifier) { + case Qt::NoModifier: + return QLatin1Literal("No"); + break; + case Qt::ControlModifier: + return QLatin1Literal("Ctrl"); + break; + case Qt::ShiftModifier: + return QLatin1Literal("Shift"); + break; + default: + qFatal("Unexpected keyboard modifier"); + return QLatin1String(); + } +} + +static QLatin1String sectionToName(const QDateTimeEdit::Section section) +{ + switch (section) { + case QDateTimeEdit::SecondSection: + return QLatin1Literal("Second"); + case QDateTimeEdit::MinuteSection: + return QLatin1Literal("Minute"); + case QDateTimeEdit::HourSection: + return QLatin1Literal("Hours"); + case QDateTimeEdit::DaySection: + return QLatin1Literal("Day"); + case QDateTimeEdit::MonthSection: + return QLatin1Literal("Month"); + case QDateTimeEdit::YearSection: + return QLatin1Literal("Year"); + default: + qFatal("Unexpected section"); + return QLatin1String(); + } +} + +static QDate stepDate(const QDate& startDate, const QDateTimeEdit::Section section, + const int steps) +{ + switch (section) { + case QDateTimeEdit::DaySection: + return startDate.addDays(steps); + case QDateTimeEdit::MonthSection: + return startDate.addMonths(steps); + case QDateTimeEdit::YearSection: + return startDate.addYears(steps); + default: + qFatal("Unexpected section"); + return QDate(); + } +} + +static QTime stepTime(const QTime& startTime, const QDateTimeEdit::Section section, + const int steps) +{ + switch (section) { + case QDateTimeEdit::SecondSection: + return startTime.addSecs(steps); + case QDateTimeEdit::MinuteSection: + return QTime(startTime.hour(), + startTime.minute() + steps, + startTime.second()); + case QDateTimeEdit::HourSection: + return QTime(startTime.hour() + steps, + startTime.minute(), + startTime.second()); + default: + qFatal("Unexpected section"); + return QTime(); + } +} + // Testing get/set functions void tst_QDateTimeEdit::getSetCheck() { @@ -3000,20 +3105,154 @@ void tst_QDateTimeEdit::ddMMMMyyyy() QCOMPARE(testWidget->lineEdit()->text(), "01." + QDate::longMonthName(1) + ".200"); } +void tst_QDateTimeEdit::wheelEvent_data() +{ #if QT_CONFIG(wheelevent) + QTest::addColumn<QPoint>("angleDelta"); + QTest::addColumn<int>("qt4Delta"); + QTest::addColumn<Qt::KeyboardModifiers>("modifiers"); + QTest::addColumn<Qt::MouseEventSource>("source"); + QTest::addColumn<QDateTimeEdit::Section>("section"); + QTest::addColumn<QDate>("startDate"); + QTest::addColumn<DateList>("expectedDates"); + + const auto fractions = {false, true}; + + const auto directions = {true, false}; + + const auto modifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; + + const auto sources = {Qt::MouseEventNotSynthesized, + Qt::MouseEventSynthesizedBySystem, + Qt::MouseEventSynthesizedByQt, + Qt::MouseEventSynthesizedByApplication}; + + const auto sections = {QDateTimeEdit::DaySection, + QDateTimeEdit::MonthSection, + QDateTimeEdit::YearSection}; + + for (auto fraction : fractions) { + for (auto up : directions) { + + const QDate startDate(2000, up ? 2 : 12, 17); + + const int units = (fraction ? 60 : 120) * (up ? 1 : -1); + + for (auto modifier : modifierList) { + + const Qt::KeyboardModifiers modifiers(modifier); + + const auto modifierName = modifierToName(modifier); + if (modifierName.isEmpty()) + continue; + + const int steps = (modifier & Qt::ControlModifier ? 10 : 1) + * (up ? 1 : -1); + + for (auto source : sources) { + +#ifdef Q_OS_MACOS + QPoint angleDelta; + if ((modifier & Qt::ShiftModifier) && + source == Qt::MouseEventNotSynthesized) { + // On macOS the Shift modifier converts vertical + // mouse wheel events to horizontal. + angleDelta = { units, 0 }; + } else { + // However, this is not the case for trackpad scroll + // events. + angleDelta = { 0, units }; + } +#else + const QPoint angleDelta(0, units); +#endif + + QLatin1String sourceName; + switch (source) { + case Qt::MouseEventNotSynthesized: + sourceName = QLatin1Literal("NotSynthesized"); + break; + case Qt::MouseEventSynthesizedBySystem: + sourceName = QLatin1Literal("SynthesizedBySystem"); + break; + case Qt::MouseEventSynthesizedByQt: + sourceName = QLatin1Literal("SynthesizedByQt"); + break; + case Qt::MouseEventSynthesizedByApplication: + sourceName = QLatin1Literal("SynthesizedByApplication"); + break; + default: + qFatal("Unexpected wheel event source"); + continue; + } + + for (const auto section : sections) { + + DateList expectedDates; + if (fraction) + expectedDates << startDate; + + const auto expectedDate = stepDate(startDate, section, steps); + if (!expectedDate.isValid()) + continue; + + expectedDates << expectedDate; + + const QLatin1String sectionName = sectionToName(section); + + QTest::addRow("%s%s%sWith%sKeyboardModifier%s", + fraction ? "half" : "full", + up ? "Up" : "Down", + sectionName.latin1(), + modifierName.latin1(), + sourceName.latin1()) + << angleDelta + << units + << modifiers + << source + << section + << startDate + << expectedDates; + } + } + } + } + } +#else + QSKIP("Built with --no-feature-wheelevent"); +#endif +} + void tst_QDateTimeEdit::wheelEvent() { - testWidget->setDisplayFormat("dddd/MM"); - testWidget->setDate(QDate(2000, 2, 21)); - testWidget->setCurrentSection(QDateTimeEdit::DaySection); - QWheelEvent w(testWidget->lineEdit()->geometry().center(), 120, 0, 0); - qApp->sendEvent(testWidget, &w); - QCOMPARE(testWidget->date(), QDate(2000, 2, 22)); - testWidget->setCurrentSection(QDateTimeEdit::MonthSection); - qApp->sendEvent(testWidget, &w); - QCOMPARE(testWidget->date(), QDate(2000, 3, 22)); -} +#if QT_CONFIG(wheelevent) + QFETCH(QPoint, angleDelta); + QFETCH(int, qt4Delta); + QFETCH(Qt::KeyboardModifiers, modifiers); + QFETCH(Qt::MouseEventSource, source); + QFETCH(QDateTimeEdit::Section, section); + QFETCH(QDate, startDate); + QFETCH(DateList, expectedDates); + + EditorDateEdit edit(0); + edit.setDate(startDate); + edit.setCurrentSection(section); + + QWheelEvent event(QPointF(), QPointF(), QPoint(), angleDelta, qt4Delta, + Qt::Vertical, Qt::NoButton, modifiers, Qt::NoScrollPhase, + source); + + QCOMPARE(edit.date(), startDate); + for (QDate expected : expectedDates) { + qApp->sendEvent(&edit, &event); + QCOMPARE(edit.date(), expected); + } +#else + QSKIP("Built with --no-feature-wheelevent"); #endif // QT_CONFIG(wheelevent) +} void tst_QDateTimeEdit::specialValueCornerCase() { @@ -3735,5 +3974,238 @@ void tst_QDateTimeEdit::dateEditCorrectSectionSize() } #endif +void tst_QDateTimeEdit::stepModifierKeys_data() +{ + QTest::addColumn<QDate>("startDate"); + QTest::addColumn<QDateTimeEdit::Section>("section"); + QTest::addColumn<QTestEventList>("keys"); + QTest::addColumn<QDate>("expectedDate"); + + const auto keyList = {Qt::Key_Up, Qt::Key_Down}; + + const auto modifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; + + const auto sections = {QDateTimeEdit::DaySection, + QDateTimeEdit::MonthSection, + QDateTimeEdit::YearSection}; + + for (auto key : keyList) { + + const bool up = key == Qt::Key_Up; + Q_ASSERT(up || key == Qt::Key_Down); + + const QDate startDate(2000, up ? 2 : 12, 17); + + for (auto modifier : modifierList) { + + QTestEventList keys; + keys.addKeyClick(key, modifier); + + const auto modifierName = modifierToName(modifier); + if (modifierName.isEmpty()) + continue; + + const int steps = (modifier & Qt::ControlModifier ? 10 : 1) + * (up ? 1 : -1); + + for (const auto section : sections) { + + const auto expectedDate = stepDate(startDate, section, steps); + if (!expectedDate.isValid()) + continue; + + const auto sectionName = sectionToName(section); + + QTest::addRow("%s%sWith%sKeyboardModifier", + up ? "up" : "down", + sectionName.latin1(), + modifierName.latin1()) + << startDate + << section + << keys + << expectedDate; + } + } + } +} + +void tst_QDateTimeEdit::stepModifierKeys() +{ + QFETCH(QDate, startDate); + QFETCH(QDateTimeEdit::Section, section); + QFETCH(QTestEventList, keys); + QFETCH(QDate, expectedDate); + + QDateTimeEdit edit(0); + edit.setDate(startDate); + edit.show(); + QVERIFY(QTest::qWaitForWindowActive(&edit)); + edit.setCurrentSection(section); + + QCOMPARE(edit.date(), startDate); + keys.simulate(&edit); + QCOMPARE(edit.date(), expectedDate); +} + +void tst_QDateTimeEdit::stepModifierButtons_data() +{ + QTest::addColumn<QStyle::SubControl>("subControl"); + QTest::addColumn<Qt::KeyboardModifiers>("modifiers"); + QTest::addColumn<QDateTimeEdit::Section>("section"); + QTest::addColumn<QTime>("startTime"); + QTest::addColumn<QTime>("expectedTime"); + + const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown}; + + const auto modifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; + + const auto sections = {QDateTimeEdit::SecondSection, + QDateTimeEdit::MinuteSection, + QDateTimeEdit::HourSection}; + + const QTime startTime(12, 36, 24); + + for (auto subControl : subControls) { + + const bool up = subControl == QStyle::SC_SpinBoxUp; + Q_ASSERT(up || subControl == QStyle::SC_SpinBoxDown); + + for (auto modifier : modifierList) { + + const Qt::KeyboardModifiers modifiers(modifier); + + const auto modifierName = modifierToName(modifier); + if (modifierName.isEmpty()) + continue; + + const int steps = (modifier & Qt::ControlModifier ? 10 : 1) + * (up ? 1 : -1); + + for (const auto section : sections) { + + const auto expectedTime = stepTime(startTime, section, steps); + if (!expectedTime.isValid()) + continue; + + const auto sectionName = sectionToName(section); + + QTest::addRow("%s%sWith%sKeyboardModifier", + up ? "up" : "down", + sectionName.latin1(), + modifierName.latin1()) + << subControl + << modifiers + << section + << startTime + << expectedTime; + } + } + } +} + +void tst_QDateTimeEdit::stepModifierButtons() +{ + QFETCH(QStyle::SubControl, subControl); + QFETCH(Qt::KeyboardModifiers, modifiers); + QFETCH(QDateTimeEdit::Section, section); + QFETCH(QTime, startTime); + QFETCH(QTime, expectedTime); + + EditorDateEdit edit(0); + edit.setTime(startTime); + edit.show(); + QVERIFY(QTest::qWaitForWindowActive(&edit)); + edit.setCurrentSection(section); + + QStyleOptionSpinBox spinBoxStyleOption; + edit.initStyleOption(&spinBoxStyleOption); + + const QRect buttonRect = edit.style()->subControlRect( + QStyle::CC_SpinBox, &spinBoxStyleOption, subControl, &edit); + + QCOMPARE(edit.time(), startTime); + QTest::mouseClick(&edit, Qt::LeftButton, modifiers, buttonRect.center()); + QCOMPARE(edit.time(), expectedTime); +} + +void tst_QDateTimeEdit::stepModifierPressAndHold_data() +{ + QTest::addColumn<QStyle::SubControl>("subControl"); + QTest::addColumn<Qt::KeyboardModifiers>("modifiers"); + QTest::addColumn<int>("expectedStepModifier"); + + const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown}; + + const auto modifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; + + for (auto subControl : subControls) { + + const bool up = subControl == QStyle::SC_SpinBoxUp; + Q_ASSERT(up || subControl == QStyle::SC_SpinBoxDown); + + for (auto modifier : modifierList) { + + const Qt::KeyboardModifiers modifiers(modifier); + + const auto modifierName = modifierToName(modifier); + if (modifierName.isEmpty()) + continue; + + const int steps = (modifier & Qt::ControlModifier ? 10 : 1) + * (up ? 1 : -1); + + QTest::addRow("%sWith%sKeyboardModifier", + up ? "up" : "down", + modifierName.latin1()) + << subControl + << modifiers + << steps; + } + } +} + +void tst_QDateTimeEdit::stepModifierPressAndHold() +{ + QFETCH(QStyle::SubControl, subControl); + QFETCH(Qt::KeyboardModifiers, modifiers); + QFETCH(int, expectedStepModifier); + + const QDate startDate(2000, 1, 1); + + EditorDateEdit edit(0); + QScopedPointer<PressAndHoldStyle, QScopedPointerDeleteLater> pressAndHoldStyle( + new PressAndHoldStyle); + edit.setStyle(pressAndHoldStyle.data()); + edit.setDate(startDate); + + QSignalSpy spy(&edit, &EditorDateEdit::dateChanged); + + edit.show(); + QVERIFY(QTest::qWaitForWindowActive(&edit)); + edit.setCurrentSection(QDateTimeEdit::YearSection); + + QStyleOptionSpinBox spinBoxStyleOption; + edit.initStyleOption(&spinBoxStyleOption); + + const QRect buttonRect = edit.style()->subControlRect( + QStyle::CC_SpinBox, &spinBoxStyleOption, subControl, &edit); + + QTest::mousePress(&edit, Qt::LeftButton, modifiers, buttonRect.center()); + QTRY_VERIFY(spy.length() >= 3); + QTest::mouseRelease(&edit, Qt::LeftButton, modifiers, buttonRect.center()); + + const auto value = spy.last().at(0); + QVERIFY(value.type() == QVariant::Date); + const QDate expectedDate = startDate.addYears(spy.length() * + expectedStepModifier); + QCOMPARE(value.toDate(), expectedDate); +} + QTEST_MAIN(tst_QDateTimeEdit) #include "tst_qdatetimeedit.moc" diff --git a/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp b/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp index d44cc40527..ee9446f00b 100644 --- a/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp +++ b/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp @@ -31,6 +31,7 @@ #include <qapplication.h> #include <limits.h> +#include <cmath> #include <float.h> #include <qspinbox.h> @@ -40,6 +41,10 @@ #include <qlineedit.h> #include <qdebug.h> +#include <QStyleOptionSpinBox> +#include <QStyle> +#include <QProxyStyle> + class DoubleSpinBox : public QDoubleSpinBox { Q_OBJECT @@ -59,6 +64,16 @@ public: { return QDoubleSpinBox::valueFromText(text); } +#if QT_CONFIG(wheelevent) + void wheelEvent(QWheelEvent *event) + { + QDoubleSpinBox::wheelEvent(event); + } +#endif + void initStyleOption(QStyleOptionSpinBox *option) const + { + QDoubleSpinBox::initStyleOption(option); + } QLineEdit* lineEdit() const { return QDoubleSpinBox::lineEdit(); } public slots: @@ -68,6 +83,26 @@ public slots: } }; +class PressAndHoldStyle : public QProxyStyle +{ + Q_OBJECT +public: + using QProxyStyle::QProxyStyle; + + int styleHint(QStyle::StyleHint hint, const QStyleOption *option = nullptr, + const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const override + { + switch (hint) { + case QStyle::SH_SpinBox_ClickAutoRepeatRate: + return 5; + case QStyle::SH_SpinBox_ClickAutoRepeatThreshold: + return 10; + default: + return QProxyStyle::styleHint(hint, option, widget, returnData); + } + } +}; + class tst_QDoubleSpinBox : public QObject { @@ -135,6 +170,19 @@ private slots: void setGroupSeparatorShown_data(); void setGroupSeparatorShown(); + void adaptiveDecimalStep(); + + void wheelEvents_data(); + void wheelEvents(); + + void stepModifierKeys_data(); + void stepModifierKeys(); + + void stepModifierButtons_data(); + void stepModifierButtons(); + + void stepModifierPressAndHold_data(); + void stepModifierPressAndHold(); public slots: void valueChangedHelper(const QString &); void valueChangedHelper(double); @@ -149,6 +197,24 @@ typedef QList<double> DoubleList; Q_DECLARE_METATYPE(QLocale::Language) Q_DECLARE_METATYPE(QLocale::Country) +static QLatin1String modifierToName(Qt::KeyboardModifier modifier) +{ + switch (modifier) { + case Qt::NoModifier: + return QLatin1Literal("No"); + break; + case Qt::ControlModifier: + return QLatin1Literal("Ctrl"); + break; + case Qt::ShiftModifier: + return QLatin1Literal("Shift"); + break; + default: + qFatal("Unexpected keyboard modifier"); + return QLatin1String(); + } +} + tst_QDoubleSpinBox::tst_QDoubleSpinBox() { @@ -1139,5 +1205,460 @@ void tst_QDoubleSpinBox::setGroupSeparatorShown() QCOMPARE(spinBox.value()+1000, 33000.44); } +void tst_QDoubleSpinBox::adaptiveDecimalStep() +{ + DoubleSpinBox spinBox; + spinBox.setRange(-100000, 100000); + spinBox.setDecimals(4); + spinBox.setStepType(DoubleSpinBox::StepType::AdaptiveDecimalStepType); + + // Positive values + + spinBox.setValue(0); + + // Go from 0 to 0.01 + for (double i = 0; i < 0.00999; i += 0.0001) { + QVERIFY(qFuzzyCompare(spinBox.value(), i)); + spinBox.stepBy(1); + } + + // Go from 0.01 to 0.1 + for (double i = 0.01; i < 0.0999; i += 0.001) { + QVERIFY(qFuzzyCompare(spinBox.value(), i)); + spinBox.stepBy(1); + } + + // Go from 0.1 to 1 + for (double i = 0.1; i < 0.999; i += 0.01) { + QVERIFY(qFuzzyCompare(spinBox.value(), i)); + spinBox.stepBy(1); + } + + // Go from 1 to 10 + for (double i = 1; i < 9.99; i += 0.1) { + QVERIFY(qFuzzyCompare(spinBox.value(), i)); + spinBox.stepBy(1); + } + + // Go from 10 to 100 + for (int i = 10; i < 100; i++) { + QVERIFY(qFuzzyCompare(spinBox.value(), i)); + spinBox.stepBy(1); + } + + // Go from 100 to 1000 + for (int i = 100; i < 1000; i += 10) { + QVERIFY(qFuzzyCompare(spinBox.value(), i)); + spinBox.stepBy(1); + } + + // Go from 1000 to 10000 + for (int i = 1000; i < 10000; i += 100) { + QVERIFY(qFuzzyCompare(spinBox.value(), i)); + spinBox.stepBy(1); + } + + // Test decreasing the values now + + // Go from 10000 down to 1000 + for (int i = 10000; i > 1000; i -= 100) { + QVERIFY(qFuzzyCompare(spinBox.value(), i)); + spinBox.stepBy(-1); + } + + // Go from 1000 down to 100 + for (int i = 1000; i > 100; i -= 10) { + QVERIFY(qFuzzyCompare(spinBox.value(), i)); + spinBox.stepBy(-1); + } + + // Negative values + + spinBox.setValue(0); + + // Go from 0 to -0.01 + for (double i = 0; i > -0.00999; i -= 0.0001) { + QVERIFY(qFuzzyCompare(spinBox.value(), i)); + spinBox.stepBy(-1); + } + + // Go from -0.01 to -0.1 + for (double i = -0.01; i > -0.0999; i -= 0.001) { + QVERIFY(qFuzzyCompare(spinBox.value(), i)); + spinBox.stepBy(-1); + } + + // Go from -0.1 to -1 + for (double i = -0.1; i > -0.999; i -= 0.01) { + QVERIFY(qFuzzyCompare(spinBox.value(), i)); + spinBox.stepBy(-1); + } + + // Go from -1 to -10 + for (double i = -1; i > -9.99; i -= 0.1) { + QVERIFY(qFuzzyCompare(spinBox.value(), i)); + spinBox.stepBy(-1); + } + + // Go from -10 to -100 + for (int i = -10; i > -100; i--) { + QVERIFY(qFuzzyCompare(spinBox.value(), i)); + spinBox.stepBy(-1); + } + + // Go from -100 to -1000 + for (int i = -100; i > -1000; i -= 10) { + QVERIFY(qFuzzyCompare(spinBox.value(), i)); + spinBox.stepBy(-1); + } + + // Go from 1000 to 10000 + for (int i = -1000; i > -10000; i -= 100) { + QVERIFY(qFuzzyCompare(spinBox.value(), i)); + spinBox.stepBy(-1); + } + + // Test increasing the values now + + // Go from -10000 up to -1000 + for (int i = -10000; i < -1000; i += 100) { + QVERIFY(qFuzzyCompare(spinBox.value(), i)); + spinBox.stepBy(1); + } + + // Go from -1000 up to -100 + for (int i = -1000; i < -100; i += 10) { + QVERIFY(qFuzzyCompare(spinBox.value(), i)); + spinBox.stepBy(1); + } +} + +void tst_QDoubleSpinBox::wheelEvents_data() +{ +#if QT_CONFIG(wheelevent) + QTest::addColumn<QPoint>("angleDelta"); + QTest::addColumn<int>("qt4Delta"); + QTest::addColumn<Qt::KeyboardModifiers>("modifier"); + QTest::addColumn<Qt::MouseEventSource>("source"); + QTest::addColumn<double>("start"); + QTest::addColumn<DoubleList>("expectedValues"); + + const auto fractions = {false, true}; + + const auto directions = {true, false}; + + const auto modifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; + + const auto sources = {Qt::MouseEventNotSynthesized, + Qt::MouseEventSynthesizedBySystem, + Qt::MouseEventSynthesizedByQt, + Qt::MouseEventSynthesizedByApplication}; + + const double startValue = 0.0; + + for (auto fraction : fractions) { + for (auto up : directions) { + + const int units = (fraction ? 60 : 120) * (up ? 1 : -1); + + for (auto modifier : modifierList) { + + const Qt::KeyboardModifiers modifiers(modifier); + + const auto modifierName = modifierToName(modifier); + if (modifierName.isEmpty()) + continue; + + const int steps = (modifier & Qt::ControlModifier ? 10 : 1) + * (up ? 1 : -1); + + for (auto source : sources) { + +#ifdef Q_OS_MACOS + QPoint angleDelta; + if ((modifier & Qt::ShiftModifier) && + source == Qt::MouseEventNotSynthesized) { + // On macOS the Shift modifier converts vertical + // mouse wheel events to horizontal. + angleDelta = { units, 0 }; + } else { + // However, this is not the case for trackpad scroll + // events. + angleDelta = { 0, units }; + } +#else + const QPoint angleDelta(0, units); +#endif + + QLatin1String sourceName; + switch (source) { + case Qt::MouseEventNotSynthesized: + sourceName = QLatin1Literal("NotSynthesized"); + break; + case Qt::MouseEventSynthesizedBySystem: + sourceName = QLatin1Literal("SynthesizedBySystem"); + break; + case Qt::MouseEventSynthesizedByQt: + sourceName = QLatin1Literal("SynthesizedByQt"); + break; + case Qt::MouseEventSynthesizedByApplication: + sourceName = QLatin1Literal("SynthesizedByApplication"); + break; + default: + qFatal("Unexpected wheel event source"); + continue; + } + + DoubleList expectedValues; + if (fraction) + expectedValues << startValue; + expectedValues << startValue + steps; + + QTest::addRow("%s%sWith%sKeyboardModifier%s", + fraction ? "half" : "full", + up ? "Up" : "Down", + modifierName.latin1(), + sourceName.latin1()) + << angleDelta + << units + << modifiers + << source + << startValue + << expectedValues; + } + } + } + } +#else + QSKIP("Built with --no-feature-wheelevent"); +#endif +} + +void tst_QDoubleSpinBox::wheelEvents() +{ +#if QT_CONFIG(wheelevent) + QFETCH(QPoint, angleDelta); + QFETCH(int, qt4Delta); + QFETCH(Qt::KeyboardModifiers, modifier); + QFETCH(Qt::MouseEventSource, source); + QFETCH(double, start); + QFETCH(DoubleList, expectedValues); + + DoubleSpinBox spinBox; + spinBox.setRange(-20, 20); + spinBox.setValue(start); + + QWheelEvent event(QPointF(), QPointF(), QPoint(), angleDelta, qt4Delta, + Qt::Vertical, Qt::NoButton, modifier, Qt::NoScrollPhase, + source); + for (int expected : expectedValues) { + qApp->sendEvent(&spinBox, &event); + QCOMPARE(spinBox.value(), expected); + } +#else + QSKIP("Built with --no-feature-wheelevent"); +#endif +} + +void tst_QDoubleSpinBox::stepModifierKeys_data() +{ + QTest::addColumn<double>("startValue"); + QTest::addColumn<QTestEventList>("keys"); + QTest::addColumn<double>("expectedValue"); + + const auto keyList = {Qt::Key_Up, Qt::Key_Down}; + + const auto modifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; + + for (auto key : keyList) { + + const bool up = key == Qt::Key_Up; + Q_ASSERT(up || key == Qt::Key_Down); + + const double startValue = up ? 0.0 : 10.0; + + for (auto modifier : modifierList) { + + QTestEventList keys; + keys.addKeyClick(key, modifier); + + const auto modifierName = modifierToName(modifier); + if (modifierName.isEmpty()) + continue; + + const int steps = (modifier & Qt::ControlModifier ? 10 : 1) + * (up ? 1 : -1); + + const double expectedValue = startValue + steps; + + QTest::addRow("%sWith%sKeyboardModifier", + up ? "up" : "down", + modifierName.latin1()) + << startValue + << keys + << expectedValue; + } + } +} + +void tst_QDoubleSpinBox::stepModifierKeys() +{ + QFETCH(double, startValue); + QFETCH(QTestEventList, keys); + QFETCH(double, expectedValue); + + QDoubleSpinBox spin(0); + spin.setValue(startValue); + spin.show(); + QVERIFY(QTest::qWaitForWindowActive(&spin)); + + QCOMPARE(spin.value(), startValue); + keys.simulate(&spin); + QCOMPARE(spin.value(), expectedValue); +} + +void tst_QDoubleSpinBox::stepModifierButtons_data() +{ + QTest::addColumn<QStyle::SubControl>("subControl"); + QTest::addColumn<Qt::KeyboardModifiers>("modifiers"); + QTest::addColumn<double>("startValue"); + QTest::addColumn<double>("expectedValue"); + + const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown}; + + const auto modifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; + + for (auto subControl : subControls) { + + const bool up = subControl == QStyle::SC_SpinBoxUp; + Q_ASSERT(up || subControl == QStyle::SC_SpinBoxDown); + + const double startValue = up ? 0 : 10; + + for (auto modifier : modifierList) { + + const Qt::KeyboardModifiers modifiers(modifier); + + const auto modifierName = modifierToName(modifier); + if (modifierName.isEmpty()) + continue; + + const int steps = (modifier & Qt::ControlModifier ? 10 : 1) + * (up ? 1 : -1); + + const double expectedValue = startValue + steps; + + QTest::addRow("%sWith%sKeyboardModifier", + up ? "up" : "down", + modifierName.latin1()) + << subControl + << modifiers + << startValue + << expectedValue; + } + } +} + +void tst_QDoubleSpinBox::stepModifierButtons() +{ + QFETCH(QStyle::SubControl, subControl); + QFETCH(Qt::KeyboardModifiers, modifiers); + QFETCH(double, startValue); + QFETCH(double, expectedValue); + + DoubleSpinBox spin(0); + spin.setRange(-20, 20); + spin.setValue(startValue); + spin.show(); + QVERIFY(QTest::qWaitForWindowActive(&spin)); + + QStyleOptionSpinBox spinBoxStyleOption; + spin.initStyleOption(&spinBoxStyleOption); + + const QRect buttonRect = spin.style()->subControlRect( + QStyle::CC_SpinBox, &spinBoxStyleOption, subControl, &spin); + + QCOMPARE(spin.value(), startValue); + QTest::mouseClick(&spin, Qt::LeftButton, modifiers, buttonRect.center()); + QCOMPARE(spin.value(), expectedValue); +} + +void tst_QDoubleSpinBox::stepModifierPressAndHold_data() +{ + QTest::addColumn<QStyle::SubControl>("subControl"); + QTest::addColumn<Qt::KeyboardModifiers>("modifiers"); + QTest::addColumn<int>("expectedStepModifier"); + + const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown}; + + const auto modifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; + + for (auto subControl : subControls) { + + const bool up = subControl == QStyle::SC_SpinBoxUp; + Q_ASSERT(up || subControl == QStyle::SC_SpinBoxDown); + + for (auto modifier : modifierList) { + + const Qt::KeyboardModifiers modifiers(modifier); + + const auto modifierName = modifierToName(modifier); + if (modifierName.isEmpty()) + continue; + + const int steps = (modifier & Qt::ControlModifier ? 10 : 1) + * (up ? 1 : -1); + + QTest::addRow("%sWith%sKeyboardModifier", + up ? "up" : "down", + modifierName.latin1()) + << subControl + << modifiers + << steps; + } + } +} + +void tst_QDoubleSpinBox::stepModifierPressAndHold() +{ + QFETCH(QStyle::SubControl, subControl); + QFETCH(Qt::KeyboardModifiers, modifiers); + QFETCH(int, expectedStepModifier); + + DoubleSpinBox spin(0); + QScopedPointer<PressAndHoldStyle, QScopedPointerDeleteLater> pressAndHoldStyle( + new PressAndHoldStyle); + spin.setStyle(pressAndHoldStyle.data()); + spin.setRange(-100.0, 100.0); + spin.setValue(0.0); + + QSignalSpy spy(&spin, QOverload<double>::of(&DoubleSpinBox::valueChanged)); + + spin.show(); + QVERIFY(QTest::qWaitForWindowExposed(&spin)); + + QStyleOptionSpinBox spinBoxStyleOption; + spin.initStyleOption(&spinBoxStyleOption); + + const QRect buttonRect = spin.style()->subControlRect( + QStyle::CC_SpinBox, &spinBoxStyleOption, subControl, &spin); + + QTest::mousePress(&spin, Qt::LeftButton, modifiers, buttonRect.center()); + QTRY_VERIFY(spy.length() >= 3); + QTest::mouseRelease(&spin, Qt::LeftButton, modifiers, buttonRect.center()); + + const auto value = spy.last().at(0); + QVERIFY(value.type() == QVariant::Double); + QCOMPARE(value.toDouble(), spy.length() * expectedStepModifier); +} + QTEST_MAIN(tst_QDoubleSpinBox) #include "tst_qdoublespinbox.moc" diff --git a/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp b/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp index 17462a5f86..7eb739611a 100644 --- a/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp +++ b/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp @@ -2279,6 +2279,16 @@ void tst_QLineEdit::deleteSelectedText() } +class ToUpperValidator : public QValidator +{ +public: + ToUpperValidator() {} + State validate(QString &input, int &) const override + { + input = input.toUpper(); + return QValidator::Acceptable; + } +}; void tst_QLineEdit::textChangedAndTextEdited() { @@ -2320,6 +2330,23 @@ void tst_QLineEdit::textChangedAndTextEdited() QCOMPARE(edited_count, 0); QVERIFY(changed_string.isEmpty()); QVERIFY(!changed_string.isNull()); + + changed_count = 0; + edited_count = 0; + changed_string.clear(); + + QScopedPointer<ToUpperValidator> validator(new ToUpperValidator()); + testWidget->setValidator(validator.data()); + testWidget->setText("foo"); + QCOMPARE(changed_count, 1); + QCOMPARE(edited_count, 0); + QCOMPARE(changed_string, QLatin1String("FOO")); + testWidget->setCursorPosition(sizeof("foo")); + QTest::keyClick(testWidget, 'b'); + QCOMPARE(changed_count, 2); + QCOMPARE(edited_count, 1); + QCOMPARE(changed_string, QLatin1String("FOOB")); + testWidget->setValidator(nullptr); } void tst_QLineEdit::onTextChanged(const QString &text) diff --git a/tests/auto/widgets/widgets/qmenu/qmenu.pro b/tests/auto/widgets/widgets/qmenu/qmenu.pro index 84b6530184..4a492ee941 100644 --- a/tests/auto/widgets/widgets/qmenu/qmenu.pro +++ b/tests/auto/widgets/widgets/qmenu/qmenu.pro @@ -5,4 +5,6 @@ SOURCES += tst_qmenu.cpp macx:{ OBJECTIVE_SOURCES += tst_qmenu_mac.mm LIBS += -lobjc +} else { + DEFINES += QTEST_QPA_MOUSE_HANDLING } diff --git a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp index 0dde385bdb..4a7d653064 100644 --- a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp +++ b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp @@ -986,9 +986,6 @@ void tst_QMenu::task258920_mouseBorder() QAction *action = menu.addAction("test"); const QPoint center = QApplication::desktop()->availableGeometry().center(); -#ifndef QT_NO_CURSOR - QCursor::setPos(center - QPoint(100, 100)); // Mac: Ensure cursor is outside -#endif menu.popup(center); QVERIFY(QTest::qWaitForWindowExposed(&menu)); QRect actionRect = menu.actionGeometry(action); @@ -1229,96 +1226,90 @@ public: void tst_QMenu::QTBUG47515_widgetActionEnterLeave() { -#if !QT_CONFIG(cursor) - QSKIP("This test requires QCursor API"); -#else if (QGuiApplication::platformName() == QLatin1String("cocoa")) QSKIP("See QTBUG-63031"); - QPoint screenCenter = QGuiApplication::primaryScreen()->availableGeometry().center(); - QPoint pointOutsideMenu = screenCenter - QPoint(100, 100); + const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry(); + QRect geometry(QPoint(), availableGeometry.size() / 3); + geometry.moveCenter(availableGeometry.center()); + QPoint pointOutsideMenu = geometry.bottomRight() - QPoint(5, 5); - QMenu menu("Menu1"); - QMenu submenu("Menu2"); + QMainWindow topLevel; + topLevel.setGeometry(geometry); - QWidgetAction menuAction(&menu); - MyWidget w1(&menu); - menuAction.setDefaultWidget(&w1); + QMenuBar *menuBar = topLevel.menuBar(); + menuBar->setNativeMenuBar(false); + QMenu *menu = menuBar->addMenu("Menu1"); + QMenu *submenu = menu->addMenu("Menu2"); - QWidgetAction submenuAction(&submenu); - MyWidget w2(&submenu); - submenuAction.setDefaultWidget(&w2); + QWidgetAction *menuAction = new QWidgetAction(menu); + MyWidget *w1 = new MyWidget(menu); + menuAction->setDefaultWidget(w1); - QAction *nextMenuAct = menu.addMenu(&submenu); + QWidgetAction *submenuAction = new QWidgetAction(submenu); + MyWidget *w2 = new MyWidget(submenu); + submenuAction->setDefaultWidget(w2); - menu.addAction(&menuAction); - submenu.addAction(&submenuAction); + QAction *nextMenuAct = menu->addMenu(submenu); - // Root menu - { - QCursor::setPos(pointOutsideMenu); - QTRY_COMPARE(QCursor::pos(), pointOutsideMenu); - menu.popup(screenCenter); - QVERIFY(QTest::qWaitForWindowExposed(&menu)); + menu->addAction(menuAction); + submenu->addAction(submenuAction); - w1.enter = 0; - w1.leave = 0; - QPoint w1Center = w1.rect().center(); - const QPoint w1CenterGlobal = w1.mapToGlobal(w1Center); - QCursor::setPos(w1CenterGlobal); - QTRY_COMPARE(QCursor::pos(), w1CenterGlobal); - QVERIFY(w1.isVisible()); - QTRY_COMPARE(w1.leave, 0); - QTRY_COMPARE(w1.enter, 1); + topLevel.show(); + topLevel.setWindowTitle(QTest::currentTestFunction()); + QVERIFY(QTest::qWaitForWindowActive(&topLevel)); + QWindow *topLevelWindow = topLevel.windowHandle(); + QVERIFY(topLevelWindow); + + // Root menu: Click on menu bar to open menu1 + { + const QPoint menuActionPos = menuBar->mapTo(&topLevel, menuBar->actionGeometry(menu->menuAction()).center()); + QTest::mouseClick(topLevelWindow, Qt::LeftButton, Qt::KeyboardModifiers(), menuActionPos); + QVERIFY(QTest::qWaitForWindowExposed(menu)); + + w1->enter = 0; + w1->leave = 0; + QPoint w1Center = topLevel.mapFromGlobal(w1->mapToGlobal(w1->rect().center())); + QTest::mouseMove(topLevelWindow, w1Center); + QVERIFY(w1->isVisible()); + QTRY_COMPARE(w1->leave, 0); + QTRY_COMPARE(w1->enter, 1); // Check whether leave event is not delivered on mouse move - w1.move = 0; - QWidget *nativeParent = w1.nativeParentWidget(); - QVERIFY(nativeParent); - QWindow *window = nativeParent->windowHandle(); - QVERIFY(window); - QTest::mouseMove(window, w1.mapTo(nativeParent, w1Center + QPoint(1, 1))); - QTRY_COMPARE(w1.move, 1); - QTRY_COMPARE(w1.leave, 0); - QTRY_COMPARE(w1.enter, 1); - - QCursor::setPos(pointOutsideMenu); - QTRY_COMPARE(QCursor::pos(), pointOutsideMenu); - QTRY_COMPARE(w1.leave, 1); - QTRY_COMPARE(w1.enter, 1); + w1->move = 0; + QTest::mouseMove(topLevelWindow, w1Center + QPoint(1, 1)); + QTRY_COMPARE(w1->move, 1); + QTRY_COMPARE(w1->leave, 0); + QTRY_COMPARE(w1->enter, 1); + + QTest::mouseMove(topLevelWindow, topLevel.mapFromGlobal(pointOutsideMenu)); + QTRY_COMPARE(w1->leave, 1); + QTRY_COMPARE(w1->enter, 1); } // Submenu { - menu.setActiveAction(nextMenuAct); - QVERIFY(QTest::qWaitForWindowExposed(&submenu)); + menu->setActiveAction(nextMenuAct); + QVERIFY(QTest::qWaitForWindowExposed(submenu)); - QPoint w2Center = w2.rect().center(); - const QPoint w2CenterGlobal = w2.mapToGlobal(w2Center); - QCursor::setPos(w2CenterGlobal); - QTRY_COMPARE(QCursor::pos(), w2CenterGlobal); + QPoint w2Center = topLevel.mapFromGlobal(w2->mapToGlobal(w2->rect().center())); + QTest::mouseMove(topLevelWindow, w2Center); - QVERIFY(w2.isVisible()); - QTRY_COMPARE(w2.leave, 0); - QTRY_COMPARE(w2.enter, 1); + QVERIFY(w2->isVisible()); + QTRY_COMPARE(w2->leave, 0); + QTRY_COMPARE(w2->enter, 1); // Check whether leave event is not delivered on mouse move - w2.move = 0; - QWidget *nativeParent = w2.nativeParentWidget(); - QVERIFY(nativeParent); - QWindow *window = nativeParent->windowHandle(); - QVERIFY(window); - QTest::mouseMove(window, w2.mapTo(nativeParent, w2Center + QPoint(1, 1))); - QTRY_COMPARE(w2.move, 1); - QTRY_COMPARE(w2.leave, 0); - QTRY_COMPARE(w2.enter, 1); - - QCursor::setPos(pointOutsideMenu); - QTRY_COMPARE(QCursor::pos(), pointOutsideMenu); - QTRY_COMPARE(w2.leave, 1); - QTRY_COMPARE(w2.enter, 1); + w2->move = 0; + QTest::mouseMove(topLevelWindow, w2Center + QPoint(1, 1)); + QTRY_COMPARE(w2->move, 1); + QTRY_COMPARE(w2->leave, 0); + QTRY_COMPARE(w2->enter, 1); + + QTest::mouseMove(topLevelWindow, topLevel.mapFromGlobal(pointOutsideMenu)); + QTRY_COMPARE(w2->leave, 1); + QTRY_COMPARE(w2->enter, 1); } -#endif // QT_NO_CURSOR } class MyMenu : public QMenu diff --git a/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp b/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp index ab82268578..60fdd6c204 100644 --- a/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp +++ b/tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp @@ -1754,7 +1754,6 @@ void tst_QMenuBar::QTBUG_57404_existingMenuItemException() mb->addMenu(editMenu); QAction *copyAction = editMenu->addAction("&Copy"); copyAction->setShortcut(QKeySequence("Ctrl+C")); - QTest::ignoreMessage(QtWarningMsg, "Menu item \"&Copy\" has unsupported role QPlatformMenuItem::MenuRole(NoRole)"); copyAction->setMenuRole(QAction::NoRole); QVERIFY(QTest::qWaitForWindowExposed(&mw2)); diff --git a/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp b/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp index 57816f9f70..0eb2cf5cdc 100644 --- a/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp +++ b/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp @@ -50,6 +50,9 @@ #include <QKeySequence> #include <QStackedWidget> #include <QDebug> +#include <QStyleOptionSpinBox> +#include <QStyle> +#include <QProxyStyle> class SpinBox : public QSpinBox { @@ -75,10 +78,34 @@ public: QSpinBox::wheelEvent(event); } #endif + void initStyleOption(QStyleOptionSpinBox *option) const + { + QSpinBox::initStyleOption(option); + } QLineEdit *lineEdit() const { return QSpinBox::lineEdit(); } }; +class PressAndHoldStyle : public QProxyStyle +{ + Q_OBJECT +public: + using QProxyStyle::QProxyStyle; + + int styleHint(QStyle::StyleHint hint, const QStyleOption *option = nullptr, + const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const override + { + switch (hint) { + case QStyle::SH_SpinBox_ClickAutoRepeatRate: + return 5; + case QStyle::SH_SpinBox_ClickAutoRepeatThreshold: + return 10; + default: + return QProxyStyle::styleHint(hint, option, widget, returnData); + } + } +}; + class tst_QSpinBox : public QObject { Q_OBJECT @@ -143,8 +170,19 @@ private slots: void setGroupSeparatorShown_data(); void setGroupSeparatorShown(); + void wheelEvents_data(); void wheelEvents(); + void adaptiveDecimalStep(); + + void stepModifierKeys_data(); + void stepModifierKeys(); + + void stepModifierButtons_data(); + void stepModifierButtons(); + + void stepModifierPressAndHold_data(); + void stepModifierPressAndHold(); public slots: void valueChangedHelper(const QString &); void valueChangedHelper(int); @@ -158,6 +196,24 @@ typedef QList<int> IntList; Q_DECLARE_METATYPE(QLocale::Language) Q_DECLARE_METATYPE(QLocale::Country) +static QLatin1String modifierToName(Qt::KeyboardModifier modifier) +{ + switch (modifier) { + case Qt::NoModifier: + return QLatin1Literal("No"); + break; + case Qt::ControlModifier: + return QLatin1Literal("Ctrl"); + break; + case Qt::ShiftModifier: + return QLatin1Literal("Shift"); + break; + default: + qFatal("Unexpected keyboard modifier"); + return QLatin1String(); + } +} + // Testing get/set functions void tst_QSpinBox::getSetCheck() { @@ -1215,28 +1271,410 @@ void tst_QSpinBox::setGroupSeparatorShown() QCOMPARE(spinBox.value()+1000, 33000); } +void tst_QSpinBox::wheelEvents_data() +{ +#if QT_CONFIG(wheelevent) + QTest::addColumn<QPoint>("angleDelta"); + QTest::addColumn<int>("qt4Delta"); + QTest::addColumn<Qt::KeyboardModifiers>("modifier"); + QTest::addColumn<Qt::MouseEventSource>("source"); + QTest::addColumn<int>("start"); + QTest::addColumn<IntList>("expectedValues"); + + const auto fractions = {false, true}; + + const auto directions = {true, false}; + + const auto modifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; + + const auto sources = {Qt::MouseEventNotSynthesized, + Qt::MouseEventSynthesizedBySystem, + Qt::MouseEventSynthesizedByQt, + Qt::MouseEventSynthesizedByApplication}; + + const int startValue = 0; + + for (auto fraction : fractions) { + for (auto up : directions) { + + const int units = (fraction ? 60 : 120) * (up ? 1 : -1); + + for (auto modifier : modifierList) { + + const Qt::KeyboardModifiers modifiers(modifier); + + const auto modifierName = modifierToName(modifier); + if (modifierName.isEmpty()) + continue; + + const int steps = (modifier & Qt::ControlModifier ? 10 : 1) + * (up ? 1 : -1); + + for (auto source : sources) { + +#ifdef Q_OS_MACOS + QPoint angleDelta; + if ((modifier & Qt::ShiftModifier) && + source == Qt::MouseEventNotSynthesized) { + // On macOS the Shift modifier converts vertical + // mouse wheel events to horizontal. + angleDelta = { units, 0 }; + } else { + // However, this is not the case for trackpad scroll + // events. + angleDelta = { 0, units }; + } +#else + const QPoint angleDelta(0, units); +#endif + + QLatin1String sourceName; + switch (source) { + case Qt::MouseEventNotSynthesized: + sourceName = QLatin1Literal("NotSynthesized"); + break; + case Qt::MouseEventSynthesizedBySystem: + sourceName = QLatin1Literal("SynthesizedBySystem"); + break; + case Qt::MouseEventSynthesizedByQt: + sourceName = QLatin1Literal("SynthesizedByQt"); + break; + case Qt::MouseEventSynthesizedByApplication: + sourceName = QLatin1Literal("SynthesizedByApplication"); + break; + default: + qFatal("Unexpected wheel event source"); + continue; + } + + IntList expectedValues; + if (fraction) + expectedValues << startValue; + expectedValues << startValue + steps; + + QTest::addRow("%s%sWith%sKeyboardModifier%s", + fraction ? "half" : "full", + up ? "Up" : "Down", + modifierName.latin1(), + sourceName.latin1()) + << angleDelta + << units + << modifiers + << source + << startValue + << expectedValues; + } + } + } + } +#else + QSKIP("Built with --no-feature-wheelevent"); +#endif +} + void tst_QSpinBox::wheelEvents() { #if QT_CONFIG(wheelevent) + QFETCH(QPoint, angleDelta); + QFETCH(int, qt4Delta); + QFETCH(Qt::KeyboardModifiers, modifier); + QFETCH(Qt::MouseEventSource, source); + QFETCH(int, start); + QFETCH(IntList, expectedValues); + SpinBox spinBox; spinBox.setRange(-20, 20); + spinBox.setValue(start); + + QWheelEvent event(QPointF(), QPointF(), QPoint(), angleDelta, qt4Delta, + Qt::Vertical, Qt::NoButton, modifier, Qt::NoScrollPhase, + source); + for (int expected : expectedValues) { + qApp->sendEvent(&spinBox, &event); + QCOMPARE(spinBox.value(), expected); + } +#else + QSKIP("Built with --no-feature-wheelevent"); +#endif +} + +void tst_QSpinBox::adaptiveDecimalStep() +{ + SpinBox spinBox; + spinBox.setRange(-100000, 100000); + spinBox.setStepType(SpinBox::StepType::AdaptiveDecimalStepType); + + // Positive values + spinBox.setValue(0); - QWheelEvent wheelUp(QPointF(), QPointF(), QPoint(), QPoint(0, 120), 120, Qt::Vertical, Qt::NoButton, Qt::NoModifier); - spinBox.wheelEvent(&wheelUp); - QCOMPARE(spinBox.value(), 1); + // Go from 0 to 100 + for (int i = 0; i < 100; i++) { + QCOMPARE(spinBox.value(), i); + spinBox.stepBy(1); + } - QWheelEvent wheelDown(QPointF(), QPointF(), QPoint(), QPoint(0, -120), -120, Qt::Vertical, Qt::NoButton, Qt::NoModifier); - spinBox.wheelEvent(&wheelDown); - spinBox.wheelEvent(&wheelDown); - QCOMPARE(spinBox.value(), -1); + // Go from 100 to 1000 + for (int i = 100; i < 1000; i += 10) { + QCOMPARE(spinBox.value(), i); + spinBox.stepBy(1); + } - QWheelEvent wheelHalfUp(QPointF(), QPointF(), QPoint(), QPoint(0, 60), 60, Qt::Vertical, Qt::NoButton, Qt::NoModifier); - spinBox.wheelEvent(&wheelHalfUp); - QCOMPARE(spinBox.value(), -1); - spinBox.wheelEvent(&wheelHalfUp); - QCOMPARE(spinBox.value(), 0); -#endif + // Go from 1000 to 10000 + for (int i = 1000; i < 10000; i += 100) { + QCOMPARE(spinBox.value(), i); + spinBox.stepBy(1); + } + + // Test decreasing the values now + + // Go from 10000 down to 1000 + for (int i = 10000; i > 1000; i -= 100) { + QCOMPARE(spinBox.value(), i); + spinBox.stepBy(-1); + } + + // Go from 1000 down to 100 + for (int i = 1000; i > 100; i -= 10) { + QCOMPARE(spinBox.value(), i); + spinBox.stepBy(-1); + } + + // Negative values + + spinBox.setValue(0); + + // Go from 0 to -100 + for (int i = 0; i > -100; i--) { + QCOMPARE(spinBox.value(), i); + spinBox.stepBy(-1); + } + + // Go from -100 to -1000 + for (int i = -100; i > -1000; i -= 10) { + QCOMPARE(spinBox.value(), i); + spinBox.stepBy(-1); + } + + // Go from 1000 to 10000 + for (int i = -1000; i > -10000; i -= 100) { + QCOMPARE(spinBox.value(), i); + spinBox.stepBy(-1); + } + + // Test increasing the values now + + // Go from -10000 up to -1000 + for (int i = -10000; i < -1000; i += 100) { + QCOMPARE(spinBox.value(), i); + spinBox.stepBy(1); + } + + // Go from -1000 up to -100 + for (int i = -1000; i < -100; i += 10) { + QCOMPARE(spinBox.value(), i); + spinBox.stepBy(1); + } +} + +void tst_QSpinBox::stepModifierKeys_data() +{ + QTest::addColumn<int>("startValue"); + QTest::addColumn<QTestEventList>("keys"); + QTest::addColumn<int>("expectedValue"); + + const auto keyList = {Qt::Key_Up, Qt::Key_Down}; + + const auto modifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; + + for (auto key : keyList) { + + const bool up = key == Qt::Key_Up; + Q_ASSERT(up || key == Qt::Key_Down); + + const int startValue = up ? 0.0 : 10.0; + + for (auto modifier : modifierList) { + + QTestEventList keys; + keys.addKeyClick(key, modifier); + + const auto modifierName = modifierToName(modifier); + if (modifierName.isEmpty()) + continue; + + const int steps = (modifier & Qt::ControlModifier ? 10 : 1) + * (up ? 1 : -1); + + const int expectedValue = startValue + steps; + + QTest::addRow("%sWith%sKeyboardModifier", + up ? "up" : "down", + modifierName.latin1()) + << startValue + << keys + << expectedValue; + } + } +} + +void tst_QSpinBox::stepModifierKeys() +{ + QFETCH(int, startValue); + QFETCH(QTestEventList, keys); + QFETCH(int, expectedValue); + + QSpinBox spin(0); + spin.setValue(startValue); + spin.show(); + QVERIFY(QTest::qWaitForWindowActive(&spin)); + + QCOMPARE(spin.value(), startValue); + keys.simulate(&spin); + QCOMPARE(spin.value(), expectedValue); +} + +void tst_QSpinBox::stepModifierButtons_data() +{ + QTest::addColumn<QStyle::SubControl>("subControl"); + QTest::addColumn<Qt::KeyboardModifiers>("modifiers"); + QTest::addColumn<int>("startValue"); + QTest::addColumn<int>("expectedValue"); + + const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown}; + + const auto modifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; + + for (auto subControl : subControls) { + + const bool up = subControl == QStyle::SC_SpinBoxUp; + Q_ASSERT(up || subControl == QStyle::SC_SpinBoxDown); + + const int startValue = up ? 0 : 10; + + for (auto modifier : modifierList) { + + const Qt::KeyboardModifiers modifiers(modifier); + + const auto modifierName = modifierToName(modifier); + if (modifierName.isEmpty()) + continue; + + const int steps = (modifier & Qt::ControlModifier ? 10 : 1) + * (up ? 1 : -1); + + const int expectedValue = startValue + steps; + + QTest::addRow("%sWith%sKeyboardModifier", + up ? "up" : "down", + modifierName.latin1()) + << subControl + << modifiers + << startValue + << expectedValue; + } + } +} + +void tst_QSpinBox::stepModifierButtons() +{ + QFETCH(QStyle::SubControl, subControl); + QFETCH(Qt::KeyboardModifiers, modifiers); + QFETCH(int, startValue); + QFETCH(int, expectedValue); + + SpinBox spin(0); + spin.setRange(-20, 20); + spin.setValue(startValue); + spin.show(); + QVERIFY(QTest::qWaitForWindowActive(&spin)); + + QStyleOptionSpinBox spinBoxStyleOption; + spin.initStyleOption(&spinBoxStyleOption); + + const QRect buttonRect = spin.style()->subControlRect( + QStyle::CC_SpinBox, &spinBoxStyleOption, subControl, &spin); + + QCOMPARE(spin.value(), startValue); + QTest::mouseClick(&spin, Qt::LeftButton, modifiers, buttonRect.center()); + QCOMPARE(spin.value(), expectedValue); +} + +void tst_QSpinBox::stepModifierPressAndHold_data() +{ + QTest::addColumn<QStyle::SubControl>("subControl"); + QTest::addColumn<Qt::KeyboardModifiers>("modifiers"); + QTest::addColumn<int>("expectedStepModifier"); + + const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown}; + + const auto modifierList = {Qt::NoModifier, + Qt::ControlModifier, + Qt::ShiftModifier}; + + for (auto subControl : subControls) { + + const bool up = subControl == QStyle::SC_SpinBoxUp; + Q_ASSERT(up || subControl == QStyle::SC_SpinBoxDown); + + for (auto modifier : modifierList) { + + const Qt::KeyboardModifiers modifiers(modifier); + + const auto modifierName = modifierToName(modifier); + if (modifierName.isEmpty()) + continue; + + const int steps = (modifier & Qt::ControlModifier ? 10 : 1) + * (up ? 1 : -1); + + QTest::addRow("%sWith%sKeyboardModifier", + up ? "up" : "down", + modifierName.latin1()) + << subControl + << modifiers + << steps; + } + } +} + +void tst_QSpinBox::stepModifierPressAndHold() +{ + QFETCH(QStyle::SubControl, subControl); + QFETCH(Qt::KeyboardModifiers, modifiers); + QFETCH(int, expectedStepModifier); + + SpinBox spin(0); + QScopedPointer<PressAndHoldStyle, QScopedPointerDeleteLater> pressAndHoldStyle( + new PressAndHoldStyle); + spin.setStyle(pressAndHoldStyle.data()); + spin.setRange(-100, 100); + spin.setValue(0); + + QSignalSpy spy(&spin, QOverload<int>::of(&SpinBox::valueChanged)); + + spin.show(); + QVERIFY(QTest::qWaitForWindowActive(&spin)); + + QStyleOptionSpinBox spinBoxStyleOption; + spin.initStyleOption(&spinBoxStyleOption); + + const QRect buttonRect = spin.style()->subControlRect( + QStyle::CC_SpinBox, &spinBoxStyleOption, subControl, &spin); + + QTest::mousePress(&spin, Qt::LeftButton, modifiers, buttonRect.center()); + QTRY_VERIFY(spy.length() >= 3); + QTest::mouseRelease(&spin, Qt::LeftButton, modifiers, buttonRect.center()); + + const auto value = spy.last().at(0); + QVERIFY(value.type() == QVariant::Int); + QCOMPARE(value.toInt(), spy.length() * expectedStepModifier); } QTEST_MAIN(tst_QSpinBox) |