| 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/custom_button.h" | |
| 6 | |
| 7 #include "base/basictypes.h" | |
| 8 #include "base/debug/trace_event.h" | |
| 9 #include "base/logging.h" | |
| 10 #include "chrome/browser/chrome_notification_types.h" | |
| 11 #include "chrome/browser/ui/gtk/gtk_chrome_button.h" | |
| 12 #include "chrome/browser/ui/gtk/gtk_theme_service.h" | |
| 13 #include "chrome/browser/ui/gtk/gtk_util.h" | |
| 14 #include "content/public/browser/notification_source.h" | |
| 15 #include "grit/theme_resources.h" | |
| 16 #include "grit/ui_resources.h" | |
| 17 #include "third_party/skia/include/core/SkBitmap.h" | |
| 18 #include "ui/base/resource/resource_bundle.h" | |
| 19 #include "ui/gfx/gtk_util.h" | |
| 20 #include "ui/gfx/image/cairo_cached_surface.h" | |
| 21 #include "ui/gfx/image/image.h" | |
| 22 #include "ui/gfx/skbitmap_operations.h" | |
| 23 | |
| 24 namespace { | |
| 25 | |
| 26 GdkPixbuf* GetImage(int resource_id) { | |
| 27 if (!resource_id) | |
| 28 return NULL; | |
| 29 return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed( | |
| 30 resource_id, ui::ResourceBundle::RTL_ENABLED).ToGdkPixbuf(); | |
| 31 } | |
| 32 | |
| 33 } // namespace | |
| 34 | |
| 35 CustomDrawButtonBase::CustomDrawButtonBase(GtkThemeService* theme_provider, | |
| 36 int normal_id, | |
| 37 int pressed_id, | |
| 38 int hover_id, | |
| 39 int disabled_id) | |
| 40 : paint_override_(-1), | |
| 41 normal_id_(normal_id), | |
| 42 pressed_id_(pressed_id), | |
| 43 hover_id_(hover_id), | |
| 44 disabled_id_(disabled_id), | |
| 45 theme_service_(theme_provider), | |
| 46 flipped_(false) { | |
| 47 for (int i = 0; i < (GTK_STATE_INSENSITIVE + 1); ++i) | |
| 48 surfaces_[i].reset(new gfx::CairoCachedSurface); | |
| 49 background_image_.reset(new gfx::CairoCachedSurface); | |
| 50 | |
| 51 if (theme_provider) { | |
| 52 // Load images by pretending that we got a BROWSER_THEME_CHANGED | |
| 53 // notification. | |
| 54 theme_provider->InitThemesFor(this); | |
| 55 | |
| 56 registrar_.Add(this, | |
| 57 chrome::NOTIFICATION_BROWSER_THEME_CHANGED, | |
| 58 content::Source<ThemeService>(theme_provider)); | |
| 59 } else { | |
| 60 // Load the button images from the resource bundle. | |
| 61 surfaces_[GTK_STATE_NORMAL]->UsePixbuf(GetImage(normal_id_)); | |
| 62 surfaces_[GTK_STATE_ACTIVE]->UsePixbuf(GetImage(pressed_id_)); | |
| 63 surfaces_[GTK_STATE_PRELIGHT]->UsePixbuf(GetImage(hover_id_)); | |
| 64 surfaces_[GTK_STATE_SELECTED]->UsePixbuf(NULL); | |
| 65 surfaces_[GTK_STATE_INSENSITIVE]->UsePixbuf(GetImage(disabled_id_)); | |
| 66 } | |
| 67 } | |
| 68 | |
| 69 CustomDrawButtonBase::~CustomDrawButtonBase() { | |
| 70 } | |
| 71 | |
| 72 int CustomDrawButtonBase::Width() const { | |
| 73 return surfaces_[0]->Width(); | |
| 74 } | |
| 75 | |
| 76 int CustomDrawButtonBase::Height() const { | |
| 77 return surfaces_[0]->Height(); | |
| 78 } | |
| 79 | |
| 80 gboolean CustomDrawButtonBase::OnExpose(GtkWidget* widget, | |
| 81 GdkEventExpose* e, | |
| 82 gdouble hover_state) { | |
| 83 TRACE_EVENT0("ui::gtk", "CustomDrawButtonBase::OnExpose"); | |
| 84 int paint_state = paint_override_ >= 0 ? | |
| 85 paint_override_ : gtk_widget_get_state(widget); | |
| 86 | |
| 87 // If the paint state is PRELIGHT then set it to NORMAL (we will paint the | |
| 88 // hover state according to |hover_state_|). | |
| 89 if (paint_state == GTK_STATE_PRELIGHT) | |
| 90 paint_state = GTK_STATE_NORMAL; | |
| 91 bool animating_hover = hover_state > 0.0 && | |
| 92 paint_state == GTK_STATE_NORMAL; | |
| 93 gfx::CairoCachedSurface* pixbuf = PixbufForState(paint_state); | |
| 94 gfx::CairoCachedSurface* hover_pixbuf = PixbufForState(GTK_STATE_PRELIGHT); | |
| 95 | |
| 96 if (!pixbuf || !pixbuf->valid()) | |
| 97 return FALSE; | |
| 98 if (animating_hover && (!hover_pixbuf || !hover_pixbuf->valid())) | |
| 99 return FALSE; | |
| 100 | |
| 101 cairo_t* cairo_context = gdk_cairo_create(GDK_DRAWABLE( | |
| 102 gtk_widget_get_window(widget))); | |
| 103 GtkAllocation allocation; | |
| 104 gtk_widget_get_allocation(widget, &allocation); | |
| 105 cairo_translate(cairo_context, allocation.x, allocation.y); | |
| 106 | |
| 107 if (flipped_) { | |
| 108 // Horizontally flip the image for non-LTR/RTL reasons. | |
| 109 cairo_translate(cairo_context, allocation.width, 0.0f); | |
| 110 cairo_scale(cairo_context, -1.0f, 1.0f); | |
| 111 } | |
| 112 | |
| 113 // The widget might be larger than the pixbuf. Paint the pixbuf flush with the | |
| 114 // start of the widget (left for LTR, right for RTL) and its bottom. | |
| 115 gfx::Rect bounds = gfx::Rect(0, 0, pixbuf->Width(), 0); | |
| 116 int x = gtk_util::MirroredLeftPointForRect(widget, bounds); | |
| 117 int y = allocation.height - pixbuf->Height(); | |
| 118 | |
| 119 if (background_image_->valid()) { | |
| 120 background_image_->SetSource(cairo_context, widget, x, y); | |
| 121 cairo_paint(cairo_context); | |
| 122 } | |
| 123 | |
| 124 pixbuf->SetSource(cairo_context, widget, x, y); | |
| 125 cairo_paint(cairo_context); | |
| 126 | |
| 127 if (animating_hover) { | |
| 128 hover_pixbuf->SetSource(cairo_context, widget, x, y); | |
| 129 cairo_paint_with_alpha(cairo_context, hover_state); | |
| 130 } | |
| 131 | |
| 132 cairo_destroy(cairo_context); | |
| 133 | |
| 134 GtkWidget* child = gtk_bin_get_child(GTK_BIN(widget)); | |
| 135 if (child) | |
| 136 gtk_container_propagate_expose(GTK_CONTAINER(widget), child, e); | |
| 137 | |
| 138 return TRUE; | |
| 139 } | |
| 140 | |
| 141 void CustomDrawButtonBase::SetBackground(SkColor color, | |
| 142 const SkBitmap& image, | |
| 143 const SkBitmap& mask) { | |
| 144 if (image.isNull() || mask.isNull()) { | |
| 145 if (background_image_->valid()) { | |
| 146 background_image_->UsePixbuf(NULL); | |
| 147 } | |
| 148 } else { | |
| 149 SkBitmap img = | |
| 150 SkBitmapOperations::CreateButtonBackground(color, image, mask); | |
| 151 | |
| 152 GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(img); | |
| 153 background_image_->UsePixbuf(pixbuf); | |
| 154 g_object_unref(pixbuf); | |
| 155 } | |
| 156 } | |
| 157 | |
| 158 void CustomDrawButtonBase::Observe(int type, | |
| 159 const content::NotificationSource& source, | |
| 160 const content::NotificationDetails& details) { | |
| 161 DCHECK(theme_service_); | |
| 162 DCHECK(chrome::NOTIFICATION_BROWSER_THEME_CHANGED == type); | |
| 163 | |
| 164 surfaces_[GTK_STATE_NORMAL]->UsePixbuf(normal_id_ ? | |
| 165 theme_service_->GetRTLEnabledPixbufNamed(normal_id_) : NULL); | |
| 166 surfaces_[GTK_STATE_ACTIVE]->UsePixbuf(pressed_id_ ? | |
| 167 theme_service_->GetRTLEnabledPixbufNamed(pressed_id_) : NULL); | |
| 168 surfaces_[GTK_STATE_PRELIGHT]->UsePixbuf(hover_id_ ? | |
| 169 theme_service_->GetRTLEnabledPixbufNamed(hover_id_) : NULL); | |
| 170 surfaces_[GTK_STATE_SELECTED]->UsePixbuf(NULL); | |
| 171 surfaces_[GTK_STATE_INSENSITIVE]->UsePixbuf(disabled_id_ ? | |
| 172 theme_service_->GetRTLEnabledPixbufNamed(disabled_id_) : NULL); | |
| 173 } | |
| 174 | |
| 175 gfx::CairoCachedSurface* CustomDrawButtonBase::PixbufForState(int state) { | |
| 176 gfx::CairoCachedSurface* pixbuf = surfaces_[state].get(); | |
| 177 | |
| 178 // Fall back to the default image if we don't have one for this state. | |
| 179 if (!pixbuf || !pixbuf->valid()) | |
| 180 pixbuf = surfaces_[GTK_STATE_NORMAL].get(); | |
| 181 | |
| 182 return pixbuf; | |
| 183 } | |
| 184 | |
| 185 // CustomDrawHoverController --------------------------------------------------- | |
| 186 | |
| 187 CustomDrawHoverController::CustomDrawHoverController(GtkWidget* widget) | |
| 188 : slide_animation_(this), | |
| 189 widget_(NULL) { | |
| 190 Init(widget); | |
| 191 } | |
| 192 | |
| 193 CustomDrawHoverController::CustomDrawHoverController() | |
| 194 : slide_animation_(this), | |
| 195 widget_(NULL) { | |
| 196 } | |
| 197 | |
| 198 CustomDrawHoverController::~CustomDrawHoverController() { | |
| 199 } | |
| 200 | |
| 201 void CustomDrawHoverController::Init(GtkWidget* widget) { | |
| 202 DCHECK(widget_ == NULL); | |
| 203 widget_ = widget; | |
| 204 g_signal_connect(widget_, "enter-notify-event", | |
| 205 G_CALLBACK(OnEnterThunk), this); | |
| 206 g_signal_connect(widget_, "leave-notify-event", | |
| 207 G_CALLBACK(OnLeaveThunk), this); | |
| 208 } | |
| 209 | |
| 210 void CustomDrawHoverController::AnimationProgressed( | |
| 211 const gfx::Animation* animation) { | |
| 212 gtk_widget_queue_draw(widget_); | |
| 213 } | |
| 214 | |
| 215 gboolean CustomDrawHoverController::OnEnter( | |
| 216 GtkWidget* widget, | |
| 217 GdkEventCrossing* event) { | |
| 218 slide_animation_.Show(); | |
| 219 return FALSE; | |
| 220 } | |
| 221 | |
| 222 gboolean CustomDrawHoverController::OnLeave( | |
| 223 GtkWidget* widget, | |
| 224 GdkEventCrossing* event) { | |
| 225 // When the user is holding a mouse button, we don't want to animate. | |
| 226 if (event->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)) | |
| 227 slide_animation_.Reset(); | |
| 228 else | |
| 229 slide_animation_.Hide(); | |
| 230 return FALSE; | |
| 231 } | |
| 232 | |
| 233 // CustomDrawButton ------------------------------------------------------------ | |
| 234 | |
| 235 CustomDrawButton::CustomDrawButton(int normal_id, | |
| 236 int pressed_id, | |
| 237 int hover_id, | |
| 238 int disabled_id) | |
| 239 : button_base_(NULL, normal_id, pressed_id, hover_id, disabled_id), | |
| 240 theme_service_(NULL), | |
| 241 forcing_chrome_theme_(false) { | |
| 242 Init(); | |
| 243 | |
| 244 // Initialize the theme stuff with no theme_provider. | |
| 245 SetBrowserTheme(); | |
| 246 } | |
| 247 | |
| 248 CustomDrawButton::CustomDrawButton(GtkThemeService* theme_provider, | |
| 249 int normal_id, | |
| 250 int pressed_id, | |
| 251 int hover_id, | |
| 252 int disabled_id, | |
| 253 const char* stock_id, | |
| 254 GtkIconSize stock_size) | |
| 255 : button_base_(theme_provider, normal_id, pressed_id, hover_id, | |
| 256 disabled_id), | |
| 257 theme_service_(theme_provider), | |
| 258 forcing_chrome_theme_(false) { | |
| 259 native_widget_.Own(gtk_image_new_from_stock(stock_id, stock_size)); | |
| 260 | |
| 261 Init(); | |
| 262 | |
| 263 theme_service_->InitThemesFor(this); | |
| 264 registrar_.Add(this, | |
| 265 chrome::NOTIFICATION_BROWSER_THEME_CHANGED, | |
| 266 content::Source<ThemeService>(theme_provider)); | |
| 267 } | |
| 268 | |
| 269 CustomDrawButton::CustomDrawButton(GtkThemeService* theme_provider, | |
| 270 int normal_id, | |
| 271 int pressed_id, | |
| 272 int hover_id, | |
| 273 int disabled_id, | |
| 274 GtkWidget* native_widget) | |
| 275 : button_base_(theme_provider, normal_id, pressed_id, hover_id, | |
| 276 disabled_id), | |
| 277 native_widget_(native_widget), | |
| 278 theme_service_(theme_provider), | |
| 279 forcing_chrome_theme_(false) { | |
| 280 Init(); | |
| 281 | |
| 282 theme_service_->InitThemesFor(this); | |
| 283 registrar_.Add(this, | |
| 284 chrome::NOTIFICATION_BROWSER_THEME_CHANGED, | |
| 285 content::Source<ThemeService>(theme_provider)); | |
| 286 } | |
| 287 | |
| 288 CustomDrawButton::~CustomDrawButton() { | |
| 289 widget_.Destroy(); | |
| 290 native_widget_.Destroy(); | |
| 291 } | |
| 292 | |
| 293 void CustomDrawButton::Init() { | |
| 294 widget_.Own(gtk_chrome_button_new()); | |
| 295 gtk_widget_set_can_focus(widget(), FALSE); | |
| 296 g_signal_connect(widget(), "expose-event", | |
| 297 G_CALLBACK(OnCustomExposeThunk), this); | |
| 298 hover_controller_.Init(widget()); | |
| 299 } | |
| 300 | |
| 301 void CustomDrawButton::ForceChromeTheme() { | |
| 302 forcing_chrome_theme_ = true; | |
| 303 SetBrowserTheme(); | |
| 304 } | |
| 305 | |
| 306 void CustomDrawButton::Observe(int type, | |
| 307 const content::NotificationSource& source, | |
| 308 const content::NotificationDetails& details) { | |
| 309 DCHECK(chrome::NOTIFICATION_BROWSER_THEME_CHANGED == type); | |
| 310 SetBrowserTheme(); | |
| 311 } | |
| 312 | |
| 313 GtkAllocation CustomDrawButton::WidgetAllocation() const { | |
| 314 GtkAllocation allocation; | |
| 315 gtk_widget_get_allocation(widget_.get(), &allocation); | |
| 316 return allocation; | |
| 317 } | |
| 318 | |
| 319 int CustomDrawButton::SurfaceWidth() const { | |
| 320 return button_base_.Width(); | |
| 321 } | |
| 322 | |
| 323 int CustomDrawButton::SurfaceHeight() const { | |
| 324 return button_base_.Height(); | |
| 325 } | |
| 326 | |
| 327 void CustomDrawButton::SetPaintOverride(GtkStateType state) { | |
| 328 button_base_.set_paint_override(state); | |
| 329 gtk_chrome_button_set_paint_state(GTK_CHROME_BUTTON(widget()), state); | |
| 330 gtk_widget_queue_draw(widget()); | |
| 331 } | |
| 332 | |
| 333 void CustomDrawButton::UnsetPaintOverride() { | |
| 334 button_base_.set_paint_override(-1); | |
| 335 gtk_chrome_button_unset_paint_state(GTK_CHROME_BUTTON(widget())); | |
| 336 gtk_widget_queue_draw(widget()); | |
| 337 } | |
| 338 | |
| 339 void CustomDrawButton::SetBackground(SkColor color, | |
| 340 const SkBitmap& image, | |
| 341 const SkBitmap& mask) { | |
| 342 button_base_.SetBackground(color, image, mask); | |
| 343 } | |
| 344 | |
| 345 gboolean CustomDrawButton::OnCustomExpose(GtkWidget* sender, | |
| 346 GdkEventExpose* e) { | |
| 347 UNSHIPPED_TRACE_EVENT0("ui::gtk", "CustomDrawButtonBase::OnCustomExpose"); | |
| 348 if (UseGtkTheme()) { | |
| 349 // Continue processing this expose event. | |
| 350 return FALSE; | |
| 351 } else { | |
| 352 double hover_state = hover_controller_.GetCurrentValue(); | |
| 353 return button_base_.OnExpose(sender, e, hover_state); | |
| 354 } | |
| 355 } | |
| 356 | |
| 357 // static | |
| 358 CustomDrawButton* CustomDrawButton::CloseButtonBar( | |
| 359 GtkThemeService* theme_provider) { | |
| 360 CustomDrawButton* button = new CustomDrawButton(theme_provider, | |
| 361 IDR_CLOSE_1, IDR_CLOSE_1_P, IDR_CLOSE_1_H, 0, | |
| 362 GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU); | |
| 363 return button; | |
| 364 } | |
| 365 | |
| 366 // static | |
| 367 CustomDrawButton* CustomDrawButton::CloseButtonBubble( | |
| 368 GtkThemeService* theme_provider) { | |
| 369 CustomDrawButton* button = new CustomDrawButton(theme_provider, | |
| 370 IDR_CLOSE_2, IDR_CLOSE_2_P, IDR_CLOSE_2_H, 0, | |
| 371 GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU); | |
| 372 return button; | |
| 373 } | |
| 374 | |
| 375 void CustomDrawButton::SetBrowserTheme() { | |
| 376 if (UseGtkTheme()) { | |
| 377 if (native_widget_.get()) | |
| 378 gtk_button_set_image(GTK_BUTTON(widget()), native_widget_.get()); | |
| 379 gtk_widget_set_size_request(widget(), -1, -1); | |
| 380 gtk_widget_set_app_paintable(widget(), FALSE); | |
| 381 } else { | |
| 382 if (native_widget_.get()) | |
| 383 gtk_button_set_image(GTK_BUTTON(widget()), NULL); | |
| 384 gtk_widget_set_size_request(widget(), button_base_.Width(), | |
| 385 button_base_.Height()); | |
| 386 | |
| 387 gtk_widget_set_app_paintable(widget(), TRUE); | |
| 388 } | |
| 389 | |
| 390 gtk_chrome_button_set_use_gtk_rendering( | |
| 391 GTK_CHROME_BUTTON(widget()), UseGtkTheme()); | |
| 392 } | |
| 393 | |
| 394 bool CustomDrawButton::UseGtkTheme() { | |
| 395 return !forcing_chrome_theme_ && theme_service_ && | |
| 396 theme_service_->UsingNativeTheme(); | |
| 397 } | |
| OLD | NEW |