From 6b9148127e19bbd583f0cc756cdfd45f6dcff2f6 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 12 Mar 2014 20:17:22 +0800 Subject: [PATCH 1/7] gtk: Initial implementation of message box. --- browser/ui/message_box_gtk.cc | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/browser/ui/message_box_gtk.cc b/browser/ui/message_box_gtk.cc index f5fc72ee8021..a7eed861430e 100644 --- a/browser/ui/message_box_gtk.cc +++ b/browser/ui/message_box_gtk.cc @@ -4,7 +4,10 @@ #include "browser/ui/message_box.h" +#include + #include "base/callback.h" +#include "browser/native_window.h" namespace atom { @@ -24,6 +27,24 @@ void ShowMessageBox(NativeWindow* parent_window, const std::string& message, const std::string& detail, const MessageBoxCallback& callback) { + GtkWindow* window = parent_window ? parent_window->GetNativeWindow() : NULL; + GtkWidget* dialog = gtk_dialog_new_with_buttons( + title.c_str(), + window, + static_cast(GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR), + NULL); + + for (size_t i = 0; i < buttons.size(); ++i) + gtk_dialog_add_button(GTK_DIALOG(dialog), buttons[i].c_str(), i); + + GtkWidget* content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + GtkWidget* message_label = gtk_label_new(message.c_str()); + gtk_box_pack_start(GTK_BOX(content_area), message_label, FALSE, FALSE, 0); + GtkWidget* detail_label = gtk_label_new(detail.c_str()); + gtk_box_pack_start(GTK_BOX(content_area), detail_label, FALSE, FALSE, 0); + + gtk_widget_show_all(dialog); + callback.Run(0); } From e30fef09c8eea39133d3082a0ed1ddee257520ac Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 12 Mar 2014 20:38:52 +0800 Subject: [PATCH 2/7] gtk: Add gtk_util from Chromium. --- atom.gyp | 2 + browser/ui/gtk/gtk_util.cc | 89 ++++++++++++++++++++++++++++++++++++++ browser/ui/gtk/gtk_util.h | 31 +++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 browser/ui/gtk/gtk_util.cc create mode 100644 browser/ui/gtk/gtk_util.h diff --git a/atom.gyp b/atom.gyp index cce3b2de432f..b2f65d5d32d8 100644 --- a/atom.gyp +++ b/atom.gyp @@ -127,6 +127,8 @@ 'browser/ui/gtk/gtk_custom_menu.h', 'browser/ui/gtk/gtk_custom_menu_item.cc', 'browser/ui/gtk/gtk_custom_menu_item.h', + 'browser/ui/gtk/gtk_util.cc', + 'browser/ui/gtk/gtk_util.h', 'browser/ui/gtk/gtk_window_util.cc', 'browser/ui/gtk/gtk_window_util.h', 'browser/ui/message_box.h', diff --git a/browser/ui/gtk/gtk_util.cc b/browser/ui/gtk/gtk_util.cc new file mode 100644 index 000000000000..efb1b3429030 --- /dev/null +++ b/browser/ui/gtk/gtk_util.cc @@ -0,0 +1,89 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "browser/ui/gtk/gtk_util.h" + +#include + +#include "base/logging.h" + +namespace gtk_util { + +namespace { + +const char kBoldLabelMarkup[] = "%s"; + +// Returns the approximate number of characters that can horizontally fit in +// |pixel_width| pixels. +int GetCharacterWidthForPixels(GtkWidget* widget, int pixel_width) { + DCHECK(gtk_widget_get_realized(widget)) + << " widget must be realized to compute font metrics correctly"; + + PangoContext* context = gtk_widget_create_pango_context(widget); + GtkStyle* style = gtk_widget_get_style(widget); + PangoFontMetrics* metrics = pango_context_get_metrics(context, + style->font_desc, pango_context_get_language(context)); + + // This technique (max of char and digit widths) matches the code in + // gtklabel.c. + int char_width = pixel_width * PANGO_SCALE / + std::max(pango_font_metrics_get_approximate_char_width(metrics), + pango_font_metrics_get_approximate_digit_width(metrics)); + + pango_font_metrics_unref(metrics); + g_object_unref(context); + + return char_width; +} + +void OnLabelRealize(GtkWidget* label, gpointer pixel_width) { + gtk_label_set_width_chars( + GTK_LABEL(label), + GetCharacterWidthForPixels(label, GPOINTER_TO_INT(pixel_width))); +} + +} // namespace + +GtkWidget* LeftAlignMisc(GtkWidget* misc) { + gtk_misc_set_alignment(GTK_MISC(misc), 0, 0.5); + return misc; +} + +GtkWidget* CreateBoldLabel(const std::string& text) { + GtkWidget* label = gtk_label_new(NULL); + char* markup = g_markup_printf_escaped(kBoldLabelMarkup, text.c_str()); + gtk_label_set_markup(GTK_LABEL(label), markup); + g_free(markup); + + return LeftAlignMisc(label); +} + +bool IsWidgetAncestryVisible(GtkWidget* widget) { + GtkWidget* parent = widget; + while (parent && gtk_widget_get_visible(parent)) + parent = gtk_widget_get_parent(parent); + return !parent; +} + +void SetLabelWidth(GtkWidget* label, int pixel_width) { + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + // Do the simple thing in LTR because the bug only affects right-aligned + // text. Also, when using the workaround, the label tries to maintain + // uniform line-length, which we don't really want. + if (gtk_widget_get_direction(label) == GTK_TEXT_DIR_LTR) { + gtk_widget_set_size_request(label, pixel_width, -1); + } else { + // The label has to be realized before we can adjust its width. + if (gtk_widget_get_realized(label)) { + OnLabelRealize(label, GINT_TO_POINTER(pixel_width)); + } else { + g_signal_connect(label, "realize", G_CALLBACK(OnLabelRealize), + GINT_TO_POINTER(pixel_width)); + } + } +} + +} // namespace gtk_util diff --git a/browser/ui/gtk/gtk_util.h b/browser/ui/gtk/gtk_util.h new file mode 100644 index 000000000000..38961fc0b9dc --- /dev/null +++ b/browser/ui/gtk/gtk_util.h @@ -0,0 +1,31 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_UI_GTK_GTK_UTIL_H_ +#define ATOM_BROWSER_UI_GTK_GTK_UTIL_H_ + +#include +#include + +namespace gtk_util { + +// Left-align the given GtkMisc and return the same pointer. +GtkWidget* LeftAlignMisc(GtkWidget* misc); + +// Create a left-aligned label with the given text in bold. +GtkWidget* CreateBoldLabel(const std::string& text); + +// Checks whether a widget is actually visible, i.e. whether it and all its +// ancestors up to its toplevel are visible. +bool IsWidgetAncestryVisible(GtkWidget* widget); + +// Sets the given label's size request to |pixel_width|. This will cause the +// label to wrap if it needs to. The reason for this function is that some +// versions of GTK mis-align labels that have a size request and line wrapping, +// and this function hides the complexity of the workaround. +void SetLabelWidth(GtkWidget* label, int pixel_width); + +} // namespace gtk_util + +#endif // ATOM_BROWSER_UI_GTK_GTK_UTIL_H_ From df692f164c2123c397f932b81adb07b805cf9f3e Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 12 Mar 2014 20:39:04 +0800 Subject: [PATCH 3/7] gtk: Left align message box text. --- browser/ui/message_box_gtk.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/browser/ui/message_box_gtk.cc b/browser/ui/message_box_gtk.cc index a7eed861430e..ea58f0153662 100644 --- a/browser/ui/message_box_gtk.cc +++ b/browser/ui/message_box_gtk.cc @@ -4,10 +4,9 @@ #include "browser/ui/message_box.h" -#include - #include "base/callback.h" #include "browser/native_window.h" +#include "browser/ui/gtk/gtk_util.h" namespace atom { @@ -38,11 +37,14 @@ void ShowMessageBox(NativeWindow* parent_window, gtk_dialog_add_button(GTK_DIALOG(dialog), buttons[i].c_str(), i); GtkWidget* content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - GtkWidget* message_label = gtk_label_new(message.c_str()); + GtkWidget* message_label = gtk_util::CreateBoldLabel(message); + gtk_util::LeftAlignMisc(message_label); gtk_box_pack_start(GTK_BOX(content_area), message_label, FALSE, FALSE, 0); GtkWidget* detail_label = gtk_label_new(detail.c_str()); + gtk_util::LeftAlignMisc(detail_label); gtk_box_pack_start(GTK_BOX(content_area), detail_label, FALSE, FALSE, 0); + gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); gtk_widget_show_all(dialog); callback.Run(0); From 4d175b8f813b478cefa330c27ae3a669a54328a3 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 12 Mar 2014 21:01:04 +0800 Subject: [PATCH 4/7] gtk: Make the message box asynchronous. --- browser/ui/message_box_gtk.cc | 89 ++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 22 deletions(-) diff --git a/browser/ui/message_box_gtk.cc b/browser/ui/message_box_gtk.cc index ea58f0153662..f477743abbc4 100644 --- a/browser/ui/message_box_gtk.cc +++ b/browser/ui/message_box_gtk.cc @@ -7,9 +7,73 @@ #include "base/callback.h" #include "browser/native_window.h" #include "browser/ui/gtk/gtk_util.h" +#include "ui/base/gtk/gtk_signal.h" namespace atom { +namespace { + +class MessageBox { + public: + MessageBox(NativeWindow* parent_window, + MessageBoxType type, + const std::vector& buttons, + const std::string& title, + const std::string& message, + const std::string& detail) { + GtkWindow* window = parent_window ? parent_window->GetNativeWindow() : NULL; + dialog_ = gtk_dialog_new_with_buttons( + title.c_str(), + window, + static_cast(GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR), + NULL); + + for (size_t i = 0; i < buttons.size(); ++i) + gtk_dialog_add_button(GTK_DIALOG(dialog_), buttons[i].c_str(), i); + + GtkWidget* content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog_)); + GtkWidget* message_label = gtk_util::CreateBoldLabel(message); + gtk_util::LeftAlignMisc(message_label); + gtk_box_pack_start(GTK_BOX(content_area), message_label, FALSE, FALSE, 0); + GtkWidget* detail_label = gtk_label_new(detail.c_str()); + gtk_util::LeftAlignMisc(detail_label); + gtk_box_pack_start(GTK_BOX(content_area), detail_label, FALSE, FALSE, 0); + + gtk_window_set_resizable(GTK_WINDOW(dialog_), FALSE); + } + + ~MessageBox() { + gtk_widget_destroy(dialog_); + } + + void RunAsynchronous(const MessageBoxCallback& callback) { + callback_ = callback; + g_signal_connect(dialog_, "delete-event", + G_CALLBACK(gtk_widget_hide_on_delete), NULL); + g_signal_connect(dialog_, "response", + G_CALLBACK(OnResponseDialogThunk), this); + gtk_widget_show_all(dialog_); + } + + CHROMEGTK_CALLBACK_1(MessageBox, void, OnResponseDialog, int); + + GtkWidget* dialog() const { return dialog_; } + + private: + GtkWidget* dialog_; + MessageBoxCallback callback_; + + DISALLOW_COPY_AND_ASSIGN(MessageBox); +}; + +void MessageBox::OnResponseDialog(GtkWidget* widget, int response) { + gtk_widget_hide_all(dialog_); + callback_.Run(response); + delete this; +} + +} // namespace + int ShowMessageBox(NativeWindow* parent_window, MessageBoxType type, const std::vector& buttons, @@ -26,28 +90,9 @@ void ShowMessageBox(NativeWindow* parent_window, const std::string& message, const std::string& detail, const MessageBoxCallback& callback) { - GtkWindow* window = parent_window ? parent_window->GetNativeWindow() : NULL; - GtkWidget* dialog = gtk_dialog_new_with_buttons( - title.c_str(), - window, - static_cast(GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR), - NULL); - - for (size_t i = 0; i < buttons.size(); ++i) - gtk_dialog_add_button(GTK_DIALOG(dialog), buttons[i].c_str(), i); - - GtkWidget* content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - GtkWidget* message_label = gtk_util::CreateBoldLabel(message); - gtk_util::LeftAlignMisc(message_label); - gtk_box_pack_start(GTK_BOX(content_area), message_label, FALSE, FALSE, 0); - GtkWidget* detail_label = gtk_label_new(detail.c_str()); - gtk_util::LeftAlignMisc(detail_label); - gtk_box_pack_start(GTK_BOX(content_area), detail_label, FALSE, FALSE, 0); - - gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); - gtk_widget_show_all(dialog); - - callback.Run(0); + MessageBox* message_box = new MessageBox( + parent_window, type, buttons, title, message, detail); + message_box->RunAsynchronous(callback); } } // namespace atom From 5f215cfa6bd63fe2f840f4e10c9ee1fb2d56a54a Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 12 Mar 2014 21:10:00 +0800 Subject: [PATCH 5/7] gtk: Be aware of "Cancel" button in message box. --- browser/ui/message_box_gtk.cc | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/browser/ui/message_box_gtk.cc b/browser/ui/message_box_gtk.cc index f477743abbc4..f2988f6d5c18 100644 --- a/browser/ui/message_box_gtk.cc +++ b/browser/ui/message_box_gtk.cc @@ -5,6 +5,7 @@ #include "browser/ui/message_box.h" #include "base/callback.h" +#include "base/strings/string_util.h" #include "browser/native_window.h" #include "browser/ui/gtk/gtk_util.h" #include "ui/base/gtk/gtk_signal.h" @@ -20,7 +21,8 @@ class MessageBox { const std::vector& buttons, const std::string& title, const std::string& message, - const std::string& detail) { + const std::string& detail) + : cancel_id_(0) { GtkWindow* window = parent_window ? parent_window->GetNativeWindow() : NULL; dialog_ = gtk_dialog_new_with_buttons( title.c_str(), @@ -29,7 +31,9 @@ class MessageBox { NULL); for (size_t i = 0; i < buttons.size(); ++i) - gtk_dialog_add_button(GTK_DIALOG(dialog_), buttons[i].c_str(), i); + gtk_dialog_add_button(GTK_DIALOG(dialog_), + TranslateToStock(i, buttons[i]), + i); GtkWidget* content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog_)); GtkWidget* message_label = gtk_util::CreateBoldLabel(message); @@ -46,6 +50,22 @@ class MessageBox { gtk_widget_destroy(dialog_); } + const char* TranslateToStock(int id, const std::string& text) { + if (LowerCaseEqualsASCII(text, "cancel")) { + cancel_id_ = id; + return GTK_STOCK_CANCEL; + } else if (LowerCaseEqualsASCII(text, "no")) { + cancel_id_ = id; + return GTK_STOCK_NO; + } else if (LowerCaseEqualsASCII(text, "ok")) { + return GTK_STOCK_OK; + } else if (LowerCaseEqualsASCII(text, "yes")) { + return GTK_STOCK_YES; + } else { + return text.c_str(); + } + } + void RunAsynchronous(const MessageBoxCallback& callback) { callback_ = callback; g_signal_connect(dialog_, "delete-event", @@ -63,12 +83,19 @@ class MessageBox { GtkWidget* dialog_; MessageBoxCallback callback_; + // The id to return when the dialog is closed without pressing buttons. + int cancel_id_; + DISALLOW_COPY_AND_ASSIGN(MessageBox); }; void MessageBox::OnResponseDialog(GtkWidget* widget, int response) { gtk_widget_hide_all(dialog_); - callback_.Run(response); + + if (response < 0) + callback_.Run(cancel_id_); + else + callback_.Run(response); delete this; } From 676a48ae286b2ac01d61ae9d688746532a63be68 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 12 Mar 2014 21:17:13 +0800 Subject: [PATCH 6/7] gtk: Make synchronous message box work. --- browser/ui/message_box_gtk.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/browser/ui/message_box_gtk.cc b/browser/ui/message_box_gtk.cc index f2988f6d5c18..0fbb156f7dd9 100644 --- a/browser/ui/message_box_gtk.cc +++ b/browser/ui/message_box_gtk.cc @@ -78,6 +78,7 @@ class MessageBox { CHROMEGTK_CALLBACK_1(MessageBox, void, OnResponseDialog, int); GtkWidget* dialog() const { return dialog_; } + int cancel_id() const { return cancel_id_; } private: GtkWidget* dialog_; @@ -107,7 +108,13 @@ int ShowMessageBox(NativeWindow* parent_window, const std::string& title, const std::string& message, const std::string& detail) { - return 0; + MessageBox message_box(parent_window, type, buttons, title, message, detail); + gtk_widget_show_all(message_box.dialog()); + int response = gtk_dialog_run(GTK_DIALOG(message_box.dialog())); + if (response < 0) + return message_box.cancel_id(); + else + return response; } void ShowMessageBox(NativeWindow* parent_window, From 7c3ac01fd307900179b4ca73cc7e0b111c466060 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 12 Mar 2014 21:25:13 +0800 Subject: [PATCH 7/7] :lipstick: Fix pylint warning. --- script/create-dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/create-dist.py b/script/create-dist.py index f96f34ce4b5e..de08d34e000f 100755 --- a/script/create-dist.py +++ b/script/create-dist.py @@ -9,7 +9,7 @@ import tarfile from lib.config import LIBCHROMIUMCONTENT_COMMIT, BASE_URL, NODE_VERSION from lib.util import scoped_cwd, rm_rf, get_atom_shell_version, make_zip, \ - safe_mkdir, safe_unlink, execute + safe_mkdir, execute ATOM_SHELL_VRESION = get_atom_shell_version()