blob: b3213643dc6159b4ea0864b2d89ea6bfff296a91 [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
9#include "base/i18n/break_iterator.h"
10#include "base/logging.h"
11#include "base/stl_util.h"
[email protected]67b981562011-12-09 00:35:0512#include "third_party/skia/include/core/SkTypeface.h"
[email protected]f308e5b12012-01-17 19:09:1813#include "third_party/skia/include/effects/SkGradientShader.h"
[email protected]bec929c2012-03-02 06:23:5014#include "ui/base/text/utf16_indexing.h"
[email protected]ff44d712011-07-25 08:42:5215#include "ui/gfx/canvas.h"
[email protected]0834e1b12012-04-11 02:23:5616#include "ui/gfx/insets.h"
[email protected]40be2012012-05-14 22:34:4517#include "ui/gfx/skia_util.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]311a09d2012-05-14 20:38:1729// Default color used for drawing selection text.
30const SkColor kDefaultSelectionColor = SK_ColorBLACK;
31
32// Default color used for drawing selection background.
33const SkColor kDefaultSelectionBackgroundColor = SK_ColorGRAY;
34
[email protected]ff44d712011-07-25 08:42:5235#ifndef NDEBUG
36// Check StyleRanges invariant conditions: sorted and non-overlapping ranges.
37void CheckStyleRanges(const gfx::StyleRanges& style_ranges, size_t length) {
38 if (length == 0) {
39 DCHECK(style_ranges.empty()) << "Style ranges exist for empty text.";
40 return;
41 }
42 for (gfx::StyleRanges::size_type i = 0; i < style_ranges.size() - 1; i++) {
43 const ui::Range& former = style_ranges[i].range;
44 const ui::Range& latter = style_ranges[i + 1].range;
[email protected]9a55b8c92012-05-17 20:53:0845 DCHECK(!former.is_empty()) << "Empty range at " << i << ":" <<
46 former.ToString();
47 DCHECK(former.IsValid()) << "Invalid range at " << i << ":" <<
48 former.ToString();
49 DCHECK(!former.is_reversed()) << "Reversed range at " << i << ":" <<
50 former.ToString();
[email protected]ff44d712011-07-25 08:42:5251 DCHECK(former.end() == latter.start()) << "Ranges gap/overlap/unsorted." <<
[email protected]9a55b8c92012-05-17 20:53:0852 "former:" << former.ToString() << ", latter:" << latter.ToString();
[email protected]ff44d712011-07-25 08:42:5253 }
54 const gfx::StyleRange& end_style = *style_ranges.rbegin();
55 DCHECK(!end_style.range.is_empty()) << "Empty range at end.";
56 DCHECK(end_style.range.IsValid()) << "Invalid range at end.";
57 DCHECK(!end_style.range.is_reversed()) << "Reversed range at end.";
58 DCHECK(end_style.range.end() == length) << "Style and text length mismatch.";
59}
60#endif
61
[email protected]8e42ba22011-08-04 21:47:0862void ApplyStyleRangeImpl(gfx::StyleRanges* style_ranges,
[email protected]c8636672011-12-29 05:07:5763 const gfx::StyleRange& style_range) {
[email protected]ff44d712011-07-25 08:42:5264 const ui::Range& new_range = style_range.range;
65 // Follow StyleRanges invariant conditions: sorted and non-overlapping ranges.
66 gfx::StyleRanges::iterator i;
[email protected]8e42ba22011-08-04 21:47:0867 for (i = style_ranges->begin(); i != style_ranges->end();) {
[email protected]ff44d712011-07-25 08:42:5268 if (i->range.end() < new_range.start()) {
69 i++;
70 } else if (i->range.start() == new_range.end()) {
71 break;
72 } else if (new_range.Contains(i->range)) {
[email protected]8e42ba22011-08-04 21:47:0873 i = style_ranges->erase(i);
74 if (i == style_ranges->end())
[email protected]ff44d712011-07-25 08:42:5275 break;
76 } else if (i->range.start() < new_range.start() &&
77 i->range.end() > new_range.end()) {
78 // Split the current style into two styles.
79 gfx::StyleRange split_style = gfx::StyleRange(*i);
80 split_style.range.set_end(new_range.start());
[email protected]8e42ba22011-08-04 21:47:0881 i = style_ranges->insert(i, split_style) + 1;
[email protected]ff44d712011-07-25 08:42:5282 i->range.set_start(new_range.end());
83 break;
84 } else if (i->range.start() < new_range.start()) {
85 i->range.set_end(new_range.start());
86 i++;
87 } else if (i->range.end() > new_range.end()) {
88 i->range.set_start(new_range.end());
89 break;
[email protected]f9a221b2011-12-10 20:25:3890 } else {
[email protected]ff44d712011-07-25 08:42:5291 NOTREACHED();
[email protected]f9a221b2011-12-10 20:25:3892 }
[email protected]ff44d712011-07-25 08:42:5293 }
94 // Add the new range in its sorted location.
[email protected]8e42ba22011-08-04 21:47:0895 style_ranges->insert(i, style_range);
[email protected]ff44d712011-07-25 08:42:5296}
97
[email protected]7aba39c2012-01-10 16:10:2798// Converts |gfx::Font::FontStyle| flags to |SkTypeface::Style| flags.
99SkTypeface::Style ConvertFontStyleToSkiaTypefaceStyle(int font_style) {
100 int skia_style = SkTypeface::kNormal;
101 if (font_style & gfx::Font::BOLD)
102 skia_style |= SkTypeface::kBold;
103 if (font_style & gfx::Font::ITALIC)
104 skia_style |= SkTypeface::kItalic;
105 return static_cast<SkTypeface::Style>(skia_style);
106}
107
[email protected]f308e5b12012-01-17 19:09:18108// Given |font| and |display_width|, returns the width of the fade gradient.
109int CalculateFadeGradientWidth(const gfx::Font& font, int display_width) {
110 // Fade in/out about 2.5 characters of the beginning/end of the string.
111 // The .5 here is helpful if one of the characters is a space.
112 // Use a quarter of the display width if the display width is very short.
113 const int average_character_width = font.GetAverageCharacterWidth();
114 const double gradient_width = std::min(average_character_width * 2.5,
115 display_width / 4.0);
116 DCHECK_GE(gradient_width, 0.0);
117 return static_cast<int>(floor(gradient_width + 0.5));
118}
119
120// Appends to |positions| and |colors| values corresponding to the fade over
121// |fade_rect| from color |c0| to color |c1|.
122void AddFadeEffect(const gfx::Rect& text_rect,
123 const gfx::Rect& fade_rect,
124 SkColor c0,
125 SkColor c1,
126 std::vector<SkScalar>* positions,
127 std::vector<SkColor>* colors) {
128 const SkScalar left = static_cast<SkScalar>(fade_rect.x() - text_rect.x());
129 const SkScalar width = static_cast<SkScalar>(fade_rect.width());
130 const SkScalar p0 = left / text_rect.width();
131 const SkScalar p1 = (left + width) / text_rect.width();
132 // Prepend 0.0 to |positions|, as required by Skia.
133 if (positions->empty() && p0 != 0.0) {
134 positions->push_back(0.0);
135 colors->push_back(c0);
136 }
137 positions->push_back(p0);
138 colors->push_back(c0);
139 positions->push_back(p1);
140 colors->push_back(c1);
141}
142
143// Creates a SkShader to fade the text, with |left_part| specifying the left
144// fade effect, if any, and |right_part| specifying the right fade effect.
145SkShader* CreateFadeShader(const gfx::Rect& text_rect,
146 const gfx::Rect& left_part,
147 const gfx::Rect& right_part,
148 SkColor color) {
149 // Fade alpha of 51/255 corresponds to a fade of 0.2 of the original color.
150 const SkColor fade_color = SkColorSetA(color, 51);
[email protected]f308e5b12012-01-17 19:09:18151 std::vector<SkScalar> positions;
152 std::vector<SkColor> colors;
153
154 if (!left_part.IsEmpty())
155 AddFadeEffect(text_rect, left_part, fade_color, color,
156 &positions, &colors);
157 if (!right_part.IsEmpty())
158 AddFadeEffect(text_rect, right_part, color, fade_color,
159 &positions, &colors);
160 DCHECK(!positions.empty());
161
162 // Terminate |positions| with 1.0, as required by Skia.
163 if (positions.back() != 1.0) {
164 positions.push_back(1.0);
165 colors.push_back(colors.back());
166 }
167
[email protected]959e1902012-03-03 02:02:17168 SkPoint points[2];
169 points[0].iset(text_rect.x(), text_rect.y());
170 points[1].iset(text_rect.right(), text_rect.y());
[email protected]f308e5b12012-01-17 19:09:18171
[email protected]959e1902012-03-03 02:02:17172 return SkGradientShader::CreateLinear(&points[0], &colors[0], &positions[0],
173 colors.size(), SkShader::kClamp_TileMode);
174}
[email protected]f308e5b12012-01-17 19:09:18175
[email protected]ff44d712011-07-25 08:42:52176} // namespace
177
178namespace gfx {
179
[email protected]67b981562011-12-09 00:35:05180namespace internal {
181
182SkiaTextRenderer::SkiaTextRenderer(Canvas* canvas)
[email protected]3bf21e12012-05-02 15:33:36183 : canvas_skia_(canvas->sk_canvas()),
184 started_drawing_(false) {
[email protected]67b981562011-12-09 00:35:05185 DCHECK(canvas_skia_);
186 paint_.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
187 paint_.setStyle(SkPaint::kFill_Style);
188 paint_.setAntiAlias(true);
189 paint_.setSubpixelText(true);
190 paint_.setLCDRenderText(true);
[email protected]808d37a2012-05-10 03:06:29191 bounds_.setEmpty();
[email protected]67b981562011-12-09 00:35:05192}
193
194SkiaTextRenderer::~SkiaTextRenderer() {
[email protected]3bf21e12012-05-02 15:33:36195 // Work-around for http://crbug.com/122743, where non-ClearType text is
196 // rendered with incorrect gamma when using the fade shader. Draw the text
197 // to a layer and restore it faded by drawing a rect in kDstIn_Mode mode.
198 //
199 // TODO(asvitkine): Remove this work-around once the Skia bug is fixed.
200 // http://code.google.com/p/skia/issues/detail?id=590
201 if (deferred_fade_shader_.get()) {
202 paint_.setShader(deferred_fade_shader_.get());
203 paint_.setXfermodeMode(SkXfermode::kDstIn_Mode);
204 canvas_skia_->drawRect(bounds_, paint_);
205 canvas_skia_->restore();
206 }
[email protected]67b981562011-12-09 00:35:05207}
208
[email protected]0834e1b12012-04-11 02:23:56209void SkiaTextRenderer::SetDrawLooper(SkDrawLooper* draw_looper) {
210 paint_.setLooper(draw_looper);
211}
212
[email protected]be5756e22012-02-17 15:53:01213void SkiaTextRenderer::SetFontSmoothingSettings(bool enable_smoothing,
214 bool enable_lcd_text) {
215 paint_.setAntiAlias(enable_smoothing);
216 paint_.setSubpixelText(enable_smoothing);
217 paint_.setLCDRenderText(enable_lcd_text);
218}
219
[email protected]cd8e5522011-12-15 00:24:10220void SkiaTextRenderer::SetTypeface(SkTypeface* typeface) {
221 paint_.setTypeface(typeface);
222}
223
224void SkiaTextRenderer::SetTextSize(int size) {
225 paint_.setTextSize(size);
226}
227
[email protected]f3dc6fb2012-03-24 21:01:24228void SkiaTextRenderer::SetFontFamilyWithStyle(const std::string& family,
229 int style) {
230 DCHECK(!family.empty());
231
232 SkTypeface::Style skia_style = ConvertFontStyleToSkiaTypefaceStyle(style);
[email protected]e166e54d2012-03-27 14:11:51233 SkTypeface* typeface = SkTypeface::CreateFromName(family.c_str(), skia_style);
234 SkAutoUnref auto_unref(typeface);
235 if (typeface) {
[email protected]f3dc6fb2012-03-24 21:01:24236 // |paint_| adds its own ref. So don't |release()| it from the ref ptr here.
[email protected]e166e54d2012-03-27 14:11:51237 SetTypeface(typeface);
[email protected]f3dc6fb2012-03-24 21:01:24238
239 // Enable fake bold text if bold style is needed but new typeface does not
240 // have it.
241 paint_.setFakeBoldText((skia_style & SkTypeface::kBold) &&
[email protected]e166e54d2012-03-27 14:11:51242 !typeface->isBold());
[email protected]f3dc6fb2012-03-24 21:01:24243 }
244}
245
[email protected]67b981562011-12-09 00:35:05246void SkiaTextRenderer::SetForegroundColor(SkColor foreground) {
247 paint_.setColor(foreground);
248}
249
[email protected]3bf21e12012-05-02 15:33:36250void SkiaTextRenderer::SetShader(SkShader* shader, const Rect& bounds) {
251 bounds_ = RectToSkRect(bounds);
[email protected]f308e5b12012-01-17 19:09:18252 paint_.setShader(shader);
253}
254
[email protected]67b981562011-12-09 00:35:05255void SkiaTextRenderer::DrawPosText(const SkPoint* pos,
256 const uint16* glyphs,
257 size_t glyph_count) {
[email protected]3bf21e12012-05-02 15:33:36258 if (!started_drawing_) {
259 started_drawing_ = true;
260 // Work-around for http://crbug.com/122743, where non-ClearType text is
261 // rendered with incorrect gamma when using the fade shader. Draw the text
262 // to a layer and restore it faded by drawing a rect in kDstIn_Mode mode.
263 //
[email protected]110b0b62012-05-04 19:57:29264 // Skip this when there is a looper which seems not working well with
265 // deferred paint. Currently a looper is only used for text shadows.
266 //
[email protected]3bf21e12012-05-02 15:33:36267 // TODO(asvitkine): Remove this work-around once the Skia bug is fixed.
268 // http://code.google.com/p/skia/issues/detail?id=590
[email protected]110b0b62012-05-04 19:57:29269 if (!paint_.isLCDRenderText() &&
270 paint_.getShader() &&
271 !paint_.getLooper()) {
[email protected]3bf21e12012-05-02 15:33:36272 deferred_fade_shader_ = paint_.getShader();
273 paint_.setShader(NULL);
274 canvas_skia_->saveLayer(&bounds_, NULL);
275 }
276 }
277
278 const size_t byte_length = glyph_count * sizeof(glyphs[0]);
[email protected]67b981562011-12-09 00:35:05279 canvas_skia_->drawPosText(&glyphs[0], byte_length, &pos[0], paint_);
280}
281
282// Draw underline and strike through text decorations.
283// Based on |SkCanvas::DrawTextDecorations()| and constants from:
284// third_party/skia/src/core/SkTextFormatParams.h
285void SkiaTextRenderer::DrawDecorations(int x, int y, int width,
[email protected]54d32cc2012-01-27 19:52:18286 const StyleRange& style) {
287 if (!style.underline && !style.strike && !style.diagonal_strike)
288 return;
289
[email protected]67b981562011-12-09 00:35:05290 // Fraction of the text size to lower a strike through below the baseline.
291 const SkScalar kStrikeThroughOffset = (-SK_Scalar1 * 6 / 21);
292 // Fraction of the text size to lower an underline below the baseline.
293 const SkScalar kUnderlineOffset = (SK_Scalar1 / 9);
294 // Fraction of the text size to use for a strike through or under-line.
295 const SkScalar kLineThickness = (SK_Scalar1 / 18);
[email protected]54d32cc2012-01-27 19:52:18296 // Fraction of the text size to use for a top margin of a diagonal strike.
297 const SkScalar kDiagonalStrikeThroughMarginOffset = (SK_Scalar1 / 4);
[email protected]67b981562011-12-09 00:35:05298
299 SkScalar text_size = paint_.getTextSize();
300 SkScalar height = SkScalarMul(text_size, kLineThickness);
301 SkRect r;
302
303 r.fLeft = x;
304 r.fRight = x + width;
305
[email protected]54d32cc2012-01-27 19:52:18306 if (style.underline) {
[email protected]67b981562011-12-09 00:35:05307 SkScalar offset = SkScalarMulAdd(text_size, kUnderlineOffset, y);
308 r.fTop = offset;
309 r.fBottom = offset + height;
310 canvas_skia_->drawRect(r, paint_);
311 }
[email protected]54d32cc2012-01-27 19:52:18312 if (style.strike) {
[email protected]67b981562011-12-09 00:35:05313 SkScalar offset = SkScalarMulAdd(text_size, kStrikeThroughOffset, y);
314 r.fTop = offset;
315 r.fBottom = offset + height;
316 canvas_skia_->drawRect(r, paint_);
317 }
[email protected]54d32cc2012-01-27 19:52:18318 if (style.diagonal_strike) {
319 SkScalar offset =
320 SkScalarMul(text_size, kDiagonalStrikeThroughMarginOffset);
321 SkPaint paint(paint_);
322 paint.setAntiAlias(true);
323 paint.setStyle(SkPaint::kFill_Style);
324 paint.setStrokeWidth(height);
325 canvas_skia_->drawLine(
326 SkIntToScalar(x), SkIntToScalar(y) - text_size + offset,
327 SkIntToScalar(x + width), SkIntToScalar(y),
328 paint);
329 }
[email protected]67b981562011-12-09 00:35:05330}
331
332} // namespace internal
333
334
[email protected]ff44d712011-07-25 08:42:52335StyleRange::StyleRange()
[email protected]8d901a82012-01-04 19:41:30336 : foreground(SK_ColorBLACK),
[email protected]7aba39c2012-01-10 16:10:27337 font_style(gfx::Font::NORMAL),
[email protected]ff44d712011-07-25 08:42:52338 strike(false),
[email protected]54d32cc2012-01-27 19:52:18339 diagonal_strike(false),
[email protected]8d901a82012-01-04 19:41:30340 underline(false) {
[email protected]ff44d712011-07-25 08:42:52341}
342
[email protected]8e42ba22011-08-04 21:47:08343RenderText::~RenderText() {
344}
345
[email protected]ff44d712011-07-25 08:42:52346void RenderText::SetText(const string16& text) {
[email protected]d3c6b0602011-09-07 19:26:06347 DCHECK(!composition_range_.IsValid());
[email protected]ff44d712011-07-25 08:42:52348 size_t old_text_length = text_.length();
349 text_ = text;
350
351 // Update the style ranges as needed.
352 if (text_.empty()) {
353 style_ranges_.clear();
354 } else if (style_ranges_.empty()) {
355 ApplyDefaultStyle();
356 } else if (text_.length() > old_text_length) {
357 style_ranges_.back().range.set_end(text_.length());
358 } else if (text_.length() < old_text_length) {
359 StyleRanges::iterator i;
360 for (i = style_ranges_.begin(); i != style_ranges_.end(); i++) {
361 if (i->range.start() >= text_.length()) {
[email protected]052ce6bb2011-10-14 19:48:20362 // Style ranges are sorted and non-overlapping, so all the subsequent
363 // style ranges should be out of text_.length() as well.
364 style_ranges_.erase(i, style_ranges_.end());
365 break;
[email protected]ff44d712011-07-25 08:42:52366 }
367 }
[email protected]052ce6bb2011-10-14 19:48:20368 // Since style ranges are sorted and non-overlapping, if there is a style
369 // range ends beyond text_.length, it must be the last one.
[email protected]ff44d712011-07-25 08:42:52370 style_ranges_.back().range.set_end(text_.length());
371 }
372#ifndef NDEBUG
373 CheckStyleRanges(style_ranges_, text_.length());
374#endif
[email protected]f6aaa0f2011-08-11 07:05:46375 cached_bounds_and_offset_valid_ = false;
[email protected]d3c6b0602011-09-07 19:26:06376
377 // Reset selection model. SetText should always followed by SetSelectionModel
378 // or SetCursorPosition in upper layer.
[email protected]d9990212012-03-13 01:09:31379 SetSelectionModel(SelectionModel());
[email protected]6002a4f32011-11-30 10:18:42380
[email protected]caa9e1e2012-03-13 20:47:04381 ResetLayout();
[email protected]ff44d712011-07-25 08:42:52382}
383
[email protected]f0ed8a2f2012-01-24 17:45:59384void RenderText::SetHorizontalAlignment(HorizontalAlignment alignment) {
385 if (horizontal_alignment_ != alignment) {
386 horizontal_alignment_ = alignment;
387 display_offset_ = Point();
388 cached_bounds_and_offset_valid_ = false;
389 }
390}
391
[email protected]8d901a82012-01-04 19:41:30392void RenderText::SetFontList(const FontList& font_list) {
393 font_list_ = font_list;
394 cached_bounds_and_offset_valid_ = false;
[email protected]caa9e1e2012-03-13 20:47:04395 ResetLayout();
[email protected]8d901a82012-01-04 19:41:30396}
397
[email protected]fdf481b2012-01-24 06:14:07398void RenderText::SetFontSize(int size) {
399 font_list_ = font_list_.DeriveFontListWithSize(size);
400 cached_bounds_and_offset_valid_ = false;
[email protected]caa9e1e2012-03-13 20:47:04401 ResetLayout();
[email protected]fdf481b2012-01-24 06:14:07402}
403
[email protected]f0ed8a2f2012-01-24 17:45:59404void RenderText::SetCursorEnabled(bool cursor_enabled) {
405 cursor_enabled_ = cursor_enabled;
406 cached_bounds_and_offset_valid_ = false;
407}
408
[email protected]8d901a82012-01-04 19:41:30409const Font& RenderText::GetFont() const {
410 return font_list_.GetFonts()[0];
411}
412
[email protected]0d717602011-08-30 06:21:14413void RenderText::ToggleInsertMode() {
414 insert_mode_ = !insert_mode_;
[email protected]f6aaa0f2011-08-11 07:05:46415 cached_bounds_and_offset_valid_ = false;
416}
417
[email protected]bec929c2012-03-02 06:23:50418void RenderText::SetObscured(bool obscured) {
419 if (obscured != obscured_) {
420 obscured_ = obscured;
421 cached_bounds_and_offset_valid_ = false;
[email protected]caa9e1e2012-03-13 20:47:04422 ResetLayout();
[email protected]bec929c2012-03-02 06:23:50423 }
424}
425
[email protected]f6aaa0f2011-08-11 07:05:46426void RenderText::SetDisplayRect(const Rect& r) {
[email protected]496de1ac2012-03-30 05:49:58427 if (r.width() != display_rect_.width())
428 ResetLayout();
[email protected]f6aaa0f2011-08-11 07:05:46429 display_rect_ = r;
430 cached_bounds_and_offset_valid_ = false;
[email protected]8e42ba22011-08-04 21:47:08431}
432
[email protected]0d717602011-08-30 06:21:14433void RenderText::SetCursorPosition(size_t position) {
434 MoveCursorTo(position, false);
[email protected]ff44d712011-07-25 08:42:52435}
436
[email protected]d66009e2012-01-21 01:27:28437void RenderText::MoveCursor(BreakType break_type,
438 VisualCursorDirection direction,
439 bool select) {
[email protected]d9990212012-03-13 01:09:31440 SelectionModel position(cursor_position(), selection_model_.caret_affinity());
[email protected]ff44d712011-07-25 08:42:52441 // Cancelling a selection moves to the edge of the selection.
[email protected]d9990212012-03-13 01:09:31442 if (break_type != LINE_BREAK && !selection().is_empty() && !select) {
[email protected]d3c6b0602011-09-07 19:26:06443 SelectionModel selection_start = GetSelectionModelForSelectionStart();
[email protected]d66009e2012-01-21 01:27:28444 int start_x = GetCursorBounds(selection_start, true).x();
445 int cursor_x = GetCursorBounds(position, true).x();
446 // Use the selection start if it is left (when |direction| is CURSOR_LEFT)
447 // or right (when |direction| is CURSOR_RIGHT) of the selection end.
448 if (direction == CURSOR_RIGHT ? start_x > cursor_x : start_x < cursor_x)
[email protected]8e42ba22011-08-04 21:47:08449 position = selection_start;
[email protected]d66009e2012-01-21 01:27:28450 // For word breaks, use the nearest word boundary in the appropriate
451 // |direction|.
[email protected]ff44d712011-07-25 08:42:52452 if (break_type == WORD_BREAK)
[email protected]d66009e2012-01-21 01:27:28453 position = GetAdjacentSelectionModel(position, break_type, direction);
[email protected]ff44d712011-07-25 08:42:52454 } else {
[email protected]d66009e2012-01-21 01:27:28455 position = GetAdjacentSelectionModel(position, break_type, direction);
[email protected]ff44d712011-07-25 08:42:52456 }
[email protected]ec7f48d2011-08-09 03:48:50457 if (select)
[email protected]d9990212012-03-13 01:09:31458 position.set_selection_start(selection().start());
[email protected]8e42ba22011-08-04 21:47:08459 MoveCursorTo(position);
[email protected]ff44d712011-07-25 08:42:52460}
461
[email protected]f9a221b2011-12-10 20:25:38462bool RenderText::MoveCursorTo(const SelectionModel& model) {
[email protected]0d717602011-08-30 06:21:14463 // Enforce valid selection model components.
[email protected]d9990212012-03-13 01:09:31464 size_t text_length = text().length();
465 ui::Range range(std::min(model.selection().start(), text_length),
466 std::min(model.caret_pos(), text_length));
[email protected]0d717602011-08-30 06:21:14467 // The current model only supports caret positions at valid character indices.
[email protected]d9990212012-03-13 01:09:31468 if (!IsCursorablePosition(range.start()) ||
469 !IsCursorablePosition(range.end()))
[email protected]53c0b1b2011-09-21 20:32:29470 return false;
[email protected]d9990212012-03-13 01:09:31471 SelectionModel sel(range, model.caret_affinity());
472 bool changed = sel != selection_model_;
[email protected]0d717602011-08-30 06:21:14473 SetSelectionModel(sel);
[email protected]ff44d712011-07-25 08:42:52474 return changed;
475}
476
[email protected]7b3cb4b22011-08-02 18:36:40477bool RenderText::MoveCursorTo(const Point& point, bool select) {
[email protected]d9990212012-03-13 01:09:31478 SelectionModel position = FindCursorPosition(point);
[email protected]8e42ba22011-08-04 21:47:08479 if (select)
[email protected]d9990212012-03-13 01:09:31480 position.set_selection_start(selection().start());
481 return MoveCursorTo(position);
[email protected]ff44d712011-07-25 08:42:52482}
483
[email protected]67e85512011-10-12 20:03:45484bool RenderText::SelectRange(const ui::Range& range) {
[email protected]d9990212012-03-13 01:09:31485 ui::Range sel(std::min(range.start(), text().length()),
486 std::min(range.end(), text().length()));
487 if (!IsCursorablePosition(sel.start()) || !IsCursorablePosition(sel.end()))
[email protected]67e85512011-10-12 20:03:45488 return false;
[email protected]d9990212012-03-13 01:09:31489 LogicalCursorDirection affinity =
490 (sel.is_reversed() || sel.is_empty()) ? CURSOR_FORWARD : CURSOR_BACKWARD;
491 SetSelectionModel(SelectionModel(sel, affinity));
[email protected]67e85512011-10-12 20:03:45492 return true;
493}
494
[email protected]8e42ba22011-08-04 21:47:08495bool RenderText::IsPointInSelection(const Point& point) {
[email protected]d9990212012-03-13 01:09:31496 if (selection().is_empty())
[email protected]0d717602011-08-30 06:21:14497 return false;
[email protected]d9990212012-03-13 01:09:31498 SelectionModel cursor = FindCursorPosition(point);
499 return RangeContainsCaret(
500 selection(), cursor.caret_pos(), cursor.caret_affinity());
[email protected]ff44d712011-07-25 08:42:52501}
502
503void RenderText::ClearSelection() {
[email protected]d9990212012-03-13 01:09:31504 SetSelectionModel(SelectionModel(cursor_position(),
505 selection_model_.caret_affinity()));
[email protected]ff44d712011-07-25 08:42:52506}
507
508void RenderText::SelectAll() {
[email protected]d9990212012-03-13 01:09:31509 SelectionModel all;
510 if (GetTextDirection() == base::i18n::LEFT_TO_RIGHT)
511 all = SelectionModel(ui::Range(0, text().length()), CURSOR_FORWARD);
512 else
513 all = SelectionModel(ui::Range(text().length(), 0), CURSOR_BACKWARD);
514 SetSelectionModel(all);
[email protected]ff44d712011-07-25 08:42:52515}
516
517void RenderText::SelectWord() {
[email protected]bec929c2012-03-02 06:23:50518 if (obscured_) {
519 SelectAll();
520 return;
521 }
522
[email protected]d9990212012-03-13 01:09:31523 size_t cursor_pos = cursor_position();
[email protected]ff44d712011-07-25 08:42:52524
[email protected]53c0b1b2011-09-21 20:32:29525 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD);
526 bool success = iter.Init();
527 DCHECK(success);
528 if (!success)
529 return;
530
[email protected]d9990212012-03-13 01:09:31531 size_t selection_start = cursor_pos;
[email protected]53c0b1b2011-09-21 20:32:29532 for (; selection_start != 0; --selection_start) {
533 if (iter.IsStartOfWord(selection_start) ||
534 iter.IsEndOfWord(selection_start))
[email protected]ff44d712011-07-25 08:42:52535 break;
536 }
537
[email protected]d9990212012-03-13 01:09:31538 if (selection_start == cursor_pos)
539 ++cursor_pos;
[email protected]53c0b1b2011-09-21 20:32:29540
[email protected]d9990212012-03-13 01:09:31541 for (; cursor_pos < text().length(); ++cursor_pos)
542 if (iter.IsEndOfWord(cursor_pos) || iter.IsStartOfWord(cursor_pos))
[email protected]ff44d712011-07-25 08:42:52543 break;
[email protected]ff44d712011-07-25 08:42:52544
[email protected]0d717602011-08-30 06:21:14545 MoveCursorTo(selection_start, false);
[email protected]d9990212012-03-13 01:09:31546 MoveCursorTo(cursor_pos, true);
[email protected]ff44d712011-07-25 08:42:52547}
548
549const ui::Range& RenderText::GetCompositionRange() const {
550 return composition_range_;
551}
552
553void RenderText::SetCompositionRange(const ui::Range& composition_range) {
554 CHECK(!composition_range.IsValid() ||
555 ui::Range(0, text_.length()).Contains(composition_range));
556 composition_range_.set_end(composition_range.end());
557 composition_range_.set_start(composition_range.start());
[email protected]caa9e1e2012-03-13 20:47:04558 ResetLayout();
[email protected]ff44d712011-07-25 08:42:52559}
560
[email protected]c8636672011-12-29 05:07:57561void RenderText::ApplyStyleRange(const StyleRange& style_range) {
[email protected]ff44d712011-07-25 08:42:52562 const ui::Range& new_range = style_range.range;
563 if (!new_range.IsValid() || new_range.is_empty())
564 return;
565 CHECK(!new_range.is_reversed());
566 CHECK(ui::Range(0, text_.length()).Contains(new_range));
[email protected]8e42ba22011-08-04 21:47:08567 ApplyStyleRangeImpl(&style_ranges_, style_range);
[email protected]ff44d712011-07-25 08:42:52568#ifndef NDEBUG
569 CheckStyleRanges(style_ranges_, text_.length());
570#endif
[email protected]f6aaa0f2011-08-11 07:05:46571 // TODO(xji): only invalidate if font or underline changes.
572 cached_bounds_and_offset_valid_ = false;
[email protected]caa9e1e2012-03-13 20:47:04573 ResetLayout();
[email protected]ff44d712011-07-25 08:42:52574}
575
576void RenderText::ApplyDefaultStyle() {
577 style_ranges_.clear();
578 StyleRange style = StyleRange(default_style_);
579 style.range.set_end(text_.length());
580 style_ranges_.push_back(style);
[email protected]f6aaa0f2011-08-11 07:05:46581 cached_bounds_and_offset_valid_ = false;
[email protected]caa9e1e2012-03-13 20:47:04582 ResetLayout();
[email protected]ff44d712011-07-25 08:42:52583}
584
[email protected]d66009e2012-01-21 01:27:28585VisualCursorDirection RenderText::GetVisualDirectionOfLogicalEnd() {
586 return GetTextDirection() == base::i18n::LEFT_TO_RIGHT ?
587 CURSOR_RIGHT : CURSOR_LEFT;
[email protected]ff44d712011-07-25 08:42:52588}
589
[email protected]7b3cb4b22011-08-02 18:36:40590void RenderText::Draw(Canvas* canvas) {
[email protected]49aa120a2012-05-31 00:01:22591 EnsureLayout();
[email protected]ff44d712011-07-25 08:42:52592
[email protected]0834e1b12012-04-11 02:23:56593 gfx::Rect clip_rect(display_rect());
594 clip_rect.Inset(ShadowValue::GetMargin(text_shadows_));
595
[email protected]f308e5b12012-01-17 19:09:18596 canvas->Save();
[email protected]0834e1b12012-04-11 02:23:56597 canvas->ClipRect(clip_rect);
[email protected]1a353f12012-01-18 04:20:56598
599 if (!text().empty())
600 DrawSelection(canvas);
601
602 DrawCursor(canvas);
603
[email protected]49aa120a2012-05-31 00:01:22604 if (!text().empty())
[email protected]6002a4f32011-11-30 10:18:42605 DrawVisualText(canvas);
[email protected]f308e5b12012-01-17 19:09:18606 canvas->Restore();
[email protected]ff44d712011-07-25 08:42:52607}
608
[email protected]d9990212012-03-13 01:09:31609Rect RenderText::GetCursorBounds(const SelectionModel& caret,
610 bool insert_mode) {
611 EnsureLayout();
612
613 size_t caret_pos = caret.caret_pos();
614 // In overtype mode, ignore the affinity and always indicate that we will
615 // overtype the next character.
616 LogicalCursorDirection caret_affinity =
617 insert_mode ? caret.caret_affinity() : CURSOR_FORWARD;
618 int x = 0, width = 0, height = 0;
619 if (caret_pos == (caret_affinity == CURSOR_BACKWARD ? 0 : text().length())) {
620 // The caret is attached to the boundary. Always return a zero-width caret,
621 // since there is nothing to overtype.
622 Size size = GetStringSize();
623 if ((GetTextDirection() == base::i18n::RIGHT_TO_LEFT) == (caret_pos == 0))
624 x = size.width();
625 height = size.height();
626 } else {
627 size_t grapheme_start = (caret_affinity == CURSOR_FORWARD) ?
628 caret_pos : IndexOfAdjacentGrapheme(caret_pos, CURSOR_BACKWARD);
629 ui::Range xspan;
630 GetGlyphBounds(grapheme_start, &xspan, &height);
631 if (insert_mode) {
632 x = (caret_affinity == CURSOR_BACKWARD) ? xspan.end() : xspan.start();
633 } else { // overtype mode
634 x = xspan.GetMin();
635 width = xspan.length();
636 }
637 }
638 height = std::min(height, display_rect().height());
639 int y = (display_rect().height() - height) / 2;
640 return Rect(ToViewPoint(Point(x, y)), Size(width, height));
641}
642
[email protected]f6aaa0f2011-08-11 07:05:46643const Rect& RenderText::GetUpdatedCursorBounds() {
644 UpdateCachedBoundsAndOffset();
[email protected]8e42ba22011-08-04 21:47:08645 return cursor_bounds_;
646}
647
[email protected]0a12d5f2012-05-16 00:37:09648size_t RenderText::IndexOfAdjacentGrapheme(size_t index,
649 LogicalCursorDirection direction) {
650 if (index > text().length())
651 return text().length();
652
653 EnsureLayout();
654
655 if (direction == CURSOR_FORWARD) {
656 while (index < text().length()) {
657 index++;
658 if (IsCursorablePosition(index))
659 return index;
660 }
661 return text().length();
662 }
663
664 while (index > 0) {
665 index--;
666 if (IsCursorablePosition(index))
667 return index;
668 }
669 return 0;
670}
671
[email protected]04b7bf322011-10-03 19:08:46672SelectionModel RenderText::GetSelectionModelForSelectionStart() {
[email protected]d9990212012-03-13 01:09:31673 const ui::Range& sel = selection();
674 if (sel.is_empty())
675 return selection_model_;
676 return SelectionModel(sel.start(),
677 sel.is_reversed() ? CURSOR_BACKWARD : CURSOR_FORWARD);
[email protected]04b7bf322011-10-03 19:08:46678}
679
[email protected]25650682012-05-29 23:09:19680void RenderText::SetTextShadows(const ShadowValues& shadows) {
[email protected]0834e1b12012-04-11 02:23:56681 text_shadows_ = shadows;
682}
683
[email protected]8e42ba22011-08-04 21:47:08684RenderText::RenderText()
[email protected]f0ed8a2f2012-01-24 17:45:59685 : horizontal_alignment_(base::i18n::IsRTL() ? ALIGN_RIGHT : ALIGN_LEFT),
686 cursor_enabled_(true),
687 cursor_visible_(false),
[email protected]8e42ba22011-08-04 21:47:08688 insert_mode_(true),
[email protected]913a1ef2012-02-29 08:40:34689 cursor_color_(kDefaultCursorColor),
[email protected]311a09d2012-05-14 20:38:17690 selection_color_(kDefaultSelectionColor),
691 selection_background_focused_color_(kDefaultSelectionBackgroundColor),
692 selection_background_unfocused_color_(kDefaultSelectionBackgroundColor),
[email protected]5abe6302011-12-20 23:44:32693 focused_(false),
[email protected]d3c6b0602011-09-07 19:26:06694 composition_range_(ui::Range::InvalidRange()),
[email protected]bec929c2012-03-02 06:23:50695 obscured_(false),
[email protected]f308e5b12012-01-17 19:09:18696 fade_head_(false),
697 fade_tail_(false),
[email protected]32d582d2012-02-28 21:50:22698 background_is_transparent_(false),
[email protected]f6aaa0f2011-08-11 07:05:46699 cached_bounds_and_offset_valid_(false) {
700}
701
702const Point& RenderText::GetUpdatedDisplayOffset() {
703 UpdateCachedBoundsAndOffset();
704 return display_offset_;
[email protected]8e42ba22011-08-04 21:47:08705}
706
[email protected]d66009e2012-01-21 01:27:28707SelectionModel RenderText::GetAdjacentSelectionModel(
708 const SelectionModel& current,
709 BreakType break_type,
710 VisualCursorDirection direction) {
711 EnsureLayout();
712
713 if (break_type == LINE_BREAK || text().empty())
714 return EdgeSelectionModel(direction);
[email protected]ec7f48d2011-08-09 03:48:50715 if (break_type == CHARACTER_BREAK)
[email protected]d66009e2012-01-21 01:27:28716 return AdjacentCharSelectionModel(current, direction);
717 DCHECK(break_type == WORD_BREAK);
718 return AdjacentWordSelectionModel(current, direction);
[email protected]0d717602011-08-30 06:21:14719}
720
[email protected]d9990212012-03-13 01:09:31721SelectionModel RenderText::EdgeSelectionModel(
722 VisualCursorDirection direction) {
723 if (direction == GetVisualDirectionOfLogicalEnd())
724 return SelectionModel(text().length(), CURSOR_FORWARD);
725 return SelectionModel(0, CURSOR_BACKWARD);
726}
[email protected]6002a4f32011-11-30 10:18:42727
[email protected]d9990212012-03-13 01:09:31728void RenderText::SetSelectionModel(const SelectionModel& model) {
729 DCHECK_LE(model.selection().GetMax(), text().length());
730 selection_model_ = model;
[email protected]6002a4f32011-11-30 10:18:42731 cached_bounds_and_offset_valid_ = false;
[email protected]0d717602011-08-30 06:21:14732}
733
[email protected]bec929c2012-03-02 06:23:50734string16 RenderText::GetDisplayText() const {
735 if (!obscured_)
736 return text_;
737 size_t obscured_text_length =
738 static_cast<size_t>(ui::UTF16IndexToOffset(text_, 0, text_.length()));
739 return string16(obscured_text_length, kPasswordReplacementChar);
740}
741
[email protected]8e42ba22011-08-04 21:47:08742void RenderText::ApplyCompositionAndSelectionStyles(
[email protected]1a353f12012-01-18 04:20:56743 StyleRanges* style_ranges) {
[email protected]8e42ba22011-08-04 21:47:08744 // TODO(msw): This pattern ought to be reconsidered; what about composition
745 // and selection overlaps, retain existing local style features?
746 // Apply a composition style override to a copy of the style ranges.
747 if (composition_range_.IsValid() && !composition_range_.is_empty()) {
748 StyleRange composition_style(default_style_);
749 composition_style.underline = true;
[email protected]d9990212012-03-13 01:09:31750 composition_style.range = composition_range_;
[email protected]8e42ba22011-08-04 21:47:08751 ApplyStyleRangeImpl(style_ranges, composition_style);
752 }
753 // Apply a selection style override to a copy of the style ranges.
[email protected]d9990212012-03-13 01:09:31754 if (!selection().is_empty()) {
[email protected]8e42ba22011-08-04 21:47:08755 StyleRange selection_style(default_style_);
[email protected]311a09d2012-05-14 20:38:17756 selection_style.foreground = selection_color_;
[email protected]d9990212012-03-13 01:09:31757 selection_style.range = ui::Range(selection().GetMin(),
758 selection().GetMax());
[email protected]8e42ba22011-08-04 21:47:08759 ApplyStyleRangeImpl(style_ranges, selection_style);
760 }
[email protected]1a353f12012-01-18 04:20:56761 // Apply replacement-mode style override to a copy of the style ranges.
762 //
763 // TODO(xji): NEED TO FIX FOR WINDOWS ASAP. Windows call this function (to
764 // apply styles) in ItemizeLogicalText(). In order for the cursor's underline
765 // character to be drawn correctly, we will need to re-layout the text. It's
766 // not practical to do layout on every cursor blink. We need to fix Windows
767 // port to apply styles during drawing phase like Linux port does.
768 // http://crbug.com/110109
769 if (!insert_mode_ && cursor_visible() && focused()) {
770 StyleRange replacement_mode_style(default_style_);
[email protected]311a09d2012-05-14 20:38:17771 replacement_mode_style.foreground = selection_color_;
[email protected]d9990212012-03-13 01:09:31772 size_t cursor = cursor_position();
[email protected]1a353f12012-01-18 04:20:56773 replacement_mode_style.range.set_start(cursor);
[email protected]d66009e2012-01-21 01:27:28774 replacement_mode_style.range.set_end(
775 IndexOfAdjacentGrapheme(cursor, CURSOR_FORWARD));
[email protected]1a353f12012-01-18 04:20:56776 ApplyStyleRangeImpl(style_ranges, replacement_mode_style);
777 }
[email protected]ff44d712011-07-25 08:42:52778}
779
[email protected]f0ed8a2f2012-01-24 17:45:59780Point RenderText::GetTextOrigin() {
781 Point origin = display_rect().origin();
782 origin = origin.Add(GetUpdatedDisplayOffset());
783 origin = origin.Add(GetAlignmentOffset());
784 return origin;
785}
786
[email protected]0d717602011-08-30 06:21:14787Point RenderText::ToTextPoint(const Point& point) {
[email protected]f0ed8a2f2012-01-24 17:45:59788 return point.Subtract(GetTextOrigin());
[email protected]0d717602011-08-30 06:21:14789}
790
791Point RenderText::ToViewPoint(const Point& point) {
[email protected]f0ed8a2f2012-01-24 17:45:59792 return point.Add(GetTextOrigin());
793}
794
795int RenderText::GetContentWidth() {
[email protected]d9990212012-03-13 01:09:31796 return GetStringSize().width() + (cursor_enabled_ ? 1 : 0);
[email protected]f0ed8a2f2012-01-24 17:45:59797}
798
799Point RenderText::GetAlignmentOffset() {
800 if (horizontal_alignment() != ALIGN_LEFT) {
801 int x_offset = display_rect().width() - GetContentWidth();
802 if (horizontal_alignment() == ALIGN_CENTER)
803 x_offset /= 2;
804 return Point(x_offset, 0);
805 }
806 return Point();
[email protected]0d717602011-08-30 06:21:14807}
808
[email protected]f9905322012-04-04 14:50:00809Point RenderText::GetOriginForDrawing() {
[email protected]f0ed8a2f2012-01-24 17:45:59810 Point origin(GetTextOrigin());
[email protected]024653b2012-04-05 21:10:53811 const int height = GetStringSize().height();
[email protected]fdf481b2012-01-24 06:14:07812 DCHECK_LE(height, display_rect().height());
[email protected]67b981562011-12-09 00:35:05813 // Center the text vertically in the display area.
814 origin.Offset(0, (display_rect().height() - height) / 2);
[email protected]67b981562011-12-09 00:35:05815 return origin;
816}
817
[email protected]f0ed8a2f2012-01-24 17:45:59818void RenderText::ApplyFadeEffects(internal::SkiaTextRenderer* renderer) {
[email protected]f308e5b12012-01-17 19:09:18819 if (!fade_head() && !fade_tail())
[email protected]f0ed8a2f2012-01-24 17:45:59820 return;
[email protected]f308e5b12012-01-17 19:09:18821
[email protected]d9990212012-03-13 01:09:31822 const int text_width = GetStringSize().width();
[email protected]f308e5b12012-01-17 19:09:18823 const int display_width = display_rect().width();
824
825 // If the text fits as-is, no need to fade.
826 if (text_width <= display_width)
[email protected]f0ed8a2f2012-01-24 17:45:59827 return;
[email protected]f308e5b12012-01-17 19:09:18828
829 int gradient_width = CalculateFadeGradientWidth(GetFont(), display_width);
830 if (gradient_width == 0)
[email protected]f0ed8a2f2012-01-24 17:45:59831 return;
[email protected]f308e5b12012-01-17 19:09:18832
833 bool fade_left = fade_head();
834 bool fade_right = fade_tail();
835 // Under RTL, |fade_right| == |fade_head|.
836 if (GetTextDirection() == base::i18n::RIGHT_TO_LEFT)
837 std::swap(fade_left, fade_right);
838
839 gfx::Rect solid_part = display_rect();
840 gfx::Rect left_part;
841 gfx::Rect right_part;
842 if (fade_left) {
843 left_part = solid_part;
844 left_part.Inset(0, 0, solid_part.width() - gradient_width, 0);
845 solid_part.Inset(gradient_width, 0, 0, 0);
846 }
847 if (fade_right) {
848 right_part = solid_part;
849 right_part.Inset(solid_part.width() - gradient_width, 0, 0, 0);
850 solid_part.Inset(0, 0, gradient_width, 0);
851 }
852
[email protected]f308e5b12012-01-17 19:09:18853 gfx::Rect text_rect = display_rect();
[email protected]f0ed8a2f2012-01-24 17:45:59854 text_rect.Inset(GetAlignmentOffset().x(), 0, 0, 0);
[email protected]f308e5b12012-01-17 19:09:18855
856 const SkColor color = default_style().foreground;
[email protected]e166e54d2012-03-27 14:11:51857 SkShader* shader = CreateFadeShader(text_rect, left_part, right_part, color);
858 SkAutoUnref auto_unref(shader);
859 if (shader) {
[email protected]f308e5b12012-01-17 19:09:18860 // |renderer| adds its own ref. So don't |release()| it from the ref ptr.
[email protected]3bf21e12012-05-02 15:33:36861 renderer->SetShader(shader, display_rect());
[email protected]f308e5b12012-01-17 19:09:18862 }
[email protected]f308e5b12012-01-17 19:09:18863}
864
[email protected]0834e1b12012-04-11 02:23:56865void RenderText::ApplyTextShadows(internal::SkiaTextRenderer* renderer) {
[email protected]9499a0b2012-05-25 16:17:59866 SkDrawLooper* looper = gfx::CreateShadowDrawLooper(text_shadows_);
[email protected]0834e1b12012-04-11 02:23:56867 SkAutoUnref auto_unref(looper);
[email protected]0834e1b12012-04-11 02:23:56868 renderer->SetDrawLooper(looper);
869}
870
[email protected]d9990212012-03-13 01:09:31871// static
872bool RenderText::RangeContainsCaret(const ui::Range& range,
873 size_t caret_pos,
874 LogicalCursorDirection caret_affinity) {
875 // NB: exploits unsigned wraparound (WG14/N1124 section 6.2.5 paragraph 9).
876 size_t adjacent = (caret_affinity == CURSOR_BACKWARD) ?
877 caret_pos - 1 : caret_pos + 1;
878 return range.Contains(ui::Range(caret_pos, adjacent));
879}
880
[email protected]0d717602011-08-30 06:21:14881void RenderText::MoveCursorTo(size_t position, bool select) {
882 size_t cursor = std::min(position, text().length());
[email protected]d9990212012-03-13 01:09:31883 if (IsCursorablePosition(cursor))
884 SetSelectionModel(SelectionModel(
885 ui::Range(select ? selection().start() : cursor, cursor),
886 (cursor == 0) ? CURSOR_FORWARD : CURSOR_BACKWARD));
[email protected]8e42ba22011-08-04 21:47:08887}
888
[email protected]f6aaa0f2011-08-11 07:05:46889void RenderText::UpdateCachedBoundsAndOffset() {
890 if (cached_bounds_and_offset_valid_)
891 return;
[email protected]f0ed8a2f2012-01-24 17:45:59892
[email protected]f6aaa0f2011-08-11 07:05:46893 // First, set the valid flag true to calculate the current cursor bounds using
894 // the stale |display_offset_|. Applying |delta_offset| at the end of this
895 // function will set |cursor_bounds_| and |display_offset_| to correct values.
896 cached_bounds_and_offset_valid_ = true;
897 cursor_bounds_ = GetCursorBounds(selection_model_, insert_mode_);
[email protected]f0ed8a2f2012-01-24 17:45:59898
[email protected]8e42ba22011-08-04 21:47:08899 // Update |display_offset_| to ensure the current cursor is visible.
[email protected]f0ed8a2f2012-01-24 17:45:59900 const int display_width = display_rect_.width();
901 const int content_width = GetContentWidth();
902
[email protected]f6aaa0f2011-08-11 07:05:46903 int delta_offset = 0;
[email protected]f0ed8a2f2012-01-24 17:45:59904 if (content_width <= display_width || !cursor_enabled()) {
905 // Don't pan if the text fits in the display width or when the cursor is
906 // disabled.
[email protected]f6aaa0f2011-08-11 07:05:46907 delta_offset = -display_offset_.x();
[email protected]65788132011-09-07 20:20:23908 } else if (cursor_bounds_.right() >= display_rect_.right()) {
[email protected]d3c6b0602011-09-07 19:26:06909 // TODO(xji): when the character overflow is a RTL character, currently, if
910 // we pan cursor at the rightmost position, the entered RTL character is not
911 // displayed. Should pan cursor to show the last logical characters.
912 //
[email protected]8e42ba22011-08-04 21:47:08913 // Pan to show the cursor when it overflows to the right,
[email protected]65788132011-09-07 20:20:23914 delta_offset = display_rect_.right() - cursor_bounds_.right() - 1;
[email protected]f6aaa0f2011-08-11 07:05:46915 } else if (cursor_bounds_.x() < display_rect_.x()) {
[email protected]d3c6b0602011-09-07 19:26:06916 // TODO(xji): have similar problem as above when overflow character is a
917 // LTR character.
918 //
[email protected]8e42ba22011-08-04 21:47:08919 // Pan to show the cursor when it overflows to the left.
[email protected]f6aaa0f2011-08-11 07:05:46920 delta_offset = display_rect_.x() - cursor_bounds_.x();
[email protected]f0ed8a2f2012-01-24 17:45:59921 } else if (display_offset_.x() != 0) {
922 // Reduce the pan offset to show additional overflow text when the display
923 // width increases.
924 const int negate_rtl = horizontal_alignment_ == ALIGN_RIGHT ? -1 : 1;
925 const int offset = negate_rtl * display_offset_.x();
926 if (display_width > (content_width + offset))
927 delta_offset = negate_rtl * (display_width - (content_width + offset));
[email protected]8e42ba22011-08-04 21:47:08928 }
[email protected]f0ed8a2f2012-01-24 17:45:59929
[email protected]f6aaa0f2011-08-11 07:05:46930 display_offset_.Offset(delta_offset, 0);
931 cursor_bounds_.Offset(delta_offset, 0);
[email protected]ff44d712011-07-25 08:42:52932}
933
[email protected]6002a4f32011-11-30 10:18:42934void RenderText::DrawSelection(Canvas* canvas) {
[email protected]311a09d2012-05-14 20:38:17935 const SkColor color = focused() ? selection_background_focused_color_ :
936 selection_background_unfocused_color_;
937 const std::vector<Rect> sel = GetSubstringBounds(selection());
[email protected]6002a4f32011-11-30 10:18:42938 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i)
[email protected]b8bdc422012-02-01 21:44:40939 canvas->FillRect(*i, color);
[email protected]6002a4f32011-11-30 10:18:42940}
941
942void RenderText::DrawCursor(Canvas* canvas) {
943 // Paint cursor. Replace cursor is drawn as rectangle for now.
944 // TODO(msw): Draw a better cursor with a better indication of association.
[email protected]f0ed8a2f2012-01-24 17:45:59945 if (cursor_enabled() && cursor_visible() && focused()) {
[email protected]1a353f12012-01-18 04:20:56946 const Rect& bounds = GetUpdatedCursorBounds();
947 if (bounds.width() != 0)
[email protected]913a1ef2012-02-29 08:40:34948 canvas->FillRect(bounds, cursor_color_);
[email protected]1a353f12012-01-18 04:20:56949 else
[email protected]913a1ef2012-02-29 08:40:34950 canvas->DrawRect(bounds, cursor_color_);
[email protected]1a353f12012-01-18 04:20:56951 }
[email protected]6002a4f32011-11-30 10:18:42952}
953
[email protected]ff44d712011-07-25 08:42:52954} // namespace gfx