feat: promisify dialog.showOpenDialog() (#16973)

* feat: promisify dialog.showOpenDialog()

* address feedback from review

* address feedback from review
This commit is contained in:
Shelley Vohr 2019-03-05 05:54:48 -08:00 committed by GitHub
parent 7936237677
commit e05985145b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 235 additions and 154 deletions

View file

@ -16,6 +16,7 @@
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/native_mate_converters/image_converter.h"
#include "atom/common/native_mate_converters/net_converter.h"
#include "atom/common/promise_util.h"
#include "native_mate/dictionary.h"
#include "atom/common/node_includes.h"
@ -51,18 +52,20 @@ void ShowMessageBox(int type,
}
}
void ShowOpenDialog(const file_dialog::DialogSettings& settings,
mate::Arguments* args) {
v8::Local<v8::Value> peek = args->PeekNext();
file_dialog::OpenDialogCallback callback;
if (mate::Converter<file_dialog::OpenDialogCallback>::FromV8(
args->isolate(), peek, &callback)) {
file_dialog::ShowOpenDialog(settings, callback);
} else {
std::vector<base::FilePath> paths;
if (file_dialog::ShowOpenDialog(settings, &paths))
args->Return(paths);
}
void ShowOpenDialogSync(const file_dialog::DialogSettings& settings,
mate::Arguments* args) {
std::vector<base::FilePath> paths;
if (file_dialog::ShowOpenDialogSync(settings, &paths))
args->Return(paths);
}
v8::Local<v8::Promise> ShowOpenDialog(
const file_dialog::DialogSettings& settings,
mate::Arguments* args) {
atom::util::Promise promise(args->isolate());
v8::Local<v8::Promise> handle = promise.GetHandle();
file_dialog::ShowOpenDialog(settings, std::move(promise));
return handle;
}
void ShowSaveDialog(const file_dialog::DialogSettings& settings,
@ -86,6 +89,7 @@ void Initialize(v8::Local<v8::Object> exports,
mate::Dictionary dict(context->GetIsolate(), exports);
dict.SetMethod("showMessageBox", &ShowMessageBox);
dict.SetMethod("showErrorBox", &atom::ShowErrorBox);
dict.SetMethod("showOpenDialogSync", &ShowOpenDialogSync);
dict.SetMethod("showOpenDialog", &ShowOpenDialog);
dict.SetMethod("showSaveDialog", &ShowSaveDialog);
#if defined(OS_MACOSX) || defined(OS_WIN)

View file

@ -454,7 +454,7 @@ void CommonWebContentsDelegate::DevToolsAddFileSystem(
settings.parent_window = owner_window();
settings.force_detached = offscreen_;
settings.properties = file_dialog::FILE_DIALOG_OPEN_DIRECTORY;
if (!file_dialog::ShowOpenDialog(settings, &paths))
if (!file_dialog::ShowOpenDialogSync(settings, &paths))
return;
path = paths[0];

View file

@ -9,8 +9,11 @@
#include <utility>
#include <vector>
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/promise_util.h"
#include "base/callback_forward.h"
#include "base/files/file_path.h"
#include "native_mate/dictionary.h"
namespace atom {
class NativeWindow;
@ -34,20 +37,11 @@ enum FileDialogProperty {
};
#if defined(MAS_BUILD)
typedef base::Callback<void(bool result,
const std::vector<base::FilePath>& paths,
const std::vector<std::string>& bookmarkData)>
OpenDialogCallback;
typedef base::Callback<void(bool result,
const base::FilePath& path,
const std::string& bookmarkData)>
SaveDialogCallback;
#else
typedef base::Callback<void(bool result,
const std::vector<base::FilePath>& paths)>
OpenDialogCallback;
typedef base::Callback<void(bool result, const base::FilePath& path)>
SaveDialogCallback;
#endif
@ -70,11 +64,11 @@ struct DialogSettings {
~DialogSettings();
};
bool ShowOpenDialog(const DialogSettings& settings,
std::vector<base::FilePath>* paths);
bool ShowOpenDialogSync(const DialogSettings& settings,
std::vector<base::FilePath>* paths);
void ShowOpenDialog(const DialogSettings& settings,
const OpenDialogCallback& callback);
atom::util::Promise promise);
bool ShowSaveDialog(const DialogSettings& settings, base::FilePath* path);

View file

@ -131,8 +131,8 @@ class FileChooserDialog {
RunAsynchronous();
}
void RunOpenAsynchronous(const OpenDialogCallback& callback) {
open_callback_ = callback;
void RunOpenAsynchronous(atom::util::Promise promise) {
open_promise_.reset(new atom::util::Promise(std::move(promise)));
RunAsynchronous();
}
@ -174,7 +174,7 @@ class FileChooserDialog {
Filters filters_;
SaveDialogCallback save_callback_;
OpenDialogCallback open_callback_;
std::unique_ptr<atom::util::Promise> open_promise_;
// Callback for when we update the preview for the selection.
CHROMEG_CALLBACK_0(FileChooserDialog, void, OnUpdatePreview, GtkWidget*);
@ -190,11 +190,17 @@ void FileChooserDialog::OnFileDialogResponse(GtkWidget* widget, int response) {
save_callback_.Run(true, GetFileName());
else
save_callback_.Run(false, base::FilePath());
} else if (!open_callback_.is_null()) {
if (response == GTK_RESPONSE_ACCEPT)
open_callback_.Run(true, GetFileNames());
else
open_callback_.Run(false, std::vector<base::FilePath>());
} else if (open_promise_) {
mate::Dictionary dict =
mate::Dictionary::CreateEmpty(open_promise_->isolate());
if (response == GTK_RESPONSE_ACCEPT) {
dict.Set("canceled", false);
dict.Set("filePaths", GetFileNames());
} else {
dict.Set("canceled", true);
dict.Set("filePaths", std::vector<base::FilePath>());
}
open_promise_->Resolve(dict.GetHandle());
}
delete this;
}
@ -252,8 +258,8 @@ void FileChooserDialog::OnUpdatePreview(GtkWidget* chooser) {
} // namespace
bool ShowOpenDialog(const DialogSettings& settings,
std::vector<base::FilePath>* paths) {
bool ShowOpenDialogSync(const DialogSettings& settings,
std::vector<base::FilePath>* paths) {
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
if (settings.properties & FILE_DIALOG_OPEN_DIRECTORY)
action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
@ -265,19 +271,18 @@ bool ShowOpenDialog(const DialogSettings& settings,
if (response == GTK_RESPONSE_ACCEPT) {
*paths = open_dialog.GetFileNames();
return true;
} else {
return false;
}
return false;
}
void ShowOpenDialog(const DialogSettings& settings,
const OpenDialogCallback& callback) {
atom::util::Promise promise) {
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
if (settings.properties & FILE_DIALOG_OPEN_DIRECTORY)
action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
FileChooserDialog* open_dialog = new FileChooserDialog(action, settings);
open_dialog->SetupProperties(settings.properties);
open_dialog->RunOpenAsynchronous(callback);
open_dialog->RunOpenAsynchronous(std::move(promise));
}
bool ShowSaveDialog(const DialogSettings& settings, base::FilePath* path) {

View file

@ -268,8 +268,8 @@ void ReadDialogPaths(NSOpenPanel* dialog, std::vector<base::FilePath>* paths) {
} // namespace
bool ShowOpenDialog(const DialogSettings& settings,
std::vector<base::FilePath>* paths) {
bool ShowOpenDialogSync(const DialogSettings& settings,
std::vector<base::FilePath>* paths) {
DCHECK(paths);
NSOpenPanel* dialog = [NSOpenPanel openPanel];
@ -287,58 +287,62 @@ bool ShowOpenDialog(const DialogSettings& settings,
void OpenDialogCompletion(int chosen,
NSOpenPanel* dialog,
bool security_scoped_bookmarks,
const OpenDialogCallback& callback) {
atom::util::Promise promise) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(promise.isolate());
if (chosen == NSFileHandlingPanelCancelButton) {
dict.Set("canceled", true);
dict.Set("filePaths", std::vector<base::FilePath>());
#if defined(MAS_BUILD)
callback.Run(false, std::vector<base::FilePath>(),
std::vector<std::string>());
#else
callback.Run(false, std::vector<base::FilePath>());
dict.Set("bookmarks", std::vector<std::string>());
#endif
promise.Resolve(dict.GetHandle());
} else {
std::vector<base::FilePath> paths;
dict.Set("canceled", false);
#if defined(MAS_BUILD)
std::vector<std::string> bookmarks;
if (security_scoped_bookmarks) {
if (security_scoped_bookmarks)
ReadDialogPathsWithBookmarks(dialog, &paths, &bookmarks);
} else {
else
ReadDialogPaths(dialog, &paths);
}
callback.Run(true, paths, bookmarks);
dict.Set("filePaths", paths);
dict.Set("bookmarks", bookmarks);
#else
ReadDialogPaths(dialog, &paths);
callback.Run(true, paths);
dict.Set("filePaths", paths);
#endif
promise.Resolve(dict.GetHandle());
}
}
void ShowOpenDialog(const DialogSettings& settings,
const OpenDialogCallback& c) {
atom::util::Promise promise) {
NSOpenPanel* dialog = [NSOpenPanel openPanel];
SetupDialog(dialog, settings);
SetupDialogForProperties(dialog, settings.properties);
// Duplicate the callback object here since c is a reference and gcd would
// only store the pointer, by duplication we can force gcd to store a copy.
__block OpenDialogCallback 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;
__block atom::util::Promise p = std::move(promise);
if (!settings.parent_window || !settings.parent_window->GetNativeWindow() ||
settings.force_detached) {
[dialog beginWithCompletionHandler:^(NSInteger chosen) {
OpenDialogCompletion(chosen, dialog, security_scoped_bookmarks, callback);
OpenDialogCompletion(chosen, dialog, security_scoped_bookmarks,
std::move(p));
}];
} else {
NSWindow* window =
settings.parent_window->GetNativeWindow().GetNativeNSWindow();
[dialog beginSheetModalForWindow:window
completionHandler:^(NSInteger chosen) {
OpenDialogCompletion(chosen, dialog,
security_scoped_bookmarks, callback);
}];
[dialog
beginSheetModalForWindow:window
completionHandler:^(NSInteger chosen) {
OpenDialogCompletion(chosen, dialog, security_scoped_bookmarks,
std::move(p));
}];
}
}

View file

@ -81,13 +81,23 @@ bool CreateDialogThread(RunState* run_state) {
return true;
}
void OnDialogOpened(atom::util::Promise promise,
bool canceled,
std::vector<base::FilePath> paths) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(promise.isolate());
dict.Set("canceled", canceled);
dict.Set("filePaths", paths);
promise.Resolve(dict.GetHandle());
}
void RunOpenDialogInNewThread(const RunState& run_state,
const DialogSettings& settings,
const OpenDialogCallback& callback) {
atom::util::Promise promise) {
std::vector<base::FilePath> paths;
bool result = ShowOpenDialog(settings, &paths);
run_state.ui_task_runner->PostTask(FROM_HERE,
base::Bind(callback, result, paths));
bool result = ShowOpenDialogSync(settings, &paths);
run_state.ui_task_runner->PostTask(
FROM_HERE,
base::BindOnce(&OnDialogOpened, std::move(promise), result, paths));
run_state.ui_task_runner->DeleteSoon(FROM_HERE, run_state.dialog_thread);
}
@ -197,8 +207,8 @@ static void ApplySettings(IFileDialog* dialog, const DialogSettings& settings) {
}
}
bool ShowOpenDialog(const DialogSettings& settings,
std::vector<base::FilePath>* paths) {
bool ShowOpenDialogSync(const DialogSettings& settings,
std::vector<base::FilePath>* paths) {
ATL::CComPtr<IFileOpenDialog> file_open_dialog;
HRESULT hr = file_open_dialog.CoCreateInstance(CLSID_FileOpenDialog);
@ -251,16 +261,18 @@ bool ShowOpenDialog(const DialogSettings& settings,
}
void ShowOpenDialog(const DialogSettings& settings,
const OpenDialogCallback& callback) {
atom::util::Promise promise) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(promise.isolate());
RunState run_state;
if (!CreateDialogThread(&run_state)) {
callback.Run(false, std::vector<base::FilePath>());
return;
dict.Set("canceled", true);
dict.Set("filePaths", std::vector<base::FilePath>());
promise.Resolve(dict.GetHandle());
} else {
run_state.dialog_thread->task_runner()->PostTask(
FROM_HERE, base::BindOnce(&RunOpenDialogInNewThread, run_state,
settings, std::move(promise)));
}
run_state.dialog_thread->task_runner()->PostTask(
FROM_HERE,
base::Bind(&RunOpenDialogInNewThread, run_state, settings, callback));
}
bool ShowSaveDialog(const DialogSettings& settings, base::FilePath* path) {

View file

@ -11,6 +11,7 @@
#include "atom/browser/atom_browser_context.h"
#include "atom/browser/native_window.h"
#include "atom/browser/ui/file_dialog.h"
#include "atom/common/native_mate_converters/callback.h"
#include "base/bind.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
@ -46,8 +47,12 @@ class FileSelectHelper : public base::RefCounted<FileSelectHelper>,
}
void ShowOpenDialog(const file_dialog::DialogSettings& settings) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
atom::util::Promise promise(isolate);
file_dialog::ShowOpenDialog(settings, std::move(promise));
auto callback = base::Bind(&FileSelectHelper::OnOpenDialogDone, this);
file_dialog::ShowOpenDialog(settings, callback);
ignore_result(promise.Then(callback));
}
void ShowSaveDialog(const file_dialog::DialogSettings& settings) {