diff options
Diffstat (limited to 'tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp')
-rw-r--r-- | tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp | 635 |
1 files changed, 635 insertions, 0 deletions
diff --git a/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp b/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp index d44cc40527..b1610c297d 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,46 @@ 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 StepModifierStyle : 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_StepModifier: + return stepModifier; + default: + return QProxyStyle::styleHint(hint, option, widget, returnData); + } + } + + Qt::KeyboardModifier stepModifier = Qt::ControlModifier; +}; + class tst_QDoubleSpinBox : public QObject { @@ -135,6 +190,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 +217,30 @@ 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; + case Qt::AltModifier: + return QLatin1Literal("Alt"); + break; + case Qt::MetaModifier: + return QLatin1Literal("Meta"); + break; + default: + qFatal("Unexpected keyboard modifier"); + return QLatin1String(); + } +} + tst_QDoubleSpinBox::tst_QDoubleSpinBox() { @@ -1139,5 +1231,548 @@ 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<int>("stepModifier"); + 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::ShiftModifier, + Qt::ControlModifier, + Qt::AltModifier, + Qt::MetaModifier}; + + const auto validStepModifierList = {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; + + for (auto stepModifier : validStepModifierList) { + + const auto stepModifierName = modifierToName(stepModifier); + if (stepModifierName.isEmpty()) + continue; + + const int steps = (modifier & stepModifier ? 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%s%sWith%sKeyboardModifier%s", + fraction ? "half" : "full", + up ? "Up" : "Down", + stepModifierName.latin1(), + modifierName.latin1(), + sourceName.latin1()) + << angleDelta + << units + << static_cast<int>(stepModifier) + << 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(int, stepModifier); + QFETCH(Qt::KeyboardModifiers, modifier); + QFETCH(Qt::MouseEventSource, source); + QFETCH(double, start); + QFETCH(DoubleList, expectedValues); + + DoubleSpinBox spinBox; + spinBox.setRange(-20, 20); + spinBox.setValue(start); + + QScopedPointer<StepModifierStyle, QScopedPointerDeleteLater> style( + new StepModifierStyle); + style->stepModifier = static_cast<Qt::KeyboardModifier>(stepModifier); + spinBox.setStyle(style.data()); + + 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<int>("stepModifier"); + QTest::addColumn<QTestEventList>("keys"); + QTest::addColumn<double>("expectedValue"); + + const auto keyList = {Qt::Key_Up, Qt::Key_Down}; + + const auto modifierList = {Qt::NoModifier, + Qt::ShiftModifier, + Qt::ControlModifier, + Qt::AltModifier, + Qt::MetaModifier}; + + const auto validStepModifierList = {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; + + for (auto stepModifier : validStepModifierList) { + + const auto stepModifierName = modifierToName(stepModifier); + if (stepModifierName.isEmpty()) + continue; + + const int steps = (modifier & stepModifier ? 10 : 1) + * (up ? 1 : -1); + + const double expectedValue = startValue + steps; + + QTest::addRow("%s%sWith%sKeyboardModifier", + up ? "up" : "down", + stepModifierName.latin1(), + modifierName.latin1()) + << startValue + << static_cast<int>(stepModifier) + << keys + << expectedValue; + } + } + } +} + +void tst_QDoubleSpinBox::stepModifierKeys() +{ + QFETCH(double, startValue); + QFETCH(int, stepModifier); + QFETCH(QTestEventList, keys); + QFETCH(double, expectedValue); + + QDoubleSpinBox spin(0); + spin.setValue(startValue); + + QScopedPointer<StepModifierStyle, QScopedPointerDeleteLater> style( + new StepModifierStyle); + style->stepModifier = static_cast<Qt::KeyboardModifier>(stepModifier); + spin.setStyle(style.data()); + + 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<int>("stepModifier"); + 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::ShiftModifier, + Qt::ControlModifier, + Qt::AltModifier, + Qt::MetaModifier}; + + const auto validStepModifierList = {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; + + for (auto stepModifier : validStepModifierList) { + + const auto stepModifierName = modifierToName(stepModifier); + if (stepModifierName.isEmpty()) + continue; + + const int steps = (modifier & stepModifier ? 10 : 1) + * (up ? 1 : -1); + + const double expectedValue = startValue + steps; + + QTest::addRow("%s%sWith%sKeyboardModifier", + up ? "up" : "down", + stepModifierName.latin1(), + modifierName.latin1()) + << subControl + << static_cast<int>(stepModifier) + << modifiers + << startValue + << expectedValue; + } + } + } +} + +void tst_QDoubleSpinBox::stepModifierButtons() +{ + QFETCH(QStyle::SubControl, subControl); + QFETCH(int, stepModifier); + QFETCH(Qt::KeyboardModifiers, modifiers); + QFETCH(double, startValue); + QFETCH(double, expectedValue); + + DoubleSpinBox spin(0); + spin.setRange(-20, 20); + spin.setValue(startValue); + + QScopedPointer<StepModifierStyle, QScopedPointerDeleteLater> style( + new StepModifierStyle); + style->stepModifier = static_cast<Qt::KeyboardModifier>(stepModifier); + spin.setStyle(style.data()); + + 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<int>("stepModifier"); + QTest::addColumn<Qt::KeyboardModifiers>("modifiers"); + QTest::addColumn<int>("expectedStepModifier"); + + const auto subControls = {QStyle::SC_SpinBoxUp, QStyle::SC_SpinBoxDown}; + + const auto modifierList = {Qt::NoModifier, + Qt::ShiftModifier, + Qt::ControlModifier, + Qt::AltModifier, + Qt::MetaModifier}; + + const auto validStepModifierList = {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; + + for (auto stepModifier : validStepModifierList) { + + const auto stepModifierName = modifierToName(stepModifier); + if (stepModifierName.isEmpty()) + continue; + + const int steps = (modifier & stepModifier ? 10 : 1) + * (up ? 1 : -1); + + QTest::addRow("%s%sWith%sKeyboardModifier", + up ? "up" : "down", + stepModifierName.latin1(), + modifierName.latin1()) + << subControl + << static_cast<int>(stepModifier) + << modifiers + << steps; + } + } + } +} + +void tst_QDoubleSpinBox::stepModifierPressAndHold() +{ + QFETCH(QStyle::SubControl, subControl); + QFETCH(int, stepModifier); + QFETCH(Qt::KeyboardModifiers, modifiers); + QFETCH(int, expectedStepModifier); + + DoubleSpinBox spin(0); + spin.setRange(-100.0, 100.0); + spin.setValue(0.0); + + QScopedPointer<StepModifierStyle, QScopedPointerDeleteLater> stepModifierStyle( + new StepModifierStyle(new PressAndHoldStyle)); + stepModifierStyle->stepModifier = static_cast<Qt::KeyboardModifier>(stepModifier); + spin.setStyle(stepModifierStyle.data()); + + 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" |