summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsail@chromium.org <sail@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-07 23:46:53 +0000
committersail@chromium.org <sail@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-07 23:46:53 +0000
commit7995319dd2575f5f8e9b63f1ad1e6b36480cef4d (patch)
treee5a77eac3a85bcc9f1a9a822f5d0e64cc9247180
parentc90aeec875b69a034e64240b3d2892302a4fdc59 (diff)
downloadchromium_src-7995319dd2575f5f8e9b63f1ad1e6b36480cef4d.zip
chromium_src-7995319dd2575f5f8e9b63f1ad1e6b36480cef4d.tar.gz
chromium_src-7995319dd2575f5f8e9b63f1ad1e6b36480cef4d.tar.bz2
Fade tab titles
This change fades tab titles instead of truncating them with a "...". I tried to copy the Mac implemenation from here: http://code.google.com/p/google-toolbox-for-mac/source/browse/trunk/AppKit/GTMFadeTruncatingTextFieldCell.m but my version is not as good since I couldn't figure out how to draw text on a transparent background. BUG=69301 TEST=Ran Chromium and verified that things looked ok. Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=80416 Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=80748 Review URL: http://codereview.chromium.org/6695043 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@80870 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/ui/views/tabs/base_tab.cc25
-rw-r--r--ui/gfx/canvas_skia.h19
-rw-r--r--ui/gfx/canvas_skia_win.cc284
3 files changed, 317 insertions, 11 deletions
diff --git a/chrome/browser/ui/views/tabs/base_tab.cc b/chrome/browser/ui/views/tabs/base_tab.cc
index 327fd8e..2e9f222 100644
--- a/chrome/browser/ui/views/tabs/base_tab.cc
+++ b/chrome/browser/ui/views/tabs/base_tab.cc
@@ -472,6 +472,9 @@ void BaseTab::PaintTitle(gfx::Canvas* canvas, SkColor title_color) {
// Paint the Title.
const gfx::Rect& title_bounds = GetTitleBounds();
string16 title = data().title;
+ bool should_truncate = false;
+ int prefix_length = 0;
+
if (title.empty()) {
title = data().loading ?
l10n_util::GetStringUTF16(IDS_TAB_LOADING_TITLE) :
@@ -481,18 +484,26 @@ void BaseTab::PaintTitle(gfx::Canvas* canvas, SkColor title_color) {
// If we'll need to truncate, check if we should also truncate
// a common prefix, but only if there is enough room for it.
// We arbitrarily choose to request enough room for 6 average chars.
- if (data().common_prefix_length > TitlePrefixMatcher::kMinElidingLength &&
+ should_truncate =
+ data().common_prefix_length > TitlePrefixMatcher::kMinElidingLength &&
font_->GetExpectedTextWidth(6) < title_bounds.width() &&
- font_->GetStringWidth(title) > title_bounds.width()) {
- title.replace(0,
- data().common_prefix_length -
- TitlePrefixMatcher::kCommonCharsToShow,
- UTF8ToUTF16(ui::kEllipsis));
- }
+ font_->GetStringWidth(title) > title_bounds.width();
+ prefix_length = data().common_prefix_length -
+ TitlePrefixMatcher::kCommonCharsToShow;
}
+
+#if defined(OS_WIN)
+ canvas->AsCanvasSkia()->DrawFadeTruncatingString(title,
+ should_truncate ? gfx::CanvasSkia::TruncateFadeHeadAndTail :
+ gfx::CanvasSkia::TruncateFadeTail,
+ prefix_length, *font_, title_color, title_bounds);
+#else
+ if (should_truncate)
+ title.replace(0, prefix_length, UTF8ToUTF16(ui::kEllipsis));
canvas->DrawStringInt(title, *font_, title_color,
title_bounds.x(), title_bounds.y(),
title_bounds.width(), title_bounds.height());
+#endif
}
void BaseTab::AnimationProgressed(const ui::Animation* animation) {
diff --git a/ui/gfx/canvas_skia.h b/ui/gfx/canvas_skia.h
index 9516e8a3..4811a81 100644
--- a/ui/gfx/canvas_skia.h
+++ b/ui/gfx/canvas_skia.h
@@ -37,6 +37,12 @@ class Canvas;
class CanvasSkia : public skia::PlatformCanvas,
public Canvas {
public:
+ enum TruncateFadeMode {
+ TruncateFadeTail,
+ TruncateFadeHead,
+ TruncateFadeHeadAndTail,
+ };
+
// Creates an empty Canvas. Callers must use initialize before using the
// canvas.
CanvasSkia();
@@ -132,6 +138,19 @@ class CanvasSkia : public skia::PlatformCanvas,
const SkColor& color,
int x, int y, int w, int h,
int flags);
+#if defined(OS_WIN)
+ // Draws the given string with the beginning and/or the end using a fade
+ // gradient. When truncating the head
+ // |desired_characters_to_truncate_from_head| specifies the maximum number of
+ // characters that can be truncated.
+ virtual void DrawFadeTruncatingString(
+ const string16& text,
+ TruncateFadeMode truncate_mode,
+ size_t desired_characters_to_truncate_from_head,
+ const gfx::Font& font,
+ const SkColor& color,
+ const gfx::Rect& display_rect);
+#endif
virtual void DrawFocusRect(int x, int y, int width, int height);
virtual void TileImageInt(const SkBitmap& bitmap, int x, int y, int w, int h);
virtual void TileImageInt(const SkBitmap& bitmap,
diff --git a/ui/gfx/canvas_skia_win.cc b/ui/gfx/canvas_skia_win.cc
index b343234..41a4f44 100644
--- a/ui/gfx/canvas_skia_win.cc
+++ b/ui/gfx/canvas_skia_win.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// 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.
@@ -7,12 +7,21 @@
#include <limits>
#include "base/i18n/rtl.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "skia/ext/bitmap_platform_device.h"
+#include "skia/ext/skia_utils_win.h"
#include "third_party/skia/include/core/SkShader.h"
#include "ui/gfx/font.h"
#include "ui/gfx/rect.h"
namespace {
+static inline int Round(double x) {
+ // Why oh why is this not in a standard header?
+ return static_cast<int>(floor(x + 0.5));
+}
+
// We make sure that LTR text we draw in an RTL context is modified
// appropriately to make sure it maintains it LTR orientation.
void DoDrawText(HDC hdc,
@@ -124,6 +133,143 @@ int ComputeFormatFlags(int flags, const string16& text) {
return f;
}
+// Changes the alpha of the given bitmap.
+// If |fade_to_right| is true then the rect fades from opaque to clear,
+// otherwise the rect fades from clear to opaque.
+void FadeBitmapRect(skia::BitmapPlatformDevice& bmp_device,
+ const gfx::Rect& rect,
+ bool fade_to_right) {
+ SkBitmap bmp = bmp_device.accessBitmap(true);
+ DCHECK_EQ(SkBitmap::kARGB_8888_Config, bmp.config());
+ SkAutoLockPixels lock(bmp);
+ float total_width = static_cast<float>(rect.width());
+
+ for (int x = rect.x(); x < rect.right(); x++) {
+ float cur_width = static_cast<float>(fade_to_right ?
+ rect.right() - x : x - rect.x());
+ // We want the fade effect to go from 0.2 to 1.0.
+ float alpha_percent = ((cur_width / total_width) * 0.8f) + 0.2f;
+
+ for (int y = rect.y(); y < rect.bottom(); y++) {
+ SkColor color = bmp.getColor(x, y);
+ SkAlpha alpha = static_cast<SkAlpha>(SkColorGetA(color) * alpha_percent);
+ *bmp.getAddr32(x, y) = SkPreMultiplyColor(SkColorSetA(color, alpha));
+ }
+ }
+}
+
+// DrawText() doesn't support alpha channels. To create a transparent background
+// this function draws black on white. It then uses the intensity of black
+// to determine how much alpha to use. The text is drawn in |gfx_text_rect| and
+// clipped to |gfx_draw_rect|.
+void DrawTextAndClearBackground(skia::BitmapPlatformDevice& bmp_device,
+ HFONT font,
+ COLORREF text_color,
+ const string16& text,
+ int flags,
+ const gfx::Rect& gfx_text_rect,
+ const gfx::Rect& gfx_draw_rect) {
+ HDC hdc = bmp_device.BeginPlatformPaint();
+
+ // Clear the background by filling with white.
+ HBRUSH fill_brush = static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
+ HANDLE old_brush = SelectObject(hdc, fill_brush);
+ RECT draw_rect = gfx_draw_rect.ToRECT();
+ FillRect(hdc, &draw_rect, fill_brush);
+ SelectObject(hdc, old_brush);
+
+ // Set black text with trasparent background.
+ SetBkMode(hdc, TRANSPARENT);
+ SetTextColor(hdc, 0);
+
+ // Draw the text.
+ int save_dc_id = SaveDC(hdc);
+ // Clip the text to the draw destination.
+ IntersectClipRect(hdc, draw_rect.left, draw_rect.top,
+ draw_rect.right, draw_rect.bottom);
+ SelectObject(hdc, font);
+ RECT text_rect = gfx_text_rect.ToRECT();
+ DoDrawText(hdc, text, &text_rect,
+ ComputeFormatFlags(flags, text));
+ RestoreDC(hdc, save_dc_id);
+
+ BYTE text_color_r = GetRValue(text_color);
+ BYTE text_color_g = GetGValue(text_color);
+ BYTE text_color_b = GetBValue(text_color);
+
+ SkBitmap bmp = bmp_device.accessBitmap(true);
+ DCHECK_EQ(SkBitmap::kARGB_8888_Config, bmp.config());
+ SkAutoLockPixels lock(bmp);
+
+ // At this point the bitmap has black text on white.
+ // The intensity of black tells us the alpha value of the text.
+ for (int y = draw_rect.top; y < draw_rect.bottom; y++) {
+ for (int x = draw_rect.left; x < draw_rect.right; x++) {
+ // Gets the color directly. DrawText doesn't premultiply alpha so
+ // using SkBitmap::getColor() won't work here.
+ SkColor color = *bmp.getAddr32(x, y);
+ // Use any of the color channels as alpha.
+ BYTE alpha = 0xFF - SkColorGetB(color);
+ *bmp.getAddr32(x, y) = SkPreMultiplyColor(
+ SkColorSetARGB(alpha, text_color_r, text_color_b, text_color_g));
+ }
+ }
+
+ bmp_device.EndPlatformPaint();
+}
+
+// Draws the given text with a fade out gradient. |bmp_device| is a bitmap
+// that is used to temporary drawing. The text is drawn in |text_rect| and
+// clipped to |draw_rect|.
+void DrawTextGradientPart(HDC hdc,
+ skia::BitmapPlatformDevice& bmp_device,
+ const string16& text,
+ const SkColor& color,
+ HFONT font,
+ const gfx::Rect& text_rect,
+ const gfx::Rect& draw_rect,
+ bool fade_to_right,
+ int flags) {
+ DrawTextAndClearBackground(bmp_device, font, skia::SkColorToCOLORREF(color),
+ text, flags, text_rect, draw_rect);
+ FadeBitmapRect(bmp_device, draw_rect, fade_to_right);
+ BLENDFUNCTION blend = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
+
+ HDC bmp_hdc = bmp_device.BeginPlatformPaint();
+ AlphaBlend(hdc, draw_rect.x(), draw_rect.y(), draw_rect.width(),
+ draw_rect.height(), bmp_hdc, draw_rect.x(), draw_rect.y(),
+ draw_rect.width(), draw_rect.height(), blend);
+ bmp_device.EndPlatformPaint();
+}
+
+enum PrimarySide {
+ PrimaryOnLeft,
+ PrimaryOnRight,
+};
+
+// Divides |rect| horizontally into a |primary| of width |primary_width| and a
+// |secondary| taking up the remainder.
+void DivideRect(const gfx::Rect& rect,
+ PrimarySide primary_side,
+ int primary_width,
+ gfx::Rect* primary,
+ gfx::Rect* secondary) {
+ *primary = rect;
+ *secondary = rect;
+ int remainder = rect.width() - primary_width;
+
+ switch (primary_side) {
+ case PrimaryOnLeft:
+ primary->Inset(0, 0, remainder, 0);
+ secondary->Inset(primary_width, 0, 0, 0);
+ break;
+ case PrimaryOnRight:
+ primary->Inset(remainder, 0, 0, 0);
+ secondary->Inset(0, 0, primary_width, 0);
+ break;
+ }
+}
+
} // anonymous namespace
namespace gfx {
@@ -182,7 +328,13 @@ void CanvasSkia::DrawStringInt(const string16& text,
const SkColor& color,
int x, int y, int w, int h,
int flags) {
- if (!IntersectsClipRectInt(x, y, w, h))
+ SkRect fclip;
+ if (!getClipBounds(&fclip))
+ return;
+ RECT text_bounds = { x, y, x + w, y + h };
+ SkIRect clip;
+ fclip.round(&clip);
+ if (!clip.intersect(skia::RECTToSkIRect(text_bounds)))
return;
// Clamp the max amount of text we'll draw to 32K. There seem to be bugs in
@@ -192,7 +344,6 @@ void CanvasSkia::DrawStringInt(const string16& text,
const int kMaxStringLength = 32768 - 1; // So the trailing \0 fits in 32K.
string16 clamped_string(text.substr(0, kMaxStringLength));
- RECT text_bounds = { x, y, x + w, y + h };
HDC dc = beginPlatformPaint();
SetBkMode(dc, TRANSPARENT);
HFONT old_font = (HFONT)SelectObject(dc, font);
@@ -211,7 +362,8 @@ void CanvasSkia::DrawStringInt(const string16& text,
// Windows will have cleared the alpha channel of the text we drew. Assume
// we're drawing to an opaque surface, or at least the text rect area is
// opaque.
- getTopPlatformDevice().makeOpaque(x, y, w, h);
+ getTopPlatformDevice().makeOpaque(clip.fLeft, clip.fTop,
+ clip.width(), clip.height());
}
void CanvasSkia::DrawStringInt(const string16& text,
@@ -303,4 +455,128 @@ ui::TextureID CanvasSkia::GetTextureID() {
return 0;
}
+void CanvasSkia::DrawFadeTruncatingString(
+ const string16& text,
+ CanvasSkia::TruncateFadeMode truncate_mode,
+ size_t desired_characters_to_truncate_from_head,
+ const gfx::Font& font,
+ const SkColor& color,
+ const gfx::Rect& display_rect) {
+ int flags = NO_ELLIPSIS;
+
+ // If the whole string fits in the destination then just draw it directly.
+ int total_string_width;
+ int total_string_height;
+ SizeStringInt(text, font, &total_string_width, &total_string_height,
+ flags | TEXT_VALIGN_TOP);
+ if (total_string_width <= display_rect.width()) {
+ DrawStringInt(text, font, color, display_rect.x(), display_rect.y(),
+ display_rect.width(), display_rect.height(), flags);
+ return;
+ }
+
+ int average_character_width = font.GetAverageCharacterWidth();
+ int clipped_string_width = total_string_width - display_rect.width();
+
+ // Clip the string by drawing it to the left by |offset_x|.
+ int offset_x = 0;
+ switch (truncate_mode) {
+ case TruncateFadeHead:
+ offset_x = clipped_string_width;
+ break;
+ case TruncateFadeHeadAndTail:
+ DCHECK_GT(desired_characters_to_truncate_from_head, 0u);
+ // Get the width of the beginning of the string we're clipping.
+ string16 clipped_head_string =
+ text.substr(0, desired_characters_to_truncate_from_head);
+ int clipped_width;
+ int clipped_height;
+ SizeStringInt(clipped_head_string, font,
+ &clipped_width, &clipped_height, flags);
+
+ // This is the offset at which we start drawing. This causes the
+ // beginning of the string to get clipped.
+ offset_x = clipped_width;
+
+ // Due to the fade effect the first character is hard to see.
+ // We want to make sure that the first character starting at
+ // |desired_characters_to_truncate_from_head| is readable so we reduce
+ // the offset by a little bit.
+ offset_x = std::max(0, Round(offset_x - average_character_width * 2));
+
+ // If the offset is so large that there's empty space at the tail
+ // then reduce the offset so we can use up the empty space.
+ offset_x = std::min(offset_x, clipped_string_width);
+ break;
+ }
+ bool is_truncating_head = offset_x > 0;
+ bool is_truncating_tail = clipped_string_width > offset_x;
+
+ bool is_rtl = (ComputeFormatFlags(flags, text) & DT_RTLREADING) != 0;
+ // |is_rtl| tells us if the given text is right to left or not. |is_rtl| can
+ // be false even if the UI is set to a right to left language.
+ // Now, normally, we right align all text if the UI is set to a right to
+ // left language. In this case though we don't want that because we render
+ // the ends of the string ourselves.
+ if (!is_rtl)
+ flags |= TEXT_ALIGN_LEFT;
+
+ // Fade in/out about 2.5 characters of the beginning/end of the string.
+ // The .5 here is helpful if one of the characters is a space.
+ // Use a quarter of the display width if the string is very short.
+ int gradient_width = Round(std::min(average_character_width * 2.5,
+ display_rect.width() / 4.0));
+
+ // Move the origin to |display_rect.origin()|. This simplifies all the
+ // drawing so that both the source and destination can be (0,0).
+ save(kMatrix_SaveFlag);
+ TranslateInt(display_rect.x(), display_rect.y());
+
+ gfx::Rect solid_part(gfx::Point(), display_rect.size());
+ gfx::Rect head_part;
+ gfx::Rect tail_part;
+ if (is_truncating_head)
+ DivideRect(solid_part, is_rtl ? PrimaryOnRight : PrimaryOnLeft,
+ gradient_width, &head_part, &solid_part);
+ if (is_truncating_tail)
+ DivideRect(solid_part, is_rtl ? PrimaryOnLeft : PrimaryOnRight,
+ gradient_width, &tail_part, &solid_part);
+
+ // Grow |display_rect| by |offset_x|.
+ gfx::Rect text_rect(gfx::Point(), display_rect.size());
+ if (!is_rtl)
+ text_rect.set_x(text_rect.x() - offset_x);
+ text_rect.set_width(text_rect.width() + offset_x);
+
+ // Disable clear type. This makes the text render in gray scale which
+ // makes it easier to compute the alpha value.
+ LOGFONT font_info;
+ GetObject(font.GetNativeFont(), sizeof(font_info), &font_info);
+ font_info.lfQuality = ANTIALIASED_QUALITY;
+ HFONT gray_scale_font = CreateFontIndirect(&font_info);
+
+ HDC hdc = beginPlatformPaint();
+ scoped_ptr<skia::BitmapPlatformDevice> gradient_bitmap(
+ skia::BitmapPlatformDevice::create(NULL, display_rect.width(),
+ display_rect.height(), false, NULL));
+ if (is_truncating_head)
+ DrawTextGradientPart(hdc, *gradient_bitmap, text, color, gray_scale_font,
+ text_rect, head_part, is_rtl, flags);
+ if (is_truncating_tail)
+ DrawTextGradientPart(hdc, *gradient_bitmap, text, color, gray_scale_font,
+ text_rect, tail_part, !is_rtl, flags);
+ endPlatformPaint();
+
+ // Draw the solid part.
+ save(kClip_SaveFlag);
+ ClipRectInt(solid_part.x(), solid_part.y(),
+ solid_part.width(), solid_part.height());
+ DrawStringInt(text, font, color,
+ text_rect.x(), text_rect.y(),
+ text_rect.width(), text_rect.height(),
+ flags);
+ restore();
+ restore();
+}
+
} // namespace gfx