summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorsail@chromium.org <sail@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-05 06:07:56 +0000
committersail@chromium.org <sail@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-05 06:07:56 +0000
commit189b01cd199726bd100fcd66fcbaee4f898c04a4 (patch)
tree0435b8dbf4af0444b47a8d3368eca7cf67302175 /ui
parent429f687ce523db9114bd6284865ecf4d0737322e (diff)
downloadchromium_src-189b01cd199726bd100fcd66fcbaee4f898c04a4.zip
chromium_src-189b01cd199726bd100fcd66fcbaee4f898c04a4.tar.gz
chromium_src-189b01cd199726bd100fcd66fcbaee4f898c04a4.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. Review URL: http://codereview.chromium.org/6695043 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@80416 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r--ui/gfx/canvas_skia.h19
-rw-r--r--ui/gfx/canvas_skia_win.cc277
2 files changed, 292 insertions, 4 deletions
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..c498bb6 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,136 @@ 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) {
+ // Clear the background by filling with white.
+ HBRUSH fill_brush = static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
+ HANDLE old_brush = SelectObject(bmp_device.getBitmapDC(), fill_brush);
+ RECT draw_rect = gfx_draw_rect.ToRECT();
+ FillRect(bmp_device.getBitmapDC(), &draw_rect, fill_brush);
+ SelectObject(bmp_device.getBitmapDC(), old_brush);
+
+ // Set black text with trasparent background.
+ SetBkMode(bmp_device.getBitmapDC(), TRANSPARENT);
+ SetTextColor(bmp_device.getBitmapDC(), 0);
+
+ // Draw the text.
+ int save_dc_id = SaveDC(bmp_device.getBitmapDC());
+ // Clip the text to the draw destination.
+ IntersectClipRect(bmp_device.getBitmapDC(), draw_rect.left, draw_rect.top,
+ draw_rect.right, draw_rect.bottom);
+ SelectObject(bmp_device.getBitmapDC(), font);
+ RECT text_rect = gfx_text_rect.ToRECT();
+ DoDrawText(bmp_device.getBitmapDC(), text, &text_rect,
+ ComputeFormatFlags(flags, text));
+ RestoreDC(bmp_device.getBitmapDC(), 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));
+ }
+ }
+}
+
+// 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};
+ AlphaBlend(hdc, draw_rect.x(), draw_rect.y(), draw_rect.width(),
+ draw_rect.height(), bmp_device.getBitmapDC(), draw_rect.x(),
+ draw_rect.y(), draw_rect.width(), draw_rect.height(), blend);
+}
+
+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 +321,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 +337,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 +355,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 +448,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