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));
40 static std::unique_ptr<qstdweb::EventCallback> changeEvent;
41 auto callback = [=](emscripten::val) { onFilesSelected.thenFunc(input[
"files"]); };
42 changeEvent = std::make_unique<qstdweb::EventCallback>(input,
"change", callback);
45 emscripten::val body = document[
"body"];
46 body.call<
void>(
"appendChild", input);
47 input.call<
void>(
"click");
48 body.call<
void>(
"removeChild", input);
51void showOpenViaLocalFileApi(
const QStringList &accept,
FileSelectMode fileSelectMode,
52 qstdweb::PromiseCallbacks callbacks)
54 using namespace qstdweb;
56 auto options = LocalFileApi::makeOpenFileOptions(accept, fileSelectMode == FileSelectMode::MultipleFiles);
59 window(), QStringLiteral(
"showOpenFilePicker"),
61 .thenFunc = [=](emscripten::val fileHandles)
mutable {
62 std::vector<emscripten::val> filePromises;
63 filePromises.reserve(fileHandles[
"length"].as<
int>());
64 for (
int i = 0; i < fileHandles[
"length"].as<
int>(); ++i)
65 filePromises.push_back(fileHandles[i].call<emscripten::val>(
"getFile"));
66 Promise::all(std::move(filePromises), callbacks);
68 .catchFunc = callbacks.catchFunc,
69 .finallyFunc = callbacks.finallyFunc,
70 }, std::move(options));
73void showSaveViaLocalFileApi(
const std::string &fileNameHint, qstdweb::PromiseCallbacks callbacks)
75 using namespace qstdweb;
76 using namespace emscripten;
78 auto options = LocalFileApi::makeSaveFileOptions(QStringList(), fileNameHint);
81 window(), QStringLiteral(
"showSaveFilePicker"),
82 std::move(callbacks), std::move(options));
87 qstdweb::PromiseCallbacks callbacks)
90 showOpenViaLocalFileApi(accept, fileSelectMode, std::move(callbacks)) :
91 showOpenViaHTMLPolyfill(accept, fileSelectMode, std::move(callbacks));
96 return hasLocalFilesApi();
99void showSave(
const std::string &fileNameHint, qstdweb::PromiseCallbacks callbacks)
102 showSaveViaLocalFileApi(fileNameHint,
std::move(callbacks));
130 file.stream(buffer, [readFile = readFile.get(), fileIndex, fileDataReady]() {
159 qstdweb::Blob contentBlob = qstdweb::Blob::copyFrom(content, size);
160 emscripten::val document = emscripten::val::global(
"document");
161 emscripten::val window = qstdweb::window();
162 emscripten::val contentUrl = window[
"URL"].call<emscripten::val>(
"createObjectURL", contentBlob.val());
163 emscripten::val contentLink = document.call<emscripten::val>(
"createElement", std::string(
"a"));
164 contentLink.set(
"href", contentUrl);
165 contentLink.set(
"download", fileNameHint);
166 contentLink.set(
"style",
"display:none");
168 emscripten::val body = document[
"body"];
169 body.call<
void>(
"appendChild", contentLink);
170 contentLink.call<
void>(
"click");
171 body.call<
void>(
"removeChild", contentLink);
173 window[
"URL"].call<emscripten::val>(
"revokeObjectURL", contentUrl);
177 const std::function<
void (
int fileCount)> &fileDialogClosed,
178 const std::function<
char *(uint64_t size,
const std::string& name)> &acceptFile,
179 const std::function<
void()> &fileDataReady)
182 .thenFunc = [=](emscripten::val result) {
183 auto files = qstdweb::FileList(result);
184 fileDialogClosed(files.length());
185 readFiles(files, acceptFile, fileDataReady);
187 .catchFunc = [=](emscripten::val) {
194 const std::function<
void (
bool fileSelected)> &fileDialogClosed,
195 const std::function<
char *(uint64_t size,
const std::string& name)> &acceptFile,
196 const std::function<
void()> &fileDataReady)
198 auto fileDialogClosedWithInt = [=](
int fileCount) { fileDialogClosed(fileCount != 0); };
204 using namespace emscripten;
205 using namespace qstdweb;
207 Promise::make(fileHandle, QStringLiteral(
"createWritable"), {
208 .thenFunc = [=](val writable) {
211 std::function<
void(val result)> continuation;
214 static constexpr size_t desiredChunkSize = 1024u;
215#if defined(__EMSCRIPTEN_SHARED_MEMORY__)
216 qstdweb::Uint8Array chunkArray(desiredChunkSize);
219 auto state = std::make_shared<State>();
221 state->continuation = [=](val)
mutable {
222 const size_t remaining = data.size() - state->written;
223 if (remaining == 0) {
224 Promise::make(writable, QStringLiteral(
"close"), { .thenFunc = [=](val) {} });
229 const auto currentChunkSize = std::min(remaining, desiredChunkSize);
231#if defined(__EMSCRIPTEN_SHARED_MEMORY__)
237 writable, QStringLiteral(
"write"),
239 .thenFunc = state->continuation,
241 chunkArray.copyFrom(data.constData() + state->written, currentChunkSize)
243 .call<emscripten::val>(
"subarray", emscripten::val(0),
244 emscripten::val(currentChunkSize)));
246 Promise::make(writable, QStringLiteral(
"write"),
248 .thenFunc = state->continuation,
250 val(typed_memory_view(currentChunkSize, data.constData() + state->written)));
252 state->written += currentChunkSize;
255 state->continuation(val::undefined());
274void saveFile(
const char *content, size_t size,
const std::string &fileNameHint)
277 downloadDataAsFile(content, size, fileNameHint);
281 QByteArray data(content, size);
283 .thenFunc = [=](emscripten::val result) {
284 saveDataToFileInChunks(result, data);
290 const std::function<
void (
bool accepted, std::vector<qstdweb::File> files)> fileDialogClosed)
293 .thenFunc = [=](emscripten::val result) {
294 if (result.isUndefined() || result.isNull()) {
295 fileDialogClosed(
false, std::vector<qstdweb::File>());
297 std::vector<qstdweb::File> files;
298 int length = result[
"length"].as<
int>();
299 files.reserve(length);
300 for (
int i = 0; i < length; ++i) {
301 emscripten::val fileVal = result[i];
302 if (!fileVal.isUndefined() && !fileVal.isNull()) {
303 files.push_back(qstdweb::File(fileVal));
306 fileDialogClosed(
true, files);
309 .catchFunc = [=](emscripten::val) {
310 fileDialogClosed(
false, std::vector<qstdweb::File>());
315void showSaveFileDialog(
const std::string &fileNameHint,
const std::function<
void (
bool accepted, qstdweb::FileSystemFileHandle fileHandle)> fileDialogClosed)
318 .thenFunc = [=](emscripten::val result) {
319 fileDialogClosed(
true, qstdweb::FileSystemFileHandle(result));
321 .catchFunc = [=](emscripten::val) {
322 fileDialogClosed(
false, qstdweb::FileSystemFileHandle());