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/panels/panel_titlebar_gtk.h" |
| 6 |
| 7 #include "base/utf_string_conversions.h" |
| 8 #include "chrome/browser/ui/gtk/custom_button.h" |
| 9 #include "chrome/browser/ui/gtk/gtk_theme_service.h" |
| 10 #include "chrome/browser/ui/gtk/gtk_util.h" |
| 11 #include "chrome/browser/ui/panels/panel.h" |
| 12 #include "chrome/browser/ui/panels/panel_gtk.h" |
| 13 #include "content/public/browser/web_contents.h" |
| 14 #include "grit/generated_resources.h" |
| 15 #include "grit/theme_resources.h" |
| 16 #include "third_party/skia/include/core/SkBitmap.h" |
| 17 #include "ui/base/gtk/gtk_compat.h" |
| 18 #include "ui/base/l10n/l10n_util.h" |
| 19 #include "ui/base/resource/resource_bundle.h" |
| 20 #include "ui/gfx/skia_utils_gtk.h" |
| 21 |
| 22 namespace { |
| 23 |
| 24 // Padding around the titlebar. |
| 25 const int kPanelTitlebarPaddingTop = 7; |
| 26 const int kPanelTitlebarPaddingBottom = 7; |
| 27 const int kPanelTitlebarPaddingLeft = 4; |
| 28 const int kPanelTitlebarPaddingRight = 8; |
| 29 |
| 30 // Spacing between buttons of panel's titlebar. |
| 31 const int kPanelButtonSpacing = 5; |
| 32 |
| 33 // Spacing between the icon and the title text. |
| 34 const int kPanelIconTitleSpacing = 9; |
| 35 |
| 36 // Color used to draw title text under default theme. |
| 37 const SkColor kTitleTextDefaultColor = SkColorSetRGB(0xf9, 0xf9, 0xf9); |
| 38 |
| 39 // Markup used to paint the title with the desired font. |
| 40 const char* const kTitleMarkupPrefix = "<span face='Arial' size='11264'>"; |
| 41 const char* const kTitleMarkupSuffix = "</span>"; |
| 42 |
| 43 } // namespace |
| 44 |
| 45 PanelTitlebarGtk::PanelTitlebarGtk(PanelGtk* panel_gtk) |
| 46 : panel_gtk_(panel_gtk), |
| 47 container_(NULL), |
| 48 titlebar_right_buttons_vbox_(NULL), |
| 49 titlebar_right_buttons_hbox_(NULL), |
| 50 icon_(NULL), |
| 51 title_(NULL), |
| 52 theme_service_(GtkThemeService::GetFrom(panel_gtk_->panel()->profile())) { |
| 53 } |
| 54 |
| 55 PanelTitlebarGtk::~PanelTitlebarGtk() { |
| 56 } |
| 57 |
| 58 void PanelTitlebarGtk::Init() { |
| 59 container_ = gtk_event_box_new(); |
| 60 gtk_widget_set_name(container_, "chrome-panel-titlebar"); |
| 61 gtk_event_box_set_visible_window(GTK_EVENT_BOX(container_), FALSE); |
| 62 |
| 63 // We use an alignment to control the titlebar paddings. |
| 64 GtkWidget* container_alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); |
| 65 gtk_container_add(GTK_CONTAINER(container_), container_alignment); |
| 66 gtk_alignment_set_padding(GTK_ALIGNMENT(container_alignment), |
| 67 kPanelTitlebarPaddingTop, |
| 68 kPanelTitlebarPaddingBottom, |
| 69 kPanelTitlebarPaddingLeft, |
| 70 kPanelTitlebarPaddingRight); |
| 71 |
| 72 // Add a container box. |
| 73 GtkWidget* container_hbox = gtk_hbox_new(FALSE, 0); |
| 74 gtk_container_add(GTK_CONTAINER(container_alignment), container_hbox); |
| 75 |
| 76 // Add minimize/restore and close buttons. Panel buttons are always placed |
| 77 // on the right part of the titlebar. |
| 78 titlebar_right_buttons_vbox_ = gtk_vbox_new(FALSE, 0); |
| 79 gtk_box_pack_end(GTK_BOX(container_hbox), titlebar_right_buttons_vbox_, |
| 80 FALSE, FALSE, 0); |
| 81 BuildButtons(); |
| 82 |
| 83 // Add hbox for holding icon and title. |
| 84 GtkWidget* icon_title_hbox = gtk_hbox_new(FALSE, kPanelIconTitleSpacing); |
| 85 gtk_box_pack_start(GTK_BOX(container_hbox), icon_title_hbox, TRUE, TRUE, 0); |
| 86 |
| 87 // Add icon. We use the app logo as a placeholder image so the title doesn't |
| 88 // jump around. |
| 89 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| 90 icon_ = gtk_image_new_from_pixbuf(rb.GetNativeImageNamed( |
| 91 IDR_PRODUCT_LOGO_16, ui::ResourceBundle::RTL_ENABLED).ToGdkPixbuf()); |
| 92 g_object_set_data(G_OBJECT(icon_), "left-align-popup", |
| 93 reinterpret_cast<void*>(true)); |
| 94 gtk_box_pack_start(GTK_BOX(icon_title_hbox), icon_, FALSE, FALSE, 0); |
| 95 |
| 96 // Add title. |
| 97 title_ = gtk_label_new(NULL); |
| 98 gtk_label_set_ellipsize(GTK_LABEL(title_), PANGO_ELLIPSIZE_END); |
| 99 gtk_misc_set_alignment(GTK_MISC(title_), 0.0, 0.5); |
| 100 gtk_box_pack_start(GTK_BOX(icon_title_hbox), title_, TRUE, TRUE, 0); |
| 101 UpdateTitleAndIcon(); |
| 102 UpdateTextColor(); |
| 103 |
| 104 gtk_widget_show_all(container_); |
| 105 } |
| 106 |
| 107 SkColor PanelTitlebarGtk::GetTextColor() const { |
| 108 if (panel_gtk_->UsingDefaultTheme()) |
| 109 return kTitleTextDefaultColor; |
| 110 return theme_service_->GetColor(panel_gtk_->paint_state() == |
| 111 PanelGtk::PAINT_AS_ACTIVE ? |
| 112 ThemeService::COLOR_TAB_TEXT : |
| 113 ThemeService::COLOR_BACKGROUND_TAB_TEXT); |
| 114 } |
| 115 |
| 116 void PanelTitlebarGtk::BuildButtons() { |
| 117 minimize_button_.reset(CreateButton(panel::MINIMIZE_BUTTON)); |
| 118 restore_button_.reset(CreateButton(panel::RESTORE_BUTTON)); |
| 119 close_button_.reset(CreateButton(panel::CLOSE_BUTTON)); |
| 120 |
| 121 // We control visibility of minimize and restore buttons. |
| 122 gtk_widget_set_no_show_all(minimize_button_->widget(), TRUE); |
| 123 gtk_widget_set_no_show_all(restore_button_->widget(), TRUE); |
| 124 |
| 125 // Now show the correct widgets in the two hierarchies. |
| 126 UpdateMinimizeRestoreButtonVisibility(); |
| 127 } |
| 128 |
| 129 CustomDrawButton* PanelTitlebarGtk::CreateButton( |
| 130 panel::TitlebarButtonType button_type) { |
| 131 int normal_image_id = -1; |
| 132 int pressed_image_id = -1; |
| 133 int hover_image_id = -1; |
| 134 int tooltip_id = -1; |
| 135 GetButtonResources(button_type, &normal_image_id, &pressed_image_id, |
| 136 &hover_image_id, &tooltip_id); |
| 137 |
| 138 CustomDrawButton* button = new CustomDrawButton(normal_image_id, |
| 139 pressed_image_id, |
| 140 hover_image_id, |
| 141 0); |
| 142 gtk_widget_add_events(GTK_WIDGET(button->widget()), GDK_POINTER_MOTION_MASK); |
| 143 g_signal_connect(button->widget(), "clicked", |
| 144 G_CALLBACK(OnButtonClickedThunk), this); |
| 145 |
| 146 std::string localized_tooltip = l10n_util::GetStringUTF8(tooltip_id); |
| 147 gtk_widget_set_tooltip_text(button->widget(), |
| 148 localized_tooltip.c_str()); |
| 149 |
| 150 GtkWidget* box = GetButtonHBox(); |
| 151 gtk_box_pack_start(GTK_BOX(box), button->widget(), FALSE, FALSE, 0); |
| 152 return button; |
| 153 } |
| 154 |
| 155 void PanelTitlebarGtk::GetButtonResources( |
| 156 panel::TitlebarButtonType button_type, |
| 157 int* normal_image_id, |
| 158 int* pressed_image_id, |
| 159 int* hover_image_id, |
| 160 int* tooltip_id) const { |
| 161 switch (button_type) { |
| 162 case panel::CLOSE_BUTTON: |
| 163 *normal_image_id = IDR_PANEL_CLOSE; |
| 164 *pressed_image_id = IDR_PANEL_CLOSE_C; |
| 165 *hover_image_id = IDR_PANEL_CLOSE_H; |
| 166 *tooltip_id = IDS_PANEL_CLOSE_TOOLTIP; |
| 167 break; |
| 168 case panel::MINIMIZE_BUTTON: |
| 169 *normal_image_id = IDR_PANEL_MINIMIZE; |
| 170 *pressed_image_id = IDR_PANEL_MINIMIZE_C; |
| 171 *hover_image_id = IDR_PANEL_MINIMIZE_H; |
| 172 *tooltip_id = IDS_PANEL_MINIMIZE_TOOLTIP; |
| 173 break; |
| 174 case panel::RESTORE_BUTTON: |
| 175 *normal_image_id = IDR_PANEL_RESTORE; |
| 176 *pressed_image_id = IDR_PANEL_RESTORE_C; |
| 177 *hover_image_id = IDR_PANEL_RESTORE_H; |
| 178 *tooltip_id = IDS_PANEL_RESTORE_TOOLTIP; |
| 179 break; |
| 180 } |
| 181 } |
| 182 |
| 183 GtkWidget* PanelTitlebarGtk::GetButtonHBox() { |
| 184 if (!titlebar_right_buttons_hbox_) { |
| 185 // We put the minimize/restore/close buttons in a vbox so they are top |
| 186 // aligned (up to padding) and don't vertically stretch. |
| 187 titlebar_right_buttons_hbox_ = gtk_hbox_new(FALSE, kPanelButtonSpacing); |
| 188 gtk_box_pack_start(GTK_BOX(titlebar_right_buttons_vbox_), |
| 189 titlebar_right_buttons_hbox_, FALSE, FALSE, 0); |
| 190 } |
| 191 |
| 192 return titlebar_right_buttons_hbox_; |
| 193 } |
| 194 |
| 195 void PanelTitlebarGtk::UpdateTitleAndIcon() { |
| 196 std::string title_text = |
| 197 UTF16ToUTF8(panel_gtk_->panel()->GetWindowTitle()); |
| 198 |
| 199 // Add the markup to show the title in the desired font. |
| 200 gchar* escaped_title_text = g_markup_escape_text(title_text.c_str(), -1); |
| 201 gchar* title_text_with_markup = g_strconcat(kTitleMarkupPrefix, |
| 202 escaped_title_text, |
| 203 kTitleMarkupSuffix, |
| 204 NULL); |
| 205 gtk_label_set_markup(GTK_LABEL(title_), title_text_with_markup); |
| 206 g_free(escaped_title_text); |
| 207 g_free(title_text_with_markup); |
| 208 } |
| 209 |
| 210 void PanelTitlebarGtk::UpdateThrobber( |
| 211 content::WebContents* web_contents) { |
| 212 if (web_contents && web_contents->IsLoading()) { |
| 213 GdkPixbuf* icon_pixbuf = |
| 214 throbber_.GetNextFrame(web_contents->IsWaitingForResponse()); |
| 215 gtk_image_set_from_pixbuf(GTK_IMAGE(icon_), icon_pixbuf); |
| 216 } else { |
| 217 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| 218 |
| 219 SkBitmap icon = panel_gtk_->panel()->GetCurrentPageIcon(); |
| 220 if (icon.empty()) { |
| 221 // Fallback to the Chromium icon if the page has no icon. |
| 222 gtk_image_set_from_pixbuf(GTK_IMAGE(icon_), |
| 223 rb.GetNativeImageNamed(IDR_PRODUCT_LOGO_16).ToGdkPixbuf()); |
| 224 } else { |
| 225 GdkPixbuf* icon_pixbuf = gfx::GdkPixbufFromSkBitmap(icon); |
| 226 gtk_image_set_from_pixbuf(GTK_IMAGE(icon_), icon_pixbuf); |
| 227 g_object_unref(icon_pixbuf); |
| 228 } |
| 229 |
| 230 throbber_.Reset(); |
| 231 } |
| 232 } |
| 233 |
| 234 void PanelTitlebarGtk::UpdateTextColor() { |
| 235 GdkColor text_color = gfx::SkColorToGdkColor(GetTextColor()); |
| 236 gtk_util::SetLabelColor(title_, &text_color); |
| 237 } |
| 238 |
| 239 void PanelTitlebarGtk::UpdateMinimizeRestoreButtonVisibility() { |
| 240 Panel* panel = panel_gtk_->panel(); |
| 241 gtk_widget_set_visible(minimize_button_->widget(), panel->CanMinimize()); |
| 242 gtk_widget_set_visible(restore_button_->widget(), panel->CanRestore()); |
| 243 } |
| 244 |
| 245 void PanelTitlebarGtk::OnButtonClicked(GtkWidget* button) { |
| 246 Panel* panel = panel_gtk_->panel(); |
| 247 if (close_button_->widget() == button) { |
| 248 panel->Close(); |
| 249 return; |
| 250 } |
| 251 |
| 252 GdkEvent* event = gtk_get_current_event(); |
| 253 DCHECK(event && event->type == GDK_BUTTON_RELEASE); |
| 254 |
| 255 if (minimize_button_->widget() == button) { |
| 256 panel->OnMinimizeButtonClicked( |
| 257 (event->button.state & GDK_CONTROL_MASK) ? |
| 258 panel::APPLY_TO_ALL : panel::NO_MODIFIER); |
| 259 } else if (restore_button_->widget() == button) { |
| 260 panel->OnRestoreButtonClicked( |
| 261 (event->button.state & GDK_CONTROL_MASK) ? |
| 262 panel::APPLY_TO_ALL : panel::NO_MODIFIER); |
| 263 } |
| 264 |
| 265 gdk_event_free(event); |
| 266 } |
| 267 |
| 268 void PanelTitlebarGtk::SendEnterNotifyToCloseButtonIfUnderMouse() { |
| 269 if (!close_button()) |
| 270 return; |
| 271 |
| 272 gint x; |
| 273 gint y; |
| 274 GtkAllocation widget_allocation = close_button()->WidgetAllocation(); |
| 275 gtk_widget_get_pointer(GTK_WIDGET(close_button()->widget()), &x, &y); |
| 276 |
| 277 gfx::Rect button_rect(0, 0, widget_allocation.width, |
| 278 widget_allocation.height); |
| 279 if (!button_rect.Contains(x, y)) { |
| 280 // Mouse is not over the close button. |
| 281 return; |
| 282 } |
| 283 |
| 284 // Create and emit an enter-notify-event on close button. |
| 285 GValue return_value; |
| 286 return_value.g_type = G_TYPE_BOOLEAN; |
| 287 g_value_set_boolean(&return_value, false); |
| 288 |
| 289 GdkEvent* event = gdk_event_new(GDK_ENTER_NOTIFY); |
| 290 event->crossing.window = |
| 291 gtk_button_get_event_window(GTK_BUTTON(close_button()->widget())); |
| 292 event->crossing.send_event = FALSE; |
| 293 event->crossing.subwindow = gtk_widget_get_window(close_button()->widget()); |
| 294 event->crossing.time = gtk_util::XTimeNow(); |
| 295 event->crossing.x = x; |
| 296 event->crossing.y = y; |
| 297 event->crossing.x_root = widget_allocation.x; |
| 298 event->crossing.y_root = widget_allocation.y; |
| 299 event->crossing.mode = GDK_CROSSING_NORMAL; |
| 300 event->crossing.detail = GDK_NOTIFY_ANCESTOR; |
| 301 event->crossing.focus = true; |
| 302 event->crossing.state = 0; |
| 303 |
| 304 g_signal_emit_by_name(GTK_OBJECT(close_button()->widget()), |
| 305 "enter-notify-event", event, |
| 306 &return_value); |
| 307 } |
| 308 |
| 309 GtkWidget* PanelTitlebarGtk::widget() const { |
| 310 return container_; |
| 311 } |
OLD | NEW |