summaryrefslogtreecommitdiffstats
path: root/tests/auto/widgets/widgets
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/widgets/widgets')
-rw-r--r--tests/auto/widgets/widgets/qcombobox/qcombobox.pro1
-rw-r--r--tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp38
-rw-r--r--tests/auto/widgets/widgets/qdatetimeedit/tst_qdatetimeedit.cpp496
-rw-r--r--tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp521
-rw-r--r--tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp27
-rw-r--r--tests/auto/widgets/widgets/qmenu/qmenu.pro2
-rw-r--r--tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp135
-rw-r--r--tests/auto/widgets/widgets/qmenubar/tst_qmenubar.cpp1
-rw-r--r--tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp464
9 files changed, 1556 insertions, 129 deletions
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)