summaryrefslogtreecommitdiffstats
path: root/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp')
-rw-r--r--tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp635
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"