Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / ui / gfx / render_text.cc
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
3 // found in the LICENSE file.
4
5 #include "ui/gfx/render_text.h"
6
7 #include <algorithm>
8 #include <climits>
9
10 #include "base/i18n/break_iterator.h"
11 #include "base/logging.h"
12 #include "base/stl_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "third_party/icu/source/common/unicode/rbbi.h"
15 #include "third_party/icu/source/common/unicode/utf16.h"
16 #include "third_party/skia/include/core/SkTypeface.h"
17 #include "third_party/skia/include/effects/SkGradientShader.h"
18 #include "ui/gfx/canvas.h"
19 #include "ui/gfx/insets.h"
20 #include "ui/gfx/skia_util.h"
21 #include "ui/gfx/text_constants.h"
22 #include "ui/gfx/text_elider.h"
23 #include "ui/gfx/text_utils.h"
24 #include "ui/gfx/utf16_indexing.h"
25
26 namespace gfx {
27
28 namespace {
29
30 // All chars are replaced by this char when the password style is set.
31 // TODO(benrg): GTK uses the first of U+25CF, U+2022, U+2731, U+273A, '*'
32 // that's available in the font (find_invisible_char() in gtkentry.c).
33 const base::char16 kPasswordReplacementChar = '*';
34
35 // Default color used for the text and cursor.
36 const SkColor kDefaultColor = SK_ColorBLACK;
37
38 // Default color used for drawing selection background.
39 const SkColor kDefaultSelectionBackgroundColor = SK_ColorGRAY;
40
41 // Fraction of the text size to lower a strike through below the baseline.
42 const SkScalar kStrikeThroughOffset = (-SK_Scalar1 * 6 / 21);
43 // Fraction of the text size to lower an underline below the baseline.
44 const SkScalar kUnderlineOffset = (SK_Scalar1 / 9);
45 // Fraction of the text size to use for a strike through or under-line.
46 const SkScalar kLineThickness = (SK_Scalar1 / 18);
47 // Fraction of the text size to use for a top margin of a diagonal strike.
48 const SkScalar kDiagonalStrikeMarginOffset = (SK_Scalar1 / 4);
49
50 // Invalid value of baseline.  Assigning this value to |baseline_| causes
51 // re-calculation of baseline.
52 const int kInvalidBaseline = INT_MAX;
53
54 // Returns the baseline, with which the text best appears vertically centered.
55 int DetermineBaselineCenteringText(const Rect& display_rect,
56                                    const FontList& font_list) {
57   const int display_height = display_rect.height();
58   const int font_height = font_list.GetHeight();
59   // Lower and upper bound of baseline shift as we try to show as much area of
60   // text as possible.  In particular case of |display_height| == |font_height|,
61   // we do not want to shift the baseline.
62   const int min_shift = std::min(0, display_height - font_height);
63   const int max_shift = std::abs(display_height - font_height);
64   const int baseline = font_list.GetBaseline();
65   const int cap_height = font_list.GetCapHeight();
66   const int internal_leading = baseline - cap_height;
67   // Some platforms don't support getting the cap height, and simply return
68   // the entire font ascent from GetCapHeight().  Centering the ascent makes
69   // the font look too low, so if GetCapHeight() returns the ascent, center
70   // the entire font height instead.
71   const int space =
72       display_height - ((internal_leading != 0) ? cap_height : font_height);
73   const int baseline_shift = space / 2 - internal_leading;
74   return baseline + std::max(min_shift, std::min(max_shift, baseline_shift));
75 }
76
77 // Converts |gfx::Font::FontStyle| flags to |SkTypeface::Style| flags.
78 SkTypeface::Style ConvertFontStyleToSkiaTypefaceStyle(int font_style) {
79   int skia_style = SkTypeface::kNormal;
80   skia_style |= (font_style & gfx::Font::BOLD) ? SkTypeface::kBold : 0;
81   skia_style |= (font_style & gfx::Font::ITALIC) ? SkTypeface::kItalic : 0;
82   return static_cast<SkTypeface::Style>(skia_style);
83 }
84
85 // Given |font| and |display_width|, returns the width of the fade gradient.
86 int CalculateFadeGradientWidth(const FontList& font_list, int display_width) {
87   // Fade in/out about 2.5 characters of the beginning/end of the string.
88   // The .5 here is helpful if one of the characters is a space.
89   // Use a quarter of the display width if the display width is very short.
90   const int average_character_width = font_list.GetExpectedTextWidth(1);
91   const double gradient_width = std::min(average_character_width * 2.5,
92                                          display_width / 4.0);
93   DCHECK_GE(gradient_width, 0.0);
94   return static_cast<int>(floor(gradient_width + 0.5));
95 }
96
97 // Appends to |positions| and |colors| values corresponding to the fade over
98 // |fade_rect| from color |c0| to color |c1|.
99 void AddFadeEffect(const Rect& text_rect,
100                    const Rect& fade_rect,
101                    SkColor c0,
102                    SkColor c1,
103                    std::vector<SkScalar>* positions,
104                    std::vector<SkColor>* colors) {
105   const SkScalar left = static_cast<SkScalar>(fade_rect.x() - text_rect.x());
106   const SkScalar width = static_cast<SkScalar>(fade_rect.width());
107   const SkScalar p0 = left / text_rect.width();
108   const SkScalar p1 = (left + width) / text_rect.width();
109   // Prepend 0.0 to |positions|, as required by Skia.
110   if (positions->empty() && p0 != 0.0) {
111     positions->push_back(0.0);
112     colors->push_back(c0);
113   }
114   positions->push_back(p0);
115   colors->push_back(c0);
116   positions->push_back(p1);
117   colors->push_back(c1);
118 }
119
120 // Creates a SkShader to fade the text, with |left_part| specifying the left
121 // fade effect, if any, and |right_part| specifying the right fade effect.
122 skia::RefPtr<SkShader> CreateFadeShader(const Rect& text_rect,
123                                         const Rect& left_part,
124                                         const Rect& right_part,
125                                         SkColor color) {
126   // Fade alpha of 51/255 corresponds to a fade of 0.2 of the original color.
127   const SkColor fade_color = SkColorSetA(color, 51);
128   std::vector<SkScalar> positions;
129   std::vector<SkColor> colors;
130
131   if (!left_part.IsEmpty())
132     AddFadeEffect(text_rect, left_part, fade_color, color,
133                   &positions, &colors);
134   if (!right_part.IsEmpty())
135     AddFadeEffect(text_rect, right_part, color, fade_color,
136                   &positions, &colors);
137   DCHECK(!positions.empty());
138
139   // Terminate |positions| with 1.0, as required by Skia.
140   if (positions.back() != 1.0) {
141     positions.push_back(1.0);
142     colors.push_back(colors.back());
143   }
144
145   SkPoint points[2];
146   points[0].iset(text_rect.x(), text_rect.y());
147   points[1].iset(text_rect.right(), text_rect.y());
148
149   return skia::AdoptRef(
150       SkGradientShader::CreateLinear(&points[0], &colors[0], &positions[0],
151                                      colors.size(), SkShader::kClamp_TileMode));
152 }
153
154 }  // namespace
155
156 namespace internal {
157
158 // Value of |underline_thickness_| that indicates that underline metrics have
159 // not been set explicitly.
160 const SkScalar kUnderlineMetricsNotSet = -1.0f;
161
162 SkiaTextRenderer::SkiaTextRenderer(Canvas* canvas)
163     : canvas_skia_(canvas->sk_canvas()),
164       started_drawing_(false),
165       underline_thickness_(kUnderlineMetricsNotSet),
166       underline_position_(0.0f) {
167   DCHECK(canvas_skia_);
168   paint_.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
169   paint_.setStyle(SkPaint::kFill_Style);
170   paint_.setAntiAlias(true);
171   paint_.setSubpixelText(true);
172   paint_.setLCDRenderText(true);
173   paint_.setHinting(SkPaint::kNormal_Hinting);
174   bounds_.setEmpty();
175 }
176
177 SkiaTextRenderer::~SkiaTextRenderer() {
178   // Work-around for http://crbug.com/122743, where non-ClearType text is
179   // rendered with incorrect gamma when using the fade shader. Draw the text
180   // to a layer and restore it faded by drawing a rect in kDstIn_Mode mode.
181   //
182   // TODO(asvitkine): Remove this work-around once the Skia bug is fixed.
183   //                  http://code.google.com/p/skia/issues/detail?id=590
184   if (deferred_fade_shader_.get()) {
185     paint_.setShader(deferred_fade_shader_.get());
186     paint_.setXfermodeMode(SkXfermode::kDstIn_Mode);
187     canvas_skia_->drawRect(bounds_, paint_);
188     canvas_skia_->restore();
189   }
190 }
191
192 void SkiaTextRenderer::SetDrawLooper(SkDrawLooper* draw_looper) {
193   paint_.setLooper(draw_looper);
194 }
195
196 void SkiaTextRenderer::SetFontSmoothingSettings(bool antialiasing,
197                                                 bool subpixel_rendering,
198                                                 bool subpixel_positioning) {
199   paint_.setAntiAlias(antialiasing);
200   paint_.setLCDRenderText(subpixel_rendering);
201   paint_.setSubpixelText(subpixel_positioning);
202 }
203
204 void SkiaTextRenderer::SetFontHinting(SkPaint::Hinting hinting) {
205   paint_.setHinting(hinting);
206 }
207
208 void SkiaTextRenderer::SetTypeface(SkTypeface* typeface) {
209   paint_.setTypeface(typeface);
210 }
211
212 void SkiaTextRenderer::SetTextSize(SkScalar size) {
213   paint_.setTextSize(size);
214 }
215
216 void SkiaTextRenderer::SetFontFamilyWithStyle(const std::string& family,
217                                               int style) {
218   DCHECK(!family.empty());
219
220   SkTypeface::Style skia_style = ConvertFontStyleToSkiaTypefaceStyle(style);
221   skia::RefPtr<SkTypeface> typeface =
222       skia::AdoptRef(SkTypeface::CreateFromName(family.c_str(), skia_style));
223   if (typeface) {
224     // |paint_| adds its own ref. So don't |release()| it from the ref ptr here.
225     SetTypeface(typeface.get());
226
227     // Enable fake bold text if bold style is needed but new typeface does not
228     // have it.
229     paint_.setFakeBoldText((skia_style & SkTypeface::kBold) &&
230                            !typeface->isBold());
231   }
232 }
233
234 void SkiaTextRenderer::SetForegroundColor(SkColor foreground) {
235   paint_.setColor(foreground);
236 }
237
238 void SkiaTextRenderer::SetShader(SkShader* shader, const Rect& bounds) {
239   bounds_ = RectToSkRect(bounds);
240   paint_.setShader(shader);
241 }
242
243 void SkiaTextRenderer::SetUnderlineMetrics(SkScalar thickness,
244                                            SkScalar position) {
245   underline_thickness_ = thickness;
246   underline_position_ = position;
247 }
248
249 void SkiaTextRenderer::DrawPosText(const SkPoint* pos,
250                                    const uint16* glyphs,
251                                    size_t glyph_count) {
252   if (!started_drawing_) {
253     started_drawing_ = true;
254     // Work-around for http://crbug.com/122743, where non-ClearType text is
255     // rendered with incorrect gamma when using the fade shader. Draw the text
256     // to a layer and restore it faded by drawing a rect in kDstIn_Mode mode.
257     //
258     // Skip this when there is a looper which seems not working well with
259     // deferred paint. Currently a looper is only used for text shadows.
260     //
261     // TODO(asvitkine): Remove this work-around once the Skia bug is fixed.
262     //                  http://code.google.com/p/skia/issues/detail?id=590
263     if (!paint_.isLCDRenderText() &&
264         paint_.getShader() &&
265         !paint_.getLooper()) {
266       deferred_fade_shader_ = skia::SharePtr(paint_.getShader());
267       paint_.setShader(NULL);
268       canvas_skia_->saveLayer(&bounds_, NULL);
269     }
270   }
271
272   const size_t byte_length = glyph_count * sizeof(glyphs[0]);
273   canvas_skia_->drawPosText(&glyphs[0], byte_length, &pos[0], paint_);
274 }
275
276 void SkiaTextRenderer::DrawDecorations(int x, int y, int width, bool underline,
277                                        bool strike, bool diagonal_strike) {
278   if (underline)
279     DrawUnderline(x, y, width);
280   if (strike)
281     DrawStrike(x, y, width);
282   if (diagonal_strike)
283     DrawDiagonalStrike(x, y, width);
284 }
285
286 void SkiaTextRenderer::DrawUnderline(int x, int y, int width) {
287   SkRect r = SkRect::MakeLTRB(x, y + underline_position_, x + width,
288                               y + underline_position_ + underline_thickness_);
289   if (underline_thickness_ == kUnderlineMetricsNotSet) {
290     const SkScalar text_size = paint_.getTextSize();
291     r.fTop = SkScalarMulAdd(text_size, kUnderlineOffset, y);
292     r.fBottom = r.fTop + SkScalarMul(text_size, kLineThickness);
293   }
294   canvas_skia_->drawRect(r, paint_);
295 }
296
297 void SkiaTextRenderer::DrawStrike(int x, int y, int width) const {
298   const SkScalar text_size = paint_.getTextSize();
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 }
304
305 void SkiaTextRenderer::DrawDiagonalStrike(int x, int y, int width) const {
306   const SkScalar text_size = paint_.getTextSize();
307   const SkScalar offset = SkScalarMul(text_size, kDiagonalStrikeMarginOffset);
308
309   SkPaint paint(paint_);
310   paint.setAntiAlias(true);
311   paint.setStyle(SkPaint::kFill_Style);
312   paint.setStrokeWidth(SkScalarMul(text_size, kLineThickness) * 2);
313   canvas_skia_->drawLine(x, y, x + width, y - text_size + offset, paint);
314 }
315
316 StyleIterator::StyleIterator(const BreakList<SkColor>& colors,
317                              const std::vector<BreakList<bool> >& styles)
318     : colors_(colors),
319       styles_(styles) {
320   color_ = colors_.breaks().begin();
321   for (size_t i = 0; i < styles_.size(); ++i)
322     style_.push_back(styles_[i].breaks().begin());
323 }
324
325 StyleIterator::~StyleIterator() {}
326
327 Range StyleIterator::GetRange() const {
328   Range range(colors_.GetRange(color_));
329   for (size_t i = 0; i < NUM_TEXT_STYLES; ++i)
330     range = range.Intersect(styles_[i].GetRange(style_[i]));
331   return range;
332 }
333
334 void StyleIterator::UpdatePosition(size_t position) {
335   color_ = colors_.GetBreak(position);
336   for (size_t i = 0; i < NUM_TEXT_STYLES; ++i)
337     style_[i] = styles_[i].GetBreak(position);
338 }
339
340 LineSegment::LineSegment() : run(0) {}
341
342 LineSegment::~LineSegment() {}
343
344 Line::Line() : preceding_heights(0), baseline(0) {}
345
346 Line::~Line() {}
347
348 }  // namespace internal
349
350 RenderText::~RenderText() {
351 }
352
353 void RenderText::SetText(const base::string16& text) {
354   DCHECK(!composition_range_.IsValid());
355   if (text_ == text)
356     return;
357   text_ = text;
358
359   // Adjust ranged styles and colors to accommodate a new text length.
360   const size_t text_length = text_.length();
361   colors_.SetMax(text_length);
362   for (size_t style = 0; style < NUM_TEXT_STYLES; ++style)
363     styles_[style].SetMax(text_length);
364   cached_bounds_and_offset_valid_ = false;
365
366   // Reset selection model. SetText should always followed by SetSelectionModel
367   // or SetCursorPosition in upper layer.
368   SetSelectionModel(SelectionModel());
369
370   // Invalidate the cached text direction if it depends on the text contents.
371   if (directionality_mode_ == DIRECTIONALITY_FROM_TEXT)
372     text_direction_ = base::i18n::UNKNOWN_DIRECTION;
373
374   obscured_reveal_index_ = -1;
375   UpdateLayoutText();
376 }
377
378 void RenderText::SetHorizontalAlignment(HorizontalAlignment alignment) {
379   if (horizontal_alignment_ != alignment) {
380     horizontal_alignment_ = alignment;
381     display_offset_ = Vector2d();
382     cached_bounds_and_offset_valid_ = false;
383   }
384 }
385
386 void RenderText::SetFontList(const FontList& font_list) {
387   font_list_ = font_list;
388   baseline_ = kInvalidBaseline;
389   cached_bounds_and_offset_valid_ = false;
390   ResetLayout();
391 }
392
393 void RenderText::SetCursorEnabled(bool cursor_enabled) {
394   cursor_enabled_ = cursor_enabled;
395   cached_bounds_and_offset_valid_ = false;
396 }
397
398 void RenderText::ToggleInsertMode() {
399   insert_mode_ = !insert_mode_;
400   cached_bounds_and_offset_valid_ = false;
401 }
402
403 void RenderText::SetObscured(bool obscured) {
404   if (obscured != obscured_) {
405     obscured_ = obscured;
406     obscured_reveal_index_ = -1;
407     cached_bounds_and_offset_valid_ = false;
408     UpdateLayoutText();
409   }
410 }
411
412 void RenderText::SetObscuredRevealIndex(int index) {
413   if (obscured_reveal_index_ == index)
414     return;
415
416   obscured_reveal_index_ = index;
417   cached_bounds_and_offset_valid_ = false;
418   UpdateLayoutText();
419 }
420
421 void RenderText::SetMultiline(bool multiline) {
422   if (multiline != multiline_) {
423     multiline_ = multiline;
424     cached_bounds_and_offset_valid_ = false;
425     lines_.clear();
426   }
427 }
428
429 void RenderText::SetElideBehavior(ElideBehavior elide_behavior) {
430   // TODO(skanuj) : Add a test for triggering layout change.
431   if (elide_behavior_ != elide_behavior) {
432     elide_behavior_ = elide_behavior;
433     UpdateLayoutText();
434   }
435 }
436
437 void RenderText::SetDisplayRect(const Rect& r) {
438   if (r != display_rect_) {
439     display_rect_ = r;
440     baseline_ = kInvalidBaseline;
441     cached_bounds_and_offset_valid_ = false;
442     lines_.clear();
443     if (elide_behavior_ != gfx::NO_ELIDE)
444       UpdateLayoutText();
445   }
446 }
447
448 void RenderText::SetCursorPosition(size_t position) {
449   MoveCursorTo(position, false);
450 }
451
452 void RenderText::MoveCursor(BreakType break_type,
453                             VisualCursorDirection direction,
454                             bool select) {
455   SelectionModel position(cursor_position(), selection_model_.caret_affinity());
456   // Cancelling a selection moves to the edge of the selection.
457   if (break_type != LINE_BREAK && !selection().is_empty() && !select) {
458     SelectionModel selection_start = GetSelectionModelForSelectionStart();
459     int start_x = GetCursorBounds(selection_start, true).x();
460     int cursor_x = GetCursorBounds(position, true).x();
461     // Use the selection start if it is left (when |direction| is CURSOR_LEFT)
462     // or right (when |direction| is CURSOR_RIGHT) of the selection end.
463     if (direction == CURSOR_RIGHT ? start_x > cursor_x : start_x < cursor_x)
464       position = selection_start;
465     // For word breaks, use the nearest word boundary in the appropriate
466     // |direction|.
467     if (break_type == WORD_BREAK)
468       position = GetAdjacentSelectionModel(position, break_type, direction);
469   } else {
470     position = GetAdjacentSelectionModel(position, break_type, direction);
471   }
472   if (select)
473     position.set_selection_start(selection().start());
474   MoveCursorTo(position);
475 }
476
477 bool RenderText::MoveCursorTo(const SelectionModel& model) {
478   // Enforce valid selection model components.
479   size_t text_length = text().length();
480   Range range(std::min(model.selection().start(), text_length),
481               std::min(model.caret_pos(), text_length));
482   // The current model only supports caret positions at valid character indices.
483   if (!IsCursorablePosition(range.start()) ||
484       !IsCursorablePosition(range.end()))
485     return false;
486   SelectionModel sel(range, model.caret_affinity());
487   bool changed = sel != selection_model_;
488   SetSelectionModel(sel);
489   return changed;
490 }
491
492 bool RenderText::MoveCursorTo(const Point& point, bool select) {
493   SelectionModel position = FindCursorPosition(point);
494   if (select)
495     position.set_selection_start(selection().start());
496   return MoveCursorTo(position);
497 }
498
499 bool RenderText::SelectRange(const Range& range) {
500   Range sel(std::min(range.start(), text().length()),
501             std::min(range.end(), text().length()));
502   if (!IsCursorablePosition(sel.start()) || !IsCursorablePosition(sel.end()))
503     return false;
504   LogicalCursorDirection affinity =
505       (sel.is_reversed() || sel.is_empty()) ? CURSOR_FORWARD : CURSOR_BACKWARD;
506   SetSelectionModel(SelectionModel(sel, affinity));
507   return true;
508 }
509
510 bool RenderText::IsPointInSelection(const Point& point) {
511   if (selection().is_empty())
512     return false;
513   SelectionModel cursor = FindCursorPosition(point);
514   return RangeContainsCaret(
515       selection(), cursor.caret_pos(), cursor.caret_affinity());
516 }
517
518 void RenderText::ClearSelection() {
519   SetSelectionModel(SelectionModel(cursor_position(),
520                                    selection_model_.caret_affinity()));
521 }
522
523 void RenderText::SelectAll(bool reversed) {
524   const size_t length = text().length();
525   const Range all = reversed ? Range(length, 0) : Range(0, length);
526   const bool success = SelectRange(all);
527   DCHECK(success);
528 }
529
530 void RenderText::SelectWord() {
531   if (obscured_) {
532     SelectAll(false);
533     return;
534   }
535
536   size_t selection_max = selection().GetMax();
537
538   base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
539   bool success = iter.Init();
540   DCHECK(success);
541   if (!success)
542     return;
543
544   size_t selection_min = selection().GetMin();
545   if (selection_min == text().length() && selection_min != 0)
546     --selection_min;
547
548   for (; selection_min != 0; --selection_min) {
549     if (iter.IsStartOfWord(selection_min) ||
550         iter.IsEndOfWord(selection_min))
551       break;
552   }
553
554   if (selection_min == selection_max && selection_max != text().length())
555     ++selection_max;
556
557   for (; selection_max < text().length(); ++selection_max)
558     if (iter.IsEndOfWord(selection_max) || iter.IsStartOfWord(selection_max))
559       break;
560
561   const bool reversed = selection().is_reversed();
562   MoveCursorTo(reversed ? selection_max : selection_min, false);
563   MoveCursorTo(reversed ? selection_min : selection_max, true);
564 }
565
566 const Range& RenderText::GetCompositionRange() const {
567   return composition_range_;
568 }
569
570 void RenderText::SetCompositionRange(const Range& composition_range) {
571   CHECK(!composition_range.IsValid() ||
572         Range(0, text_.length()).Contains(composition_range));
573   composition_range_.set_end(composition_range.end());
574   composition_range_.set_start(composition_range.start());
575   ResetLayout();
576 }
577
578 void RenderText::SetColor(SkColor value) {
579   colors_.SetValue(value);
580
581 #if defined(OS_WIN)
582   // TODO(msw): Windows applies colors and decorations in the layout process.
583   cached_bounds_and_offset_valid_ = false;
584   ResetLayout();
585 #endif
586 }
587
588 void RenderText::ApplyColor(SkColor value, const Range& range) {
589   colors_.ApplyValue(value, range);
590
591 #if defined(OS_WIN)
592   // TODO(msw): Windows applies colors and decorations in the layout process.
593   cached_bounds_and_offset_valid_ = false;
594   ResetLayout();
595 #endif
596 }
597
598 void RenderText::SetStyle(TextStyle style, bool value) {
599   styles_[style].SetValue(value);
600
601   // Only invalidate the layout on font changes; not for colors or decorations.
602   bool invalidate = (style == BOLD) || (style == ITALIC);
603 #if defined(OS_WIN)
604   // TODO(msw): Windows applies colors and decorations in the layout process.
605   invalidate = true;
606 #endif
607   if (invalidate) {
608     cached_bounds_and_offset_valid_ = false;
609     ResetLayout();
610   }
611 }
612
613 void RenderText::ApplyStyle(TextStyle style, bool value, const Range& range) {
614   styles_[style].ApplyValue(value, range);
615
616   // Only invalidate the layout on font changes; not for colors or decorations.
617   bool invalidate = (style == BOLD) || (style == ITALIC);
618 #if defined(OS_WIN)
619   // TODO(msw): Windows applies colors and decorations in the layout process.
620   invalidate = true;
621 #endif
622   if (invalidate) {
623     cached_bounds_and_offset_valid_ = false;
624     ResetLayout();
625   }
626 }
627
628 bool RenderText::GetStyle(TextStyle style) const {
629   return (styles_[style].breaks().size() == 1) &&
630       styles_[style].breaks().front().second;
631 }
632
633 void RenderText::SetDirectionalityMode(DirectionalityMode mode) {
634   if (mode == directionality_mode_)
635     return;
636
637   directionality_mode_ = mode;
638   text_direction_ = base::i18n::UNKNOWN_DIRECTION;
639   cached_bounds_and_offset_valid_ = false;
640   ResetLayout();
641 }
642
643 base::i18n::TextDirection RenderText::GetTextDirection() {
644   if (text_direction_ == base::i18n::UNKNOWN_DIRECTION) {
645     switch (directionality_mode_) {
646       case DIRECTIONALITY_FROM_TEXT:
647         // Derive the direction from the display text, which differs from text()
648         // in the case of obscured (password) textfields.
649         text_direction_ =
650             base::i18n::GetFirstStrongCharacterDirection(GetLayoutText());
651         break;
652       case DIRECTIONALITY_FROM_UI:
653         text_direction_ = base::i18n::IsRTL() ? base::i18n::RIGHT_TO_LEFT :
654                                                 base::i18n::LEFT_TO_RIGHT;
655         break;
656       case DIRECTIONALITY_FORCE_LTR:
657         text_direction_ = base::i18n::LEFT_TO_RIGHT;
658         break;
659       case DIRECTIONALITY_FORCE_RTL:
660         text_direction_ = base::i18n::RIGHT_TO_LEFT;
661         break;
662       default:
663         NOTREACHED();
664     }
665   }
666
667   return text_direction_;
668 }
669
670 VisualCursorDirection RenderText::GetVisualDirectionOfLogicalEnd() {
671   return GetTextDirection() == base::i18n::LEFT_TO_RIGHT ?
672       CURSOR_RIGHT : CURSOR_LEFT;
673 }
674
675 SizeF RenderText::GetStringSizeF() {
676   const Size size = GetStringSize();
677   return SizeF(size.width(), size.height());
678 }
679
680 int RenderText::GetContentWidth() {
681   return GetStringSize().width() + (cursor_enabled_ ? 1 : 0);
682 }
683
684 int RenderText::GetBaseline() {
685   if (baseline_ == kInvalidBaseline)
686     baseline_ = DetermineBaselineCenteringText(display_rect(), font_list());
687   DCHECK_NE(kInvalidBaseline, baseline_);
688   return baseline_;
689 }
690
691 void RenderText::Draw(Canvas* canvas) {
692   EnsureLayout();
693
694   if (clip_to_display_rect()) {
695     Rect clip_rect(display_rect());
696     clip_rect.Inset(ShadowValue::GetMargin(text_shadows_));
697
698     canvas->Save();
699     canvas->ClipRect(clip_rect);
700   }
701
702   if (!text().empty() && focused())
703     DrawSelection(canvas);
704
705   if (cursor_enabled() && cursor_visible() && focused())
706     DrawCursor(canvas, selection_model_);
707
708   if (!text().empty())
709     DrawVisualText(canvas);
710
711   if (clip_to_display_rect())
712     canvas->Restore();
713 }
714
715 void RenderText::DrawCursor(Canvas* canvas, const SelectionModel& position) {
716   // Paint cursor. Replace cursor is drawn as rectangle for now.
717   // TODO(msw): Draw a better cursor with a better indication of association.
718   canvas->FillRect(GetCursorBounds(position, true), cursor_color_);
719 }
720
721 void RenderText::DrawSelectedTextForDrag(Canvas* canvas) {
722   EnsureLayout();
723   const std::vector<Rect> sel = GetSubstringBounds(selection());
724
725   // Override the selection color with black, and force the background to be
726   // transparent so that it's rendered without subpixel antialiasing.
727   const bool saved_background_is_transparent = background_is_transparent();
728   const SkColor saved_selection_color = selection_color();
729   set_background_is_transparent(true);
730   set_selection_color(SK_ColorBLACK);
731
732   for (size_t i = 0; i < sel.size(); ++i) {
733     canvas->Save();
734     canvas->ClipRect(sel[i]);
735     DrawVisualText(canvas);
736     canvas->Restore();
737   }
738
739   // Restore saved transparency and selection color.
740   set_selection_color(saved_selection_color);
741   set_background_is_transparent(saved_background_is_transparent);
742 }
743
744 Rect RenderText::GetCursorBounds(const SelectionModel& caret,
745                                  bool insert_mode) {
746   // TODO(ckocagil): Support multiline. This function should return the height
747   //                 of the line the cursor is on. |GetStringSize()| now returns
748   //                 the multiline size, eliminate its use here.
749
750   EnsureLayout();
751
752   size_t caret_pos = caret.caret_pos();
753   DCHECK(IsCursorablePosition(caret_pos));
754   // In overtype mode, ignore the affinity and always indicate that we will
755   // overtype the next character.
756   LogicalCursorDirection caret_affinity =
757       insert_mode ? caret.caret_affinity() : CURSOR_FORWARD;
758   int x = 0, width = 1;
759   Size size = GetStringSize();
760   if (caret_pos == (caret_affinity == CURSOR_BACKWARD ? 0 : text().length())) {
761     // The caret is attached to the boundary. Always return a 1-dip width caret,
762     // since there is nothing to overtype.
763     if ((GetTextDirection() == base::i18n::RIGHT_TO_LEFT) == (caret_pos == 0))
764       x = size.width();
765   } else {
766     size_t grapheme_start = (caret_affinity == CURSOR_FORWARD) ?
767         caret_pos : IndexOfAdjacentGrapheme(caret_pos, CURSOR_BACKWARD);
768     Range xspan(GetGlyphBounds(grapheme_start));
769     if (insert_mode) {
770       x = (caret_affinity == CURSOR_BACKWARD) ? xspan.end() : xspan.start();
771     } else {  // overtype mode
772       x = xspan.GetMin();
773       width = xspan.length();
774     }
775   }
776   return Rect(ToViewPoint(Point(x, 0)), Size(width, size.height()));
777 }
778
779 const Rect& RenderText::GetUpdatedCursorBounds() {
780   UpdateCachedBoundsAndOffset();
781   return cursor_bounds_;
782 }
783
784 size_t RenderText::IndexOfAdjacentGrapheme(size_t index,
785                                            LogicalCursorDirection direction) {
786   if (index > text().length())
787     return text().length();
788
789   EnsureLayout();
790
791   if (direction == CURSOR_FORWARD) {
792     while (index < text().length()) {
793       index++;
794       if (IsCursorablePosition(index))
795         return index;
796     }
797     return text().length();
798   }
799
800   while (index > 0) {
801     index--;
802     if (IsCursorablePosition(index))
803       return index;
804   }
805   return 0;
806 }
807
808 SelectionModel RenderText::GetSelectionModelForSelectionStart() {
809   const Range& sel = selection();
810   if (sel.is_empty())
811     return selection_model_;
812   return SelectionModel(sel.start(),
813                         sel.is_reversed() ? CURSOR_BACKWARD : CURSOR_FORWARD);
814 }
815
816 void RenderText::SetTextShadows(const ShadowValues& shadows) {
817   text_shadows_ = shadows;
818 }
819
820 RenderText::RenderText()
821     : horizontal_alignment_(base::i18n::IsRTL() ? ALIGN_RIGHT : ALIGN_LEFT),
822       directionality_mode_(DIRECTIONALITY_FROM_TEXT),
823       text_direction_(base::i18n::UNKNOWN_DIRECTION),
824       cursor_enabled_(true),
825       cursor_visible_(false),
826       insert_mode_(true),
827       cursor_color_(kDefaultColor),
828       selection_color_(kDefaultColor),
829       selection_background_focused_color_(kDefaultSelectionBackgroundColor),
830       focused_(false),
831       composition_range_(Range::InvalidRange()),
832       colors_(kDefaultColor),
833       styles_(NUM_TEXT_STYLES),
834       composition_and_selection_styles_applied_(false),
835       obscured_(false),
836       obscured_reveal_index_(-1),
837       truncate_length_(0),
838       elide_behavior_(NO_ELIDE),
839       multiline_(false),
840       fade_head_(false),
841       fade_tail_(false),
842       background_is_transparent_(false),
843       clip_to_display_rect_(true),
844       baseline_(kInvalidBaseline),
845       cached_bounds_and_offset_valid_(false) {
846 }
847
848 const Vector2d& RenderText::GetUpdatedDisplayOffset() {
849   UpdateCachedBoundsAndOffset();
850   return display_offset_;
851 }
852
853 SelectionModel RenderText::GetAdjacentSelectionModel(
854     const SelectionModel& current,
855     BreakType break_type,
856     VisualCursorDirection direction) {
857   EnsureLayout();
858
859   if (break_type == LINE_BREAK || text().empty())
860     return EdgeSelectionModel(direction);
861   if (break_type == CHARACTER_BREAK)
862     return AdjacentCharSelectionModel(current, direction);
863   DCHECK(break_type == WORD_BREAK);
864   return AdjacentWordSelectionModel(current, direction);
865 }
866
867 SelectionModel RenderText::EdgeSelectionModel(
868     VisualCursorDirection direction) {
869   if (direction == GetVisualDirectionOfLogicalEnd())
870     return SelectionModel(text().length(), CURSOR_FORWARD);
871   return SelectionModel(0, CURSOR_BACKWARD);
872 }
873
874 void RenderText::SetSelectionModel(const SelectionModel& model) {
875   DCHECK_LE(model.selection().GetMax(), text().length());
876   selection_model_ = model;
877   cached_bounds_and_offset_valid_ = false;
878 }
879
880 const base::string16& RenderText::GetLayoutText() const {
881   return layout_text_.empty() ? text_ : layout_text_;
882 }
883
884 const BreakList<size_t>& RenderText::GetLineBreaks() {
885   if (line_breaks_.max() != 0)
886     return line_breaks_;
887
888   const base::string16& layout_text = GetLayoutText();
889   const size_t text_length = layout_text.length();
890   line_breaks_.SetValue(0);
891   line_breaks_.SetMax(text_length);
892   base::i18n::BreakIterator iter(layout_text,
893                                  base::i18n::BreakIterator::BREAK_LINE);
894   const bool success = iter.Init();
895   DCHECK(success);
896   if (success) {
897     do {
898       line_breaks_.ApplyValue(iter.pos(), Range(iter.pos(), text_length));
899     } while (iter.Advance());
900   }
901   return line_breaks_;
902 }
903
904 void RenderText::ApplyCompositionAndSelectionStyles() {
905   // Save the underline and color breaks to undo the temporary styles later.
906   DCHECK(!composition_and_selection_styles_applied_);
907   saved_colors_ = colors_;
908   saved_underlines_ = styles_[UNDERLINE];
909
910   // Apply an underline to the composition range in |underlines|.
911   if (composition_range_.IsValid() && !composition_range_.is_empty())
912     styles_[UNDERLINE].ApplyValue(true, composition_range_);
913
914   // Apply the selected text color to the [un-reversed] selection range.
915   if (!selection().is_empty() && focused()) {
916     const Range range(selection().GetMin(), selection().GetMax());
917     colors_.ApplyValue(selection_color_, range);
918   }
919   composition_and_selection_styles_applied_ = true;
920 }
921
922 void RenderText::UndoCompositionAndSelectionStyles() {
923   // Restore the underline and color breaks to undo the temporary styles.
924   DCHECK(composition_and_selection_styles_applied_);
925   colors_ = saved_colors_;
926   styles_[UNDERLINE] = saved_underlines_;
927   composition_and_selection_styles_applied_ = false;
928 }
929
930 Vector2d RenderText::GetLineOffset(size_t line_number) {
931   Vector2d offset = display_rect().OffsetFromOrigin();
932   // TODO(ckocagil): Apply the display offset for multiline scrolling.
933   if (!multiline())
934     offset.Add(GetUpdatedDisplayOffset());
935   else
936     offset.Add(Vector2d(0, lines_[line_number].preceding_heights));
937   offset.Add(GetAlignmentOffset(line_number));
938   return offset;
939 }
940
941 Point RenderText::ToTextPoint(const Point& point) {
942   return point - GetLineOffset(0);
943   // TODO(ckocagil): Convert multiline view space points to text space.
944 }
945
946 Point RenderText::ToViewPoint(const Point& point) {
947   if (!multiline())
948     return point + GetLineOffset(0);
949
950   // TODO(ckocagil): Traverse individual line segments for RTL support.
951   DCHECK(!lines_.empty());
952   int x = point.x();
953   size_t line = 0;
954   for (; line < lines_.size() && x > lines_[line].size.width(); ++line)
955     x -= lines_[line].size.width();
956   return Point(x, point.y()) + GetLineOffset(line);
957 }
958
959 std::vector<Rect> RenderText::TextBoundsToViewBounds(const Range& x) {
960   std::vector<Rect> rects;
961
962   if (!multiline()) {
963     rects.push_back(Rect(ToViewPoint(Point(x.GetMin(), 0)),
964                          Size(x.length(), GetStringSize().height())));
965     return rects;
966   }
967
968   EnsureLayout();
969
970   // Each line segment keeps its position in text coordinates. Traverse all line
971   // segments and if the segment intersects with the given range, add the view
972   // rect corresponding to the intersection to |rects|.
973   for (size_t line = 0; line < lines_.size(); ++line) {
974     int line_x = 0;
975     const Vector2d offset = GetLineOffset(line);
976     for (size_t i = 0; i < lines_[line].segments.size(); ++i) {
977       const internal::LineSegment* segment = &lines_[line].segments[i];
978       const Range intersection = segment->x_range.Intersect(x);
979       if (!intersection.is_empty()) {
980         Rect rect(line_x + intersection.start() - segment->x_range.start(),
981                   0, intersection.length(), lines_[line].size.height());
982         rects.push_back(rect + offset);
983       }
984       line_x += segment->x_range.length();
985     }
986   }
987
988   return rects;
989 }
990
991 Vector2d RenderText::GetAlignmentOffset(size_t line_number) {
992   // TODO(ckocagil): Enable |lines_| usage in other platforms.
993 #if defined(OS_WIN)
994   DCHECK_LT(line_number, lines_.size());
995 #endif
996   Vector2d offset;
997   if (horizontal_alignment_ != ALIGN_LEFT) {
998 #if defined(OS_WIN)
999     const int width = lines_[line_number].size.width() +
1000         (cursor_enabled_ ? 1 : 0);
1001 #else
1002     const int width = GetContentWidth();
1003 #endif
1004     offset.set_x(display_rect().width() - width);
1005     if (horizontal_alignment_ == ALIGN_CENTER)
1006       offset.set_x(offset.x() / 2);
1007   }
1008
1009   // Vertically center the text.
1010   if (multiline_) {
1011     const int text_height = lines_.back().preceding_heights +
1012         lines_.back().size.height();
1013     offset.set_y((display_rect_.height() - text_height) / 2);
1014   } else {
1015     offset.set_y(GetBaseline() - GetLayoutTextBaseline());
1016   }
1017
1018   return offset;
1019 }
1020
1021 void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) {
1022   if (multiline() || (!fade_head() && !fade_tail()))
1023     return;
1024
1025   const int display_width = display_rect().width();
1026
1027   // If the text fits as-is, no need to fade.
1028   if (GetStringSize().width() <= display_width)
1029     return;
1030
1031   int gradient_width = CalculateFadeGradientWidth(font_list(), display_width);
1032   if (gradient_width == 0)
1033     return;
1034
1035   bool fade_left = fade_head();
1036   bool fade_right = fade_tail();
1037   // Under RTL, |fade_right| == |fade_head|.
1038   // TODO(asvitkine): This is currently not based on GetTextDirection() because
1039   //                  RenderTextWin does not return a direction that's based on
1040   //                  the text content.
1041   if (horizontal_alignment_ == ALIGN_RIGHT)
1042     std::swap(fade_left, fade_right);
1043
1044   Rect solid_part = display_rect();
1045   Rect left_part;
1046   Rect right_part;
1047   if (fade_left) {
1048     left_part = solid_part;
1049     left_part.Inset(0, 0, solid_part.width() - gradient_width, 0);
1050     solid_part.Inset(gradient_width, 0, 0, 0);
1051   }
1052   if (fade_right) {
1053     right_part = solid_part;
1054     right_part.Inset(solid_part.width() - gradient_width, 0, 0, 0);
1055     solid_part.Inset(0, 0, gradient_width, 0);
1056   }
1057
1058   Rect text_rect = display_rect();
1059   text_rect.Inset(GetAlignmentOffset(0).x(), 0, 0, 0);
1060
1061   // TODO(msw): Use the actual text colors corresponding to each faded part.
1062   skia::RefPtr<SkShader> shader = CreateFadeShader(
1063       text_rect, left_part, right_part, colors_.breaks().front().second);
1064   if (shader)
1065     renderer->SetShader(shader.get(), display_rect());
1066 }
1067
1068 void RenderText::ApplyTextShadows(internal::SkiaTextRenderer* renderer) {
1069   skia::RefPtr<SkDrawLooper> looper = CreateShadowDrawLooper(text_shadows_);
1070   renderer->SetDrawLooper(looper.get());
1071 }
1072
1073 // static
1074 bool RenderText::RangeContainsCaret(const Range& range,
1075                                     size_t caret_pos,
1076                                     LogicalCursorDirection caret_affinity) {
1077   // NB: exploits unsigned wraparound (WG14/N1124 section 6.2.5 paragraph 9).
1078   size_t adjacent = (caret_affinity == CURSOR_BACKWARD) ?
1079       caret_pos - 1 : caret_pos + 1;
1080   return range.Contains(Range(caret_pos, adjacent));
1081 }
1082
1083 void RenderText::MoveCursorTo(size_t position, bool select) {
1084   size_t cursor = std::min(position, text().length());
1085   if (IsCursorablePosition(cursor))
1086     SetSelectionModel(SelectionModel(
1087         Range(select ? selection().start() : cursor, cursor),
1088         (cursor == 0) ? CURSOR_FORWARD : CURSOR_BACKWARD));
1089 }
1090
1091 void RenderText::UpdateLayoutText() {
1092   layout_text_.clear();
1093   line_breaks_.SetMax(0);
1094
1095   if (obscured_) {
1096     size_t obscured_text_length =
1097         static_cast<size_t>(gfx::UTF16IndexToOffset(text_, 0, text_.length()));
1098     layout_text_.assign(obscured_text_length, kPasswordReplacementChar);
1099
1100     if (obscured_reveal_index_ >= 0 &&
1101         obscured_reveal_index_ < static_cast<int>(text_.length())) {
1102       // Gets the index range in |text_| to be revealed.
1103       size_t start = obscured_reveal_index_;
1104       U16_SET_CP_START(text_.data(), 0, start);
1105       size_t end = start;
1106       UChar32 unused_char;
1107       U16_NEXT(text_.data(), end, text_.length(), unused_char);
1108
1109       // Gets the index in |layout_text_| to be replaced.
1110       const size_t cp_start =
1111           static_cast<size_t>(gfx::UTF16IndexToOffset(text_, 0, start));
1112       if (layout_text_.length() > cp_start)
1113         layout_text_.replace(cp_start, 1, text_.substr(start, end - start));
1114     }
1115   }
1116
1117   const base::string16& text = GetLayoutText();
1118   if (truncate_length_ > 0 && truncate_length_ < text.length()) {
1119     // Truncate the text at a valid character break and append an ellipsis.
1120     icu::StringCharacterIterator iter(text.c_str());
1121     iter.setIndex32(truncate_length_ - 1);
1122     layout_text_.assign(text.substr(0, iter.getIndex()) + gfx::kEllipsisUTF16);
1123   }
1124
1125   if (elide_behavior_ != NO_ELIDE && display_rect_.width() > 0 &&
1126       !GetLayoutText().empty() && GetContentWidth() > display_rect_.width()) {
1127     base::string16 elided_text = ElideText(GetLayoutText());
1128
1129     // This doesn't trim styles so ellipsis may get rendered as a different
1130     // style than the preceding text. See crbug.com/327850.
1131     layout_text_.assign(elided_text);
1132   }
1133   ResetLayout();
1134 }
1135
1136 // TODO(skanuj): Fix code duplication with ElideText in ui/gfx/text_elider.cc
1137 // See crbug.com/327846
1138 base::string16 RenderText::ElideText(const base::string16& text) {
1139   const bool insert_ellipsis = (elide_behavior_ != TRUNCATE_AT_END);
1140   // Create a RenderText copy with attributes that affect the rendering width.
1141   scoped_ptr<RenderText> render_text(CreateInstance());
1142   render_text->SetFontList(font_list_);
1143   render_text->SetDirectionalityMode(directionality_mode_);
1144   render_text->SetCursorEnabled(cursor_enabled_);
1145
1146   render_text->styles_ = styles_;
1147   render_text->colors_ = colors_;
1148   render_text->SetText(text);
1149   const int current_text_pixel_width = render_text->GetContentWidth();
1150
1151   const base::string16 ellipsis = base::string16(gfx::kEllipsisUTF16);
1152   const bool elide_in_middle = false;
1153   const bool elide_at_beginning = false;
1154   StringSlicer slicer(text, ellipsis, elide_in_middle, elide_at_beginning);
1155
1156   // Pango will return 0 width for absurdly long strings. Cut the string in
1157   // half and try again.
1158   // This is caused by an int overflow in Pango (specifically, in
1159   // pango_glyph_string_extents_range). It's actually more subtle than just
1160   // returning 0, since on super absurdly long strings, the int can wrap and
1161   // return positive numbers again. Detecting that is probably not worth it
1162   // (eliding way too much from a ridiculous string is probably still
1163   // ridiculous), but we should check other widths for bogus values as well.
1164   if (current_text_pixel_width <= 0 && !text.empty())
1165     return ElideText(slicer.CutString(text.length() / 2, insert_ellipsis));
1166
1167   if (current_text_pixel_width <= display_rect_.width())
1168     return text;
1169
1170   render_text->SetText(base::string16());
1171   render_text->SetText(ellipsis);
1172   const int ellipsis_width = render_text->GetContentWidth();
1173
1174   if (insert_ellipsis && (ellipsis_width >= display_rect_.width()))
1175     return base::string16();
1176
1177   // Use binary search to compute the elided text.
1178   size_t lo = 0;
1179   size_t hi = text.length() - 1;
1180   for (size_t guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) {
1181     // Restore styles and colors. They will be truncated to size by SetText.
1182     render_text->styles_ = styles_;
1183     render_text->colors_ = colors_;
1184     base::string16 new_text = slicer.CutString(guess, false);
1185     render_text->SetText(new_text);
1186
1187     // This has to be an additional step so that the ellipsis is rendered with
1188     // same style as trailing part of the text.
1189     if (insert_ellipsis) {
1190       // When ellipsis follows text whose directionality is not the same as that
1191       // of the whole text, it will be rendered with the directionality of the
1192       // whole text. Since we want ellipsis to indicate continuation of the
1193       // preceding text, we force the directionality of ellipsis to be same as
1194       // the preceding text using LTR or RTL markers.
1195       base::i18n::TextDirection leading_text_direction =
1196           base::i18n::GetFirstStrongCharacterDirection(new_text);
1197       base::i18n::TextDirection trailing_text_direction =
1198           base::i18n::GetLastStrongCharacterDirection(new_text);
1199       new_text.append(ellipsis);
1200       if (trailing_text_direction != leading_text_direction) {
1201         if (trailing_text_direction == base::i18n::LEFT_TO_RIGHT)
1202           new_text += base::i18n::kLeftToRightMark;
1203         else
1204           new_text += base::i18n::kRightToLeftMark;
1205       }
1206       render_text->SetText(new_text);
1207     }
1208
1209     // We check the width of the whole desired string at once to ensure we
1210     // handle kerning/ligatures/etc. correctly.
1211     const int guess_width = render_text->GetContentWidth();
1212     if (guess_width == display_rect_.width())
1213       break;
1214     if (guess_width > display_rect_.width()) {
1215       hi = guess - 1;
1216       // Move back if we are on loop terminating condition, and guess is wider
1217       // than available.
1218       if (hi < lo)
1219         lo = hi;
1220     } else {
1221       lo = guess + 1;
1222     }
1223   }
1224
1225   return render_text->text();
1226 }
1227
1228 void RenderText::UpdateCachedBoundsAndOffset() {
1229   if (cached_bounds_and_offset_valid_)
1230     return;
1231
1232   // TODO(ckocagil): Add support for scrolling multiline text.
1233
1234   // First, set the valid flag true to calculate the current cursor bounds using
1235   // the stale |display_offset_|. Applying |delta_offset| at the end of this
1236   // function will set |cursor_bounds_| and |display_offset_| to correct values.
1237   cached_bounds_and_offset_valid_ = true;
1238   cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_);
1239
1240   // Update |display_offset_| to ensure the current cursor is visible.
1241   const int display_width = display_rect_.width();
1242   const int content_width = GetContentWidth();
1243
1244   int delta_x = 0;
1245   if (content_width <= display_width || !cursor_enabled()) {
1246     // Don't pan if the text fits in the display width or when the cursor is
1247     // disabled.
1248     delta_x = -display_offset_.x();
1249   } else if (cursor_bounds_.right() > display_rect_.right()) {
1250     // TODO(xji): when the character overflow is a RTL character, currently, if
1251     // we pan cursor at the rightmost position, the entered RTL character is not
1252     // displayed. Should pan cursor to show the last logical characters.
1253     //
1254     // Pan to show the cursor when it overflows to the right.
1255     delta_x = display_rect_.right() - cursor_bounds_.right();
1256   } else if (cursor_bounds_.x() < display_rect_.x()) {
1257     // TODO(xji): have similar problem as above when overflow character is a
1258     // LTR character.
1259     //
1260     // Pan to show the cursor when it overflows to the left.
1261     delta_x = display_rect_.x() - cursor_bounds_.x();
1262   } else if (display_offset_.x() != 0) {
1263     // Reduce the pan offset to show additional overflow text when the display
1264     // width increases.
1265     const int negate_rtl = horizontal_alignment_ == ALIGN_RIGHT ? -1 : 1;
1266     const int offset = negate_rtl * display_offset_.x();
1267     if (display_width > (content_width + offset)) {
1268       delta_x = negate_rtl * (display_width - (content_width + offset));
1269     }
1270   }
1271
1272   Vector2d delta_offset(delta_x, 0);
1273   display_offset_ += delta_offset;
1274   cursor_bounds_ += delta_offset;
1275 }
1276
1277 void RenderText::DrawSelection(Canvas* canvas) {
1278   const std::vector<Rect> sel = GetSubstringBounds(selection());
1279   for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i)
1280     canvas->FillRect(*i, selection_background_focused_color_);
1281 }
1282
1283 }  // namespace gfx