1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/gtk/infobars/infobar_gtk.h"
7 #include "base/debug/trace_event.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/chrome_notification_types.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/themes/theme_properties.h"
12 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
13 #include "chrome/browser/ui/gtk/custom_button.h"
14 #include "chrome/browser/ui/gtk/gtk_chrome_link_button.h"
15 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
16 #include "chrome/browser/ui/gtk/gtk_util.h"
17 #include "chrome/browser/ui/gtk/infobars/infobar_container_gtk.h"
18 #include "content/public/browser/notification_source.h"
19 #include "content/public/browser/web_contents.h"
20 #include "ui/base/gtk/gtk_expanded_container.h"
21 #include "ui/base/gtk/gtk_hig_constants.h"
22 #include "ui/base/gtk/gtk_signal_registrar.h"
23 #include "ui/base/models/menu_model.h"
24 #include "ui/gfx/gtk_util.h"
25 #include "ui/gfx/image/image.h"
29 // Pixels between infobar elements.
30 const int kElementPadding = 5;
32 // Extra padding on either end of info bar.
33 const int kLeftPadding = 5;
34 const int kRightPadding = 5;
39 // InfoBar --------------------------------------------------------------------
42 const int InfoBar::kSeparatorLineHeight = 1;
43 const int InfoBar::kDefaultArrowTargetHeight = 9;
44 const int InfoBar::kMaximumArrowTargetHeight = 24;
45 const int InfoBar::kDefaultArrowTargetHalfWidth = kDefaultArrowTargetHeight;
46 const int InfoBar::kMaximumArrowTargetHalfWidth = 14;
47 const int InfoBar::kDefaultBarTargetHeight = 36;
50 // InfoBarGtk -----------------------------------------------------------------
53 const int InfoBarGtk::kEndOfLabelSpacing = 6;
55 InfoBarGtk::InfoBarGtk(InfoBarService* owner, InfoBarDelegate* delegate)
56 : InfoBar(owner, delegate),
60 signals_(new ui::GtkSignalRegistrar) {
63 InfoBarGtk::~InfoBarGtk() {
66 void InfoBarGtk::InitWidgets() {
68 DCHECK(!theme_service_);
69 theme_service_ = GtkThemeService::GetFrom(Profile::FromBrowserContext(
70 owner()->web_contents()->GetBrowserContext()));
72 // Create |hbox_| and pad the sides.
73 hbox_ = gtk_hbox_new(FALSE, kElementPadding);
75 // Make the whole infor bar horizontally shrinkable.
76 gtk_widget_set_size_request(hbox_, 0, -1);
78 GtkWidget* padding = gtk_alignment_new(0, 0, 1, 1);
79 gtk_alignment_set_padding(GTK_ALIGNMENT(padding),
80 0, 0, kLeftPadding, kRightPadding);
82 bg_box_ = gtk_event_box_new();
83 gtk_widget_set_app_paintable(bg_box_, TRUE);
84 g_signal_connect(bg_box_, "expose-event",
85 G_CALLBACK(OnBackgroundExposeThunk), this);
86 gtk_container_add(GTK_CONTAINER(padding), hbox_);
87 gtk_container_add(GTK_CONTAINER(bg_box_), padding);
89 // Add the icon on the left, if any.
90 gfx::Image icon = delegate()->GetIcon();
91 if (!icon.IsEmpty()) {
92 GtkWidget* image = gtk_image_new_from_pixbuf(icon.ToGdkPixbuf());
94 gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.5);
96 gtk_box_pack_start(GTK_BOX(hbox_), image, FALSE, FALSE, 0);
99 close_button_.reset(CustomDrawButton::CloseButtonBar(theme_service_));
100 gtk_util::CenterWidgetInHBox(hbox_, close_button_->widget(), true, 0);
101 signals_->Connect(close_button_->widget(), "clicked",
102 G_CALLBACK(OnCloseButtonThunk), this);
104 widget_.Own(gtk_expanded_container_new());
105 gtk_container_add(GTK_CONTAINER(widget_.get()), bg_box_);
106 gtk_widget_set_size_request(widget_.get(), -1, 0);
108 g_signal_connect(widget_.get(), "child-size-request",
109 G_CALLBACK(OnChildSizeRequestThunk),
112 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
113 content::Source<ThemeService>(theme_service_));
117 GdkColor InfoBarGtk::GetBorderColor() const {
118 DCHECK(theme_service_);
119 return theme_service_->GetBorderColor();
122 int InfoBarGtk::AnimatingHeight() const {
123 return animation().is_animating() ? bar_target_height() : 0;
126 SkColor InfoBarGtk::ConvertGetColor(ColorGetter getter) {
128 (this->*getter)(delegate()->GetInfoBarType(), &r, &g, &b);
129 return SkColorSetARGB(255, 255 * r, 255 * g, 255 * b);
132 void InfoBarGtk::GetTopColor(InfoBarDelegate::Type type,
133 double* r, double* g, double* b) {
134 GetBackgroundColor(GetInfoBarTopColor(type), r, g, b);
137 void InfoBarGtk::GetBottomColor(InfoBarDelegate::Type type,
138 double* r, double* g, double* b) {
139 GetBackgroundColor(GetInfoBarBottomColor(type), r, g, b);
142 void InfoBarGtk::PlatformSpecificShow(bool animate) {
146 gtk_widget_show_all(widget());
147 gtk_widget_set_size_request(widget(), -1, bar_height());
149 GdkWindow* gdk_window = gtk_widget_get_window(bg_box_);
151 gdk_window_lower(gdk_window);
154 void InfoBarGtk::PlatformSpecificOnCloseSoon() {
155 // We must close all menus and prevent any signals from being emitted while
156 // we are animating the info bar closed.
161 void InfoBarGtk::PlatformSpecificOnHeightsRecalculated() {
164 gtk_widget_set_size_request(bg_box_, -1, bar_target_height());
165 gtk_expanded_container_move(GTK_EXPANDED_CONTAINER(widget()),
167 bar_height() - bar_target_height());
169 gtk_widget_set_size_request(widget(), -1, bar_height());
170 gtk_widget_queue_draw(widget());
173 void InfoBarGtk::Observe(int type,
174 const content::NotificationSource& source,
175 const content::NotificationDetails& details) {
180 void InfoBarGtk::ForceCloseButtonToUseChromeTheme() {
181 close_button_->ForceChromeTheme();
184 GtkWidget* InfoBarGtk::CreateLabel(const std::string& text) {
185 DCHECK(theme_service_);
186 return theme_service_->BuildLabel(text, ui::kGdkBlack);
189 GtkWidget* InfoBarGtk::CreateLinkButton(const std::string& text) {
190 DCHECK(theme_service_);
191 return theme_service_->BuildChromeLinkButton(text);
195 GtkWidget* InfoBarGtk::CreateMenuButton(const std::string& text) {
196 GtkWidget* button = gtk_button_new();
197 GtkWidget* former_child = gtk_bin_get_child(GTK_BIN(button));
199 gtk_container_remove(GTK_CONTAINER(button), former_child);
201 GtkWidget* hbox = gtk_hbox_new(FALSE, 0);
203 GtkWidget* label = gtk_label_new(text.c_str());
204 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
206 GtkWidget* arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
207 gtk_box_pack_start(GTK_BOX(hbox), arrow, FALSE, FALSE, 0);
209 gtk_container_add(GTK_CONTAINER(button), hbox);
214 void InfoBarGtk::AddLabelWithInlineLink(const string16& display_text,
215 const string16& link_text,
217 GCallback callback) {
219 GtkWidget* link_button = CreateLinkButton(UTF16ToUTF8(link_text));
220 gtk_util::ForceFontSizePixels(
221 GTK_CHROME_LINK_BUTTON(link_button)->label, 13.4);
223 signals_->Connect(link_button, "clicked", callback, this);
224 gtk_util::SetButtonTriggersNavigation(link_button);
226 GtkWidget* hbox = gtk_hbox_new(FALSE, 0);
227 // We want the link to be horizontally shrinkable, so that the Chrome
228 // window can be resized freely even with a very long link.
229 gtk_widget_set_size_request(hbox, 0, -1);
230 gtk_box_pack_start(GTK_BOX(hbox_), hbox, TRUE, TRUE, 0);
232 // Need to insert the link inside the display text.
233 GtkWidget* initial_label = CreateLabel(
234 UTF16ToUTF8(display_text.substr(0, link_offset)));
235 GtkWidget* trailing_label = CreateLabel(
236 UTF16ToUTF8(display_text.substr(link_offset)));
238 gtk_util::ForceFontSizePixels(initial_label, 13.4);
239 gtk_util::ForceFontSizePixels(trailing_label, 13.4);
241 // TODO(joth): None of the label widgets are set as shrinkable here, meaning
242 // the text will run under the close button etc. when the width is restricted,
243 // rather than eliding.
245 // We don't want any spacing between the elements, so we pack them into
246 // this hbox that doesn't use kElementPadding.
247 gtk_box_pack_start(GTK_BOX(hbox), initial_label, FALSE, FALSE, 0);
248 gtk_util::CenterWidgetInHBox(hbox, link_button, false, 0);
249 gtk_box_pack_start(GTK_BOX(hbox), trailing_label, FALSE, FALSE, 0);
252 void InfoBarGtk::ShowMenuWithModel(GtkWidget* sender,
253 MenuGtk::Delegate* delegate,
254 ui::MenuModel* model) {
255 menu_.reset(new MenuGtk(delegate, model));
256 menu_->PopupForWidget(sender, 1, gtk_get_current_event_time());
259 void InfoBarGtk::GetBackgroundColor(SkColor color,
260 double* r, double* g, double* b) {
261 DCHECK(theme_service_);
262 if (theme_service_->UsingNativeTheme())
263 color = theme_service_->GetColor(ThemeProperties::COLOR_TOOLBAR);
264 *r = SkColorGetR(color) / 255.0;
265 *g = SkColorGetG(color) / 255.0;
266 *b = SkColorGetB(color) / 255.0;
269 void InfoBarGtk::UpdateBorderColor() {
271 gtk_widget_queue_draw(widget());
274 void InfoBarGtk::OnCloseButton(GtkWidget* button) {
275 // If we're not owned, we're already closing, so don't call
276 // InfoBarDismissed(), since this can lead to us double-recording dismissals.
277 if (delegate() && owner())
278 delegate()->InfoBarDismissed();
282 gboolean InfoBarGtk::OnBackgroundExpose(GtkWidget* sender,
283 GdkEventExpose* event) {
284 TRACE_EVENT0("ui::gtk", "InfoBarGtk::OnBackgroundExpose");
285 DCHECK(theme_service_);
287 GtkAllocation allocation;
288 gtk_widget_get_allocation(sender, &allocation);
289 const int height = allocation.height;
291 cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(sender));
292 gdk_cairo_rectangle(cr, &event->area);
295 cairo_pattern_t* pattern = cairo_pattern_create_linear(0, 0, 0, height);
297 double top_r, top_g, top_b;
298 GetTopColor(delegate()->GetInfoBarType(), &top_r, &top_g, &top_b);
299 cairo_pattern_add_color_stop_rgb(pattern, 0.0, top_r, top_g, top_b);
301 double bottom_r, bottom_g, bottom_b;
302 GetBottomColor(delegate()->GetInfoBarType(), &bottom_r, &bottom_g, &bottom_b);
303 cairo_pattern_add_color_stop_rgb(
304 pattern, 1.0, bottom_r, bottom_g, bottom_b);
305 cairo_set_source(cr, pattern);
307 cairo_pattern_destroy(pattern);
309 // Draw the bottom border.
310 GdkColor border_color = GetBorderColor();
311 cairo_set_source_rgb(cr, border_color.red / 65535.0,
312 border_color.green / 65535.0,
313 border_color.blue / 65535.0);
314 cairo_set_line_width(cr, 1.0);
315 cairo_move_to(cr, 0, allocation.height - 0.5);
316 cairo_rel_line_to(cr, allocation.width, 0);
322 static_cast<InfoBarContainerGtk*>(container())->
323 PaintInfobarBitsOn(sender, event, this);
329 void InfoBarGtk::OnChildSizeRequest(GtkWidget* expanded,
331 GtkRequisition* requisition) {
332 requisition->height = -1;