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/panels/panel_titlebar_gtk.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "chrome/browser/themes/theme_properties.h"
9 #include "chrome/browser/ui/gtk/custom_button.h"
10 #include "chrome/browser/ui/gtk/gtk_util.h"
11 #include "chrome/browser/ui/gtk/panels/panel_gtk.h"
12 #include "chrome/browser/ui/panels/panel.h"
13 #include "content/public/browser/web_contents.h"
14 #include "grit/generated_resources.h"
15 #include "grit/theme_resources.h"
16 #include "ui/base/l10n/l10n_util.h"
17 #include "ui/base/resource/resource_bundle.h"
18 #include "ui/gfx/gtk_compat.h"
19 #include "ui/gfx/image/image.h"
20 #include "ui/gfx/skia_utils_gtk.h"
24 // Padding around the titlebar.
25 const int kPanelTitlebarPaddingTop
= 4;
26 const int kPanelTitlebarPaddingBottom
= 8;
27 const int kPanelTitlebarPaddingLeft
= 6;
28 const int kPanelTitlebarPaddingRight
= 0;
30 // Padding around the box containing icon and title.
31 const int kPanelIconTitlePaddingTop
= 3;
32 const int kPanelIconTitlePaddingBottom
= 0;
33 const int kPanelIconTitlePaddingLeft
= 0;
34 const int kPanelIconTitlePaddingRight
= 0;
36 // Spacing between buttons of panel's titlebar.
37 const int kPanelButtonSpacing
= 5;
39 // Spacing between the icon and the title text.
40 const int kPanelIconTitleSpacing
= 9;
42 // Color used to draw title text under default theme.
43 const SkColor kTitleTextDefaultColor
= SkColorSetRGB(0xf9, 0xf9, 0xf9);
45 // Markup used to paint the title with the desired font.
46 const char* const kTitleMarkupPrefix
=
47 "<span face='Arial' weight='bold' size='11264'>";
48 const char* const kTitleMarkupSuffix
= "</span>";
52 PanelTitlebarGtk::PanelTitlebarGtk(PanelGtk
* panel_gtk
)
53 : panel_gtk_(panel_gtk
),
55 titlebar_right_buttons_vbox_(NULL
),
56 titlebar_right_buttons_hbox_(NULL
),
61 PanelTitlebarGtk::~PanelTitlebarGtk() {
64 void PanelTitlebarGtk::Init() {
65 container_
= gtk_event_box_new();
66 gtk_widget_set_name(container_
, "chrome-panel-titlebar");
67 gtk_event_box_set_visible_window(GTK_EVENT_BOX(container_
), FALSE
);
69 // We use an alignment to control the titlebar paddings.
70 GtkWidget
* container_alignment
= gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
71 gtk_container_add(GTK_CONTAINER(container_
), container_alignment
);
72 gtk_alignment_set_padding(GTK_ALIGNMENT(container_alignment
),
73 kPanelTitlebarPaddingTop
,
74 kPanelTitlebarPaddingBottom
,
75 kPanelTitlebarPaddingLeft
,
76 kPanelTitlebarPaddingRight
);
78 // Add a container box.
79 GtkWidget
* container_hbox
= gtk_hbox_new(FALSE
, 0);
80 gtk_container_add(GTK_CONTAINER(container_alignment
), container_hbox
);
82 // Add minimize/restore and close buttons. Panel buttons are always placed
83 // on the right part of the titlebar.
84 titlebar_right_buttons_vbox_
= gtk_vbox_new(FALSE
, 0);
85 gtk_box_pack_end(GTK_BOX(container_hbox
), titlebar_right_buttons_vbox_
,
89 // Add an extra alignment to control the paddings for icon and title.
90 GtkWidget
* icon_title_alignment
= gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
91 gtk_container_add(GTK_CONTAINER(container_hbox
), icon_title_alignment
);
92 gtk_alignment_set_padding(GTK_ALIGNMENT(icon_title_alignment
),
93 kPanelIconTitlePaddingTop
,
94 kPanelIconTitlePaddingBottom
,
95 kPanelIconTitlePaddingLeft
,
96 kPanelIconTitlePaddingRight
);
98 // Add hbox for holding icon and title.
99 GtkWidget
* icon_title_hbox
= gtk_hbox_new(FALSE
, kPanelIconTitleSpacing
);
100 gtk_container_add(GTK_CONTAINER(icon_title_alignment
), icon_title_hbox
);
102 // Add icon. We use the app logo as a placeholder image so the title doesn't
104 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
105 icon_
= gtk_image_new_from_pixbuf(rb
.GetNativeImageNamed(
106 IDR_PRODUCT_LOGO_16
, ui::ResourceBundle::RTL_ENABLED
).ToGdkPixbuf());
107 g_object_set_data(G_OBJECT(icon_
), "left-align-popup",
108 reinterpret_cast<void*>(true));
109 gtk_box_pack_start(GTK_BOX(icon_title_hbox
), icon_
, FALSE
, FALSE
, 0);
112 title_
= gtk_label_new(NULL
);
113 gtk_label_set_ellipsize(GTK_LABEL(title_
), PANGO_ELLIPSIZE_END
);
114 gtk_misc_set_alignment(GTK_MISC(title_
), 0.0, 0.5);
115 gtk_box_pack_start(GTK_BOX(icon_title_hbox
), title_
, TRUE
, TRUE
, 0);
116 UpdateTitleAndIcon();
119 gtk_widget_show_all(container_
);
122 SkColor
PanelTitlebarGtk::GetTextColor() const {
123 return kTitleTextDefaultColor
;
126 void PanelTitlebarGtk::BuildButtons() {
127 minimize_button_
.reset(CreateButton(panel::MINIMIZE_BUTTON
));
128 restore_button_
.reset(CreateButton(panel::RESTORE_BUTTON
));
129 close_button_
.reset(CreateButton(panel::CLOSE_BUTTON
));
131 // We control visibility of minimize and restore buttons.
132 gtk_widget_set_no_show_all(minimize_button_
->widget(), TRUE
);
133 gtk_widget_set_no_show_all(restore_button_
->widget(), TRUE
);
135 // Now show the correct widgets in the two hierarchies.
136 UpdateMinimizeRestoreButtonVisibility();
139 CustomDrawButton
* PanelTitlebarGtk::CreateButton(
140 panel::TitlebarButtonType button_type
) {
141 int normal_image_id
= -1;
142 int pressed_image_id
= -1;
143 int hover_image_id
= -1;
145 GetButtonResources(button_type
, &normal_image_id
, &pressed_image_id
,
146 &hover_image_id
, &tooltip_id
);
148 CustomDrawButton
* button
= new CustomDrawButton(normal_image_id
,
152 gtk_widget_set_size_request(button
->widget(),
153 panel::kPanelButtonSize
,
154 panel::kPanelButtonSize
);
155 gtk_widget_add_events(GTK_WIDGET(button
->widget()), GDK_POINTER_MOTION_MASK
);
156 g_signal_connect(button
->widget(), "clicked",
157 G_CALLBACK(OnButtonClickedThunk
), this);
159 std::string localized_tooltip
= l10n_util::GetStringUTF8(tooltip_id
);
160 gtk_widget_set_tooltip_text(button
->widget(),
161 localized_tooltip
.c_str());
163 GtkWidget
* box
= GetButtonHBox();
164 gtk_box_pack_start(GTK_BOX(box
), button
->widget(), FALSE
, FALSE
, 0);
168 void PanelTitlebarGtk::GetButtonResources(
169 panel::TitlebarButtonType button_type
,
170 int* normal_image_id
,
171 int* pressed_image_id
,
173 int* tooltip_id
) const {
174 switch (button_type
) {
175 case panel::CLOSE_BUTTON
:
176 *normal_image_id
= IDR_PANEL_CLOSE
;
177 *pressed_image_id
= IDR_PANEL_CLOSE_C
;
178 *hover_image_id
= IDR_PANEL_CLOSE_H
;
179 *tooltip_id
= IDS_PANEL_CLOSE_TOOLTIP
;
181 case panel::MINIMIZE_BUTTON
:
182 *normal_image_id
= IDR_PANEL_MINIMIZE
;
183 *pressed_image_id
= IDR_PANEL_MINIMIZE_C
;
184 *hover_image_id
= IDR_PANEL_MINIMIZE_H
;
185 *tooltip_id
= IDS_PANEL_MINIMIZE_TOOLTIP
;
187 case panel::RESTORE_BUTTON
:
188 *normal_image_id
= IDR_PANEL_RESTORE
;
189 *pressed_image_id
= IDR_PANEL_RESTORE_C
;
190 *hover_image_id
= IDR_PANEL_RESTORE_H
;
191 *tooltip_id
= IDS_PANEL_RESTORE_TOOLTIP
;
196 GtkWidget
* PanelTitlebarGtk::GetButtonHBox() {
197 if (!titlebar_right_buttons_hbox_
) {
198 // We put the minimize/restore/close buttons in a vbox so they are top
199 // aligned (up to padding) and don't vertically stretch.
200 titlebar_right_buttons_hbox_
= gtk_hbox_new(FALSE
, kPanelButtonSpacing
);
201 gtk_box_pack_start(GTK_BOX(titlebar_right_buttons_vbox_
),
202 titlebar_right_buttons_hbox_
, FALSE
, FALSE
, 0);
205 return titlebar_right_buttons_hbox_
;
208 void PanelTitlebarGtk::UpdateTitleAndIcon() {
209 std::string title_text
=
210 base::UTF16ToUTF8(panel_gtk_
->panel()->GetWindowTitle());
212 // Add the markup to show the title in the desired font.
213 gchar
* escaped_title_text
= g_markup_escape_text(title_text
.c_str(), -1);
214 gchar
* title_text_with_markup
= g_strconcat(kTitleMarkupPrefix
,
218 gtk_label_set_markup(GTK_LABEL(title_
), title_text_with_markup
);
219 g_free(escaped_title_text
);
220 g_free(title_text_with_markup
);
222 // Update icon from the web contents.
223 content::WebContents
* web_contents
= panel_gtk_
->panel()->GetWebContents();
225 UpdateThrobber(web_contents
);
228 void PanelTitlebarGtk::UpdateThrobber(
229 content::WebContents
* web_contents
) {
230 if (web_contents
&& web_contents
->IsLoading()) {
231 GdkPixbuf
* icon_pixbuf
=
232 throbber_
.GetNextFrame(web_contents
->IsWaitingForResponse());
233 gtk_image_set_from_pixbuf(GTK_IMAGE(icon_
), icon_pixbuf
);
235 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
237 gfx::Image icon
= panel_gtk_
->panel()->GetCurrentPageIcon();
238 if (icon
.IsEmpty()) {
239 // Fallback to the Chromium icon if the page has no icon.
240 gtk_image_set_from_pixbuf(GTK_IMAGE(icon_
),
241 rb
.GetNativeImageNamed(IDR_PRODUCT_LOGO_16
).ToGdkPixbuf());
243 gtk_image_set_from_pixbuf(GTK_IMAGE(icon_
), icon
.ToGdkPixbuf());
250 void PanelTitlebarGtk::UpdateTextColor() {
251 GdkColor text_color
= gfx::SkColorToGdkColor(GetTextColor());
252 gtk_util::SetLabelColor(title_
, &text_color
);
255 void PanelTitlebarGtk::UpdateMinimizeRestoreButtonVisibility() {
256 Panel
* panel
= panel_gtk_
->panel();
257 gtk_widget_set_visible(minimize_button_
->widget(),
258 panel
->CanShowMinimizeButton());
259 gtk_widget_set_visible(restore_button_
->widget(),
260 panel
->CanShowRestoreButton());
263 void PanelTitlebarGtk::OnButtonClicked(GtkWidget
* button
) {
264 Panel
* panel
= panel_gtk_
->panel();
265 if (close_button_
->widget() == button
) {
270 GdkEvent
* event
= gtk_get_current_event();
271 DCHECK(event
&& event
->type
== GDK_BUTTON_RELEASE
);
273 if (minimize_button_
->widget() == button
) {
274 panel
->OnMinimizeButtonClicked(
275 (event
->button
.state
& GDK_CONTROL_MASK
) ?
276 panel::APPLY_TO_ALL
: panel::NO_MODIFIER
);
277 } else if (restore_button_
->widget() == button
) {
278 panel
->OnRestoreButtonClicked(
279 (event
->button
.state
& GDK_CONTROL_MASK
) ?
280 panel::APPLY_TO_ALL
: panel::NO_MODIFIER
);
284 gdk_event_free(event
);
287 void PanelTitlebarGtk::SendEnterNotifyToCloseButtonIfUnderMouse() {
293 GtkAllocation widget_allocation
= close_button()->WidgetAllocation();
294 gtk_widget_get_pointer(GTK_WIDGET(close_button()->widget()), &x
, &y
);
296 gfx::Rect
button_rect(0, 0, widget_allocation
.width
,
297 widget_allocation
.height
);
298 if (!button_rect
.Contains(x
, y
)) {
299 // Mouse is not over the close button.
303 // Create and emit an enter-notify-event on close button.
305 return_value
.g_type
= G_TYPE_BOOLEAN
;
306 g_value_set_boolean(&return_value
, false);
308 GdkEvent
* event
= gdk_event_new(GDK_ENTER_NOTIFY
);
309 event
->crossing
.window
=
310 gtk_button_get_event_window(GTK_BUTTON(close_button()->widget()));
311 event
->crossing
.send_event
= FALSE
;
312 event
->crossing
.subwindow
= gtk_widget_get_window(close_button()->widget());
313 event
->crossing
.time
= gtk_util::XTimeNow();
314 event
->crossing
.x
= x
;
315 event
->crossing
.y
= y
;
316 event
->crossing
.x_root
= widget_allocation
.x
;
317 event
->crossing
.y_root
= widget_allocation
.y
;
318 event
->crossing
.mode
= GDK_CROSSING_NORMAL
;
319 event
->crossing
.detail
= GDK_NOTIFY_ANCESTOR
;
320 event
->crossing
.focus
= true;
321 event
->crossing
.state
= 0;
323 g_signal_emit_by_name(GTK_OBJECT(close_button()->widget()),
324 "enter-notify-event", event
,
328 GtkWidget
* PanelTitlebarGtk::widget() const {