19bool hasLocalFilesApi()
21 return !qstdweb::window()[
"showOpenFilePicker"].isUndefined();
24void showOpenViaHTMLPolyfill(
const QStringList &accept,
FileSelectMode fileSelectMode,
25 qstdweb::PromiseCallbacks onFilesSelected)
30 emscripten::val document = emscripten::val::global(
"document");
31 emscripten::val input = document.call<emscripten::val>(
"createElement", std::string(
"input"));
32 input.set(
"type",
"file");
33 input.set(
"style",
"display:none");
34 input.set(
"accept", LocalFileApi::makeFileInputAccept(accept));
36 input.set(
"multiple", emscripten::val(fileSelectMode == FileSelectMode::MultipleFiles));
39 static std::unique_ptr<qstdweb::EventCallback> changeEvent;
40 auto callback = [=](emscripten::val) { onFilesSelected.thenFunc(input[
"files"]); };
41 changeEvent = std::make_unique<qstdweb::EventCallback>(input,
"change", callback);
44 emscripten::val body = document[
"body"];
45 body.call<
void>(
"appendChild", input);
46 input.call<
void>(
"click");
47 body.call<
void>(
"removeChild", input);
50void showOpenViaLocalFileApi(
const QStringList &accept,
FileSelectMode fileSelectMode,
51 qstdweb::PromiseCallbacks callbacks)
53 using namespace qstdweb;
55 auto options = LocalFileApi::makeOpenFileOptions(accept, fileSelectMode == FileSelectMode::MultipleFiles);
58 window(), QStringLiteral(
"showOpenFilePicker"),
60 .thenFunc = [=](emscripten::val fileHandles)
mutable {
61 std::vector<emscripten::val> filePromises;
62 filePromises.reserve(fileHandles[
"length"].as<
int>());
63 for (
int i = 0; i < fileHandles[
"length"].as<
int>(); ++i)
64 filePromises.push_back(fileHandles[i].call<emscripten::val>(
"getFile"));
65 Promise::all(std::move(filePromises), callbacks);
67 .catchFunc = callbacks.catchFunc,
68 .finallyFunc = callbacks.finallyFunc,
69 }, std::move(options));
72void showSaveViaLocalFileApi(
const std::string &fileNameHint, qstdweb::PromiseCallbacks callbacks)
74 using namespace qstdweb;
75 using namespace emscripten;
77 auto options = LocalFileApi::makeSaveFileOptions(QStringList(), fileNameHint);
80 window(), QStringLiteral(
"showSaveFilePicker"),
81 std::move(callbacks), std::move(options));
86 qstdweb::PromiseCallbacks callbacks)
89 showOpenViaLocalFileApi(accept, fileSelectMode, std::move(callbacks)) :
90 showOpenViaHTMLPolyfill(accept, fileSelectMode, std::move(callbacks));
95 return hasLocalFilesApi();
98void showSave(
const std::string &fileNameHint, qstdweb::PromiseCallbacks callbacks)
101 showSaveViaLocalFileApi(fileNameHint,
std::move(callbacks));
129 file.stream(buffer, [readFile = readFile.get(), fileIndex, fileDataReady]() {
158 qstdweb::Blob contentBlob = qstdweb::Blob::copyFrom(content, size);
159 emscripten::val document = emscripten::val::global(
"document");
160 emscripten::val window = qstdweb::window();
161 emscripten::val contentUrl = window[
"URL"].call<emscripten::val>(
"createObjectURL", contentBlob.val());
162 emscripten::val contentLink = document.call<emscripten::val>(
"createElement", std::string(
"a"));
163 contentLink.set(
"href", contentUrl);
164 contentLink.set(
"download", fileNameHint);
165 contentLink.set(
"style",
"display:none");
167 emscripten::val body = document[
"body"];
168 body.call<
void>(
"appendChild", contentLink);
169 contentLink.call<
void>(
"click");
170 body.call<
void>(
"removeChild", contentLink);
172 window[
"URL"].call<emscripten::val>(
"revokeObjectURL", contentUrl);
176 const std::function<
void (
int fileCount)> &fileDialogClosed,
177 const std::function<
char *(uint64_t size,
const std::string& name)> &acceptFile,
178 const std::function<
void()> &fileDataReady)
180 FileDialog::showOpen(makeFilterList(accept), fileSelectMode, {
181 .thenFunc = [=](emscripten::val result) {
182 auto files = qstdweb::FileList(result);
183 fileDialogClosed(files.length());
184 readFiles(files, acceptFile, fileDataReady);
186 .catchFunc = [=](emscripten::val) {
193 const std::function<
void (
bool fileSelected)> &fileDialogClosed,
194 const std::function<
char *(uint64_t size,
const std::string& name)> &acceptFile,
195 const std::function<
void()> &fileDataReady)
197 auto fileDialogClosedWithInt = [=](
int fileCount) { fileDialogClosed(fileCount != 0); };
203 using namespace emscripten;
204 using namespace qstdweb;
206 Promise::make(fileHandle, QStringLiteral(
"createWritable"), {
207 .thenFunc = [=](val writable) {
210 std::function<
void(val result)> continuation;
213 static constexpr size_t desiredChunkSize = 1024u;
214#if defined(__EMSCRIPTEN_SHARED_MEMORY__)
215 qstdweb::Uint8Array chunkArray(desiredChunkSize);
218 auto state = std::make_shared<State>();
220 state->continuation = [=](val)
mutable {
221 const size_t remaining = data.size() - state->written;
222 if (remaining == 0) {
223 Promise::make(writable, QStringLiteral(
"close"), { .thenFunc = [=](val) {} });
228 const auto currentChunkSize = std::min(remaining, desiredChunkSize);
230#if defined(__EMSCRIPTEN_SHARED_MEMORY__)
236 writable, QStringLiteral(
"write"),
238 .thenFunc = state->continuation,
240 chunkArray.copyFrom(data.constData() + state->written, currentChunkSize)
242 .call<emscripten::val>(
"subarray", emscripten::val(0),
243 emscripten::val(currentChunkSize)));
245 Promise::make(writable, QStringLiteral(
"write"),
247 .thenFunc = state->continuation,
249 val(typed_memory_view(currentChunkSize, data.constData() + state->written)));
251 state->written += currentChunkSize;
254 state->continuation(val::undefined());
273void saveFile(
const char *content, size_t size,
const std::string &fileNameHint)
276 downloadDataAsFile(content, size, fileNameHint);
280 QByteArray data(content, size);
282 .thenFunc = [=](emscripten::val result) {
283 saveDataToFileInChunks(result, data);