summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-15 18:30:33 +0000
committerestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-15 18:30:33 +0000
commitdd12eb8c067510fae31d76472a8874ef16bce2f3 (patch)
tree85d0230a1732bb801d5ca510447d368807ddadce
parent9118e6bce4bf3b5643a7cc4a64e63d697f3ee05b (diff)
downloadchromium_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.cc65
-rw-r--r--chrome/browser/gtk/gtk_chrome_button.cc24
-rw-r--r--chrome/browser/views/browser_actions_container.cc2
-rwxr-xr-xchrome/chrome.gyp1
-rw-r--r--chrome/common/extensions/extension_action.cc104
-rw-r--r--chrome/common/extensions/extension_action.h7
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 : &empty;
+ 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_;