blob: 727af4a53690fcc93dda0c542c8c8aac1d9963c3 [file] [log] [blame]
[email protected]8d901a82012-01-04 19:41:301// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]ff44d712011-07-25 08:42:522// 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
[email protected]592a7ad2011-12-14 05:57:539#include "base/debug/trace_event.h"
[email protected]ff44d712011-07-25 08:42:5210#include "base/i18n/break_iterator.h"
11#include "base/logging.h"
12#include "base/stl_util.h"
[email protected]67b981562011-12-09 00:35:0513#include "third_party/skia/include/core/SkTypeface.h"
[email protected]f308e5b12012-01-17 19:09:1814#include "third_party/skia/include/effects/SkGradientShader.h"
[email protected]bec929c2012-03-02 06:23:5015#include "ui/base/text/utf16_indexing.h"
[email protected]ff44d712011-07-25 08:42:5216#include "ui/gfx/canvas.h"
[email protected]721ea922012-01-23 21:13:3317#include "ui/gfx/native_theme.h"
[email protected]ff44d712011-07-25 08:42:5218
19namespace {
20
[email protected]bec929c2012-03-02 06:23:5021// All chars are replaced by this char when the password style is set.
22// TODO(benrg): GTK uses the first of U+25CF, U+2022, U+2731, U+273A, '*'
23// that's available in the font (find_invisible_char() in gtkentry.c).
24const char16 kPasswordReplacementChar = '*';
25
[email protected]913a1ef2012-02-29 08:40:3426// Default color used for the cursor.
27const SkColor kDefaultCursorColor = SK_ColorBLACK;
[email protected]d66009e2012-01-21 01:27:2828
[email protected]ff44d712011-07-25 08:42:5229#ifndef NDEBUG
30// Check StyleRanges invariant conditions: sorted and non-overlapping ranges.
31void CheckStyleRanges(const gfx::StyleRanges& style_ranges, size_t length) {
32 if (length == 0) {
33 DCHECK(style_ranges.empty()) << "Style ranges exist for empty text.";
34 return;
35 }
36 for (gfx::StyleRanges::size_type i = 0; i < style_ranges.size() - 1; i++) {
37 const ui::Range& former = style_ranges[i].range;
38 const ui::Range& latter = style_ranges[i + 1].range;
39 DCHECK(!former.is_empty()) << "Empty range at " << i << ":" << former;
40 DCHECK(former.IsValid()) << "Invalid range at " << i << ":" << former;
41 DCHECK(!former.is_reversed()) << "Reversed range at " << i << ":" << former;
42 DCHECK(former.end() == latter.start()) << "Ranges gap/overlap/unsorted." <<
43 "former:" << former << ", latter:" << latter;
44 }
45 const gfx::StyleRange& end_style = *style_ranges.rbegin();
46 DCHECK(!end_style.range.is_empty()) << "Empty range at end.";
47 DCHECK(end_style.range.IsValid()) << "Invalid range at end.";
48 DCHECK(!end_style.range.is_reversed()) << "Reversed range at end.";
49 DCHECK(end_style.range.end() == length) << "Style and text length mismatch.";
50}
51#endif
52
[email protected]8e42ba22011-08-04 21:47:0853void ApplyStyleRangeImpl(gfx::StyleRanges* style_ranges,
[email protected]c8636672011-12-29 05:07:5754 const gfx::StyleRange& style_range) {
[email protected]ff44d712011-07-25 08:42:5255 const ui::Range& new_range = style_range.range;
56 // Follow StyleRanges invariant conditions: sorted and non-overlapping ranges.
57 gfx::StyleRanges::iterator i;
[email protected]8e42ba22011-08-04 21:47:0858 for (i = style_ranges->begin(); i != style_ranges->end();) {
[email protected]ff44d712011-07-25 08:42:5259 if (i->range.end() < new_range.start()) {
60 i++;
61 } else if (i->range.start() == new_range.end()) {
62 break;
63 } else if (new_range.Contains(i->range)) {
[email protected]8e42ba22011-08-04 21:47:0864 i = style_ranges->erase(i);
65 if (i == style_ranges->end())
[email protected]ff44d712011-07-25 08:42:5266 break;
67 } else if (i->range.start() < new_range.start() &&
68 i->range.end() > new_range.end()) {
69 // Split the current style into two styles.
70 gfx::StyleRange split_style = gfx::StyleRange(*i);
71 split_style.range.set_end(new_range.start());
[email protected]8e42ba22011-08-04 21:47:0872 i = style_ranges->insert(i, split_style) + 1;
[email protected]ff44d712011-07-25 08:42:5273 i->range.set_start(new_range.end());
74 break;
75 } else if (i->range.start() < new_range.start()) {
76 i->range.set_end(new_range.start());
77 i++;
78 } else if (i->range.end() > new_range.end()) {
79 i->range.set_start(new_range.end());
80 break;
[email protected]f9a221b2011-12-10 20:25:3881 } else {
[email protected]ff44d712011-07-25 08:42:5282 NOTREACHED();
[email protected]f9a221b2011-12-10 20:25:3883 }
[email protected]ff44d712011-07-25 08:42:5284 }
85 // Add the new range in its sorted location.
[email protected]8e42ba22011-08-04 21:47:0886 style_ranges->insert(i, style_range);
[email protected]ff44d712011-07-25 08:42:5287}
88
[email protected]7aba39c2012-01-10 16:10:2789// Converts |gfx::Font::FontStyle| flags to |SkTypeface::Style| flags.
90SkTypeface::Style ConvertFontStyleToSkiaTypefaceStyle(int font_style) {
91 int skia_style = SkTypeface::kNormal;
92 if (font_style & gfx::Font::BOLD)
93 skia_style |= SkTypeface::kBold;
94 if (font_style & gfx::Font::ITALIC)
95 skia_style |= SkTypeface::kItalic;
96 return static_cast<SkTypeface::Style>(skia_style);
97}
98
[email protected]f308e5b12012-01-17 19:09:1899// Given |font| and |display_width|, returns the width of the fade gradient.
100int CalculateFadeGradientWidth(const gfx::Font& font, int display_width) {
101 // Fade in/out about 2.5 characters of the beginning/end of the string.
102 // The .5 here is helpful if one of the characters is a space.
103 // Use a quarter of the display width if the display width is very short.
104 const int average_character_width = font.GetAverageCharacterWidth();
105 const double gradient_width = std::min(average_character_width * 2.5,
106 display_width / 4.0);
107 DCHECK_GE(gradient_width, 0.0);
108 return static_cast<int>(floor(gradient_width + 0.5));
109}
110
111// Appends to |positions| and |colors| values corresponding to the fade over
112// |fade_rect| from color |c0| to color |c1|.
113void AddFadeEffect(const gfx::Rect& text_rect,
114 const gfx::Rect& fade_rect,
115 SkColor c0,
116 SkColor c1,
117 std::vector<SkScalar>* positions,
118 std::vector<SkColor>* colors) {
119 const SkScalar left = static_cast<SkScalar>(fade_rect.x() - text_rect.x());
120 const SkScalar width = static_cast<SkScalar>(fade_rect.width());
121 const SkScalar p0 = left / text_rect.width();
122 const SkScalar p1 = (left + width) / text_rect.width();
123 // Prepend 0.0 to |positions|, as required by Skia.
124 if (positions->empty() && p0 != 0.0) {
125 positions->push_back(0.0);
126 colors->push_back(c0);
127 }
128 positions->push_back(p0);
129 colors->push_back(c0);
130 positions->push_back(p1);
131 colors->push_back(c1);
132}
133
134// Creates a SkShader to fade the text, with |left_part| specifying the left
135// fade effect, if any, and |right_part| specifying the right fade effect.
136SkShader* CreateFadeShader(const gfx::Rect& text_rect,
137 const gfx::Rect& left_part,
138 const gfx::Rect& right_part,
139 SkColor color) {
140 // Fade alpha of 51/255 corresponds to a fade of 0.2 of the original color.
141 const SkColor fade_color = SkColorSetA(color, 51);
[email protected]f308e5b12012-01-17 19:09:18142 std::vector<SkScalar> positions;
143 std::vector<SkColor> colors;
144
145 if (!left_part.IsEmpty())
146 AddFadeEffect(text_rect, left_part, fade_color, color,
147 &positions, &colors);
148 if (!right_part.IsEmpty())
149 AddFadeEffect(text_rect, right_part, color, fade_color,
150 &positions, &colors);
151 DCHECK(!positions.empty());
152
153 // Terminate |positions| with 1.0, as required by Skia.
154 if (positions.back() != 1.0) {
155 positions.push_back(1.0);
156 colors.push_back(colors.back());
157 }
158
[email protected]959e1902012-03-03 02:02:17159 SkPoint points[2];
160 points[0].iset(text_rect.x(), text_rect.y());
161 points[1].iset(text_rect.right(), text_rect.y());
[email protected]f308e5b12012-01-17 19:09:18162
[email protected]959e1902012-03-03 02:02:17163 return SkGradientShader::CreateLinear(&points[0], &colors[0], &positions[0],
164 colors.size(), SkShader::kClamp_TileMode);
165}
[email protected]f308e5b12012-01-17 19:09:18166
[email protected]ff44d712011-07-25 08:42:52167} // namespace
168
169namespace gfx {
170
[email protected]67b981562011-12-09 00:35:05171namespace internal {
172
173SkiaTextRenderer::SkiaTextRenderer(Canvas* canvas)
[email protected]f0e90a42012-03-13 23:05:57174 : canvas_skia_(canvas->sk_canvas()) {
[email protected]67b981562011-12-09 00:35:05175 DCHECK(canvas_skia_);
176 paint_.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
177 paint_.setStyle(SkPaint::kFill_Style);
178 paint_.setAntiAlias(true);
179 paint_.setSubpixelText(true);
180 paint_.setLCDRenderText(true);
181}
182
183SkiaTextRenderer::~SkiaTextRenderer() {
184}
185
[email protected]be5756e22012-02-17 15:53:01186void SkiaTextRenderer::SetFontSmoothingSettings(bool enable_smoothing,
187 bool enable_lcd_text) {
188 paint_.setAntiAlias(enable_smoothing);
189 paint_.setSubpixelText(enable_smoothing);
190 paint_.setLCDRenderText(enable_lcd_text);
191}
192
[email protected]cd8e5522011-12-15 00:24:10193void SkiaTextRenderer::SetTypeface(SkTypeface* typeface) {
194 paint_.setTypeface(typeface);
195}
196
197void SkiaTextRenderer::SetTextSize(int size) {
198 paint_.setTextSize(size);
199}
200
[email protected]7aba39c2012-01-10 16:10:27201void SkiaTextRenderer::SetFontStyle(int style) {
202 SkTypeface::Style skia_style = ConvertFontStyleToSkiaTypefaceStyle(style);
203 SkTypeface* current_typeface = paint_.getTypeface();
204
205 if (current_typeface->style() == skia_style)
206 return;
207
[email protected]ab96da42012-01-09 23:05:30208 SkAutoTUnref<SkTypeface> typeface(
[email protected]7aba39c2012-01-10 16:10:27209 SkTypeface::CreateFromTypeface(current_typeface, skia_style));
210 if (typeface.get()) {
211 // |paint_| adds its own ref. So don't |release()| it from the ref ptr here.
212 SetTypeface(typeface.get());
213 }
214}
215
216void SkiaTextRenderer::SetFont(const gfx::Font& font) {
217 SkTypeface::Style skia_style =
218 ConvertFontStyleToSkiaTypefaceStyle(font.GetStyle());
219 SkAutoTUnref<SkTypeface> typeface(
220 SkTypeface::CreateFromName(font.GetFontName().c_str(), skia_style));
[email protected]67b981562011-12-09 00:35:05221 if (typeface.get()) {
222 // |paint_| adds its own ref. So don't |release()| it from the ref ptr here.
[email protected]cd8e5522011-12-15 00:24:10223 SetTypeface(typeface.get());
[email protected]67b981562011-12-09 00:35:05224 }
[email protected]cd8e5522011-12-15 00:24:10225 SetTextSize(font.GetFontSize());
[email protected]67b981562011-12-09 00:35:05226}
227
228void SkiaTextRenderer::SetForegroundColor(SkColor foreground) {
229 paint_.setColor(foreground);
230}
231
[email protected]f308e5b12012-01-17 19:09:18232void SkiaTextRenderer::SetShader(SkShader* shader) {
233 paint_.setShader(shader);
234}
235
[email protected]67b981562011-12-09 00:35:05236void SkiaTextRenderer::DrawPosText(const SkPoint* pos,
237 const uint16* glyphs,
238 size_t glyph_count) {
239 size_t byte_length = glyph_count * sizeof(glyphs[0]);
240 canvas_skia_->drawPosText(&glyphs[0], byte_length, &pos[0], paint_);
241}
242
243// Draw underline and strike through text decorations.
244// Based on |SkCanvas::DrawTextDecorations()| and constants from:
245// third_party/skia/src/core/SkTextFormatParams.h
246void SkiaTextRenderer::DrawDecorations(int x, int y, int width,
[email protected]54d32cc2012-01-27 19:52:18247 const StyleRange& style) {
248 if (!style.underline && !style.strike && !style.diagonal_strike)
249 return;
250
[email protected]67b981562011-12-09 00:35:05251 // Fraction of the text size to lower a strike through below the baseline.
252 const SkScalar kStrikeThroughOffset = (-SK_Scalar1 * 6 / 21);
253 // Fraction of the text size to lower an underline below the baseline.
254 const SkScalar kUnderlineOffset = (SK_Scalar1 / 9);
255 // Fraction of the text size to use for a strike through or under-line.
256 const SkScalar kLineThickness = (SK_Scalar1 / 18);
[email protected]54d32cc2012-01-27 19:52:18257 // Fraction of the text size to use for a top margin of a diagonal strike.
258 const SkScalar kDiagonalStrikeThroughMarginOffset = (SK_Scalar1 / 4);
[email protected]67b981562011-12-09 00:35:05259
260 SkScalar text_size = paint_.getTextSize();
261 SkScalar height = SkScalarMul(text_size, kLineThickness);
262 SkRect r;
263
264 r.fLeft = x;
265 r.fRight = x + width;
266
[email protected]54d32cc2012-01-27 19:52:18267 if (style.underline) {
[email protected]67b981562011-12-09 00:35:05268 SkScalar offset = SkScalarMulAdd(text_size, kUnderlineOffset, y);
269 r.fTop = offset;
270 r.fBottom = offset + height;
271 canvas_skia_->drawRect(r, paint_);
272 }
[email protected]54d32cc2012-01-27 19:52:18273 if (style.strike) {
[email protected]67b981562011-12-09 00:35:05274 SkScalar offset = SkScalarMulAdd(text_size, kStrikeThroughOffset, y);
275 r.fTop = offset;
276 r.fBottom = offset + height;
277 canvas_skia_->drawRect(r, paint_);
278 }
[email protected]54d32cc2012-01-27 19:52:18279 if (style.diagonal_strike) {
280 SkScalar offset =
281 SkScalarMul(text_size, kDiagonalStrikeThroughMarginOffset);
282 SkPaint paint(paint_);
283 paint.setAntiAlias(true);
284 paint.setStyle(SkPaint::kFill_Style);
285 paint.setStrokeWidth(height);
286 canvas_skia_->drawLine(
287 SkIntToScalar(x), SkIntToScalar(y) - text_size + offset,
288 SkIntToScalar(x + width), SkIntToScalar(y),
289 paint);
290 }
[email protected]67b981562011-12-09 00:35:05291}
292
293} // namespace internal
294
295
[email protected]ff44d712011-07-25 08:42:52296StyleRange::StyleRange()
[email protected]8d901a82012-01-04 19:41:30297 : foreground(SK_ColorBLACK),
[email protected]7aba39c2012-01-10 16:10:27298 font_style(gfx::Font::NORMAL),
[email protected]ff44d712011-07-25 08:42:52299 strike(false),
[email protected]54d32cc2012-01-27 19:52:18300 diagonal_strike(false),
[email protected]8d901a82012-01-04 19:41:30301 underline(false) {
[email protected]ff44d712011-07-25 08:42:52302}
303
[email protected]8e42ba22011-08-04 21:47:08304RenderText::~RenderText() {
305}
306
[email protected]ff44d712011-07-25 08:42:52307void RenderText::SetText(const string16& text) {
[email protected]d3c6b0602011-09-07 19:26:06308 DCHECK(!composition_range_.IsValid());
[email protected]ff44d712011-07-25 08:42:52309 size_t old_text_length = text_.length();
310 text_ = text;
311
312 // Update the style ranges as needed.
313 if (text_.empty()) {
314 style_ranges_.clear();
315 } else if (style_ranges_.empty()) {
316 ApplyDefaultStyle();
317 } else if (text_.length() > old_text_length) {
318 style_ranges_.back().range.set_end(text_.length());
319 } else if (text_.length() < old_text_length) {
320 StyleRanges::iterator i;
321 for (i = style_ranges_.begin(); i != style_ranges_.end(); i++) {
322 if (i->range.start() >= text_.length()) {
[email protected]052ce6bb2011-10-14 19:48:20323 // Style ranges are sorted and non-overlapping, so all the subsequent
324 // style ranges should be out of text_.length() as well.
325 style_ranges_.erase(i, style_ranges_.end());
326 break;
[email protected]ff44d712011-07-25 08:42:52327 }
328 }
[email protected]052ce6bb2011-10-14 19:48:20329 // Since style ranges are sorted and non-overlapping, if there is a style
330 // range ends beyond text_.length, it must be the last one.
[email protected]ff44d712011-07-25 08:42:52331 style_ranges_.back().range.set_end(text_.length());
332 }
333#ifndef NDEBUG
334 CheckStyleRanges(style_ranges_, text_.length());
335#endif
[email protected]f6aaa0f2011-08-11 07:05:46336 cached_bounds_and_offset_valid_ = false;
[email protected]d3c6b0602011-09-07 19:26:06337
338 // Reset selection model. SetText should always followed by SetSelectionModel
339 // or SetCursorPosition in upper layer.
[email protected]d9990212012-03-13 01:09:31340 SetSelectionModel(SelectionModel());
[email protected]6002a4f32011-11-30 10:18:42341
[email protected]caa9e1e2012-03-13 20:47:04342 ResetLayout();
[email protected]ff44d712011-07-25 08:42:52343}
344
[email protected]f0ed8a2f2012-01-24 17:45:59345void RenderText::SetHorizontalAlignment(HorizontalAlignment alignment) {
346 if (horizontal_alignment_ != alignment) {
347 horizontal_alignment_ = alignment;
348 display_offset_ = Point();
349 cached_bounds_and_offset_valid_ = false;
350 }
351}
352
[email protected]8d901a82012-01-04 19:41:30353void RenderText::SetFontList(const FontList& font_list) {
354 font_list_ = font_list;
355 cached_bounds_and_offset_valid_ = false;
[email protected]caa9e1e2012-03-13 20:47:04356 ResetLayout();
[email protected]8d901a82012-01-04 19:41:30357}
358
[email protected]fdf481b2012-01-24 06:14:07359void RenderText::SetFontSize(int size) {
360 font_list_ = font_list_.DeriveFontListWithSize(size);
361 cached_bounds_and_offset_valid_ = false;
[email protected]caa9e1e2012-03-13 20:47:04362 ResetLayout();
[email protected]fdf481b2012-01-24 06:14:07363}
364
[email protected]f0ed8a2f2012-01-24 17:45:59365void RenderText::SetCursorEnabled(bool cursor_enabled) {
366 cursor_enabled_ = cursor_enabled;
367 cached_bounds_and_offset_valid_ = false;
368}
369
[email protected]8d901a82012-01-04 19:41:30370const Font& RenderText::GetFont() const {
371 return font_list_.GetFonts()[0];
372}
373
[email protected]0d717602011-08-30 06:21:14374void RenderText::ToggleInsertMode() {
375 insert_mode_ = !insert_mode_;
[email protected]f6aaa0f2011-08-11 07:05:46376 cached_bounds_and_offset_valid_ = false;
377}
378
[email protected]bec929c2012-03-02 06:23:50379void RenderText::SetObscured(bool obscured) {
380 if (obscured != obscured_) {
381 obscured_ = obscured;
382 cached_bounds_and_offset_valid_ = false;
[email protected]caa9e1e2012-03-13 20:47:04383 ResetLayout();
[email protected]bec929c2012-03-02 06:23:50384 }
385}
386
[email protected]f6aaa0f2011-08-11 07:05:46387void RenderText::SetDisplayRect(const Rect& r) {
388 display_rect_ = r;
389 cached_bounds_and_offset_valid_ = false;
[email protected]caa9e1e2012-03-13 20:47:04390 ResetLayout();
[email protected]8e42ba22011-08-04 21:47:08391}
392
[email protected]0d717602011-08-30 06:21:14393void RenderText::SetCursorPosition(size_t position) {
394 MoveCursorTo(position, false);
[email protected]ff44d712011-07-25 08:42:52395}
396
[email protected]d66009e2012-01-21 01:27:28397void RenderText::MoveCursor(BreakType break_type,
398 VisualCursorDirection direction,
399 bool select) {
[email protected]d9990212012-03-13 01:09:31400 SelectionModel position(cursor_position(), selection_model_.caret_affinity());
[email protected]ff44d712011-07-25 08:42:52401 // Cancelling a selection moves to the edge of the selection.
[email protected]d9990212012-03-13 01:09:31402 if (break_type != LINE_BREAK && !selection().is_empty() && !select) {
[email protected]d3c6b0602011-09-07 19:26:06403 SelectionModel selection_start = GetSelectionModelForSelectionStart();
[email protected]d66009e2012-01-21 01:27:28404 int start_x = GetCursorBounds(selection_start, true).x();
405 int cursor_x = GetCursorBounds(position, true).x();
406 // Use the selection start if it is left (when |direction| is CURSOR_LEFT)
407 // or right (when |direction| is CURSOR_RIGHT) of the selection end.
408 if (direction == CURSOR_RIGHT ? start_x > cursor_x : start_x < cursor_x)
[email protected]8e42ba22011-08-04 21:47:08409 position = selection_start;
[email protected]d66009e2012-01-21 01:27:28410 // For word breaks, use the nearest word boundary in the appropriate
411 // |direction|.
[email protected]ff44d712011-07-25 08:42:52412 if (break_type == WORD_BREAK)
[email protected]d66009e2012-01-21 01:27:28413 position = GetAdjacentSelectionModel(position, break_type, direction);
[email protected]ff44d712011-07-25 08:42:52414 } else {
[email protected]d66009e2012-01-21 01:27:28415 position = GetAdjacentSelectionModel(position, break_type, direction);
[email protected]ff44d712011-07-25 08:42:52416 }
[email protected]ec7f48d2011-08-09 03:48:50417 if (select)
[email protected]d9990212012-03-13 01:09:31418 position.set_selection_start(selection().start());
[email protected]8e42ba22011-08-04 21:47:08419 MoveCursorTo(position);
[email protected]ff44d712011-07-25 08:42:52420}
421
[email protected]f9a221b2011-12-10 20:25:38422bool RenderText::MoveCursorTo(const SelectionModel& model) {
[email protected]0d717602011-08-30 06:21:14423 // Enforce valid selection model components.
[email protected]d9990212012-03-13 01:09:31424 size_t text_length = text().length();
425 ui::Range range(std::min(model.selection().start(), text_length),
426 std::min(model.caret_pos(), text_length));
[email protected]0d717602011-08-30 06:21:14427 // The current model only supports caret positions at valid character indices.
[email protected]d9990212012-03-13 01:09:31428 if (!IsCursorablePosition(range.start()) ||
429 !IsCursorablePosition(range.end()))
[email protected]53c0b1b2011-09-21 20:32:29430 return false;
[email protected]d9990212012-03-13 01:09:31431 SelectionModel sel(range, model.caret_affinity());
432 bool changed = sel != selection_model_;
[email protected]0d717602011-08-30 06:21:14433 SetSelectionModel(sel);
[email protected]ff44d712011-07-25 08:42:52434 return changed;
435}
436
[email protected]7b3cb4b22011-08-02 18:36:40437bool RenderText::MoveCursorTo(const Point& point, bool select) {
[email protected]d9990212012-03-13 01:09:31438 SelectionModel position = FindCursorPosition(point);
[email protected]8e42ba22011-08-04 21:47:08439 if (select)
[email protected]d9990212012-03-13 01:09:31440 position.set_selection_start(selection().start());
441 return MoveCursorTo(position);
[email protected]ff44d712011-07-25 08:42:52442}
443
[email protected]67e85512011-10-12 20:03:45444bool RenderText::SelectRange(const ui::Range& range) {
[email protected]d9990212012-03-13 01:09:31445 ui::Range sel(std::min(range.start(), text().length()),
446 std::min(range.end(), text().length()));
447 if (!IsCursorablePosition(sel.start()) || !IsCursorablePosition(sel.end()))
[email protected]67e85512011-10-12 20:03:45448 return false;
[email protected]d9990212012-03-13 01:09:31449 LogicalCursorDirection affinity =
450 (sel.is_reversed() || sel.is_empty()) ? CURSOR_FORWARD : CURSOR_BACKWARD;
451 SetSelectionModel(SelectionModel(sel, affinity));
[email protected]67e85512011-10-12 20:03:45452 return true;
453}
454
[email protected]8e42ba22011-08-04 21:47:08455bool RenderText::IsPointInSelection(const Point& point) {
[email protected]d9990212012-03-13 01:09:31456 if (selection().is_empty())
[email protected]0d717602011-08-30 06:21:14457 return false;
[email protected]d9990212012-03-13 01:09:31458 SelectionModel cursor = FindCursorPosition(point);
459 return RangeContainsCaret(
460 selection(), cursor.caret_pos(), cursor.caret_affinity());
[email protected]ff44d712011-07-25 08:42:52461}
462
463void RenderText::ClearSelection() {
[email protected]d9990212012-03-13 01:09:31464 SetSelectionModel(SelectionModel(cursor_position(),
465 selection_model_.caret_affinity()));
[email protected]ff44d712011-07-25 08:42:52466}
467
468void RenderText::SelectAll() {
[email protected]d9990212012-03-13 01:09:31469 SelectionModel all;
470 if (GetTextDirection() == base::i18n::LEFT_TO_RIGHT)
471 all = SelectionModel(ui::Range(0, text().length()), CURSOR_FORWARD);
472 else
473 all = SelectionModel(ui::Range(text().length(), 0), CURSOR_BACKWARD);
474 SetSelectionModel(all);
[email protected]ff44d712011-07-25 08:42:52475}
476
477void RenderText::SelectWord() {
[email protected]bec929c2012-03-02 06:23:50478 if (obscured_) {
479 SelectAll();
480 return;
481 }
482
[email protected]d9990212012-03-13 01:09:31483 size_t cursor_pos = cursor_position();
[email protected]ff44d712011-07-25 08:42:52484
[email protected]53c0b1b2011-09-21 20:32:29485 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
486 bool success = iter.Init();
487 DCHECK(success);
488 if (!success)
489 return;
490
[email protected]d9990212012-03-13 01:09:31491 size_t selection_start = cursor_pos;
[email protected]53c0b1b2011-09-21 20:32:29492 for (; selection_start != 0; --selection_start) {
493 if (iter.IsStartOfWord(selection_start) ||
494 iter.IsEndOfWord(selection_start))
[email protected]ff44d712011-07-25 08:42:52495 break;
496 }
497
[email protected]d9990212012-03-13 01:09:31498 if (selection_start == cursor_pos)
499 ++cursor_pos;
[email protected]53c0b1b2011-09-21 20:32:29500
[email protected]d9990212012-03-13 01:09:31501 for (; cursor_pos < text().length(); ++cursor_pos)
502 if (iter.IsEndOfWord(cursor_pos) || iter.IsStartOfWord(cursor_pos))
[email protected]ff44d712011-07-25 08:42:52503 break;
[email protected]ff44d712011-07-25 08:42:52504
[email protected]0d717602011-08-30 06:21:14505 MoveCursorTo(selection_start, false);
[email protected]d9990212012-03-13 01:09:31506 MoveCursorTo(cursor_pos, true);
[email protected]ff44d712011-07-25 08:42:52507}
508
509const ui::Range& RenderText::GetCompositionRange() const {
510 return composition_range_;
511}
512
513void RenderText::SetCompositionRange(const ui::Range& composition_range) {
514 CHECK(!composition_range.IsValid() ||
515 ui::Range(0, text_.length()).Contains(composition_range));
516 composition_range_.set_end(composition_range.end());
517 composition_range_.set_start(composition_range.start());
[email protected]caa9e1e2012-03-13 20:47:04518 ResetLayout();
[email protected]ff44d712011-07-25 08:42:52519}
520
[email protected]c8636672011-12-29 05:07:57521void RenderText::ApplyStyleRange(const StyleRange& style_range) {
[email protected]ff44d712011-07-25 08:42:52522 const ui::Range& new_range = style_range.range;
523 if (!new_range.IsValid() || new_range.is_empty())
524 return;
525 CHECK(!new_range.is_reversed());
526 CHECK(ui::Range(0, text_.length()).Contains(new_range));
[email protected]8e42ba22011-08-04 21:47:08527 ApplyStyleRangeImpl(&style_ranges_, style_range);
[email protected]ff44d712011-07-25 08:42:52528#ifndef NDEBUG
529 CheckStyleRanges(style_ranges_, text_.length());
530#endif
[email protected]f6aaa0f2011-08-11 07:05:46531 // TODO(xji): only invalidate if font or underline changes.
532 cached_bounds_and_offset_valid_ = false;
[email protected]caa9e1e2012-03-13 20:47:04533 ResetLayout();
[email protected]ff44d712011-07-25 08:42:52534}
535
536void RenderText::ApplyDefaultStyle() {
537 style_ranges_.clear();
538 StyleRange style = StyleRange(default_style_);
539 style.range.set_end(text_.length());
540 style_ranges_.push_back(style);
[email protected]f6aaa0f2011-08-11 07:05:46541 cached_bounds_and_offset_valid_ = false;
[email protected]caa9e1e2012-03-13 20:47:04542 ResetLayout();
[email protected]ff44d712011-07-25 08:42:52543}
544
[email protected]d66009e2012-01-21 01:27:28545VisualCursorDirection RenderText::GetVisualDirectionOfLogicalEnd() {
546 return GetTextDirection() == base::i18n::LEFT_TO_RIGHT ?
547 CURSOR_RIGHT : CURSOR_LEFT;
[email protected]ff44d712011-07-25 08:42:52548}
549
[email protected]7b3cb4b22011-08-02 18:36:40550void RenderText::Draw(Canvas* canvas) {
[email protected]592a7ad2011-12-14 05:57:53551 TRACE_EVENT0("gfx", "RenderText::Draw");
552 {
553 TRACE_EVENT0("gfx", "RenderText::EnsureLayout");
554 EnsureLayout();
555 }
[email protected]ff44d712011-07-25 08:42:52556
[email protected]f308e5b12012-01-17 19:09:18557 canvas->Save();
558 canvas->ClipRect(display_rect());
[email protected]1a353f12012-01-18 04:20:56559
560 if (!text().empty())
561 DrawSelection(canvas);
562
563 DrawCursor(canvas);
564
[email protected]6002a4f32011-11-30 10:18:42565 if (!text().empty()) {
[email protected]592a7ad2011-12-14 05:57:53566 TRACE_EVENT0("gfx", "RenderText::Draw draw text");
[email protected]6002a4f32011-11-30 10:18:42567 DrawVisualText(canvas);
[email protected]ff44d712011-07-25 08:42:52568 }
[email protected]f308e5b12012-01-17 19:09:18569 canvas->Restore();
[email protected]ff44d712011-07-25 08:42:52570}
571
[email protected]d9990212012-03-13 01:09:31572Rect RenderText::GetCursorBounds(const SelectionModel& caret,
573 bool insert_mode) {
574 EnsureLayout();
575
576 size_t caret_pos = caret.caret_pos();
577 // In overtype mode, ignore the affinity and always indicate that we will
578 // overtype the next character.
579 LogicalCursorDirection caret_affinity =
580 insert_mode ? caret.caret_affinity() : CURSOR_FORWARD;
581 int x = 0, width = 0, height = 0;
582 if (caret_pos == (caret_affinity == CURSOR_BACKWARD ? 0 : text().length())) {
583 // The caret is attached to the boundary. Always return a zero-width caret,
584 // since there is nothing to overtype.
585 Size size = GetStringSize();
586 if ((GetTextDirection() == base::i18n::RIGHT_TO_LEFT) == (caret_pos == 0))
587 x = size.width();
588 height = size.height();
589 } else {
590 size_t grapheme_start = (caret_affinity == CURSOR_FORWARD) ?
591 caret_pos : IndexOfAdjacentGrapheme(caret_pos, CURSOR_BACKWARD);
592 ui::Range xspan;
593 GetGlyphBounds(grapheme_start, &xspan, &height);
594 if (insert_mode) {
595 x = (caret_affinity == CURSOR_BACKWARD) ? xspan.end() : xspan.start();
596 } else { // overtype mode
597 x = xspan.GetMin();
598 width = xspan.length();
599 }
600 }
601 height = std::min(height, display_rect().height());
602 int y = (display_rect().height() - height) / 2;
603 return Rect(ToViewPoint(Point(x, y)), Size(width, height));
604}
605
[email protected]f6aaa0f2011-08-11 07:05:46606const Rect& RenderText::GetUpdatedCursorBounds() {
607 UpdateCachedBoundsAndOffset();
[email protected]8e42ba22011-08-04 21:47:08608 return cursor_bounds_;
609}
610
[email protected]04b7bf322011-10-03 19:08:46611SelectionModel RenderText::GetSelectionModelForSelectionStart() {
[email protected]d9990212012-03-13 01:09:31612 const ui::Range& sel = selection();
613 if (sel.is_empty())
614 return selection_model_;
615 return SelectionModel(sel.start(),
616 sel.is_reversed() ? CURSOR_BACKWARD : CURSOR_FORWARD);
[email protected]04b7bf322011-10-03 19:08:46617}
618
[email protected]8e42ba22011-08-04 21:47:08619RenderText::RenderText()
[email protected]f0ed8a2f2012-01-24 17:45:59620 : horizontal_alignment_(base::i18n::IsRTL() ? ALIGN_RIGHT : ALIGN_LEFT),
621 cursor_enabled_(true),
622 cursor_visible_(false),
[email protected]8e42ba22011-08-04 21:47:08623 insert_mode_(true),
[email protected]913a1ef2012-02-29 08:40:34624 cursor_color_(kDefaultCursorColor),
[email protected]5abe6302011-12-20 23:44:32625 focused_(false),
[email protected]d3c6b0602011-09-07 19:26:06626 composition_range_(ui::Range::InvalidRange()),
[email protected]bec929c2012-03-02 06:23:50627 obscured_(false),
[email protected]f308e5b12012-01-17 19:09:18628 fade_head_(false),
629 fade_tail_(false),
[email protected]32d582d2012-02-28 21:50:22630 background_is_transparent_(false),
[email protected]f6aaa0f2011-08-11 07:05:46631 cached_bounds_and_offset_valid_(false) {
632}
633
634const Point& RenderText::GetUpdatedDisplayOffset() {
635 UpdateCachedBoundsAndOffset();
636 return display_offset_;
[email protected]8e42ba22011-08-04 21:47:08637}
638
[email protected]d66009e2012-01-21 01:27:28639SelectionModel RenderText::GetAdjacentSelectionModel(
640 const SelectionModel& current,
641 BreakType break_type,
642 VisualCursorDirection direction) {
643 EnsureLayout();
644
645 if (break_type == LINE_BREAK || text().empty())
646 return EdgeSelectionModel(direction);
[email protected]ec7f48d2011-08-09 03:48:50647 if (break_type == CHARACTER_BREAK)
[email protected]d66009e2012-01-21 01:27:28648 return AdjacentCharSelectionModel(current, direction);
649 DCHECK(break_type == WORD_BREAK);
650 return AdjacentWordSelectionModel(current, direction);
[email protected]0d717602011-08-30 06:21:14651}
652
[email protected]d9990212012-03-13 01:09:31653SelectionModel RenderText::EdgeSelectionModel(
654 VisualCursorDirection direction) {
655 if (direction == GetVisualDirectionOfLogicalEnd())
656 return SelectionModel(text().length(), CURSOR_FORWARD);
657 return SelectionModel(0, CURSOR_BACKWARD);
658}
[email protected]6002a4f32011-11-30 10:18:42659
[email protected]d9990212012-03-13 01:09:31660void RenderText::SetSelectionModel(const SelectionModel& model) {
661 DCHECK_LE(model.selection().GetMax(), text().length());
662 selection_model_ = model;
[email protected]6002a4f32011-11-30 10:18:42663 cached_bounds_and_offset_valid_ = false;
[email protected]0d717602011-08-30 06:21:14664}
665
[email protected]bec929c2012-03-02 06:23:50666string16 RenderText::GetDisplayText() const {
667 if (!obscured_)
668 return text_;
669 size_t obscured_text_length =
670 static_cast<size_t>(ui::UTF16IndexToOffset(text_, 0, text_.length()));
671 return string16(obscured_text_length, kPasswordReplacementChar);
672}
673
[email protected]8e42ba22011-08-04 21:47:08674void RenderText::ApplyCompositionAndSelectionStyles(
[email protected]1a353f12012-01-18 04:20:56675 StyleRanges* style_ranges) {
[email protected]8e42ba22011-08-04 21:47:08676 // TODO(msw): This pattern ought to be reconsidered; what about composition
677 // and selection overlaps, retain existing local style features?
678 // Apply a composition style override to a copy of the style ranges.
679 if (composition_range_.IsValid() && !composition_range_.is_empty()) {
680 StyleRange composition_style(default_style_);
681 composition_style.underline = true;
[email protected]d9990212012-03-13 01:09:31682 composition_style.range = composition_range_;
[email protected]8e42ba22011-08-04 21:47:08683 ApplyStyleRangeImpl(style_ranges, composition_style);
684 }
685 // Apply a selection style override to a copy of the style ranges.
[email protected]d9990212012-03-13 01:09:31686 if (!selection().is_empty()) {
[email protected]8e42ba22011-08-04 21:47:08687 StyleRange selection_style(default_style_);
[email protected]721ea922012-01-23 21:13:33688 selection_style.foreground = NativeTheme::instance()->GetSystemColor(
689 NativeTheme::kColorId_TextfieldSelectionColor);
[email protected]d9990212012-03-13 01:09:31690 selection_style.range = ui::Range(selection().GetMin(),
691 selection().GetMax());
[email protected]8e42ba22011-08-04 21:47:08692 ApplyStyleRangeImpl(style_ranges, selection_style);
693 }
[email protected]1a353f12012-01-18 04:20:56694 // Apply replacement-mode style override to a copy of the style ranges.
695 //
696 // TODO(xji): NEED TO FIX FOR WINDOWS ASAP. Windows call this function (to
697 // apply styles) in ItemizeLogicalText(). In order for the cursor's underline
698 // character to be drawn correctly, we will need to re-layout the text. It's
699 // not practical to do layout on every cursor blink. We need to fix Windows
700 // port to apply styles during drawing phase like Linux port does.
701 // http://crbug.com/110109
702 if (!insert_mode_ && cursor_visible() && focused()) {
703 StyleRange replacement_mode_style(default_style_);
[email protected]721ea922012-01-23 21:13:33704 replacement_mode_style.foreground = NativeTheme::instance()->GetSystemColor(
705 NativeTheme::kColorId_TextfieldSelectionColor);
[email protected]d9990212012-03-13 01:09:31706 size_t cursor = cursor_position();
[email protected]1a353f12012-01-18 04:20:56707 replacement_mode_style.range.set_start(cursor);
[email protected]d66009e2012-01-21 01:27:28708 replacement_mode_style.range.set_end(
709 IndexOfAdjacentGrapheme(cursor, CURSOR_FORWARD));
[email protected]1a353f12012-01-18 04:20:56710 ApplyStyleRangeImpl(style_ranges, replacement_mode_style);
711 }
[email protected]ff44d712011-07-25 08:42:52712}
713
[email protected]f0ed8a2f2012-01-24 17:45:59714Point RenderText::GetTextOrigin() {
715 Point origin = display_rect().origin();
716 origin = origin.Add(GetUpdatedDisplayOffset());
717 origin = origin.Add(GetAlignmentOffset());
718 return origin;
719}
720
[email protected]0d717602011-08-30 06:21:14721Point RenderText::ToTextPoint(const Point& point) {
[email protected]f0ed8a2f2012-01-24 17:45:59722 return point.Subtract(GetTextOrigin());
[email protected]0d717602011-08-30 06:21:14723}
724
725Point RenderText::ToViewPoint(const Point& point) {
[email protected]f0ed8a2f2012-01-24 17:45:59726 return point.Add(GetTextOrigin());
727}
728
729int RenderText::GetContentWidth() {
[email protected]d9990212012-03-13 01:09:31730 return GetStringSize().width() + (cursor_enabled_ ? 1 : 0);
[email protected]f0ed8a2f2012-01-24 17:45:59731}
732
733Point RenderText::GetAlignmentOffset() {
734 if (horizontal_alignment() != ALIGN_LEFT) {
735 int x_offset = display_rect().width() - GetContentWidth();
736 if (horizontal_alignment() == ALIGN_CENTER)
737 x_offset /= 2;
738 return Point(x_offset, 0);
739 }
740 return Point();
[email protected]0d717602011-08-30 06:21:14741}
742
[email protected]67b981562011-12-09 00:35:05743Point RenderText::GetOriginForSkiaDrawing() {
[email protected]f0ed8a2f2012-01-24 17:45:59744 Point origin(GetTextOrigin());
[email protected]67b981562011-12-09 00:35:05745 // TODO(msw): Establish a vertical baseline for strings of mixed font heights.
[email protected]8d901a82012-01-04 19:41:30746 const Font& font = GetFont();
[email protected]5f4fd30e2012-01-12 02:19:33747 int height = font.GetHeight();
[email protected]fdf481b2012-01-24 06:14:07748 DCHECK_LE(height, display_rect().height());
[email protected]67b981562011-12-09 00:35:05749 // Center the text vertically in the display area.
750 origin.Offset(0, (display_rect().height() - height) / 2);
751 // Offset by the font size to account for Skia expecting y to be the bottom.
752 origin.Offset(0, font.GetFontSize());
753 return origin;
754}
755
[email protected]f0ed8a2f2012-01-24 17:45:59756void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) {
[email protected]f308e5b12012-01-17 19:09:18757 if (!fade_head() && !fade_tail())
[email protected]f0ed8a2f2012-01-24 17:45:59758 return;
[email protected]f308e5b12012-01-17 19:09:18759
[email protected]d9990212012-03-13 01:09:31760 const int text_width = GetStringSize().width();
[email protected]f308e5b12012-01-17 19:09:18761 const int display_width = display_rect().width();
762
763 // If the text fits as-is, no need to fade.
764 if (text_width <= display_width)
[email protected]f0ed8a2f2012-01-24 17:45:59765 return;
[email protected]f308e5b12012-01-17 19:09:18766
767 int gradient_width = CalculateFadeGradientWidth(GetFont(), display_width);
768 if (gradient_width == 0)
[email protected]f0ed8a2f2012-01-24 17:45:59769 return;
[email protected]f308e5b12012-01-17 19:09:18770
771 bool fade_left = fade_head();
772 bool fade_right = fade_tail();
773 // Under RTL, |fade_right| == |fade_head|.
774 if (GetTextDirection() == base::i18n::RIGHT_TO_LEFT)
775 std::swap(fade_left, fade_right);
776
777 gfx::Rect solid_part = display_rect();
778 gfx::Rect left_part;
779 gfx::Rect right_part;
780 if (fade_left) {
781 left_part = solid_part;
782 left_part.Inset(0, 0, solid_part.width() - gradient_width, 0);
783 solid_part.Inset(gradient_width, 0, 0, 0);
784 }
785 if (fade_right) {
786 right_part = solid_part;
787 right_part.Inset(solid_part.width() - gradient_width, 0, 0, 0);
788 solid_part.Inset(0, 0, gradient_width, 0);
789 }
790
[email protected]f308e5b12012-01-17 19:09:18791 gfx::Rect text_rect = display_rect();
[email protected]f0ed8a2f2012-01-24 17:45:59792 text_rect.Inset(GetAlignmentOffset().x(), 0, 0, 0);
[email protected]f308e5b12012-01-17 19:09:18793
794 const SkColor color = default_style().foreground;
795 SkAutoTUnref<SkShader> shader(
796 CreateFadeShader(text_rect, left_part, right_part, color));
797 if (shader.get()) {
798 // |renderer| adds its own ref. So don't |release()| it from the ref ptr.
799 renderer->SetShader(shader.get());
800 }
[email protected]f308e5b12012-01-17 19:09:18801}
802
[email protected]d9990212012-03-13 01:09:31803// static
804bool RenderText::RangeContainsCaret(const ui::Range& range,
805 size_t caret_pos,
806 LogicalCursorDirection caret_affinity) {
807 // NB: exploits unsigned wraparound (WG14/N1124 section 6.2.5 paragraph 9).
808 size_t adjacent = (caret_affinity == CURSOR_BACKWARD) ?
809 caret_pos - 1 : caret_pos + 1;
810 return range.Contains(ui::Range(caret_pos, adjacent));
811}
812
[email protected]0d717602011-08-30 06:21:14813void RenderText::MoveCursorTo(size_t position, bool select) {
814 size_t cursor = std::min(position, text().length());
[email protected]d9990212012-03-13 01:09:31815 if (IsCursorablePosition(cursor))
816 SetSelectionModel(SelectionModel(
817 ui::Range(select ? selection().start() : cursor, cursor),
818 (cursor == 0) ? CURSOR_FORWARD : CURSOR_BACKWARD));
[email protected]8e42ba22011-08-04 21:47:08819}
820
[email protected]f6aaa0f2011-08-11 07:05:46821void RenderText::UpdateCachedBoundsAndOffset() {
822 if (cached_bounds_and_offset_valid_)
823 return;
[email protected]f0ed8a2f2012-01-24 17:45:59824
[email protected]f6aaa0f2011-08-11 07:05:46825 // First, set the valid flag true to calculate the current cursor bounds using
826 // the stale |display_offset_|. Applying |delta_offset| at the end of this
827 // function will set |cursor_bounds_| and |display_offset_| to correct values.
828 cached_bounds_and_offset_valid_ = true;
829 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_);
[email protected]f0ed8a2f2012-01-24 17:45:59830
[email protected]8e42ba22011-08-04 21:47:08831 // Update |display_offset_| to ensure the current cursor is visible.
[email protected]f0ed8a2f2012-01-24 17:45:59832 const int display_width = display_rect_.width();
833 const int content_width = GetContentWidth();
834
[email protected]f6aaa0f2011-08-11 07:05:46835 int delta_offset = 0;
[email protected]f0ed8a2f2012-01-24 17:45:59836 if (content_width <= display_width || !cursor_enabled()) {
837 // Don't pan if the text fits in the display width or when the cursor is
838 // disabled.
[email protected]f6aaa0f2011-08-11 07:05:46839 delta_offset = -display_offset_.x();
[email protected]65788132011-09-07 20:20:23840 } else if (cursor_bounds_.right() >= display_rect_.right()) {
[email protected]d3c6b0602011-09-07 19:26:06841 // TODO(xji): when the character overflow is a RTL character, currently, if
842 // we pan cursor at the rightmost position, the entered RTL character is not
843 // displayed. Should pan cursor to show the last logical characters.
844 //
[email protected]8e42ba22011-08-04 21:47:08845 // Pan to show the cursor when it overflows to the right,
[email protected]65788132011-09-07 20:20:23846 delta_offset = display_rect_.right() - cursor_bounds_.right() - 1;
[email protected]f6aaa0f2011-08-11 07:05:46847 } else if (cursor_bounds_.x() < display_rect_.x()) {
[email protected]d3c6b0602011-09-07 19:26:06848 // TODO(xji): have similar problem as above when overflow character is a
849 // LTR character.
850 //
[email protected]8e42ba22011-08-04 21:47:08851 // Pan to show the cursor when it overflows to the left.
[email protected]f6aaa0f2011-08-11 07:05:46852 delta_offset = display_rect_.x() - cursor_bounds_.x();
[email protected]f0ed8a2f2012-01-24 17:45:59853 } else if (display_offset_.x() != 0) {
854 // Reduce the pan offset to show additional overflow text when the display
855 // width increases.
856 const int negate_rtl = horizontal_alignment_ == ALIGN_RIGHT ? -1 : 1;
857 const int offset = negate_rtl * display_offset_.x();
858 if (display_width > (content_width + offset))
859 delta_offset = negate_rtl * (display_width - (content_width + offset));
[email protected]8e42ba22011-08-04 21:47:08860 }
[email protected]f0ed8a2f2012-01-24 17:45:59861
[email protected]f6aaa0f2011-08-11 07:05:46862 display_offset_.Offset(delta_offset, 0);
863 cursor_bounds_.Offset(delta_offset, 0);
[email protected]ff44d712011-07-25 08:42:52864}
865
[email protected]6002a4f32011-11-30 10:18:42866void RenderText::DrawSelection(Canvas* canvas) {
[email protected]d9990212012-03-13 01:09:31867 std::vector<Rect> sel = GetSubstringBounds(selection());
[email protected]721ea922012-01-23 21:13:33868 NativeTheme::ColorId color_id = focused() ?
869 NativeTheme::kColorId_TextfieldSelectionBackgroundFocused :
870 NativeTheme::kColorId_TextfieldSelectionBackgroundUnfocused;
871 SkColor color = NativeTheme::instance()->GetSystemColor(color_id);
[email protected]6002a4f32011-11-30 10:18:42872 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i)
[email protected]b8bdc422012-02-01 21:44:40873 canvas->FillRect(*i, color);
[email protected]6002a4f32011-11-30 10:18:42874}
875
876void RenderText::DrawCursor(Canvas* canvas) {
877 // Paint cursor. Replace cursor is drawn as rectangle for now.
878 // TODO(msw): Draw a better cursor with a better indication of association.
[email protected]f0ed8a2f2012-01-24 17:45:59879 if (cursor_enabled() && cursor_visible() && focused()) {
[email protected]1a353f12012-01-18 04:20:56880 const Rect& bounds = GetUpdatedCursorBounds();
881 if (bounds.width() != 0)
[email protected]913a1ef2012-02-29 08:40:34882 canvas->FillRect(bounds, cursor_color_);
[email protected]1a353f12012-01-18 04:20:56883 else
[email protected]913a1ef2012-02-29 08:40:34884 canvas->DrawRect(bounds, cursor_color_);
[email protected]1a353f12012-01-18 04:20:56885 }
[email protected]6002a4f32011-11-30 10:18:42886}
887
[email protected]ff44d712011-07-25 08:42:52888} // namespace gfx