OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |