Web: Add opening/saving file support.
- Opening file is implemented as uploading. - Saving file is implemented as downloading. - The filename is suffixed with current date and time.
This commit is contained in:
parent
56b9d36030
commit
64948c4526
@ -78,7 +78,7 @@ static val Wrap(std::function<void()> *func) {
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FatalError(const std::string &message) {
|
||||
fprintf(stderr, "%s", message.c_str());
|
||||
dbp("%s", message.c_str());
|
||||
#ifndef NDEBUG
|
||||
emscripten_debugger();
|
||||
#endif
|
||||
@ -808,6 +808,10 @@ public:
|
||||
|
||||
std::vector<std::function<void()>> responseFuncs;
|
||||
|
||||
bool is_shown = false;
|
||||
|
||||
Response latestResponse = Response::NONE;
|
||||
|
||||
MessageDialogImplHtml() :
|
||||
htmlModal(val::global("document").call<val>("createElement", val("div"))),
|
||||
htmlDialog(val::global("document").call<val>("createElement", val("div"))),
|
||||
@ -850,6 +854,7 @@ public:
|
||||
|
||||
std::function<void()> responseFunc = [this, response] {
|
||||
htmlModal.call<void>("remove");
|
||||
this->latestResponse = response;
|
||||
if(onResponse) {
|
||||
onResponse(response);
|
||||
}
|
||||
@ -860,16 +865,43 @@ public:
|
||||
responseFuncs.push_back(responseFunc);
|
||||
htmlButton.call<void>("addEventListener", val("trigger"), Wrap(&responseFuncs.back()));
|
||||
|
||||
static std::function<void()> updateShowFlagFunc = [this] {
|
||||
this->is_shown = false;
|
||||
};
|
||||
htmlButton.call<void>("addEventListener", val("trigger"), Wrap(&updateShowFlagFunc));
|
||||
|
||||
htmlButtons.call<void>("appendChild", htmlButton);
|
||||
}
|
||||
|
||||
Response RunModal() {
|
||||
ssassert(false, "RunModal not supported on Emscripten");
|
||||
// ssassert(false, "RunModal not supported on Emscripten");
|
||||
dbp("MessageDialog::RunModal() called.");
|
||||
this->ShowModal();
|
||||
//FIXME(emscripten): use val::await() with JavaScript's Promise
|
||||
while (true) {
|
||||
if (this->is_shown) {
|
||||
dbp("MessageDialog::RunModal(): is_shown == true");
|
||||
emscripten_sleep(2000);
|
||||
} else {
|
||||
dbp("MessageDialog::RunModal(): break due to is_shown == false");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->latestResponse != Response::NONE) {
|
||||
return this->latestResponse;
|
||||
} else {
|
||||
// FIXME(emscripten):
|
||||
dbp("MessageDialog::RunModal(): Cannot get Response.");
|
||||
return this->latestResponse;
|
||||
}
|
||||
}
|
||||
|
||||
void ShowModal() {
|
||||
dialogsOnScreen.push_back(shared_from_this());
|
||||
val::global("document")["body"].call<void>("appendChild", htmlModal);
|
||||
|
||||
this->is_shown = true;
|
||||
}
|
||||
};
|
||||
|
||||
@ -881,20 +913,187 @@ MessageDialogRef CreateMessageDialog(WindowRef parentWindow) {
|
||||
// File dialogs
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class FileDialogImplHtml : public FileDialog {
|
||||
class FileOpenDialogImplHtml : public FileDialog {
|
||||
public:
|
||||
// FIXME(emscripten): implement
|
||||
std::string title;
|
||||
std::string filename;
|
||||
std::string filters;
|
||||
|
||||
emscripten::val fileUploadHelper;
|
||||
|
||||
FileOpenDialogImplHtml() {
|
||||
//FIXME(emscripten):
|
||||
dbp("FileOpenDialogImplHtml::FileOpenDialogImplHtml()");
|
||||
// FIXME(emscripten): workaround. following code raises "constructor is not a constructor" exception.
|
||||
// val fuh = val::global("FileUploadHelper");
|
||||
// this->fileUploadHelper = fuh.new_();
|
||||
this->fileUploadHelper = val::global().call<val>("createFileUploadHelperInstance");
|
||||
dbp("FileOpenDialogImplHtml::FileOpenDialogImplHtml() OK.");
|
||||
}
|
||||
|
||||
~FileOpenDialogImplHtml() override {
|
||||
dbp("FileOpenDialogImplHtml::~FileOpenDialogImplHtml()");
|
||||
this->fileUploadHelper.call<void>("dispose");
|
||||
}
|
||||
|
||||
void SetTitle(std::string title) override {
|
||||
//FIXME(emscripten):
|
||||
dbp("FileOpenDialogImplHtml::SetTitle(): title=%s", title.c_str());
|
||||
this->title = title;
|
||||
//FIXME(emscripten):
|
||||
this->fileUploadHelper.set("title", val(this->title));
|
||||
}
|
||||
|
||||
void SetCurrentName(std::string name) override {
|
||||
//FIXME(emscripten):
|
||||
dbp("FileOpenDialogImplHtml::SetCurrentName(): name=%s", name.c_str());
|
||||
SetFilename(GetFilename().Parent().Join(name));
|
||||
}
|
||||
|
||||
Platform::Path GetFilename() override {
|
||||
//FIXME(emscripten):
|
||||
dbp("FileOpenDialogImplHtml::GetFilename()");
|
||||
return Platform::Path::From(this->filename.c_str());
|
||||
}
|
||||
|
||||
void SetFilename(Platform::Path path) override {
|
||||
//FIXME(emscripten):
|
||||
dbp("FileOpenDialogImplHtml::SetFilename(): path=%s", path.raw.c_str());
|
||||
this->filename = path.raw;
|
||||
//FIXME(emscripten):
|
||||
this->fileUploadHelper.set("filename", val(this->filename));
|
||||
}
|
||||
|
||||
void SuggestFilename(Platform::Path path) override {
|
||||
//FIXME(emscripten):
|
||||
dbp("FileOpenDialogImplHtml::SuggestFilename(): path=%s", path.raw.c_str());
|
||||
SetFilename(Platform::Path::From(path.FileStem()));
|
||||
}
|
||||
|
||||
void AddFilter(std::string name, std::vector<std::string> extensions) override {
|
||||
//FIXME(emscripten):
|
||||
dbp("FileOpenDialogImplHtml::AddFilter()");
|
||||
this->filters = "";
|
||||
for (auto extension : extensions) {
|
||||
this->filters = "." + extension;
|
||||
this->filters += ",";
|
||||
}
|
||||
dbp("filter=%s", this->filters.c_str());
|
||||
}
|
||||
|
||||
void FreezeChoices(SettingsRef settings, const std::string &key) override {
|
||||
//FIXME(emscripten):
|
||||
dbp("FileOpenDialogImplHtml::FreezeChoise()");
|
||||
}
|
||||
|
||||
void ThawChoices(SettingsRef settings, const std::string &key) override {
|
||||
//FIXME(emscripten):
|
||||
dbp("FileOpenDialogImplHtml::ThawChoices()");
|
||||
}
|
||||
|
||||
bool RunModal() override {
|
||||
//FIXME(emscripten):
|
||||
dbp("FileOpenDialogImplHtml::RunModal()");
|
||||
this->filename = "untitled.slvs";
|
||||
this->fileUploadHelper.call<void>("showDialog");
|
||||
|
||||
//FIXME(emscripten): use val::await() with JavaScript's Promise
|
||||
dbp("FileOpenDialogImplHtml: start loop");
|
||||
// Wait until fileUploadHelper.is_shown == false
|
||||
while (true) {
|
||||
bool is_shown = this->fileUploadHelper["is_shown"].as<bool>();
|
||||
if (!is_shown) {
|
||||
dbp("FileOpenDialogImplHtml: break due to is_shown == false");
|
||||
break;
|
||||
} else {
|
||||
// dbp("FileOpenDialogImplHtml: sleep 100msec... (%d)", is_shown);
|
||||
emscripten_sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
val selectedFilenameVal = this->fileUploadHelper["currentFilename"];
|
||||
|
||||
if (selectedFilenameVal == val::null()) {
|
||||
dbp("selectedFilenameVal is null");
|
||||
return false;
|
||||
} else {
|
||||
std::string selectedFilename = selectedFilenameVal.as<std::string>();
|
||||
this->filename = selectedFilename;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class FileSaveDummyDialogImplHtml : public FileDialog {
|
||||
public:
|
||||
std::string title;
|
||||
std::string filename;
|
||||
std::string filters;
|
||||
|
||||
FileSaveDummyDialogImplHtml() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
~FileSaveDummyDialogImplHtml() override {
|
||||
|
||||
}
|
||||
|
||||
void SetTitle(std::string title) override {
|
||||
this->title = title;
|
||||
}
|
||||
|
||||
void SetCurrentName(std::string name) override {
|
||||
SetFilename(GetFilename().Parent().Join(name));
|
||||
}
|
||||
|
||||
Platform::Path GetFilename() override {
|
||||
return Platform::Path::From(this->filename.c_str());
|
||||
}
|
||||
|
||||
void SetFilename(Platform::Path path) override {
|
||||
this->filename = path.raw;
|
||||
}
|
||||
|
||||
void SuggestFilename(Platform::Path path) override {
|
||||
SetFilename(Platform::Path::From(path.FileStem()));
|
||||
}
|
||||
|
||||
void AddFilter(std::string name, std::vector<std::string> extensions) override {
|
||||
this->filters = "";
|
||||
for (auto extension : extensions) {
|
||||
this->filters = "." + extension;
|
||||
this->filters += ",";
|
||||
}
|
||||
dbp("filter=%s", this->filters.c_str());
|
||||
}
|
||||
|
||||
void FreezeChoices(SettingsRef settings, const std::string &key) override {
|
||||
|
||||
}
|
||||
|
||||
void ThawChoices(SettingsRef settings, const std::string &key) override {
|
||||
|
||||
}
|
||||
|
||||
bool RunModal() override {
|
||||
if (this->filename.length() < 1) {
|
||||
this->filename = "untitled.slvs";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
FileDialogRef CreateOpenFileDialog(WindowRef parentWindow) {
|
||||
// FIXME(emscripten): implement
|
||||
return std::shared_ptr<FileDialogImplHtml>();
|
||||
|
||||
dbp("CreateOpenFileDialog()");
|
||||
return std::shared_ptr<FileOpenDialogImplHtml>(new FileOpenDialogImplHtml());
|
||||
}
|
||||
|
||||
FileDialogRef CreateSaveFileDialog(WindowRef parentWindow) {
|
||||
// FIXME(emscripten): implement
|
||||
return std::shared_ptr<FileDialogImplHtml>();
|
||||
dbp("CreateSaveFileDialog()");
|
||||
return std::shared_ptr<FileSaveDummyDialogImplHtml>(new FileSaveDummyDialogImplHtml());
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -909,11 +1108,21 @@ void OpenInBrowser(const std::string &url) {
|
||||
val::global("window").call<void>("open", Wrap(url));
|
||||
}
|
||||
|
||||
|
||||
void OnSaveFinishedCallback(const Platform::Path& filename, bool is_saveAs, bool is_autosave) {
|
||||
dbp("OnSaveFinished(): %s, is_saveAs=%d, is_autosave=%d\n", filename.FileName().c_str(), is_saveAs, is_autosave);
|
||||
std::string filename_str = filename.FileName();
|
||||
EM_ASM(saveFileDone(UTF8ToString($0), $1, $2), filename_str.c_str(), is_saveAs, is_autosave);
|
||||
}
|
||||
|
||||
std::vector<std::string> InitGui(int argc, char **argv) {
|
||||
static std::function<void()> onBeforeUnload = std::bind(&SolveSpaceUI::Exit, &SS);
|
||||
val::global("window").call<void>("addEventListener", val("beforeunload"),
|
||||
Wrap(&onBeforeUnload));
|
||||
|
||||
dbp("Set onSaveFinished");
|
||||
SS.OnSaveFinished = OnSaveFinishedCallback;
|
||||
|
||||
// FIXME(emscripten): get locale from user preferences
|
||||
SetLocale("en_US");
|
||||
|
||||
|
@ -11,6 +11,34 @@ function isModal() {
|
||||
return hasModal || hasMenuBar || hasPopupMenu || hasEditor;
|
||||
}
|
||||
|
||||
/* String helpers */
|
||||
|
||||
/**
|
||||
* @param {string} s - original string
|
||||
* @param {number} digits - char length of generating string
|
||||
* @param {string} ch - string to be used for padding
|
||||
* @return {string} generated string ($digits chars length) or $s
|
||||
*/
|
||||
function stringPadLeft(s, digits, ch) {
|
||||
if (s.length > digits) {
|
||||
return s;
|
||||
}
|
||||
for (let i = s.length; i < digits; i++) {
|
||||
s = ch + s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/** Generate a string expression of now
|
||||
* @return {string} like a "2022_08_31_2245" string (for 2022-08-31 22:45; local time)
|
||||
*/
|
||||
function GetCurrentDateTimeString() {
|
||||
const now = new Date();
|
||||
const padLeft2 = (num) => { return stringPadLeft(num.toString(), 2, '0') };
|
||||
return (`${now.getFullYear()}_${padLeft2(now.getMonth()+1)}_${padLeft2(now.getDate())}` +
|
||||
`_` + `${padLeft2(now.getHours())}${padLeft2(now.getMinutes())}`);
|
||||
}
|
||||
|
||||
/* CSS helpers */
|
||||
function hasClass(element, className) {
|
||||
return element.classList.contains(className);
|
||||
@ -346,3 +374,272 @@ window.addEventListener('keyup', function(event) {
|
||||
removeClass(document.body, 'mnemonic');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// FIXME(emscripten): Should be implemnted in guihtmlcpp ?
|
||||
class FileUploadHelper {
|
||||
constructor() {
|
||||
this.modalRoot = document.createElement("div");
|
||||
addClass(this.modalRoot, "modal");
|
||||
this.modalRoot.style.display = "none";
|
||||
this.modalRoot.style.zIndex = 1000;
|
||||
|
||||
this.dialogRoot = document.createElement("div");
|
||||
addClass(this.dialogRoot, "dialog");
|
||||
this.modalRoot.appendChild(this.dialogRoot);
|
||||
|
||||
this.messageHeader = document.createElement("strong");
|
||||
this.dialogRoot.appendChild(this.messageHeader);
|
||||
|
||||
this.descriptionParagraph = document.createElement("p");
|
||||
this.dialogRoot.appendChild(this.descriptionParagraph);
|
||||
|
||||
this.currentFileListHeader = document.createElement("p");
|
||||
this.currentFileListHeader.textContent = "Current uploaded files:";
|
||||
this.dialogRoot.appendChild(this.currentFileListHeader);
|
||||
|
||||
this.currentFileList = document.createElement("div");
|
||||
this.dialogRoot.appendChild(this.currentFileList);
|
||||
|
||||
this.fileInputContainer = document.createElement("div");
|
||||
|
||||
this.fileInputElement = document.createElement("input");
|
||||
this.fileInputElement.setAttribute("type", "file");
|
||||
this.fileInputElement.addEventListener("change", (ev)=> this.onFileInputChanged(ev));
|
||||
this.fileInputContainer.appendChild(this.fileInputElement);
|
||||
|
||||
this.dialogRoot.appendChild(this.fileInputContainer);
|
||||
|
||||
this.buttonHolder = document.createElement("div");
|
||||
addClass(this.buttonHolder, "buttons");
|
||||
this.dialogRoot.appendChild(this.buttonHolder);
|
||||
|
||||
this.AddButton("OK", 0, false);
|
||||
this.AddButton("Cancel", 1, true);
|
||||
|
||||
this.closeDialog();
|
||||
|
||||
document.querySelector("body").appendChild(this.modalRoot);
|
||||
|
||||
this.currentFilename = null;
|
||||
|
||||
// FIXME(emscripten): For debugging
|
||||
this.title = "";
|
||||
this.filename = "";
|
||||
this.filters = "";
|
||||
}
|
||||
|
||||
dispose() {
|
||||
document.querySelector("body").removeChild(this.modalRoot);
|
||||
}
|
||||
|
||||
AddButton(label, response, isDefault) {
|
||||
// FIXME(emscripten): implement
|
||||
const buttonElem = document.createElement("div");
|
||||
addClass(buttonElem, "button");
|
||||
setLabelWithMnemonic(buttonElem, label);
|
||||
if (isDefault) {
|
||||
addClass(buttonElem, "default");
|
||||
addClass(buttonElem, "selected");
|
||||
}
|
||||
buttonElem.addEventListener("click", () => {
|
||||
this.closeDialog();
|
||||
});
|
||||
|
||||
this.buttonHolder.appendChild(buttonElem);
|
||||
}
|
||||
|
||||
getFileEntries() {
|
||||
const basePath = '/';
|
||||
/** @type {Array<object} */
|
||||
const nodes = FS.readdir(basePath);
|
||||
const files = nodes.filter((nodename) => {
|
||||
return FS.isFile(FS.lstat(basePath + nodename).mode);
|
||||
}).map((filename) => {
|
||||
return basePath + filename;
|
||||
});
|
||||
return files;
|
||||
}
|
||||
|
||||
generateFileList() {
|
||||
let filepaths = this.getFileEntries();
|
||||
const listElem = document.createElement("ul");
|
||||
for (let i = 0; i < filepaths.length; i++) {
|
||||
const listitemElem = document.createElement("li");
|
||||
const stat = FS.lstat(filepaths[i]);
|
||||
const text = `"${filepaths[i]}" (${stat.size} bytes)`;
|
||||
listitemElem.textContent = text;
|
||||
listElem.appendChild(listitemElem);
|
||||
}
|
||||
return listElem;
|
||||
}
|
||||
|
||||
updateFileList() {
|
||||
this.currentFileList.innerHTML = "";
|
||||
this.currentFileList.appendChild(this.generateFileList());
|
||||
}
|
||||
|
||||
onFileInputChanged(ev) {
|
||||
const selectedFiles = ev.target.files;
|
||||
if (selectedFiles.length < 1) {
|
||||
return;
|
||||
}
|
||||
const selectedFile = selectedFiles[0];
|
||||
const selectedFilename = selectedFile.name;
|
||||
this.filename = selectedFilename;
|
||||
this.currentFilename = selectedFilename;
|
||||
|
||||
// Prepare FileReader
|
||||
const fileReader = new FileReader();
|
||||
const fileReaderReadAsArrayBufferPromise = new Promise((resolve, reject) => {
|
||||
fileReader.addEventListener("load", (ev) => {
|
||||
resolve(ev.target.result);
|
||||
});
|
||||
fileReader.addEventListener("abort", (err) => {
|
||||
reject(err);
|
||||
});
|
||||
fileReader.readAsArrayBuffer(selectedFile);
|
||||
});
|
||||
|
||||
fileReaderReadAsArrayBufferPromise
|
||||
.then((arrayBuffer) => {
|
||||
// Write selected file to FS
|
||||
console.log(`Write uploaded file blob to filesystem. "${selectedFilename}" (${arrayBuffer.byteLength} bytes)`);
|
||||
const u8array = new Uint8Array(arrayBuffer);
|
||||
const fs = FS.open("/" + selectedFilename, "w");
|
||||
FS.write(fs, u8array, 0, u8array.length, 0);
|
||||
FS.close(fs);
|
||||
|
||||
// Update file list in dialog
|
||||
this.updateFileList();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Error while fileReader.readAsArrayBuffer():", err);
|
||||
});
|
||||
}
|
||||
|
||||
showDialog() {
|
||||
this.updateFileList();
|
||||
|
||||
this.is_shown = true;
|
||||
this.modalRoot.style.display = "block";
|
||||
}
|
||||
|
||||
closeDialog() {
|
||||
this.is_shown = false;
|
||||
this.modalRoot.style.display = "none";
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME(emscripten): Workaround
|
||||
function createFileUploadHelperInstance() {
|
||||
return new FileUploadHelper();
|
||||
}
|
||||
|
||||
// FIXME(emscripten): Should be implemnted in guihtmlcpp ?
|
||||
class FileDownloadHelper {
|
||||
constructor() {
|
||||
this.modalRoot = document.createElement("div");
|
||||
addClass(this.modalRoot, "modal");
|
||||
this.modalRoot.style.display = "none";
|
||||
this.modalRoot.style.zIndex = 1000;
|
||||
|
||||
this.dialogRoot = document.createElement("div");
|
||||
addClass(this.dialogRoot, "dialog");
|
||||
this.modalRoot.appendChild(this.dialogRoot);
|
||||
|
||||
this.messageHeader = document.createElement("strong");
|
||||
this.dialogRoot.appendChild(this.messageHeader);
|
||||
|
||||
this.descriptionParagraph = document.createElement("p");
|
||||
this.dialogRoot.appendChild(this.descriptionParagraph);
|
||||
|
||||
this.buttonHolder = document.createElement("div");
|
||||
addClass(this.buttonHolder, "buttons");
|
||||
this.dialogRoot.appendChild(this.buttonHolder);
|
||||
|
||||
this.closeDialog();
|
||||
|
||||
document.querySelector("body").appendChild(this.modalRoot);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
document.querySelector("body").removeChild(this.modalRoot);
|
||||
}
|
||||
|
||||
AddButton(label, response, isDefault) {
|
||||
// FIXME(emscripten): implement
|
||||
const buttonElem = document.createElement("div");
|
||||
addClass(buttonElem, "button");
|
||||
setLabelWithMnemonic(buttonElem, label);
|
||||
if (isDefault) {
|
||||
addClass(buttonElem, "default");
|
||||
addClass(buttonElem, "selected");
|
||||
}
|
||||
buttonElem.addEventListener("click", () => {
|
||||
this.closeDialog();
|
||||
this.dispose();
|
||||
});
|
||||
|
||||
this.buttonHolder.appendChild(buttonElem);
|
||||
}
|
||||
|
||||
createBlobURLFromArrayBuffer(arrayBuffer) {
|
||||
const u8array = new Uint8Array(arrayBuffer);
|
||||
let dataUrl = "data:application/octet-stream;base64,";
|
||||
let binaryString = "";
|
||||
for (let i = 0; i < u8array.length; i++) {
|
||||
binaryString += String.fromCharCode(u8array[i]);
|
||||
}
|
||||
dataUrl += btoa(binaryString);
|
||||
|
||||
return dataUrl;
|
||||
}
|
||||
|
||||
prepareFile(filename) {
|
||||
this.messageHeader.textContent = "Your file ready";
|
||||
|
||||
const stat = FS.lstat(filename);
|
||||
const filesize = stat.size;
|
||||
const fs = FS.open(filename, "r");
|
||||
const readbuffer = new Uint8Array(filesize);
|
||||
FS.read(fs, readbuffer, 0, filesize, 0);
|
||||
FS.close(fs);
|
||||
|
||||
const blobURL = this.createBlobURLFromArrayBuffer(readbuffer.buffer);
|
||||
|
||||
this.descriptionParagraph.innerHTML = "";
|
||||
const linkElem = document.createElement("a");
|
||||
let downloadfilename = "solvespace_browser-";
|
||||
downloadfilename += `${GetCurrentDateTimeString()}.slvs`;
|
||||
linkElem.setAttribute("download", downloadfilename);
|
||||
linkElem.setAttribute("href", blobURL);
|
||||
// WORKAROUND: FIXME(emscripten)
|
||||
linkElem.style.color = "lightblue";
|
||||
linkElem.textContent = downloadfilename;
|
||||
this.descriptionParagraph.appendChild(linkElem);
|
||||
}
|
||||
|
||||
showDialog() {
|
||||
this.is_shown = true;
|
||||
this.modalRoot.style.display = "block";
|
||||
}
|
||||
|
||||
closeDialog() {
|
||||
this.is_shown = false;
|
||||
this.modalRoot.style.display = "none";
|
||||
}
|
||||
};
|
||||
|
||||
function saveFileDone(filename, isSaveAs, isAutosave) {
|
||||
console.log(`saveFileDone(${filename}, ${isSaveAs}, ${isAutosave})`);
|
||||
if (isAutosave) {
|
||||
return;
|
||||
}
|
||||
const fileDownloadHelper = new FileDownloadHelper();
|
||||
fileDownloadHelper.AddButton("OK", 0, true);
|
||||
fileDownloadHelper.prepareFile(filename);
|
||||
console.log(`Calling shoDialog()...`);
|
||||
fileDownloadHelper.showDialog();
|
||||
console.log(`shoDialog() finished.`);
|
||||
}
|
||||
|
@ -560,12 +560,18 @@ bool SolveSpaceUI::GetFilenameAndSave(bool saveAs) {
|
||||
|
||||
if(saveAs || saveFile.IsEmpty()) {
|
||||
Platform::FileDialogRef dialog = Platform::CreateSaveFileDialog(GW.window);
|
||||
// FIXME(emscripten):
|
||||
dbp("Calling AddFilter()...");
|
||||
dialog->AddFilter(C_("file-type", "SolveSpace models"), { SKETCH_EXT });
|
||||
dbp("Calling ThawChoices()...");
|
||||
dialog->ThawChoices(settings, "Sketch");
|
||||
if(!newSaveFile.IsEmpty()) {
|
||||
dbp("Calling SetFilename()...");
|
||||
dialog->SetFilename(newSaveFile);
|
||||
}
|
||||
dbp("Calling RunModal()...");
|
||||
if(dialog->RunModal()) {
|
||||
dbp("Calling FreezeChoices()...");
|
||||
dialog->FreezeChoices(settings, "Sketch");
|
||||
newSaveFile = dialog->GetFilename();
|
||||
} else {
|
||||
@ -578,6 +584,9 @@ bool SolveSpaceUI::GetFilenameAndSave(bool saveAs) {
|
||||
RemoveAutosave();
|
||||
saveFile = newSaveFile;
|
||||
unsaved = false;
|
||||
if (this->OnSaveFinished) {
|
||||
this->OnSaveFinished(newSaveFile, saveAs, false);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -589,7 +598,11 @@ void SolveSpaceUI::Autosave()
|
||||
ScheduleAutosave();
|
||||
|
||||
if(!saveFile.IsEmpty() && unsaved) {
|
||||
SaveToFile(saveFile.WithExtension(BACKUP_EXT));
|
||||
Platform::Path saveFileName = saveFile.WithExtension(BACKUP_EXT);
|
||||
SaveToFile(saveFileName);
|
||||
if (this->OnSaveFinished) {
|
||||
this->OnSaveFinished(saveFileName, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -683,6 +683,7 @@ public:
|
||||
void NewFile();
|
||||
bool SaveToFile(const Platform::Path &filename);
|
||||
bool LoadAutosaveFor(const Platform::Path &filename);
|
||||
std::function<void(const Platform::Path &filename, bool is_saveAs, bool is_autosave)> OnSaveFinished;
|
||||
bool LoadFromFile(const Platform::Path &filename, bool canCancel = false);
|
||||
void UpgradeLegacyData();
|
||||
bool LoadEntitiesFromFile(const Platform::Path &filename, EntityList *le,
|
||||
|
Loading…
Reference in New Issue
Block a user