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