Expose formattedDataSize() in QML Locale type

This was added to qtbase in 9d23aeb.

Qt Quick Dialogs needs it to display file sizes in FileDialog.

[ChangeLog][QML][Locale] Added formattedDataSize() for formatting
quantities of bytes as kB, MB, GB etc.

Fixes: QTBUG-91283
Change-Id: I8ea64f961c04d4900d18fa45398670df89882c56
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
Mitch Curtis 2021-02-23 12:01:32 +01:00
parent acf3a16800
commit d61ececdb8
4 changed files with 175 additions and 1 deletions

View File

@ -495,6 +495,41 @@ ReturnedValue QQmlLocaleData::method_set_numberOptions(const QV4::FunctionObject
return Encode::undefined();
}
ReturnedValue QQmlLocaleData::method_get_formattedDataSize(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
{
QV4::Scope scope(b);
const QLocale *locale = getThisLocale(scope, thisObject);
if (!locale)
return Encode::undefined();
if (argc < 1 || argc > 3) {
THROW_ERROR(QString::fromLatin1(
"Locale: formattedDataSize(): Expected 1-3 arguments, but received %1").arg(argc).toLatin1());
}
const qint64 bytes = static_cast<qint64>(argv[0].toInteger());
if (argc == 1)
RETURN_RESULT(scope.engine->newString(locale->formattedDataSize(bytes)));
int precision = 0;
if (argc >= 2) {
if (!argv[1].isInteger())
THROW_ERROR("Locale: formattedDataSize(): Invalid argument ('precision' must be an int)");
precision = argv[1].toInt32();
if (argc == 2)
RETURN_RESULT(scope.engine->newString(locale->formattedDataSize(bytes, precision)));
}
// argc >= 3
if (!argv[2].isNumber())
THROW_ERROR("Locale: formattedDataSize(): Invalid argument ('format' must be DataSizeFormat)");
const quint32 intFormat = argv[2].toUInt32();
const auto format = QLocale::DataSizeFormats(intFormat);
RETURN_RESULT(scope.engine->newString(locale->formattedDataSize(bytes, precision, format)));
}
ReturnedValue QQmlLocaleData::method_get_measurementSystem(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
{
QV4::Scope scope(b);
@ -701,6 +736,7 @@ QV4LocaleDataDeletable::QV4LocaleDataDeletable(QV4::ExecutionEngine *engine)
o->defineDefaultProperty(QStringLiteral("monthName"), QQmlLocaleData::method_monthName, 0);
o->defineDefaultProperty(QStringLiteral("currencySymbol"), QQmlLocaleData::method_currencySymbol, 0);
o->defineDefaultProperty(QStringLiteral("dateTimeFormat"), QQmlLocaleData::method_dateTimeFormat, 0);
o->defineDefaultProperty(QStringLiteral("formattedDataSize"), QQmlLocaleData::method_get_formattedDataSize, 0);
o->defineAccessorProperty(QStringLiteral("name"), QQmlLocaleData::method_get_name, nullptr);
o->defineAccessorProperty(QStringLiteral("positiveSign"), QQmlLocaleData::method_get_positiveSign, nullptr);
o->defineAccessorProperty(QStringLiteral("uiLanguages"), QQmlLocaleData::method_get_uiLanguages, nullptr);
@ -950,6 +986,20 @@ ReturnedValue QQmlLocale::method_localeCompare(const QV4::FunctionObject *b, con
\sa Date
*/
/*!
\qmlmethod string QtQml::Locale::formattedDataSize(int bytes, int precision, DataSizeFormat format)
\since 6.2
Converts a size in \a bytes to a human-readable localized string, comprising a
number and a quantified unit.
The \a precision and \a format arguments are optional.
For more information, see \l QLocale::formattedDataSize().
\sa QLocale::DataSizeFormats
*/
/*!
\qmlmethod string QtQml::Locale::monthName(month, type)

View File

@ -139,6 +139,15 @@ namespace QQmlLocale
};
Q_ENUM_NS(NumberOptions)
enum DataSizeFormat {
DataSizeBase1000 = QLocale::DataSizeBase1000,
DataSizeSIQuantifiers = QLocale::DataSizeSIQuantifiers,
DataSizeIecFormat = QLocale::DataSizeIecFormat,
DataSizeTraditionalFormat = QLocale::DataSizeTraditionalFormat,
DataSizeSIFormat = QLocale::DataSizeSIFormat
};
Q_ENUM_NS(DataSizeFormat)
Q_QML_PRIVATE_EXPORT QV4::ReturnedValue locale(QV4::ExecutionEngine *engine, const QString &localeName);
Q_QML_PRIVATE_EXPORT QV4::ReturnedValue wrap(QV4::ExecutionEngine *engine, const QLocale &locale);
Q_QML_PRIVATE_EXPORT void registerStringLocaleCompare(QV4::ExecutionEngine *engine);
@ -205,6 +214,8 @@ struct QQmlLocaleData : public QV4::Object
static QV4::ReturnedValue method_get_numberOptions(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_set_numberOptions(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_get_formattedDataSize(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
};
}

View File

@ -1,8 +1,11 @@
import QtQuick 2.0
import QtQuick
QtObject {
property var locale: Qt.locale()
// TODO: Workaround for not being able to use "Locale" in QQmlExpression (QTBUG-91747).
property var localeType: Locale
function setLocale(l) {
locale = Qt.locale(l)
}

View File

@ -29,6 +29,7 @@
#include <QDebug>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlexpression.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlcontext.h>
#include <QtCore/QDateTime>
@ -76,6 +77,8 @@ private slots:
#if defined(Q_OS_UNIX) && QT_CONFIG(timezone)
void timeZoneUpdated();
#endif
void formattedDataSize_data();
void formattedDataSize();
void dateToLocaleString_data();
void dateToLocaleString();
@ -118,6 +121,7 @@ private:
void addDateTimeFormatData(const QString &l);
void addDateFormatData(const QString &l);
void addTimeFormatData(const QString &l);
void addFormattedDataSizeDataForLocale(const QString &l);
QQmlEngine engine;
};
@ -629,6 +633,112 @@ void tst_qqmllocale::timeFormat()
QCOMPARE(val.toString(), l.timeFormat(format));
}
void tst_qqmllocale::addFormattedDataSizeDataForLocale(const QString &localeStr)
{
const QByteArray localeByteArray = localeStr.toLatin1();
QString functionCallScript;
QString expectedResult;
QString expectedErrorMessage;
const QLocale locale(localeStr);
const auto makeTag = [&](){
QRegularExpression argRegex("formattedDataSize\\((.*)\\)");
QString tag = functionCallScript;
const auto match = argRegex.match(functionCallScript);
if (match.hasMatch())
tag = match.captured(1);
return localeStr + QLatin1String(", ") + tag;
};
functionCallScript = QLatin1String("locale.formattedDataSize(1000000)");
expectedResult = locale.formattedDataSize(1000000);
QTest::newRow(qPrintable(makeTag())) << localeStr << functionCallScript << expectedResult << expectedErrorMessage;
functionCallScript = QLatin1String("locale.formattedDataSize(1000000, 3)");
expectedResult = locale.formattedDataSize(1000000, 3);
QTest::newRow(qPrintable(makeTag())) << localeStr << functionCallScript << expectedResult << expectedErrorMessage;
functionCallScript = QLatin1String("locale.formattedDataSize(1000000, 3, localeType.DataSizeIecFormat)");
expectedResult = locale.formattedDataSize(1000000, 3, QLocale::DataSizeIecFormat);
QTest::newRow(qPrintable(makeTag())) << localeStr << functionCallScript << expectedResult << expectedErrorMessage;
functionCallScript = QLatin1String("locale.formattedDataSize(1000000, 3, localeType.DataSizeTraditionalFormat)");
expectedResult = locale.formattedDataSize(1000000, 3, QLocale::DataSizeTraditionalFormat);
QTest::newRow(qPrintable(makeTag())) << localeStr << functionCallScript << expectedResult << expectedErrorMessage;
functionCallScript = QLatin1String("locale.formattedDataSize(1000000, 3, localeType.DataSizeSIFormat)");
expectedResult = locale.formattedDataSize(1000000, 3, QLocale::DataSizeSIFormat);
QTest::newRow(qPrintable(makeTag())) << localeStr << functionCallScript << expectedResult << expectedErrorMessage;
}
void tst_qqmllocale::formattedDataSize_data()
{
QTest::addColumn<QString>("localeStr");
QTest::addColumn<QString>("functionCallScript");
QTest::addColumn<QString>("expectedResult");
QTest::addColumn<QString>("expectedErrorMessagePattern");
addFormattedDataSizeDataForLocale("en_US");
addFormattedDataSizeDataForLocale("de_DE");
addFormattedDataSizeDataForLocale("ar_SA");
addFormattedDataSizeDataForLocale("hi_IN");
addFormattedDataSizeDataForLocale("zh_CN");
addFormattedDataSizeDataForLocale("th_TH");
// Test error conditions (which aren't locale-specific).
QString functionCallScript = "locale.formattedDataSize()";
QString errorMessage = ".*Locale: formattedDataSize\\(\\): Expected 1-3 arguments, but received 0";
QTest::newRow("too few args") << "en_AU" << functionCallScript << QString() << errorMessage;
functionCallScript = "locale.formattedDataSize(10, 1, localeType.DataSizeIecFormat, \"foo\")";
errorMessage = ".*Locale: formattedDataSize\\(\\): Expected 1-3 arguments, but received 4";
QTest::newRow("too many args") << "en_AU" << functionCallScript << QString() << errorMessage;
functionCallScript = "locale.formattedDataSize(10, \"no\")";
errorMessage = ".*Locale: formattedDataSize\\(\\): Invalid argument \\('precision' must be an int\\)";
QTest::newRow("precision wrong type") << "en_AU" << functionCallScript << QString() << errorMessage;
functionCallScript = "locale.formattedDataSize(10, 1, \"no\")";
errorMessage = ".*Locale: formattedDataSize\\(\\): Invalid argument \\('format' must be DataSizeFormat\\)";
QTest::newRow("format wrong type") << "en_AU" << functionCallScript << QString() << errorMessage;
}
void tst_qqmllocale::formattedDataSize()
{
QFETCH(QString, localeStr);
QFETCH(QString, functionCallScript);
QFETCH(QString, expectedResult);
QFETCH(QString, expectedErrorMessagePattern);
QQmlComponent component(&engine, testFileUrl("functions.qml"));
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
QScopedPointer<QObject> object(component.create());
QVERIFY(object);
QLocale locale(localeStr);
QVariant returnValue;
QVERIFY(QMetaObject::invokeMethod(object.data(), "setLocale", Qt::DirectConnection,
Q_ARG(QVariant, QVariant(localeStr))));
QQmlExpression qmlExpression(engine.rootContext(), object.data(), functionCallScript);
const QVariant evaluationResult = qmlExpression.evaluate();
if (expectedErrorMessagePattern.isEmpty()) {
QVERIFY2(!qmlExpression.hasError(), qPrintable(qmlExpression.error().toString()));
QVERIFY(evaluationResult.canConvert<QString>());
QCOMPARE(evaluationResult.toString(), expectedResult);
} else {
QVERIFY(qmlExpression.hasError());
QRegularExpression errorRegex(expectedErrorMessagePattern);
QVERIFY(errorRegex.isValid());
QVERIFY2(errorRegex.match(qmlExpression.error().toString()).hasMatch(),
qPrintable(QString::fromLatin1("Mismatch in actual vs expected error message:\n Actual: %1\n Expected: %2")
.arg(qmlExpression.error().toString()).arg(expectedErrorMessagePattern)));
}
}
void tst_qqmllocale::dateToLocaleString_data()
{
addStandardFormatData();