Chromium Code Reviews| 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/utf_string_conversions.h" | 14 #include "base/strings/utf_string_conversions.h" |
| 15 #include "third_party/icu/source/common/unicode/rbbi.h" | 15 #include "third_party/icu/source/common/unicode/rbbi.h" |
| 16 #include "third_party/icu/source/common/unicode/utf16.h" | 16 #include "third_party/icu/source/common/unicode/utf16.h" |
| 17 #include "third_party/skia/include/core/SkTypeface.h" | 17 #include "third_party/skia/include/core/SkTypeface.h" |
| 18 #include "third_party/skia/include/effects/SkGradientShader.h" | 18 #include "third_party/skia/include/effects/SkGradientShader.h" |
| 19 #include "ui/gfx/canvas.h" | 19 #include "ui/gfx/canvas.h" |
| 20 #include "ui/gfx/insets.h" | 20 #include "ui/gfx/insets.h" |
| 21 #include "ui/gfx/render_text_harfbuzz.h" | 21 #include "ui/gfx/render_text_harfbuzz.h" |
| 22 #include "ui/gfx/scoped_canvas.h" | 22 #include "ui/gfx/scoped_canvas.h" |
| 23 #include "ui/gfx/skia_util.h" | 23 #include "ui/gfx/skia_util.h" |
| 24 #include "ui/gfx/switches.h" | 24 #include "ui/gfx/switches.h" |
| 25 #include "ui/gfx/text_constants.h" | |
| 26 #include "ui/gfx/text_elider.h" | 25 #include "ui/gfx/text_elider.h" |
| 27 #include "ui/gfx/text_utils.h" | 26 #include "ui/gfx/text_utils.h" |
| 28 #include "ui/gfx/utf16_indexing.h" | 27 #include "ui/gfx/utf16_indexing.h" |
| 29 | 28 |
| 30 namespace gfx { | 29 namespace gfx { |
| 31 | 30 |
| 32 namespace { | 31 namespace { |
| 33 | 32 |
| 34 // All chars are replaced by this char when the password style is set. | 33 // All chars are replaced by this char when the password style is set. |
| 35 // TODO(benrg): GTK uses the first of U+25CF, U+2022, U+2731, U+273A, '*' | 34 // 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... | |
| 506 UpdateLayoutText(); | 505 UpdateLayoutText(); |
| 507 } | 506 } |
| 508 } | 507 } |
| 509 | 508 |
| 510 void RenderText::SetDisplayRect(const Rect& r) { | 509 void RenderText::SetDisplayRect(const Rect& r) { |
| 511 if (r != display_rect_) { | 510 if (r != display_rect_) { |
| 512 display_rect_ = r; | 511 display_rect_ = r; |
| 513 baseline_ = kInvalidBaseline; | 512 baseline_ = kInvalidBaseline; |
| 514 cached_bounds_and_offset_valid_ = false; | 513 cached_bounds_and_offset_valid_ = false; |
| 515 lines_.clear(); | 514 lines_.clear(); |
| 516 if (elide_behavior_ != TRUNCATE) | 515 if (elide_behavior_ != NO_ELIDE) |
| 517 UpdateLayoutText(); | 516 UpdateLayoutText(); |
| 518 } | 517 } |
| 519 } | 518 } |
| 520 | 519 |
| 521 void RenderText::SetCursorPosition(size_t position) { | 520 void RenderText::SetCursorPosition(size_t position) { |
| 522 MoveCursorTo(position, false); | 521 MoveCursorTo(position, false); |
| 523 } | 522 } |
| 524 | 523 |
| 525 void RenderText::MoveCursor(BreakType break_type, | 524 void RenderText::MoveCursor(BreakType break_type, |
| 526 VisualCursorDirection direction, | 525 VisualCursorDirection direction, |
| (...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 738 VisualCursorDirection RenderText::GetVisualDirectionOfLogicalEnd() { | 737 VisualCursorDirection RenderText::GetVisualDirectionOfLogicalEnd() { |
| 739 return GetTextDirection() == base::i18n::LEFT_TO_RIGHT ? | 738 return GetTextDirection() == base::i18n::LEFT_TO_RIGHT ? |
| 740 CURSOR_RIGHT : CURSOR_LEFT; | 739 CURSOR_RIGHT : CURSOR_LEFT; |
| 741 } | 740 } |
| 742 | 741 |
| 743 SizeF RenderText::GetStringSizeF() { | 742 SizeF RenderText::GetStringSizeF() { |
| 744 const Size size = GetStringSize(); | 743 const Size size = GetStringSize(); |
| 745 return SizeF(size.width(), size.height()); | 744 return SizeF(size.width(), size.height()); |
| 746 } | 745 } |
| 747 | 746 |
| 748 int RenderText::GetContentWidth() { | 747 float RenderText::GetContentWidth() { |
| 749 return GetStringSize().width() + (cursor_enabled_ ? 1 : 0); | 748 return GetStringSizeF().width() + (cursor_enabled_ ? 1 : 0); |
| 750 } | 749 } |
| 751 | 750 |
| 752 int RenderText::GetBaseline() { | 751 int RenderText::GetBaseline() { |
| 753 if (baseline_ == kInvalidBaseline) | 752 if (baseline_ == kInvalidBaseline) |
| 754 baseline_ = DetermineBaselineCenteringText(display_rect(), font_list()); | 753 baseline_ = DetermineBaselineCenteringText(display_rect(), font_list()); |
| 755 DCHECK_NE(kInvalidBaseline, baseline_); | 754 DCHECK_NE(kInvalidBaseline, baseline_); |
| 756 return baseline_; | 755 return baseline_; |
| 757 } | 756 } |
| 758 | 757 |
| 759 void RenderText::Draw(Canvas* canvas) { | 758 void RenderText::Draw(Canvas* canvas) { |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 883 selection_color_(kDefaultColor), | 882 selection_color_(kDefaultColor), |
| 884 selection_background_focused_color_(kDefaultSelectionBackgroundColor), | 883 selection_background_focused_color_(kDefaultSelectionBackgroundColor), |
| 885 focused_(false), | 884 focused_(false), |
| 886 composition_range_(Range::InvalidRange()), | 885 composition_range_(Range::InvalidRange()), |
| 887 colors_(kDefaultColor), | 886 colors_(kDefaultColor), |
| 888 styles_(NUM_TEXT_STYLES), | 887 styles_(NUM_TEXT_STYLES), |
| 889 composition_and_selection_styles_applied_(false), | 888 composition_and_selection_styles_applied_(false), |
| 890 obscured_(false), | 889 obscured_(false), |
| 891 obscured_reveal_index_(-1), | 890 obscured_reveal_index_(-1), |
| 892 truncate_length_(0), | 891 truncate_length_(0), |
| 893 elide_behavior_(TRUNCATE), | 892 elide_behavior_(NO_ELIDE), |
| 894 multiline_(false), | 893 multiline_(false), |
| 895 background_is_transparent_(false), | 894 background_is_transparent_(false), |
| 896 clip_to_display_rect_(true), | 895 clip_to_display_rect_(true), |
| 897 baseline_(kInvalidBaseline), | 896 baseline_(kInvalidBaseline), |
| 898 cached_bounds_and_offset_valid_(false) { | 897 cached_bounds_and_offset_valid_(false) { |
| 899 } | 898 } |
| 900 | 899 |
| 901 const Vector2d& RenderText::GetUpdatedDisplayOffset() { | 900 const Vector2d& RenderText::GetUpdatedDisplayOffset() { |
| 902 UpdateCachedBoundsAndOffset(); | 901 UpdateCachedBoundsAndOffset(); |
| 903 return display_offset_; | 902 return display_offset_; |
| (...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1152 layout_text_.replace(cp_start, 1, text_.substr(start, end - start)); | 1151 layout_text_.replace(cp_start, 1, text_.substr(start, end - start)); |
| 1153 } | 1152 } |
| 1154 } else { | 1153 } else { |
| 1155 layout_text_ = text_; | 1154 layout_text_ = text_; |
| 1156 } | 1155 } |
| 1157 | 1156 |
| 1158 const base::string16& text = layout_text_; | 1157 const base::string16& text = layout_text_; |
| 1159 if (truncate_length_ > 0 && truncate_length_ < text.length()) { | 1158 if (truncate_length_ > 0 && truncate_length_ < text.length()) { |
| 1160 // Truncate the text at a valid character break and append an ellipsis. | 1159 // Truncate the text at a valid character break and append an ellipsis. |
| 1161 icu::StringCharacterIterator iter(text.c_str()); | 1160 icu::StringCharacterIterator iter(text.c_str()); |
| 1162 iter.setIndex32(truncate_length_ - 1); | 1161 // Respect ELIDE_HEAD and ELIDE_MIDDLE preferences during truncation. |
| 1163 layout_text_.assign(text.substr(0, iter.getIndex()) + kEllipsisUTF16); | 1162 if (elide_behavior_ == ELIDE_HEAD) { |
| 1163 iter.setIndex32(text.length() - truncate_length_ + 1); | |
|
sky
2014/07/09 18:19:38
Is it possible for truncate_length_ to equal text.
msw
2014/07/09 19:16:08
The condition at line 1158 ensures |truncate_lengt
| |
| 1164 layout_text_.assign(kEllipsisUTF16 + text.substr(iter.getIndex())); | |
| 1165 } else if (elide_behavior_ == ELIDE_MIDDLE) { | |
| 1166 iter.setIndex32(truncate_length_ / 2); | |
| 1167 const size_t ellipsis_start = iter.getIndex(); | |
| 1168 iter.setIndex32(text.length() - (truncate_length_ / 2)); | |
| 1169 const size_t ellipsis_end = iter.getIndex(); | |
| 1170 layout_text_.assign(text.substr(0, ellipsis_start) + kEllipsisUTF16 + | |
| 1171 text.substr(ellipsis_end)); | |
|
sky
2014/07/09 18:19:38
Might it be possible for ellipsis_start to be > el
msw
2014/07/09 19:16:08
Afaict, that would only happen if |truncate_length
| |
| 1172 } else { | |
| 1173 iter.setIndex32(truncate_length_ - 1); | |
| 1174 layout_text_.assign(text.substr(0, iter.getIndex()) + kEllipsisUTF16); | |
| 1175 } | |
| 1164 } | 1176 } |
| 1165 | 1177 |
| 1166 if (elide_behavior_ != TRUNCATE && elide_behavior_ != FADE_TAIL && | 1178 if (elide_behavior_ != NO_ELIDE && elide_behavior_ != FADE_TAIL && |
| 1167 display_rect_.width() > 0 && !layout_text_.empty() && | 1179 !layout_text_.empty() && GetContentWidth() > display_rect_.width()) { |
| 1168 GetContentWidth() > display_rect_.width()) { | |
| 1169 // This doesn't trim styles so ellipsis may get rendered as a different | 1180 // This doesn't trim styles so ellipsis may get rendered as a different |
| 1170 // style than the preceding text. See crbug.com/327850. | 1181 // style than the preceding text. See crbug.com/327850. |
| 1171 layout_text_.assign(ElideText(layout_text_)); | 1182 layout_text_.assign( |
| 1183 Elide(layout_text_, display_rect_.width(), elide_behavior_)); | |
| 1172 } | 1184 } |
| 1173 | 1185 |
| 1174 ResetLayout(); | 1186 ResetLayout(); |
| 1175 } | 1187 } |
| 1176 | 1188 |
| 1177 // TODO(skanuj): Fix code duplication with ElideText in ui/gfx/text_elider.cc | 1189 base::string16 RenderText::Elide(const base::string16& text, |
| 1178 // See crbug.com/327846 | 1190 float available_width, |
| 1179 base::string16 RenderText::ElideText(const base::string16& text) { | 1191 ElideBehavior behavior) { |
| 1180 const bool insert_ellipsis = (elide_behavior_ != TRUNCATE); | 1192 if (available_width <= 0 || text.empty()) |
| 1193 return base::string16(); | |
| 1194 if (behavior == ELIDE_EMAIL) | |
| 1195 return ElideEmail(text, available_width); | |
| 1196 | |
| 1181 // Create a RenderText copy with attributes that affect the rendering width. | 1197 // Create a RenderText copy with attributes that affect the rendering width. |
| 1182 scoped_ptr<RenderText> render_text(CreateInstance()); | 1198 scoped_ptr<RenderText> render_text(CreateInstance()); |
| 1183 render_text->SetFontList(font_list_); | 1199 render_text->SetFontList(font_list_); |
| 1184 render_text->SetDirectionalityMode(directionality_mode_); | 1200 render_text->SetDirectionalityMode(directionality_mode_); |
| 1185 render_text->SetCursorEnabled(cursor_enabled_); | 1201 render_text->SetCursorEnabled(cursor_enabled_); |
| 1186 | 1202 render_text->set_truncate_length(truncate_length_); |
| 1187 render_text->styles_ = styles_; | 1203 render_text->styles_ = styles_; |
| 1188 render_text->colors_ = colors_; | 1204 render_text->colors_ = colors_; |
| 1189 render_text->SetText(text); | 1205 render_text->SetText(text); |
| 1190 const int current_text_pixel_width = render_text->GetContentWidth(); | 1206 if (render_text->GetContentWidth() <= available_width) |
| 1207 return text; | |
| 1191 | 1208 |
| 1192 const base::string16 ellipsis = base::string16(kEllipsisUTF16); | 1209 const base::string16 ellipsis = base::string16(kEllipsisUTF16); |
| 1193 const bool elide_in_middle = false; | 1210 const bool insert_ellipsis = (behavior != TRUNCATE); |
| 1194 const bool elide_at_beginning = false; | 1211 const bool elide_in_middle = (behavior == ELIDE_MIDDLE); |
| 1212 const bool elide_at_beginning = (behavior == ELIDE_HEAD); | |
| 1195 StringSlicer slicer(text, ellipsis, elide_in_middle, elide_at_beginning); | 1213 StringSlicer slicer(text, ellipsis, elide_in_middle, elide_at_beginning); |
| 1196 | 1214 |
| 1197 // Pango will return 0 width for absurdly long strings. Cut the string in | 1215 render_text->SetText(ellipsis); |
| 1198 // half and try again. | 1216 const float ellipsis_width = render_text->GetContentWidth(); |
| 1199 // This is caused by an int overflow in Pango (specifically, in | |
| 1200 // pango_glyph_string_extents_range). It's actually more subtle than just | |
| 1201 // returning 0, since on super absurdly long strings, the int can wrap and | |
| 1202 // return positive numbers again. Detecting that is probably not worth it | |
| 1203 // (eliding way too much from a ridiculous string is probably still | |
| 1204 // ridiculous), but we should check other widths for bogus values as well. | |
| 1205 if (current_text_pixel_width <= 0 && !text.empty()) | |
| 1206 return ElideText(slicer.CutString(text.length() / 2, insert_ellipsis)); | |
| 1207 | 1217 |
| 1208 if (current_text_pixel_width <= display_rect_.width()) | 1218 if (insert_ellipsis && (ellipsis_width > available_width)) |
| 1209 return text; | |
| 1210 | |
| 1211 render_text->SetText(base::string16()); | |
| 1212 render_text->SetText(ellipsis); | |
| 1213 const int ellipsis_width = render_text->GetContentWidth(); | |
| 1214 | |
| 1215 if (insert_ellipsis && (ellipsis_width >= display_rect_.width())) | |
| 1216 return base::string16(); | 1219 return base::string16(); |
| 1217 | 1220 |
| 1218 // Use binary search to compute the elided text. | 1221 // Use binary search to compute the elided text. |
| 1219 size_t lo = 0; | 1222 size_t lo = 0; |
| 1220 size_t hi = text.length() - 1; | 1223 size_t hi = text.length() - 1; |
| 1221 const base::i18n::TextDirection text_direction = GetTextDirection(); | 1224 const base::i18n::TextDirection text_direction = GetTextDirection(); |
| 1222 for (size_t guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) { | 1225 for (size_t guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) { |
| 1223 // Restore styles and colors. They will be truncated to size by SetText. | 1226 // Restore styles and colors. They will be truncated to size by SetText. |
| 1224 render_text->styles_ = styles_; | 1227 render_text->styles_ = styles_; |
| 1225 render_text->colors_ = colors_; | 1228 render_text->colors_ = colors_; |
| 1226 base::string16 new_text = slicer.CutString(guess, false); | 1229 base::string16 new_text = |
| 1230 slicer.CutString(guess, insert_ellipsis && behavior != ELIDE_TAIL); | |
| 1227 render_text->SetText(new_text); | 1231 render_text->SetText(new_text); |
| 1228 | 1232 |
| 1229 // This has to be an additional step so that the ellipsis is rendered with | 1233 // This has to be an additional step so that the ellipsis is rendered with |
| 1230 // same style as trailing part of the text. | 1234 // same style as trailing part of the text. |
| 1231 if (insert_ellipsis) { | 1235 if (insert_ellipsis && behavior == ELIDE_TAIL) { |
| 1232 // When ellipsis follows text whose directionality is not the same as that | 1236 // When ellipsis follows text whose directionality is not the same as that |
| 1233 // of the whole text, it will be rendered with the directionality of the | 1237 // of the whole text, it will be rendered with the directionality of the |
| 1234 // whole text. Since we want ellipsis to indicate continuation of the | 1238 // whole text. Since we want ellipsis to indicate continuation of the |
| 1235 // preceding text, we force the directionality of ellipsis to be same as | 1239 // preceding text, we force the directionality of ellipsis to be same as |
| 1236 // the preceding text using LTR or RTL markers. | 1240 // the preceding text using LTR or RTL markers. |
| 1237 base::i18n::TextDirection trailing_text_direction = | 1241 base::i18n::TextDirection trailing_text_direction = |
| 1238 base::i18n::GetLastStrongCharacterDirection(new_text); | 1242 base::i18n::GetLastStrongCharacterDirection(new_text); |
| 1239 new_text.append(ellipsis); | 1243 new_text.append(ellipsis); |
| 1240 if (trailing_text_direction != text_direction) { | 1244 if (trailing_text_direction != text_direction) { |
| 1241 if (trailing_text_direction == base::i18n::LEFT_TO_RIGHT) | 1245 if (trailing_text_direction == base::i18n::LEFT_TO_RIGHT) |
| 1242 new_text += base::i18n::kLeftToRightMark; | 1246 new_text += base::i18n::kLeftToRightMark; |
| 1243 else | 1247 else |
| 1244 new_text += base::i18n::kRightToLeftMark; | 1248 new_text += base::i18n::kRightToLeftMark; |
| 1245 } | 1249 } |
| 1246 render_text->SetText(new_text); | 1250 render_text->SetText(new_text); |
| 1247 } | 1251 } |
| 1248 | 1252 |
| 1249 // We check the width of the whole desired string at once to ensure we | 1253 // We check the width of the whole desired string at once to ensure we |
| 1250 // handle kerning/ligatures/etc. correctly. | 1254 // handle kerning/ligatures/etc. correctly. |
| 1251 const int guess_width = render_text->GetContentWidth(); | 1255 const float guess_width = render_text->GetContentWidth(); |
| 1252 if (guess_width == display_rect_.width()) | 1256 if (guess_width == available_width) |
| 1253 break; | 1257 break; |
| 1254 if (guess_width > display_rect_.width()) { | 1258 if (guess_width > available_width) { |
| 1255 hi = guess - 1; | 1259 hi = guess - 1; |
| 1256 // Move back if we are on loop terminating condition, and guess is wider | 1260 // Move back on the loop terminating condition when the guess is too wide. |
| 1257 // than available. | |
| 1258 if (hi < lo) | 1261 if (hi < lo) |
| 1259 lo = hi; | 1262 lo = hi; |
| 1260 } else { | 1263 } else { |
| 1261 lo = guess + 1; | 1264 lo = guess + 1; |
| 1262 } | 1265 } |
| 1263 } | 1266 } |
| 1264 | 1267 |
| 1265 return render_text->text(); | 1268 return render_text->text(); |
| 1266 } | 1269 } |
| 1267 | 1270 |
| 1271 base::string16 RenderText::ElideEmail(const base::string16& email, | |
| 1272 float available_width) { | |
| 1273 // The returned string will have at least one character besides the ellipsis | |
| 1274 // on either side of '@'; if that's impossible, a single ellipsis is returned. | |
| 1275 // If possible, only the username is elided. Otherwise, the domain is elided | |
| 1276 // in the middle, splitting available width equally with the elided username. | |
| 1277 // If the username is short enough that it doesn't need half the available | |
| 1278 // width, the elided domain will occupy that extra width. | |
| 1279 | |
| 1280 // Split the email into its local-part (username) and domain-part. The email | |
| 1281 // spec allows for @ symbols in the username under some special requirements, | |
| 1282 // but not in the domain part, so splitting at the last @ symbol is safe. | |
| 1283 const size_t split_index = email.find_last_of('@'); | |
| 1284 DCHECK_NE(split_index, base::string16::npos); | |
| 1285 base::string16 username = email.substr(0, split_index); | |
| 1286 base::string16 domain = email.substr(split_index + 1); | |
| 1287 DCHECK(!username.empty()); | |
| 1288 DCHECK(!domain.empty()); | |
| 1289 | |
| 1290 // Subtract the @ symbol from the available width as it is mandatory. | |
| 1291 const base::string16 kAtSignUTF16 = base::ASCIIToUTF16("@"); | |
| 1292 available_width -= GetStringWidthF(kAtSignUTF16, font_list()); | |
| 1293 | |
| 1294 // Check whether eliding the domain is necessary: if eliding the username | |
| 1295 // is sufficient, the domain will not be elided. | |
| 1296 const float full_username_width = GetStringWidthF(username, font_list()); | |
| 1297 const float available_domain_width = available_width - | |
| 1298 std::min(full_username_width, | |
| 1299 GetStringWidthF(username.substr(0, 1) + kEllipsisUTF16, font_list())); | |
| 1300 if (GetStringWidthF(domain, font_list()) > available_domain_width) { | |
| 1301 // Elide the domain so that it only takes half of the available width. | |
| 1302 // Should the username not need all the width available in its half, the | |
| 1303 // domain will occupy the leftover width. | |
| 1304 // If |desired_domain_width| is greater than |available_domain_width|: the | |
| 1305 // minimal username elision allowed by the specifications will not fit; thus | |
| 1306 // |desired_domain_width| must be <= |available_domain_width| at all cost. | |
| 1307 const float desired_domain_width = | |
| 1308 std::min<float>(available_domain_width, | |
| 1309 std::max<float>(available_width - full_username_width, | |
| 1310 available_width / 2)); | |
| 1311 domain = Elide(domain, desired_domain_width, ELIDE_MIDDLE); | |
| 1312 // Failing to elide the domain such that at least one character remains | |
| 1313 // (other than the ellipsis itself) remains: return a single ellipsis. | |
| 1314 if (domain.length() <= 1U) | |
| 1315 return base::string16(kEllipsisUTF16); | |
| 1316 } | |
| 1317 | |
| 1318 // Fit the username in the remaining width (at this point the elided username | |
| 1319 // is guaranteed to fit with at least one character remaining given all the | |
| 1320 // precautions taken earlier). | |
| 1321 available_width -= GetStringWidthF(domain, font_list()); | |
| 1322 username = Elide(username, available_width, ELIDE_TAIL); | |
| 1323 return username + kAtSignUTF16 + domain; | |
| 1324 } | |
| 1325 | |
| 1268 void RenderText::UpdateCachedBoundsAndOffset() { | 1326 void RenderText::UpdateCachedBoundsAndOffset() { |
| 1269 if (cached_bounds_and_offset_valid_) | 1327 if (cached_bounds_and_offset_valid_) |
| 1270 return; | 1328 return; |
| 1271 | 1329 |
| 1272 // TODO(ckocagil): Add support for scrolling multiline text. | 1330 // TODO(ckocagil): Add support for scrolling multiline text. |
| 1273 | 1331 |
| 1274 // First, set the valid flag true to calculate the current cursor bounds using | 1332 // First, set the valid flag true to calculate the current cursor bounds using |
| 1275 // the stale |display_offset_|. Applying |delta_offset| at the end of this | 1333 // the stale |display_offset_|. Applying |delta_offset| at the end of this |
| 1276 // function will set |cursor_bounds_| and |display_offset_| to correct values. | 1334 // function will set |cursor_bounds_| and |display_offset_| to correct values. |
| 1277 cached_bounds_and_offset_valid_ = true; | 1335 cached_bounds_and_offset_valid_ = true; |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1315 cursor_bounds_ += delta_offset; | 1373 cursor_bounds_ += delta_offset; |
| 1316 } | 1374 } |
| 1317 | 1375 |
| 1318 void RenderText::DrawSelection(Canvas* canvas) { | 1376 void RenderText::DrawSelection(Canvas* canvas) { |
| 1319 const std::vector<Rect> sel = GetSubstringBounds(selection()); | 1377 const std::vector<Rect> sel = GetSubstringBounds(selection()); |
| 1320 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) | 1378 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) |
| 1321 canvas->FillRect(*i, selection_background_focused_color_); | 1379 canvas->FillRect(*i, selection_background_focused_color_); |
| 1322 } | 1380 } |
| 1323 | 1381 |
| 1324 } // namespace gfx | 1382 } // namespace gfx |
| OLD | NEW |