Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(373)

Side by Side Diff: ui/gfx/render_text.cc

Issue 916203002: Reduce the number of text reshaping in RenderText (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 "base/trace_event/trace_event.h"
16 #include "third_party/icu/source/common/unicode/rbbi.h" 17 #include "third_party/icu/source/common/unicode/rbbi.h"
17 #include "third_party/icu/source/common/unicode/utf16.h" 18 #include "third_party/icu/source/common/unicode/utf16.h"
18 #include "third_party/skia/include/core/SkTypeface.h" 19 #include "third_party/skia/include/core/SkTypeface.h"
19 #include "third_party/skia/include/effects/SkGradientShader.h" 20 #include "third_party/skia/include/effects/SkGradientShader.h"
20 #include "ui/gfx/canvas.h" 21 #include "ui/gfx/canvas.h"
21 #include "ui/gfx/geometry/insets.h" 22 #include "ui/gfx/geometry/insets.h"
22 #include "ui/gfx/geometry/safe_integer_conversions.h" 23 #include "ui/gfx/geometry/safe_integer_conversions.h"
23 #include "ui/gfx/render_text_harfbuzz.h" 24 #include "ui/gfx/render_text_harfbuzz.h"
24 #include "ui/gfx/scoped_canvas.h" 25 #include "ui/gfx/scoped_canvas.h"
25 #include "ui/gfx/skia_util.h" 26 #include "ui/gfx/skia_util.h"
(...skipping 430 matching lines...) Expand 10 before | Expand all | Expand 10 after
456 } 457 }
457 458
458 void RenderText::SetFontList(const FontList& font_list) { 459 void RenderText::SetFontList(const FontList& font_list) {
459 font_list_ = font_list; 460 font_list_ = font_list;
460 const int font_style = font_list.GetFontStyle(); 461 const int font_style = font_list.GetFontStyle();
461 SetStyle(BOLD, (font_style & gfx::Font::BOLD) != 0); 462 SetStyle(BOLD, (font_style & gfx::Font::BOLD) != 0);
462 SetStyle(ITALIC, (font_style & gfx::Font::ITALIC) != 0); 463 SetStyle(ITALIC, (font_style & gfx::Font::ITALIC) != 0);
463 SetStyle(UNDERLINE, (font_style & gfx::Font::UNDERLINE) != 0); 464 SetStyle(UNDERLINE, (font_style & gfx::Font::UNDERLINE) != 0);
464 baseline_ = kInvalidBaseline; 465 baseline_ = kInvalidBaseline;
465 cached_bounds_and_offset_valid_ = false; 466 cached_bounds_and_offset_valid_ = false;
466 ResetLayout(); 467 OnLayoutTextShapeChanged(false);
467 } 468 }
468 469
469 void RenderText::SetCursorEnabled(bool cursor_enabled) { 470 void RenderText::SetCursorEnabled(bool cursor_enabled) {
470 cursor_enabled_ = cursor_enabled; 471 cursor_enabled_ = cursor_enabled;
471 cached_bounds_and_offset_valid_ = false; 472 cached_bounds_and_offset_valid_ = false;
472 } 473 }
473 474
474 void RenderText::ToggleInsertMode() { 475 void RenderText::ToggleInsertMode() {
475 insert_mode_ = !insert_mode_; 476 insert_mode_ = !insert_mode_;
476 cached_bounds_and_offset_valid_ = false; 477 cached_bounds_and_offset_valid_ = false;
(...skipping 21 matching lines...) Expand all
498 replace_newline_chars_with_symbols_ = replace; 499 replace_newline_chars_with_symbols_ = replace;
499 cached_bounds_and_offset_valid_ = false; 500 cached_bounds_and_offset_valid_ = false;
500 UpdateLayoutText(); 501 UpdateLayoutText();
501 } 502 }
502 503
503 void RenderText::SetMultiline(bool multiline) { 504 void RenderText::SetMultiline(bool multiline) {
504 if (multiline != multiline_) { 505 if (multiline != multiline_) {
505 multiline_ = multiline; 506 multiline_ = multiline;
506 cached_bounds_and_offset_valid_ = false; 507 cached_bounds_and_offset_valid_ = false;
507 lines_.clear(); 508 lines_.clear();
509 OnElidedTextShapeChanged();
508 } 510 }
509 } 511 }
510 512
511 void RenderText::SetMinLineHeight(int line_height) { 513 void RenderText::SetMinLineHeight(int line_height) {
512 if (min_line_height_ == line_height) 514 if (min_line_height_ == line_height)
513 return; 515 return;
514 min_line_height_ = line_height; 516 min_line_height_ = line_height;
515 cached_bounds_and_offset_valid_ = false; 517 cached_bounds_and_offset_valid_ = false;
516 lines_.clear(); 518 lines_.clear();
519 OnElidedTextShapeChanged();
517 } 520 }
518 521
519 void RenderText::SetElideBehavior(ElideBehavior elide_behavior) { 522 void RenderText::SetElideBehavior(ElideBehavior elide_behavior) {
520 // TODO(skanuj) : Add a test for triggering layout change. 523 // TODO(skanuj) : Add a test for triggering layout change.
521 if (elide_behavior_ != elide_behavior) { 524 if (elide_behavior_ != elide_behavior) {
522 elide_behavior_ = elide_behavior; 525 elide_behavior_ = elide_behavior;
523 UpdateLayoutText(); 526 OnElidedTextShapeChanged();
524 } 527 }
525 } 528 }
526 529
527 void RenderText::SetDisplayRect(const Rect& r) { 530 void RenderText::SetDisplayRect(const Rect& r) {
528 if (r != display_rect_) { 531 if (r != display_rect_) {
529 display_rect_ = r; 532 display_rect_ = r;
530 baseline_ = kInvalidBaseline; 533 baseline_ = kInvalidBaseline;
531 cached_bounds_and_offset_valid_ = false; 534 cached_bounds_and_offset_valid_ = false;
532 lines_.clear(); 535 lines_.clear();
533 if (elide_behavior_ != NO_ELIDE) 536 if (elide_behavior_ != NO_ELIDE &&
534 UpdateLayoutText(); 537 elide_behavior_ != FADE_TAIL) {
538 OnElidedTextShapeChanged();
539 }
535 } 540 }
536 } 541 }
537 542
538 void RenderText::SetCursorPosition(size_t position) { 543 void RenderText::SetCursorPosition(size_t position) {
539 MoveCursorTo(position, false); 544 MoveCursorTo(position, false);
540 } 545 }
541 546
542 void RenderText::MoveCursor(BreakType break_type, 547 void RenderText::MoveCursor(BreakType break_type,
543 VisualCursorDirection direction, 548 VisualCursorDirection direction,
544 bool select) { 549 bool select) {
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
650 655
651 const Range& RenderText::GetCompositionRange() const { 656 const Range& RenderText::GetCompositionRange() const {
652 return composition_range_; 657 return composition_range_;
653 } 658 }
654 659
655 void RenderText::SetCompositionRange(const Range& composition_range) { 660 void RenderText::SetCompositionRange(const Range& composition_range) {
656 CHECK(!composition_range.IsValid() || 661 CHECK(!composition_range.IsValid() ||
657 Range(0, text_.length()).Contains(composition_range)); 662 Range(0, text_.length()).Contains(composition_range));
658 composition_range_.set_end(composition_range.end()); 663 composition_range_.set_end(composition_range.end());
659 composition_range_.set_start(composition_range.start()); 664 composition_range_.set_start(composition_range.start());
660 ResetLayout(); 665 OnLayoutTextShapeChanged(false);
661 } 666 }
662 667
663 void RenderText::SetColor(SkColor value) { 668 void RenderText::SetColor(SkColor value) {
664 colors_.SetValue(value); 669 colors_.SetValue(value);
665 } 670 }
666 671
667 void RenderText::ApplyColor(SkColor value, const Range& range) { 672 void RenderText::ApplyColor(SkColor value, const Range& range) {
668 colors_.ApplyValue(value, range); 673 colors_.ApplyValue(value, range);
669 } 674 }
670 675
671 void RenderText::SetStyle(TextStyle style, bool value) { 676 void RenderText::SetStyle(TextStyle style, bool value) {
672 styles_[style].SetValue(value); 677 styles_[style].SetValue(value);
673 678
674 cached_bounds_and_offset_valid_ = false; 679 cached_bounds_and_offset_valid_ = false;
675 ResetLayout(); 680 OnLayoutTextShapeChanged(false);
676 } 681 }
677 682
678 void RenderText::ApplyStyle(TextStyle style, bool value, const Range& range) { 683 void RenderText::ApplyStyle(TextStyle style, bool value, const Range& range) {
679 // Do not change styles mid-grapheme to avoid breaking ligatures. 684 // Do not change styles mid-grapheme to avoid breaking ligatures.
680 const size_t start = IsValidCursorIndex(range.start()) ? range.start() : 685 const size_t start = IsValidCursorIndex(range.start()) ? range.start() :
681 IndexOfAdjacentGrapheme(range.start(), CURSOR_BACKWARD); 686 IndexOfAdjacentGrapheme(range.start(), CURSOR_BACKWARD);
682 const size_t end = IsValidCursorIndex(range.end()) ? range.end() : 687 const size_t end = IsValidCursorIndex(range.end()) ? range.end() :
683 IndexOfAdjacentGrapheme(range.end(), CURSOR_FORWARD); 688 IndexOfAdjacentGrapheme(range.end(), CURSOR_FORWARD);
684 styles_[style].ApplyValue(value, Range(start, end)); 689 styles_[style].ApplyValue(value, Range(start, end));
685 690
686 cached_bounds_and_offset_valid_ = false; 691 cached_bounds_and_offset_valid_ = false;
687 ResetLayout(); 692 OnLayoutTextShapeChanged(false);
688 } 693 }
689 694
690 bool RenderText::GetStyle(TextStyle style) const { 695 bool RenderText::GetStyle(TextStyle style) const {
691 return (styles_[style].breaks().size() == 1) && 696 return (styles_[style].breaks().size() == 1) &&
692 styles_[style].breaks().front().second; 697 styles_[style].breaks().front().second;
693 } 698 }
694 699
695 void RenderText::SetDirectionalityMode(DirectionalityMode mode) { 700 void RenderText::SetDirectionalityMode(DirectionalityMode mode) {
696 if (mode == directionality_mode_) 701 if (mode == directionality_mode_)
697 return; 702 return;
698 703
699 directionality_mode_ = mode; 704 directionality_mode_ = mode;
700 text_direction_ = base::i18n::UNKNOWN_DIRECTION; 705 text_direction_ = base::i18n::UNKNOWN_DIRECTION;
701 cached_bounds_and_offset_valid_ = false; 706 cached_bounds_and_offset_valid_ = false;
702 ResetLayout(); 707 OnLayoutTextShapeChanged(false);
703 } 708 }
704 709
705 base::i18n::TextDirection RenderText::GetTextDirection() { 710 base::i18n::TextDirection RenderText::GetTextDirection() {
706 if (text_direction_ == base::i18n::UNKNOWN_DIRECTION) { 711 if (text_direction_ == base::i18n::UNKNOWN_DIRECTION) {
707 switch (directionality_mode_) { 712 switch (directionality_mode_) {
708 case DIRECTIONALITY_FROM_TEXT: 713 case DIRECTIONALITY_FROM_TEXT:
709 // Derive the direction from the display text, which differs from text() 714 // Derive the direction from the display text, which differs from text()
710 // in the case of obscured (password) textfields. 715 // in the case of obscured (password) textfields.
711 text_direction_ = 716 text_direction_ =
712 base::i18n::GetFirstStrongCharacterDirection(GetLayoutText()); 717 base::i18n::GetFirstStrongCharacterDirection(GetLayoutText());
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after
924 selection_background_focused_color_(kDefaultSelectionBackgroundColor), 929 selection_background_focused_color_(kDefaultSelectionBackgroundColor),
925 focused_(false), 930 focused_(false),
926 composition_range_(Range::InvalidRange()), 931 composition_range_(Range::InvalidRange()),
927 colors_(kDefaultColor), 932 colors_(kDefaultColor),
928 styles_(NUM_TEXT_STYLES), 933 styles_(NUM_TEXT_STYLES),
929 composition_and_selection_styles_applied_(false), 934 composition_and_selection_styles_applied_(false),
930 obscured_(false), 935 obscured_(false),
931 obscured_reveal_index_(-1), 936 obscured_reveal_index_(-1),
932 truncate_length_(0), 937 truncate_length_(0),
933 elide_behavior_(NO_ELIDE), 938 elide_behavior_(NO_ELIDE),
939 text_elided_(false),
934 replace_newline_chars_with_symbols_(true), 940 replace_newline_chars_with_symbols_(true),
935 min_line_height_(0), 941 min_line_height_(0),
936 multiline_(false), 942 multiline_(false),
937 background_is_transparent_(false), 943 background_is_transparent_(false),
938 clip_to_display_rect_(true), 944 clip_to_display_rect_(true),
939 baseline_(kInvalidBaseline), 945 baseline_(kInvalidBaseline),
940 cached_bounds_and_offset_valid_(false) { 946 cached_bounds_and_offset_valid_(false) {
941 } 947 }
942 948
943 SelectionModel RenderText::GetAdjacentSelectionModel( 949 SelectionModel RenderText::GetAdjacentSelectionModel(
(...skipping 16 matching lines...) Expand all
960 return SelectionModel(text().length(), CURSOR_FORWARD); 966 return SelectionModel(text().length(), CURSOR_FORWARD);
961 return SelectionModel(0, CURSOR_BACKWARD); 967 return SelectionModel(0, CURSOR_BACKWARD);
962 } 968 }
963 969
964 void RenderText::SetSelectionModel(const SelectionModel& model) { 970 void RenderText::SetSelectionModel(const SelectionModel& model) {
965 DCHECK_LE(model.selection().GetMax(), text().length()); 971 DCHECK_LE(model.selection().GetMax(), text().length());
966 selection_model_ = model; 972 selection_model_ = model;
967 cached_bounds_and_offset_valid_ = false; 973 cached_bounds_and_offset_valid_ = false;
968 } 974 }
969 975
970 const base::string16& RenderText::GetLayoutText() const { 976 void RenderText::UpdateElidedText(float visual_width) {
971 return layout_text_; 977 if (multiline_ ||
978 elide_behavior() == NO_ELIDE ||
979 elide_behavior() == FADE_TAIL ||
980 visual_width < display_rect_.width() ||
981 layout_text_.empty()) {
982 text_elided_ = false;
983 elided_text_.clear();
984 return;
985 }
986
987 // This doesn't trim styles so ellipsis may get rendered as a different
988 // style than the preceding text. See crbug.com/327850.
989 elided_text_.assign(Elide(layout_text_,
990 visual_width,
991 static_cast<float>(display_rect_.width()),
992 elide_behavior_));
993
994 text_elided_ = elided_text_ != layout_text_;
995 if (!text_elided_)
996 elided_text_.clear();
972 } 997 }
973 998
974 const BreakList<size_t>& RenderText::GetLineBreaks() { 999 const BreakList<size_t>& RenderText::GetLineBreaks() {
975 if (line_breaks_.max() != 0) 1000 if (line_breaks_.max() != 0)
976 return line_breaks_; 1001 return line_breaks_;
977 1002
978 const base::string16& layout_text = GetLayoutText(); 1003 const base::string16& layout_text = GetLayoutText();
979 const size_t text_length = layout_text.length(); 1004 const size_t text_length = layout_text.length();
980 line_breaks_.SetValue(0); 1005 line_breaks_.SetValue(0);
981 line_breaks_.SetMax(text_length); 1006 line_breaks_.SetMax(text_length);
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after
1169 void RenderText::MoveCursorTo(size_t position, bool select) { 1194 void RenderText::MoveCursorTo(size_t position, bool select) {
1170 size_t cursor = std::min(position, text().length()); 1195 size_t cursor = std::min(position, text().length());
1171 if (IsValidCursorIndex(cursor)) 1196 if (IsValidCursorIndex(cursor))
1172 SetSelectionModel(SelectionModel( 1197 SetSelectionModel(SelectionModel(
1173 Range(select ? selection().start() : cursor, cursor), 1198 Range(select ? selection().start() : cursor, cursor),
1174 (cursor == 0) ? CURSOR_FORWARD : CURSOR_BACKWARD)); 1199 (cursor == 0) ? CURSOR_FORWARD : CURSOR_BACKWARD));
1175 } 1200 }
1176 1201
1177 void RenderText::UpdateLayoutText() { 1202 void RenderText::UpdateLayoutText() {
1178 layout_text_.clear(); 1203 layout_text_.clear();
1204 elided_text_.clear();
1179 line_breaks_.SetMax(0); 1205 line_breaks_.SetMax(0);
1180 1206
1181 if (obscured_) { 1207 if (obscured_) {
1182 size_t obscured_text_length = 1208 size_t obscured_text_length =
1183 static_cast<size_t>(UTF16IndexToOffset(text_, 0, text_.length())); 1209 static_cast<size_t>(UTF16IndexToOffset(text_, 0, text_.length()));
1184 layout_text_.assign(obscured_text_length, kPasswordReplacementChar); 1210 layout_text_.assign(obscured_text_length, kPasswordReplacementChar);
1185 1211
1186 if (obscured_reveal_index_ >= 0 && 1212 if (obscured_reveal_index_ >= 0 &&
1187 obscured_reveal_index_ < static_cast<int>(text_.length())) { 1213 obscured_reveal_index_ < static_cast<int>(text_.length())) {
1188 // Gets the index range in |text_| to be revealed. 1214 // Gets the index range in |text_| to be revealed.
(...skipping 28 matching lines...) Expand all
1217 const size_t ellipsis_end = iter.getIndex(); 1243 const size_t ellipsis_end = iter.getIndex();
1218 DCHECK_LE(ellipsis_start, ellipsis_end); 1244 DCHECK_LE(ellipsis_start, ellipsis_end);
1219 layout_text_.assign(text.substr(0, ellipsis_start) + kEllipsisUTF16 + 1245 layout_text_.assign(text.substr(0, ellipsis_start) + kEllipsisUTF16 +
1220 text.substr(ellipsis_end)); 1246 text.substr(ellipsis_end));
1221 } else { 1247 } else {
1222 iter.setIndex32(truncate_length_ - 1); 1248 iter.setIndex32(truncate_length_ - 1);
1223 layout_text_.assign(text.substr(0, iter.getIndex()) + kEllipsisUTF16); 1249 layout_text_.assign(text.substr(0, iter.getIndex()) + kEllipsisUTF16);
1224 } 1250 }
1225 } 1251 }
1226 1252
1227 if (elide_behavior_ != NO_ELIDE &&
1228 elide_behavior_ != FADE_TAIL &&
1229 !layout_text_.empty() &&
1230 GetContentWidth() > display_rect_.width()) {
1231 // This doesn't trim styles so ellipsis may get rendered as a different
1232 // style than the preceding text. See crbug.com/327850.
1233 layout_text_.assign(Elide(layout_text_,
1234 static_cast<float>(display_rect_.width()),
1235 elide_behavior_));
1236 }
1237
1238 // Replace the newline character with a newline symbol in single line mode. 1253 // Replace the newline character with a newline symbol in single line mode.
1239 static const base::char16 kNewline[] = { '\n', 0 }; 1254 static const base::char16 kNewline[] = { '\n', 0 };
1240 static const base::char16 kNewlineSymbol[] = { 0x2424, 0 }; 1255 static const base::char16 kNewlineSymbol[] = { 0x2424, 0 };
1241 if (!multiline_ && replace_newline_chars_with_symbols_) 1256 if (!multiline_ && replace_newline_chars_with_symbols_)
1242 base::ReplaceChars(layout_text_, kNewline, kNewlineSymbol, &layout_text_); 1257 base::ReplaceChars(layout_text_, kNewline, kNewlineSymbol, &layout_text_);
1243 1258
1244 ResetLayout(); 1259 OnLayoutTextShapeChanged(true);
1245 } 1260 }
1246 1261
1247 base::string16 RenderText::Elide(const base::string16& text, 1262 base::string16 RenderText::Elide(const base::string16& text,
1263 float visual_width,
msw 2015/02/12 22:45:06 nit: match the argument name with the declaration'
oshima 2015/02/13 00:29:43 Done.
1248 float available_width, 1264 float available_width,
1249 ElideBehavior behavior) { 1265 ElideBehavior behavior) {
1250 if (available_width <= 0 || text.empty()) 1266 if (available_width <= 0 || text.empty())
1251 return base::string16(); 1267 return base::string16();
1252 if (behavior == ELIDE_EMAIL) 1268 if (behavior == ELIDE_EMAIL)
1253 return ElideEmail(text, available_width); 1269 return ElideEmail(text, available_width);
1270 TRACE_EVENT0("ui", "RenderText::Elide");
Jun Mukai 2015/02/12 21:56:46 Is this intentionally added?
oshima 2015/02/12 22:09:17 Yes. This is very expensive operation and I think
1254 1271
1255 // Create a RenderText copy with attributes that affect the rendering width. 1272 // Create a RenderText copy with attributes that affect the rendering width.
1256 scoped_ptr<RenderText> render_text = CreateInstanceOfSameType(); 1273 scoped_ptr<RenderText> render_text = CreateInstanceOfSameType();
1257 render_text->SetFontList(font_list_); 1274 render_text->SetFontList(font_list_);
1258 render_text->SetDirectionalityMode(directionality_mode_); 1275 render_text->SetDirectionalityMode(directionality_mode_);
1259 render_text->SetCursorEnabled(cursor_enabled_); 1276 render_text->SetCursorEnabled(cursor_enabled_);
1260 render_text->set_truncate_length(truncate_length_); 1277 render_text->set_truncate_length(truncate_length_);
1261 render_text->styles_ = styles_; 1278 render_text->styles_ = styles_;
1262 render_text->colors_ = colors_; 1279 render_text->colors_ = colors_;
1263 render_text->SetText(text); 1280 if (visual_width == 0) {
1264 if (render_text->GetContentWidthF() <= available_width) 1281 render_text->SetText(text);
1282 visual_width = render_text->GetContentWidthF();
1283 }
1284 if (visual_width <= available_width)
msw 2015/02/12 22:45:06 nit: return at the top if a non-zero visual_width
oshima 2015/02/13 00:29:43 Done.
1265 return text; 1285 return text;
1266 1286
1267 const base::string16 ellipsis = base::string16(kEllipsisUTF16); 1287 const base::string16 ellipsis = base::string16(kEllipsisUTF16);
1268 const bool insert_ellipsis = (behavior != TRUNCATE); 1288 const bool insert_ellipsis = (behavior != TRUNCATE);
1269 const bool elide_in_middle = (behavior == ELIDE_MIDDLE); 1289 const bool elide_in_middle = (behavior == ELIDE_MIDDLE);
1270 const bool elide_at_beginning = (behavior == ELIDE_HEAD); 1290 const bool elide_at_beginning = (behavior == ELIDE_HEAD);
1291
1292 if (insert_ellipsis) {
1293 render_text->SetText(ellipsis);
1294 const float ellipsis_width = render_text->GetContentWidthF();
msw 2015/02/12 22:45:06 I doubt it, but maybe it'd be helpful to cache the
oshima 2015/02/13 00:29:43 The ellipse size depends on the font list, so we c
1295 if (ellipsis_width > available_width)
1296 return base::string16();
1297 }
1298
1271 StringSlicer slicer(text, ellipsis, elide_in_middle, elide_at_beginning); 1299 StringSlicer slicer(text, ellipsis, elide_in_middle, elide_at_beginning);
1272 1300
1273 render_text->SetText(ellipsis);
1274 const float ellipsis_width = render_text->GetContentWidthF();
1275
1276 if (insert_ellipsis && (ellipsis_width > available_width))
1277 return base::string16();
1278
1279 // Use binary search to compute the elided text. 1301 // Use binary search to compute the elided text.
1280 size_t lo = 0; 1302 size_t lo = 0;
1281 size_t hi = text.length() - 1; 1303 size_t hi = text.length() - 1;
1282 const base::i18n::TextDirection text_direction = GetTextDirection(); 1304 const base::i18n::TextDirection text_direction = GetTextDirection();
1283 for (size_t guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) { 1305 for (size_t guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) {
1284 // Restore colors. They will be truncated to size by SetText. 1306 // Restore colors. They will be truncated to size by SetText.
1285 render_text->colors_ = colors_; 1307 render_text->colors_ = colors_;
1286 base::string16 new_text = 1308 base::string16 new_text =
1287 slicer.CutString(guess, insert_ellipsis && behavior != ELIDE_TAIL); 1309 slicer.CutString(guess, insert_ellipsis && behavior != ELIDE_TAIL);
1288 render_text->SetText(new_text); 1310 render_text->SetText(new_text);
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
1377 // Elide the domain so that it only takes half of the available width. 1399 // Elide the domain so that it only takes half of the available width.
1378 // Should the username not need all the width available in its half, the 1400 // Should the username not need all the width available in its half, the
1379 // domain will occupy the leftover width. 1401 // domain will occupy the leftover width.
1380 // If |desired_domain_width| is greater than |available_domain_width|: the 1402 // If |desired_domain_width| is greater than |available_domain_width|: the
1381 // minimal username elision allowed by the specifications will not fit; thus 1403 // minimal username elision allowed by the specifications will not fit; thus
1382 // |desired_domain_width| must be <= |available_domain_width| at all cost. 1404 // |desired_domain_width| must be <= |available_domain_width| at all cost.
1383 const float desired_domain_width = 1405 const float desired_domain_width =
1384 std::min<float>(available_domain_width, 1406 std::min<float>(available_domain_width,
1385 std::max<float>(available_width - full_username_width, 1407 std::max<float>(available_width - full_username_width,
1386 available_width / 2)); 1408 available_width / 2));
1387 domain = Elide(domain, desired_domain_width, ELIDE_MIDDLE); 1409 domain = Elide(domain, 0, desired_domain_width, ELIDE_MIDDLE);
1388 // Failing to elide the domain such that at least one character remains 1410 // Failing to elide the domain such that at least one character remains
1389 // (other than the ellipsis itself) remains: return a single ellipsis. 1411 // (other than the ellipsis itself) remains: return a single ellipsis.
1390 if (domain.length() <= 1U) 1412 if (domain.length() <= 1U)
1391 return base::string16(kEllipsisUTF16); 1413 return base::string16(kEllipsisUTF16);
1392 } 1414 }
1393 1415
1394 // Fit the username in the remaining width (at this point the elided username 1416 // Fit the username in the remaining width (at this point the elided username
1395 // is guaranteed to fit with at least one character remaining given all the 1417 // is guaranteed to fit with at least one character remaining given all the
1396 // precautions taken earlier). 1418 // precautions taken earlier).
1397 available_width -= GetStringWidthF(domain, font_list()); 1419 available_width -= GetStringWidthF(domain, font_list());
1398 username = Elide(username, available_width, ELIDE_TAIL); 1420 username = Elide(username, 0, available_width, ELIDE_TAIL);
1399 return username + kAtSignUTF16 + domain; 1421 return username + kAtSignUTF16 + domain;
1400 } 1422 }
1401 1423
1402 void RenderText::UpdateCachedBoundsAndOffset() { 1424 void RenderText::UpdateCachedBoundsAndOffset() {
1403 if (cached_bounds_and_offset_valid_) 1425 if (cached_bounds_and_offset_valid_)
1404 return; 1426 return;
1405 1427
1406 // TODO(ckocagil): Add support for scrolling multiline text. 1428 // TODO(ckocagil): Add support for scrolling multiline text.
1407 1429
1408 int delta_x = 0; 1430 int delta_x = 0;
(...skipping 10 matching lines...) Expand all
1419 if (cursor_bounds_.right() > display_rect_.right()) 1441 if (cursor_bounds_.right() > display_rect_.right())
1420 delta_x = display_rect_.right() - cursor_bounds_.right(); 1442 delta_x = display_rect_.right() - cursor_bounds_.right();
1421 else if (cursor_bounds_.x() < display_rect_.x()) 1443 else if (cursor_bounds_.x() < display_rect_.x())
1422 delta_x = display_rect_.x() - cursor_bounds_.x(); 1444 delta_x = display_rect_.x() - cursor_bounds_.x();
1423 } 1445 }
1424 1446
1425 SetDisplayOffset(display_offset_.x() + delta_x); 1447 SetDisplayOffset(display_offset_.x() + delta_x);
1426 } 1448 }
1427 1449
1428 void RenderText::DrawSelection(Canvas* canvas) { 1450 void RenderText::DrawSelection(Canvas* canvas) {
1429 const std::vector<Rect> sel = GetSubstringBounds(selection()); 1451 const std::vector<Rect> selections = GetSubstringBounds(selection());
msw 2015/02/12 22:45:06 nit: I think you can inline this in the range-base
oshima 2015/02/13 00:29:43 Done.
1430 for (std::vector<Rect>::const_iterator i = sel.begin(); i < sel.end(); ++i) 1452 for (const Rect& s : selections)
1431 canvas->FillRect(*i, selection_background_focused_color_); 1453 canvas->FillRect(s, selection_background_focused_color_);
1432 } 1454 }
1433 1455
1434 } // namespace gfx 1456 } // namespace gfx
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698