feat: promisify dialog.showOpenDialog() (#16973)
* feat: promisify dialog.showOpenDialog() * address feedback from review * address feedback from review
This commit is contained in:
parent
7936237677
commit
e05985145b
12 changed files with 235 additions and 154 deletions
|
@ -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)
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -23,7 +23,67 @@ console.log(dialog)
|
|||
|
||||
The `dialog` module has the following methods:
|
||||
|
||||
### `dialog.showOpenDialog([browserWindow, ]options[, callback])`
|
||||
### `dialog.showOpenDialogSync([browserWindow, ]options)`
|
||||
|
||||
* `browserWindow` [BrowserWindow](browser-window.md) (optional)
|
||||
* `options` Object
|
||||
* `title` String (optional)
|
||||
* `defaultPath` String (optional)
|
||||
* `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)
|
||||
* `properties` String[] (optional) - Contains which features the dialog should
|
||||
use. The following values are supported:
|
||||
* `openFile` - Allow files to be selected.
|
||||
* `openDirectory` - Allow directories to be selected.
|
||||
* `multiSelections` - Allow multiple paths to be selected.
|
||||
* `showHiddenFiles` - Show hidden files in dialog.
|
||||
* `createDirectory` _macOS_ - Allow creating new directories from dialog.
|
||||
* `promptToCreate` _Windows_ - Prompt for creation if the file path entered
|
||||
in the dialog does not exist. This does not actually create the file at
|
||||
the path but allows non-existent paths to be returned that should be
|
||||
created by the application.
|
||||
* `noResolveAliases` _macOS_ - Disable the automatic alias (symlink) path
|
||||
resolution. Selected aliases will now return the alias path instead of
|
||||
their target path.
|
||||
* `treatPackageAsDirectory` _macOS_ - Treat packages, such as `.app` folders,
|
||||
as a directory instead of a file.
|
||||
* `message` String (optional) _macOS_ - Message to display above input
|
||||
boxes.
|
||||
* `securityScopedBookmarks` Boolean (optional) _masOS_ _mas_ - Create [security scoped bookmarks](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.
|
||||
|
||||
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 or
|
||||
selected when you want to limit the user to a specific type. For example:
|
||||
|
||||
```javascript
|
||||
{
|
||||
filters: [
|
||||
{ name: 'Images', extensions: ['jpg', 'png', 'gif'] },
|
||||
{ name: 'Movies', extensions: ['mkv', 'avi', 'mp4'] },
|
||||
{ name: 'Custom File Type', extensions: ['as'] },
|
||||
{ name: 'All Files', extensions: ['*'] }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The `extensions` array should contain extensions without wildcards or dots (e.g.
|
||||
`'png'` is good but `'.png'` and `'*.png'` are bad). To show all files, use the
|
||||
`'*'` wildcard (no other wildcard is supported).
|
||||
|
||||
**Note:** On Windows and Linux an open dialog can not be both a file selector
|
||||
and a directory selector, so if you set `properties` to
|
||||
`['openFile', 'openDirectory']` on these platforms, a directory selector will be
|
||||
shown.
|
||||
|
||||
```js
|
||||
dialog.showOpenDialogSync(mainWindow, {
|
||||
properties: ['openFile', 'openDirectory']
|
||||
})
|
||||
```
|
||||
|
||||
### `dialog.showOpenDialog([browserWindow, ]options)`
|
||||
|
||||
* `browserWindow` [BrowserWindow](browser-window.md) (optional)
|
||||
* `options` Object
|
||||
|
@ -52,11 +112,12 @@ The `dialog` module has the following methods:
|
|||
boxes.
|
||||
* `securityScopedBookmarks` Boolean (optional) _masOS_ _mas_ - Create [security scoped bookmarks](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.
|
||||
* `callback` Function (optional)
|
||||
* `filePaths` String[] (optional) - An array of file paths chosen by the user. If the dialog is cancelled this will be `undefined`.
|
||||
* `bookmarks` String[] (optional) _macOS_ _mas_ - An array matching the `filePaths` array of base64 encoded strings which contains security scoped bookmark data. `securityScopedBookmarks` must be enabled for this to be populated.
|
||||
|
||||
Returns `String[] | undefined`, an array of file paths chosen by the user,
|
||||
if the callback is provided it returns `undefined`.
|
||||
Returns `Promise<Object>` - Resolve wih an object containing the following:
|
||||
|
||||
* `canceled` - Boolean - whether or not the dialog was canceled.
|
||||
* `filePaths` String[] (optional) - An array of file paths chosen by the user. If the dialog is cancelled this will be an empty array.
|
||||
* `bookmarks` String[] (optional) _macOS_ _mas_ - An array matching the `filePaths` array of base64 encoded strings which contains security scoped bookmark data. `securityScopedBookmarks` must be enabled for this to be populated.
|
||||
|
||||
The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal.
|
||||
|
||||
|
@ -78,14 +139,22 @@ The `extensions` array should contain extensions without wildcards or dots (e.g.
|
|||
`'png'` is good but `'.png'` and `'*.png'` are bad). To show all files, use the
|
||||
`'*'` wildcard (no other wildcard is supported).
|
||||
|
||||
If a `callback` is passed, the API call will be asynchronous and the result
|
||||
will be passed via `callback(filenames)`.
|
||||
|
||||
**Note:** On Windows and Linux an open dialog can not be both a file selector
|
||||
and a directory selector, so if you set `properties` to
|
||||
`['openFile', 'openDirectory']` on these platforms, a directory selector will be
|
||||
shown.
|
||||
|
||||
```js
|
||||
dialog.showOpenDialog(mainWindow, {
|
||||
properties: ['openFile', 'openDirectory']
|
||||
}).then(result => {
|
||||
console.log(result.canceled)
|
||||
console.log(result.filePaths)
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
```
|
||||
|
||||
### `dialog.showSaveDialog([browserWindow, ]options[, callback])`
|
||||
|
||||
* `browserWindow` [BrowserWindow](browser-window.md) (optional)
|
||||
|
@ -201,9 +270,9 @@ attached to the parent window, making it modal.
|
|||
|
||||
On Windows the options are more limited, due to the Win32 APIs used:
|
||||
|
||||
- The `message` argument is not used, as the OS provides its own confirmation
|
||||
* The `message` argument is not used, as the OS provides its own confirmation
|
||||
dialog.
|
||||
- The `browserWindow` argument is ignored since it is not possible to make
|
||||
* The `browserWindow` argument is ignored since it is not possible to make
|
||||
this confirmation dialog modal.
|
||||
|
||||
## Sheets
|
||||
|
|
|
@ -9,7 +9,6 @@ When a majority of affected functions are migrated, this flag will be enabled by
|
|||
### Candidate Functions
|
||||
|
||||
- [app.importCertificate(options, callback)](https://github.com/electron/electron/blob/master/docs/api/app.md#importCertificate)
|
||||
- [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)
|
||||
- [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)
|
||||
|
@ -46,6 +45,7 @@ When a majority of affected functions are migrated, this flag will be enabled by
|
|||
- [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)
|
||||
- [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)
|
||||
- [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)
|
||||
- [webviewTag.capturePage([rect, ]callback)](https://github.com/electron/electron/blob/master/docs/api/webview-tag.md#capturePage)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict'
|
||||
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
const { app, BrowserWindow, deprecate } = require('electron')
|
||||
const binding = process.atomBinding('dialog')
|
||||
const v8Util = process.atomBinding('v8_util')
|
||||
|
||||
|
@ -70,70 +70,54 @@ const checkAppInitialized = function () {
|
|||
}
|
||||
}
|
||||
|
||||
const openDialog = (sync, window, options) => {
|
||||
checkAppInitialized()
|
||||
|
||||
if (window.constructor !== BrowserWindow) options = window
|
||||
if (options == null) {
|
||||
options = {
|
||||
title: 'Open',
|
||||
properties: ['openFile']
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
buttonLabel = '',
|
||||
defaultPath = '',
|
||||
filters = [],
|
||||
properties = ['openFile'],
|
||||
title = '',
|
||||
message = '',
|
||||
securityScopedBookmarks = false
|
||||
} = options
|
||||
|
||||
if (!Array.isArray(properties)) throw new TypeError('Properties must be an array')
|
||||
|
||||
let dialogProperties = 0
|
||||
for (const prop in fileDialogProperties) {
|
||||
if (properties.includes(prop)) {
|
||||
dialogProperties |= fileDialogProperties[prop]
|
||||
}
|
||||
}
|
||||
|
||||
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')
|
||||
|
||||
const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, window }
|
||||
settings.properties = dialogProperties
|
||||
|
||||
return (sync) ? binding.showOpenDialogSync(settings) : binding.showOpenDialog(settings)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
showOpenDialog: function (...args) {
|
||||
checkAppInitialized()
|
||||
|
||||
let [window, options, callback] = parseArgs(...args)
|
||||
|
||||
if (options == null) {
|
||||
options = {
|
||||
title: 'Open',
|
||||
properties: ['openFile']
|
||||
}
|
||||
}
|
||||
|
||||
let { buttonLabel, defaultPath, filters, properties, title, message, securityScopedBookmarks = false } = options
|
||||
|
||||
if (properties == null) {
|
||||
properties = ['openFile']
|
||||
} else if (!Array.isArray(properties)) {
|
||||
throw new TypeError('Properties must be an array')
|
||||
}
|
||||
|
||||
let dialogProperties = 0
|
||||
for (const prop in fileDialogProperties) {
|
||||
if (properties.includes(prop)) {
|
||||
dialogProperties |= fileDialogProperties[prop]
|
||||
}
|
||||
}
|
||||
|
||||
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')
|
||||
}
|
||||
|
||||
const wrappedCallback = typeof callback === 'function' ? function (success, result, bookmarkData) {
|
||||
return success ? callback(result, bookmarkData) : callback()
|
||||
} : null
|
||||
const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, window }
|
||||
settings.properties = dialogProperties
|
||||
return binding.showOpenDialog(settings, wrappedCallback)
|
||||
showOpenDialog: function (window, options) {
|
||||
return openDialog(false, window, options)
|
||||
},
|
||||
showOpenDialogSync: function (window, options) {
|
||||
return openDialog(true, window, options)
|
||||
},
|
||||
|
||||
showSaveDialog: function (...args) {
|
||||
checkAppInitialized()
|
||||
|
||||
|
@ -306,6 +290,8 @@ module.exports = {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports.showOpenDialog = deprecate.promisify(module.exports.showOpenDialog)
|
||||
|
||||
// Mark standard asynchronous functions.
|
||||
v8Util.setHiddenValue(module.exports.showMessageBox, 'asynchronous', true)
|
||||
v8Util.setHiddenValue(module.exports.showOpenDialog, 'asynchronous', true)
|
||||
|
|
|
@ -86,9 +86,9 @@ ipcMainUtils.handle('ELECTRON_INSPECTOR_SELECT_FILE', function (event) {
|
|||
return new Promise((resolve, reject) => {
|
||||
assertChromeDevTools(event.sender, 'window.UI.createFileSelectorElement()')
|
||||
|
||||
dialog.showOpenDialog({}, function (files) {
|
||||
if (files) {
|
||||
const path = files[0]
|
||||
dialog.showOpenDialog({}, function (result) {
|
||||
if (!result.canceled) {
|
||||
const path = result.filePaths[0]
|
||||
fs.readFile(path, (error, data) => {
|
||||
if (error) {
|
||||
reject(error)
|
||||
|
|
|
@ -479,7 +479,7 @@ contentTracing.startRecording(options, function () {
|
|||
// https://github.com/atom/electron/blob/master/docs/api/dialog.md
|
||||
|
||||
// variant without browserWindow
|
||||
let openDialogResult: string[] = dialog.showOpenDialog({
|
||||
dialog.showOpenDialogSync({
|
||||
title: 'Testing showOpenDialog',
|
||||
defaultPath: '/var/log/syslog',
|
||||
filters: [{ name: '', extensions: [''] }],
|
||||
|
@ -487,11 +487,13 @@ let openDialogResult: string[] = dialog.showOpenDialog({
|
|||
})
|
||||
|
||||
// variant with browserWindow
|
||||
openDialogResult = dialog.showOpenDialog(win3, {
|
||||
dialog.showOpenDialog(win3, {
|
||||
title: 'Testing showOpenDialog',
|
||||
defaultPath: '/var/log/syslog',
|
||||
filters: [{ name: '', extensions: [''] }],
|
||||
properties: ['openFile', 'openDirectory', 'multiSelections']
|
||||
}).then(ret => {
|
||||
console.log(ret)
|
||||
})
|
||||
|
||||
// global-shortcut
|
||||
|
|
Loading…
Reference in a new issue