OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "chrome/browser/ui/gtk/infobars/infobar_gtk.h" | |
6 | |
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" | |
26 | |
27 namespace { | |
28 | |
29 // Pixels between infobar elements. | |
30 const int kElementPadding = 5; | |
31 | |
32 // Extra padding on either end of info bar. | |
33 const int kLeftPadding = 5; | |
34 const int kRightPadding = 5; | |
35 | |
36 } // namespace | |
37 | |
38 | |
39 // InfoBar -------------------------------------------------------------------- | |
40 | |
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; | |
48 | |
49 | |
50 // InfoBarGtk ----------------------------------------------------------------- | |
51 | |
52 // static | |
53 const int InfoBarGtk::kEndOfLabelSpacing = 6; | |
54 | |
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) { | |
61 } | |
62 | |
63 InfoBarGtk::~InfoBarGtk() { | |
64 } | |
65 | |
66 GdkColor InfoBarGtk::GetBorderColor() const { | |
67 DCHECK(theme_service_); | |
68 return theme_service_->GetBorderColor(); | |
69 } | |
70 | |
71 int InfoBarGtk::AnimatingHeight() const { | |
72 return animation().is_animating() ? bar_target_height() : 0; | |
73 } | |
74 | |
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); | |
79 } | |
80 | |
81 void InfoBarGtk::GetTopColor(InfoBarDelegate::Type type, | |
82 double* r, double* g, double* b) { | |
83 GetBackgroundColor(InfoBar::GetTopColor(type), r, g, b); | |
84 } | |
85 | |
86 void InfoBarGtk::GetBottomColor(InfoBarDelegate::Type type, | |
87 double* r, double* g, double* b) { | |
88 GetBackgroundColor(InfoBar::GetBottomColor(type), r, g, b); | |
89 } | |
90 | |
91 void InfoBarGtk::PlatformSpecificSetOwner() { | |
92 DCHECK(owner()); | |
93 DCHECK(!theme_service_); | |
94 theme_service_ = GtkThemeService::GetFrom(Profile::FromBrowserContext( | |
95 owner()->web_contents()->GetBrowserContext())); | |
96 | |
97 // Create |hbox_| and pad the sides. | |
98 hbox_ = gtk_hbox_new(FALSE, kElementPadding); | |
99 | |
100 // Make the whole infor bar horizontally shrinkable. | |
101 gtk_widget_set_size_request(hbox_, 0, -1); | |
102 | |
103 GtkWidget* padding = gtk_alignment_new(0, 0, 1, 1); | |
104 gtk_alignment_set_padding(GTK_ALIGNMENT(padding), | |
105 0, 0, kLeftPadding, kRightPadding); | |
106 | |
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); | |
113 | |
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()); | |
118 | |
119 gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.5); | |
120 | |
121 gtk_box_pack_start(GTK_BOX(hbox_), image, FALSE, FALSE, 0); | |
122 } | |
123 | |
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); | |
128 | |
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); | |
132 | |
133 g_signal_connect(widget_.get(), "child-size-request", | |
134 G_CALLBACK(OnChildSizeRequestThunk), | |
135 this); | |
136 | |
137 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED, | |
138 content::Source<ThemeService>(theme_service_)); | |
139 UpdateBorderColor(); | |
140 } | |
141 | |
142 void InfoBarGtk::PlatformSpecificShow(bool animate) { | |
143 DCHECK(bg_box_); | |
144 | |
145 DCHECK(widget()); | |
146 gtk_widget_show_all(widget()); | |
147 gtk_widget_set_size_request(widget(), -1, bar_height()); | |
148 | |
149 GdkWindow* gdk_window = gtk_widget_get_window(bg_box_); | |
150 if (gdk_window) | |
151 gdk_window_lower(gdk_window); | |
152 } | |
153 | |
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(); | |
159 } | |
160 | |
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()); | |
168 | |
169 gtk_widget_set_size_request(widget(), -1, bar_height()); | |
170 gtk_widget_queue_draw(widget()); | |
171 } | |
172 | |
173 void InfoBarGtk::Observe(int type, | |
174 const content::NotificationSource& source, | |
175 const content::NotificationDetails& details) { | |
176 DCHECK(widget()); | |
177 UpdateBorderColor(); | |
178 } | |
179 | |
180 void InfoBarGtk::ForceCloseButtonToUseChromeTheme() { | |
181 close_button_->ForceChromeTheme(); | |
182 } | |
183 | |
184 GtkWidget* InfoBarGtk::CreateLabel(const std::string& text) { | |
185 DCHECK(theme_service_); | |
186 return theme_service_->BuildLabel(text, ui::kGdkBlack); | |
187 } | |
188 | |
189 GtkWidget* InfoBarGtk::CreateLinkButton(const std::string& text) { | |
190 DCHECK(theme_service_); | |
191 return theme_service_->BuildChromeLinkButton(text); | |
192 } | |
193 | |
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); | |
200 | |
201 GtkWidget* hbox = gtk_hbox_new(FALSE, 0); | |
202 | |
203 GtkWidget* label = gtk_label_new(text.c_str()); | |
204 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); | |
205 | |
206 GtkWidget* arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE); | |
207 gtk_box_pack_start(GTK_BOX(hbox), arrow, FALSE, FALSE, 0); | |
208 | |
209 gtk_container_add(GTK_CONTAINER(button), hbox); | |
210 | |
211 return button; | |
212 } | |
213 | |
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); | |
225 | |
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); | |
231 | |
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))); | |
237 | |
238 gtk_util::ForceFontSizePixels(initial_label, 13.4); | |
239 gtk_util::ForceFontSizePixels(trailing_label, 13.4); | |
240 | |
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. | |
244 | |
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); | |
250 } | |
251 | |
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()); | |
257 } | |
258 | |
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; | |
267 } | |
268 | |
269 void InfoBarGtk::UpdateBorderColor() { | |
270 DCHECK(widget()); | |
271 gtk_widget_queue_draw(widget()); | |
272 } | |
273 | |
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(); | |
280 } | |
281 | |
282 gboolean InfoBarGtk::OnBackgroundExpose(GtkWidget* sender, | |
283 GdkEventExpose* event) { | |
284 TRACE_EVENT0("ui::gtk", "InfoBarGtk::OnBackgroundExpose"); | |
285 DCHECK(theme_service_); | |
286 | |
287 GtkAllocation allocation; | |
288 gtk_widget_get_allocation(sender, &allocation); | |
289 const int height = allocation.height; | |
290 | |
291 cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(sender)); | |
292 gdk_cairo_rectangle(cr, &event->area); | |
293 cairo_clip(cr); | |
294 | |
295 cairo_pattern_t* pattern = cairo_pattern_create_linear(0, 0, 0, height); | |
296 | |
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); | |
300 | |
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); | |
308 | |
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); | |
318 | |
319 cairo_destroy(cr); | |
320 | |
321 if (container()) { | |
322 static_cast<InfoBarContainerGtk*>(container())-> | |
323 PaintInfobarBitsOn(sender, event, this); | |
324 } | |
325 | |
326 return FALSE; | |
327 } | |
328 | |
329 void InfoBarGtk::OnChildSizeRequest(GtkWidget* expanded, | |
330 GtkWidget* child, | |
331 GtkRequisition* requisition) { | |
332 requisition->height = -1; | |
333 } | |
OLD | NEW |