diff options
author | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-15 18:30:33 +0000 |
---|---|---|
committer | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-15 18:30:33 +0000 |
commit | dd12eb8c067510fae31d76472a8874ef16bce2f3 (patch) | |
tree | 85d0230a1732bb801d5ca510447d368807ddadce | |
parent | 9118e6bce4bf3b5643a7cc4a64e63d697f3ee05b (diff) | |
download | chromium_src-dd12eb8c067510fae31d76472a8874ef16bce2f3.zip chromium_src-dd12eb8c067510fae31d76472a8874ef16bce2f3.tar.gz chromium_src-dd12eb8c067510fae31d76472a8874ef16bce2f3.tar.bz2 |
Extensions: Make badge drawing code cross platform so linux (and eventually mac) can use it.
I will switch Windows over to the common function in a follow-up patch.
BUG=23882
TEST=gmail browser action sample extension
Review URL: http://codereview.chromium.org/279008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@29152 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/gtk/browser_actions_toolbar_gtk.cc | 65 | ||||
-rw-r--r-- | chrome/browser/gtk/gtk_chrome_button.cc | 24 | ||||
-rw-r--r-- | chrome/browser/views/browser_actions_container.cc | 2 | ||||
-rwxr-xr-x | chrome/chrome.gyp | 1 | ||||
-rw-r--r-- | chrome/common/extensions/extension_action.cc | 104 | ||||
-rw-r--r-- | chrome/common/extensions/extension_action.h | 7 |
6 files changed, 167 insertions, 36 deletions
diff --git a/chrome/browser/gtk/browser_actions_toolbar_gtk.cc b/chrome/browser/gtk/browser_actions_toolbar_gtk.cc index a5f8796..0e57a9a 100644 --- a/chrome/browser/gtk/browser_actions_toolbar_gtk.cc +++ b/chrome/browser/gtk/browser_actions_toolbar_gtk.cc @@ -7,15 +7,18 @@ #include <gtk/gtk.h> #include <vector> +#include "app/gfx/canvas_paint.h" #include "app/gfx/gtk_util.h" #include "chrome/browser/browser.h" #include "chrome/browser/extensions/extension_browser_event_router.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/extensions/image_loading_tracker.h" #include "chrome/browser/gtk/gtk_chrome_button.h" +#include "chrome/browser/gtk/gtk_theme_provider.h" #include "chrome/browser/profile.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/notification_details.h" +#include "chrome/common/notification_service.h" #include "chrome/common/notification_source.h" #include "chrome/common/notification_type.h" @@ -47,9 +50,15 @@ class BrowserActionButton : public NotificationObserver, // We need to hook up extension popups here. http://crbug.com/23897 g_signal_connect(button_.get(), "clicked", G_CALLBACK(OnButtonClicked), this); + g_signal_connect_after(button_.get(), "expose-event", + G_CALLBACK(OnExposeEvent), this); registrar_.Add(this, NotificationType::EXTENSION_BROWSER_ACTION_UPDATED, Source<ExtensionAction>(extension->browser_action())); + registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, + NotificationService::AllSources()); + + OnThemeChanged(); } ~BrowserActionButton() { @@ -64,12 +73,26 @@ class BrowserActionButton : public NotificationObserver, GtkWidget* widget() { return button_.get(); } - static void OnButtonClicked(GtkWidget* widget, BrowserActionButton* action) { - ExtensionBrowserEventRouter::GetInstance()->BrowserActionExecuted( - action->browser_->profile(), action->extension_->id(), - action->browser_); + void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type == NotificationType::EXTENSION_BROWSER_ACTION_UPDATED) + OnStateUpdated(); + else if (type == NotificationType::BROWSER_THEME_CHANGED) + OnThemeChanged(); + else + NOTREACHED(); } + // ImageLoadingTracker::Observer implementation. + void OnImageLoaded(SkBitmap* image, size_t index) { + SkBitmap empty; + SkBitmap* bitmap = image ? image : ∅ + browser_action_icons_[index] = gfx::GdkPixbufFromSkBitmap(bitmap); + OnStateUpdated(); + } + + private: // Called when the tooltip has changed or an image has loaded. void OnStateUpdated() { gtk_widget_set_tooltip_text(button_.get(), @@ -86,26 +109,30 @@ class BrowserActionButton : public NotificationObserver, } } - // NotificationObserver implementation. - void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - OnStateUpdated(); + void OnThemeChanged() { + gtk_chrome_button_set_use_gtk_rendering(GTK_CHROME_BUTTON(button_.get()), + GtkThemeProvider::GetFrom(browser_->profile())->UseGtkTheme()); } - // ImageLoadingTracker::Observer implementation. - void OnImageLoaded(SkBitmap* image, size_t index) { - if (image) { - browser_action_icons_[index] = gfx::GdkPixbufFromSkBitmap(image); - } else { - SkBitmap empty; - browser_action_icons_[index] = gfx::GdkPixbufFromSkBitmap(&empty); - } + static void OnButtonClicked(GtkWidget* widget, BrowserActionButton* action) { + ExtensionBrowserEventRouter::GetInstance()->BrowserActionExecuted( + action->browser_->profile(), action->extension_->id(), + action->browser_); + } - OnStateUpdated(); + static gboolean OnExposeEvent(GtkWidget* widget, + GdkEventExpose* event, + BrowserActionButton* action) { + if (action->extension_->browser_action_state()->badge_text().empty()) + return FALSE; + + gfx::CanvasPaint canvas(event, false); + gfx::Rect bounding_rect(widget->allocation); + action->extension_->browser_action_state()->PaintBadge(&canvas, + bounding_rect); + return FALSE; } - private: // The Browser that executes a command when the button is pressed. Browser* browser_; diff --git a/chrome/browser/gtk/gtk_chrome_button.cc b/chrome/browser/gtk/gtk_chrome_button.cc index 309c4a8..748cb13 100644 --- a/chrome/browser/gtk/gtk_chrome_button.cc +++ b/chrome/browser/gtk/gtk_chrome_button.cc @@ -81,8 +81,6 @@ static void gtk_chrome_button_init(GtkChromeButton* button) { priv->paint_state = -1; priv->use_gtk_rendering = FALSE; - gtk_widget_set_app_paintable(GTK_WIDGET(button), TRUE); - GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS); } @@ -97,14 +95,8 @@ static gboolean gtk_chrome_button_expose(GtkWidget* widget, // rendering AND we're in either the prelight or active state so that we // get the button border for the current GTK theme drawn. if (paint_state == GTK_STATE_PRELIGHT || paint_state == GTK_STATE_ACTIVE) { - (*GTK_WIDGET_CLASS(gtk_chrome_button_parent_class)->expose_event) + return GTK_WIDGET_CLASS(gtk_chrome_button_parent_class)->expose_event (widget, event); - } else if (gtk_bin_get_child(GTK_BIN(widget))) { - // Otherwise, we're still responsible for rendering our children if we - // have any. - gtk_container_propagate_expose(GTK_CONTAINER(widget), - gtk_bin_get_child(GTK_BIN(widget)), - event); } } else { NineBox* nine_box = NULL; @@ -116,16 +108,16 @@ static gboolean gtk_chrome_button_expose(GtkWidget* widget, // Only draw theme graphics if we have some. if (nine_box) nine_box->RenderToWidget(widget); + } - // If we have a child widget, draw it. - if (gtk_bin_get_child(GTK_BIN(widget))) { - gtk_container_propagate_expose(GTK_CONTAINER(widget), - gtk_bin_get_child(GTK_BIN(widget)), - event); - } + // If we have a child widget, draw it. + if (gtk_bin_get_child(GTK_BIN(widget))) { + gtk_container_propagate_expose(GTK_CONTAINER(widget), + gtk_bin_get_child(GTK_BIN(widget)), + event); } - return TRUE; // Don't propagate, we are the default handler. + return FALSE; } GtkWidget* gtk_chrome_button_new(void) { diff --git a/chrome/browser/views/browser_actions_container.cc b/chrome/browser/views/browser_actions_container.cc index df6d905..4ce1197 100644 --- a/chrome/browser/views/browser_actions_container.cc +++ b/chrome/browser/views/browser_actions_container.cc @@ -34,7 +34,7 @@ static const int kHorizontalPadding = 4; // This is the same value from toolbar.cc. We position the browser actions // container flush with the edges of the toolbar as a special case so that we -// can draw the badge outside the visual bounds of the contianer. +// can draw the badge outside the visual bounds of the container. static const int kControlVertOffset = 6; // The maximum of the minimum number of browser actions present when there is diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 80f942b..1f922cc 100755 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -502,6 +502,7 @@ 'chrome_strings', 'theme_resources', '../app/app.gyp:app_base', + '../app/app.gyp:app_resources', '../base/base.gyp:base', '../base/base.gyp:base_i18n', '../build/temp_gyp/googleurl.gyp:googleurl', diff --git a/chrome/common/extensions/extension_action.cc b/chrome/common/extensions/extension_action.cc index 2d3291e..13ee202 100644 --- a/chrome/common/extensions/extension_action.cc +++ b/chrome/common/extensions/extension_action.cc @@ -2,7 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "app/gfx/canvas.h" +#include "app/resource_bundle.h" +#include "base/gfx/rect.h" +#include "chrome/app/chrome_dll_resource.h" #include "chrome/common/extensions/extension_action.h" +#include "grit/app_resources.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkTypeface.h" +#include "third_party/skia/include/effects/SkGradientShader.h" ExtensionAction::ExtensionAction() : type_(PAGE_ACTION) { @@ -10,3 +18,99 @@ ExtensionAction::ExtensionAction() ExtensionAction::~ExtensionAction() { } + +void ExtensionActionState::PaintBadge(gfx::Canvas* canvas, + const gfx::Rect& bounds) { + const std::string& text = badge_text(); + if (text.empty()) + return; + + // Different platforms need slightly different constants to look good. +#if defined(OS_LINUX) + const int kTextSize = 9; + const int kBottomMargin = 4; + const int kPadding = 2; + const int kBadgeHeight = 12; + const int kMaxTextWidth = 23; + // The minimum width for center-aligning the badge. + const int kCenterAlignThreshold = 20; +#else + const int kTextSize = 8; + const int kBottomMargin = 5; + const int kPadding = 2; + const int kBadgeHeight = 11; + const int kMaxTextWidth = 23; + // The minimum width for center-aligning the badge. + const int kCenterAlignThreshold = 20; +#endif + + canvas->save(); + + SkTypeface* typeface = SkTypeface::CreateFromName("Arial", SkTypeface::kBold); + SkPaint text_paint; + text_paint.setAntiAlias(true); + text_paint.setColor(SK_ColorWHITE); + text_paint.setFakeBoldText(true); + text_paint.setTextAlign(SkPaint::kLeft_Align); + text_paint.setTextSize(SkIntToScalar(kTextSize)); + text_paint.setTypeface(typeface); + + // Calculate text width. We clamp it to a max size. + SkScalar text_width = text_paint.measureText(text.c_str(), text.size()); + text_width = SkIntToScalar( + std::min(kMaxTextWidth, SkScalarFloor(text_width))); + + // Cacluate badge size. It is clamped to a min width just because it looks + // silly if it is too skinny. + int badge_width = SkScalarFloor(text_width) + kPadding * 2; + badge_width = std::max(kBadgeHeight, badge_width); + + // Paint the badge background color in the right location. It is usually + // right-aligned, but it can also be center-aligned if it is large. + SkRect rect; + rect.fBottom = SkIntToScalar(bounds.bottom() - kBottomMargin); + rect.fTop = rect.fBottom - SkIntToScalar(kBadgeHeight); + if (badge_width >= kCenterAlignThreshold) { + rect.fLeft = SkIntToScalar((bounds.right() - badge_width) / 2); + rect.fRight = rect.fLeft + SkIntToScalar(badge_width); + } else { + rect.fRight = SkIntToScalar(bounds.right()); + rect.fLeft = rect.fRight - badge_width; + } + + SkPaint rect_paint; + rect_paint.setStyle(SkPaint::kFill_Style); + rect_paint.setAntiAlias(true); + rect_paint.setColor(badge_background_color()); + canvas->drawRoundRect(rect, SkIntToScalar(2), SkIntToScalar(2), rect_paint); + + // Overlay the gradient. It is stretchy, so we do this in three parts. + ResourceBundle& resource_bundle = ResourceBundle::GetSharedInstance(); + SkBitmap* gradient_left = resource_bundle.GetBitmapNamed( + IDR_BROWSER_ACTION_BADGE_LEFT); + SkBitmap* gradient_right = resource_bundle.GetBitmapNamed( + IDR_BROWSER_ACTION_BADGE_RIGHT); + SkBitmap* gradient_center = resource_bundle.GetBitmapNamed( + IDR_BROWSER_ACTION_BADGE_CENTER); + + canvas->drawBitmap(*gradient_left, rect.fLeft, rect.fTop); + canvas->TileImageInt(*gradient_center, + SkScalarFloor(rect.fLeft) + gradient_left->width(), + SkScalarFloor(rect.fTop), + SkScalarFloor(rect.width()) - gradient_left->width() - + gradient_right->width(), + SkScalarFloor(rect.height())); + canvas->drawBitmap(*gradient_right, + rect.fRight - SkIntToScalar(gradient_right->width()), rect.fTop); + + // Finally, draw the text centered within the badge. We set a clip in case the + // text was too large. + rect.fLeft += kPadding; + rect.fRight -= kPadding; + canvas->clipRect(rect); + canvas->drawText(text.c_str(), text.size(), + rect.fLeft + (rect.width() - text_width) / 2, + rect.fTop + kTextSize + 1, + text_paint); + canvas->restore(); +} diff --git a/chrome/common/extensions/extension_action.h b/chrome/common/extensions/extension_action.h index 1577224..2e34bbb 100644 --- a/chrome/common/extensions/extension_action.h +++ b/chrome/common/extensions/extension_action.h @@ -15,6 +15,11 @@ #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkColor.h" +namespace gfx { +class Canvas; +class Rect; +} + class ExtensionAction { public: ExtensionAction(); @@ -107,6 +112,8 @@ class ExtensionActionState { SkBitmap* icon() const { return icon_.get(); } void set_icon(SkBitmap* icon) { icon_.reset(icon); } + void PaintBadge(gfx::Canvas* canvas, const gfx::Rect& bounds); + private: // The title text to use for tooltips and labels. std::string title_; |