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:
parent
747a4de311
commit
b444b792ad
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)();
|
||||
|
Loading…
x
Reference in New Issue
Block a user