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. Pick-to: 6.5 Change-Id: I1899ebffdb90e40429dcb10313ccc5334f20c34f Reviewed-by: Mikołaj Boc <Mikolaj.Boc@qt.io> Reviewed-by: Lorn Potter <lorn.potter@gmail.com>
This commit is contained in:
parent
7b64eb71c0
commit
fbf2a318dd
@ -9,6 +9,8 @@
|
|||||||
#include <emscripten/html5.h>
|
#include <emscripten/html5.h>
|
||||||
#include <emscripten/val.h>
|
#include <emscripten/val.h>
|
||||||
|
|
||||||
|
#include <QtCore/qregularexpression.h>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
namespace QWasmLocalFileAccess {
|
namespace QWasmLocalFileAccess {
|
||||||
@ -132,15 +134,76 @@ void readFiles(const qstdweb::FileList &fileList,
|
|||||||
|
|
||||||
(*readFile)(0);
|
(*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
|
// Save a file by creating programmatically clicking a download
|
||||||
// link to an object url to a Blob containing a copy of the file
|
// 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
|
// content. The copy is made so that the passed in content buffer
|
||||||
// can be released as soon as this function returns.
|
// 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 document = emscripten::val::global("document");
|
||||||
emscripten::val window = qstdweb::window();
|
emscripten::val window = qstdweb::window();
|
||||||
emscripten::val contentUrl = window["URL"].call<emscripten::val>("createObjectURL", contentBlob.val());
|
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);
|
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<void (int fileCount)> &fileDialogClosed,
|
||||||
const std::function<char *(uint64_t size, const std::string& name)> &acceptFile,
|
const std::function<char *(uint64_t size, const std::string& name)> &acceptFile,
|
||||||
const std::function<void()> &fileDataReady)
|
const std::function<void()> &fileDataReady)
|
||||||
{
|
{
|
||||||
FileDialog::showOpen(accept, fileSelectMode, {
|
FileDialog::showOpen(acceptListFromQtFormat(accept), fileSelectMode, {
|
||||||
.thenFunc = [=](emscripten::val result) {
|
.thenFunc = [=](emscripten::val result) {
|
||||||
auto files = qstdweb::FileList(result);
|
auto files = qstdweb::FileList(result);
|
||||||
fileDialogClosed(files.length());
|
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<void (bool fileSelected)> &fileDialogClosed,
|
||||||
const std::function<char *(uint64_t size, const std::string& name)> &acceptFile,
|
const std::function<char *(uint64_t size, const std::string& name)> &acceptFile,
|
||||||
const std::function<void()> &fileDataReady)
|
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)
|
void saveFile(const QByteArray &data, const std::string &fileNameHint)
|
||||||
{
|
{
|
||||||
if (!FileDialog::canShowSave()) {
|
if (!FileDialog::canShowSave()) {
|
||||||
downloadDataAsFile(data, fileNameHint);
|
downloadDataAsFile(data.constData(), data.size(), fileNameHint);
|
||||||
return;
|
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
|
} // namespace QWasmLocalFileAccess
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -25,17 +25,18 @@ namespace QWasmLocalFileAccess {
|
|||||||
|
|
||||||
enum class FileSelectMode { SingleFile, MultipleFiles };
|
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<void (int fileCount)> &fileDialogClosed,
|
||||||
const std::function<char *(uint64_t size, const std::string& name)> &acceptFile,
|
const std::function<char *(uint64_t size, const std::string& name)> &acceptFile,
|
||||||
const std::function<void()> &fileDataReady);
|
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<void (bool fileSelected)> &fileDialogClosed,
|
||||||
const std::function<char *(uint64_t size, const std::string& name)> &acceptFile,
|
const std::function<char *(uint64_t size, const std::string& name)> &acceptFile,
|
||||||
const std::function<void()> &fileDataReady);
|
const std::function<void()> &fileDataReady);
|
||||||
|
|
||||||
Q_CORE_EXPORT void saveFile(const QByteArray &data, const std::string &fileNameHint);
|
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
|
} // namespace QWasmLocalFileAccess
|
||||||
|
|
||||||
|
@ -2296,7 +2296,7 @@ void QFileDialog::getOpenFileContent(const QString &nameFilter, const std::funct
|
|||||||
openFileImpl.reset();
|
openFileImpl.reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
QWasmLocalFileAccess::openFile(qt_make_filter_list(nameFilter), fileDialogClosed, acceptFile, fileContentReady);
|
QWasmLocalFileAccess::openFile(nameFilter.toStdString(), fileDialogClosed, acceptFile, fileContentReady);
|
||||||
};
|
};
|
||||||
|
|
||||||
(*openFileImpl)();
|
(*openFileImpl)();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user