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

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

Issue 1340423006: Improve text elision in TruncateString(). (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Review comments Created 5 years, 3 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
« no previous file with comments | « no previous file | ui/gfx/text_elider_unittest.cc » ('j') | ui/gfx/text_elider_unittest.cc » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 // This file implements utility functions for eliding and formatting UI text. 5 // This file implements utility functions for eliding and formatting UI text.
6 // 6 //
7 // Note that several of the functions declared in text_elider.h are implemented 7 // Note that several of the functions declared in text_elider.h are implemented
8 // in this file using helper classes in an unnamed namespace. 8 // in this file using helper classes in an unnamed namespace.
9 9
10 #include "ui/gfx/text_elider.h" 10 #include "ui/gfx/text_elider.h"
(...skipping 734 matching lines...) Expand 10 before | Expand all | Expand 10 after
745 wrap_behavior, 745 wrap_behavior,
746 lines); 746 lines);
747 rect.Init(); 747 rect.Init();
748 rect.AddString(input); 748 rect.AddString(input);
749 return rect.Finalize(); 749 return rect.Finalize();
750 } 750 }
751 751
752 base::string16 TruncateString(const base::string16& string, 752 base::string16 TruncateString(const base::string16& string,
753 size_t length, 753 size_t length,
754 BreakType break_type) { 754 BreakType break_type) {
755 DCHECK(break_type == CHARACTER_BREAK || break_type == WORD_BREAK); 755 const bool word_break = break_type == WORD_BREAK;
756 DCHECK(word_break || (break_type == CHARACTER_BREAK));
756 757
757 if (string.size() <= length) 758 if (string.size() <= length)
758 // String fits, return it. 759 return string; // No need to elide.
759 return string;
760 760
761 if (length == 0) 761 if (length == 0)
762 // No room for the elide string, return an empty string. 762 return base::string16(); // No room for anything, even an ellipsis.
763 return base::string16();
764
765 size_t max = length - 1;
766 763
767 // Added to the end of strings that are too big. 764 // Added to the end of strings that are too big.
768 static const base::char16 kElideString[] = { 0x2026, 0 }; 765 static const base::char16 kElideString[] = { 0x2026, 0 };
769 766
770 if (max == 0) 767 if (length == 1)
771 // Just enough room for the elide string. 768 return kElideString; // Only room for an ellipsis.
772 return kElideString;
773 769
774 int32_t index = static_cast<int32_t>(max); 770 int32_t index = static_cast<int32_t>(length - 1);
775 if (break_type == WORD_BREAK) { 771 if (word_break) {
776 // Use a line iterator to find the first boundary. 772 // Use a word iterator to find the first boundary.
777 UErrorCode status = U_ZERO_ERROR; 773 UErrorCode status = U_ZERO_ERROR;
778 scoped_ptr<icu::BreakIterator> bi( 774 scoped_ptr<icu::BreakIterator> bi(
779 icu::RuleBasedBreakIterator::createLineInstance( 775 icu::RuleBasedBreakIterator::createWordInstance(
780 icu::Locale::getDefault(), status)); 776 icu::Locale::getDefault(), status));
781 if (U_FAILURE(status)) 777 if (U_FAILURE(status))
782 return string.substr(0, max) + kElideString; 778 return string.substr(0, length - 1) + kElideString;
783 bi->setText(string.c_str()); 779 bi->setText(string.c_str());
784 index = bi->preceding(index); 780 index = bi->preceding(static_cast<int32_t>(length));
785 if (index == icu::BreakIterator::DONE || index == 0) { 781 if (index == icu::BreakIterator::DONE || index == 0) {
786 // We either found no valid line break at all, or one right at the 782 // We either found no valid word break at all, or one right at the
787 // beginning of the string. Go back to the end; we'll have to break in the 783 // beginning of the string. Go back to the end; we'll have to break in the
788 // middle of a word. 784 // middle of a word.
789 index = static_cast<int32_t>(max); 785 index = static_cast<int32_t>(length - 1);
790 } 786 }
791 } 787 }
792 788
793 // Use a character iterator to find the previous non-whitespace character. 789 // By this point, |index| should point at the character that's a candidate for
790 // replacing with an ellipsis. Use a character iterator to check previous
791 // characters and stop as soon as we find a previous non-whitespace character.
794 icu::StringCharacterIterator char_iterator(string.c_str()); 792 icu::StringCharacterIterator char_iterator(string.c_str());
795 char_iterator.setIndex(index); 793 char_iterator.setIndex(index);
796 while (char_iterator.hasPrevious()) { 794 while (char_iterator.hasPrevious()) {
797 char_iterator.previous(); 795 char_iterator.previous();
798 if (!(u_isspace(char_iterator.current()) || 796 if (!(u_isspace(char_iterator.current()) ||
799 u_charType(char_iterator.current()) == U_CONTROL_CHAR || 797 u_charType(char_iterator.current()) == U_CONTROL_CHAR ||
800 u_charType(char_iterator.current()) == U_NON_SPACING_MARK)) { 798 u_charType(char_iterator.current()) == U_NON_SPACING_MARK)) {
801 // Not a whitespace character. Advance the iterator so that we 799 // Not a whitespace character. Truncate to everything up to and including
802 // include the current character in the truncated string. 800 // this character, and append an ellipsis.
803 char_iterator.next(); 801 char_iterator.next();
804 break; 802 return string.substr(0, char_iterator.getIndex()) + kElideString;
805 } 803 }
806 } 804 }
807 if (char_iterator.hasPrevious()) {
808 // Found a valid break point.
809 index = char_iterator.getIndex();
810 } else {
811 // String has leading whitespace, return the elide string.
812 return kElideString;
813 }
814 805
815 return string.substr(0, index) + kElideString; 806 // Couldn't find a previous non-whitespace character. If we were originally
807 // word-breaking, and index != length - 1, then the string is |index|
808 // whitespace characters followed by a word we're trying to break in the midst
809 // of, and we can fit at least one character of the word in the elided string.
810 // Do that rather than just returning an ellipsis.
811 if (word_break && (index != static_cast<int32_t>(length - 1)))
812 return string.substr(0, length - 1) + kElideString;
msw 2015/09/21 18:28:10 This is the one new place that might break multi-c
813
814 // Trying to break after only whitespace, elide all of it.
815 return kElideString;
816 } 816 }
817 817
818 } // namespace gfx 818 } // namespace gfx
OLDNEW
« no previous file with comments | « no previous file | ui/gfx/text_elider_unittest.cc » ('j') | ui/gfx/text_elider_unittest.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698