wasm: align QWasmLocalFileAccess API with 6.4

This API is undocumented, but by keeping unchanged makes
it easier to use from external projects.

openFile(): Copy the accept/filter list parsing function
from 6.4. It makes sense to implement this behind the
API, so that user code doesn't have to reimplement it.

saveFile(): Slightly more complicated; the new variant which
takes a QByteArray is better since the implementation can
then keep a reference to the data for as long as it needs,
without copying the data. Add the const char * variant to
keep existing code going for now.

Adjust the calling code in widgets.

Change-Id: I1899ebffdb90e40429dcb10313ccc5334f20c34f
Reviewed-by: Mikołaj Boc <Mikolaj.Boc@qt.io>
Reviewed-by: Lorn Potter <lorn.potter@gmail.com>
(cherry picked from commit fbf2a318dd3f1e0f9fc8dadb68d47923dff3d672)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Morten Sørvig 2022-12-16 15:22:32 +01:00 committed by Qt Cherry-pick Bot
parent 747a4de311
commit b444b792ad
3 changed files with 87 additions and 9 deletions

View File

@ -9,6 +9,8 @@
#include <emscripten/html5.h>
#include <emscripten/val.h>
#include <QtCore/qregularexpression.h>
QT_BEGIN_NAMESPACE
namespace QWasmLocalFileAccess {
@ -132,15 +134,76 @@ void readFiles(const qstdweb::FileList &fileList,
(*readFile)(0);
}
QStringList acceptListFromQtFormat(const std::string &qtAcceptList)
{
// copy of qt_make_filter_list() from qfiledialog.cpp
auto make_filter_list = [](const QString &filter) -> QStringList
{
if (filter.isEmpty())
return QStringList();
QString sep(";;");
if (!filter.contains(sep) && filter.contains(u'\n'))
sep = u'\n';
return filter.split(sep);
};
const QStringList fileFilter = make_filter_list(QString::fromStdString(qtAcceptList));
QStringList transformed;
for (const auto &element : fileFilter) {
// Accepts either a string in format:
// GROUP3
// or in this format:
// GROUP1 (GROUP2)
// Group 1 is treated as the description, whereas group 2 or 3 are treated as the filter
// list.
static QRegularExpression regex(
QString(QStringLiteral("(?:([^(]*)\\(([^()]+)\\)[^)]*)|([^()]+)")));
static QRegularExpression wordCharacterRegex(QString(QStringLiteral("\\w")));
const auto match = regex.match(element);
if (!match.hasMatch())
continue;
constexpr size_t FilterListFromParensIndex = 2;
constexpr size_t PlainFilterListIndex = 3;
QString filterList = match.captured(match.hasCaptured(FilterListFromParensIndex)
? FilterListFromParensIndex
: PlainFilterListIndex);
for (auto singleExtension : filterList.split(QStringLiteral(" "), Qt::SkipEmptyParts)) {
// Checks for a filter that matches everything:
// Any number of asterisks or any number of asterisks with a '.' between them.
// The web filter does not support wildcards.
static QRegularExpression qtAcceptAllRegex(QRegularExpression::anchoredPattern(
QString(QStringLiteral("[*]+|[*]+\\.[*]+"))));
if (qtAcceptAllRegex.match(singleExtension).hasMatch())
continue;
// Checks for correctness. The web filter only allows filename extensions and does not
// filter the actual filenames, therefore we check whether the filter provided only
// filters for the extension.
static QRegularExpression qtFilenameMatcherRegex(QRegularExpression::anchoredPattern(
QString(QStringLiteral("(\\*?)(\\.[^*]+)"))));
auto extensionMatch = qtFilenameMatcherRegex.match(singleExtension);
if (extensionMatch.hasMatch())
transformed.append(extensionMatch.captured(2));
}
}
return transformed;
}
void downloadDataAsFile(const QByteArray &data, const std::string &fileNameHint)
}
void downloadDataAsFile(const char *content, size_t size, const std::string &fileNameHint)
{
// Save a file by creating programmatically clicking a download
// link to an object url to a Blob containing a copy of the file
// content. The copy is made so that the passed in content buffer
// can be released as soon as this function returns.
qstdweb::Blob contentBlob = qstdweb::Blob::copyFrom(data.constData(), data.size());
qstdweb::Blob contentBlob = qstdweb::Blob::copyFrom(content, size);
emscripten::val document = emscripten::val::global("document");
emscripten::val window = qstdweb::window();
emscripten::val contentUrl = window["URL"].call<emscripten::val>("createObjectURL", contentBlob.val());
@ -157,12 +220,12 @@ void downloadDataAsFile(const QByteArray &data, const std::string &fileNameHint)
window["URL"].call<emscripten::val>("revokeObjectURL", contentUrl);
}
void openFiles(const QStringList &accept, FileSelectMode fileSelectMode,
void openFiles(const std::string &accept, FileSelectMode fileSelectMode,
const std::function<void (int fileCount)> &fileDialogClosed,
const std::function<char *(uint64_t size, const std::string& name)> &acceptFile,
const std::function<void()> &fileDataReady)
{
FileDialog::showOpen(accept, fileSelectMode, {
FileDialog::showOpen(acceptListFromQtFormat(accept), fileSelectMode, {
.thenFunc = [=](emscripten::val result) {
auto files = qstdweb::FileList(result);
fileDialogClosed(files.length());
@ -174,7 +237,7 @@ void openFiles(const QStringList &accept, FileSelectMode fileSelectMode,
});
}
void openFile(const QStringList &accept,
void openFile(const std::string &accept,
const std::function<void (bool fileSelected)> &fileDialogClosed,
const std::function<char *(uint64_t size, const std::string& name)> &acceptFile,
const std::function<void()> &fileDataReady)
@ -220,7 +283,7 @@ void saveDataToFileInChunks(emscripten::val fileHandle, const QByteArray &data)
void saveFile(const QByteArray &data, const std::string &fileNameHint)
{
if (!FileDialog::canShowSave()) {
downloadDataAsFile(data, fileNameHint);
downloadDataAsFile(data.constData(), data.size(), fileNameHint);
return;
}
@ -231,6 +294,20 @@ void saveFile(const QByteArray &data, const std::string &fileNameHint)
});
}
void saveFile(const char *content, size_t size, const std::string &fileNameHint)
{
if (!FileDialog::canShowSave()) {
downloadDataAsFile(content, size, fileNameHint);
return;
}
FileDialog::showSave(fileNameHint, {
.thenFunc = [=](emscripten::val result) {
saveDataToFileInChunks(result, QByteArray(content, size));
},
});
}
} // namespace QWasmLocalFileAccess
QT_END_NAMESPACE

View File

@ -25,17 +25,18 @@ namespace QWasmLocalFileAccess {
enum class FileSelectMode { SingleFile, MultipleFiles };
Q_CORE_EXPORT void openFiles(const QStringList &accept, FileSelectMode fileSelectMode,
Q_CORE_EXPORT void openFiles(const std::string &accept, FileSelectMode fileSelectMode,
const std::function<void (int fileCount)> &fileDialogClosed,
const std::function<char *(uint64_t size, const std::string& name)> &acceptFile,
const std::function<void()> &fileDataReady);
Q_CORE_EXPORT void openFile(const QStringList &accept,
Q_CORE_EXPORT void openFile(const std::string &accept,
const std::function<void (bool fileSelected)> &fileDialogClosed,
const std::function<char *(uint64_t size, const std::string& name)> &acceptFile,
const std::function<void()> &fileDataReady);
Q_CORE_EXPORT void saveFile(const QByteArray &data, const std::string &fileNameHint);
Q_CORE_EXPORT void saveFile(const char *content, size_t size, const std::string &fileNameHint);
} // namespace QWasmLocalFileAccess

View File

@ -2296,7 +2296,7 @@ void QFileDialog::getOpenFileContent(const QString &nameFilter, const std::funct
openFileImpl.reset();
};
QWasmLocalFileAccess::openFile(qt_make_filter_list(nameFilter), fileDialogClosed, acceptFile, fileContentReady);
QWasmLocalFileAccess::openFile(nameFilter.toStdString(), fileDialogClosed, acceptFile, fileContentReady);
};
(*openFileImpl)();