diff options
-rw-r--r-- | src/gui/platform/wasm/qlocalfileapi.cpp | 108 | ||||
-rw-r--r-- | src/gui/platform/wasm/qlocalfileapi_p.h | 22 | ||||
-rw-r--r-- | src/gui/platform/wasm/qwasmlocalfileaccess.cpp | 2 | ||||
-rw-r--r-- | tests/auto/wasm/tst_localfileapi.cpp | 113 |
4 files changed, 157 insertions, 88 deletions
diff --git a/src/gui/platform/wasm/qlocalfileapi.cpp b/src/gui/platform/wasm/qlocalfileapi.cpp index b56490eba4..bfcb5a5866 100644 --- a/src/gui/platform/wasm/qlocalfileapi.cpp +++ b/src/gui/platform/wasm/qlocalfileapi.cpp @@ -8,6 +8,33 @@ QT_BEGIN_NAMESPACE namespace LocalFileApi { namespace { +std::string qtFilterListToFileInputAccept(const QStringList &filterList) +{ + QStringList transformed; + for (const auto &filter : filterList) { + + emscripten::val::global("console").call<void>("log", filter.toStdString()); + + const auto type = Type::fromQt(filter); + if (type && type->accept()) { + const auto &extensions = type->accept()->mimeType().extensions(); + for (const auto &ext : extensions) { + emscripten::val::global("console").call<void>("log", + ext.value().toString().toStdString()); + } + + std::transform(extensions.begin(), extensions.end(), std::back_inserter(transformed), + [](const Type::Accept::MimeType::Extension &extension) { + return extension.value().toString(); + }); + } + } + for (const QString &tran : transformed) { + emscripten::val::global("console").call<void>("log", tran.toStdString()); + } + return transformed.join(QStringLiteral(",")).toStdString(); +} + std::optional<emscripten::val> qtFilterListToTypes(const QStringList &filterList) { using namespace qstdweb; @@ -17,20 +44,38 @@ std::optional<emscripten::val> qtFilterListToTypes(const QStringList &filterList for (const auto &fileFilter : filterList) { auto type = Type::fromQt(fileFilter); - if (type) - types.call<void>("push", type->asVal()); + if (type) { + auto jsType = val::object(); + jsType.set("description", type->description().toString().toStdString()); + if (type->accept()) { + jsType.set("accept", ([&mimeType = type->accept()->mimeType()]() { + val acceptDict = val::object(); + + QList<emscripten::val> extensions; + extensions.reserve(mimeType.extensions().size()); + std::transform( + mimeType.extensions().begin(), mimeType.extensions().end(), + std::back_inserter(extensions), + [](const Type::Accept::MimeType::Extension &extension) { + return val(extension.value().toString().toStdString()); + }); + acceptDict.set("application/octet-stream", + emscripten::val::array(extensions.begin(), + extensions.end())); + return acceptDict; + })()); + } + types.call<void>("push", std::move(jsType)); + } } return types["length"].as<int>() == 0 ? std::optional<emscripten::val>() : types; } -} +} // namespace Type::Type(QStringView description, std::optional<Accept> accept) - : m_storage(emscripten::val::object()) + : m_description(description.trimmed()), m_accept(std::move(accept)) { - m_storage.set("description", description.trimmed().toString().toStdString()); - if (accept) - m_storage.set("accept", accept->asVal()); } Type::~Type() = default; @@ -69,12 +114,7 @@ std::optional<Type> Type::fromQt(QStringView type) return Type(description, std::move(*accept)); } -emscripten::val Type::asVal() const -{ - return m_storage; -} - -Type::Accept::Accept() : m_storage(emscripten::val::object()) { } +Type::Accept::Accept() = default; Type::Accept::~Accept() = default; @@ -100,39 +140,25 @@ std::optional<Type::Accept> Type::Accept::fromQt(QStringView qtRepresentation) internalMatch = internalRegex.matchView(qtRepresentation, internalMatch.capturedEnd()); } - accept.addMimeType(mimeType); + accept.setMimeType(mimeType); return accept; } -void Type::Accept::addMimeType(MimeType mimeType) -{ - // The mime type provided here does not seem to have any effect at the result at all. - m_storage.set("application/octet-stream", mimeType.asVal()); -} - -emscripten::val Type::Accept::asVal() const +void Type::Accept::setMimeType(MimeType mimeType) { - return m_storage; + m_mimeType = std::move(mimeType); } -Type::Accept::MimeType::MimeType() : m_storage(emscripten::val::array()) { } +Type::Accept::MimeType::MimeType() = default; Type::Accept::MimeType::~MimeType() = default; void Type::Accept::MimeType::addExtension(Extension extension) { - m_storage.call<void>("push", extension.asVal()); + m_extensions.push_back(std::move(extension)); } -emscripten::val Type::Accept::MimeType::asVal() const -{ - return m_storage; -} - -Type::Accept::MimeType::Extension::Extension(QStringView extension) - : m_storage(extension.toString().toStdString()) -{ -} +Type::Accept::MimeType::Extension::Extension(QStringView extension) : m_value(extension) { } Type::Accept::MimeType::Extension::~Extension() = default; @@ -161,15 +187,10 @@ Type::Accept::MimeType::Extension::fromQt(QStringView qtRepresentation) return std::nullopt; } -emscripten::val Type::Accept::MimeType::Extension::asVal() const -{ - return m_storage; -} - emscripten::val makeOpenFileOptions(const QStringList &filterList, bool acceptMultiple) { auto options = emscripten::val::object(); - if (auto typeList = LocalFileApi::qtFilterListToTypes(filterList)) { + if (auto typeList = qtFilterListToTypes(filterList)) { options.set("types", std::move(*typeList)); options.set("excludeAcceptAllOption", true); } @@ -186,12 +207,17 @@ emscripten::val makeSaveFileOptions(const QStringList &filterList, const std::st if (!suggestedName.empty()) options.set("suggestedName", emscripten::val(suggestedName)); - if (auto typeList = LocalFileApi::qtFilterListToTypes(filterList)) + if (auto typeList = qtFilterListToTypes(filterList)) options.set("types", emscripten::val(std::move(*typeList))); return options; } -} // namespace LocalFileApi +std::string makeFileInputAccept(const QStringList &filterList) +{ + return qtFilterListToFileInputAccept(filterList); +} + +} // namespace LocalFileApi QT_END_NAMESPACE diff --git a/src/gui/platform/wasm/qlocalfileapi_p.h b/src/gui/platform/wasm/qlocalfileapi_p.h index a8e7f666f9..b8fa6adb99 100644 --- a/src/gui/platform/wasm/qlocalfileapi_p.h +++ b/src/gui/platform/wasm/qlocalfileapi_p.h @@ -36,12 +36,12 @@ public: ~Extension(); - emscripten::val asVal() const; + const QStringView &value() const { return m_value; } private: explicit Extension(QStringView extension); - emscripten::val m_storage; + QStringView m_value; }; MimeType(); @@ -49,38 +49,42 @@ public: void addExtension(Extension type); - emscripten::val asVal() const; + const std::vector<Extension> &extensions() const { return m_extensions; } private: - emscripten::val m_storage; + std::vector<Extension> m_extensions; }; static std::optional<Accept> fromQt(QStringView type); ~Accept(); - void addMimeType(MimeType mimeType); + void setMimeType(MimeType mimeType); - emscripten::val asVal() const; + const MimeType &mimeType() const { return m_mimeType; } private: Accept(); - emscripten::val m_storage; + MimeType m_mimeType; }; Type(QStringView description, std::optional<Accept> accept); ~Type(); static std::optional<Type> fromQt(QStringView type); - emscripten::val asVal() const; + const QStringView &description() const { return m_description; } + const std::optional<Accept> &accept() const { return m_accept; } private: - emscripten::val m_storage; + QStringView m_description; + std::optional<Accept> m_accept; }; Q_CORE_EXPORT emscripten::val makeOpenFileOptions(const QStringList &filterList, bool acceptMultiple); Q_CORE_EXPORT emscripten::val makeSaveFileOptions(const QStringList &filterList, const std::string& suggestedName); +Q_AUTOTEST_EXPORT std::string makeFileInputAccept(const QStringList &filterList); + } // namespace LocalFileApi QT_END_NAMESPACE diff --git a/src/gui/platform/wasm/qwasmlocalfileaccess.cpp b/src/gui/platform/wasm/qwasmlocalfileaccess.cpp index 72f1dcc339..762d5e1a40 100644 --- a/src/gui/platform/wasm/qwasmlocalfileaccess.cpp +++ b/src/gui/platform/wasm/qwasmlocalfileaccess.cpp @@ -31,7 +31,7 @@ void showOpenViaHTMLPolyfill(const QStringList &accept, FileSelectMode fileSelec emscripten::val input = document.call<emscripten::val>("createElement", std::string("input")); input.set("type", "file"); input.set("style", "display:none"); - // input.set("accept", emscripten::val(accept)); + input.set("accept", LocalFileApi::makeFileInputAccept(accept)); Q_UNUSED(accept); input.set("multiple", emscripten::val(fileSelectMode == FileSelectMode::MultipleFiles)); diff --git a/tests/auto/wasm/tst_localfileapi.cpp b/tests/auto/wasm/tst_localfileapi.cpp index 0d17202d4b..95bdbcffb2 100644 --- a/tests/auto/wasm/tst_localfileapi.cpp +++ b/tests/auto/wasm/tst_localfileapi.cpp @@ -11,14 +11,16 @@ class tst_LocalFileApi : public QObject Q_OBJECT private: - emscripten::val makeAccept(std::vector<emscripten::val> types) { + emscripten::val makeAccept(std::vector<emscripten::val> types) + { auto accept = emscripten::val::object(); accept.set("application/octet-stream", emscripten::val::array(std::move(types))); return accept; } - emscripten::val makeType(QString description, std::vector<emscripten::val> acceptExtensions) { + emscripten::val makeType(QString description, std::vector<emscripten::val> acceptExtensions) + { using namespace emscripten; auto type = val::object(); @@ -65,6 +67,8 @@ private Q_SLOTS: void openFileOptions(); void saveFileOptions_data(); void saveFileOptions(); + void fileInputAccept_data(); + void fileInputAccept(); }; bool valDeepEquals(emscripten::val lhs, emscripten::val rhs) @@ -79,24 +83,27 @@ bool valDeepEquals(emscripten::val lhs, emscripten::val rhs) void tst_LocalFileApi::fileExtensionFilterTransformation_data() { QTest::addColumn<QString>("qtFileFilter"); - QTest::addColumn<std::optional<std::string>>("expectedWebExtensionFilter"); - - QTest::newRow("PNG extension with an asterisk") << QString("*.png") << std::make_optional<std::string>(".png"); - QTest::newRow("Long extension with an asterisk") << QString("*.someotherfile") << std::make_optional<std::string>(".someotherfile"); - QTest::newRow(".dat with no asterisk") << QString(".dat") << std::make_optional<std::string>(".dat"); - QTest::newRow("Multiple asterisks") << QString("*ot*.abc") << std::optional<std::string>(); - QTest::newRow("Filename") << QString("abcd.abc") << std::optional<std::string>(); - QTest::newRow("match all") << QString("*.*") << std::optional<std::string>(); + QTest::addColumn<std::optional<QString>>("expectedWebExtensionFilter"); + + QTest::newRow("PNG extension with an asterisk") + << QString("*.png") << std::make_optional<QString>(".png"); + QTest::newRow("Long extension with an asterisk") + << QString("*.someotherfile") << std::make_optional<QString>(".someotherfile"); + QTest::newRow(".dat with no asterisk") + << QString(".dat") << std::make_optional<QString>(".dat"); + QTest::newRow("Multiple asterisks") << QString("*ot*.abc") << std::optional<QString>(); + QTest::newRow("Filename") << QString("abcd.abc") << std::optional<QString>(); + QTest::newRow("match all") << QString("*.*") << std::optional<QString>(); } void tst_LocalFileApi::fileExtensionFilterTransformation() { QFETCH(QString, qtFileFilter); - QFETCH(std::optional<std::string>, expectedWebExtensionFilter); + QFETCH(std::optional<QString>, expectedWebExtensionFilter); auto result = LocalFileApi::Type::Accept::MimeType::Extension::fromQt(qtFileFilter); if (expectedWebExtensionFilter) { - QCOMPARE_EQ(expectedWebExtensionFilter, result->asVal().as<std::string>()); + QCOMPARE_EQ(expectedWebExtensionFilter, result->value()); } else { QVERIFY(!result.has_value()); } @@ -107,34 +114,37 @@ void tst_LocalFileApi::acceptTransformation_data() using namespace emscripten; QTest::addColumn<QString>("qtFilterList"); - QTest::addColumn<emscripten::val>("expectedWebType"); + QTest::addColumn<QStringList>("expectedExtensionList"); - QTest::newRow("Multiple types") << QString("*.png *.other *.txt") - << makeAccept(std::vector<val> { val(".png"), val(".other"), val(".txt") }); + QTest::newRow("Multiple types") + << QString("*.png *.other *.txt") << QStringList{ ".png", ".other", ".txt" }; - QTest::newRow("Single type") << QString("*.png") - << makeAccept(std::vector<val> { val(".png") }); + QTest::newRow("Single type") << QString("*.png") << QStringList{ ".png" }; - QTest::newRow("No filter when accepts all") << QString("*.*") - << val::undefined(); + QTest::newRow("No filter when accepts all") << QString("*.*") << QStringList(); - QTest::newRow("No filter when one filter accepts all") << QString("*.* *.jpg") - << val::undefined(); + QTest::newRow("No filter when one filter accepts all") << QString("*.* *.jpg") << QStringList(); QTest::newRow("Weird spaces") << QString(" *.jpg *.png *.icon ") - << makeAccept(std::vector<val> { val(".jpg"), val(".png"), val(".icon") }); + << QStringList{ ".jpg", ".png", ".icon" }; } void tst_LocalFileApi::acceptTransformation() { QFETCH(QString, qtFilterList); - QFETCH(emscripten::val, expectedWebType); + QFETCH(QStringList, expectedExtensionList); auto result = LocalFileApi::Type::Accept::fromQt(qtFilterList); - if (!expectedWebType.isUndefined()) { - QVERIFY(valDeepEquals(result->asVal(), expectedWebType)); - } else { + if (expectedExtensionList.isEmpty()) { QVERIFY(!result.has_value()); + } else { + QStringList transformed; + std::transform(result->mimeType().extensions().begin(), + result->mimeType().extensions().end(), std::back_inserter(transformed), + [](const LocalFileApi::Type::Accept::MimeType::Extension &extension) { + return extension.value().toString(); + }); + QCOMPARE_EQ(expectedExtensionList, transformed); } } @@ -143,26 +153,31 @@ void tst_LocalFileApi::typeTransformation_data() using namespace emscripten; QTest::addColumn<QString>("qtFilterList"); - QTest::addColumn<emscripten::val>("expectedWebType"); + QTest::addColumn<QString>("expectedDescription"); + QTest::addColumn<QStringList>("expectedExtensions"); - QTest::newRow("With description") << QString("Text files (*.txt)") - << makeType("Text files", std::vector<val> { val(".txt") }); + QTest::newRow("With description") + << QString("Text files (*.txt)") << QString("Text files") << QStringList{ ".txt" }; - QTest::newRow("No description") << QString("*.jpg") - << makeType("", std::vector<val> { val(".jpg") }); + QTest::newRow("No description") << QString("*.jpg") << QString("") << QStringList{ ".jpg" }; } void tst_LocalFileApi::typeTransformation() { QFETCH(QString, qtFilterList); - QFETCH(emscripten::val, expectedWebType); + QFETCH(QString, expectedDescription); + QFETCH(QStringList, expectedExtensions); auto result = LocalFileApi::Type::fromQt(qtFilterList); - if (!expectedWebType.isUndefined()) { - QVERIFY(valDeepEquals(result->asVal(), expectedWebType)); - } else { - QVERIFY(!result.has_value()); - } + QCOMPARE_EQ(result->description(), expectedDescription); + + QStringList transformed; + std::transform(result->accept()->mimeType().extensions().begin(), + result->accept()->mimeType().extensions().end(), std::back_inserter(transformed), + [](const LocalFileApi::Type::Accept::MimeType::Extension &extension) { + return extension.value().toString(); + }); + QCOMPARE_EQ(expectedExtensions, transformed); } void tst_LocalFileApi::openFileOptions_data() @@ -217,5 +232,29 @@ void tst_LocalFileApi::saveFileOptions() QVERIFY(valDeepEquals(result, expectedWebType)); } +void tst_LocalFileApi::fileInputAccept_data() +{ + using namespace emscripten; + + QTest::addColumn<QStringList>("qtFilterList"); + QTest::addColumn<QString>("expectedAccept"); + + QTest::newRow("Multiple files") + << QStringList{ "Text files (*.txt)", "Images (*.jpg *.png)", "*.bat" } + << ".txt,.jpg,.png,.bat"; + QTest::newRow("Spaces") << QStringList{ " Documents (*.doc)", "Everything (*.*)", + " Stuff ( *.stf *.tng)", " *.exe" } + << ".doc,.stf,.tng,.exe"; +} + +void tst_LocalFileApi::fileInputAccept() +{ + QFETCH(QStringList, qtFilterList); + QFETCH(QString, expectedAccept); + + auto result = LocalFileApi::makeFileInputAccept(qtFilterList); + QCOMPARE_EQ(expectedAccept, QString::fromStdString(result)); +} + QTEST_APPLESS_MAIN(tst_LocalFileApi) #include "tst_localfileapi.moc" |