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

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: Address comments. 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
35 // Fraction of the text size to lower a strike through below the baseline.
36 const SkScalar kStrikeThroughOffset = (-SK_Scalar1 * 6 / 21);
37 // Fraction of the text size to lower an underline below the baseline.
38 const SkScalar kUnderlineOffset = (SK_Scalar1 / 9);
39 // Fraction of the text size to use for a strike through or under-line.
40 const SkScalar kLineThickness = (SK_Scalar1 / 18);
41 // Fraction of the text size to use for a top margin of a diagonal strike.
42 const SkScalar kDiagonalStrikeMarginOffset = (SK_Scalar1 / 4);
43
44 // Get the break at or preceeding |index|; |breaks| must not be empty.
Alexei Svitkine (slow) 2013/01/23 16:52:54 Please mention what units the |index| is. The way
msw 2013/01/25 09:10:02 Replaced 'index' terminology with 'position' and m
45 template <class T>
46 typename std::vector<std::pair<size_t, T> >::iterator GetBreak(
Alexei Svitkine (slow) 2013/01/23 16:52:54 Can you make this templated on the vector too, to
msw 2013/01/25 09:10:02 Moot.
47 std::vector<std::pair<size_t, T> >* breaks,
48 size_t index) {
49 typename std::vector<std::pair<size_t, T> >::iterator i = breaks->end() - 1;
50 for (; i != breaks->begin() && i->first > index; --i);
51 return i;
52 }
53
36 #ifndef NDEBUG 54 #ifndef NDEBUG
37 // Check StyleRanges invariant conditions: sorted and non-overlapping ranges. 55 // Check for ordered breaks [0->length) with no adjacent equivalent values.
38 void CheckStyleRanges(const gfx::StyleRanges& style_ranges, size_t length) { 56 template <class T>
39 if (length == 0) { 57 void CheckBreaks(const std::vector<std::pair<size_t, T> >& breaks,
40 DCHECK(style_ranges.empty()) << "Style ranges exist for empty text."; 58 size_t text_length) {
41 return; 59 DCHECK_EQ(breaks[0].first, 0U) << "The first break must be at index 0.";
60 for (size_t i = 0; i < breaks.size() - 1; ++i) {
61 DCHECK_LT(breaks[i].first, breaks[i + 1].first) << "Break out of order.";
62 DCHECK_NE(breaks[i].second, breaks[i + 1].second) << "Redundant break.";
42 } 63 }
43 for (gfx::StyleRanges::size_type i = 0; i < style_ranges.size() - 1; i++) { 64 if (text_length > 0)
44 const ui::Range& former = style_ranges[i].range; 65 DCHECK_LT(breaks.back().first, text_length) << "Break beyond text length.";
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 } 66 }
61 #endif 67 #endif
62 68
63 void ApplyStyleRangeImpl(gfx::StyleRanges* style_ranges, 69 template <class T>
64 const gfx::StyleRange& style_range) { 70 void ResizeBreaks(std::vector<std::pair<size_t, T> >* breaks, size_t length) {
Alexei Svitkine (slow) 2013/01/23 16:52:54 Add a comment. Also, |breaks| should be last in pa
msw 2013/01/25 09:10:02 Moot.
65 const ui::Range& new_range = style_range.range; 71 if (length == 0) {
66 // Follow StyleRanges invariant conditions: sorted and non-overlapping ranges. 72 breaks->resize(1);
67 gfx::StyleRanges::iterator i; 73 } else {
68 for (i = style_ranges->begin(); i != style_ranges->end();) { 74 for (size_t i = 0; i < breaks->size(); ++i)
69 if (i->range.end() < new_range.start()) { 75 if (breaks->at(i).first >= length)
70 i++; 76 breaks->resize(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 } 77 }
95 // Add the new range in its sorted location. 78 #ifndef NDEBUG
96 style_ranges->insert(i, style_range); 79 CheckBreaks(*breaks, length);
80 #endif
97 } 81 }
98 82
99 // Converts |gfx::Font::FontStyle| flags to |SkTypeface::Style| flags. 83 // Converts |gfx::Font::FontStyle| flags to |SkTypeface::Style| flags.
100 SkTypeface::Style ConvertFontStyleToSkiaTypefaceStyle(int font_style) { 84 SkTypeface::Style ConvertFontStyleToSkiaTypefaceStyle(int font_style) {
101 int skia_style = SkTypeface::kNormal; 85 int skia_style = SkTypeface::kNormal;
102 if (font_style & gfx::Font::BOLD) 86 skia_style |= (font_style & gfx::Font::BOLD) ? SkTypeface::kBold : 0;
103 skia_style |= SkTypeface::kBold; 87 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); 88 return static_cast<SkTypeface::Style>(skia_style);
107 } 89 }
108 90
109 // Given |font| and |display_width|, returns the width of the fade gradient. 91 // Given |font| and |display_width|, returns the width of the fade gradient.
110 int CalculateFadeGradientWidth(const gfx::Font& font, int display_width) { 92 int CalculateFadeGradientWidth(const Font& font, int display_width) {
111 // Fade in/out about 2.5 characters of the beginning/end of the string. 93 // 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. 94 // 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. 95 // Use a quarter of the display width if the display width is very short.
114 const int average_character_width = font.GetAverageCharacterWidth(); 96 const int average_character_width = font.GetAverageCharacterWidth();
115 const double gradient_width = std::min(average_character_width * 2.5, 97 const double gradient_width = std::min(average_character_width * 2.5,
116 display_width / 4.0); 98 display_width / 4.0);
117 DCHECK_GE(gradient_width, 0.0); 99 DCHECK_GE(gradient_width, 0.0);
118 return static_cast<int>(floor(gradient_width + 0.5)); 100 return static_cast<int>(floor(gradient_width + 0.5));
119 } 101 }
120 102
121 // Appends to |positions| and |colors| values corresponding to the fade over 103 // Appends to |positions| and |colors| values corresponding to the fade over
122 // |fade_rect| from color |c0| to color |c1|. 104 // |fade_rect| from color |c0| to color |c1|.
123 void AddFadeEffect(const gfx::Rect& text_rect, 105 void AddFadeEffect(const Rect& text_rect,
124 const gfx::Rect& fade_rect, 106 const Rect& fade_rect,
125 SkColor c0, 107 SkColor c0,
126 SkColor c1, 108 SkColor c1,
127 std::vector<SkScalar>* positions, 109 std::vector<SkScalar>* positions,
128 std::vector<SkColor>* colors) { 110 std::vector<SkColor>* colors) {
129 const SkScalar left = static_cast<SkScalar>(fade_rect.x() - text_rect.x()); 111 const SkScalar left = static_cast<SkScalar>(fade_rect.x() - text_rect.x());
130 const SkScalar width = static_cast<SkScalar>(fade_rect.width()); 112 const SkScalar width = static_cast<SkScalar>(fade_rect.width());
131 const SkScalar p0 = left / text_rect.width(); 113 const SkScalar p0 = left / text_rect.width();
132 const SkScalar p1 = (left + width) / text_rect.width(); 114 const SkScalar p1 = (left + width) / text_rect.width();
133 // Prepend 0.0 to |positions|, as required by Skia. 115 // Prepend 0.0 to |positions|, as required by Skia.
134 if (positions->empty() && p0 != 0.0) { 116 if (positions->empty() && p0 != 0.0) {
135 positions->push_back(0.0); 117 positions->push_back(0.0);
136 colors->push_back(c0); 118 colors->push_back(c0);
137 } 119 }
138 positions->push_back(p0); 120 positions->push_back(p0);
139 colors->push_back(c0); 121 colors->push_back(c0);
140 positions->push_back(p1); 122 positions->push_back(p1);
141 colors->push_back(c1); 123 colors->push_back(c1);
142 } 124 }
143 125
144 // Creates a SkShader to fade the text, with |left_part| specifying the left 126 // 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. 127 // fade effect, if any, and |right_part| specifying the right fade effect.
146 skia::RefPtr<SkShader> CreateFadeShader(const gfx::Rect& text_rect, 128 skia::RefPtr<SkShader> CreateFadeShader(const Rect& text_rect,
147 const gfx::Rect& left_part, 129 const Rect& left_part,
148 const gfx::Rect& right_part, 130 const Rect& right_part,
149 SkColor color) { 131 SkColor color) {
150 // Fade alpha of 51/255 corresponds to a fade of 0.2 of the original color. 132 // Fade alpha of 51/255 corresponds to a fade of 0.2 of the original color.
151 const SkColor fade_color = SkColorSetA(color, 51); 133 const SkColor fade_color = SkColorSetA(color, 51);
152 std::vector<SkScalar> positions; 134 std::vector<SkScalar> positions;
153 std::vector<SkColor> colors; 135 std::vector<SkColor> colors;
154 136
155 if (!left_part.IsEmpty()) 137 if (!left_part.IsEmpty())
156 AddFadeEffect(text_rect, left_part, fade_color, color, 138 AddFadeEffect(text_rect, left_part, fade_color, color,
157 &positions, &colors); 139 &positions, &colors);
158 if (!right_part.IsEmpty()) 140 if (!right_part.IsEmpty())
(...skipping 11 matching lines...) Expand all
170 points[0].iset(text_rect.x(), text_rect.y()); 152 points[0].iset(text_rect.x(), text_rect.y());
171 points[1].iset(text_rect.right(), text_rect.y()); 153 points[1].iset(text_rect.right(), text_rect.y());
172 154
173 return skia::AdoptRef( 155 return skia::AdoptRef(
174 SkGradientShader::CreateLinear(&points[0], &colors[0], &positions[0], 156 SkGradientShader::CreateLinear(&points[0], &colors[0], &positions[0],
175 colors.size(), SkShader::kClamp_TileMode)); 157 colors.size(), SkShader::kClamp_TileMode));
176 } 158 }
177 159
178 } // namespace 160 } // namespace
179 161
180 namespace gfx {
181
182 namespace internal { 162 namespace internal {
183 163
184 // Value of |underline_thickness_| that indicates that underline metrics have 164 // Value of |underline_thickness_| that indicates that underline metrics have
185 // not been set explicitly. 165 // not been set explicitly.
186 const SkScalar kUnderlineMetricsNotSet = -1.0f; 166 const SkScalar kUnderlineMetricsNotSet = -1.0f;
187 167
188 SkiaTextRenderer::SkiaTextRenderer(Canvas* canvas) 168 SkiaTextRenderer::SkiaTextRenderer(Canvas* canvas)
189 : canvas_skia_(canvas->sk_canvas()), 169 : canvas_skia_(canvas->sk_canvas()),
190 started_drawing_(false), 170 started_drawing_(false),
191 underline_thickness_(kUnderlineMetricsNotSet), 171 underline_thickness_(kUnderlineMetricsNotSet),
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
286 deferred_fade_shader_ = paint_.getShader(); 266 deferred_fade_shader_ = paint_.getShader();
287 paint_.setShader(NULL); 267 paint_.setShader(NULL);
288 canvas_skia_->saveLayer(&bounds_, NULL); 268 canvas_skia_->saveLayer(&bounds_, NULL);
289 } 269 }
290 } 270 }
291 271
292 const size_t byte_length = glyph_count * sizeof(glyphs[0]); 272 const size_t byte_length = glyph_count * sizeof(glyphs[0]);
293 canvas_skia_->drawPosText(&glyphs[0], byte_length, &pos[0], paint_); 273 canvas_skia_->drawPosText(&glyphs[0], byte_length, &pos[0], paint_);
294 } 274 }
295 275
296 // Draw underline and strike through text decorations. 276 void SkiaTextRenderer::DrawDecorations(int x, int y, int width, bool underline,
297 // Based on |SkCanvas::DrawTextDecorations()| and constants from: 277 bool strike, bool diagonal_strike) {
298 // third_party/skia/src/core/SkTextFormatParams.h 278 if (underline)
299 void SkiaTextRenderer::DrawDecorations(int x, int y, int width, 279 DrawUnderline(x, y, width);
300 const StyleRange& style) { 280 if (strike)
301 if (!style.underline && !style.strike && !style.diagonal_strike) 281 DrawStrike(x, y, width);
302 return; 282 if (diagonal_strike)
283 DrawDiagonalStrike(x, y, width);
284 }
303 285
304 // Fraction of the text size to lower a strike through below the baseline. 286 void SkiaTextRenderer::DrawUnderline(int x, int y, int width) {
305 const SkScalar kStrikeThroughOffset = (-SK_Scalar1 * 6 / 21); 287 SkRect r = SkRect::MakeLTRB(x, y + underline_position_, x + width,
306 // Fraction of the text size to lower an underline below the baseline. 288 y + underline_position_ + underline_thickness_);
307 const SkScalar kUnderlineOffset = (SK_Scalar1 / 9); 289 if (underline_thickness_ == kUnderlineMetricsNotSet) {
308 // Fraction of the text size to use for a strike through or under-line. 290 const SkScalar text_size = paint_.getTextSize();
309 const SkScalar kLineThickness = (SK_Scalar1 / 18); 291 r.fTop = SkScalarMulAdd(text_size, kUnderlineOffset, y);
310 // Fraction of the text size to use for a top margin of a diagonal strike. 292 r.fBottom = r.fTop + SkScalarMul(text_size, kLineThickness);
311 const SkScalar kDiagonalStrikeThroughMarginOffset = (SK_Scalar1 / 4); 293 }
294 canvas_skia_->drawRect(r, paint_);
295 }
312 296
313 SkScalar text_size = paint_.getTextSize(); 297 void SkiaTextRenderer::DrawStrike(int x, int y, int width) const {
314 SkScalar height = SkScalarMul(text_size, kLineThickness); 298 const SkScalar text_size = paint_.getTextSize();
315 SkRect r; 299 const SkScalar height = SkScalarMul(text_size, kLineThickness);
300 const SkScalar offset = SkScalarMulAdd(text_size, kStrikeThroughOffset, y);
301 const SkRect r = SkRect::MakeLTRB(x, offset, x + width, offset + height);
302 canvas_skia_->drawRect(r, paint_);
303 }
316 304
317 r.fLeft = x; 305 void SkiaTextRenderer::DrawDiagonalStrike(int x, int y, int width) const {
318 r.fRight = x + width; 306 const SkScalar text_size = paint_.getTextSize();
307 const SkScalar offset = SkScalarMul(text_size, kDiagonalStrikeMarginOffset);
319 308
320 if (style.underline) { 309 SkPaint paint(paint_);
321 if (underline_thickness_ == kUnderlineMetricsNotSet) { 310 paint.setAntiAlias(true);
322 r.fTop = SkScalarMulAdd(text_size, kUnderlineOffset, y); 311 paint.setStyle(SkPaint::kFill_Style);
323 r.fBottom = r.fTop + height; 312 paint.setStrokeWidth(SkScalarMul(text_size, kLineThickness) * 2);
324 } else { 313 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 } 314 }
349 315
350 } // namespace internal 316 } // namespace internal
351 317
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() { 318 RenderText::~RenderText() {
362 } 319 }
363 320
364 void RenderText::SetText(const string16& text) { 321 void RenderText::SetText(const string16& text) {
365 DCHECK(!composition_range_.IsValid()); 322 DCHECK(!composition_range_.IsValid());
366 size_t old_text_length = text_.length(); 323 const size_t old_length = text_.length();
367 text_ = text; 324 text_ = text;
368 325
369 // Update the style ranges as needed. 326 // Adjust any ranged styles and colors to accomodate a new text size.
Alexei Svitkine (slow) 2013/01/23 16:52:54 Nit: "text size" -> "text length" in comment, to a
msw 2013/01/25 09:10:02 Done.
370 if (text_.empty()) { 327 const size_t new_length = text_.length();
371 style_ranges_.clear(); 328 if (new_length < old_length) {
372 } else if (style_ranges_.empty()) { 329 ResizeBreaks(&colors_, new_length);
373 ApplyDefaultStyle(); 330 for (size_t style = 0; style < NUM_TEXT_STYLES; ++style)
374 } else if (text_.length() > old_text_length) { 331 ResizeBreaks(&styles_[style], 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 } 332 }
390 #ifndef NDEBUG
391 CheckStyleRanges(style_ranges_, text_.length());
392 #endif
393 cached_bounds_and_offset_valid_ = false; 333 cached_bounds_and_offset_valid_ = false;
394 334
395 // Reset selection model. SetText should always followed by SetSelectionModel 335 // Reset selection model. SetText should always followed by SetSelectionModel
396 // or SetCursorPosition in upper layer. 336 // or SetCursorPosition in upper layer.
397 SetSelectionModel(SelectionModel()); 337 SetSelectionModel(SelectionModel());
398 338
399 // Invalidate the cached text direction if it depends on the text contents. 339 // Invalidate the cached text direction if it depends on the text contents.
400 if (directionality_mode_ == DIRECTIONALITY_FROM_TEXT) 340 if (directionality_mode_ == DIRECTIONALITY_FROM_TEXT)
401 text_direction_ = base::i18n::UNKNOWN_DIRECTION; 341 text_direction_ = base::i18n::UNKNOWN_DIRECTION;
402 342
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after
575 } 515 }
576 516
577 void RenderText::SetCompositionRange(const ui::Range& composition_range) { 517 void RenderText::SetCompositionRange(const ui::Range& composition_range) {
578 CHECK(!composition_range.IsValid() || 518 CHECK(!composition_range.IsValid() ||
579 ui::Range(0, text_.length()).Contains(composition_range)); 519 ui::Range(0, text_.length()).Contains(composition_range));
580 composition_range_.set_end(composition_range.end()); 520 composition_range_.set_end(composition_range.end());
581 composition_range_.set_start(composition_range.start()); 521 composition_range_.set_start(composition_range.start());
582 ResetLayout(); 522 ResetLayout();
583 } 523 }
584 524
585 void RenderText::ApplyStyleRange(const StyleRange& style_range) { 525 void RenderText::SetColor(SkColor value) {
586 const ui::Range& new_range = style_range.range; 526 ApplyColor(value, ui::Range(0, text().length()));
587 if (!new_range.IsValid() || new_range.is_empty()) 527 }
588 return; 528
589 CHECK(!new_range.is_reversed()); 529 void RenderText::ApplyColor(SkColor value, const ui::Range& range) {
590 CHECK(ui::Range(0, text_.length()).Contains(new_range)); 530 ApplyBreaks(value, range, &colors_);
591 ApplyStyleRangeImpl(&style_ranges_, style_range); 531
592 #ifndef NDEBUG 532 #if defined(OS_WIN)
593 CheckStyleRanges(style_ranges_, text_.length()); 533 // TODO(msw): Windows applies colors and decorations in the layout process.
594 #endif
595 // TODO(xji): only invalidate if font or underline changes.
596 cached_bounds_and_offset_valid_ = false; 534 cached_bounds_and_offset_valid_ = false;
597 ResetLayout(); 535 ResetLayout();
536 #endif
598 } 537 }
599 538
600 void RenderText::ApplyDefaultStyle() { 539 void RenderText::SetStyle(TextStyle style, bool value) {
601 style_ranges_.clear(); 540 ApplyStyle(style, value, ui::Range(0, text().length()));
602 StyleRange style = StyleRange(default_style_); 541 }
603 style.range.set_end(text_.length()); 542
604 style_ranges_.push_back(style); 543 void RenderText::ApplyStyle(TextStyle style,
605 cached_bounds_and_offset_valid_ = false; 544 bool value,
606 ResetLayout(); 545 const ui::Range& range) {
546 ApplyBreaks(value, range, &styles_[style]);
547
548 // Only invalidate the layout on font changes; not for colors or decorations.
549 bool invalidate = (style == BOLD) || (style == UNDERLINE);
550 #if defined(OS_WIN)
551 // TODO(msw): Windows applies colors and decorations in the layout process.
552 invalidate = true;
553 #endif
554 if (invalidate) {
555 cached_bounds_and_offset_valid_ = false;
556 ResetLayout();
557 }
607 } 558 }
608 559
609 void RenderText::SetDirectionalityMode(DirectionalityMode mode) { 560 void RenderText::SetDirectionalityMode(DirectionalityMode mode) {
610 if (mode == directionality_mode_) 561 if (mode == directionality_mode_)
611 return; 562 return;
612 563
613 directionality_mode_ = mode; 564 directionality_mode_ = mode;
614 text_direction_ = base::i18n::UNKNOWN_DIRECTION; 565 text_direction_ = base::i18n::UNKNOWN_DIRECTION;
615 ResetLayout(); 566 ResetLayout();
616 } 567 }
(...skipping 27 matching lines...) Expand all
644 595
645 VisualCursorDirection RenderText::GetVisualDirectionOfLogicalEnd() { 596 VisualCursorDirection RenderText::GetVisualDirectionOfLogicalEnd() {
646 return GetTextDirection() == base::i18n::LEFT_TO_RIGHT ? 597 return GetTextDirection() == base::i18n::LEFT_TO_RIGHT ?
647 CURSOR_RIGHT : CURSOR_LEFT; 598 CURSOR_RIGHT : CURSOR_LEFT;
648 } 599 }
649 600
650 void RenderText::Draw(Canvas* canvas) { 601 void RenderText::Draw(Canvas* canvas) {
651 EnsureLayout(); 602 EnsureLayout();
652 603
653 if (clip_to_display_rect()) { 604 if (clip_to_display_rect()) {
654 gfx::Rect clip_rect(display_rect()); 605 Rect clip_rect(display_rect());
655 clip_rect.Inset(ShadowValue::GetMargin(text_shadows_)); 606 clip_rect.Inset(ShadowValue::GetMargin(text_shadows_));
656 607
657 canvas->Save(); 608 canvas->Save();
658 canvas->ClipRect(clip_rect); 609 canvas->ClipRect(clip_rect);
659 } 610 }
660 611
661 if (!text().empty()) 612 if (!text().empty())
662 DrawSelection(canvas); 613 DrawSelection(canvas);
663 614
664 DrawCursor(canvas); 615 DrawCursor(canvas);
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
745 text_shadows_ = shadows; 696 text_shadows_ = shadows;
746 } 697 }
747 698
748 RenderText::RenderText() 699 RenderText::RenderText()
749 : horizontal_alignment_(base::i18n::IsRTL() ? ALIGN_RIGHT : ALIGN_LEFT), 700 : horizontal_alignment_(base::i18n::IsRTL() ? ALIGN_RIGHT : ALIGN_LEFT),
750 directionality_mode_(DIRECTIONALITY_FROM_TEXT), 701 directionality_mode_(DIRECTIONALITY_FROM_TEXT),
751 text_direction_(base::i18n::UNKNOWN_DIRECTION), 702 text_direction_(base::i18n::UNKNOWN_DIRECTION),
752 cursor_enabled_(true), 703 cursor_enabled_(true),
753 cursor_visible_(false), 704 cursor_visible_(false),
754 insert_mode_(true), 705 insert_mode_(true),
755 cursor_color_(kDefaultCursorColor), 706 cursor_color_(kDefaultColor),
756 selection_color_(kDefaultSelectionColor), 707 selection_color_(kDefaultColor),
757 selection_background_focused_color_(kDefaultSelectionBackgroundColor), 708 selection_background_focused_color_(kDefaultSelectionBackgroundColor),
758 selection_background_unfocused_color_(kDefaultSelectionBackgroundColor), 709 selection_background_unfocused_color_(kDefaultSelectionBackgroundColor),
759 focused_(false), 710 focused_(false),
760 composition_range_(ui::Range::InvalidRange()), 711 composition_range_(ui::Range::InvalidRange()),
761 obscured_(false), 712 obscured_(false),
762 fade_head_(false), 713 fade_head_(false),
763 fade_tail_(false), 714 fade_tail_(false),
764 background_is_transparent_(false), 715 background_is_transparent_(false),
765 clip_to_display_rect_(true), 716 clip_to_display_rect_(true),
766 cached_bounds_and_offset_valid_(false) { 717 cached_bounds_and_offset_valid_(false) {
718 colors_.push_back(ColorBreak(0, kDefaultColor));
719 for (size_t style = 0; style < NUM_TEXT_STYLES; ++style)
720 styles_[style].push_back(StyleBreak(0, false));
767 } 721 }
768 722
769 const Vector2d& RenderText::GetUpdatedDisplayOffset() { 723 const Vector2d& RenderText::GetUpdatedDisplayOffset() {
770 UpdateCachedBoundsAndOffset(); 724 UpdateCachedBoundsAndOffset();
771 return display_offset_; 725 return display_offset_;
772 } 726 }
773 727
774 SelectionModel RenderText::GetAdjacentSelectionModel( 728 SelectionModel RenderText::GetAdjacentSelectionModel(
775 const SelectionModel& current, 729 const SelectionModel& current,
776 BreakType break_type, 730 BreakType break_type,
(...skipping 18 matching lines...) Expand all
795 void RenderText::SetSelectionModel(const SelectionModel& model) { 749 void RenderText::SetSelectionModel(const SelectionModel& model) {
796 DCHECK_LE(model.selection().GetMax(), text().length()); 750 DCHECK_LE(model.selection().GetMax(), text().length());
797 selection_model_ = model; 751 selection_model_ = model;
798 cached_bounds_and_offset_valid_ = false; 752 cached_bounds_and_offset_valid_ = false;
799 } 753 }
800 754
801 const string16& RenderText::GetLayoutText() const { 755 const string16& RenderText::GetLayoutText() const {
802 return obscured() ? obscured_text_ : text(); 756 return obscured() ? obscured_text_ : text();
803 } 757 }
804 758
805 void RenderText::ApplyCompositionAndSelectionStyles( 759 void RenderText::ApplyCompositionStyle(std::vector<StyleBreak>* underlines) {
806 StyleRanges* style_ranges) { 760 // Apply an underline to the composition range in |underlines|.
807 // TODO(msw): This pattern ought to be reconsidered; what about composition 761 if (composition_range_.IsValid() && !composition_range_.is_empty())
808 // and selection overlaps, retain existing local style features? 762 ApplyBreaks(true, composition_range_, underlines);
809 // Apply a composition style override to a copy of the style ranges. 763 }
810 if (composition_range_.IsValid() && !composition_range_.is_empty()) { 764
811 StyleRange composition_style(default_style_); 765 void RenderText::ApplySelectionColor(std::vector<ColorBreak>* colors) {
812 composition_style.underline = true; 766 // Apply the selected text color to the selection range in |colors|.
813 composition_style.range = composition_range_;
814 ApplyStyleRangeImpl(style_ranges, composition_style);
815 }
816 // Apply a selection style override to a copy of the style ranges.
817 if (!selection().is_empty()) { 767 if (!selection().is_empty()) {
818 StyleRange selection_style(default_style_); 768 const ui::Range range(selection().GetMin(), selection().GetMax());
819 selection_style.foreground = selection_color_; 769 ApplyBreaks(selection_color_, range, colors);
820 selection_style.range = ui::Range(selection().GetMin(),
821 selection().GetMax());
822 ApplyStyleRangeImpl(style_ranges, selection_style);
823 } 770 }
824 // Apply replacement-mode style override to a copy of the style ranges. 771 // Apply replacement-mode style override to a copy of the style ranges.
825 // 772 //
826 // TODO(xji): NEED TO FIX FOR WINDOWS ASAP. Windows call this function (to 773 // TODO(xji|msw): RenderTextWin calls this function in ItemizeLogicalText(),
827 // apply styles) in ItemizeLogicalText(). In order for the cursor's underline 774 // but the character at the cursor changes color on every cursor blink in
828 // character to be drawn correctly, we will need to re-layout the text. It's 775 // overtype mode. RenderTextWin should apply colors in DrawVisualText(), as
829 // not practical to do layout on every cursor blink. We need to fix Windows 776 // done by RenderTextLinux; see http://crbug.com/110109
830 // port to apply styles during drawing phase like Linux port does.
831 // http://crbug.com/110109
832 if (!insert_mode_ && cursor_visible() && focused()) { 777 if (!insert_mode_ && cursor_visible() && focused()) {
833 StyleRange replacement_mode_style(default_style_); 778 const size_t cursor = cursor_position();
834 replacement_mode_style.foreground = selection_color_; 779 const size_t next = IndexOfAdjacentGrapheme(cursor, CURSOR_FORWARD);
835 size_t cursor = cursor_position(); 780 ApplyBreaks(selection_color_, ui::Range(cursor, next), colors);
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 } 781 }
841 } 782 }
842 783
843 Vector2d RenderText::GetTextOffset() { 784 Vector2d RenderText::GetTextOffset() {
844 Vector2d offset = display_rect().OffsetFromOrigin(); 785 Vector2d offset = display_rect().OffsetFromOrigin();
845 offset.Add(GetUpdatedDisplayOffset()); 786 offset.Add(GetUpdatedDisplayOffset());
846 offset.Add(GetAlignmentOffset()); 787 offset.Add(GetAlignmentOffset());
847 return offset; 788 return offset;
848 } 789 }
849 790
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
892 833
893 bool fade_left = fade_head(); 834 bool fade_left = fade_head();
894 bool fade_right = fade_tail(); 835 bool fade_right = fade_tail();
895 // Under RTL, |fade_right| == |fade_head|. 836 // Under RTL, |fade_right| == |fade_head|.
896 // TODO(asvitkine): This is currently not based on GetTextDirection() because 837 // TODO(asvitkine): This is currently not based on GetTextDirection() because
897 // RenderTextWin does not return a direction that's based on 838 // RenderTextWin does not return a direction that's based on
898 // the text content. 839 // the text content.
899 if (horizontal_alignment() == ALIGN_RIGHT) 840 if (horizontal_alignment() == ALIGN_RIGHT)
900 std::swap(fade_left, fade_right); 841 std::swap(fade_left, fade_right);
901 842
902 gfx::Rect solid_part = display_rect(); 843 Rect solid_part = display_rect();
903 gfx::Rect left_part; 844 Rect left_part;
904 gfx::Rect right_part; 845 Rect right_part;
905 if (fade_left) { 846 if (fade_left) {
906 left_part = solid_part; 847 left_part = solid_part;
907 left_part.Inset(0, 0, solid_part.width() - gradient_width, 0); 848 left_part.Inset(0, 0, solid_part.width() - gradient_width, 0);
908 solid_part.Inset(gradient_width, 0, 0, 0); 849 solid_part.Inset(gradient_width, 0, 0, 0);
909 } 850 }
910 if (fade_right) { 851 if (fade_right) {
911 right_part = solid_part; 852 right_part = solid_part;
912 right_part.Inset(solid_part.width() - gradient_width, 0, 0, 0); 853 right_part.Inset(solid_part.width() - gradient_width, 0, 0, 0);
913 solid_part.Inset(0, 0, gradient_width, 0); 854 solid_part.Inset(0, 0, gradient_width, 0);
914 } 855 }
915 856
916 gfx::Rect text_rect = display_rect(); 857 Rect text_rect = display_rect();
917 text_rect.Inset(GetAlignmentOffset().x(), 0, 0, 0); 858 text_rect.Inset(GetAlignmentOffset().x(), 0, 0, 0);
918 859
919 const SkColor color = default_style().foreground; 860 skia::RefPtr<SkShader> shader = CreateFadeShader(
920 skia::RefPtr<SkShader> shader = 861 text_rect, left_part, right_part, colors_.front().second);
921 CreateFadeShader(text_rect, left_part, right_part, color);
922 if (shader) 862 if (shader)
923 renderer->SetShader(shader.get(), display_rect()); 863 renderer->SetShader(shader.get(), display_rect());
924 } 864 }
925 865
926 void RenderText::ApplyTextShadows(internal::SkiaTextRenderer* renderer) { 866 void RenderText::ApplyTextShadows(internal::SkiaTextRenderer* renderer) {
927 skia::RefPtr<SkDrawLooper> looper = 867 skia::RefPtr<SkDrawLooper> looper = CreateShadowDrawLooper(text_shadows_);
928 gfx::CreateShadowDrawLooper(text_shadows_);
929 renderer->SetDrawLooper(looper.get()); 868 renderer->SetDrawLooper(looper.get());
930 } 869 }
931 870
932 // static 871 // static
933 bool RenderText::RangeContainsCaret(const ui::Range& range, 872 bool RenderText::RangeContainsCaret(const ui::Range& range,
934 size_t caret_pos, 873 size_t caret_pos,
935 LogicalCursorDirection caret_affinity) { 874 LogicalCursorDirection caret_affinity) {
936 // NB: exploits unsigned wraparound (WG14/N1124 section 6.2.5 paragraph 9). 875 // NB: exploits unsigned wraparound (WG14/N1124 section 6.2.5 paragraph 9).
937 size_t adjacent = (caret_affinity == CURSOR_BACKWARD) ? 876 size_t adjacent = (caret_affinity == CURSOR_BACKWARD) ?
938 caret_pos - 1 : caret_pos + 1; 877 caret_pos - 1 : caret_pos + 1;
939 return range.Contains(ui::Range(caret_pos, adjacent)); 878 return range.Contains(ui::Range(caret_pos, adjacent));
940 } 879 }
941 880
881 template <class T>
882 void RenderText::ApplyBreaks(T value,
883 const ui::Range& range,
884 std::vector<std::pair<size_t, T> >* breaks) {
885 if (!range.IsValid() || (range.is_empty() && !text().empty()))
886 return;
887 DCHECK(!breaks->empty());
888 DCHECK(!range.is_reversed());
889 DCHECK(ui::Range(0, text_.length()).Contains(range));
890
891 // Erase any breaks in |range|, then add start and end breaks as needed.
892 typename std::vector<std::pair<size_t, T> >::iterator start =
893 GetBreak(breaks, range.start());
894 start += start->first < range.start() ? 1 : 0;
895 typename std::vector<std::pair<size_t, T> >::iterator end =
896 GetBreak(breaks, range.end());
897 T trailing_value = text().empty() ? value : end->second;
898 typename std::vector<std::pair<size_t, T> >::iterator i =
899 start == breaks->end() ? start : breaks->erase(start, end + 1);
900 if (range.start() == 0 || (i - 1)->second != value)
901 i = breaks->insert(i, std::pair<size_t, T>(range.start(), value)) + 1;
902 if (trailing_value != value && range.end() != text_.length())
903 breaks->insert(i, std::pair<size_t, T>(range.end(), trailing_value));
904
905 #ifndef NDEBUG
906 CheckBreaks(*breaks, text_.length());
907 #endif
908 }
909
942 void RenderText::MoveCursorTo(size_t position, bool select) { 910 void RenderText::MoveCursorTo(size_t position, bool select) {
943 size_t cursor = std::min(position, text().length()); 911 size_t cursor = std::min(position, text().length());
944 if (IsCursorablePosition(cursor)) 912 if (IsCursorablePosition(cursor))
945 SetSelectionModel(SelectionModel( 913 SetSelectionModel(SelectionModel(
946 ui::Range(select ? selection().start() : cursor, cursor), 914 ui::Range(select ? selection().start() : cursor, cursor),
947 (cursor == 0) ? CURSOR_FORWARD : CURSOR_BACKWARD)); 915 (cursor == 0) ? CURSOR_FORWARD : CURSOR_BACKWARD));
948 } 916 }
949 917
950 void RenderText::UpdateObscuredText() { 918 void RenderText::UpdateObscuredText() {
951 if (!obscured_) 919 if (!obscured_)
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
992 } else if (display_offset_.x() != 0) { 960 } else if (display_offset_.x() != 0) {
993 // Reduce the pan offset to show additional overflow text when the display 961 // Reduce the pan offset to show additional overflow text when the display
994 // width increases. 962 // width increases.
995 const int negate_rtl = horizontal_alignment_ == ALIGN_RIGHT ? -1 : 1; 963 const int negate_rtl = horizontal_alignment_ == ALIGN_RIGHT ? -1 : 1;
996 const int offset = negate_rtl * display_offset_.x(); 964 const int offset = negate_rtl * display_offset_.x();
997 if (display_width > (content_width + offset)) { 965 if (display_width > (content_width + offset)) {
998 delta_x = negate_rtl * (display_width - (content_width + offset)); 966 delta_x = negate_rtl * (display_width - (content_width + offset));
999 } 967 }
1000 } 968 }
1001 969
1002 gfx::Vector2d delta_offset(delta_x, 0); 970 Vector2d delta_offset(delta_x, 0);
1003 display_offset_ += delta_offset; 971 display_offset_ += delta_offset;
1004 cursor_bounds_ += delta_offset; 972 cursor_bounds_ += delta_offset;
1005 } 973 }
1006 974
1007 void RenderText::DrawSelection(Canvas* canvas) { 975 void RenderText::DrawSelection(Canvas* canvas) {
1008 const SkColor color = focused() ? selection_background_focused_color_ : 976 const SkColor color = focused() ? selection_background_focused_color_ :
1009 selection_background_unfocused_color_; 977 selection_background_unfocused_color_;
1010 const std::vector<Rect> sel = GetSubstringBounds(selection()); 978 const std::vector<Rect> sel = GetSubstringBounds(selection());
1011 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) 979 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i)
1012 canvas->FillRect(*i, color); 980 canvas->FillRect(*i, color);
1013 } 981 }
1014 982
1015 void RenderText::DrawCursor(Canvas* canvas) { 983 void RenderText::DrawCursor(Canvas* canvas) {
1016 // Paint cursor. Replace cursor is drawn as rectangle for now. 984 // Paint cursor. Replace cursor is drawn as rectangle for now.
1017 // TODO(msw): Draw a better cursor with a better indication of association. 985 // TODO(msw): Draw a better cursor with a better indication of association.
1018 if (cursor_enabled() && cursor_visible() && focused()) { 986 if (cursor_enabled() && cursor_visible() && focused()) {
1019 const Rect& bounds = GetUpdatedCursorBounds(); 987 const Rect& bounds = GetUpdatedCursorBounds();
1020 DCHECK(bounds.width()); 988 DCHECK(bounds.width());
1021 canvas->FillRect(bounds, cursor_color_); 989 canvas->FillRect(bounds, cursor_color_);
1022 } 990 }
1023 } 991 }
1024 992
1025 } // namespace gfx 993 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698