Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(239)

Side by Side Diff: ui/gfx/render_text.cc

Issue 11535014: Replace StyleRange with BreakList; update RenderText, etc. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add GetNextBreak and AdvanceIterators convenience methods; cleanup. Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/gfx/render_text.h" 5 #include "ui/gfx/render_text.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/i18n/break_iterator.h" 9 #include "base/i18n/break_iterator.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "base/stl_util.h" 11 #include "base/stl_util.h"
12 #include "third_party/skia/include/core/SkTypeface.h" 12 #include "third_party/skia/include/core/SkTypeface.h"
13 #include "third_party/skia/include/effects/SkGradientShader.h" 13 #include "third_party/skia/include/effects/SkGradientShader.h"
14 #include "ui/base/text/utf16_indexing.h" 14 #include "ui/base/text/utf16_indexing.h"
15 #include "ui/gfx/canvas.h" 15 #include "ui/gfx/canvas.h"
16 #include "ui/gfx/insets.h" 16 #include "ui/gfx/insets.h"
17 #include "ui/gfx/skia_util.h" 17 #include "ui/gfx/skia_util.h"
18 #include "ui/gfx/text_constants.h" 18 #include "ui/gfx/text_constants.h"
19 19
20 namespace gfx {
21
20 namespace { 22 namespace {
21 23
22 // All chars are replaced by this char when the password style is set. 24 // All chars are replaced by this char when the password style is set.
23 // TODO(benrg): GTK uses the first of U+25CF, U+2022, U+2731, U+273A, '*' 25 // TODO(benrg): GTK uses the first of U+25CF, U+2022, U+2731, U+273A, '*'
24 // that's available in the font (find_invisible_char() in gtkentry.c). 26 // that's available in the font (find_invisible_char() in gtkentry.c).
25 const char16 kPasswordReplacementChar = '*'; 27 const char16 kPasswordReplacementChar = '*';
26 28
27 // Default color used for the cursor. 29 // Default color used for the text and cursor.
28 const SkColor kDefaultCursorColor = SK_ColorBLACK; 30 const SkColor kDefaultColor = SK_ColorBLACK;
29
30 // Default color used for drawing selection text.
31 const SkColor kDefaultSelectionColor = SK_ColorBLACK;
32 31
33 // Default color used for drawing selection background. 32 // Default color used for drawing selection background.
34 const SkColor kDefaultSelectionBackgroundColor = SK_ColorGRAY; 33 const SkColor kDefaultSelectionBackgroundColor = SK_ColorGRAY;
35 34
36 #ifndef NDEBUG 35 // Fraction of the text size to lower a strike through below the baseline.
37 // Check StyleRanges invariant conditions: sorted and non-overlapping ranges. 36 const SkScalar kStrikeThroughOffset = (-SK_Scalar1 * 6 / 21);
38 void CheckStyleRanges(const gfx::StyleRanges& style_ranges, size_t length) { 37 // Fraction of the text size to lower an underline below the baseline.
39 if (length == 0) { 38 const SkScalar kUnderlineOffset = (SK_Scalar1 / 9);
40 DCHECK(style_ranges.empty()) << "Style ranges exist for empty text."; 39 // Fraction of the text size to use for a strike through or under-line.
41 return; 40 const SkScalar kLineThickness = (SK_Scalar1 / 18);
42 } 41 // Fraction of the text size to use for a top margin of a diagonal strike.
43 for (gfx::StyleRanges::size_type i = 0; i < style_ranges.size() - 1; i++) { 42 const SkScalar kDiagonalStrikeMarginOffset = (SK_Scalar1 / 4);
44 const ui::Range& former = style_ranges[i].range;
45 const ui::Range& latter = style_ranges[i + 1].range;
46 DCHECK(!former.is_empty()) << "Empty range at " << i << ":" <<
47 former.ToString();
48 DCHECK(former.IsValid()) << "Invalid range at " << i << ":" <<
49 former.ToString();
50 DCHECK(!former.is_reversed()) << "Reversed range at " << i << ":" <<
51 former.ToString();
52 DCHECK(former.end() == latter.start()) << "Ranges gap/overlap/unsorted." <<
53 "former:" << former.ToString() << ", latter:" << latter.ToString();
54 }
55 const gfx::StyleRange& end_style = *style_ranges.rbegin();
56 DCHECK(!end_style.range.is_empty()) << "Empty range at end.";
57 DCHECK(end_style.range.IsValid()) << "Invalid range at end.";
58 DCHECK(!end_style.range.is_reversed()) << "Reversed range at end.";
59 DCHECK(end_style.range.end() == length) << "Style and text length mismatch.";
60 }
61 #endif
62
63 void ApplyStyleRangeImpl(gfx::StyleRanges* style_ranges,
64 const gfx::StyleRange& style_range) {
65 const ui::Range& new_range = style_range.range;
66 // Follow StyleRanges invariant conditions: sorted and non-overlapping ranges.
67 gfx::StyleRanges::iterator i;
68 for (i = style_ranges->begin(); i != style_ranges->end();) {
69 if (i->range.end() < new_range.start()) {
70 i++;
71 } else if (i->range.start() == new_range.end()) {
72 break;
73 } else if (new_range.Contains(i->range)) {
74 i = style_ranges->erase(i);
75 if (i == style_ranges->end())
76 break;
77 } else if (i->range.start() < new_range.start() &&
78 i->range.end() > new_range.end()) {
79 // Split the current style into two styles.
80 gfx::StyleRange split_style = gfx::StyleRange(*i);
81 split_style.range.set_end(new_range.start());
82 i = style_ranges->insert(i, split_style) + 1;
83 i->range.set_start(new_range.end());
84 break;
85 } else if (i->range.start() < new_range.start()) {
86 i->range.set_end(new_range.start());
87 i++;
88 } else if (i->range.end() > new_range.end()) {
89 i->range.set_start(new_range.end());
90 break;
91 } else {
92 NOTREACHED();
93 }
94 }
95 // Add the new range in its sorted location.
96 style_ranges->insert(i, style_range);
97 }
98 43
99 // Converts |gfx::Font::FontStyle| flags to |SkTypeface::Style| flags. 44 // Converts |gfx::Font::FontStyle| flags to |SkTypeface::Style| flags.
100 SkTypeface::Style ConvertFontStyleToSkiaTypefaceStyle(int font_style) { 45 SkTypeface::Style ConvertFontStyleToSkiaTypefaceStyle(int font_style) {
101 int skia_style = SkTypeface::kNormal; 46 int skia_style = SkTypeface::kNormal;
102 if (font_style & gfx::Font::BOLD) 47 skia_style |= (font_style & gfx::Font::BOLD) ? SkTypeface::kBold : 0;
103 skia_style |= SkTypeface::kBold; 48 skia_style |= (font_style & gfx::Font::ITALIC) ? SkTypeface::kItalic : 0;
104 if (font_style & gfx::Font::ITALIC)
105 skia_style |= SkTypeface::kItalic;
106 return static_cast<SkTypeface::Style>(skia_style); 49 return static_cast<SkTypeface::Style>(skia_style);
107 } 50 }
108 51
109 // Given |font| and |display_width|, returns the width of the fade gradient. 52 // Given |font| and |display_width|, returns the width of the fade gradient.
110 int CalculateFadeGradientWidth(const gfx::Font& font, int display_width) { 53 int CalculateFadeGradientWidth(const Font& font, int display_width) {
111 // Fade in/out about 2.5 characters of the beginning/end of the string. 54 // Fade in/out about 2.5 characters of the beginning/end of the string.
112 // The .5 here is helpful if one of the characters is a space. 55 // The .5 here is helpful if one of the characters is a space.
113 // Use a quarter of the display width if the display width is very short. 56 // Use a quarter of the display width if the display width is very short.
114 const int average_character_width = font.GetAverageCharacterWidth(); 57 const int average_character_width = font.GetAverageCharacterWidth();
115 const double gradient_width = std::min(average_character_width * 2.5, 58 const double gradient_width = std::min(average_character_width * 2.5,
116 display_width / 4.0); 59 display_width / 4.0);
117 DCHECK_GE(gradient_width, 0.0); 60 DCHECK_GE(gradient_width, 0.0);
118 return static_cast<int>(floor(gradient_width + 0.5)); 61 return static_cast<int>(floor(gradient_width + 0.5));
119 } 62 }
120 63
121 // Appends to |positions| and |colors| values corresponding to the fade over 64 // Appends to |positions| and |colors| values corresponding to the fade over
122 // |fade_rect| from color |c0| to color |c1|. 65 // |fade_rect| from color |c0| to color |c1|.
123 void AddFadeEffect(const gfx::Rect& text_rect, 66 void AddFadeEffect(const Rect& text_rect,
124 const gfx::Rect& fade_rect, 67 const Rect& fade_rect,
125 SkColor c0, 68 SkColor c0,
126 SkColor c1, 69 SkColor c1,
127 std::vector<SkScalar>* positions, 70 std::vector<SkScalar>* positions,
128 std::vector<SkColor>* colors) { 71 std::vector<SkColor>* colors) {
129 const SkScalar left = static_cast<SkScalar>(fade_rect.x() - text_rect.x()); 72 const SkScalar left = static_cast<SkScalar>(fade_rect.x() - text_rect.x());
130 const SkScalar width = static_cast<SkScalar>(fade_rect.width()); 73 const SkScalar width = static_cast<SkScalar>(fade_rect.width());
131 const SkScalar p0 = left / text_rect.width(); 74 const SkScalar p0 = left / text_rect.width();
132 const SkScalar p1 = (left + width) / text_rect.width(); 75 const SkScalar p1 = (left + width) / text_rect.width();
133 // Prepend 0.0 to |positions|, as required by Skia. 76 // Prepend 0.0 to |positions|, as required by Skia.
134 if (positions->empty() && p0 != 0.0) { 77 if (positions->empty() && p0 != 0.0) {
135 positions->push_back(0.0); 78 positions->push_back(0.0);
136 colors->push_back(c0); 79 colors->push_back(c0);
137 } 80 }
138 positions->push_back(p0); 81 positions->push_back(p0);
139 colors->push_back(c0); 82 colors->push_back(c0);
140 positions->push_back(p1); 83 positions->push_back(p1);
141 colors->push_back(c1); 84 colors->push_back(c1);
142 } 85 }
143 86
144 // Creates a SkShader to fade the text, with |left_part| specifying the left 87 // Creates a SkShader to fade the text, with |left_part| specifying the left
145 // fade effect, if any, and |right_part| specifying the right fade effect. 88 // fade effect, if any, and |right_part| specifying the right fade effect.
146 skia::RefPtr<SkShader> CreateFadeShader(const gfx::Rect& text_rect, 89 skia::RefPtr<SkShader> CreateFadeShader(const Rect& text_rect,
147 const gfx::Rect& left_part, 90 const Rect& left_part,
148 const gfx::Rect& right_part, 91 const Rect& right_part,
149 SkColor color) { 92 SkColor color) {
150 // Fade alpha of 51/255 corresponds to a fade of 0.2 of the original color. 93 // Fade alpha of 51/255 corresponds to a fade of 0.2 of the original color.
151 const SkColor fade_color = SkColorSetA(color, 51); 94 const SkColor fade_color = SkColorSetA(color, 51);
152 std::vector<SkScalar> positions; 95 std::vector<SkScalar> positions;
153 std::vector<SkColor> colors; 96 std::vector<SkColor> colors;
154 97
155 if (!left_part.IsEmpty()) 98 if (!left_part.IsEmpty())
156 AddFadeEffect(text_rect, left_part, fade_color, color, 99 AddFadeEffect(text_rect, left_part, fade_color, color,
157 &positions, &colors); 100 &positions, &colors);
158 if (!right_part.IsEmpty()) 101 if (!right_part.IsEmpty())
(...skipping 11 matching lines...) Expand all
170 points[0].iset(text_rect.x(), text_rect.y()); 113 points[0].iset(text_rect.x(), text_rect.y());
171 points[1].iset(text_rect.right(), text_rect.y()); 114 points[1].iset(text_rect.right(), text_rect.y());
172 115
173 return skia::AdoptRef( 116 return skia::AdoptRef(
174 SkGradientShader::CreateLinear(&points[0], &colors[0], &positions[0], 117 SkGradientShader::CreateLinear(&points[0], &colors[0], &positions[0],
175 colors.size(), SkShader::kClamp_TileMode)); 118 colors.size(), SkShader::kClamp_TileMode));
176 } 119 }
177 120
178 } // namespace 121 } // namespace
179 122
180 namespace gfx {
181
182 namespace internal { 123 namespace internal {
183 124
184 // Value of |underline_thickness_| that indicates that underline metrics have 125 // Value of |underline_thickness_| that indicates that underline metrics have
185 // not been set explicitly. 126 // not been set explicitly.
186 const SkScalar kUnderlineMetricsNotSet = -1.0f; 127 const SkScalar kUnderlineMetricsNotSet = -1.0f;
187 128
188 SkiaTextRenderer::SkiaTextRenderer(Canvas* canvas) 129 SkiaTextRenderer::SkiaTextRenderer(Canvas* canvas)
189 : canvas_skia_(canvas->sk_canvas()), 130 : canvas_skia_(canvas->sk_canvas()),
190 started_drawing_(false), 131 started_drawing_(false),
191 underline_thickness_(kUnderlineMetricsNotSet), 132 underline_thickness_(kUnderlineMetricsNotSet),
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
286 deferred_fade_shader_ = paint_.getShader(); 227 deferred_fade_shader_ = paint_.getShader();
287 paint_.setShader(NULL); 228 paint_.setShader(NULL);
288 canvas_skia_->saveLayer(&bounds_, NULL); 229 canvas_skia_->saveLayer(&bounds_, NULL);
289 } 230 }
290 } 231 }
291 232
292 const size_t byte_length = glyph_count * sizeof(glyphs[0]); 233 const size_t byte_length = glyph_count * sizeof(glyphs[0]);
293 canvas_skia_->drawPosText(&glyphs[0], byte_length, &pos[0], paint_); 234 canvas_skia_->drawPosText(&glyphs[0], byte_length, &pos[0], paint_);
294 } 235 }
295 236
296 // Draw underline and strike through text decorations. 237 void SkiaTextRenderer::DrawDecorations(int x, int y, int width, bool underline,
297 // Based on |SkCanvas::DrawTextDecorations()| and constants from: 238 bool strike, bool diagonal_strike) {
298 // third_party/skia/src/core/SkTextFormatParams.h 239 if (underline)
299 void SkiaTextRenderer::DrawDecorations(int x, int y, int width, 240 DrawUnderline(x, y, width);
300 const StyleRange& style) { 241 if (strike)
301 if (!style.underline && !style.strike && !style.diagonal_strike) 242 DrawStrike(x, y, width);
302 return; 243 if (diagonal_strike)
244 DrawDiagonalStrike(x, y, width);
245 }
303 246
304 // Fraction of the text size to lower a strike through below the baseline. 247 void SkiaTextRenderer::DrawUnderline(int x, int y, int width) {
305 const SkScalar kStrikeThroughOffset = (-SK_Scalar1 * 6 / 21); 248 SkRect r = SkRect::MakeLTRB(x, y + underline_position_, x + width,
306 // Fraction of the text size to lower an underline below the baseline. 249 y + underline_position_ + underline_thickness_);
307 const SkScalar kUnderlineOffset = (SK_Scalar1 / 9); 250 if (underline_thickness_ == kUnderlineMetricsNotSet) {
308 // Fraction of the text size to use for a strike through or under-line. 251 const SkScalar text_size = paint_.getTextSize();
309 const SkScalar kLineThickness = (SK_Scalar1 / 18); 252 r.fTop = SkScalarMulAdd(text_size, kUnderlineOffset, y);
310 // Fraction of the text size to use for a top margin of a diagonal strike. 253 r.fBottom = r.fTop + SkScalarMul(text_size, kLineThickness);
311 const SkScalar kDiagonalStrikeThroughMarginOffset = (SK_Scalar1 / 4); 254 }
255 canvas_skia_->drawRect(r, paint_);
256 }
312 257
313 SkScalar text_size = paint_.getTextSize(); 258 void SkiaTextRenderer::DrawStrike(int x, int y, int width) const {
314 SkScalar height = SkScalarMul(text_size, kLineThickness); 259 const SkScalar text_size = paint_.getTextSize();
315 SkRect r; 260 const SkScalar height = SkScalarMul(text_size, kLineThickness);
261 const SkScalar offset = SkScalarMulAdd(text_size, kStrikeThroughOffset, y);
262 const SkRect r = SkRect::MakeLTRB(x, offset, x + width, offset + height);
263 canvas_skia_->drawRect(r, paint_);
264 }
316 265
317 r.fLeft = x; 266 void SkiaTextRenderer::DrawDiagonalStrike(int x, int y, int width) const {
318 r.fRight = x + width; 267 const SkScalar text_size = paint_.getTextSize();
268 const SkScalar offset = SkScalarMul(text_size, kDiagonalStrikeMarginOffset);
319 269
320 if (style.underline) { 270 SkPaint paint(paint_);
321 if (underline_thickness_ == kUnderlineMetricsNotSet) { 271 paint.setAntiAlias(true);
322 r.fTop = SkScalarMulAdd(text_size, kUnderlineOffset, y); 272 paint.setStyle(SkPaint::kFill_Style);
323 r.fBottom = r.fTop + height; 273 paint.setStrokeWidth(SkScalarMul(text_size, kLineThickness) * 2);
324 } else { 274 canvas_skia_->drawLine(x, y, x + width, y - text_size + offset, paint);
325 r.fTop = y + underline_position_;
326 r.fBottom = r.fTop + underline_thickness_;
327 }
328 canvas_skia_->drawRect(r, paint_);
329 }
330 if (style.strike) {
331 SkScalar offset = SkScalarMulAdd(text_size, kStrikeThroughOffset, y);
332 r.fTop = offset;
333 r.fBottom = offset + height;
334 canvas_skia_->drawRect(r, paint_);
335 }
336 if (style.diagonal_strike) {
337 SkScalar offset =
338 SkScalarMul(text_size, kDiagonalStrikeThroughMarginOffset);
339 SkPaint paint(paint_);
340 paint.setAntiAlias(true);
341 paint.setStyle(SkPaint::kFill_Style);
342 paint.setStrokeWidth(height * 2);
343 canvas_skia_->drawLine(
344 SkIntToScalar(x), SkIntToScalar(y),
345 SkIntToScalar(x + width), SkIntToScalar(y) - text_size + offset,
346 paint);
347 }
348 } 275 }
349 276
350 } // namespace internal 277 } // namespace internal
351 278
352
353 StyleRange::StyleRange()
354 : foreground(SK_ColorBLACK),
355 font_style(gfx::Font::NORMAL),
356 strike(false),
357 diagonal_strike(false),
358 underline(false) {
359 }
360
361 RenderText::~RenderText() { 279 RenderText::~RenderText() {
362 } 280 }
363 281
364 void RenderText::SetText(const string16& text) { 282 void RenderText::SetText(const string16& text) {
365 DCHECK(!composition_range_.IsValid()); 283 DCHECK(!composition_range_.IsValid());
366 size_t old_text_length = text_.length(); 284 const size_t old_length = text_.length();
367 text_ = text; 285 text_ = text;
368 286
369 // Update the style ranges as needed. 287 // Adjust any ranged styles and colors to accommodate a new text length.
370 if (text_.empty()) { 288 const size_t new_length = text_.length();
371 style_ranges_.clear(); 289 if (new_length < old_length) {
372 } else if (style_ranges_.empty()) { 290 colors_.TrimBreaks(new_length);
373 ApplyDefaultStyle(); 291 for (size_t style = 0; style < NUM_TEXT_STYLES; ++style)
374 } else if (text_.length() > old_text_length) { 292 styles_[style].TrimBreaks(new_length);
375 style_ranges_.back().range.set_end(text_.length());
376 } else if (text_.length() < old_text_length) {
377 StyleRanges::iterator i;
378 for (i = style_ranges_.begin(); i != style_ranges_.end(); i++) {
379 if (i->range.start() >= text_.length()) {
380 // Style ranges are sorted and non-overlapping, so all the subsequent
381 // style ranges should be out of text_.length() as well.
382 style_ranges_.erase(i, style_ranges_.end());
383 break;
384 }
385 }
386 // Since style ranges are sorted and non-overlapping, if there is a style
387 // range ends beyond text_.length, it must be the last one.
388 style_ranges_.back().range.set_end(text_.length());
389 } 293 }
390 #ifndef NDEBUG
391 CheckStyleRanges(style_ranges_, text_.length());
392 #endif
393 cached_bounds_and_offset_valid_ = false; 294 cached_bounds_and_offset_valid_ = false;
394 295
395 // Reset selection model. SetText should always followed by SetSelectionModel 296 // Reset selection model. SetText should always followed by SetSelectionModel
396 // or SetCursorPosition in upper layer. 297 // or SetCursorPosition in upper layer.
397 SetSelectionModel(SelectionModel()); 298 SetSelectionModel(SelectionModel());
398 299
399 // Invalidate the cached text direction if it depends on the text contents. 300 // Invalidate the cached text direction if it depends on the text contents.
400 if (directionality_mode_ == DIRECTIONALITY_FROM_TEXT) 301 if (directionality_mode_ == DIRECTIONALITY_FROM_TEXT)
401 text_direction_ = base::i18n::UNKNOWN_DIRECTION; 302 text_direction_ = base::i18n::UNKNOWN_DIRECTION;
402 303
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after
575 } 476 }
576 477
577 void RenderText::SetCompositionRange(const ui::Range& composition_range) { 478 void RenderText::SetCompositionRange(const ui::Range& composition_range) {
578 CHECK(!composition_range.IsValid() || 479 CHECK(!composition_range.IsValid() ||
579 ui::Range(0, text_.length()).Contains(composition_range)); 480 ui::Range(0, text_.length()).Contains(composition_range));
580 composition_range_.set_end(composition_range.end()); 481 composition_range_.set_end(composition_range.end());
581 composition_range_.set_start(composition_range.start()); 482 composition_range_.set_start(composition_range.start());
582 ResetLayout(); 483 ResetLayout();
583 } 484 }
584 485
585 void RenderText::ApplyStyleRange(const StyleRange& style_range) { 486 void RenderText::SetColor(SkColor value) {
586 const ui::Range& new_range = style_range.range; 487 // TODO(msw): Fix Windows invalidation and use colors_.SetValue(value).
587 if (!new_range.IsValid() || new_range.is_empty()) 488 ApplyColor(value, ui::Range(0, text().length()));
588 return; 489 }
589 CHECK(!new_range.is_reversed()); 490
590 CHECK(ui::Range(0, text_.length()).Contains(new_range)); 491 void RenderText::ApplyColor(SkColor value, const ui::Range& range) {
591 ApplyStyleRangeImpl(&style_ranges_, style_range); 492 DCHECK(ui::Range(0, text().length()).Contains(range));
592 #ifndef NDEBUG 493 colors_.ApplyValue(value, range.end() >= text().length() ?
593 CheckStyleRanges(style_ranges_, text_.length()); 494 ui::Range(range.start(), INT_MAX) : range);
594 #endif 495
595 // TODO(xji): only invalidate if font or underline changes. 496 #if defined(OS_WIN)
497 // TODO(msw): Windows applies colors and decorations in the layout process.
596 cached_bounds_and_offset_valid_ = false; 498 cached_bounds_and_offset_valid_ = false;
597 ResetLayout(); 499 ResetLayout();
500 #endif
598 } 501 }
599 502
600 void RenderText::ApplyDefaultStyle() { 503 void RenderText::SetStyle(TextStyle style, bool value) {
601 style_ranges_.clear(); 504 // TODO(msw): Fix Windows invalidation and use styles_[style].SetValue(value).
602 StyleRange style = StyleRange(default_style_); 505 ApplyStyle(style, value, ui::Range(0, text().length()));
603 style.range.set_end(text_.length()); 506 }
604 style_ranges_.push_back(style); 507
605 cached_bounds_and_offset_valid_ = false; 508 void RenderText::ApplyStyle(TextStyle style,
606 ResetLayout(); 509 bool value,
510 const ui::Range& range) {
511 DCHECK(ui::Range(0, text().length()).Contains(range));
512 styles_[style].ApplyValue(value, range.end() >= text().length() ?
513 ui::Range(range.start(), INT_MAX) : range);
514
515 // Only invalidate the layout on font changes; not for colors or decorations.
516 bool invalidate = (style == BOLD) || (style == UNDERLINE);
Alexei Svitkine (slow) 2013/01/28 20:52:30 Don't you mean (style == BOLD) || (style == ITALIC
msw 2013/01/29 17:17:25 Done; thanks for the catch!
517 #if defined(OS_WIN)
518 // TODO(msw): Windows applies colors and decorations in the layout process.
519 invalidate = true;
520 #endif
521 if (invalidate) {
522 cached_bounds_and_offset_valid_ = false;
523 ResetLayout();
524 }
607 } 525 }
608 526
609 void RenderText::SetDirectionalityMode(DirectionalityMode mode) { 527 void RenderText::SetDirectionalityMode(DirectionalityMode mode) {
610 if (mode == directionality_mode_) 528 if (mode == directionality_mode_)
611 return; 529 return;
612 530
613 directionality_mode_ = mode; 531 directionality_mode_ = mode;
614 text_direction_ = base::i18n::UNKNOWN_DIRECTION; 532 text_direction_ = base::i18n::UNKNOWN_DIRECTION;
615 ResetLayout(); 533 ResetLayout();
616 } 534 }
(...skipping 27 matching lines...) Expand all
644 562
645 VisualCursorDirection RenderText::GetVisualDirectionOfLogicalEnd() { 563 VisualCursorDirection RenderText::GetVisualDirectionOfLogicalEnd() {
646 return GetTextDirection() == base::i18n::LEFT_TO_RIGHT ? 564 return GetTextDirection() == base::i18n::LEFT_TO_RIGHT ?
647 CURSOR_RIGHT : CURSOR_LEFT; 565 CURSOR_RIGHT : CURSOR_LEFT;
648 } 566 }
649 567
650 void RenderText::Draw(Canvas* canvas) { 568 void RenderText::Draw(Canvas* canvas) {
651 EnsureLayout(); 569 EnsureLayout();
652 570
653 if (clip_to_display_rect()) { 571 if (clip_to_display_rect()) {
654 gfx::Rect clip_rect(display_rect()); 572 Rect clip_rect(display_rect());
655 clip_rect.Inset(ShadowValue::GetMargin(text_shadows_)); 573 clip_rect.Inset(ShadowValue::GetMargin(text_shadows_));
656 574
657 canvas->Save(); 575 canvas->Save();
658 canvas->ClipRect(clip_rect); 576 canvas->ClipRect(clip_rect);
659 } 577 }
660 578
661 if (!text().empty()) 579 if (!text().empty())
662 DrawSelection(canvas); 580 DrawSelection(canvas);
663 581
664 DrawCursor(canvas); 582 DrawCursor(canvas);
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
745 text_shadows_ = shadows; 663 text_shadows_ = shadows;
746 } 664 }
747 665
748 RenderText::RenderText() 666 RenderText::RenderText()
749 : horizontal_alignment_(base::i18n::IsRTL() ? ALIGN_RIGHT : ALIGN_LEFT), 667 : horizontal_alignment_(base::i18n::IsRTL() ? ALIGN_RIGHT : ALIGN_LEFT),
750 directionality_mode_(DIRECTIONALITY_FROM_TEXT), 668 directionality_mode_(DIRECTIONALITY_FROM_TEXT),
751 text_direction_(base::i18n::UNKNOWN_DIRECTION), 669 text_direction_(base::i18n::UNKNOWN_DIRECTION),
752 cursor_enabled_(true), 670 cursor_enabled_(true),
753 cursor_visible_(false), 671 cursor_visible_(false),
754 insert_mode_(true), 672 insert_mode_(true),
755 cursor_color_(kDefaultCursorColor), 673 cursor_color_(kDefaultColor),
756 selection_color_(kDefaultSelectionColor), 674 selection_color_(kDefaultColor),
757 selection_background_focused_color_(kDefaultSelectionBackgroundColor), 675 selection_background_focused_color_(kDefaultSelectionBackgroundColor),
758 selection_background_unfocused_color_(kDefaultSelectionBackgroundColor), 676 selection_background_unfocused_color_(kDefaultSelectionBackgroundColor),
759 focused_(false), 677 focused_(false),
760 composition_range_(ui::Range::InvalidRange()), 678 composition_range_(ui::Range::InvalidRange()),
679 colors_(kDefaultColor),
761 obscured_(false), 680 obscured_(false),
762 fade_head_(false), 681 fade_head_(false),
763 fade_tail_(false), 682 fade_tail_(false),
764 background_is_transparent_(false), 683 background_is_transparent_(false),
765 clip_to_display_rect_(true), 684 clip_to_display_rect_(true),
766 cached_bounds_and_offset_valid_(false) { 685 cached_bounds_and_offset_valid_(false) {
767 } 686 }
768 687
769 const Vector2d& RenderText::GetUpdatedDisplayOffset() { 688 const Vector2d& RenderText::GetUpdatedDisplayOffset() {
770 UpdateCachedBoundsAndOffset(); 689 UpdateCachedBoundsAndOffset();
(...skipping 24 matching lines...) Expand all
795 void RenderText::SetSelectionModel(const SelectionModel& model) { 714 void RenderText::SetSelectionModel(const SelectionModel& model) {
796 DCHECK_LE(model.selection().GetMax(), text().length()); 715 DCHECK_LE(model.selection().GetMax(), text().length());
797 selection_model_ = model; 716 selection_model_ = model;
798 cached_bounds_and_offset_valid_ = false; 717 cached_bounds_and_offset_valid_ = false;
799 } 718 }
800 719
801 const string16& RenderText::GetLayoutText() const { 720 const string16& RenderText::GetLayoutText() const {
802 return obscured() ? obscured_text_ : text(); 721 return obscured() ? obscured_text_ : text();
803 } 722 }
804 723
805 void RenderText::ApplyCompositionAndSelectionStyles( 724 size_t RenderText::GetNextBreak(
806 StyleRanges* style_ranges) { 725 const BreakList<SkColor>::const_iterator& color,
807 // TODO(msw): This pattern ought to be reconsidered; what about composition 726 const BreakList<bool>::const_iterator style[NUM_TEXT_STYLES]) const {
808 // and selection overlaps, retain existing local style features? 727 size_t next_break = colors_.GetBreakEnd(color);
809 // Apply a composition style override to a copy of the style ranges. 728 for (size_t i = 0; i < NUM_TEXT_STYLES; ++i)
810 if (composition_range_.IsValid() && !composition_range_.is_empty()) { 729 next_break = std::min(next_break, styles_[i].GetBreakEnd(style[i]));
811 StyleRange composition_style(default_style_); 730 return TextIndexToLayoutIndex(std::min(text().length(), next_break));
812 composition_style.underline = true; 731 }
813 composition_style.range = composition_range_; 732
814 ApplyStyleRangeImpl(style_ranges, composition_style); 733 void RenderText::AdvanceIterators(
734 size_t layout_index,
735 BreakList<SkColor>::const_iterator* color,
736 BreakList<bool>::const_iterator style[NUM_TEXT_STYLES]) const {
737 const size_t text_index = LayoutIndexToTextIndex(layout_index);
738 // Note: Using BreakList::GetBreak() would generalize better but run slower.
739 *color += colors_.GetBreakEnd(*color) <= text_index ? 1 : 0;
Alexei Svitkine (slow) 2013/01/28 20:52:30 Can you add a DCHECK() that the new |color| has a
msw 2013/01/29 17:17:25 Moot, now BreakList::GetBreak(), as per your comme
740 for (size_t i = 0; i < NUM_TEXT_STYLES; ++i)
741 style[i] += styles_[i].GetBreakEnd(style[i]) <= text_index ? 1 : 0;
Alexei Svitkine (slow) 2013/01/28 20:52:30 Also, DCHECK() here, same as my comment above.
msw 2013/01/29 17:17:25 Moot, now BreakList::GetBreak(), as per your comme
742 }
743
744 void RenderText::ApplyCompositionAndSelectionStyles() {
745 // Save the underline and color lists to undo the temporary styles later.
746 styles_[UNDERLINE].SaveList();
747 colors_.SaveList();
748
749 // Apply an underline to the composition range in |underlines|.
750 if (composition_range_.IsValid() && !composition_range_.is_empty())
751 styles_[UNDERLINE].ApplyValue(true, composition_range_);
752
753 // Apply the selected text color to the selection range.
754 if (!selection().is_empty()) {
755 const ui::Range range(selection().GetMin(), selection().GetMax());
Alexei Svitkine (slow) 2013/01/28 20:52:30 Is there a reason you can't just pass selection()
msw 2013/01/29 17:17:25 ApplyValue (like ApplyStyleRangeImpl) won't correc
756 colors_.ApplyValue(selection_color_, range);
815 } 757 }
816 // Apply a selection style override to a copy of the style ranges. 758 // Apply the selected text color to the cursor range in overtype mode.
817 if (!selection().is_empty()) { 759 if (!insert_mode_ && cursor_visible() && focused()) {
818 StyleRange selection_style(default_style_); 760 const size_t cursor = cursor_position();
819 selection_style.foreground = selection_color_; 761 const size_t next = IndexOfAdjacentGrapheme(cursor, CURSOR_FORWARD);
820 selection_style.range = ui::Range(selection().GetMin(), 762 colors_.ApplyValue(selection_color_, ui::Range(cursor, next));
821 selection().GetMax());
822 ApplyStyleRangeImpl(style_ranges, selection_style);
823 } 763 }
824 // Apply replacement-mode style override to a copy of the style ranges. 764 }
825 // 765
826 // TODO(xji): NEED TO FIX FOR WINDOWS ASAP. Windows call this function (to 766 void RenderText::UndoCompositionAndSelectionStyles() {
Alexei Svitkine (slow) 2013/01/28 20:52:30 Can you add a bool to keep track of whether the se
msw 2013/01/29 17:17:25 Done.
827 // apply styles) in ItemizeLogicalText(). In order for the cursor's underline 767 // Restore the underline and color lists to undo the temporary styles.
828 // character to be drawn correctly, we will need to re-layout the text. It's 768 styles_[UNDERLINE].RestoreList();
829 // not practical to do layout on every cursor blink. We need to fix Windows 769 colors_.RestoreList();
830 // port to apply styles during drawing phase like Linux port does.
831 // http://crbug.com/110109
832 if (!insert_mode_ && cursor_visible() && focused()) {
833 StyleRange replacement_mode_style(default_style_);
834 replacement_mode_style.foreground = selection_color_;
835 size_t cursor = cursor_position();
836 replacement_mode_style.range.set_start(cursor);
837 replacement_mode_style.range.set_end(
838 IndexOfAdjacentGrapheme(cursor, CURSOR_FORWARD));
839 ApplyStyleRangeImpl(style_ranges, replacement_mode_style);
840 }
841 } 770 }
842 771
843 Vector2d RenderText::GetTextOffset() { 772 Vector2d RenderText::GetTextOffset() {
844 Vector2d offset = display_rect().OffsetFromOrigin(); 773 Vector2d offset = display_rect().OffsetFromOrigin();
845 offset.Add(GetUpdatedDisplayOffset()); 774 offset.Add(GetUpdatedDisplayOffset());
846 offset.Add(GetAlignmentOffset()); 775 offset.Add(GetAlignmentOffset());
847 return offset; 776 return offset;
848 } 777 }
849 778
850 Point RenderText::ToTextPoint(const Point& point) { 779 Point RenderText::ToTextPoint(const Point& point) {
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
892 821
893 bool fade_left = fade_head(); 822 bool fade_left = fade_head();
894 bool fade_right = fade_tail(); 823 bool fade_right = fade_tail();
895 // Under RTL, |fade_right| == |fade_head|. 824 // Under RTL, |fade_right| == |fade_head|.
896 // TODO(asvitkine): This is currently not based on GetTextDirection() because 825 // TODO(asvitkine): This is currently not based on GetTextDirection() because
897 // RenderTextWin does not return a direction that's based on 826 // RenderTextWin does not return a direction that's based on
898 // the text content. 827 // the text content.
899 if (horizontal_alignment() == ALIGN_RIGHT) 828 if (horizontal_alignment() == ALIGN_RIGHT)
900 std::swap(fade_left, fade_right); 829 std::swap(fade_left, fade_right);
901 830
902 gfx::Rect solid_part = display_rect(); 831 Rect solid_part = display_rect();
903 gfx::Rect left_part; 832 Rect left_part;
904 gfx::Rect right_part; 833 Rect right_part;
905 if (fade_left) { 834 if (fade_left) {
906 left_part = solid_part; 835 left_part = solid_part;
907 left_part.Inset(0, 0, solid_part.width() - gradient_width, 0); 836 left_part.Inset(0, 0, solid_part.width() - gradient_width, 0);
908 solid_part.Inset(gradient_width, 0, 0, 0); 837 solid_part.Inset(gradient_width, 0, 0, 0);
909 } 838 }
910 if (fade_right) { 839 if (fade_right) {
911 right_part = solid_part; 840 right_part = solid_part;
912 right_part.Inset(solid_part.width() - gradient_width, 0, 0, 0); 841 right_part.Inset(solid_part.width() - gradient_width, 0, 0, 0);
913 solid_part.Inset(0, 0, gradient_width, 0); 842 solid_part.Inset(0, 0, gradient_width, 0);
914 } 843 }
915 844
916 gfx::Rect text_rect = display_rect(); 845 Rect text_rect = display_rect();
917 text_rect.Inset(GetAlignmentOffset().x(), 0, 0, 0); 846 text_rect.Inset(GetAlignmentOffset().x(), 0, 0, 0);
918 847
919 const SkColor color = default_style().foreground; 848 skia::RefPtr<SkShader> shader = CreateFadeShader(
920 skia::RefPtr<SkShader> shader = 849 text_rect, left_part, right_part, colors_.list().front().second);
921 CreateFadeShader(text_rect, left_part, right_part, color);
922 if (shader) 850 if (shader)
923 renderer->SetShader(shader.get(), display_rect()); 851 renderer->SetShader(shader.get(), display_rect());
924 } 852 }
925 853
926 void RenderText::ApplyTextShadows(internal::SkiaTextRenderer* renderer) { 854 void RenderText::ApplyTextShadows(internal::SkiaTextRenderer* renderer) {
927 skia::RefPtr<SkDrawLooper> looper = 855 skia::RefPtr<SkDrawLooper> looper = CreateShadowDrawLooper(text_shadows_);
928 gfx::CreateShadowDrawLooper(text_shadows_);
929 renderer->SetDrawLooper(looper.get()); 856 renderer->SetDrawLooper(looper.get());
930 } 857 }
931 858
932 // static 859 // static
933 bool RenderText::RangeContainsCaret(const ui::Range& range, 860 bool RenderText::RangeContainsCaret(const ui::Range& range,
934 size_t caret_pos, 861 size_t caret_pos,
935 LogicalCursorDirection caret_affinity) { 862 LogicalCursorDirection caret_affinity) {
936 // NB: exploits unsigned wraparound (WG14/N1124 section 6.2.5 paragraph 9). 863 // NB: exploits unsigned wraparound (WG14/N1124 section 6.2.5 paragraph 9).
937 size_t adjacent = (caret_affinity == CURSOR_BACKWARD) ? 864 size_t adjacent = (caret_affinity == CURSOR_BACKWARD) ?
938 caret_pos - 1 : caret_pos + 1; 865 caret_pos - 1 : caret_pos + 1;
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
992 } else if (display_offset_.x() != 0) { 919 } else if (display_offset_.x() != 0) {
993 // Reduce the pan offset to show additional overflow text when the display 920 // Reduce the pan offset to show additional overflow text when the display
994 // width increases. 921 // width increases.
995 const int negate_rtl = horizontal_alignment_ == ALIGN_RIGHT ? -1 : 1; 922 const int negate_rtl = horizontal_alignment_ == ALIGN_RIGHT ? -1 : 1;
996 const int offset = negate_rtl * display_offset_.x(); 923 const int offset = negate_rtl * display_offset_.x();
997 if (display_width > (content_width + offset)) { 924 if (display_width > (content_width + offset)) {
998 delta_x = negate_rtl * (display_width - (content_width + offset)); 925 delta_x = negate_rtl * (display_width - (content_width + offset));
999 } 926 }
1000 } 927 }
1001 928
1002 gfx::Vector2d delta_offset(delta_x, 0); 929 Vector2d delta_offset(delta_x, 0);
1003 display_offset_ += delta_offset; 930 display_offset_ += delta_offset;
1004 cursor_bounds_ += delta_offset; 931 cursor_bounds_ += delta_offset;
1005 } 932 }
1006 933
1007 void RenderText::DrawSelection(Canvas* canvas) { 934 void RenderText::DrawSelection(Canvas* canvas) {
1008 const SkColor color = focused() ? selection_background_focused_color_ : 935 const SkColor color = focused() ? selection_background_focused_color_ :
1009 selection_background_unfocused_color_; 936 selection_background_unfocused_color_;
1010 const std::vector<Rect> sel = GetSubstringBounds(selection()); 937 const std::vector<Rect> sel = GetSubstringBounds(selection());
1011 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) 938 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i)
1012 canvas->FillRect(*i, color); 939 canvas->FillRect(*i, color);
1013 } 940 }
1014 941
1015 void RenderText::DrawCursor(Canvas* canvas) { 942 void RenderText::DrawCursor(Canvas* canvas) {
1016 // Paint cursor. Replace cursor is drawn as rectangle for now. 943 // Paint cursor. Replace cursor is drawn as rectangle for now.
1017 // TODO(msw): Draw a better cursor with a better indication of association. 944 // TODO(msw): Draw a better cursor with a better indication of association.
1018 if (cursor_enabled() && cursor_visible() && focused()) { 945 if (cursor_enabled() && cursor_visible() && focused()) {
1019 const Rect& bounds = GetUpdatedCursorBounds(); 946 const Rect& bounds = GetUpdatedCursorBounds();
1020 DCHECK(bounds.width()); 947 DCHECK(bounds.width());
1021 canvas->FillRect(bounds, cursor_color_); 948 canvas->FillRect(bounds, cursor_color_);
1022 } 949 }
1023 } 950 }
1024 951
1025 } // namespace gfx 952 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698