diff --git a/atom/browser/api/atom_api_dialog.cc b/atom/browser/api/atom_api_dialog.cc index 5f0b7fec5f..326834472d 100644 --- a/atom/browser/api/atom_api_dialog.cc +++ b/atom/browser/api/atom_api_dialog.cc @@ -35,6 +35,27 @@ struct Converter { } }; +template<> +struct Converter { + static bool FromV8(v8::Isolate* isolate, + v8::Local val, + file_dialog::DialogSettings* out) { + mate::Dictionary dict; + if (!ConvertFromV8(isolate, val, &dict)) + return false; + dict.Get("window", &(out->parent_window)); + dict.Get("title", &(out->title)); + dict.Get("message", &(out->message)); + dict.Get("buttonLabel", &(out->button_label)); + dict.Get("nameFieldLabel", &(out->name_field_label)); + dict.Get("defaultPath", &(out->default_path)); + dict.Get("filters", &(out->filters)); + dict.Get("properties", &(out->properties)); + dict.Get("showsTagField", &(out->shows_tag_field)); + return true; + } +}; + } // namespace mate namespace { @@ -47,6 +68,8 @@ void ShowMessageBox(int type, const std::string& title, const std::string& message, const std::string& detail, + const std::string& checkbox_label, + bool checkbox_checked, const gfx::ImageSkia& icon, atom::NativeWindow* window, mate::Arguments* args) { @@ -56,8 +79,8 @@ void ShowMessageBox(int type, peek, &callback)) { atom::ShowMessageBox(window, (atom::MessageBoxType)type, buttons, - default_id, cancel_id, options, title, - message, detail, icon, callback); + default_id, cancel_id, options, title, message, detail, + checkbox_label, checkbox_checked, icon, callback); } else { int chosen = atom::ShowMessageBox(window, (atom::MessageBoxType)type, buttons, default_id, cancel_id, @@ -66,51 +89,32 @@ void ShowMessageBox(int type, } } -void ShowOpenDialog(const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const file_dialog::Filters& filters, - int properties, - const std::string& message, - atom::NativeWindow* window, +void ShowOpenDialog(const file_dialog::DialogSettings& settings, mate::Arguments* args) { v8::Local peek = args->PeekNext(); file_dialog::OpenDialogCallback callback; if (mate::Converter::FromV8(args->isolate(), peek, &callback)) { - file_dialog::ShowOpenDialog(window, title, button_label, default_path, - filters, properties, message, callback); + file_dialog::ShowOpenDialog(settings, callback); } else { std::vector paths; - if (file_dialog::ShowOpenDialog(window, title, button_label, default_path, - filters, properties, message, &paths)) + if (file_dialog::ShowOpenDialog(settings, &paths)) args->Return(paths); } } -void ShowSaveDialog(const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const file_dialog::Filters& filters, - const std::string& message, - const std::string& name_field_label, - bool shows_tag_field, - atom::NativeWindow* window, +void ShowSaveDialog(const file_dialog::DialogSettings& settings, mate::Arguments* args) { v8::Local peek = args->PeekNext(); file_dialog::SaveDialogCallback callback; if (mate::Converter::FromV8(args->isolate(), peek, &callback)) { - file_dialog::ShowSaveDialog(window, title, button_label, default_path, - filters, message, name_field_label, - shows_tag_field, callback); + file_dialog::ShowSaveDialog(settings, callback); } else { base::FilePath path; - if (file_dialog::ShowSaveDialog(window, title, button_label, default_path, - filters, message, name_field_label, - shows_tag_field, &path)) + if (file_dialog::ShowSaveDialog(settings, &path)) args->Return(path); } } diff --git a/atom/browser/atom_download_manager_delegate.cc b/atom/browser/atom_download_manager_delegate.cc index b92e3e18bc..ccbc43416a 100644 --- a/atom/browser/atom_download_manager_delegate.cc +++ b/atom/browser/atom_download_manager_delegate.cc @@ -90,11 +90,11 @@ void AtomDownloadManagerDelegate::OnDownloadPathGenerated( base::FilePath path; GetItemSavePath(item, &path); // Show save dialog if save path was not set already on item - if (path.empty() && file_dialog::ShowSaveDialog(window, item->GetURL().spec(), - "", default_path, - file_dialog::Filters(), - "", "", false, - &path)) { + file_dialog::DialogSettings settings; + settings.parent_window = window; + settings.title = item->GetURL().spec(); + settings.default_path = default_path; + if (path.empty() && file_dialog::ShowSaveDialog(settings, &path)) { // Remember the last selected download directory. AtomBrowserContext* browser_context = static_cast( download_manager_->GetBrowserContext()); diff --git a/atom/browser/atom_javascript_dialog_manager.cc b/atom/browser/atom_javascript_dialog_manager.cc index 24197915e6..f874a99db5 100644 --- a/atom/browser/atom_javascript_dialog_manager.cc +++ b/atom/browser/atom_javascript_dialog_manager.cc @@ -38,14 +38,9 @@ void AtomJavaScriptDialogManager::RunJavaScriptDialog( } atom::ShowMessageBox(NativeWindow::FromWebContents(web_contents), - atom::MessageBoxType::MESSAGE_BOX_TYPE_NONE, - buttons, - -1, - 0, - atom::MessageBoxOptions::MESSAGE_BOX_NONE, - "", - base::UTF16ToUTF8(message_text), - "", + atom::MessageBoxType::MESSAGE_BOX_TYPE_NONE, buttons, -1, + 0, atom::MessageBoxOptions::MESSAGE_BOX_NONE, "", + base::UTF16ToUTF8(message_text), "", "", false, gfx::ImageSkia(), base::Bind(&OnMessageBoxCallback, callback)); } @@ -66,7 +61,9 @@ void AtomJavaScriptDialogManager::CancelDialogs( // static void AtomJavaScriptDialogManager::OnMessageBoxCallback( - const DialogClosedCallback& callback, int code) { + const DialogClosedCallback& callback, + int code, + bool checkbox_checked) { callback.Run(code == 0, base::string16()); } diff --git a/atom/browser/atom_javascript_dialog_manager.h b/atom/browser/atom_javascript_dialog_manager.h index e5bb6114bc..01cc76248c 100644 --- a/atom/browser/atom_javascript_dialog_manager.h +++ b/atom/browser/atom_javascript_dialog_manager.h @@ -32,7 +32,8 @@ class AtomJavaScriptDialogManager : public content::JavaScriptDialogManager { private: static void OnMessageBoxCallback(const DialogClosedCallback& callback, - int code); + int code, + bool checkbox_checked); }; } // namespace atom diff --git a/atom/browser/common_web_contents_delegate.cc b/atom/browser/common_web_contents_delegate.cc index 5b3961464c..89c1e37255 100644 --- a/atom/browser/common_web_contents_delegate.cc +++ b/atom/browser/common_web_contents_delegate.cc @@ -294,10 +294,11 @@ void CommonWebContentsDelegate::DevToolsSaveToFile( if (it != saved_files_.end() && !save_as) { path = it->second; } else { - file_dialog::Filters filters; - base::FilePath default_path(base::FilePath::FromUTF8Unsafe(url)); - if (!file_dialog::ShowSaveDialog(owner_window(), url, "", default_path, - filters, "", "", false, &path)) { + file_dialog::DialogSettings settings; + settings.parent_window = owner_window(); + settings.title = url; + settings.default_path = base::FilePath::FromUTF8Unsafe(url); + if (!file_dialog::ShowSaveDialog(settings, &path)) { base::StringValue url_value(url); web_contents_->CallClientFunction( "DevToolsAPI.canceledSaveURL", &url_value, nullptr, nullptr); @@ -358,12 +359,11 @@ void CommonWebContentsDelegate::DevToolsAddFileSystem( const base::FilePath& file_system_path) { base::FilePath path = file_system_path; if (path.empty()) { - file_dialog::Filters filters; - base::FilePath default_path; std::vector paths; - int flag = file_dialog::FILE_DIALOG_OPEN_DIRECTORY; - if (!file_dialog::ShowOpenDialog(owner_window(), "", "", default_path, - filters, flag, "", &paths)) + file_dialog::DialogSettings settings; + settings.parent_window = owner_window(); + settings.properties = file_dialog::FILE_DIALOG_OPEN_DIRECTORY; + if (!file_dialog::ShowOpenDialog(settings, &paths)) return; path = paths[0]; diff --git a/atom/browser/ui/file_dialog.h b/atom/browser/ui/file_dialog.h index 546722b4e6..e9734adeb6 100644 --- a/atom/browser/ui/file_dialog.h +++ b/atom/browser/ui/file_dialog.h @@ -37,42 +37,28 @@ typedef base::Callback SaveDialogCallback; -bool ShowOpenDialog(atom::NativeWindow* parent_window, - const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const Filters& filters, - int properties, - const std::string& message, +struct DialogSettings { + atom::NativeWindow* parent_window = nullptr; + std::string title; + std::string message; + std::string button_label; + std::string name_field_label; + base::FilePath default_path; + Filters filters; + int properties = 0; + bool shows_tag_field = true; +}; + +bool ShowOpenDialog(const DialogSettings& settings, std::vector* paths); -void ShowOpenDialog(atom::NativeWindow* parent_window, - const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const Filters& filters, - int properties, - const std::string& message, +void ShowOpenDialog(const DialogSettings& settings, const OpenDialogCallback& callback); -bool ShowSaveDialog(atom::NativeWindow* parent_window, - const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const Filters& filters, - const std::string& message, - const std::string& name_field_label, - bool shows_tag_field, +bool ShowSaveDialog(const DialogSettings& settings, base::FilePath* path); -void ShowSaveDialog(atom::NativeWindow* parent_window, - const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const Filters& filters, - const std::string& message, - const std::string& name_field_label, - bool shows_tag_field, +void ShowSaveDialog(const DialogSettings& settings, const SaveDialogCallback& callback); } // namespace file_dialog diff --git a/atom/browser/ui/file_dialog_gtk.cc b/atom/browser/ui/file_dialog_gtk.cc index f4aaa38037..c35d2d9d36 100644 --- a/atom/browser/ui/file_dialog_gtk.cc +++ b/atom/browser/ui/file_dialog_gtk.cc @@ -36,24 +36,20 @@ void OnFileFilterDataDestroyed(std::string* file_extension) { class FileChooserDialog { public: FileChooserDialog(GtkFileChooserAction action, - atom::NativeWindow* parent_window, - const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const Filters& filters) - : parent_(static_cast(parent_window)), - filters_(filters) { + const DialogSettings& settings) + : parent_(static_cast(settings.parent_window)), + filters_(settings.filters) { const char* confirm_text = GTK_STOCK_OK; - if (!button_label.empty()) - confirm_text = button_label.c_str(); + if (!settings.button_label.empty()) + confirm_text = settings.button_label.c_str(); else if (action == GTK_FILE_CHOOSER_ACTION_SAVE) confirm_text = GTK_STOCK_SAVE; else if (action == GTK_FILE_CHOOSER_ACTION_OPEN) confirm_text = GTK_STOCK_OPEN; dialog_ = gtk_file_chooser_dialog_new( - title.c_str(), + settings.title.c_str(), NULL, action, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, @@ -71,20 +67,20 @@ class FileChooserDialog { if (action != GTK_FILE_CHOOSER_ACTION_OPEN) gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(dialog_), TRUE); - if (!default_path.empty()) { - if (base::DirectoryExists(default_path)) { + if (!settings.default_path.empty()) { + if (base::DirectoryExists(settings.default_path)) { gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog_), - default_path.value().c_str()); + settings.default_path.value().c_str()); } else { gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog_), - default_path.DirName().value().c_str()); + settings.default_path.DirName().value().c_str()); gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog_), - default_path.BaseName().value().c_str()); + settings.default_path.BaseName().value().c_str()); } } - if (!filters.empty()) - AddFilters(filters); + if (!settings.filters.empty()) + AddFilters(settings.filters); } ~FileChooserDialog() { @@ -230,20 +226,13 @@ base::FilePath FileChooserDialog::AddExtensionForFilename( } // namespace -bool ShowOpenDialog(atom::NativeWindow* parent_window, - const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const Filters& filters, - int properties, - const std::string& message, +bool ShowOpenDialog(const DialogSettings& settings, std::vector* paths) { GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN; - if (properties & FILE_DIALOG_OPEN_DIRECTORY) + if (settings.properties & FILE_DIALOG_OPEN_DIRECTORY) action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; - FileChooserDialog open_dialog(action, parent_window, title, button_label, - default_path, filters); - open_dialog.SetupProperties(properties); + FileChooserDialog open_dialog(action, settings); + open_dialog.SetupProperties(settings.properties); gtk_widget_show_all(open_dialog.dialog()); int response = gtk_dialog_run(GTK_DIALOG(open_dialog.dialog())); @@ -255,34 +244,19 @@ bool ShowOpenDialog(atom::NativeWindow* parent_window, } } -void ShowOpenDialog(atom::NativeWindow* parent_window, - const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const Filters& filters, - int properties, - const std::string& message, +void ShowOpenDialog(const DialogSettings& settings, const OpenDialogCallback& callback) { GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN; - if (properties & FILE_DIALOG_OPEN_DIRECTORY) + if (settings.properties & FILE_DIALOG_OPEN_DIRECTORY) action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; - FileChooserDialog* open_dialog = new FileChooserDialog( - action, parent_window, title, button_label, default_path, filters); - open_dialog->SetupProperties(properties); + FileChooserDialog* open_dialog = new FileChooserDialog(action, settings); + open_dialog->SetupProperties(settings.properties); open_dialog->RunOpenAsynchronous(callback); } -bool ShowSaveDialog(atom::NativeWindow* parent_window, - const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const Filters& filters, - const std::string& message, - const std::string& name_field_label, - bool shows_tag_field, +bool ShowSaveDialog(const DialogSettings& settings, base::FilePath* path) { - FileChooserDialog save_dialog(GTK_FILE_CHOOSER_ACTION_SAVE, parent_window, - title, button_label, default_path, filters); + FileChooserDialog save_dialog(GTK_FILE_CHOOSER_ACTION_SAVE, settings); gtk_widget_show_all(save_dialog.dialog()); int response = gtk_dialog_run(GTK_DIALOG(save_dialog.dialog())); if (response == GTK_RESPONSE_ACCEPT) { @@ -293,18 +267,10 @@ bool ShowSaveDialog(atom::NativeWindow* parent_window, } } -void ShowSaveDialog(atom::NativeWindow* parent_window, - const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const Filters& filters, - const std::string& message, - const std::string& name_field_label, - bool shows_tag_field, +void ShowSaveDialog(const DialogSettings& settings, const SaveDialogCallback& callback) { FileChooserDialog* save_dialog = new FileChooserDialog( - GTK_FILE_CHOOSER_ACTION_SAVE, parent_window, title, button_label, - default_path, filters); + GTK_FILE_CHOOSER_ACTION_SAVE, settings); save_dialog->RunSaveAsynchronous(callback); } diff --git a/atom/browser/ui/file_dialog_mac.mm b/atom/browser/ui/file_dialog_mac.mm index 70d5694562..6b625e3a53 100644 --- a/atom/browser/ui/file_dialog_mac.mm +++ b/atom/browser/ui/file_dialog_mac.mm @@ -44,36 +44,31 @@ void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) { } void SetupDialog(NSSavePanel* dialog, - const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const Filters& filters, - const std::string& message, - const std::string& name_field_label, - const bool& shows_tag_field) { - if (!title.empty()) - [dialog setTitle:base::SysUTF8ToNSString(title)]; + const DialogSettings& settings) { + if (!settings.title.empty()) + [dialog setTitle:base::SysUTF8ToNSString(settings.title)]; - if (!button_label.empty()) - [dialog setPrompt:base::SysUTF8ToNSString(button_label)]; + if (!settings.button_label.empty()) + [dialog setPrompt:base::SysUTF8ToNSString(settings.button_label)]; - if (!message.empty()) - [dialog setMessage:base::SysUTF8ToNSString(message)]; + if (!settings.message.empty()) + [dialog setMessage:base::SysUTF8ToNSString(settings.message)]; - if (!name_field_label.empty()) - [dialog setNameFieldLabel:base::SysUTF8ToNSString(name_field_label)]; + if (!settings.name_field_label.empty()) + [dialog setNameFieldLabel:base::SysUTF8ToNSString(settings.name_field_label)]; - [dialog setShowsTagField:shows_tag_field]; + [dialog setShowsTagField:settings.shows_tag_field]; NSString* default_dir = nil; NSString* default_filename = nil; - if (!default_path.empty()) { - if (base::DirectoryExists(default_path)) { - default_dir = base::SysUTF8ToNSString(default_path.value()); + if (!settings.default_path.empty()) { + if (base::DirectoryExists(settings.default_path)) { + default_dir = base::SysUTF8ToNSString(settings.default_path.value()); } else { - default_dir = base::SysUTF8ToNSString(default_path.DirName().value()); + default_dir = + base::SysUTF8ToNSString(settings.default_path.DirName().value()); default_filename = - base::SysUTF8ToNSString(default_path.BaseName().value()); + base::SysUTF8ToNSString(settings.default_path.BaseName().value()); } } @@ -82,10 +77,10 @@ void SetupDialog(NSSavePanel* dialog, if (default_filename) [dialog setNameFieldStringValue:default_filename]; - if (filters.empty()) + if (settings.filters.empty()) [dialog setAllowsOtherFileTypes:YES]; else - SetAllowedFileTypes(dialog, filters); + SetAllowedFileTypes(dialog, settings.filters); } void SetupDialogForProperties(NSOpenPanel* dialog, int properties) { @@ -128,22 +123,15 @@ void ReadDialogPaths(NSOpenPanel* dialog, std::vector* paths) { } // namespace -bool ShowOpenDialog(atom::NativeWindow* parent_window, - const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const Filters& filters, - int properties, - const std::string& message, +bool ShowOpenDialog(const DialogSettings& settings, std::vector* paths) { DCHECK(paths); NSOpenPanel* dialog = [NSOpenPanel openPanel]; - // NSOpenPanel does not support name_field_label and shows_tag_field - SetupDialog(dialog, title, button_label, default_path, filters, message, "", false); - SetupDialogForProperties(dialog, properties); + SetupDialog(dialog, settings); + SetupDialogForProperties(dialog, settings.properties); - int chosen = RunModalDialog(dialog, parent_window); + int chosen = RunModalDialog(dialog, settings.parent_window); if (chosen == NSFileHandlingPanelCancelButton) return false; @@ -151,25 +139,20 @@ bool ShowOpenDialog(atom::NativeWindow* parent_window, return true; } -void ShowOpenDialog(atom::NativeWindow* parent_window, - const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const Filters& filters, - int properties, - const std::string& message, +void ShowOpenDialog(const DialogSettings& settings, const OpenDialogCallback& c) { NSOpenPanel* dialog = [NSOpenPanel openPanel]; - // NSOpenPanel does not support name_field_label and shows_tag_field - SetupDialog(dialog, title, button_label, default_path, filters, message, "", false); - SetupDialogForProperties(dialog, properties); + 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; - NSWindow* window = parent_window ? parent_window->GetNativeWindow() : NULL; + NSWindow* window = settings.parent_window ? + settings.parent_window->GetNativeWindow() : + NULL; [dialog beginSheetModalForWindow:window completionHandler:^(NSInteger chosen) { if (chosen == NSFileHandlingPanelCancelButton) { @@ -182,22 +165,14 @@ void ShowOpenDialog(atom::NativeWindow* parent_window, }]; } -bool ShowSaveDialog(atom::NativeWindow* parent_window, - const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const Filters& filters, - const std::string& message, - const std::string& name_field_label, - bool shows_tag_field, +bool ShowSaveDialog(const DialogSettings& settings, base::FilePath* path) { DCHECK(path); NSSavePanel* dialog = [NSSavePanel savePanel]; - SetupDialog(dialog, title, button_label, default_path, filters, message, - name_field_label, shows_tag_field); + SetupDialog(dialog, settings); - int chosen = RunModalDialog(dialog, parent_window); + int chosen = RunModalDialog(dialog, settings.parent_window); if (chosen == NSFileHandlingPanelCancelButton || ![[dialog URL] isFileURL]) return false; @@ -205,24 +180,18 @@ bool ShowSaveDialog(atom::NativeWindow* parent_window, return true; } -void ShowSaveDialog(atom::NativeWindow* parent_window, - const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const Filters& filters, - const std::string& message, - const std::string& name_field_label, - bool shows_tag_field, +void ShowSaveDialog(const DialogSettings& settings, const SaveDialogCallback& c) { NSSavePanel* dialog = [NSSavePanel savePanel]; - SetupDialog(dialog, title, button_label, default_path, filters, message, - name_field_label, shows_tag_field); + SetupDialog(dialog, settings); [dialog setCanSelectHiddenExtension:YES]; __block SaveDialogCallback callback = c; - NSWindow* window = parent_window ? parent_window->GetNativeWindow() : NULL; + NSWindow* window = settings.parent_window ? + settings.parent_window->GetNativeWindow() : + NULL; [dialog beginSheetModalForWindow:window completionHandler:^(NSInteger chosen) { if (chosen == NSFileHandlingPanelCancelButton) { diff --git a/atom/browser/ui/file_dialog_win.cc b/atom/browser/ui/file_dialog_win.cc index 7b5a1f4897..eaaac5e39e 100644 --- a/atom/browser/ui/file_dialog_win.cc +++ b/atom/browser/ui/file_dialog_win.cc @@ -66,26 +66,24 @@ void ConvertFilters(const Filters& filters, template class FileDialog { public: - FileDialog(const base::FilePath& default_path, - const std::string& title, - const std::string& button_label, - const Filters& filters, int options) { + FileDialog(const DialogSettings& settings, int options) { std::wstring file_part; - if (!IsDirectory(default_path)) - file_part = default_path.BaseName().value(); + if (!IsDirectory(settings.default_path)) + file_part = settings.default_path.BaseName().value(); std::vector buffer; std::vector filterspec; - ConvertFilters(filters, &buffer, &filterspec); + ConvertFilters(settings.filters, &buffer, &filterspec); dialog_.reset(new T(file_part.c_str(), options, NULL, filterspec.data(), filterspec.size())); - if (!title.empty()) - GetPtr()->SetTitle(base::UTF8ToUTF16(title).c_str()); + if (!settings.title.empty()) + GetPtr()->SetTitle(base::UTF8ToUTF16(settings.title).c_str()); - if (!button_label.empty()) - GetPtr()->SetOkButtonLabel(base::UTF8ToUTF16(button_label).c_str()); + if (!settings.button_label.empty()) + GetPtr()->SetOkButtonLabel( + base::UTF8ToUTF16(settings.button_label).c_str()); // By default, *.* will be added to the file name if file type is "*.*". In // Electron, we disable it to make a better experience. @@ -107,7 +105,7 @@ class FileDialog { } } - SetDefaultFolder(default_path); + SetDefaultFolder(settings.default_path); } bool Show(atom::NativeWindow* parent_window) { @@ -160,31 +158,20 @@ bool CreateDialogThread(RunState* run_state) { } void RunOpenDialogInNewThread(const RunState& run_state, - atom::NativeWindow* parent, - const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const Filters& filters, - int properties, + const DialogSettings& settings, const OpenDialogCallback& callback) { std::vector paths; - bool result = ShowOpenDialog(parent, title, button_label, default_path, - filters, properties, "", &paths); + bool result = ShowOpenDialog(settings, &paths); run_state.ui_task_runner->PostTask(FROM_HERE, base::Bind(callback, result, paths)); run_state.ui_task_runner->DeleteSoon(FROM_HERE, run_state.dialog_thread); } void RunSaveDialogInNewThread(const RunState& run_state, - atom::NativeWindow* parent, - const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const Filters& filters, + const DialogSettings& settings, const SaveDialogCallback& callback) { base::FilePath path; - bool result = ShowSaveDialog(parent, title, button_label, default_path, - filters, "", "", false, &path); + bool result = ShowSaveDialog(settings, &path); run_state.ui_task_runner->PostTask(FROM_HERE, base::Bind(callback, result, path)); run_state.ui_task_runner->DeleteSoon(FROM_HERE, run_state.dialog_thread); @@ -192,27 +179,20 @@ void RunSaveDialogInNewThread(const RunState& run_state, } // namespace -bool ShowOpenDialog(atom::NativeWindow* parent_window, - const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const Filters& filters, - int properties, - const std::string& message, +bool ShowOpenDialog(const DialogSettings& settings, std::vector* paths) { int options = FOS_FORCEFILESYSTEM | FOS_FILEMUSTEXIST; - if (properties & FILE_DIALOG_OPEN_DIRECTORY) + if (settings.properties & FILE_DIALOG_OPEN_DIRECTORY) options |= FOS_PICKFOLDERS; - if (properties & FILE_DIALOG_MULTI_SELECTIONS) + if (settings.properties & FILE_DIALOG_MULTI_SELECTIONS) options |= FOS_ALLOWMULTISELECT; - if (properties & FILE_DIALOG_SHOW_HIDDEN_FILES) + if (settings.properties & FILE_DIALOG_SHOW_HIDDEN_FILES) options |= FOS_FORCESHOWHIDDEN; - if (properties & FILE_DIALOG_PROMPT_TO_CREATE) + if (settings.properties & FILE_DIALOG_PROMPT_TO_CREATE) options |= FOS_CREATEPROMPT; - FileDialog open_dialog( - default_path, title, button_label, filters, options); - if (!open_dialog.Show(parent_window)) + FileDialog open_dialog(settings, options); + if (!open_dialog.Show(settings.parent_window)) return false; ATL::CComPtr items; @@ -245,13 +225,7 @@ bool ShowOpenDialog(atom::NativeWindow* parent_window, return true; } -void ShowOpenDialog(atom::NativeWindow* parent, - const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const Filters& filters, - int properties, - const std::string& message, +void ShowOpenDialog(const DialogSettings& settings, const OpenDialogCallback& callback) { RunState run_state; if (!CreateDialogThread(&run_state)) { @@ -261,23 +235,14 @@ void ShowOpenDialog(atom::NativeWindow* parent, run_state.dialog_thread->task_runner()->PostTask( FROM_HERE, - base::Bind(&RunOpenDialogInNewThread, run_state, parent, title, - button_label, default_path, filters, properties, callback)); + base::Bind(&RunOpenDialogInNewThread, run_state, settings, callback)); } -bool ShowSaveDialog(atom::NativeWindow* parent_window, - const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const Filters& filters, - const std::string& message, - const std::string& name_field_label, - bool shows_tag_field, +bool ShowSaveDialog(const DialogSettings& settings, base::FilePath* path) { FileDialog save_dialog( - default_path, title, button_label, filters, - FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_OVERWRITEPROMPT); - if (!save_dialog.Show(parent_window)) + settings, FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_OVERWRITEPROMPT); + if (!save_dialog.Show(settings.parent_window)) return false; wchar_t buffer[MAX_PATH]; @@ -289,14 +254,7 @@ bool ShowSaveDialog(atom::NativeWindow* parent_window, return true; } -void ShowSaveDialog(atom::NativeWindow* parent, - const std::string& title, - const std::string& button_label, - const base::FilePath& default_path, - const Filters& filters, - const std::string& message, - const std::string& name_field_label, - bool shows_tag_field, +void ShowSaveDialog(const DialogSettings& settings, const SaveDialogCallback& callback) { RunState run_state; if (!CreateDialogThread(&run_state)) { @@ -306,8 +264,7 @@ void ShowSaveDialog(atom::NativeWindow* parent, run_state.dialog_thread->task_runner()->PostTask( FROM_HERE, - base::Bind(&RunSaveDialogInNewThread, run_state, parent, title, - button_label, default_path, filters, callback)); + base::Bind(&RunSaveDialogInNewThread, run_state, settings, callback)); } } // namespace file_dialog diff --git a/atom/browser/ui/message_box.h b/atom/browser/ui/message_box.h index 16eef3c463..6c826719ee 100644 --- a/atom/browser/ui/message_box.h +++ b/atom/browser/ui/message_box.h @@ -32,7 +32,8 @@ enum MessageBoxOptions { MESSAGE_BOX_NO_LINK = 1 << 0, }; -typedef base::Callback MessageBoxCallback; +typedef base::Callback + MessageBoxCallback; int ShowMessageBox(NativeWindow* parent_window, MessageBoxType type, @@ -54,6 +55,8 @@ void ShowMessageBox(NativeWindow* parent_window, const std::string& title, const std::string& message, const std::string& detail, + const std::string& checkbox_label, + bool checkbox_checked, const gfx::ImageSkia& icon, const MessageBoxCallback& callback); diff --git a/atom/browser/ui/message_box_gtk.cc b/atom/browser/ui/message_box_gtk.cc index 0717d4d06d..a7bbe51ecf 100644 --- a/atom/browser/ui/message_box_gtk.cc +++ b/atom/browser/ui/message_box_gtk.cc @@ -36,8 +36,11 @@ class GtkMessageBox : public NativeWindowObserver { const std::string& title, const std::string& message, const std::string& detail, + const std::string& checkbox_label, + bool checkbox_checked, const gfx::ImageSkia& icon) : cancel_id_(cancel_id), + checkbox_checked_(false), parent_(static_cast(parent_window)) { // Create dialog. dialog_ = gtk_message_dialog_new( @@ -68,6 +71,18 @@ class GtkMessageBox : public NativeWindowObserver { g_object_unref(pixbuf); } + if (!checkbox_label.empty()) { + GtkWidget* message_area = + gtk_message_dialog_get_message_area(GTK_MESSAGE_DIALOG(dialog_)); + GtkWidget* check_button = + gtk_check_button_new_with_label(checkbox_label.c_str()); + g_signal_connect(check_button, "toggled", + G_CALLBACK(OnCheckboxToggledThunk), this); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button), + checkbox_checked); + gtk_container_add(GTK_CONTAINER(message_area), check_button); + } + // Add buttons. for (size_t i = 0; i < buttons.size(); ++i) { GtkWidget* button = gtk_dialog_add_button( @@ -154,6 +169,7 @@ class GtkMessageBox : public NativeWindowObserver { } CHROMEGTK_CALLBACK_1(GtkMessageBox, void, OnResponseDialog, int); + CHROMEGTK_CALLBACK_0(GtkMessageBox, void, OnCheckboxToggled); private: atom::UnresponsiveSuppressor unresponsive_suppressor_; @@ -161,6 +177,8 @@ class GtkMessageBox : public NativeWindowObserver { // The id to return when the dialog is closed without pressing buttons. int cancel_id_; + bool checkbox_checked_; + NativeWindowViews* parent_; GtkWidget* dialog_; MessageBoxCallback callback_; @@ -172,12 +190,16 @@ void GtkMessageBox::OnResponseDialog(GtkWidget* widget, int response) { gtk_widget_hide(dialog_); if (response < 0) - callback_.Run(cancel_id_); + callback_.Run(cancel_id_, checkbox_checked_); else - callback_.Run(response); + callback_.Run(response, checkbox_checked_); delete this; } +void GtkMessageBox::OnCheckboxToggled(GtkWidget* widget) { + checkbox_checked_ = GTK_TOGGLE_BUTTON(widget)->active; +} + } // namespace int ShowMessageBox(NativeWindow* parent, @@ -190,8 +212,9 @@ int ShowMessageBox(NativeWindow* parent, const std::string& message, const std::string& detail, const gfx::ImageSkia& icon) { - return GtkMessageBox(parent, type, buttons, default_id, cancel_id, - title, message, detail, icon).RunSynchronous(); + return GtkMessageBox(parent, type, buttons, default_id, cancel_id, title, + message, detail, "", false, icon) + .RunSynchronous(); } void ShowMessageBox(NativeWindow* parent, @@ -203,18 +226,22 @@ void ShowMessageBox(NativeWindow* parent, const std::string& title, const std::string& message, const std::string& detail, + const std::string& checkbox_label, + bool checkbox_checked, const gfx::ImageSkia& icon, const MessageBoxCallback& callback) { - (new GtkMessageBox(parent, type, buttons, default_id, cancel_id, - title, message, detail, icon))->RunAsynchronous(callback); + (new GtkMessageBox(parent, type, buttons, default_id, cancel_id, title, + message, detail, checkbox_label, checkbox_checked, icon)) + ->RunAsynchronous(callback); } void ShowErrorBox(const base::string16& title, const base::string16& content) { if (Browser::Get()->is_ready()) { - GtkMessageBox(nullptr, MESSAGE_BOX_TYPE_ERROR, { "OK" }, -1, 0, "Error", + GtkMessageBox(nullptr, MESSAGE_BOX_TYPE_ERROR, {"OK"}, -1, 0, "Error", base::UTF16ToUTF8(title).c_str(), - base::UTF16ToUTF8(content).c_str(), - gfx::ImageSkia()).RunSynchronous(); + base::UTF16ToUTF8(content).c_str(), "", false, + gfx::ImageSkia()) + .RunSynchronous(); } else { fprintf(stderr, ANSI_TEXT_BOLD ANSI_BACKGROUND_GRAY diff --git a/atom/browser/ui/message_box_mac.mm b/atom/browser/ui/message_box_mac.mm index 8fd28e2446..0d03eefc20 100644 --- a/atom/browser/ui/message_box_mac.mm +++ b/atom/browser/ui/message_box_mac.mm @@ -39,7 +39,7 @@ - (void)alertDidEnd:(NSAlert*)alert returnCode:(NSInteger)returnCode contextInfo:(void*)contextInfo { - callback_.Run(returnCode); + callback_.Run(returnCode, alert.suppressionButton.state == NSOnState); [alert_ release]; [self release]; @@ -60,6 +60,8 @@ NSAlert* CreateNSAlert(NativeWindow* parent_window, const std::string& title, const std::string& message, const std::string& detail, + const std::string& checkbox_label, + bool checkbox_checked, const gfx::ImageSkia& icon) { // Ignore the title; it's the window title on other platforms and ignorable. NSAlert* alert = [[NSAlert alloc] init]; @@ -95,6 +97,12 @@ NSAlert* CreateNSAlert(NativeWindow* parent_window, [[ns_buttons objectAtIndex:default_id] setKeyEquivalent:@"\r"]; } + if (!checkbox_label.empty()) { + alert.showsSuppressionButton = YES; + alert.suppressionButton.title = base::SysUTF8ToNSString(checkbox_label); + alert.suppressionButton.state = checkbox_checked ? NSOnState : NSOffState; + } + if (!icon.isNull()) { NSImage* image = skia::SkBitmapToNSImageWithColorSpace( *icon.bitmap(), base::mac::GetGenericRGBColorSpace()); @@ -104,7 +112,7 @@ NSAlert* CreateNSAlert(NativeWindow* parent_window, return alert; } -void SetReturnCode(int* ret_code, int result) { +void SetReturnCode(int* ret_code, int result, bool checkbox_checked) { *ret_code = result; } @@ -120,9 +128,8 @@ int ShowMessageBox(NativeWindow* parent_window, const std::string& message, const std::string& detail, const gfx::ImageSkia& icon) { - NSAlert* alert = CreateNSAlert( - parent_window, type, buttons, default_id, title, message, - detail, icon); + NSAlert* alert = CreateNSAlert(parent_window, type, buttons, default_id, + title, message, detail, "", false, icon); // Use runModal for synchronous alert without parent, since we don't have a // window to wait for. @@ -154,11 +161,13 @@ void ShowMessageBox(NativeWindow* parent_window, const std::string& title, const std::string& message, const std::string& detail, + const std::string& checkbox_label, + bool checkbox_checked, const gfx::ImageSkia& icon, const MessageBoxCallback& callback) { - NSAlert* alert = CreateNSAlert( - parent_window, type, buttons, default_id, title, message, - detail, icon); + NSAlert* alert = + CreateNSAlert(parent_window, type, buttons, default_id, title, message, + detail, checkbox_label, checkbox_checked, icon); ModalDelegate* delegate = [[ModalDelegate alloc] initWithCallback:callback andAlert:alert callEndModal:false]; diff --git a/atom/browser/ui/message_box_win.cc b/atom/browser/ui/message_box_win.cc index b6777fb1da..844a057ae6 100644 --- a/atom/browser/ui/message_box_win.cc +++ b/atom/browser/ui/message_box_win.cc @@ -72,7 +72,7 @@ void MapToCommonID(const std::vector& buttons, } } -int ShowMessageBoxUTF16(HWND parent, +int ShowTaskDialogUTF16(NativeWindow* parent, MessageBoxType type, const std::vector& buttons, int default_id, @@ -81,6 +81,8 @@ int ShowMessageBoxUTF16(HWND parent, const base::string16& title, const base::string16& message, const base::string16& detail, + const base::string16& checkbox_label, + bool* checkbox_checked, const gfx::ImageSkia& icon) { TASKDIALOG_FLAGS flags = TDF_SIZE_TO_CONTENT | // Show all content. @@ -88,10 +90,14 @@ int ShowMessageBoxUTF16(HWND parent, TASKDIALOGCONFIG config = { 0 }; config.cbSize = sizeof(config); - config.hwndParent = parent; config.hInstance = GetModuleHandle(NULL); config.dwFlags = flags; + if (parent) { + config.hwndParent = + static_cast(parent)->GetAcceleratedWidget(); + } + if (default_id > 0) config.nDefaultButton = kIDStart + default_id; @@ -132,6 +138,14 @@ int ShowMessageBoxUTF16(HWND parent, config.pszContent = detail.c_str(); } + if (!checkbox_label.empty()) { + config.pszVerificationText = checkbox_label.c_str(); + + if (checkbox_checked && *checkbox_checked) { + config.dwFlags |= TDF_VERIFICATION_FLAG_CHECKED; + } + } + // Iterate through the buttons, put common buttons in dwCommonButtons // and custom buttons in pButtons. std::map id_map; @@ -151,7 +165,12 @@ int ShowMessageBoxUTF16(HWND parent, } int id = 0; - TaskDialogIndirect(&config, &id, NULL, NULL); + BOOL verificationFlagChecked = FALSE; + TaskDialogIndirect(&config, &id, nullptr, &verificationFlagChecked); + if (checkbox_checked) { + *checkbox_checked = verificationFlagChecked; + } + if (id_map.find(id) != id_map.end()) // common button. return id_map[id]; else if (id >= kIDStart) // custom button. @@ -160,6 +179,29 @@ int ShowMessageBoxUTF16(HWND parent, return cancel_id; } +int ShowTaskDialogUTF8(NativeWindow* parent, + MessageBoxType type, + const std::vector& buttons, + int default_id, + int cancel_id, + int options, + const std::string& title, + const std::string& message, + const std::string& detail, + const std::string& checkbox_label, + bool* checkbox_checked, + const gfx::ImageSkia& icon) { + std::vector utf16_buttons; + for (const auto& button : buttons) + utf16_buttons.push_back(base::UTF8ToUTF16(button)); + + return ShowTaskDialogUTF16( + parent, type, utf16_buttons, default_id, cancel_id, options, + base::UTF8ToUTF16(title), base::UTF8ToUTF16(message), + base::UTF8ToUTF16(detail), base::UTF8ToUTF16(checkbox_label), + checkbox_checked, icon); +} + void RunMessageBoxInNewThread(base::Thread* thread, NativeWindow* parent, MessageBoxType type, @@ -170,12 +212,16 @@ void RunMessageBoxInNewThread(base::Thread* thread, const std::string& title, const std::string& message, const std::string& detail, + const std::string& checkbox_label, + bool checkbox_checked, const gfx::ImageSkia& icon, const MessageBoxCallback& callback) { - int result = ShowMessageBox(parent, type, buttons, default_id, - cancel_id, options, title, message, detail, icon); + int result = ShowTaskDialogUTF8(parent, type, buttons, default_id, cancel_id, + options, title, message, detail, + checkbox_label, &checkbox_checked, icon); content::BrowserThread::PostTask( - content::BrowserThread::UI, FROM_HERE, base::Bind(callback, result)); + content::BrowserThread::UI, FROM_HERE, + base::Bind(callback, result, checkbox_checked)); content::BrowserThread::DeleteSoon( content::BrowserThread::UI, FROM_HERE, thread); } @@ -192,25 +238,9 @@ int ShowMessageBox(NativeWindow* parent, const std::string& message, const std::string& detail, const gfx::ImageSkia& icon) { - std::vector utf16_buttons; - for (const auto& button : buttons) - utf16_buttons.push_back(base::UTF8ToUTF16(button)); - - HWND hwnd_parent = parent ? - static_cast(parent)->GetAcceleratedWidget() : - NULL; - atom::UnresponsiveSuppressor suppressor; - return ShowMessageBoxUTF16(hwnd_parent, - type, - utf16_buttons, - default_id, - cancel_id, - options, - base::UTF8ToUTF16(title), - base::UTF8ToUTF16(message), - base::UTF8ToUTF16(detail), - icon); + return ShowTaskDialogUTF8(parent, type, buttons, default_id, cancel_id, + options, title, message, detail, "", nullptr, icon); } void ShowMessageBox(NativeWindow* parent, @@ -222,13 +252,15 @@ void ShowMessageBox(NativeWindow* parent, const std::string& title, const std::string& message, const std::string& detail, + const std::string& checkbox_label, + bool checkbox_checked, const gfx::ImageSkia& icon, const MessageBoxCallback& callback) { std::unique_ptr thread( new base::Thread(ATOM_PRODUCT_NAME "MessageBoxThread")); thread->init_com_with_mta(false); if (!thread->Start()) { - callback.Run(cancel_id); + callback.Run(cancel_id, checkbox_checked); return; } @@ -237,13 +269,14 @@ void ShowMessageBox(NativeWindow* parent, FROM_HERE, base::Bind(&RunMessageBoxInNewThread, base::Unretained(unretained), parent, type, buttons, default_id, cancel_id, options, title, - message, detail, icon, callback)); + message, detail, checkbox_label, checkbox_checked, icon, + callback)); } void ShowErrorBox(const base::string16& title, const base::string16& content) { atom::UnresponsiveSuppressor suppressor; - ShowMessageBoxUTF16(NULL, MESSAGE_BOX_TYPE_ERROR, {}, -1, 0, 0, L"Error", - title, content, gfx::ImageSkia()); + ShowTaskDialogUTF16(nullptr, MESSAGE_BOX_TYPE_ERROR, {}, -1, 0, 0, L"Error", + title, content, L"", nullptr, gfx::ImageSkia()); } } // namespace atom diff --git a/atom/browser/web_dialog_helper.cc b/atom/browser/web_dialog_helper.cc index 0e28b07525..af7cc27674 100644 --- a/atom/browser/web_dialog_helper.cc +++ b/atom/browser/web_dialog_helper.cc @@ -81,19 +81,16 @@ void WebDialogHelper::RunFileChooser( content::RenderFrameHost* render_frame_host, const content::FileChooserParams& params) { std::vector result; - file_dialog::Filters filters = GetFileTypesFromAcceptType( - params.accept_types); + + file_dialog::DialogSettings settings; + settings.filters = GetFileTypesFromAcceptType(params.accept_types); + settings.parent_window = window_; + settings.title = base::UTF16ToUTF8(params.title); + if (params.mode == content::FileChooserParams::Save) { base::FilePath path; - if (file_dialog::ShowSaveDialog(window_, - base::UTF16ToUTF8(params.title), - "", - params.default_file_name, - filters, - "", - "", - false, - &path)) { + settings.default_path = params.default_file_name; + if (file_dialog::ShowSaveDialog(settings, &path)) { content::FileChooserFileInfo info; info.file_path = path; info.display_name = path.BaseName().value(); @@ -117,16 +114,10 @@ void WebDialogHelper::RunFileChooser( std::vector paths; AtomBrowserContext* browser_context = static_cast( window_->web_contents()->GetBrowserContext()); - base::FilePath default_file_path = browser_context->prefs()->GetFilePath( + settings.default_path = browser_context->prefs()->GetFilePath( prefs::kSelectFileLastDirectory).Append(params.default_file_name); - if (file_dialog::ShowOpenDialog(window_, - base::UTF16ToUTF8(params.title), - "", - default_file_path, - filters, - flags, - "", - &paths)) { + settings.properties = flags; + if (file_dialog::ShowOpenDialog(settings, &paths)) { for (auto& path : paths) { content::FileChooserFileInfo info; info.file_path = path; diff --git a/docs/api/dialog.md b/docs/api/dialog.md index 1bdb5f5cc7..10326731aa 100644 --- a/docs/api/dialog.md +++ b/docs/api/dialog.md @@ -127,6 +127,11 @@ will be passed via `callback(filename)` * `title` String (optional) - Title of the message box, some platforms will not show it. * `message` String - Content of the message box. * `detail` String (optional) - Extra information of the message. + * `checkboxLabel` String (optional) - If provided, the message box will + include a checkbox with the given label. The checkbox state can be + inspected only when using `callback`. + * `checkboxChecked` Boolean (optional) - Initial checked state of the + checkbox. `false` by default. * `icon` [NativeImage](native-image.md) (optional) * `cancelId` Integer (optional) - The value will be returned when user cancels the dialog instead of clicking the buttons of the dialog. By default it is the index @@ -140,6 +145,8 @@ will be passed via `callback(filename)` set `noLink` to `true`. * `callback` Function (optional) * `response` Number - The index of the button that was clicked + * `checkboxChecked` Boolean - The checked state of the checkbox if + `checkboxLabel` was set. Otherwise `false`. Returns `Integer`, the index of the clicked button, if a callback is provided it returns undefined. diff --git a/lib/browser/api/dialog.js b/lib/browser/api/dialog.js index b000e46e15..2bee42eb41 100644 --- a/lib/browser/api/dialog.js +++ b/lib/browser/api/dialog.js @@ -127,9 +127,9 @@ module.exports = { const wrappedCallback = typeof callback === 'function' ? function (success, result) { return callback(success ? result : void 0) } : null - return binding.showOpenDialog(title, buttonLabel, defaultPath, filters, - dialogProperties, message, window, - wrappedCallback) + const settings = {title, buttonLabel, defaultPath, filters, message, window} + settings.properties = dialogProperties + return binding.showOpenDialog(settings, wrappedCallback) }, showSaveDialog: function (...args) { @@ -187,9 +187,8 @@ module.exports = { const wrappedCallback = typeof callback === 'function' ? function (success, result) { return callback(success ? result : void 0) } : null - return binding.showSaveDialog(title, buttonLabel, defaultPath, filters, - message, nameFieldLabel, showsTagField, - window, wrappedCallback) + const settings = {title, buttonLabel, defaultPath, filters, message, nameFieldLabel, showsTagField, window} + return binding.showSaveDialog(settings, wrappedCallback) }, showMessageBox: function (...args) { @@ -203,7 +202,10 @@ module.exports = { } } - let {buttons, cancelId, defaultId, detail, icon, message, title, type} = options + let { + buttons, cancelId, checkboxLabel, checkboxChecked, defaultId, detail, + icon, message, title, type + } = options if (type == null) { type = 'none' @@ -242,6 +244,14 @@ module.exports = { throw new TypeError('Detail must be a string') } + checkboxChecked = !!checkboxChecked + + if (checkboxLabel == null) { + checkboxLabel = '' + } else if (typeof checkboxLabel !== 'string') { + throw new TypeError('checkboxLabel must be a string') + } + if (icon == null) { icon = null } @@ -264,8 +274,8 @@ module.exports = { const flags = options.noLink ? messageBoxOptions.noLink : 0 return binding.showMessageBox(messageBoxType, buttons, defaultId, cancelId, - flags, title, message, detail, icon, window, - callback) + flags, title, message, detail, checkboxLabel, + checkboxChecked, icon, window, callback) }, showErrorBox: function (...args) { diff --git a/spec/api-dialog-spec.js b/spec/api-dialog-spec.js index a07548e953..6b245c2b65 100644 --- a/spec/api-dialog-spec.js +++ b/spec/api-dialog-spec.js @@ -71,6 +71,10 @@ describe('dialog module', () => { assert.throws(() => { dialog.showMessageBox({detail: 3.14}) }, /Detail must be a string/) + + assert.throws(() => { + dialog.showMessageBox({checkboxLabel: false}) + }, /checkboxLabel must be a string/) }) })