20bool hasLocalFilesApi()
22 return !qstdweb::window()[
"showOpenFilePicker"].isUndefined();
25void showOpenViaHTMLPolyfill(
const QStringList &accept,
FileSelectMode fileSelectMode,
26 qstdweb::PromiseCallbacks onFilesSelected)
31 emscripten::val document = emscripten::val::global(
"document");
32 emscripten::val input = document.call<emscripten::val>(
"createElement", std::string(
"input"));
33 input.set(
"type",
"file");
34 input.set(
"style",
"display:none");
35 input.set(
"accept", LocalFileApi::makeFileInputAccept(accept));
37 input.set(
"multiple", emscripten::val(fileSelectMode == FileSelectMode::MultipleFiles));
39 static std::unique_ptr<qstdweb::EventCallback> changeEvent;
40 static std::unique_ptr<qstdweb::EventCallback> cancelEvent;
41 auto callback = [=](emscripten::val) { onFilesSelected.thenFunc(input[
"files"]); };
42 auto cancelCallback = [=](emscripten::val) { onFilesSelected.catchFunc(emscripten::val::undefined()); };
43 changeEvent = std::make_unique<qstdweb::EventCallback>(input,
"change", callback);
44 cancelEvent = std::make_unique<qstdweb::EventCallback>(input,
"cancel", cancelCallback);
47 emscripten::val body = document[
"body"];
48 body.call<
void>(
"appendChild", input);
49 input.call<
void>(
"click");
50 body.call<
void>(
"removeChild", input);
53void showOpenViaLocalFileApi(
const QStringList &accept,
FileSelectMode fileSelectMode,
54 qstdweb::PromiseCallbacks callbacks)
56 using namespace qstdweb;
58 auto options = LocalFileApi::makeOpenFileOptions(accept, fileSelectMode == FileSelectMode::MultipleFiles);
61 window(), QStringLiteral(
"showOpenFilePicker"),
63 .thenFunc = [=](emscripten::val fileHandles)
mutable {
64 std::vector<emscripten::val> filePromises;
65 filePromises.reserve(fileHandles[
"length"].as<
int>());
66 for (
int i = 0; i < fileHandles[
"length"].as<
int>(); ++i)
67 filePromises.push_back(fileHandles[i].call<emscripten::val>(
"getFile"));
68 Promise::all(std::move(filePromises), callbacks);
70 .catchFunc = callbacks.catchFunc,
71 .finallyFunc = callbacks.finallyFunc,
72 }, std::move(options));
75void showSaveViaLocalFileApi(
const std::string &fileNameHint, qstdweb::PromiseCallbacks callbacks)
77 using namespace qstdweb;
78 using namespace emscripten;
80 auto options = LocalFileApi::makeSaveFileOptions(QStringList(), fileNameHint);
83 window(), QStringLiteral(
"showSaveFilePicker"),
84 std::move(callbacks), std::move(options));
89 qstdweb::PromiseCallbacks callbacks)
92 showOpenViaLocalFileApi(accept, fileSelectMode, std::move(callbacks)) :
93 showOpenViaHTMLPolyfill(accept, fileSelectMode, std::move(callbacks));
98 return hasLocalFilesApi();
101void showSave(
const std::string &fileNameHint, qstdweb::PromiseCallbacks callbacks)
104 showSaveViaLocalFileApi(fileNameHint,
std::move(callbacks));
132 file.stream(buffer, [readFile = readFile.get(), fileIndex, fileDataReady]() {
161 qstdweb::Blob contentBlob = qstdweb::Blob::copyFrom(content, size);
162 emscripten::val document = emscripten::val::global(
"document");
163 emscripten::val window = qstdweb::window();
164 emscripten::val contentUrl = window[
"URL"].call<emscripten::val>(
"createObjectURL", contentBlob.val());
165 emscripten::val contentLink = document.call<emscripten::val>(
"createElement", std::string(
"a"));
166 contentLink.set(
"href", contentUrl);
167 contentLink.set(
"download", fileNameHint);
168 contentLink.set(
"style",
"display:none");
170 emscripten::val body = document[
"body"];
171 body.call<
void>(
"appendChild", contentLink);
172 contentLink.call<
void>(
"click");
173 body.call<
void>(
"removeChild", contentLink);
175 window[
"URL"].call<emscripten::val>(
"revokeObjectURL", contentUrl);
179 const std::function<
void (
int fileCount)> &fileDialogClosed,
180 const std::function<
char *(uint64_t size,
const std::string& name)> &acceptFile,
181 const std::function<
void()> &fileDataReady)
184 .thenFunc = [=](emscripten::val result) {
185 auto files = qstdweb::FileList(result);
186 fileDialogClosed(files.length());
187 readFiles(files, acceptFile, fileDataReady);
189 .catchFunc = [=](emscripten::val) {
196 const std::function<
void (
bool fileSelected)> &fileDialogClosed,
197 const std::function<
char *(uint64_t size,
const std::string& name)> &acceptFile,
198 const std::function<
void()> &fileDataReady)
200 auto fileDialogClosedWithInt = [=](
int fileCount) { fileDialogClosed(fileCount != 0); };
206 using namespace emscripten;
207 using namespace qstdweb;
209 Promise::make(fileHandle, QStringLiteral(
"createWritable"), {
210 .thenFunc = [=](val writable) {
213 std::function<
void(val result)> continuation;
216 static constexpr size_t desiredChunkSize = 1024u;
217#if defined(__EMSCRIPTEN_SHARED_MEMORY__)
218 qstdweb::Uint8Array chunkArray(desiredChunkSize);
221 auto state = std::make_shared<State>();
223 state->continuation = [=](val)
mutable {
224 const size_t remaining = data.size() - state->written;
225 if (remaining == 0) {
226 Promise::make(writable, QStringLiteral(
"close"), { .thenFunc = [=](val) {} });
231 const auto currentChunkSize = std::min(remaining, desiredChunkSize);
233#if defined(__EMSCRIPTEN_SHARED_MEMORY__)
239 writable, QStringLiteral(
"write"),
241 .thenFunc = state->continuation,
243 chunkArray.copyFrom(data.constData() + state->written, currentChunkSize)
245 .call<emscripten::val>(
"subarray", emscripten::val(0),
246 emscripten::val(currentChunkSize)));
248 Promise::make(writable, QStringLiteral(
"write"),
250 .thenFunc = state->continuation,
252 val(typed_memory_view(currentChunkSize, data.constData() + state->written)));
254 state->written += currentChunkSize;
257 state->continuation(val::undefined());
276void saveFile(
const char *content, size_t size,
const std::string &fileNameHint)
279 downloadDataAsFile(content, size, fileNameHint);
283 QByteArray data(content, size);
285 .thenFunc = [=](emscripten::val result) {
286 saveDataToFileInChunks(result, data);
292 const std::function<
void (
bool accepted, std::vector<qstdweb::File> files)> fileDialogClosed)
295 .thenFunc = [=](emscripten::val result) {
296 if (result.isUndefined() || result.isNull()) {
297 fileDialogClosed(
false, std::vector<qstdweb::File>());
299 std::vector<qstdweb::File> files;
300 int length = result[
"length"].as<
int>();
301 files.reserve(length);
302 for (
int i = 0; i < length; ++i) {
303 emscripten::val fileVal = result[i];
304 if (!fileVal.isUndefined() && !fileVal.isNull()) {
305 files.push_back(qstdweb::File(fileVal));
308 fileDialogClosed(
true, files);
311 .catchFunc = [=](emscripten::val) {
312 fileDialogClosed(
false, std::vector<qstdweb::File>());
317void showSaveFileDialog(
const std::string &fileNameHint,
const std::function<
void (
bool accepted, qstdweb::FileSystemFileHandle fileHandle)> fileDialogClosed)
320 .thenFunc = [=](emscripten::val result) {
321 fileDialogClosed(
true, qstdweb::FileSystemFileHandle(result));
323 .catchFunc = [=](emscripten::val) {
324 fileDialogClosed(
false, qstdweb::FileSystemFileHandle());