Disable TabDragController tests that fail with a real compositor.
[chromium-blink-merge.git] / chrome / browser / ui / gtk / infobars / infobar_gtk.cc
blob386811e34461f94cbcbeb58e31b1a0717c1d8a83
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"
27 namespace {
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;
36 } // namespace
39 // InfoBar --------------------------------------------------------------------
41 // static
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 -----------------------------------------------------------------
52 // static
53 const int InfoBarGtk::kEndOfLabelSpacing = 6;
55 InfoBarGtk::InfoBarGtk(scoped_ptr<InfoBarDelegate> delegate)
56 : InfoBar(delegate.Pass()),
57 bg_box_(NULL),
58 hbox_(NULL),
59 theme_service_(NULL),
60 signals_(new ui::GtkSignalRegistrar) {
63 InfoBarGtk::~InfoBarGtk() {
66 GdkColor InfoBarGtk::GetBorderColor() const {
67 DCHECK(theme_service_);
68 return theme_service_->GetBorderColor();
71 int InfoBarGtk::AnimatingHeight() const {
72 return animation().is_animating() ? bar_target_height() : 0;
75 SkColor InfoBarGtk::ConvertGetColor(ColorGetter getter) {
76 double r, g, b;
77 (this->*getter)(delegate()->GetInfoBarType(), &r, &g, &b);
78 return SkColorSetARGB(255, 255 * r, 255 * g, 255 * b);
81 void InfoBarGtk::GetTopColor(InfoBarDelegate::Type type,
82 double* r, double* g, double* b) {
83 GetBackgroundColor(InfoBar::GetTopColor(type), r, g, b);
86 void InfoBarGtk::GetBottomColor(InfoBarDelegate::Type type,
87 double* r, double* g, double* b) {
88 GetBackgroundColor(InfoBar::GetBottomColor(type), r, g, b);
91 void InfoBarGtk::PlatformSpecificSetOwner() {
92 DCHECK(owner());
93 DCHECK(!theme_service_);
94 theme_service_ = GtkThemeService::GetFrom(Profile::FromBrowserContext(
95 owner()->web_contents()->GetBrowserContext()));
97 // Create |hbox_| and pad the sides.
98 hbox_ = gtk_hbox_new(FALSE, kElementPadding);
100 // Make the whole infor bar horizontally shrinkable.
101 gtk_widget_set_size_request(hbox_, 0, -1);
103 GtkWidget* padding = gtk_alignment_new(0, 0, 1, 1);
104 gtk_alignment_set_padding(GTK_ALIGNMENT(padding),
105 0, 0, kLeftPadding, kRightPadding);
107 bg_box_ = gtk_event_box_new();
108 gtk_widget_set_app_paintable(bg_box_, TRUE);
109 g_signal_connect(bg_box_, "expose-event",
110 G_CALLBACK(OnBackgroundExposeThunk), this);
111 gtk_container_add(GTK_CONTAINER(padding), hbox_);
112 gtk_container_add(GTK_CONTAINER(bg_box_), padding);
114 // Add the icon on the left, if any.
115 gfx::Image icon = delegate()->GetIcon();
116 if (!icon.IsEmpty()) {
117 GtkWidget* image = gtk_image_new_from_pixbuf(icon.ToGdkPixbuf());
119 gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.5);
121 gtk_box_pack_start(GTK_BOX(hbox_), image, FALSE, FALSE, 0);
124 close_button_.reset(CustomDrawButton::CloseButtonBar(theme_service_));
125 gtk_util::CenterWidgetInHBox(hbox_, close_button_->widget(), true, 0);
126 signals_->Connect(close_button_->widget(), "clicked",
127 G_CALLBACK(OnCloseButtonThunk), this);
129 widget_.Own(gtk_expanded_container_new());
130 gtk_container_add(GTK_CONTAINER(widget_.get()), bg_box_);
131 gtk_widget_set_size_request(widget_.get(), -1, 0);
133 g_signal_connect(widget_.get(), "child-size-request",
134 G_CALLBACK(OnChildSizeRequestThunk),
135 this);
137 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
138 content::Source<ThemeService>(theme_service_));
139 UpdateBorderColor();
142 void InfoBarGtk::PlatformSpecificShow(bool animate) {
143 DCHECK(bg_box_);
145 DCHECK(widget());
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_);
150 if (gdk_window)
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.
157 menu_.reset();
158 signals_.reset();
161 void InfoBarGtk::PlatformSpecificOnHeightsRecalculated() {
162 DCHECK(bg_box_);
163 DCHECK(widget());
164 gtk_widget_set_size_request(bg_box_, -1, bar_target_height());
165 gtk_expanded_container_move(GTK_EXPANDED_CONTAINER(widget()),
166 bg_box_, 0,
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) {
176 DCHECK(widget());
177 UpdateBorderColor();
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);
194 // static
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));
198 if (former_child)
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);
211 return button;
214 void InfoBarGtk::AddLabelWithInlineLink(const base::string16& display_text,
215 const base::string16& link_text,
216 size_t link_offset,
217 GCallback callback) {
218 DCHECK(hbox_);
219 GtkWidget* link_button = CreateLinkButton(base::UTF16ToUTF8(link_text));
220 gtk_util::ForceFontSizePixels(
221 GTK_CHROME_LINK_BUTTON(link_button)->label, 13.4);
222 DCHECK(callback);
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 base::UTF16ToUTF8(display_text.substr(0, link_offset)));
235 GtkWidget* trailing_label = CreateLabel(
236 base::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() {
270 DCHECK(widget());
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 (owner())
278 delegate()->InfoBarDismissed();
279 RemoveSelf();
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);
293 cairo_clip(cr);
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);
306 cairo_paint(cr);
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);
317 cairo_stroke(cr);
319 cairo_destroy(cr);
321 if (container()) {
322 static_cast<InfoBarContainerGtk*>(container())->
323 PaintInfobarBitsOn(sender, event, this);
326 return FALSE;
329 void InfoBarGtk::OnChildSizeRequest(GtkWidget* expanded,
330 GtkWidget* child,
331 GtkRequisition* requisition) {
332 requisition->height = -1;