diff options
Diffstat (limited to 'chrome/browser/ui/gtk/custom_button.cc')
-rw-r--r-- | chrome/browser/ui/gtk/custom_button.cc | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/chrome/browser/ui/gtk/custom_button.cc b/chrome/browser/ui/gtk/custom_button.cc new file mode 100644 index 0000000..31d1cf6 --- /dev/null +++ b/chrome/browser/ui/gtk/custom_button.cc @@ -0,0 +1,345 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/gtk/custom_button.h" + +#include "app/resource_bundle.h" +#include "base/basictypes.h" +#include "chrome/browser/gtk/cairo_cached_surface.h" +#include "chrome/browser/gtk/gtk_chrome_button.h" +#include "chrome/browser/gtk/gtk_theme_provider.h" +#include "chrome/browser/gtk/gtk_util.h" +#include "chrome/common/notification_service.h" +#include "gfx/gtk_util.h" +#include "gfx/skbitmap_operations.h" +#include "grit/theme_resources.h" +#include "third_party/skia/include/core/SkBitmap.h" + +CustomDrawButtonBase::CustomDrawButtonBase(GtkThemeProvider* theme_provider, + int normal_id, + int pressed_id, + int hover_id, + int disabled_id) + : background_image_(NULL), + paint_override_(-1), + normal_id_(normal_id), + pressed_id_(pressed_id), + hover_id_(hover_id), + disabled_id_(disabled_id), + theme_provider_(theme_provider), + flipped_(false) { + for (int i = 0; i < (GTK_STATE_INSENSITIVE + 1); ++i) + surfaces_[i].reset(new CairoCachedSurface); + background_image_.reset(new CairoCachedSurface); + + if (theme_provider) { + // Load images by pretending that we got a BROWSER_THEME_CHANGED + // notification. + theme_provider->InitThemesFor(this); + + registrar_.Add(this, + NotificationType::BROWSER_THEME_CHANGED, + NotificationService::AllSources()); + } else { + // Load the button images from the resource bundle. + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + surfaces_[GTK_STATE_NORMAL]->UsePixbuf( + normal_id_ ? rb.GetRTLEnabledPixbufNamed(normal_id_) : NULL); + surfaces_[GTK_STATE_ACTIVE]->UsePixbuf( + pressed_id_ ? rb.GetRTLEnabledPixbufNamed(pressed_id_) : NULL); + surfaces_[GTK_STATE_PRELIGHT]->UsePixbuf( + hover_id_ ? rb.GetRTLEnabledPixbufNamed(hover_id_) : NULL); + surfaces_[GTK_STATE_SELECTED]->UsePixbuf(NULL); + surfaces_[GTK_STATE_INSENSITIVE]->UsePixbuf( + disabled_id_ ? rb.GetRTLEnabledPixbufNamed(disabled_id_) : NULL); + } +} + +CustomDrawButtonBase::~CustomDrawButtonBase() { +} + +int CustomDrawButtonBase::Width() const { + return surfaces_[0]->Width(); +} + +int CustomDrawButtonBase::Height() const { + return surfaces_[0]->Height(); +} + +gboolean CustomDrawButtonBase::OnExpose(GtkWidget* widget, + GdkEventExpose* e, + gdouble hover_state) { + int paint_state = paint_override_ >= 0 ? + paint_override_ : GTK_WIDGET_STATE(widget); + + // If the paint state is PRELIGHT then set it to NORMAL (we will paint the + // hover state according to |hover_state_|). + if (paint_state == GTK_STATE_PRELIGHT) + paint_state = GTK_STATE_NORMAL; + bool animating_hover = hover_state > 0.0 && + paint_state == GTK_STATE_NORMAL; + CairoCachedSurface* pixbuf = PixbufForState(paint_state); + CairoCachedSurface* hover_pixbuf = PixbufForState(GTK_STATE_PRELIGHT); + + if (!pixbuf || !pixbuf->valid()) + return FALSE; + if (animating_hover && (!hover_pixbuf || !hover_pixbuf->valid())) + return FALSE; + + cairo_t* cairo_context = gdk_cairo_create(GDK_DRAWABLE(widget->window)); + cairo_translate(cairo_context, widget->allocation.x, widget->allocation.y); + + if (flipped_) { + // Horizontally flip the image for non-LTR/RTL reasons. + cairo_translate(cairo_context, widget->allocation.width, 0.0f); + cairo_scale(cairo_context, -1.0f, 1.0f); + } + + // The widget might be larger than the pixbuf. Paint the pixbuf flush with the + // start of the widget (left for LTR, right for RTL) and its bottom. + gfx::Rect bounds = gfx::Rect(0, 0, pixbuf->Width(), 0); + int x = gtk_util::MirroredLeftPointForRect(widget, bounds); + int y = widget->allocation.height - pixbuf->Height(); + + if (background_image_->valid()) { + background_image_->SetSource(cairo_context, x, y); + cairo_paint(cairo_context); + } + + pixbuf->SetSource(cairo_context, x, y); + cairo_paint(cairo_context); + + if (animating_hover) { + hover_pixbuf->SetSource(cairo_context, x, y); + cairo_paint_with_alpha(cairo_context, hover_state); + } + + cairo_destroy(cairo_context); + + GtkWidget* child = gtk_bin_get_child(GTK_BIN(widget)); + if (child) + gtk_container_propagate_expose(GTK_CONTAINER(widget), child, e); + + return TRUE; +} + +void CustomDrawButtonBase::SetBackground(SkColor color, + SkBitmap* image, SkBitmap* mask) { + if (!image || !mask) { + if (background_image_->valid()) { + background_image_->UsePixbuf(NULL); + } + } else { + SkBitmap img = + SkBitmapOperations::CreateButtonBackground(color, *image, *mask); + + GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&img); + background_image_->UsePixbuf(pixbuf); + g_object_unref(pixbuf); + } +} + +void CustomDrawButtonBase::Observe(NotificationType type, + const NotificationSource& source, const NotificationDetails& details) { + DCHECK(theme_provider_); + DCHECK(NotificationType::BROWSER_THEME_CHANGED == type); + + surfaces_[GTK_STATE_NORMAL]->UsePixbuf(normal_id_ ? + theme_provider_->GetRTLEnabledPixbufNamed(normal_id_) : NULL); + surfaces_[GTK_STATE_ACTIVE]->UsePixbuf(pressed_id_ ? + theme_provider_->GetRTLEnabledPixbufNamed(pressed_id_) : NULL); + surfaces_[GTK_STATE_PRELIGHT]->UsePixbuf(hover_id_ ? + theme_provider_->GetRTLEnabledPixbufNamed(hover_id_) : NULL); + surfaces_[GTK_STATE_SELECTED]->UsePixbuf(NULL); + surfaces_[GTK_STATE_INSENSITIVE]->UsePixbuf(disabled_id_ ? + theme_provider_->GetRTLEnabledPixbufNamed(disabled_id_) : NULL); +} + +CairoCachedSurface* CustomDrawButtonBase::PixbufForState(int state) { + CairoCachedSurface* pixbuf = surfaces_[state].get(); + + // Fall back to the default image if we don't have one for this state. + if (!pixbuf || !pixbuf->valid()) + pixbuf = surfaces_[GTK_STATE_NORMAL].get(); + + return pixbuf; +} + +// CustomDrawHoverController --------------------------------------------------- + +CustomDrawHoverController::CustomDrawHoverController(GtkWidget* widget) + : slide_animation_(this), + widget_(NULL) { + Init(widget); +} + +CustomDrawHoverController::CustomDrawHoverController() + : slide_animation_(this), + widget_(NULL) { +} + +CustomDrawHoverController::~CustomDrawHoverController() { +} + +void CustomDrawHoverController::Init(GtkWidget* widget) { + DCHECK(widget_ == NULL); + widget_ = widget; + g_signal_connect(widget_, "enter-notify-event", + G_CALLBACK(OnEnterThunk), this); + g_signal_connect(widget_, "leave-notify-event", + G_CALLBACK(OnLeaveThunk), this); +} + +void CustomDrawHoverController::AnimationProgressed( + const ui::Animation* animation) { + gtk_widget_queue_draw(widget_); +} + +gboolean CustomDrawHoverController::OnEnter( + GtkWidget* widget, + GdkEventCrossing* event) { + slide_animation_.Show(); + return FALSE; +} + +gboolean CustomDrawHoverController::OnLeave( + GtkWidget* widget, + GdkEventCrossing* event) { + // When the user is holding a mouse button, we don't want to animate. + if (event->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)) + slide_animation_.Reset(); + else + slide_animation_.Hide(); + return FALSE; +} + +// CustomDrawButton ------------------------------------------------------------ + +CustomDrawButton::CustomDrawButton(int normal_id, + int pressed_id, + int hover_id, + int disabled_id) + : button_base_(NULL, normal_id, pressed_id, hover_id, disabled_id), + theme_provider_(NULL) { + Init(); + + // Initialize the theme stuff with no theme_provider. + SetBrowserTheme(); +} + +CustomDrawButton::CustomDrawButton(GtkThemeProvider* theme_provider, + int normal_id, + int pressed_id, + int hover_id, + int disabled_id, + const char* stock_id, + GtkIconSize stock_size) + : button_base_(theme_provider, normal_id, pressed_id, hover_id, + disabled_id), + theme_provider_(theme_provider) { + native_widget_.Own(gtk_image_new_from_stock(stock_id, stock_size)); + + Init(); + + theme_provider_->InitThemesFor(this); + registrar_.Add(this, + NotificationType::BROWSER_THEME_CHANGED, + NotificationService::AllSources()); +} + +CustomDrawButton::CustomDrawButton(GtkThemeProvider* theme_provider, + int normal_id, + int pressed_id, + int hover_id, + int disabled_id, + GtkWidget* native_widget) + : button_base_(theme_provider, normal_id, pressed_id, hover_id, + disabled_id), + native_widget_(native_widget), + theme_provider_(theme_provider) { + Init(); + + theme_provider_->InitThemesFor(this); + registrar_.Add(this, + NotificationType::BROWSER_THEME_CHANGED, + NotificationService::AllSources()); +} + +CustomDrawButton::~CustomDrawButton() { + widget_.Destroy(); + native_widget_.Destroy(); +} + +void CustomDrawButton::Init() { + widget_.Own(gtk_chrome_button_new()); + GTK_WIDGET_UNSET_FLAGS(widget(), GTK_CAN_FOCUS); + g_signal_connect(widget(), "expose-event", + G_CALLBACK(OnCustomExposeThunk), this); + hover_controller_.Init(widget()); +} + +void CustomDrawButton::Observe(NotificationType type, + const NotificationSource& source, const NotificationDetails& details) { + DCHECK(NotificationType::BROWSER_THEME_CHANGED == type); + SetBrowserTheme(); +} + +void CustomDrawButton::SetPaintOverride(GtkStateType state) { + button_base_.set_paint_override(state); + gtk_chrome_button_set_paint_state(GTK_CHROME_BUTTON(widget()), state); + gtk_widget_queue_draw(widget()); +} + +void CustomDrawButton::UnsetPaintOverride() { + button_base_.set_paint_override(-1); + gtk_chrome_button_unset_paint_state(GTK_CHROME_BUTTON(widget())); + gtk_widget_queue_draw(widget()); +} + +void CustomDrawButton::SetBackground(SkColor color, + SkBitmap* image, SkBitmap* mask) { + button_base_.SetBackground(color, image, mask); +} + +gboolean CustomDrawButton::OnCustomExpose(GtkWidget* sender, + GdkEventExpose* e) { + if (UseGtkTheme()) { + // Continue processing this expose event. + return FALSE; + } else { + double hover_state = hover_controller_.GetCurrentValue(); + return button_base_.OnExpose(sender, e, hover_state); + } +} + +// static +CustomDrawButton* CustomDrawButton::CloseButton( + GtkThemeProvider* theme_provider) { + CustomDrawButton* button = new CustomDrawButton(theme_provider, IDR_CLOSE_BAR, + IDR_CLOSE_BAR_P, IDR_CLOSE_BAR_H, 0, GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU); + return button; +} + +void CustomDrawButton::SetBrowserTheme() { + if (UseGtkTheme()) { + if (native_widget_.get()) + gtk_button_set_image(GTK_BUTTON(widget()), native_widget_.get()); + gtk_widget_set_size_request(widget(), -1, -1); + gtk_widget_set_app_paintable(widget(), FALSE); + } else { + if (native_widget_.get()) + gtk_button_set_image(GTK_BUTTON(widget()), NULL); + gtk_widget_set_size_request(widget(), button_base_.Width(), + button_base_.Height()); + + gtk_widget_set_app_paintable(widget(), TRUE); + } + + gtk_chrome_button_set_use_gtk_rendering( + GTK_CHROME_BUTTON(widget()), UseGtkTheme()); +} + +bool CustomDrawButton::UseGtkTheme() { + return theme_provider_ && theme_provider_->UseGtkTheme(); +} |