Web: Improve file dialog.
parent
4981570844
commit
1603402df2
|
@ -345,9 +345,15 @@ if(ENABLE_GUI)
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/platform/html/solvespaceui.js
|
${CMAKE_CURRENT_SOURCE_DIR}/platform/html/solvespaceui.js
|
||||||
${EXECUTABLE_OUTPUT_PATH}/solvespaceui.js
|
${EXECUTABLE_OUTPUT_PATH}/solvespaceui.js
|
||||||
COMMENT "Copying UI script"
|
COMMENT "Copying UI script solvespaceui.js"
|
||||||
|
VERBATIM)
|
||||||
|
add_custom_command(
|
||||||
|
TARGET solvespace POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/platform/html/filemanagerui.js
|
||||||
|
${EXECUTABLE_OUTPUT_PATH}/filemanagerui.js
|
||||||
|
COMMENT "Copying UI script filemanagerui.sj"
|
||||||
VERBATIM)
|
VERBATIM)
|
||||||
|
|
||||||
else()
|
else()
|
||||||
target_sources(solvespace PRIVATE
|
target_sources(solvespace PRIVATE
|
||||||
platform/guigtk.cpp)
|
platform/guigtk.cpp)
|
||||||
|
|
|
@ -1229,29 +1229,28 @@ public:
|
||||||
auto it = std::remove(dialogsOnScreen.begin(), dialogsOnScreen.end(),
|
auto it = std::remove(dialogsOnScreen.begin(), dialogsOnScreen.end(),
|
||||||
shared_from_this());
|
shared_from_this());
|
||||||
dialogsOnScreen.erase(it);
|
dialogsOnScreen.erase(it);
|
||||||
};
|
|
||||||
responseFuncs.push_back(responseFunc);
|
|
||||||
htmlButton.call<void>("addEventListener", val("trigger"), Wrap(&responseFuncs.back()));
|
|
||||||
|
|
||||||
static std::function<void()> updateShowFlagFunc = [this] {
|
|
||||||
this->is_shown = false;
|
this->is_shown = false;
|
||||||
};
|
};
|
||||||
htmlButton.call<void>("addEventListener", val("trigger"), Wrap(&updateShowFlagFunc));
|
if (responseFuncs.size() == 0) {
|
||||||
|
//FIXME(emscripten): I don't know why but the item in the head of responseFuncs cannot call.
|
||||||
|
// So add dummy item
|
||||||
|
responseFuncs.push_back([]{ });
|
||||||
|
}
|
||||||
|
responseFuncs.push_back(responseFunc);
|
||||||
|
std::function<void()>* callback = &responseFuncs.back();
|
||||||
|
htmlButton.call<void>("addEventListener", val("trigger"), Wrap(callback));
|
||||||
|
|
||||||
htmlButtons.call<void>("appendChild", htmlButton);
|
htmlButtons.call<void>("appendChild", htmlButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
Response RunModal() {
|
Response RunModal() {
|
||||||
// ssassert(false, "RunModal not supported on Emscripten");
|
|
||||||
// dbp("MessageDialog::RunModal() called.");
|
|
||||||
this->ShowModal();
|
this->ShowModal();
|
||||||
//FIXME(emscripten): use val::await() with JavaScript's Promise
|
//FIXME(emscripten): use val::await() with JavaScript's Promise
|
||||||
while (true) {
|
while (true) {
|
||||||
if (this->is_shown) {
|
if (this->is_shown) {
|
||||||
// dbp("MessageDialog::RunModal(): is_shown == true");
|
emscripten_sleep(50);
|
||||||
emscripten_sleep(2000);
|
|
||||||
} else {
|
} else {
|
||||||
// dbp("MessageDialog::RunModal(): break due to is_shown == false");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1260,7 +1259,6 @@ public:
|
||||||
return this->latestResponse;
|
return this->latestResponse;
|
||||||
} else {
|
} else {
|
||||||
// FIXME(emscripten):
|
// FIXME(emscripten):
|
||||||
// dbp("MessageDialog::RunModal(): Cannot get Response.");
|
|
||||||
return this->latestResponse;
|
return this->latestResponse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1281,161 +1279,97 @@ MessageDialogRef CreateMessageDialog(WindowRef parentWindow) {
|
||||||
// File dialogs
|
// File dialogs
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
class FileOpenDialogImplHtml : public FileDialog {
|
// In emscripten psuedo filesystem, all userdata will be stored in this directory.
|
||||||
|
static std::string basePathInFilesystem = "/data/";
|
||||||
|
|
||||||
|
|
||||||
|
/* FileDialog that can open, save and browse. Also refer `src/platform/html/filemanagerui.js`.
|
||||||
|
*/
|
||||||
|
class FileDialogImplHtml : public FileDialog {
|
||||||
public:
|
public:
|
||||||
std::string title;
|
|
||||||
std::string filename;
|
|
||||||
std::string filters;
|
|
||||||
|
|
||||||
emscripten::val fileUploadHelper;
|
enum class Modes {
|
||||||
|
OPEN = 0,
|
||||||
FileOpenDialogImplHtml() {
|
SAVE,
|
||||||
//FIXME(emscripten):
|
BROWSER
|
||||||
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 {
|
Modes mode;
|
||||||
public:
|
|
||||||
std::string title;
|
std::string title;
|
||||||
std::string filename;
|
std::string filename;
|
||||||
std::string filters;
|
std::string filters;
|
||||||
|
|
||||||
FileSaveDummyDialogImplHtml() {
|
val jsFileManagerUI;
|
||||||
|
|
||||||
|
|
||||||
|
FileDialogImplHtml(Modes mode) {
|
||||||
|
dbp("FileDialogImplHtml::FileDialogImplHtml()");
|
||||||
|
val fileManagerUIClass = val::global("window")["FileManagerUI"];
|
||||||
|
val dialogModeValue;
|
||||||
|
this->mode = mode;
|
||||||
|
if (mode == Modes::OPEN) {
|
||||||
|
dialogModeValue = val(0);
|
||||||
|
} else if (mode == Modes::SAVE) {
|
||||||
|
dialogModeValue = val(1);
|
||||||
|
} else {
|
||||||
|
dialogModeValue = val(2);
|
||||||
|
}
|
||||||
|
this->jsFileManagerUI = fileManagerUIClass.new_(dialogModeValue);
|
||||||
|
dbp("FileDialogImplHtml::FileDialogImplHtml() Done.");
|
||||||
}
|
}
|
||||||
|
|
||||||
~FileSaveDummyDialogImplHtml() override {
|
~FileDialogImplHtml() override {
|
||||||
|
dbp("FileDialogImplHtml::~FileDialogImplHtml()");
|
||||||
|
this->jsFileManagerUI.call<void>("dispose");
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetTitle(std::string title) override {
|
void SetTitle(std::string title) override {
|
||||||
|
dbp("FileDialogImplHtml::SetTitle(): title=\"%s\"", title.c_str());
|
||||||
this->title = title;
|
this->title = title;
|
||||||
|
this->jsFileManagerUI.call<void>("setTitle", val(title));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetCurrentName(std::string name) override {
|
void SetCurrentName(std::string name) override {
|
||||||
|
dbp("FileDialogImplHtml::SetCurrentName(): name=\"%s\", parent=\"%s\"", name.c_str(), this->GetFilename().Parent().raw.c_str());
|
||||||
|
|
||||||
|
Path filepath = Path::From(name);
|
||||||
|
if (filepath.IsAbsolute()) {
|
||||||
|
// dbp("FileDialogImplHtml::SetCurrentName(): path is absolute.");
|
||||||
|
SetFilename(filepath);
|
||||||
|
} else {
|
||||||
|
// dbp("FileDialogImplHtml::SetCurrentName(): path is relative.");
|
||||||
SetFilename(GetFilename().Parent().Join(name));
|
SetFilename(GetFilename().Parent().Join(name));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Platform::Path GetFilename() override {
|
Platform::Path GetFilename() override {
|
||||||
return Platform::Path::From(this->filename.c_str());
|
return Platform::Path::From(this->filename.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetFilename(Platform::Path path) override {
|
void SetFilename(Platform::Path path) override {
|
||||||
this->filename = path.raw;
|
dbp("FileDialogImplHtml::GetFilename(): path=\"%s\"", path.raw.c_str());
|
||||||
|
this->filename = std::string(path.raw);
|
||||||
|
std::string filename_ = Path::From(this->filename).FileName();
|
||||||
|
this->jsFileManagerUI.call<void>("setDefaultFilename", val(filename_));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SuggestFilename(Platform::Path path) override {
|
void SuggestFilename(Platform::Path path) override {
|
||||||
|
dbp("FileDialogImplHtml::SuggestFilename(): path=\"%s\"", path.raw.c_str());
|
||||||
SetFilename(Platform::Path::From(path.FileStem()));
|
SetFilename(Platform::Path::From(path.FileStem()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddFilter(std::string name, std::vector<std::string> extensions) override {
|
void AddFilter(std::string name, std::vector<std::string> extensions) override {
|
||||||
this->filters = "";
|
if (this->filters.length() > 0) {
|
||||||
|
this->filters += ",";
|
||||||
|
}
|
||||||
for (size_t i = 0; i < extensions.size(); i++) {
|
for (size_t i = 0; i < extensions.size(); i++) {
|
||||||
if (i != 0) {
|
if (i != 0) {
|
||||||
this->filters += ",";
|
this->filters += ",";
|
||||||
}
|
}
|
||||||
this->filters = "." + extensions[i];
|
this->filters += "." + extensions[i];
|
||||||
}
|
}
|
||||||
dbp("filter=%s", this->filters.c_str());
|
dbp("FileDialogImplHtml::AddFilter(): filter=%s", this->filters.c_str());
|
||||||
|
this->jsFileManagerUI.call<void>("setFilter", val(this->filters));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FreezeChoices(SettingsRef settings, const std::string &key) override {
|
void FreezeChoices(SettingsRef settings, const std::string &key) override {
|
||||||
|
@ -1443,27 +1377,49 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThawChoices(SettingsRef settings, const std::string &key) override {
|
void ThawChoices(SettingsRef settings, const std::string &key) override {
|
||||||
|
//FIXME(emscripten): implement
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RunModal() override {
|
bool RunModal() override {
|
||||||
if (this->filename.length() < 1) {
|
dbp("FileDialogImplHtml::RunModal()");
|
||||||
this->filename = "untitled.slvs";
|
|
||||||
|
this->jsFileManagerUI.call<void>("setBasePath", val(basePathInFilesystem));
|
||||||
|
this->jsFileManagerUI.call<void>("show");
|
||||||
|
while (true) {
|
||||||
|
bool isShown = this->jsFileManagerUI.call<bool>("isShown");
|
||||||
|
if (!isShown) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
emscripten_sleep(50);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dbp("FileSaveDialogImplHtml::RunModal() : dialog closed.");
|
||||||
|
|
||||||
|
std::string selectedFilename = this->jsFileManagerUI.call<std::string>("getSelectedFilename");
|
||||||
|
if (selectedFilename.length() > 0) {
|
||||||
|
// Dummy call to set parent directory
|
||||||
|
this->SetFilename(Path::From(basePathInFilesystem + "/dummy"));
|
||||||
|
this->SetCurrentName(selectedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (selectedFilename.length() > 0) {
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
FileDialogRef CreateOpenFileDialog(WindowRef parentWindow) {
|
FileDialogRef CreateOpenFileDialog(WindowRef parentWindow) {
|
||||||
// FIXME(emscripten): implement
|
dbp("CreateOpenFileDialog()");
|
||||||
// dbp("CreateOpenFileDialog()");
|
return std::shared_ptr<FileDialogImplHtml>(new FileDialogImplHtml(FileDialogImplHtml::Modes::OPEN));
|
||||||
return std::shared_ptr<FileOpenDialogImplHtml>(new FileOpenDialogImplHtml());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDialogRef CreateSaveFileDialog(WindowRef parentWindow) {
|
FileDialogRef CreateSaveFileDialog(WindowRef parentWindow) {
|
||||||
// FIXME(emscripten): implement
|
dbp("CreateSaveFileDialog()");
|
||||||
// dbp("CreateSaveFileDialog()");
|
return std::shared_ptr<FileDialogImplHtml>(new FileDialogImplHtml(FileDialogImplHtml::Modes::SAVE));
|
||||||
return std::shared_ptr<FileSaveDummyDialogImplHtml>(new FileSaveDummyDialogImplHtml());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -1481,7 +1437,7 @@ void OpenInBrowser(const std::string &url) {
|
||||||
|
|
||||||
void OnSaveFinishedCallback(const Platform::Path& filename, bool is_saveAs, bool is_autosave) {
|
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);
|
dbp("OnSaveFinished(): %s, is_saveAs=%d, is_autosave=%d\n", filename.FileName().c_str(), is_saveAs, is_autosave);
|
||||||
std::string filename_str = filename.FileName();
|
std::string filename_str = filename.raw;
|
||||||
EM_ASM(saveFileDone(UTF8ToString($0), $1, $2), filename_str.c_str(), is_saveAs, is_autosave);
|
EM_ASM(saveFileDone(UTF8ToString($0), $1, $2), filename_str.c_str(), is_saveAs, is_autosave);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
--><link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.1/css/all.css" integrity="sha384-O8whS3fhG2OnA5Kas0Y9l3cfpmYjapjI0E4theH4iuMD+pLhbf6JI0jIMfYcK3yZ" crossorigin="anonymous"><!--
|
--><link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.1/css/all.css" integrity="sha384-O8whS3fhG2OnA5Kas0Y9l3cfpmYjapjI0E4theH4iuMD+pLhbf6JI0jIMfYcK3yZ" crossorigin="anonymous"><!--
|
||||||
--><link rel="stylesheet" href="solvespaceui.css"><!--
|
--><link rel="stylesheet" href="solvespaceui.css"><!--
|
||||||
--><script src="solvespaceui.js"></script><!--
|
--><script src="solvespaceui.js"></script><!--
|
||||||
|
--><script src="filemanagerui.js"></script><!--
|
||||||
--></head><!--
|
--></head><!--
|
||||||
--><body><!--
|
--><body><!--
|
||||||
--><div id="splash">
|
--><div id="splash">
|
||||||
|
|
|
@ -0,0 +1,525 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const FileManagerUI_OPEN = 0;
|
||||||
|
const FileManagerUI_SAVE = FileManagerUI_OPEN + 1;
|
||||||
|
const FileManagerUI_BROWSE = FileManagerUI_SAVE + 1;
|
||||||
|
|
||||||
|
//FIXME(emscripten): File size thresholds. How large file can we accept safely ?
|
||||||
|
|
||||||
|
/** Maximum filesize for a uploaded file.
|
||||||
|
* @type {number} */
|
||||||
|
const FileManagerUI_UPLOAD_FILE_SIZE_LIMIT = 50 * 1000 * 1000;
|
||||||
|
|
||||||
|
const tryMakeDirectory = (path) => {
|
||||||
|
try {
|
||||||
|
FS.mkdir(path);
|
||||||
|
} catch {
|
||||||
|
// NOP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FileManagerUI {
|
||||||
|
/**
|
||||||
|
* @param {number} mode - dialog mode FileManagerUI_[ OPEN, SAVE, BROWSE ]
|
||||||
|
*/
|
||||||
|
constructor(mode) {
|
||||||
|
/** @type {boolean} */
|
||||||
|
this.__isOpenDialog = false;
|
||||||
|
/** @type {boolean} */
|
||||||
|
this.__isSaveDialog = false;
|
||||||
|
/** @type {boolean} */
|
||||||
|
this.__isBrowseDialog = false;
|
||||||
|
|
||||||
|
if (mode == FileManagerUI_OPEN) {
|
||||||
|
this.__isOpenDialog = true;
|
||||||
|
} else if (mode == FileManagerUI_SAVE) {
|
||||||
|
this.__isSaveDialog = true;
|
||||||
|
} else {
|
||||||
|
this.__isBrowseDialog = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {boolean} true if the dialog is shown. */
|
||||||
|
this.__isShown = false;
|
||||||
|
|
||||||
|
/** @type {string[]} */
|
||||||
|
this.__extension_filters = [".slvs"];
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
this.__basePathInFilesystem = "";
|
||||||
|
|
||||||
|
/** @type {string} filename user selected. empty if nothing selected */
|
||||||
|
this.__selectedFilename = "";
|
||||||
|
|
||||||
|
this.__closedWithCancel = false;
|
||||||
|
|
||||||
|
this.__defaultFilename = "untitled";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** deconstructor
|
||||||
|
*/
|
||||||
|
dispose() {
|
||||||
|
if (this.__dialogRootElement) {
|
||||||
|
this.__dialogHeaderElement = null;
|
||||||
|
this.__descriptionElement = null;
|
||||||
|
this.__filelistElement = null;
|
||||||
|
this.__fileInputElement = null;
|
||||||
|
this.__saveFilenameInputElement = null;
|
||||||
|
this.__buttonContainerElement = null;
|
||||||
|
this.__dialogRootElement.parentElement.removeChild(this.__dialogRootElement);
|
||||||
|
this.__dialogRootElement = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} label
|
||||||
|
* @param {string} response
|
||||||
|
* @param {bool} isDefault
|
||||||
|
*/
|
||||||
|
__addButton(label, response, isDefault, onclick) {
|
||||||
|
const buttonElem = document.createElement("div");
|
||||||
|
addClass(buttonElem, "button");
|
||||||
|
setLabelWithMnemonic(buttonElem, label);
|
||||||
|
if (isDefault) {
|
||||||
|
addClass(buttonElem, "default");
|
||||||
|
addClass(buttonElem, "selected");
|
||||||
|
}
|
||||||
|
buttonElem.addEventListener("click", () => {
|
||||||
|
if (onclick) {
|
||||||
|
if (onclick()) {
|
||||||
|
this.__close();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.__close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.__buttonContainerElement.appendChild(buttonElem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {HTMLElement} div element that built
|
||||||
|
*/
|
||||||
|
buildDialog() {
|
||||||
|
const root = document.createElement('div');
|
||||||
|
addClass(root, "modal");
|
||||||
|
root.style.display = "none";
|
||||||
|
root.style.zIndex = 1000;
|
||||||
|
|
||||||
|
const dialog = document.createElement('div');
|
||||||
|
addClass(dialog, "dialog");
|
||||||
|
addClass(dialog, "wide");
|
||||||
|
root.appendChild(dialog);
|
||||||
|
|
||||||
|
const messageHeader = document.createElement('strong');
|
||||||
|
this.__dialogHeaderElement = messageHeader;
|
||||||
|
addClass(messageHeader, "dialog_header");
|
||||||
|
dialog.appendChild(messageHeader);
|
||||||
|
|
||||||
|
const description = document.createElement('p');
|
||||||
|
this.__descriptionElement = description;
|
||||||
|
dialog.appendChild(description);
|
||||||
|
|
||||||
|
const filelistheader = document.createElement('h3');
|
||||||
|
filelistheader.textContent = 'Files:';
|
||||||
|
dialog.appendChild(filelistheader);
|
||||||
|
|
||||||
|
const filelist = document.createElement('ul');
|
||||||
|
this.__filelistElement = filelist;
|
||||||
|
addClass(filelist, 'filelist');
|
||||||
|
dialog.appendChild(filelist);
|
||||||
|
|
||||||
|
const dummyfilelistitem = document.createElement('li');
|
||||||
|
dummyfilelistitem.textContent = "(No file in psuedo filesystem)";
|
||||||
|
filelist.appendChild(dummyfilelistitem);
|
||||||
|
|
||||||
|
if (this.__isOpenDialog) {
|
||||||
|
const fileuploadcontainer = document.createElement('div');
|
||||||
|
dialog.appendChild(fileuploadcontainer);
|
||||||
|
|
||||||
|
const fileuploadheader = document.createElement('h3');
|
||||||
|
fileuploadheader.textContent = "Upload file:";
|
||||||
|
fileuploadcontainer.appendChild(fileuploadheader);
|
||||||
|
|
||||||
|
const dragdropdescription = document.createElement('p');
|
||||||
|
dragdropdescription.textContent = "(Drag & drop file to the following box)";
|
||||||
|
dragdropdescription.style.fontSize = "0.8em";
|
||||||
|
dragdropdescription.style.margin = "0.1em";
|
||||||
|
fileuploadcontainer.appendChild(dragdropdescription);
|
||||||
|
|
||||||
|
const filedroparea = document.createElement('div');
|
||||||
|
addClass(filedroparea, 'filedrop');
|
||||||
|
filedroparea.addEventListener('dragstart', (ev) => this.__onFileDragDrop(ev));
|
||||||
|
filedroparea.addEventListener('dragover', (ev) => this.__onFileDragDrop(ev));
|
||||||
|
filedroparea.addEventListener('dragleave', (ev) => this.__onFileDragDrop(ev));
|
||||||
|
filedroparea.addEventListener('drop', (ev) => this.__onFileDragDrop(ev));
|
||||||
|
fileuploadcontainer.appendChild(filedroparea);
|
||||||
|
|
||||||
|
const fileinput = document.createElement('input');
|
||||||
|
this.__fileInputElement = fileinput;
|
||||||
|
fileinput.setAttribute('type', 'file');
|
||||||
|
fileinput.style.width = "100%";
|
||||||
|
fileinput.addEventListener('change', (ev) => this.__onFileInputChanged(ev));
|
||||||
|
filedroparea.appendChild(fileinput);
|
||||||
|
|
||||||
|
} else if (this.__isSaveDialog) {
|
||||||
|
const filenameinputcontainer = document.createElement('div');
|
||||||
|
dialog.appendChild(filenameinputcontainer);
|
||||||
|
|
||||||
|
const filenameinputheader = document.createElement('h3');
|
||||||
|
filenameinputheader.textContent = "Filename:";
|
||||||
|
filenameinputcontainer.appendChild(filenameinputheader);
|
||||||
|
|
||||||
|
const filenameinput = document.createElement('input');
|
||||||
|
filenameinput.setAttribute('type', 'input');
|
||||||
|
filenameinput.style.width = "90%";
|
||||||
|
filenameinput.style.margin = "auto 1em auto 1em";
|
||||||
|
this.__saveFilenameInputElement = filenameinput;
|
||||||
|
filenameinputcontainer.appendChild(filenameinput);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paragraph element for spacer
|
||||||
|
dialog.appendChild(document.createElement('p'));
|
||||||
|
|
||||||
|
const buttoncontainer = document.createElement('div');
|
||||||
|
this.__buttonContainerElement = buttoncontainer;
|
||||||
|
addClass(buttoncontainer, "buttons");
|
||||||
|
dialog.appendChild(buttoncontainer);
|
||||||
|
|
||||||
|
this.__addButton('OK', 0, false, () => {
|
||||||
|
if (this.__isOpenDialog) {
|
||||||
|
let selectedFilename = null;
|
||||||
|
const fileitems = document.querySelectorAll('input[type="radio"][name="filemanager_filelist"]');
|
||||||
|
Array.from(fileitems).forEach((radiobox) => {
|
||||||
|
if (radiobox.checked) {
|
||||||
|
selectedFilename = radiobox.parentElement.getAttribute('data-filename');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (selectedFilename) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.__addButton('Cancel', 1, true, () => {
|
||||||
|
this.__closedWithCancel = true;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} text
|
||||||
|
*/
|
||||||
|
setTitle(text) {
|
||||||
|
this.__dialogHeaderText = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} text
|
||||||
|
*/
|
||||||
|
setDescription(text) {
|
||||||
|
this.__descriptionText = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} path file prefix. (ex) 'tmp/' to '/tmp/filename.txt'
|
||||||
|
*/
|
||||||
|
setBasePath(path) {
|
||||||
|
this.__basePathInFilesystem = path;
|
||||||
|
tryMakeDirectory(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} filename
|
||||||
|
*/
|
||||||
|
setDefaultFilename(filename) {
|
||||||
|
this.__defaultFilename = filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} filter comma-separated extensions like ".slvs,.stl;."
|
||||||
|
*/
|
||||||
|
setFilter(filter) {
|
||||||
|
const exts = filter.split(',');
|
||||||
|
this.__extension_filters = exts;
|
||||||
|
}
|
||||||
|
|
||||||
|
__buildFileEntry(filename) {
|
||||||
|
const lielem = document.createElement('li');
|
||||||
|
const label = document.createElement('label');
|
||||||
|
label.setAttribute('data-filename', filename);
|
||||||
|
lielem.appendChild(label);
|
||||||
|
const radiobox = document.createElement('input');
|
||||||
|
radiobox.setAttribute('type', 'radio');
|
||||||
|
if (!this.__isOpenDialog) {
|
||||||
|
radiobox.style.display = "none";
|
||||||
|
}
|
||||||
|
radiobox.setAttribute('name', 'filemanager_filelist');
|
||||||
|
label.appendChild(radiobox);
|
||||||
|
const filenametext = document.createTextNode(filename);
|
||||||
|
label.appendChild(filenametext);
|
||||||
|
|
||||||
|
return lielem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string[]} filename array
|
||||||
|
*/
|
||||||
|
__getFileEntries() {
|
||||||
|
const basePath = this.__basePathInFilesystem;
|
||||||
|
/** @type {any[]} */
|
||||||
|
const nodes = FS.readdir(basePath);
|
||||||
|
/** @type {string[]} */
|
||||||
|
const files = nodes.filter((nodename) => {
|
||||||
|
return FS.isFile(FS.lstat(basePath + nodename).mode);
|
||||||
|
});
|
||||||
|
/*.map((filename) => {
|
||||||
|
return basePath + filename;
|
||||||
|
});*/
|
||||||
|
console.log(`__getFileEntries():`, files);
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string[]?} files file list already constructed
|
||||||
|
* @returns {string[]} filename array
|
||||||
|
*/
|
||||||
|
__getFileEntries_recurse(basePath) {
|
||||||
|
//FIXME:remove try catch block
|
||||||
|
try {
|
||||||
|
//const basePath = this.__basePathInFilesystem;
|
||||||
|
FS.currentPath = basePath;
|
||||||
|
/** @type {any[]} */
|
||||||
|
const nodes = FS.readdir(basePath);
|
||||||
|
|
||||||
|
const filesInThisDirectory = nodes.filter((nodename) => {
|
||||||
|
return FS.isFile(FS.lstat(basePath + "/" + nodename).mode);
|
||||||
|
}).map((filename) => {
|
||||||
|
return basePath + "/" + filename;
|
||||||
|
});
|
||||||
|
let files = filesInThisDirectory;
|
||||||
|
|
||||||
|
const directories = nodes.filter((nodename) => {
|
||||||
|
return FS.isDir(FS.lstat(basePath + "/" + nodename).mode);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let i = 0; i < directories.length; i++) {
|
||||||
|
const directoryname = directories[i];
|
||||||
|
if (directoryname == '.' || directoryname == '..') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const orig_cwd = FS.currentPath;
|
||||||
|
const directoryfullpath = basePath + "/" + directoryname;
|
||||||
|
FS.currentPath = directoryfullpath;
|
||||||
|
files = files.concat(this.__getFileEntries_recurse(directoryfullpath));
|
||||||
|
FS.currentPath = orig_cwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`__getFileEntries_recurse(): in "${basePath}"`, files);
|
||||||
|
return files;
|
||||||
|
|
||||||
|
} catch (excep) {
|
||||||
|
console.log(excep);
|
||||||
|
throw excep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__updateFileList() {
|
||||||
|
console.log(`__updateFileList()`);
|
||||||
|
Array.from(this.__filelistElement.children).forEach((elem) => {
|
||||||
|
this.__filelistElement.removeChild(elem);
|
||||||
|
});
|
||||||
|
// const files = this.__getFileEntries();
|
||||||
|
FS.currentPath = this.__basePathInFilesystem;
|
||||||
|
const files = this.__getFileEntries_recurse(this.__basePathInFilesystem);
|
||||||
|
if (files.length < 1) {
|
||||||
|
const dummyfilelistitem = document.createElement('li');
|
||||||
|
dummyfilelistitem.textContent = "(No file in psuedo filesystem)";
|
||||||
|
this.__filelistElement.appendChild(dummyfilelistitem);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
files.forEach((entry) => {
|
||||||
|
this.__filelistElement.appendChild(this.__buildFileEntry(entry));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {File} file
|
||||||
|
*/
|
||||||
|
__getFileAsArrayBuffer(file) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const filereader = new FileReader();
|
||||||
|
filereader.onerror = (ev) => {
|
||||||
|
reject(ev);
|
||||||
|
};
|
||||||
|
filereader.onload = (ev) => {
|
||||||
|
resolve(ev.target.result);
|
||||||
|
};
|
||||||
|
filereader.readAsArrayBuffer(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {File} file
|
||||||
|
*/
|
||||||
|
async __tryAddFile(file) {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
if (!file) {
|
||||||
|
reject(new Error(`Invalid arg: file is ${file}`));
|
||||||
|
|
||||||
|
} else if (file.size > FileManagerUI_UPLOAD_FILE_SIZE_LIMIT) {
|
||||||
|
//FIXME(emscripten): Use our MessageDialog instead of browser's alert().
|
||||||
|
alert(`Specified file is larger than limit of ${FileManagerUI_UPLOAD_FILE_SIZE_LIMIT} bytes. Canceced.`);
|
||||||
|
reject(new Error(`File is too large: "${file.name} is ${file.size} bytes`));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Just add to Filesystem
|
||||||
|
const path = `${this.__basePathInFilesystem}${file.name}`;
|
||||||
|
const blobArrayBuffer = await this.__getFileAsArrayBuffer(file);
|
||||||
|
const u8array = new Uint8Array(blobArrayBuffer);
|
||||||
|
const fs = FS.open(path, "w");
|
||||||
|
FS.write(fs, u8array, 0, u8array.length, 0);
|
||||||
|
FS.close(fs);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
__addSelectedFile() {
|
||||||
|
if (this.__fileInputElement.files.length < 1) {
|
||||||
|
console.warn(`No file selected.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = this.__fileInputElement.files[0];
|
||||||
|
this.__tryAddFile(file)
|
||||||
|
.then(() => {
|
||||||
|
this.__updateFileList();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.__fileInputElement.value = null;
|
||||||
|
console.error(err);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {DragEvent} ev
|
||||||
|
*/
|
||||||
|
__onFileDragDrop(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
if (ev.type == "dragenter" || ev.type == "dragover" || ev.type == "dragleave") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ev.dataTransfer.files.length < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.__fileInputElement.files = ev.dataTransfer.files;
|
||||||
|
|
||||||
|
this.__addSelectedFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {InputEvent} _ev
|
||||||
|
*/
|
||||||
|
__onFileInputChanged(_ev) {
|
||||||
|
this.__addSelectedFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Show the FileManager UI dialog */
|
||||||
|
__show() {
|
||||||
|
this.__closedWithCancel = false;
|
||||||
|
|
||||||
|
/** @type {HTMLElement} */
|
||||||
|
this.__dialogRootElement = this.buildDialog();
|
||||||
|
document.querySelector('body').appendChild(this.__dialogRootElement);
|
||||||
|
|
||||||
|
this.__dialogHeaderElement.textContent = this.__dialogHeaderText || "File manager";
|
||||||
|
this.__descriptionElement.textContent = this.__descriptionText || "Select a file.";
|
||||||
|
if (this.__extension_filters) {
|
||||||
|
this.__descriptionElement.textContent += "Requested filter is " + this.__extension_filters.join(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.__isOpenDialog && this.__extension_filters) {
|
||||||
|
this.__fileInputElement.accept = this.__extension_filters.concat(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.__isSaveDialog) {
|
||||||
|
this.__saveFilenameInputElement.value = this.__defaultFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.__dialogRootElement.style.display = "block";
|
||||||
|
this.__isShown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Close the dialog */
|
||||||
|
__close() {
|
||||||
|
this.__selectedFilename = "";
|
||||||
|
if (this.__isOpenDialog) {
|
||||||
|
Array.from(document.querySelectorAll('input[type="radio"][name="filemanager_filelist"]'))
|
||||||
|
.forEach((elem) => {
|
||||||
|
if (elem.checked) {
|
||||||
|
this.__selectedFilename = elem.parentElement.getAttribute("data-filename");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (this.__isSaveDialog) {
|
||||||
|
if (!this.__closedWithCancel) {
|
||||||
|
this.__selectedFilename = this.__saveFilenameInputElement.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.from(this.__filelistElement.children).forEach((elem) => {
|
||||||
|
this.__filelistElement.removeChild(elem);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.dispose();
|
||||||
|
|
||||||
|
this.__isShown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
isShown() {
|
||||||
|
return this.__isShown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {Promise} filename string on resolved.
|
||||||
|
*/
|
||||||
|
showModalAsync() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.__show();
|
||||||
|
this.__updateFileList();
|
||||||
|
const intervalTimer = setInterval(() => {
|
||||||
|
if (!this.isShown()) {
|
||||||
|
clearInterval(intervalTimer);
|
||||||
|
resolve(this.__selectedFilename);
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectedFilename() {
|
||||||
|
return this.__selectedFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
this.__show();
|
||||||
|
this.__updateFileList();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
window.FileManagerUI = FileManagerUI;
|
|
@ -246,11 +246,34 @@ main {
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
|
max-height: 70%;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.dialog.wide {
|
||||||
|
width: 80%;
|
||||||
|
max-width: 1200px;
|
||||||
}
|
}
|
||||||
.dialog > .buttons {
|
.dialog > .buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
}
|
}
|
||||||
|
.dialog .filedrop {
|
||||||
|
margin: 1em 0 1em 0;
|
||||||
|
padding: 1em;
|
||||||
|
border: 2px solid black;
|
||||||
|
background-color: hsl(0, 0%, 50%);
|
||||||
|
}
|
||||||
|
.dialog .filelist {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.dialog .filelist li {
|
||||||
|
padding: 0.2em 0.5em 0.2em 0.5em;
|
||||||
|
break-inside: avoid;
|
||||||
|
}
|
||||||
|
|
||||||
/* Mnemonics */
|
/* Mnemonics */
|
||||||
.label > u {
|
.label > u {
|
||||||
|
|
|
@ -657,8 +657,9 @@ class FileDownloadHelper {
|
||||||
|
|
||||||
this.descriptionParagraph.innerHTML = "";
|
this.descriptionParagraph.innerHTML = "";
|
||||||
const linkElem = document.createElement("a");
|
const linkElem = document.createElement("a");
|
||||||
let downloadfilename = "solvespace_browser-";
|
//let downloadfilename = "solvespace_browser-";
|
||||||
downloadfilename += `${GetCurrentDateTimeString()}.slvs`;
|
//downloadfilename += `${GetCurrentDateTimeString()}.slvs`;
|
||||||
|
let downloadfilename = filename;
|
||||||
linkElem.setAttribute("download", downloadfilename);
|
linkElem.setAttribute("download", downloadfilename);
|
||||||
linkElem.setAttribute("href", blobURL);
|
linkElem.setAttribute("href", blobURL);
|
||||||
// WORKAROUND: FIXME(emscripten)
|
// WORKAROUND: FIXME(emscripten)
|
||||||
|
|
|
@ -702,6 +702,9 @@ void SolveSpaceUI::MenuFile(Command id) {
|
||||||
if(dialog->RunModal()) {
|
if(dialog->RunModal()) {
|
||||||
dialog->FreezeChoices(settings, "ExportImage");
|
dialog->FreezeChoices(settings, "ExportImage");
|
||||||
SS.ExportAsPngTo(dialog->GetFilename());
|
SS.ExportAsPngTo(dialog->GetFilename());
|
||||||
|
if (SS.OnSaveFinished) {
|
||||||
|
SS.OnSaveFinished(dialog->GetFilename(), false, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -727,6 +730,9 @@ void SolveSpaceUI::MenuFile(Command id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SS.ExportViewOrWireframeTo(dialog->GetFilename(), /*exportWireframe=*/false);
|
SS.ExportViewOrWireframeTo(dialog->GetFilename(), /*exportWireframe=*/false);
|
||||||
|
if (SS.OnSaveFinished) {
|
||||||
|
SS.OnSaveFinished(dialog->GetFilename(), false, false);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -739,6 +745,9 @@ void SolveSpaceUI::MenuFile(Command id) {
|
||||||
dialog->FreezeChoices(settings, "ExportWireframe");
|
dialog->FreezeChoices(settings, "ExportWireframe");
|
||||||
|
|
||||||
SS.ExportViewOrWireframeTo(dialog->GetFilename(), /*exportWireframe*/true);
|
SS.ExportViewOrWireframeTo(dialog->GetFilename(), /*exportWireframe*/true);
|
||||||
|
if (SS.OnSaveFinished) {
|
||||||
|
SS.OnSaveFinished(dialog->GetFilename(), false, false);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -751,6 +760,9 @@ void SolveSpaceUI::MenuFile(Command id) {
|
||||||
dialog->FreezeChoices(settings, "ExportSection");
|
dialog->FreezeChoices(settings, "ExportSection");
|
||||||
|
|
||||||
SS.ExportSectionTo(dialog->GetFilename());
|
SS.ExportSectionTo(dialog->GetFilename());
|
||||||
|
if (SS.OnSaveFinished) {
|
||||||
|
SS.OnSaveFinished(dialog->GetFilename(), false, false);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -763,6 +775,10 @@ void SolveSpaceUI::MenuFile(Command id) {
|
||||||
dialog->FreezeChoices(settings, "ExportMesh");
|
dialog->FreezeChoices(settings, "ExportMesh");
|
||||||
|
|
||||||
SS.ExportMeshTo(dialog->GetFilename());
|
SS.ExportMeshTo(dialog->GetFilename());
|
||||||
|
if (SS.OnSaveFinished) {
|
||||||
|
SS.OnSaveFinished(dialog->GetFilename(), false, false);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -776,6 +792,9 @@ void SolveSpaceUI::MenuFile(Command id) {
|
||||||
|
|
||||||
StepFileWriter sfw = {};
|
StepFileWriter sfw = {};
|
||||||
sfw.ExportSurfacesTo(dialog->GetFilename());
|
sfw.ExportSurfacesTo(dialog->GetFilename());
|
||||||
|
if (SS.OnSaveFinished) {
|
||||||
|
SS.OnSaveFinished(dialog->GetFilename(), false, false);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue