summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan Solovev <[email protected]>2021-09-14 11:45:24 +0200
committerIvan Solovev <[email protected]>2021-09-17 09:55:11 +0200
commitfb3549fc47b19de2956cd2dda07ef67ec529cf3e (patch)
tree02beef1e21a4436f55b23f9f388e38f8f1d373a7
parentd47278fd09f73ddc34011ab980dafc23aa453e71 (diff)
Introduce QDoubleValidator::fixup()
The provided implementation tries to fix positions for the group separator. In case of scientific notation it can also converts the value to normalized form. It uses QLocale::FloatingPointShortest internally to convert the double value back to string, so the number of decimals may change after calling this method. Change-Id: I963bc5f97b653e2bb912f4b95b09a4d1ee201e7f Reviewed-by: Edward Welbourne <[email protected]>
-rw-r--r--src/gui/doc/snippets/code/src_gui_util_qvalidator.cpp19
-rw-r--r--src/gui/util/qvalidator.cpp90
-rw-r--r--src/gui/util/qvalidator.h1
-rw-r--r--tests/auto/gui/util/qdoublevalidator/tst_qdoublevalidator.cpp220
4 files changed, 328 insertions, 2 deletions
diff --git a/src/gui/doc/snippets/code/src_gui_util_qvalidator.cpp b/src/gui/doc/snippets/code/src_gui_util_qvalidator.cpp
index 51fc3a6e149..7503c12afb3 100644
--- a/src/gui/doc/snippets/code/src_gui_util_qvalidator.cpp
+++ b/src/gui/doc/snippets/code/src_gui_util_qvalidator.cpp
@@ -57,6 +57,7 @@ struct Wrapper : public QWidget {
void wrapper0();
void wrapper1();
void wrapper2();
+ void wrapper3();
};
void Wrapper::wrapper0() {
@@ -164,4 +165,22 @@ s = "readm"; v.validate(s, pos); // Returns Intermediate
} // Wrapper::wrapper2
+void Wrapper::wrapper3()
+{
+//! [7]
+QString input = "0.98765e2";
+QDoubleValidator val;
+val.setLocale(QLocale::C);
+val.setNotation(QDoubleValidator::ScientificNotation);
+val.fixup(input); // input == "9.8765e+01"
+//! [7]
+//! [8]
+input = "-1234.6789";
+val.setDecimals(2);
+val.setLocale(QLocale::C);
+val.setNotation(QDoubleValidator::StandardNotation);
+val.fixup(input); // input == "-1234.68"
+//! [8]
+} // Wrapper::wrapper3
+
} // src_gui_util_qvalidator
diff --git a/src/gui/util/qvalidator.cpp b/src/gui/util/qvalidator.cpp
index 6f93738340f..52dfad11a47 100644
--- a/src/gui/util/qvalidator.cpp
+++ b/src/gui/util/qvalidator.cpp
@@ -543,6 +543,8 @@ public:
QDoubleValidator::Notation notation;
QValidator::State validateWithLocale(QString & input, QLocaleData::NumberMode numMode, const QLocale &locale) const;
+ void fixupWithLocale(QString &input, QLocaleData::NumberMode numMode,
+ const QLocale &locale) const;
};
@@ -554,8 +556,7 @@ public:
\inmodule QtGui
QDoubleValidator provides an upper bound, a lower bound, and a
- limit on the number of digits after the decimal point. It does not
- provide a fixup() function.
+ limit on the number of digits after the decimal point.
You can set the acceptable range in one call with setRange(), or
with setBottom() and setTop(). Set the number of decimal places
@@ -712,6 +713,91 @@ QValidator::State QDoubleValidatorPrivate::validateWithLocale(QString &input, QL
}
/*!
+ \since 6.3
+ \overload
+
+ Attempts to fix the \a input string to an \l Acceptable representation of a
+ double.
+
+ The format of the number is determined by \l notation(), \l decimals(),
+ \l locale() and the latter's \l {QLocale::}{numberOptions()}.
+
+ To comply with \l notation(), when \l ScientificNotation is used, the fixed
+ value will be represented in its normalized form, which means that any
+ non-zero value will have one non-zero digit before the decimal point.
+
+ \snippet code/src_gui_util_qvalidator.cpp 7
+
+ To comply with \l decimals(), when it is \c {-1} the number of digits used
+ will be determined by \l QLocale::FloatingPointShortest. Otherwise, the
+ fractional part of the number is truncated (with rounding, as appropriate)
+ if its length exceeds \l decimals(). When \l notation() is
+ \l ScientificNotation this is done after the number has been put into its
+ normalized form.
+
+ \snippet code/src_gui_util_qvalidator.cpp 8
+
+ \note If \l decimals() is set to, and the string provides, more than
+ \c {std::numeric_limits<double>::digits10}, digits beyond that many in the
+ fractional part may be changed. The resulting string shall encode the same
+ floating-point number, when parsed to a \c double.
+*/
+void QDoubleValidator::fixup(QString &input) const
+{
+ Q_D(const QDoubleValidator);
+ const auto numberMode = d->notation == StandardNotation ? QLocaleData::DoubleStandardMode
+ : QLocaleData::DoubleScientificMode;
+
+ d->fixupWithLocale(input, numberMode, locale());
+}
+
+void QDoubleValidatorPrivate::fixupWithLocale(QString &input, QLocaleData::NumberMode numMode,
+ const QLocale &locale) const
+{
+ Q_Q(const QDoubleValidator);
+ QByteArray buff;
+ // Passing -1 as the number of decimals, because fixup() exists to improve
+ // an Intermediate value, if it can.
+ if (!locale.d->m_data->validateChars(input, numMode, &buff, -1, locale.numberOptions()))
+ return;
+
+ // buff now contains data in C locale.
+ bool ok = false;
+ const double entered = buff.toDouble(&ok);
+ if (ok) {
+ // Here we need to adjust the output format accordingly
+ char mode;
+ if (numMode == QLocaleData::DoubleStandardMode) {
+ mode = 'f';
+ } else {
+ // scientific mode can be either 'e' or 'E'
+ mode = input.contains(QChar::fromLatin1('E')) ? 'E' : 'e';
+ }
+ int precision;
+ if (q->dec < 0) {
+ precision = QLocale::FloatingPointShortest;
+ } else {
+ if (mode == 'f') {
+ const auto decimalPointIndex = buff.indexOf('.');
+ precision = decimalPointIndex >= 0 ? buff.size() - decimalPointIndex - 1 : 0;
+ } else {
+ auto eIndex = buff.indexOf('e');
+ // No need to check for 'E' because we can get only 'e' after a
+ // call to validateChars()
+ if (eIndex < 0)
+ eIndex = buff.size();
+ precision = eIndex - (buff.contains('.') ? 1 : 0)
+ - (buff.startsWith('-') || buff.startsWith('+') ? 1 : 0);
+ }
+ // Use q->dec to limit the number of decimals, because we want the
+ // fixup() result to pass validate().
+ precision = qMin(precision, q->dec);
+ }
+ input = locale.toString(entered, mode, precision);
+ }
+}
+
+/*!
Sets the validator to accept doubles from \a minimum to \a maximum
inclusive, with at most \a decimals digits after the decimal
point.
diff --git a/src/gui/util/qvalidator.h b/src/gui/util/qvalidator.h
index 7e72a60f248..be9042d6535 100644
--- a/src/gui/util/qvalidator.h
+++ b/src/gui/util/qvalidator.h
@@ -140,6 +140,7 @@ public:
};
Q_ENUM(Notation)
QValidator::State validate(QString &, int &) const override;
+ void fixup(QString &input) const override;
void setRange(double bottom, double top, int decimals = 0);
void setBottom(double);
diff --git a/tests/auto/gui/util/qdoublevalidator/tst_qdoublevalidator.cpp b/tests/auto/gui/util/qdoublevalidator/tst_qdoublevalidator.cpp
index d1684e9153a..26fea94797d 100644
--- a/tests/auto/gui/util/qdoublevalidator/tst_qdoublevalidator.cpp
+++ b/tests/auto/gui/util/qdoublevalidator/tst_qdoublevalidator.cpp
@@ -45,6 +45,8 @@ private slots:
void validateIntEquiv_data();
void validateIntEquiv();
void notifySignals();
+ void fixup();
+ void fixup_data();
};
Q_DECLARE_METATYPE(QValidator::State);
@@ -393,6 +395,224 @@ void tst_QDoubleValidator::notifySignals()
QCOMPARE(changedSpy.count(), 9);
}
+void tst_QDoubleValidator::fixup()
+{
+ QFETCH(QString, localeName);
+ QFETCH(QDoubleValidator::Notation, notation);
+ QFETCH(int, decimals);
+ QFETCH(QString, input);
+ QFETCH(QString, output);
+
+ QDoubleValidator val;
+ val.setLocale(QLocale(localeName));
+ val.setNotation(notation);
+ val.setDecimals(decimals);
+
+ val.fixup(input);
+ QCOMPARE(input, output);
+}
+
+void tst_QDoubleValidator::fixup_data()
+{
+ QTest::addColumn<QString>("localeName");
+ QTest::addColumn<QDoubleValidator::Notation>("notation");
+ QTest::addColumn<int>("decimals");
+ QTest::addColumn<QString>("input");
+ QTest::addColumn<QString>("output");
+
+ // C locale uses '.' as decimal point and ',' as grouping separator.
+ // C locale does not group digits by default.
+ QTest::newRow("C standard no digit grouping")
+ << "C" << QDoubleValidator::StandardNotation << -1 << "12.345"
+ << "12.345";
+ QTest::newRow("C standard with digit grouping")
+ << "C" << QDoubleValidator::StandardNotation << -1 << "-12,345.678"
+ << "-12345.678";
+ QTest::newRow("C standard with invalid digit grouping")
+ << "C" << QDoubleValidator::StandardNotation << -1 << "1,234,5.678"
+ << "12345.678";
+ QTest::newRow("C standard with invalid number of decimals")
+ << "C" << QDoubleValidator::StandardNotation << 2 << "-12,34.678"
+ << "-1234.68";
+ QTest::newRow("C standard truncate decimals")
+ << "C" << QDoubleValidator::StandardNotation << -1
+ << "1.23456789012345678901234567890"
+ << "1.2345678901234567";
+ QTest::newRow("C standard skip trailing zeroes")
+ << "C" << QDoubleValidator::StandardNotation << -1 << "1,234.5670000"
+ << "1234.567";
+ QTest::newRow("C standard zero value")
+ << "C" << QDoubleValidator::StandardNotation << -1 << "0.0"
+ << "0";
+ QTest::newRow("C standard scientific value")
+ << "C" << QDoubleValidator::StandardNotation << -1 << "1.23e-2"
+ << "1.23e-2";
+ QTest::newRow("C standard no fractional part")
+ << "C" << QDoubleValidator::StandardNotation << -1 << "-1,234"
+ << "-1234";
+
+ QTest::newRow("C scientific no digit grouping")
+ << "C" << QDoubleValidator::ScientificNotation << -1 << "0.98765e2"
+ << "9.8765e+01";
+ QTest::newRow("C scientific with digit grouping")
+ << "C" << QDoubleValidator::ScientificNotation << -1 << "-1,234.98765E-4"
+ << "-1.23498765E-01";
+ QTest::newRow("C scientific with invalid digit grouping")
+ << "C" << QDoubleValidator::ScientificNotation << -1 << "12,34.98765e2"
+ << "1.23498765e+05";
+ QTest::newRow("C scientific with invalid number of decimals")
+ << "C" << QDoubleValidator::ScientificNotation << 2 << "-12,34.98765e2"
+ << "-1.23e+05";
+ QTest::newRow("C scientific truncate decimals")
+ << "C" << QDoubleValidator::ScientificNotation << -1
+ << "1.23456789012345678901234567890E5"
+ << "1.2345678901234567E+05";
+ QTest::newRow("C scientific skip trailing zeroes")
+ << "C" << QDoubleValidator::ScientificNotation << -1 << "1,234.5670000e3"
+ << "1.234567e+06";
+ QTest::newRow("C scientific zero value")
+ << "C" << QDoubleValidator::ScientificNotation << -1 << "0.0"
+ << "0e+00";
+ QTest::newRow("C scientific standard value")
+ << "C" << QDoubleValidator::ScientificNotation << -1 << "12.345"
+ << "1.2345e+01";
+ QTest::newRow("C scientific no fractional part")
+ << "C" << QDoubleValidator::ScientificNotation << -1 << "1,234e2"
+ << "1.234e+05";
+ QTest::newRow("C scientific negative no fractional part")
+ << "C" << QDoubleValidator::ScientificNotation << -1 << "-1,234e2"
+ << "-1.234e+05";
+ QTest::newRow("C scientific no fractional and exponent")
+ << "C" << QDoubleValidator::ScientificNotation << -1 << "1,234"
+ << "1.234e+03";
+ QTest::newRow("C scientific negative no fractional and exponent")
+ << "C" << QDoubleValidator::ScientificNotation << -1 << "-1,234"
+ << "-1.234e+03";
+
+ // en locale uses '.' as decimal point and ',' as grouping separator.
+ // en locale groups digits by default. 'E' is used in scientific notation.
+ QTest::newRow("en standard no digit grouping")
+ << "en" << QDoubleValidator::StandardNotation << -1 << "-12.345"
+ << "-12.345";
+ QTest::newRow("en standard with digit grouping")
+ << "en" << QDoubleValidator::StandardNotation << -1 << "12,345.678"
+ << "12,345.678";
+ QTest::newRow("en standard with invalid digit grouping")
+ << "en" << QDoubleValidator::StandardNotation << -1 << "-1,234,5.678"
+ << "-12,345.678";
+ QTest::newRow("en standard with invalid number of decimals")
+ << "en" << QDoubleValidator::StandardNotation << 2 << "12,34.678"
+ << "1,234.68";
+ QTest::newRow("en standard no fractional part")
+ << "en" << QDoubleValidator::StandardNotation << -1 << "-12,34"
+ << "-1,234";
+
+ QTest::newRow("en scientific no digit grouping")
+ << "en" << QDoubleValidator::ScientificNotation << -1 << "-0.98765e2"
+ << "-9.8765E+01";
+ QTest::newRow("en scientific with digit grouping")
+ << "en" << QDoubleValidator::ScientificNotation << -1 << "1,234.98765E-4"
+ << "1.23498765E-01";
+ QTest::newRow("en scientific with invalid digit grouping")
+ << "en" << QDoubleValidator::ScientificNotation << -1 << "-12,34.98765e2"
+ << "-1.23498765E+05";
+ QTest::newRow("en scientific with invalid number of decimals")
+ << "en" << QDoubleValidator::ScientificNotation << 2 << "12,34.98765e2"
+ << "1.23E+05";
+ QTest::newRow("en scientific no fractional part")
+ << "en" << QDoubleValidator::ScientificNotation << -1 << "12,34e2"
+ << "1.234E+05";
+ QTest::newRow("en scientific negative no fractional part")
+ << "en" << QDoubleValidator::ScientificNotation << -1 << "-12,34e2"
+ << "-1.234E+05";
+ QTest::newRow("en scientific no fractional and exponent")
+ << "en" << QDoubleValidator::ScientificNotation << -1 << "1,234"
+ << "1.234E+03";
+ QTest::newRow("en scientific negative no fractional and exponent")
+ << "en" << QDoubleValidator::ScientificNotation << -1 << "-1,234"
+ << "-1.234E+03";
+
+ // de locale uses ',' as decimal point and '.' as grouping separator.
+ // de locale groups digits by default. 'E' is used in scientific notation.
+ QTest::newRow("de standard no digit grouping")
+ << "de" << QDoubleValidator::StandardNotation << -1 << "12,345"
+ << "12,345";
+ QTest::newRow("de standard with digit grouping")
+ << "de" << QDoubleValidator::StandardNotation << -1 << "-12.345,678"
+ << "-12.345,678";
+ QTest::newRow("de standard with invalid digit grouping")
+ << "de" << QDoubleValidator::StandardNotation << -1 << "1.234.5,678"
+ << "12.345,678";
+ QTest::newRow("de standard with invalid number of decimals")
+ << "de" << QDoubleValidator::StandardNotation << 2 << "-12.34,678"
+ << "-1.234,68";
+ QTest::newRow("de standard no fractional part")
+ << "de" << QDoubleValidator::StandardNotation << -1 << "12.34" << "1.234";
+
+ QTest::newRow("de scientific no digit grouping")
+ << "de" << QDoubleValidator::ScientificNotation << -1 << "0,98765e2"
+ << "9,8765E+01";
+ QTest::newRow("de scientific with digit grouping")
+ << "de" << QDoubleValidator::ScientificNotation << -1 << "-1.234,98765E-4"
+ << "-1,23498765E-01";
+ QTest::newRow("de scientific with invalid digit grouping")
+ << "de" << QDoubleValidator::ScientificNotation << -1 << "12.34,98765e2"
+ << "1,23498765E+05";
+ QTest::newRow("de scientific with invalid number of decimals")
+ << "de" << QDoubleValidator::ScientificNotation << 2 << "-12.34,98765e2"
+ << "-1,23E+05";
+ QTest::newRow("de scientific no fractional part")
+ << "de" << QDoubleValidator::ScientificNotation << -1 << "1.234e2"
+ << "1,234E+05";
+ QTest::newRow("de scientific negative no fractional part")
+ << "de" << QDoubleValidator::ScientificNotation << -1 << "-1.234e2"
+ << "-1,234E+05";
+ QTest::newRow("de scientific no fractional and exponent")
+ << "de" << QDoubleValidator::ScientificNotation << -1 << "12.34"
+ << "1,234E+03";
+ QTest::newRow("de scientific negative no fractional and exponent")
+ << "de" << QDoubleValidator::ScientificNotation << -1 << "-12.34"
+ << "-1,234E+03";
+
+ // hi locale uses '.' as decimal point and ',' as grouping separator.
+ // The rightmost group is of three digits, all the others contain two
+ // digits.
+ QTest::newRow("hi standard no digit grouping")
+ << "hi" << QDoubleValidator::StandardNotation << -1 << "123456.78"
+ << "1,23,456.78";
+ QTest::newRow("hi standard with digit grouping")
+ << "hi" << QDoubleValidator::StandardNotation << -1 << "-12,345.678"
+ << "-12,345.678";
+ QTest::newRow("hi standard with invalid digit grouping")
+ << "hi" << QDoubleValidator::StandardNotation << -1 << "12,34,56.78"
+ << "1,23,456.78";
+ QTest::newRow("hi standard no fractional part")
+ << "hi" << QDoubleValidator::StandardNotation << -1 << "-12,345,6"
+ << "-1,23,456";
+
+ QTest::newRow("hi scientific no digit grouping")
+ << "hi" << QDoubleValidator::ScientificNotation << -1 << "-0.123e-2"
+ << "-1.23E-03";
+ QTest::newRow("hi scientific with digit grouping")
+ << "hi" << QDoubleValidator::ScientificNotation << -1 << "12,345.678e-2"
+ << "1.2345678E+02";
+ QTest::newRow("hi scientific with invalid digit grouping")
+ << "hi" << QDoubleValidator::ScientificNotation << -1 << "-1,23,45.678e-2"
+ << "-1.2345678E+02";
+ QTest::newRow("hi scientific no fractional part")
+ << "hi" << QDoubleValidator::ScientificNotation << -1 << "1,23,456e2"
+ << "1.23456E+07";
+ QTest::newRow("hi scientific negative no fractional part")
+ << "hi" << QDoubleValidator::ScientificNotation << -1 << "-1,23,456e2"
+ << "-1.23456E+07";
+ QTest::newRow("hi scientific no fractional and exponent")
+ << "hi" << QDoubleValidator::ScientificNotation << -1 << "1,234,56"
+ << "1.23456E+05";
+ QTest::newRow("hi scientific negative no fractional and exponent")
+ << "hi" << QDoubleValidator::ScientificNotation << -1 << "-1,234,56"
+ << "-1.23456E+05";
+}
+
void tst_QDoubleValidator::validateIntEquiv_data()
{
QTest::addColumn<double>("minimum");