OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/gfx/render_text.h" | 5 #include "ui/gfx/render_text.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <climits> | 8 #include <climits> |
9 | 9 |
10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
11 #include "base/i18n/break_iterator.h" | 11 #include "base/i18n/break_iterator.h" |
12 #include "base/logging.h" | 12 #include "base/logging.h" |
13 #include "base/stl_util.h" | 13 #include "base/stl_util.h" |
14 #include "base/strings/string_util.h" | 14 #include "base/strings/string_util.h" |
15 #include "base/strings/utf_string_conversions.h" | 15 #include "base/strings/utf_string_conversions.h" |
16 #include "third_party/icu/source/common/unicode/rbbi.h" | 16 #include "third_party/icu/source/common/unicode/rbbi.h" |
17 #include "third_party/icu/source/common/unicode/utf16.h" | 17 #include "third_party/icu/source/common/unicode/utf16.h" |
18 #include "third_party/skia/include/core/SkTypeface.h" | 18 #include "third_party/skia/include/core/SkTypeface.h" |
19 #include "third_party/skia/include/effects/SkGradientShader.h" | 19 #include "third_party/skia/include/effects/SkGradientShader.h" |
20 #include "ui/gfx/canvas.h" | 20 #include "ui/gfx/canvas.h" |
21 #include "ui/gfx/insets.h" | 21 #include "ui/gfx/insets.h" |
22 #include "ui/gfx/render_text_harfbuzz.h" | 22 #include "ui/gfx/render_text_harfbuzz.h" |
23 #include "ui/gfx/scoped_canvas.h" | 23 #include "ui/gfx/scoped_canvas.h" |
24 #include "ui/gfx/skia_util.h" | 24 #include "ui/gfx/skia_util.h" |
25 #include "ui/gfx/switches.h" | 25 #include "ui/gfx/switches.h" |
26 #include "ui/gfx/text_constants.h" | |
27 #include "ui/gfx/text_elider.h" | 26 #include "ui/gfx/text_elider.h" |
28 #include "ui/gfx/text_utils.h" | 27 #include "ui/gfx/text_utils.h" |
29 #include "ui/gfx/utf16_indexing.h" | 28 #include "ui/gfx/utf16_indexing.h" |
30 | 29 |
31 namespace gfx { | 30 namespace gfx { |
32 | 31 |
33 namespace { | 32 namespace { |
34 | 33 |
35 // All chars are replaced by this char when the password style is set. | 34 // All chars are replaced by this char when the password style is set. |
36 // TODO(benrg): GTK uses the first of U+25CF, U+2022, U+2731, U+273A, '*' | 35 // TODO(benrg): GTK uses the first of U+25CF, U+2022, U+2731, U+273A, '*' |
(...skipping 470 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
507 UpdateLayoutText(); | 506 UpdateLayoutText(); |
508 } | 507 } |
509 } | 508 } |
510 | 509 |
511 void RenderText::SetDisplayRect(const Rect& r) { | 510 void RenderText::SetDisplayRect(const Rect& r) { |
512 if (r != display_rect_) { | 511 if (r != display_rect_) { |
513 display_rect_ = r; | 512 display_rect_ = r; |
514 baseline_ = kInvalidBaseline; | 513 baseline_ = kInvalidBaseline; |
515 cached_bounds_and_offset_valid_ = false; | 514 cached_bounds_and_offset_valid_ = false; |
516 lines_.clear(); | 515 lines_.clear(); |
517 if (elide_behavior_ != TRUNCATE) | 516 if (elide_behavior_ != NO_ELIDE) |
518 UpdateLayoutText(); | 517 UpdateLayoutText(); |
519 } | 518 } |
520 } | 519 } |
521 | 520 |
522 void RenderText::SetCursorPosition(size_t position) { | 521 void RenderText::SetCursorPosition(size_t position) { |
523 MoveCursorTo(position, false); | 522 MoveCursorTo(position, false); |
524 } | 523 } |
525 | 524 |
526 void RenderText::MoveCursor(BreakType break_type, | 525 void RenderText::MoveCursor(BreakType break_type, |
527 VisualCursorDirection direction, | 526 VisualCursorDirection direction, |
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
739 VisualCursorDirection RenderText::GetVisualDirectionOfLogicalEnd() { | 738 VisualCursorDirection RenderText::GetVisualDirectionOfLogicalEnd() { |
740 return GetTextDirection() == base::i18n::LEFT_TO_RIGHT ? | 739 return GetTextDirection() == base::i18n::LEFT_TO_RIGHT ? |
741 CURSOR_RIGHT : CURSOR_LEFT; | 740 CURSOR_RIGHT : CURSOR_LEFT; |
742 } | 741 } |
743 | 742 |
744 SizeF RenderText::GetStringSizeF() { | 743 SizeF RenderText::GetStringSizeF() { |
745 const Size size = GetStringSize(); | 744 const Size size = GetStringSize(); |
746 return SizeF(size.width(), size.height()); | 745 return SizeF(size.width(), size.height()); |
747 } | 746 } |
748 | 747 |
749 int RenderText::GetContentWidth() { | 748 float RenderText::GetContentWidth() { |
750 return GetStringSize().width() + (cursor_enabled_ ? 1 : 0); | 749 return GetStringSizeF().width() + (cursor_enabled_ ? 1 : 0); |
751 } | 750 } |
752 | 751 |
753 int RenderText::GetBaseline() { | 752 int RenderText::GetBaseline() { |
754 if (baseline_ == kInvalidBaseline) | 753 if (baseline_ == kInvalidBaseline) |
755 baseline_ = DetermineBaselineCenteringText(display_rect(), font_list()); | 754 baseline_ = DetermineBaselineCenteringText(display_rect(), font_list()); |
756 DCHECK_NE(kInvalidBaseline, baseline_); | 755 DCHECK_NE(kInvalidBaseline, baseline_); |
757 return baseline_; | 756 return baseline_; |
758 } | 757 } |
759 | 758 |
760 void RenderText::Draw(Canvas* canvas) { | 759 void RenderText::Draw(Canvas* canvas) { |
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
920 selection_color_(kDefaultColor), | 919 selection_color_(kDefaultColor), |
921 selection_background_focused_color_(kDefaultSelectionBackgroundColor), | 920 selection_background_focused_color_(kDefaultSelectionBackgroundColor), |
922 focused_(false), | 921 focused_(false), |
923 composition_range_(Range::InvalidRange()), | 922 composition_range_(Range::InvalidRange()), |
924 colors_(kDefaultColor), | 923 colors_(kDefaultColor), |
925 styles_(NUM_TEXT_STYLES), | 924 styles_(NUM_TEXT_STYLES), |
926 composition_and_selection_styles_applied_(false), | 925 composition_and_selection_styles_applied_(false), |
927 obscured_(false), | 926 obscured_(false), |
928 obscured_reveal_index_(-1), | 927 obscured_reveal_index_(-1), |
929 truncate_length_(0), | 928 truncate_length_(0), |
930 elide_behavior_(TRUNCATE), | 929 elide_behavior_(NO_ELIDE), |
931 multiline_(false), | 930 multiline_(false), |
932 background_is_transparent_(false), | 931 background_is_transparent_(false), |
933 clip_to_display_rect_(true), | 932 clip_to_display_rect_(true), |
934 baseline_(kInvalidBaseline), | 933 baseline_(kInvalidBaseline), |
935 cached_bounds_and_offset_valid_(false) { | 934 cached_bounds_and_offset_valid_(false) { |
936 } | 935 } |
937 | 936 |
938 SelectionModel RenderText::GetAdjacentSelectionModel( | 937 SelectionModel RenderText::GetAdjacentSelectionModel( |
939 const SelectionModel& current, | 938 const SelectionModel& current, |
940 BreakType break_type, | 939 BreakType break_type, |
(...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1184 layout_text_.replace(cp_start, 1, text_.substr(start, end - start)); | 1183 layout_text_.replace(cp_start, 1, text_.substr(start, end - start)); |
1185 } | 1184 } |
1186 } else { | 1185 } else { |
1187 layout_text_ = text_; | 1186 layout_text_ = text_; |
1188 } | 1187 } |
1189 | 1188 |
1190 const base::string16& text = layout_text_; | 1189 const base::string16& text = layout_text_; |
1191 if (truncate_length_ > 0 && truncate_length_ < text.length()) { | 1190 if (truncate_length_ > 0 && truncate_length_ < text.length()) { |
1192 // Truncate the text at a valid character break and append an ellipsis. | 1191 // Truncate the text at a valid character break and append an ellipsis. |
1193 icu::StringCharacterIterator iter(text.c_str()); | 1192 icu::StringCharacterIterator iter(text.c_str()); |
1194 iter.setIndex32(truncate_length_ - 1); | 1193 // Respect ELIDE_HEAD and ELIDE_MIDDLE preferences during truncation. |
1195 layout_text_.assign(text.substr(0, iter.getIndex()) + kEllipsisUTF16); | 1194 if (elide_behavior_ == ELIDE_HEAD) { |
| 1195 iter.setIndex32(text.length() - truncate_length_ + 1); |
| 1196 layout_text_.assign(kEllipsisUTF16 + text.substr(iter.getIndex())); |
| 1197 } else if (elide_behavior_ == ELIDE_MIDDLE) { |
| 1198 iter.setIndex32(truncate_length_ / 2); |
| 1199 const size_t ellipsis_start = iter.getIndex(); |
| 1200 iter.setIndex32(text.length() - (truncate_length_ / 2)); |
| 1201 const size_t ellipsis_end = iter.getIndex(); |
| 1202 DCHECK_LE(ellipsis_start, ellipsis_end); |
| 1203 layout_text_.assign(text.substr(0, ellipsis_start) + kEllipsisUTF16 + |
| 1204 text.substr(ellipsis_end)); |
| 1205 } else { |
| 1206 iter.setIndex32(truncate_length_ - 1); |
| 1207 layout_text_.assign(text.substr(0, iter.getIndex()) + kEllipsisUTF16); |
| 1208 } |
1196 } | 1209 } |
1197 | 1210 |
1198 if (elide_behavior_ != TRUNCATE && elide_behavior_ != FADE_TAIL && | 1211 if (elide_behavior_ != NO_ELIDE && elide_behavior_ != FADE_TAIL && |
1199 display_rect_.width() > 0 && !layout_text_.empty() && | 1212 !layout_text_.empty() && GetContentWidth() > display_rect_.width()) { |
1200 GetContentWidth() > display_rect_.width()) { | |
1201 // This doesn't trim styles so ellipsis may get rendered as a different | 1213 // This doesn't trim styles so ellipsis may get rendered as a different |
1202 // style than the preceding text. See crbug.com/327850. | 1214 // style than the preceding text. See crbug.com/327850. |
1203 layout_text_.assign(ElideText(layout_text_)); | 1215 layout_text_.assign( |
| 1216 Elide(layout_text_, display_rect_.width(), elide_behavior_)); |
1204 } | 1217 } |
1205 | 1218 |
1206 // Replace the newline character with a newline symbol in single line mode. | 1219 // Replace the newline character with a newline symbol in single line mode. |
1207 static const base::char16 kNewline[] = { '\n', 0 }; | 1220 static const base::char16 kNewline[] = { '\n', 0 }; |
1208 static const base::char16 kNewlineSymbol[] = { 0x2424, 0 }; | 1221 static const base::char16 kNewlineSymbol[] = { 0x2424, 0 }; |
1209 if (!multiline_) | 1222 if (!multiline_) |
1210 base::ReplaceChars(layout_text_, kNewline, kNewlineSymbol, &layout_text_); | 1223 base::ReplaceChars(layout_text_, kNewline, kNewlineSymbol, &layout_text_); |
1211 | 1224 |
1212 ResetLayout(); | 1225 ResetLayout(); |
1213 } | 1226 } |
1214 | 1227 |
1215 // TODO(skanuj): Fix code duplication with ElideText in ui/gfx/text_elider.cc | 1228 base::string16 RenderText::Elide(const base::string16& text, |
1216 // See crbug.com/327846 | 1229 float available_width, |
1217 base::string16 RenderText::ElideText(const base::string16& text) { | 1230 ElideBehavior behavior) { |
1218 const bool insert_ellipsis = (elide_behavior_ != TRUNCATE); | 1231 if (available_width <= 0 || text.empty()) |
| 1232 return base::string16(); |
| 1233 if (behavior == ELIDE_EMAIL) |
| 1234 return ElideEmail(text, available_width); |
| 1235 |
1219 // Create a RenderText copy with attributes that affect the rendering width. | 1236 // Create a RenderText copy with attributes that affect the rendering width. |
1220 scoped_ptr<RenderText> render_text(CreateInstance()); | 1237 scoped_ptr<RenderText> render_text(CreateInstance()); |
1221 render_text->SetFontList(font_list_); | 1238 render_text->SetFontList(font_list_); |
1222 render_text->SetDirectionalityMode(directionality_mode_); | 1239 render_text->SetDirectionalityMode(directionality_mode_); |
1223 render_text->SetCursorEnabled(cursor_enabled_); | 1240 render_text->SetCursorEnabled(cursor_enabled_); |
1224 | 1241 render_text->set_truncate_length(truncate_length_); |
1225 render_text->styles_ = styles_; | 1242 render_text->styles_ = styles_; |
1226 render_text->colors_ = colors_; | 1243 render_text->colors_ = colors_; |
1227 render_text->SetText(text); | 1244 render_text->SetText(text); |
1228 const int current_text_pixel_width = render_text->GetContentWidth(); | 1245 if (render_text->GetContentWidth() <= available_width) |
| 1246 return text; |
1229 | 1247 |
1230 const base::string16 ellipsis = base::string16(kEllipsisUTF16); | 1248 const base::string16 ellipsis = base::string16(kEllipsisUTF16); |
1231 const bool elide_in_middle = false; | 1249 const bool insert_ellipsis = (behavior != TRUNCATE); |
1232 const bool elide_at_beginning = false; | 1250 const bool elide_in_middle = (behavior == ELIDE_MIDDLE); |
| 1251 const bool elide_at_beginning = (behavior == ELIDE_HEAD); |
1233 StringSlicer slicer(text, ellipsis, elide_in_middle, elide_at_beginning); | 1252 StringSlicer slicer(text, ellipsis, elide_in_middle, elide_at_beginning); |
1234 | 1253 |
1235 // Pango will return 0 width for absurdly long strings. Cut the string in | 1254 render_text->SetText(ellipsis); |
1236 // half and try again. | 1255 const float ellipsis_width = render_text->GetContentWidth(); |
1237 // This is caused by an int overflow in Pango (specifically, in | |
1238 // pango_glyph_string_extents_range). It's actually more subtle than just | |
1239 // returning 0, since on super absurdly long strings, the int can wrap and | |
1240 // return positive numbers again. Detecting that is probably not worth it | |
1241 // (eliding way too much from a ridiculous string is probably still | |
1242 // ridiculous), but we should check other widths for bogus values as well. | |
1243 if (current_text_pixel_width <= 0 && !text.empty()) | |
1244 return ElideText(slicer.CutString(text.length() / 2, insert_ellipsis)); | |
1245 | 1256 |
1246 if (current_text_pixel_width <= display_rect_.width()) | 1257 if (insert_ellipsis && (ellipsis_width > available_width)) |
1247 return text; | |
1248 | |
1249 render_text->SetText(base::string16()); | |
1250 render_text->SetText(ellipsis); | |
1251 const int ellipsis_width = render_text->GetContentWidth(); | |
1252 | |
1253 if (insert_ellipsis && (ellipsis_width >= display_rect_.width())) | |
1254 return base::string16(); | 1258 return base::string16(); |
1255 | 1259 |
1256 // Use binary search to compute the elided text. | 1260 // Use binary search to compute the elided text. |
1257 size_t lo = 0; | 1261 size_t lo = 0; |
1258 size_t hi = text.length() - 1; | 1262 size_t hi = text.length() - 1; |
1259 const base::i18n::TextDirection text_direction = GetTextDirection(); | 1263 const base::i18n::TextDirection text_direction = GetTextDirection(); |
1260 for (size_t guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) { | 1264 for (size_t guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) { |
1261 // Restore styles and colors. They will be truncated to size by SetText. | 1265 // Restore styles and colors. They will be truncated to size by SetText. |
1262 render_text->styles_ = styles_; | 1266 render_text->styles_ = styles_; |
1263 render_text->colors_ = colors_; | 1267 render_text->colors_ = colors_; |
1264 base::string16 new_text = slicer.CutString(guess, false); | 1268 base::string16 new_text = |
| 1269 slicer.CutString(guess, insert_ellipsis && behavior != ELIDE_TAIL); |
1265 render_text->SetText(new_text); | 1270 render_text->SetText(new_text); |
1266 | 1271 |
1267 // This has to be an additional step so that the ellipsis is rendered with | 1272 // This has to be an additional step so that the ellipsis is rendered with |
1268 // same style as trailing part of the text. | 1273 // same style as trailing part of the text. |
1269 if (insert_ellipsis) { | 1274 if (insert_ellipsis && behavior == ELIDE_TAIL) { |
1270 // When ellipsis follows text whose directionality is not the same as that | 1275 // When ellipsis follows text whose directionality is not the same as that |
1271 // of the whole text, it will be rendered with the directionality of the | 1276 // of the whole text, it will be rendered with the directionality of the |
1272 // whole text. Since we want ellipsis to indicate continuation of the | 1277 // whole text. Since we want ellipsis to indicate continuation of the |
1273 // preceding text, we force the directionality of ellipsis to be same as | 1278 // preceding text, we force the directionality of ellipsis to be same as |
1274 // the preceding text using LTR or RTL markers. | 1279 // the preceding text using LTR or RTL markers. |
1275 base::i18n::TextDirection trailing_text_direction = | 1280 base::i18n::TextDirection trailing_text_direction = |
1276 base::i18n::GetLastStrongCharacterDirection(new_text); | 1281 base::i18n::GetLastStrongCharacterDirection(new_text); |
1277 new_text.append(ellipsis); | 1282 new_text.append(ellipsis); |
1278 if (trailing_text_direction != text_direction) { | 1283 if (trailing_text_direction != text_direction) { |
1279 if (trailing_text_direction == base::i18n::LEFT_TO_RIGHT) | 1284 if (trailing_text_direction == base::i18n::LEFT_TO_RIGHT) |
1280 new_text += base::i18n::kLeftToRightMark; | 1285 new_text += base::i18n::kLeftToRightMark; |
1281 else | 1286 else |
1282 new_text += base::i18n::kRightToLeftMark; | 1287 new_text += base::i18n::kRightToLeftMark; |
1283 } | 1288 } |
1284 render_text->SetText(new_text); | 1289 render_text->SetText(new_text); |
1285 } | 1290 } |
1286 | 1291 |
1287 // We check the width of the whole desired string at once to ensure we | 1292 // We check the width of the whole desired string at once to ensure we |
1288 // handle kerning/ligatures/etc. correctly. | 1293 // handle kerning/ligatures/etc. correctly. |
1289 const int guess_width = render_text->GetContentWidth(); | 1294 const float guess_width = render_text->GetContentWidth(); |
1290 if (guess_width == display_rect_.width()) | 1295 if (guess_width == available_width) |
1291 break; | 1296 break; |
1292 if (guess_width > display_rect_.width()) { | 1297 if (guess_width > available_width) { |
1293 hi = guess - 1; | 1298 hi = guess - 1; |
1294 // Move back if we are on loop terminating condition, and guess is wider | 1299 // Move back on the loop terminating condition when the guess is too wide. |
1295 // than available. | |
1296 if (hi < lo) | 1300 if (hi < lo) |
1297 lo = hi; | 1301 lo = hi; |
1298 } else { | 1302 } else { |
1299 lo = guess + 1; | 1303 lo = guess + 1; |
1300 } | 1304 } |
1301 } | 1305 } |
1302 | 1306 |
1303 return render_text->text(); | 1307 return render_text->text(); |
1304 } | 1308 } |
1305 | 1309 |
| 1310 base::string16 RenderText::ElideEmail(const base::string16& email, |
| 1311 float available_width) { |
| 1312 // The returned string will have at least one character besides the ellipsis |
| 1313 // on either side of '@'; if that's impossible, a single ellipsis is returned. |
| 1314 // If possible, only the username is elided. Otherwise, the domain is elided |
| 1315 // in the middle, splitting available width equally with the elided username. |
| 1316 // If the username is short enough that it doesn't need half the available |
| 1317 // width, the elided domain will occupy that extra width. |
| 1318 |
| 1319 // Split the email into its local-part (username) and domain-part. The email |
| 1320 // spec allows for @ symbols in the username under some special requirements, |
| 1321 // but not in the domain part, so splitting at the last @ symbol is safe. |
| 1322 const size_t split_index = email.find_last_of('@'); |
| 1323 DCHECK_NE(split_index, base::string16::npos); |
| 1324 base::string16 username = email.substr(0, split_index); |
| 1325 base::string16 domain = email.substr(split_index + 1); |
| 1326 DCHECK(!username.empty()); |
| 1327 DCHECK(!domain.empty()); |
| 1328 |
| 1329 // Subtract the @ symbol from the available width as it is mandatory. |
| 1330 const base::string16 kAtSignUTF16 = base::ASCIIToUTF16("@"); |
| 1331 available_width -= GetStringWidthF(kAtSignUTF16, font_list()); |
| 1332 |
| 1333 // Check whether eliding the domain is necessary: if eliding the username |
| 1334 // is sufficient, the domain will not be elided. |
| 1335 const float full_username_width = GetStringWidthF(username, font_list()); |
| 1336 const float available_domain_width = available_width - |
| 1337 std::min(full_username_width, |
| 1338 GetStringWidthF(username.substr(0, 1) + kEllipsisUTF16, font_list())); |
| 1339 if (GetStringWidthF(domain, font_list()) > available_domain_width) { |
| 1340 // Elide the domain so that it only takes half of the available width. |
| 1341 // Should the username not need all the width available in its half, the |
| 1342 // domain will occupy the leftover width. |
| 1343 // If |desired_domain_width| is greater than |available_domain_width|: the |
| 1344 // minimal username elision allowed by the specifications will not fit; thus |
| 1345 // |desired_domain_width| must be <= |available_domain_width| at all cost. |
| 1346 const float desired_domain_width = |
| 1347 std::min<float>(available_domain_width, |
| 1348 std::max<float>(available_width - full_username_width, |
| 1349 available_width / 2)); |
| 1350 domain = Elide(domain, desired_domain_width, ELIDE_MIDDLE); |
| 1351 // Failing to elide the domain such that at least one character remains |
| 1352 // (other than the ellipsis itself) remains: return a single ellipsis. |
| 1353 if (domain.length() <= 1U) |
| 1354 return base::string16(kEllipsisUTF16); |
| 1355 } |
| 1356 |
| 1357 // Fit the username in the remaining width (at this point the elided username |
| 1358 // is guaranteed to fit with at least one character remaining given all the |
| 1359 // precautions taken earlier). |
| 1360 available_width -= GetStringWidthF(domain, font_list()); |
| 1361 username = Elide(username, available_width, ELIDE_TAIL); |
| 1362 return username + kAtSignUTF16 + domain; |
| 1363 } |
| 1364 |
1306 void RenderText::UpdateCachedBoundsAndOffset() { | 1365 void RenderText::UpdateCachedBoundsAndOffset() { |
1307 if (cached_bounds_and_offset_valid_) | 1366 if (cached_bounds_and_offset_valid_) |
1308 return; | 1367 return; |
1309 | 1368 |
1310 // TODO(ckocagil): Add support for scrolling multiline text. | 1369 // TODO(ckocagil): Add support for scrolling multiline text. |
1311 | 1370 |
1312 // First, set the valid flag true to calculate the current cursor bounds using | 1371 // First, set the valid flag true to calculate the current cursor bounds using |
1313 // the stale |display_offset_|. Applying |delta_offset| at the end of this | 1372 // the stale |display_offset_|. Applying |delta_offset| at the end of this |
1314 // function will set |cursor_bounds_| and |display_offset_| to correct values. | 1373 // function will set |cursor_bounds_| and |display_offset_| to correct values. |
1315 cached_bounds_and_offset_valid_ = true; | 1374 cached_bounds_and_offset_valid_ = true; |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1353 cursor_bounds_ += delta_offset; | 1412 cursor_bounds_ += delta_offset; |
1354 } | 1413 } |
1355 | 1414 |
1356 void RenderText::DrawSelection(Canvas* canvas) { | 1415 void RenderText::DrawSelection(Canvas* canvas) { |
1357 const std::vector<Rect> sel = GetSubstringBounds(selection()); | 1416 const std::vector<Rect> sel = GetSubstringBounds(selection()); |
1358 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) | 1417 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) |
1359 canvas->FillRect(*i, selection_background_focused_color_); | 1418 canvas->FillRect(*i, selection_background_focused_color_); |
1360 } | 1419 } |
1361 | 1420 |
1362 } // namespace gfx | 1421 } // namespace gfx |
OLD | NEW |