summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/widgets/widgets/qabstractspinbox.cpp15
-rw-r--r--src/widgets/widgets/qabstractspinbox.h5
-rw-r--r--src/widgets/widgets/qabstractspinbox_p.h3
-rw-r--r--src/widgets/widgets/qspinbox.cpp116
-rw-r--r--src/widgets/widgets/qspinbox.h8
-rw-r--r--tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp131
-rw-r--r--tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp81
7 files changed, 358 insertions, 1 deletions
diff --git a/src/widgets/widgets/qabstractspinbox.cpp b/src/widgets/widgets/qabstractspinbox.cpp
index 8dbae25dce..c5ccfac109 100644
--- a/src/widgets/widgets/qabstractspinbox.cpp
+++ b/src/widgets/widgets/qabstractspinbox.cpp
@@ -639,7 +639,15 @@ void QAbstractSpinBox::stepBy(int steps)
e = AlwaysEmit;
}
if (!dontstep) {
- d->setValue(d->bound(d->value + (d->singleStep * steps), old, steps), e);
+ QVariant singleStep;
+ switch (d->stepType) {
+ case QAbstractSpinBox::StepType::AdaptiveDecimalStepType:
+ singleStep = d->calculateAdaptiveDecimalStep(steps);
+ break;
+ default:
+ singleStep = d->singleStep;
+ }
+ d->setValue(d->bound(d->value + (singleStep * steps), old, steps), e);
} else if (e == AlwaysEmit) {
d->emitSignals(e, old);
}
@@ -1898,6 +1906,11 @@ void QAbstractSpinBoxPrivate::clearCache() const
cachedState = QValidator::Acceptable;
}
+QVariant QAbstractSpinBoxPrivate::calculateAdaptiveDecimalStep(int steps) const
+{
+ Q_UNUSED(steps)
+ return singleStep;
+}
// --- QSpinBoxValidator ---
diff --git a/src/widgets/widgets/qabstractspinbox.h b/src/widgets/widgets/qabstractspinbox.h
index 83bf83d779..552c51fc9d 100644
--- a/src/widgets/widgets/qabstractspinbox.h
+++ b/src/widgets/widgets/qabstractspinbox.h
@@ -127,6 +127,11 @@ public:
virtual void fixup(QString &input) const;
virtual void stepBy(int steps);
+
+ enum StepType {
+ DefaultStepType,
+ AdaptiveDecimalStepType
+ };
public Q_SLOTS:
void stepUp();
void stepDown();
diff --git a/src/widgets/widgets/qabstractspinbox_p.h b/src/widgets/widgets/qabstractspinbox_p.h
index 8f312fa900..b8bc088160 100644
--- a/src/widgets/widgets/qabstractspinbox_p.h
+++ b/src/widgets/widgets/qabstractspinbox_p.h
@@ -122,6 +122,8 @@ public:
static int variantCompare(const QVariant &arg1, const QVariant &arg2);
static QVariant variantBound(const QVariant &min, const QVariant &value, const QVariant &max);
+ virtual QVariant calculateAdaptiveDecimalStep(int steps) const;
+
QLineEdit *edit;
QString prefix, suffix, specialValueText;
QVariant value, minimum, maximum, singleStep;
@@ -143,6 +145,7 @@ public:
uint cleared : 1;
uint ignoreUpdateEdit : 1;
QAbstractSpinBox::CorrectionMode correctionMode;
+ QAbstractSpinBox::StepType stepType = QAbstractSpinBox::StepType::DefaultStepType;
int acceleration;
QStyle::SubControl hoverControl;
QRect hoverRect;
diff --git a/src/widgets/widgets/qspinbox.cpp b/src/widgets/widgets/qspinbox.cpp
index 561215ec85..7f29c0c52c 100644
--- a/src/widgets/widgets/qspinbox.cpp
+++ b/src/widgets/widgets/qspinbox.cpp
@@ -45,6 +45,8 @@
#include <qvalidator.h>
#include <qdebug.h>
+#include <algorithm>
+#include <cmath>
#include <float.h>
QT_BEGIN_NAMESPACE
@@ -75,6 +77,8 @@ public:
}
int displayIntegerBase;
+
+ QVariant calculateAdaptiveDecimalStep(int steps) const override;
};
class QDoubleSpinBoxPrivate : public QAbstractSpinBoxPrivate
@@ -100,6 +104,8 @@ public:
// When fiddling with the decimals property, we may lose precision in these properties.
double actualMin;
double actualMax;
+
+ QVariant calculateAdaptiveDecimalStep(int steps) const override;
};
@@ -415,6 +421,41 @@ void QSpinBox::setRange(int minimum, int maximum)
}
/*!
+ Sets the step type for the spin box: single step or adaptive
+ decimal step.
+
+ Adaptive decimal step means that the step size will continuously be
+ adjusted to one power of ten below the current \l value. So when
+ the value is 1100, the step is set to 100, so stepping up once
+ increases it to 1200. For 1200 stepping up takes it to 1300. For
+ negative values, stepping down from -1100 goes to -1200.
+
+ Step direction is taken into account to handle edges cases, so
+ that stepping down from 100 takes the value to 99 instead of 90.
+ Thus a step up followed by a step down -- or vice versa -- always
+ lands on the starting value; 99 -> 100 -> 99.
+
+ Setting this will cause the spin box to disregard the value of
+ \l singleStep, although it is preserved so that \l singleStep
+ comes into effect if adaptive decimal step is later turned off.
+
+ \sa QAbstractSpinBox::groupSeparator()
+ \since 5.12
+*/
+
+void QSpinBox::setStepType(QAbstractSpinBox::StepType stepType)
+{
+ Q_D(QSpinBox);
+ d->stepType = stepType;
+}
+
+QAbstractSpinBox::StepType QSpinBox::stepType() const
+{
+ Q_D(const QSpinBox);
+ return d->stepType;
+}
+
+/*!
\property QSpinBox::displayIntegerBase
\brief the base used to display the value of the spin box
@@ -847,6 +888,44 @@ void QDoubleSpinBox::setRange(double minimum, double maximum)
}
/*!
+ Sets the step type for the spin box: single step or adaptive
+ decimal step.
+
+ Adaptive decimal step means that the step size will continuously be
+ adjusted to one power of ten below the current \l value. So when
+ the value is 1100, the step is set to 100, so stepping up once
+ increases it to 1200. For 1200 stepping up takes it to 1300. For
+ negative values, stepping down from -1100 goes to -1200.
+
+ It also works for any decimal values, 0.041 is increased to 0.042
+ by stepping once.
+
+ Step direction is taken into account to handle edges cases, so
+ that stepping down from 100 takes the value to 99 instead of 90.
+ Thus a step up followed by a step down -- or vice versa -- always
+ lands on the starting value; 99 -> 100 -> 99.
+
+ Setting this will cause the spin box to disregard the value of
+ \l singleStep, although it is preserved so that \l singleStep
+ comes into effect if adaptive decimal step is later turned off.
+
+ \sa QAbstractSpinBox::groupSeparator()
+ \since 5.12
+*/
+
+void QDoubleSpinBox::setStepType(StepType stepType)
+{
+ Q_D(QDoubleSpinBox);
+ d->stepType = stepType;
+}
+
+QAbstractSpinBox::StepType QDoubleSpinBox::stepType() const
+{
+ Q_D(const QDoubleSpinBox);
+ return d->stepType;
+}
+
+/*!
\property QDoubleSpinBox::decimals
\brief the precision of the spin box, in decimals
@@ -1078,6 +1157,22 @@ QVariant QSpinBoxPrivate::validateAndInterpret(QString &input, int &pos,
return cachedValue;
}
+QVariant QSpinBoxPrivate::calculateAdaptiveDecimalStep(int steps) const
+{
+ const int intValue = value.toInt();
+ const int absValue = qAbs(intValue);
+
+ if (absValue < 100)
+ return 1;
+
+ const bool valueNegative = intValue < 0;
+ const bool stepsNegative = steps < 0;
+ const int signCompensation = (valueNegative == stepsNegative) ? 0 : 1;
+
+ const int log = static_cast<int>(std::log10(absValue - signCompensation)) - 1;
+ return static_cast<int>(std::pow(10, log));
+}
+
// --- QDoubleSpinBoxPrivate ---
/*!
@@ -1303,6 +1398,27 @@ QString QDoubleSpinBoxPrivate::textFromValue(const QVariant &f) const
return q->textFromValue(f.toDouble());
}
+QVariant QDoubleSpinBoxPrivate::calculateAdaptiveDecimalStep(int steps) const
+{
+ const double doubleValue = value.toDouble();
+ const double minStep = std::pow(10, -decimals);
+ double absValue = qAbs(doubleValue);
+
+ if (absValue < minStep)
+ return minStep;
+
+ const bool valueNegative = doubleValue < 0;
+ const bool stepsNegative = steps < 0;
+ if (valueNegative != stepsNegative)
+ absValue /= 1.01;
+
+ const double shift = std::pow(10, 1 - std::floor(std::log10(absValue)));
+ const double absRounded = round(absValue * shift) / shift;
+ const double log = floorf(std::log10(absRounded)) - 1;
+
+ return std::max(minStep, std::pow(10, log));
+}
+
/*! \reimp */
bool QSpinBox::event(QEvent *event)
{
diff --git a/src/widgets/widgets/qspinbox.h b/src/widgets/widgets/qspinbox.h
index 73489c9a68..d2eac903fb 100644
--- a/src/widgets/widgets/qspinbox.h
+++ b/src/widgets/widgets/qspinbox.h
@@ -58,6 +58,7 @@ class Q_WIDGETS_EXPORT QSpinBox : public QAbstractSpinBox
Q_PROPERTY(int minimum READ minimum WRITE setMinimum)
Q_PROPERTY(int maximum READ maximum WRITE setMaximum)
Q_PROPERTY(int singleStep READ singleStep WRITE setSingleStep)
+ Q_PROPERTY(StepType stepType READ stepType WRITE setStepType)
Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged USER true)
Q_PROPERTY(int displayIntegerBase READ displayIntegerBase WRITE setDisplayIntegerBase)
@@ -86,6 +87,9 @@ public:
void setRange(int min, int max);
+ StepType stepType() const;
+ void setStepType(StepType stepType);
+
int displayIntegerBase() const;
void setDisplayIntegerBase(int base);
@@ -121,6 +125,7 @@ class Q_WIDGETS_EXPORT QDoubleSpinBox : public QAbstractSpinBox
Q_PROPERTY(double minimum READ minimum WRITE setMinimum)
Q_PROPERTY(double maximum READ maximum WRITE setMaximum)
Q_PROPERTY(double singleStep READ singleStep WRITE setSingleStep)
+ Q_PROPERTY(StepType stepType READ stepType WRITE setStepType)
Q_PROPERTY(double value READ value WRITE setValue NOTIFY valueChanged USER true)
public:
explicit QDoubleSpinBox(QWidget *parent = nullptr);
@@ -147,6 +152,9 @@ public:
void setRange(double min, double max);
+ StepType stepType() const;
+ void setStepType(StepType stepType);
+
int decimals() const;
void setDecimals(int prec);
diff --git a/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp b/tests/auto/widgets/widgets/qdoublespinbox/tst_qdoublespinbox.cpp
index 59ddd54194..5382d5c6db 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>
@@ -135,6 +136,8 @@ private slots:
void setGroupSeparatorShown_data();
void setGroupSeparatorShown();
+ void adaptiveDecimalStep();
+
public slots:
void valueChangedHelper(const QString &);
void valueChangedHelper(double);
@@ -1139,5 +1142,133 @@ 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);
+ }
+}
+
QTEST_MAIN(tst_QDoubleSpinBox)
#include "tst_qdoublespinbox.moc"
diff --git a/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp b/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp
index 07a2fd859d..5ee9160916 100644
--- a/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp
+++ b/tests/auto/widgets/widgets/qspinbox/tst_qspinbox.cpp
@@ -145,6 +145,8 @@ private slots:
void wheelEvents();
+ void adaptiveDecimalStep();
+
public slots:
void valueChangedHelper(const QString &);
void valueChangedHelper(int);
@@ -1240,5 +1242,84 @@ void tst_QSpinBox::wheelEvents()
#endif
}
+void tst_QSpinBox::adaptiveDecimalStep()
+{
+ SpinBox spinBox;
+ spinBox.setRange(-100000, 100000);
+ spinBox.setStepType(SpinBox::StepType::AdaptiveDecimalStepType);
+
+ // Positive 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 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);
+ }
+}
+
QTEST_MAIN(tst_QSpinBox)
#include "tst_qspinbox.moc"