feat: promisify dialog.showSaveDialog() (#17054)
* feat: promisify dialog.showSaveDialog() * address some feedback from review * filename => filePath * fix last filename => filePath
This commit is contained in:
parent
92c9dbc179
commit
6cb7b8d3a4
11 changed files with 166 additions and 135 deletions
|
@ -68,18 +68,21 @@ v8::Local<v8::Promise> ShowOpenDialog(
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShowSaveDialog(const file_dialog::DialogSettings& settings,
|
void ShowSaveDialogSync(const file_dialog::DialogSettings& settings,
|
||||||
mate::Arguments* args) {
|
mate::Arguments* args) {
|
||||||
v8::Local<v8::Value> peek = args->PeekNext();
|
|
||||||
file_dialog::SaveDialogCallback callback;
|
|
||||||
if (mate::Converter<file_dialog::SaveDialogCallback>::FromV8(
|
|
||||||
args->isolate(), peek, &callback)) {
|
|
||||||
file_dialog::ShowSaveDialog(settings, callback);
|
|
||||||
} else {
|
|
||||||
base::FilePath path;
|
base::FilePath path;
|
||||||
if (file_dialog::ShowSaveDialog(settings, &path))
|
if (file_dialog::ShowSaveDialogSync(settings, &path))
|
||||||
args->Return(path);
|
args->Return(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v8::Local<v8::Promise> ShowSaveDialog(
|
||||||
|
const file_dialog::DialogSettings& settings,
|
||||||
|
mate::Arguments* args) {
|
||||||
|
atom::util::Promise promise(args->isolate());
|
||||||
|
v8::Local<v8::Promise> handle = promise.GetHandle();
|
||||||
|
|
||||||
|
file_dialog::ShowSaveDialog(settings, std::move(promise));
|
||||||
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Initialize(v8::Local<v8::Object> exports,
|
void Initialize(v8::Local<v8::Object> exports,
|
||||||
|
@ -91,6 +94,7 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||||
dict.SetMethod("showErrorBox", &atom::ShowErrorBox);
|
dict.SetMethod("showErrorBox", &atom::ShowErrorBox);
|
||||||
dict.SetMethod("showOpenDialogSync", &ShowOpenDialogSync);
|
dict.SetMethod("showOpenDialogSync", &ShowOpenDialogSync);
|
||||||
dict.SetMethod("showOpenDialog", &ShowOpenDialog);
|
dict.SetMethod("showOpenDialog", &ShowOpenDialog);
|
||||||
|
dict.SetMethod("showSaveDialogSync", &ShowSaveDialogSync);
|
||||||
dict.SetMethod("showSaveDialog", &ShowSaveDialog);
|
dict.SetMethod("showSaveDialog", &ShowSaveDialog);
|
||||||
#if defined(OS_MACOSX) || defined(OS_WIN)
|
#if defined(OS_MACOSX) || defined(OS_WIN)
|
||||||
dict.SetMethod("showCertificateTrustDialog",
|
dict.SetMethod("showCertificateTrustDialog",
|
||||||
|
|
|
@ -5,12 +5,14 @@
|
||||||
#include "atom/browser/atom_download_manager_delegate.h"
|
#include "atom/browser/atom_download_manager_delegate.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "atom/browser/api/atom_api_download_item.h"
|
#include "atom/browser/api/atom_api_download_item.h"
|
||||||
#include "atom/browser/atom_browser_context.h"
|
#include "atom/browser/atom_browser_context.h"
|
||||||
#include "atom/browser/native_window.h"
|
#include "atom/browser/native_window.h"
|
||||||
#include "atom/browser/ui/file_dialog.h"
|
#include "atom/browser/ui/file_dialog.h"
|
||||||
#include "atom/browser/web_contents_preferences.h"
|
#include "atom/browser/web_contents_preferences.h"
|
||||||
|
#include "atom/common/native_mate_converters/callback.h"
|
||||||
#include "atom/common/options_switches.h"
|
#include "atom/common/options_switches.h"
|
||||||
#include "base/bind.h"
|
#include "base/bind.h"
|
||||||
#include "base/files/file_util.h"
|
#include "base/files/file_util.h"
|
||||||
|
@ -119,10 +121,14 @@ void AtomDownloadManagerDelegate::OnDownloadPathGenerated(
|
||||||
!web_preferences || web_preferences->IsEnabled(options::kOffscreen);
|
!web_preferences || web_preferences->IsEnabled(options::kOffscreen);
|
||||||
settings.force_detached = offscreen;
|
settings.force_detached = offscreen;
|
||||||
|
|
||||||
|
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||||
|
atom::util::Promise dialog_promise(isolate);
|
||||||
auto dialog_callback =
|
auto dialog_callback =
|
||||||
base::Bind(&AtomDownloadManagerDelegate::OnDownloadSaveDialogDone,
|
base::Bind(&AtomDownloadManagerDelegate::OnDownloadSaveDialogDone,
|
||||||
base::Unretained(this), download_id, callback);
|
base::Unretained(this), download_id, callback);
|
||||||
file_dialog::ShowSaveDialog(settings, dialog_callback);
|
|
||||||
|
file_dialog::ShowSaveDialog(settings, std::move(dialog_promise));
|
||||||
|
ignore_result(dialog_promise.Then(dialog_callback));
|
||||||
} else {
|
} else {
|
||||||
callback.Run(path, download::DownloadItem::TARGET_DISPOSITION_PROMPT,
|
callback.Run(path, download::DownloadItem::TARGET_DISPOSITION_PROMPT,
|
||||||
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, path,
|
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, path,
|
||||||
|
|
|
@ -382,7 +382,7 @@ void CommonWebContentsDelegate::DevToolsSaveToFile(const std::string& url,
|
||||||
settings.force_detached = offscreen_;
|
settings.force_detached = offscreen_;
|
||||||
settings.title = url;
|
settings.title = url;
|
||||||
settings.default_path = base::FilePath::FromUTF8Unsafe(url);
|
settings.default_path = base::FilePath::FromUTF8Unsafe(url);
|
||||||
if (!file_dialog::ShowSaveDialog(settings, &path)) {
|
if (!file_dialog::ShowSaveDialogSync(settings, &path)) {
|
||||||
base::Value url_value(url);
|
base::Value url_value(url);
|
||||||
web_contents_->CallClientFunction("DevToolsAPI.canceledSaveURL",
|
web_contents_->CallClientFunction("DevToolsAPI.canceledSaveURL",
|
||||||
&url_value, nullptr, nullptr);
|
&url_value, nullptr, nullptr);
|
||||||
|
|
|
@ -36,16 +36,6 @@ enum FileDialogProperty {
|
||||||
FILE_DIALOG_TREAT_PACKAGE_APP_AS_DIRECTORY = 1 << 7,
|
FILE_DIALOG_TREAT_PACKAGE_APP_AS_DIRECTORY = 1 << 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(MAS_BUILD)
|
|
||||||
typedef base::Callback<void(bool result,
|
|
||||||
const base::FilePath& path,
|
|
||||||
const std::string& bookmarkData)>
|
|
||||||
SaveDialogCallback;
|
|
||||||
#else
|
|
||||||
typedef base::Callback<void(bool result, const base::FilePath& path)>
|
|
||||||
SaveDialogCallback;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct DialogSettings {
|
struct DialogSettings {
|
||||||
atom::NativeWindow* parent_window = nullptr;
|
atom::NativeWindow* parent_window = nullptr;
|
||||||
std::string title;
|
std::string title;
|
||||||
|
@ -70,10 +60,10 @@ bool ShowOpenDialogSync(const DialogSettings& settings,
|
||||||
void ShowOpenDialog(const DialogSettings& settings,
|
void ShowOpenDialog(const DialogSettings& settings,
|
||||||
atom::util::Promise promise);
|
atom::util::Promise promise);
|
||||||
|
|
||||||
bool ShowSaveDialog(const DialogSettings& settings, base::FilePath* path);
|
bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path);
|
||||||
|
|
||||||
void ShowSaveDialog(const DialogSettings& settings,
|
void ShowSaveDialog(const DialogSettings& settings,
|
||||||
const SaveDialogCallback& callback);
|
atom::util::Promise promise);
|
||||||
|
|
||||||
} // namespace file_dialog
|
} // namespace file_dialog
|
||||||
|
|
||||||
|
|
|
@ -126,8 +126,8 @@ class FileChooserDialog {
|
||||||
gtk_window_present_with_time(GTK_WINDOW(dialog_), time);
|
gtk_window_present_with_time(GTK_WINDOW(dialog_), time);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunSaveAsynchronous(const SaveDialogCallback& callback) {
|
void RunSaveAsynchronous(atom::util::Promise promise) {
|
||||||
save_callback_ = callback;
|
save_promise_.reset(new atom::util::Promise(std::move(promise)));
|
||||||
RunAsynchronous();
|
RunAsynchronous();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ class FileChooserDialog {
|
||||||
GtkWidget* preview_;
|
GtkWidget* preview_;
|
||||||
|
|
||||||
Filters filters_;
|
Filters filters_;
|
||||||
SaveDialogCallback save_callback_;
|
std::unique_ptr<atom::util::Promise> save_promise_;
|
||||||
std::unique_ptr<atom::util::Promise> open_promise_;
|
std::unique_ptr<atom::util::Promise> open_promise_;
|
||||||
|
|
||||||
// Callback for when we update the preview for the selection.
|
// Callback for when we update the preview for the selection.
|
||||||
|
@ -184,12 +184,17 @@ class FileChooserDialog {
|
||||||
|
|
||||||
void FileChooserDialog::OnFileDialogResponse(GtkWidget* widget, int response) {
|
void FileChooserDialog::OnFileDialogResponse(GtkWidget* widget, int response) {
|
||||||
gtk_widget_hide(dialog_);
|
gtk_widget_hide(dialog_);
|
||||||
|
if (save_promise_) {
|
||||||
if (!save_callback_.is_null()) {
|
mate::Dictionary dict =
|
||||||
if (response == GTK_RESPONSE_ACCEPT)
|
mate::Dictionary::CreateEmpty(save_promise_->isolate());
|
||||||
save_callback_.Run(true, GetFileName());
|
if (response == GTK_RESPONSE_ACCEPT) {
|
||||||
else
|
dict.Set("canceled", false);
|
||||||
save_callback_.Run(false, base::FilePath());
|
dict.Set("filePath", GetFileName());
|
||||||
|
} else {
|
||||||
|
dict.Set("canceled", true);
|
||||||
|
dict.Set("filePath", base::FilePath());
|
||||||
|
}
|
||||||
|
save_promise_->Resolve(dict.GetHandle());
|
||||||
} else if (open_promise_) {
|
} else if (open_promise_) {
|
||||||
mate::Dictionary dict =
|
mate::Dictionary dict =
|
||||||
mate::Dictionary::CreateEmpty(open_promise_->isolate());
|
mate::Dictionary::CreateEmpty(open_promise_->isolate());
|
||||||
|
@ -285,23 +290,22 @@ void ShowOpenDialog(const DialogSettings& settings,
|
||||||
open_dialog->RunOpenAsynchronous(std::move(promise));
|
open_dialog->RunOpenAsynchronous(std::move(promise));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShowSaveDialog(const DialogSettings& settings, base::FilePath* path) {
|
bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) {
|
||||||
FileChooserDialog save_dialog(GTK_FILE_CHOOSER_ACTION_SAVE, settings);
|
FileChooserDialog save_dialog(GTK_FILE_CHOOSER_ACTION_SAVE, settings);
|
||||||
gtk_widget_show_all(save_dialog.dialog());
|
gtk_widget_show_all(save_dialog.dialog());
|
||||||
int response = gtk_dialog_run(GTK_DIALOG(save_dialog.dialog()));
|
int response = gtk_dialog_run(GTK_DIALOG(save_dialog.dialog()));
|
||||||
if (response == GTK_RESPONSE_ACCEPT) {
|
if (response == GTK_RESPONSE_ACCEPT) {
|
||||||
*path = save_dialog.GetFileName();
|
*path = save_dialog.GetFileName();
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShowSaveDialog(const DialogSettings& settings,
|
void ShowSaveDialog(const DialogSettings& settings,
|
||||||
const SaveDialogCallback& callback) {
|
atom::util::Promise promise) {
|
||||||
FileChooserDialog* save_dialog =
|
FileChooserDialog* save_dialog =
|
||||||
new FileChooserDialog(GTK_FILE_CHOOSER_ACTION_SAVE, settings);
|
new FileChooserDialog(GTK_FILE_CHOOSER_ACTION_SAVE, settings);
|
||||||
save_dialog->RunSaveAsynchronous(callback);
|
save_dialog->RunSaveAsynchronous(std::move(promise));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace file_dialog
|
} // namespace file_dialog
|
||||||
|
|
|
@ -346,7 +346,7 @@ void ShowOpenDialog(const DialogSettings& settings,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShowSaveDialog(const DialogSettings& settings, base::FilePath* path) {
|
bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) {
|
||||||
DCHECK(path);
|
DCHECK(path);
|
||||||
NSSavePanel* dialog = [NSSavePanel savePanel];
|
NSSavePanel* dialog = [NSSavePanel savePanel];
|
||||||
|
|
||||||
|
@ -363,49 +363,56 @@ bool ShowSaveDialog(const DialogSettings& settings, base::FilePath* path) {
|
||||||
void SaveDialogCompletion(int chosen,
|
void SaveDialogCompletion(int chosen,
|
||||||
NSSavePanel* dialog,
|
NSSavePanel* dialog,
|
||||||
bool security_scoped_bookmarks,
|
bool security_scoped_bookmarks,
|
||||||
const SaveDialogCallback& callback) {
|
atom::util::Promise promise) {
|
||||||
|
mate::Dictionary dict = mate::Dictionary::CreateEmpty(promise.isolate());
|
||||||
if (chosen == NSFileHandlingPanelCancelButton) {
|
if (chosen == NSFileHandlingPanelCancelButton) {
|
||||||
|
dict.Set("canceled", true);
|
||||||
|
dict.Set("filePath", base::FilePath());
|
||||||
#if defined(MAS_BUILD)
|
#if defined(MAS_BUILD)
|
||||||
callback.Run(false, base::FilePath(), "");
|
dict.Set("bookmark", "");
|
||||||
#else
|
|
||||||
callback.Run(false, base::FilePath());
|
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
std::string path = base::SysNSStringToUTF8([[dialog URL] path]);
|
std::string path = base::SysNSStringToUTF8([[dialog URL] path]);
|
||||||
|
dict.Set("filePath", base::FilePath(path));
|
||||||
|
dict.Set("canceled", false);
|
||||||
#if defined(MAS_BUILD)
|
#if defined(MAS_BUILD)
|
||||||
std::string bookmark;
|
std::string bookmark;
|
||||||
if (security_scoped_bookmarks) {
|
if (security_scoped_bookmarks) {
|
||||||
bookmark = GetBookmarkDataFromNSURL([dialog URL]);
|
bookmark = GetBookmarkDataFromNSURL([dialog URL]);
|
||||||
|
dict.Set("bookmark", bookmark);
|
||||||
}
|
}
|
||||||
callback.Run(true, base::FilePath(path), bookmark);
|
|
||||||
#else
|
|
||||||
callback.Run(true, base::FilePath(path));
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
promise.Resolve(dict.GetHandle());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShowSaveDialog(const DialogSettings& settings,
|
void ShowSaveDialog(const DialogSettings& settings,
|
||||||
const SaveDialogCallback& c) {
|
atom::util::Promise promise) {
|
||||||
NSSavePanel* dialog = [NSSavePanel savePanel];
|
NSSavePanel* dialog = [NSSavePanel savePanel];
|
||||||
|
|
||||||
SetupDialog(dialog, settings);
|
SetupDialog(dialog, settings);
|
||||||
[dialog setCanSelectHiddenExtension:YES];
|
[dialog setCanSelectHiddenExtension:YES];
|
||||||
|
|
||||||
__block SaveDialogCallback callback = c;
|
// Capture the value of the security_scoped_bookmarks settings flag
|
||||||
|
// and pass it to the completion handler.
|
||||||
bool security_scoped_bookmarks = settings.security_scoped_bookmarks;
|
bool security_scoped_bookmarks = settings.security_scoped_bookmarks;
|
||||||
|
|
||||||
|
__block atom::util::Promise p = std::move(promise);
|
||||||
|
|
||||||
if (!settings.parent_window || !settings.parent_window->GetNativeWindow() ||
|
if (!settings.parent_window || !settings.parent_window->GetNativeWindow() ||
|
||||||
settings.force_detached) {
|
settings.force_detached) {
|
||||||
[dialog beginWithCompletionHandler:^(NSInteger chosen) {
|
[dialog beginWithCompletionHandler:^(NSInteger chosen) {
|
||||||
SaveDialogCompletion(chosen, dialog, security_scoped_bookmarks, callback);
|
SaveDialogCompletion(chosen, dialog, security_scoped_bookmarks,
|
||||||
|
std::move(p));
|
||||||
}];
|
}];
|
||||||
} else {
|
} else {
|
||||||
NSWindow* window =
|
NSWindow* window =
|
||||||
settings.parent_window->GetNativeWindow().GetNativeNSWindow();
|
settings.parent_window->GetNativeWindow().GetNativeNSWindow();
|
||||||
[dialog beginSheetModalForWindow:window
|
[dialog
|
||||||
|
beginSheetModalForWindow:window
|
||||||
completionHandler:^(NSInteger chosen) {
|
completionHandler:^(NSInteger chosen) {
|
||||||
SaveDialogCompletion(chosen, dialog,
|
SaveDialogCompletion(chosen, dialog, security_scoped_bookmarks,
|
||||||
security_scoped_bookmarks, callback);
|
std::move(p));
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,13 +101,23 @@ void RunOpenDialogInNewThread(const RunState& run_state,
|
||||||
run_state.ui_task_runner->DeleteSoon(FROM_HERE, run_state.dialog_thread);
|
run_state.ui_task_runner->DeleteSoon(FROM_HERE, run_state.dialog_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OnSaveDialogDone(atom::util::Promise promise,
|
||||||
|
bool canceled,
|
||||||
|
const base::FilePath path) {
|
||||||
|
mate::Dictionary dict = mate::Dictionary::CreateEmpty(promise.isolate());
|
||||||
|
dict.Set("canceled", canceled);
|
||||||
|
dict.Set("filePath", path);
|
||||||
|
promise.Resolve(dict.GetHandle());
|
||||||
|
}
|
||||||
|
|
||||||
void RunSaveDialogInNewThread(const RunState& run_state,
|
void RunSaveDialogInNewThread(const RunState& run_state,
|
||||||
const DialogSettings& settings,
|
const DialogSettings& settings,
|
||||||
const SaveDialogCallback& callback) {
|
atom::util::Promise promise) {
|
||||||
base::FilePath path;
|
base::FilePath path;
|
||||||
bool result = ShowSaveDialog(settings, &path);
|
bool result = ShowSaveDialogSync(settings, &path);
|
||||||
run_state.ui_task_runner->PostTask(FROM_HERE,
|
run_state.ui_task_runner->PostTask(
|
||||||
base::Bind(callback, result, path));
|
FROM_HERE,
|
||||||
|
base::BindOnce(&OnSaveDialogDone, std::move(promise), result, path));
|
||||||
run_state.ui_task_runner->DeleteSoon(FROM_HERE, run_state.dialog_thread);
|
run_state.ui_task_runner->DeleteSoon(FROM_HERE, run_state.dialog_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,7 +285,7 @@ void ShowOpenDialog(const DialogSettings& settings,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShowSaveDialog(const DialogSettings& settings, base::FilePath* path) {
|
bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) {
|
||||||
ATL::CComPtr<IFileSaveDialog> file_save_dialog;
|
ATL::CComPtr<IFileSaveDialog> file_save_dialog;
|
||||||
HRESULT hr = file_save_dialog.CoCreateInstance(CLSID_FileSaveDialog);
|
HRESULT hr = file_save_dialog.CoCreateInstance(CLSID_FileSaveDialog);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
|
@ -306,16 +316,18 @@ bool ShowSaveDialog(const DialogSettings& settings, base::FilePath* path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShowSaveDialog(const DialogSettings& settings,
|
void ShowSaveDialog(const DialogSettings& settings,
|
||||||
const SaveDialogCallback& callback) {
|
atom::util::Promise promise) {
|
||||||
RunState run_state;
|
RunState run_state;
|
||||||
if (!CreateDialogThread(&run_state)) {
|
if (!CreateDialogThread(&run_state)) {
|
||||||
callback.Run(false, base::FilePath());
|
mate::Dictionary dict = mate::Dictionary::CreateEmpty(promise.isolate());
|
||||||
return;
|
dict.Set("canceled", false);
|
||||||
}
|
dict.Set("filePath", base::FilePath());
|
||||||
|
promise.Resolve(dict.GetHandle());
|
||||||
|
} else {
|
||||||
run_state.dialog_thread->task_runner()->PostTask(
|
run_state.dialog_thread->task_runner()->PostTask(
|
||||||
FROM_HERE,
|
FROM_HERE, base::BindOnce(&RunSaveDialogInNewThread, run_state,
|
||||||
base::Bind(&RunSaveDialogInNewThread, run_state, settings, callback));
|
settings, std::move(promise)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace file_dialog
|
} // namespace file_dialog
|
||||||
|
|
|
@ -56,8 +56,16 @@ class FileSelectHelper : public base::RefCounted<FileSelectHelper>,
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShowSaveDialog(const file_dialog::DialogSettings& settings) {
|
void ShowSaveDialog(const file_dialog::DialogSettings& settings) {
|
||||||
auto callback = base::Bind(&FileSelectHelper::OnSaveDialogDone, this);
|
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||||
file_dialog::ShowSaveDialog(settings, callback);
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
||||||
|
atom::util::Promise promise(isolate);
|
||||||
|
v8::Local<v8::Promise> handle = promise.GetHandle();
|
||||||
|
|
||||||
|
file_dialog::ShowSaveDialog(settings, std::move(promise));
|
||||||
|
ignore_result(handle->Then(
|
||||||
|
context,
|
||||||
|
v8::Local<v8::Function>::Cast(mate::ConvertToV8(
|
||||||
|
isolate, base::Bind(&FileSelectHelper::OnSaveDialogDone, this)))));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -155,7 +155,7 @@ dialog.showOpenDialog(mainWindow, {
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
### `dialog.showSaveDialog([browserWindow, ]options[, callback])`
|
### `dialog.showSaveDialog([browserWindow, ]options)`
|
||||||
|
|
||||||
* `browserWindow` [BrowserWindow](browser-window.md) (optional)
|
* `browserWindow` [BrowserWindow](browser-window.md) (optional)
|
||||||
* `options` Object
|
* `options` Object
|
||||||
|
@ -171,22 +171,42 @@ dialog.showOpenDialog(mainWindow, {
|
||||||
* `showsTagField` Boolean (optional) _macOS_ - Show the tags input box,
|
* `showsTagField` Boolean (optional) _macOS_ - Show the tags input box,
|
||||||
defaults to `true`.
|
defaults to `true`.
|
||||||
* `securityScopedBookmarks` Boolean (optional) _macOS_ _mas_ - Create a [security scoped bookmark](https://developer.apple.com/library/content/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW16) when packaged for the Mac App Store. If this option is enabled and the file doesn't already exist a blank file will be created at the chosen path.
|
* `securityScopedBookmarks` Boolean (optional) _macOS_ _mas_ - Create a [security scoped bookmark](https://developer.apple.com/library/content/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW16) when packaged for the Mac App Store. If this option is enabled and the file doesn't already exist a blank file will be created at the chosen path.
|
||||||
* `callback` Function (optional)
|
|
||||||
* `filename` String (optional) If the dialog is cancelled this will be `undefined`.
|
|
||||||
* `bookmark` String (optional) _macOS_ _mas_ - Base64 encoded string which contains the security scoped bookmark data for the saved file. `securityScopedBookmarks` must be enabled for this to be present.
|
|
||||||
|
|
||||||
Returns `String | undefined`, the path of the file chosen by the user,
|
Returns `String | undefined`, the path of the file chosen by the user; if the dialog is cancelled it returns `undefined`.
|
||||||
if a callback is provided or the dialog is cancelled it returns `undefined`.
|
|
||||||
|
|
||||||
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
|
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
|
||||||
|
|
||||||
The `filters` specifies an array of file types that can be displayed, see
|
The `filters` specifies an array of file types that can be displayed, see
|
||||||
`dialog.showOpenDialog` for an example.
|
`dialog.showOpenDialog` for an example.
|
||||||
|
|
||||||
If a `callback` is passed, the API call will be asynchronous and the result
|
### `dialog.showSaveDialog([browserWindow, ]options)`
|
||||||
will be passed via `callback(filename)`.
|
|
||||||
|
|
||||||
**Note:** On macOS, using the `callback` is recommended to avoid issues when
|
* `browserWindow` [BrowserWindow](browser-window.md) (optional)
|
||||||
|
* `options` Object
|
||||||
|
* `title` String (optional)
|
||||||
|
* `defaultPath` String (optional) - Absolute directory path, absolute file
|
||||||
|
path, or file name to use by default.
|
||||||
|
* `buttonLabel` String (optional) - Custom label for the confirmation button, when
|
||||||
|
left empty the default label will be used.
|
||||||
|
* `filters` [FileFilter[]](structures/file-filter.md) (optional)
|
||||||
|
* `message` String (optional) _macOS_ - Message to display above text fields.
|
||||||
|
* `nameFieldLabel` String (optional) _macOS_ - Custom label for the text
|
||||||
|
displayed in front of the filename text field.
|
||||||
|
* `showsTagField` Boolean (optional) _macOS_ - Show the tags input box,
|
||||||
|
defaults to `true`.
|
||||||
|
* `securityScopedBookmarks` Boolean (optional) _macOS_ _mas_ - Create a [security scoped bookmark](https://developer.apple.com/library/content/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW16) when packaged for the Mac App Store. If this option is enabled and the file doesn't already exist a blank file will be created at the chosen path.
|
||||||
|
|
||||||
|
Returns `Promise<Object>` - Resolve with an object containing the following:
|
||||||
|
* `canceled` Boolean - whether or not the dialog was canceled.
|
||||||
|
* `filePath` String (optional) If the dialog is canceled this will be `undefined`.
|
||||||
|
* `bookmark` String (optional) _macOS_ _mas_ - Base64 encoded string which contains the security scoped bookmark data for the saved file. `securityScopedBookmarks` must be enabled for this to be present.
|
||||||
|
|
||||||
|
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
|
||||||
|
|
||||||
|
The `filters` specifies an array of file types that can be displayed, see
|
||||||
|
`dialog.showOpenDialog` for an example.
|
||||||
|
|
||||||
|
**Note:** On macOS, using the asynchronous version is recommended to avoid issues when
|
||||||
expanding and collapsing the dialog.
|
expanding and collapsing the dialog.
|
||||||
|
|
||||||
### `dialog.showMessageBox([browserWindow, ]options[, callback])`
|
### `dialog.showMessageBox([browserWindow, ]options[, callback])`
|
||||||
|
|
|
@ -9,7 +9,6 @@ When a majority of affected functions are migrated, this flag will be enabled by
|
||||||
### Candidate Functions
|
### Candidate Functions
|
||||||
|
|
||||||
- [app.importCertificate(options, callback)](https://github.com/electron/electron/blob/master/docs/api/app.md#importCertificate)
|
- [app.importCertificate(options, callback)](https://github.com/electron/electron/blob/master/docs/api/app.md#importCertificate)
|
||||||
- [dialog.showSaveDialog([browserWindow, ]options[, callback])](https://github.com/electron/electron/blob/master/docs/api/dialog.md#showSaveDialog)
|
|
||||||
- [dialog.showMessageBox([browserWindow, ]options[, callback])](https://github.com/electron/electron/blob/master/docs/api/dialog.md#showMessageBox)
|
- [dialog.showMessageBox([browserWindow, ]options[, callback])](https://github.com/electron/electron/blob/master/docs/api/dialog.md#showMessageBox)
|
||||||
- [dialog.showCertificateTrustDialog([browserWindow, ]options, callback)](https://github.com/electron/electron/blob/master/docs/api/dialog.md#showCertificateTrustDialog)
|
- [dialog.showCertificateTrustDialog([browserWindow, ]options, callback)](https://github.com/electron/electron/blob/master/docs/api/dialog.md#showCertificateTrustDialog)
|
||||||
- [inAppPurchase.purchaseProduct(productID, quantity, callback)](https://github.com/electron/electron/blob/master/docs/api/in-app-purchase.md#purchaseProduct)
|
- [inAppPurchase.purchaseProduct(productID, quantity, callback)](https://github.com/electron/electron/blob/master/docs/api/in-app-purchase.md#purchaseProduct)
|
||||||
|
@ -44,8 +43,9 @@ When a majority of affected functions are migrated, this flag will be enabled by
|
||||||
- [cookies.set(details, callback)](https://github.com/electron/electron/blob/master/docs/api/cookies.md#set)
|
- [cookies.set(details, callback)](https://github.com/electron/electron/blob/master/docs/api/cookies.md#set)
|
||||||
- [debugger.sendCommand(method[, commandParams, callback])](https://github.com/electron/electron/blob/master/docs/api/debugger.md#sendCommand)
|
- [debugger.sendCommand(method[, commandParams, callback])](https://github.com/electron/electron/blob/master/docs/api/debugger.md#sendCommand)
|
||||||
- [desktopCapturer.getSources(options, callback)](https://github.com/electron/electron/blob/master/docs/api/desktop-capturer.md#getSources)
|
- [desktopCapturer.getSources(options, callback)](https://github.com/electron/electron/blob/master/docs/api/desktop-capturer.md#getSources)
|
||||||
- [netLog.stopLogging([callback])](https://github.com/electron/electron/blob/master/docs/api/net-log.md#stopLogging)
|
|
||||||
- [dialog.showOpenDialog([browserWindow, ]options[, callback])](https://github.com/electron/electron/blob/master/docs/api/dialog.md#showOpenDialog)
|
- [dialog.showOpenDialog([browserWindow, ]options[, callback])](https://github.com/electron/electron/blob/master/docs/api/dialog.md#showOpenDialog)
|
||||||
|
- [dialog.showSaveDialog([browserWindow, ]options[, callback])](https://github.com/electron/electron/blob/master/docs/api/dialog.md#showSaveDialog)
|
||||||
|
- [netLog.stopLogging([callback])](https://github.com/electron/electron/blob/master/docs/api/net-log.md#stopLogging)
|
||||||
- [protocol.isProtocolHandled(scheme, callback)](https://github.com/electron/electron/blob/master/docs/api/protocol.md#isProtocolHandled)
|
- [protocol.isProtocolHandled(scheme, callback)](https://github.com/electron/electron/blob/master/docs/api/protocol.md#isProtocolHandled)
|
||||||
- [shell.openExternal(url[, options, callback])](https://github.com/electron/electron/blob/master/docs/api/shell.md#openExternal)
|
- [shell.openExternal(url[, options, callback])](https://github.com/electron/electron/blob/master/docs/api/shell.md#openExternal)
|
||||||
- [webviewTag.capturePage([rect, ]callback)](https://github.com/electron/electron/blob/master/docs/api/webview-tag.md#capturePage)
|
- [webviewTag.capturePage([rect, ]callback)](https://github.com/electron/electron/blob/master/docs/api/webview-tag.md#capturePage)
|
||||||
|
|
|
@ -70,6 +70,33 @@ const checkAppInitialized = function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const saveDialog = (sync, window, options) => {
|
||||||
|
checkAppInitialized()
|
||||||
|
|
||||||
|
if (window.constructor !== BrowserWindow) options = window
|
||||||
|
if (options == null) options = { title: 'Save' }
|
||||||
|
|
||||||
|
const {
|
||||||
|
buttonLabel = '',
|
||||||
|
defaultPath = '',
|
||||||
|
filters = [],
|
||||||
|
title = '',
|
||||||
|
message = '',
|
||||||
|
securityScopedBookmarks = false,
|
||||||
|
nameFieldLabel = '',
|
||||||
|
showsTagField = true
|
||||||
|
} = options
|
||||||
|
|
||||||
|
if (typeof title !== 'string') throw new TypeError('Title must be a string')
|
||||||
|
if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string')
|
||||||
|
if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string')
|
||||||
|
if (typeof message !== 'string') throw new TypeError('Message must be a string')
|
||||||
|
if (typeof nameFieldLabel !== 'string') throw new TypeError('Name field label must be a string')
|
||||||
|
|
||||||
|
const settings = { buttonLabel, defaultPath, filters, title, message, securityScopedBookmarks, nameFieldLabel, showsTagField, window }
|
||||||
|
return (sync) ? binding.showSaveDialogSync(settings) : binding.showSaveDialog(settings)
|
||||||
|
}
|
||||||
|
|
||||||
const openDialog = (sync, window, options) => {
|
const openDialog = (sync, window, options) => {
|
||||||
checkAppInitialized()
|
checkAppInitialized()
|
||||||
|
|
||||||
|
@ -115,65 +142,17 @@ module.exports = {
|
||||||
showOpenDialog: function (window, options) {
|
showOpenDialog: function (window, options) {
|
||||||
return openDialog(false, window, options)
|
return openDialog(false, window, options)
|
||||||
},
|
},
|
||||||
|
|
||||||
showOpenDialogSync: function (window, options) {
|
showOpenDialogSync: function (window, options) {
|
||||||
return openDialog(true, window, options)
|
return openDialog(true, window, options)
|
||||||
},
|
},
|
||||||
showSaveDialog: function (...args) {
|
|
||||||
checkAppInitialized()
|
|
||||||
|
|
||||||
let [window, options, callback] = parseArgs(...args)
|
showSaveDialog: function (window, options) {
|
||||||
|
return saveDialog(false, window, options)
|
||||||
|
},
|
||||||
|
|
||||||
if (options == null) {
|
showSaveDialogSync: function (window, options) {
|
||||||
options = {
|
return saveDialog(true, window, options)
|
||||||
title: 'Save'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let { buttonLabel, defaultPath, filters, title, message, securityScopedBookmarks = false, nameFieldLabel, showsTagField } = options
|
|
||||||
|
|
||||||
if (title == null) {
|
|
||||||
title = ''
|
|
||||||
} else if (typeof title !== 'string') {
|
|
||||||
throw new TypeError('Title must be a string')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buttonLabel == null) {
|
|
||||||
buttonLabel = ''
|
|
||||||
} else if (typeof buttonLabel !== 'string') {
|
|
||||||
throw new TypeError('Button label must be a string')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (defaultPath == null) {
|
|
||||||
defaultPath = ''
|
|
||||||
} else if (typeof defaultPath !== 'string') {
|
|
||||||
throw new TypeError('Default path must be a string')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters == null) {
|
|
||||||
filters = []
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message == null) {
|
|
||||||
message = ''
|
|
||||||
} else if (typeof message !== 'string') {
|
|
||||||
throw new TypeError('Message must be a string')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nameFieldLabel == null) {
|
|
||||||
nameFieldLabel = ''
|
|
||||||
} else if (typeof nameFieldLabel !== 'string') {
|
|
||||||
throw new TypeError('Name field label must be a string')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showsTagField == null) {
|
|
||||||
showsTagField = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const wrappedCallback = typeof callback === 'function' ? function (success, result, bookmarkData) {
|
|
||||||
return success ? callback(result, bookmarkData) : callback()
|
|
||||||
} : null
|
|
||||||
const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, nameFieldLabel, showsTagField, window }
|
|
||||||
return binding.showSaveDialog(settings, wrappedCallback)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
showMessageBox: function (...args) {
|
showMessageBox: function (...args) {
|
||||||
|
@ -291,6 +270,7 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.showOpenDialog = deprecate.promisify(module.exports.showOpenDialog)
|
module.exports.showOpenDialog = deprecate.promisify(module.exports.showOpenDialog)
|
||||||
|
module.exports.showSaveDialog = deprecate.promisify(module.exports.showSaveDialog)
|
||||||
|
|
||||||
// Mark standard asynchronous functions.
|
// Mark standard asynchronous functions.
|
||||||
v8Util.setHiddenValue(module.exports.showMessageBox, 'asynchronous', true)
|
v8Util.setHiddenValue(module.exports.showMessageBox, 'asynchronous', true)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue