diff --git a/atom/browser/ui/file_dialog_win.cc b/atom/browser/ui/file_dialog_win.cc index 4656557bf337..7fa68d521135 100644 --- a/atom/browser/ui/file_dialog_win.cc +++ b/atom/browser/ui/file_dialog_win.cc @@ -120,12 +120,13 @@ struct RunState { }; bool CreateDialogThread(RunState* run_state) { - base::Thread* thread = new base::Thread(ATOM_PRODUCT_NAME "FileDialogThread"); + scoped_ptr thread( + new base::Thread(ATOM_PRODUCT_NAME "FileDialogThread")); thread->init_com_with_mta(false); if (!thread->Start()) return false; - run_state->dialog_thread = thread; + run_state->dialog_thread = thread.release(); run_state->ui_message_loop = base::MessageLoop::current(); return true; } diff --git a/atom/browser/ui/message_box_win.cc b/atom/browser/ui/message_box_win.cc index e613b9aeb00b..0811db26fcd0 100644 --- a/atom/browser/ui/message_box_win.cc +++ b/atom/browser/ui/message_box_win.cc @@ -4,333 +4,66 @@ #include "atom/browser/ui/message_box.h" -#include "atom/browser/native_window.h" -#include "base/callback.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "base/strings/string_util.h" -#include "base/strings/string16.h" -#include "base/strings/utf_string_conversions.h" -#include "ui/views/background.h" -#include "ui/views/controls/button/label_button.h" -#include "ui/views/controls/message_box_view.h" -#include "ui/views/layout/grid_layout.h" -#include "ui/views/layout/layout_constants.h" -#include "ui/views/bubble/bubble_border.h" -#include "ui/views/bubble/bubble_frame_view.h" -#include "ui/views/widget/widget.h" -#include "ui/views/widget/widget_delegate.h" -#include "ui/wm/core/shadow_types.h" +#include +#include -#if defined(OS_WIN) -#include "ui/base/win/message_box_win.h" -#endif +#include "atom/browser/native_window_views.h" +#include "base/callback.h" +#include "base/strings/utf_string_conversions.h" +#include "base/threading/thread.h" +#include "content/public/browser/browser_thread.h" namespace atom { namespace { -// The group used by the buttons. This name is chosen voluntarily big not to -// conflict with other groups that could be in the dialog content. -const int kButtonGroup = 1127; +int ShowMessageBoxUTF16(HWND parent, + const std::vector& buttons, + int cancel_id, + const base::string16& title, + const base::string16& message, + const base::string16& detail) { + std::vector dialog_buttons; + for (size_t i = 0; i < buttons.size(); ++i) + dialog_buttons.push_back({i, buttons[i].c_str()}); -class MessageDialogClientView; + TASKDIALOGCONFIG config = { 0 }; + config.cbSize = sizeof(config); + config.hwndParent = parent; + config.hInstance = GetModuleHandle(NULL); + config.dwFlags = TDF_SIZE_TO_CONTENT; + config.pszWindowTitle = title.c_str(); + config.pszMainInstruction = message.c_str(); + config.pszContent = detail.c_str(); + config.pButtons = &dialog_buttons.front(); + config.cButtons = dialog_buttons.size(); -class MessageDialog : public views::WidgetDelegate, - public views::View, - public views::ButtonListener { - public: - MessageDialog(NativeWindow* parent_window, - MessageBoxType type, - const std::vector& buttons, - int cancel_id, - const std::string& title, - const std::string& message, - const std::string& detail, - const gfx::ImageSkia& icon); - virtual ~MessageDialog(); - - void Show(base::RunLoop* run_loop = NULL); - void Close(); - - int GetResult() const; - - void set_callback(const MessageBoxCallback& callback) { - delete_on_close_ = true; - callback_ = callback; - } - - private: - // Overridden from views::WidgetDelegate: - base::string16 GetWindowTitle() const override; - gfx::ImageSkia GetWindowAppIcon() override; - gfx::ImageSkia GetWindowIcon() override; - bool ShouldShowWindowIcon() const override; - views::Widget* GetWidget() override; - const views::Widget* GetWidget() const override; - views::View* GetContentsView() override; - views::View* GetInitiallyFocusedView() override; - ui::ModalType GetModalType() const override; - views::NonClientFrameView* CreateNonClientFrameView( - views::Widget* widget) override; - views::ClientView* CreateClientView(views::Widget* widget) override; - - // Overridden from views::View: - gfx::Size GetPreferredSize() const override; - void Layout() override; - bool AcceleratorPressed(const ui::Accelerator& accelerator) override; - - // Overridden from views::ButtonListener: - void ButtonPressed(views::Button* sender, const ui::Event& event) override; - - gfx::ImageSkia icon_; - - bool delete_on_close_; - int cancel_id_; - int result_; - base::string16 title_; - - NativeWindow* parent_; - scoped_ptr widget_; - views::MessageBoxView* message_box_view_; - std::vector buttons_; - - base::RunLoop* run_loop_; - scoped_ptr dialog_scope_; - MessageBoxCallback callback_; - - DISALLOW_COPY_AND_ASSIGN(MessageDialog); -}; - -class MessageDialogClientView : public views::ClientView { - public: - MessageDialogClientView(MessageDialog* dialog, views::Widget* widget) - : views::ClientView(widget, dialog), - dialog_(dialog) { - } - - // views::ClientView: - bool CanClose() override { - dialog_->Close(); - return false; - } - - private: - MessageDialog* dialog_; - - DISALLOW_COPY_AND_ASSIGN(MessageDialogClientView); -}; - -//////////////////////////////////////////////////////////////////////////////// -// MessageDialog, public: - -MessageDialog::MessageDialog(NativeWindow* parent_window, - MessageBoxType type, - const std::vector& buttons, - int cancel_id, - const std::string& title, - const std::string& message, - const std::string& detail, - const gfx::ImageSkia& icon) - : icon_(icon), - delete_on_close_(false), - cancel_id_(cancel_id), - result_(-1), - title_(base::UTF8ToUTF16(title)), - parent_(parent_window), - message_box_view_(NULL), - run_loop_(NULL), - dialog_scope_(new NativeWindow::DialogScope(parent_window)) { - DCHECK_GT(buttons.size(), 0u); - set_owned_by_client(); - - if (!parent_) - set_background(views::Background::CreateStandardPanelBackground()); - - std::string content = message + "\n" + detail; - views::MessageBoxView::InitParams box_params(base::UTF8ToUTF16(content)); - message_box_view_ = new views::MessageBoxView(box_params); - AddChildView(message_box_view_); - - for (size_t i = 0; i < buttons.size(); ++i) { - views::LabelButton* button = new views::LabelButton( - this, base::UTF8ToUTF16(buttons[i])); - button->set_tag(i); - button->SetMinSize(gfx::Size(60, 30)); - button->SetStyle(views::Button::STYLE_BUTTON); - button->SetGroup(kButtonGroup); - - buttons_.push_back(button); - AddChildView(button); - } - - // First button is always default button. - buttons_[0]->SetIsDefault(true); - buttons_[0]->AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE)); - - views::Widget::InitParams params; - params.delegate = this; - params.type = views::Widget::InitParams::TYPE_WINDOW; - params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - if (parent_) { - params.parent = parent_->GetNativeWindow(); - params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; - // Use bubble style for dialog has a parent. - params.remove_standard_frame = true; - } - - widget_.reset(new views::Widget); - widget_->Init(params); - widget_->UpdateWindowIcon(); - - // Bind to ESC. - AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE)); + int id = 0; + TaskDialogIndirect(&config, &id, NULL, NULL); + return id; } -MessageDialog::~MessageDialog() { -} - -void MessageDialog::Show(base::RunLoop* run_loop) { - run_loop_ = run_loop; - widget_->Show(); -} - -void MessageDialog::Close() { - dialog_scope_.reset(); - - if (delete_on_close_) { - callback_.Run(GetResult()); - base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); - } else if (run_loop_) { - run_loop_->Quit(); - } -} - -int MessageDialog::GetResult() const { - if (result_ == -1) - return cancel_id_; - else - return result_; -} - -//////////////////////////////////////////////////////////////////////////////// -// MessageDialog, private: - -base::string16 MessageDialog::GetWindowTitle() const { - return title_; -} - -gfx::ImageSkia MessageDialog::GetWindowAppIcon() { - return icon_; -} - -gfx::ImageSkia MessageDialog::GetWindowIcon() { - return icon_; -} - -bool MessageDialog::ShouldShowWindowIcon() const { - return true; -} - -views::Widget* MessageDialog::GetWidget() { - return widget_.get(); -} - -const views::Widget* MessageDialog::GetWidget() const { - return widget_.get(); -} - -views::View* MessageDialog::GetContentsView() { - return this; -} - -views::View* MessageDialog::GetInitiallyFocusedView() { - if (buttons_.size() > 0) - return buttons_[0]; - else - return this; -} - -ui::ModalType MessageDialog::GetModalType() const { - return ui::MODAL_TYPE_SYSTEM; -} - -views::NonClientFrameView* MessageDialog::CreateNonClientFrameView( - views::Widget* widget) { - if (!parent_) - return NULL; - - // Create a bubble style frame like Chrome. - views::BubbleFrameView* frame = new views::BubbleFrameView(gfx::Insets()); - const SkColor color = widget->GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_DialogBackground); - scoped_ptr border(new views::BubbleBorder( - views::BubbleBorder::FLOAT, views::BubbleBorder::SMALL_SHADOW, color)); - frame->SetBubbleBorder(border.Pass()); - wm::SetShadowType(widget->GetNativeWindow(), wm::SHADOW_TYPE_NONE); - return frame; -} - -views::ClientView* MessageDialog::CreateClientView(views::Widget* widget) { - return new MessageDialogClientView(this, widget); -} - -gfx::Size MessageDialog::GetPreferredSize() const { - gfx::Size size(0, buttons_[0]->GetPreferredSize().height()); - for (size_t i = 0; i < buttons_.size(); ++i) - size.Enlarge(buttons_[i]->GetPreferredSize().width(), 0); - - // Button spaces. - size.Enlarge(views::kRelatedButtonHSpacing * (buttons_.size() - 1), - views::kRelatedControlVerticalSpacing); - - // The message box view. - gfx::Size contents_size = message_box_view_->GetPreferredSize(); - size.Enlarge(0, contents_size.height()); - if (contents_size.width() > size.width()) - size.set_width(contents_size.width()); - - return size; -} - -void MessageDialog::Layout() { - gfx::Rect bounds = GetContentsBounds(); - - // Layout the row containing the buttons. - int x = bounds.width(); - int height = buttons_[0]->GetPreferredSize().height() + - views::kRelatedControlVerticalSpacing; - - // NB: We iterate through the buttons backwards here because - // Mac and Windows buttons are laid out in opposite order. - for (int i = buttons_.size() - 1; i >= 0; --i) { - gfx::Size size = buttons_[i]->GetPreferredSize(); - x -= size.width() + views::kRelatedButtonHSpacing; - - buttons_[i]->SetBounds(x, bounds.height() - height, - size.width(), size.height()); - } - - // Layout the message box view. - message_box_view_->SetBounds(bounds.x(), bounds.y(), bounds.width(), - bounds.height() - height); -} - -bool MessageDialog::AcceleratorPressed(const ui::Accelerator& accelerator) { - DCHECK_EQ(accelerator.key_code(), ui::VKEY_ESCAPE); - widget_->Close(); - return true; -} - -void MessageDialog::ButtonPressed(views::Button* sender, - const ui::Event& event) { - result_ = sender->tag(); - widget_->Close(); +void RunMessageBoxInNewThread(base::Thread* thread, + NativeWindow* parent, + MessageBoxType type, + const std::vector& buttons, + int cancel_id, + const std::string& title, + const std::string& message, + const std::string& detail, + const gfx::ImageSkia& icon, + const MessageBoxCallback& callback) { + int result = ShowMessageBox(parent, type, buttons, cancel_id, title, message, + detail, icon); + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, base::Bind(callback, result)); + content::BrowserThread::DeleteSoon( + content::BrowserThread::UI, FROM_HERE, thread); } } // namespace -int ShowMessageBox(NativeWindow* parent_window, +int ShowMessageBox(NativeWindow* parent, MessageBoxType type, const std::vector& buttons, int cancel_id, @@ -338,20 +71,24 @@ int ShowMessageBox(NativeWindow* parent_window, const std::string& message, const std::string& detail, const gfx::ImageSkia& icon) { - MessageDialog dialog( - parent_window, type, buttons, cancel_id, title, message, detail, icon); - { - base::MessageLoop::ScopedNestableTaskAllower allow( - base::MessageLoopForUI::current()); - base::RunLoop run_loop; - dialog.Show(&run_loop); - run_loop.Run(); - } + std::vector utf16_buttons; + for (const auto& button : buttons) + utf16_buttons.push_back(base::UTF8ToUTF16(button)); - return dialog.GetResult(); + HWND hwnd_parent = parent ? + static_cast(parent)->GetAcceleratedWidget() : + NULL; + + NativeWindow::DialogScope dialog_scope(parent); + return ShowMessageBoxUTF16(hwnd_parent, + utf16_buttons, + cancel_id, + base::UTF8ToUTF16(title), + base::UTF8ToUTF16(message), + base::UTF8ToUTF16(detail)); } -void ShowMessageBox(NativeWindow* parent_window, +void ShowMessageBox(NativeWindow* parent, MessageBoxType type, const std::vector& buttons, int cancel_id, @@ -360,15 +97,23 @@ void ShowMessageBox(NativeWindow* parent_window, const std::string& detail, const gfx::ImageSkia& icon, const MessageBoxCallback& callback) { - // The dialog would be deleted when the dialog is closed. - MessageDialog* dialog = new MessageDialog( - parent_window, type, buttons, cancel_id, title, message, detail, icon); - dialog->set_callback(callback); - dialog->Show(); + scoped_ptr thread( + new base::Thread(ATOM_PRODUCT_NAME "MessageBoxThread")); + thread->init_com_with_mta(false); + if (!thread->Start()) { + callback.Run(cancel_id); + return; + } + + base::Thread* unretained = thread.release(); + unretained->message_loop()->PostTask( + FROM_HERE, + base::Bind(&RunMessageBoxInNewThread, base::Unretained(unretained), + parent, type, buttons, cancel_id, title, message, detail, icon, + callback)); } void ShowErrorBox(const base::string16& title, const base::string16& content) { - ui::MessageBox(NULL, content, title, MB_OK | MB_ICONERROR | MB_TASKMODAL); } } // namespace atom