| 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 // 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 21 matching lines...) Expand all Loading... |
| 32 | 32 |
| 33 namespace gfx { | 33 namespace gfx { |
| 34 | 34 |
| 35 // U+2026 in utf8 | 35 // U+2026 in utf8 |
| 36 const char kEllipsis[] = "\xE2\x80\xA6"; | 36 const char kEllipsis[] = "\xE2\x80\xA6"; |
| 37 const base::char16 kEllipsisUTF16[] = { 0x2026, 0 }; | 37 const base::char16 kEllipsisUTF16[] = { 0x2026, 0 }; |
| 38 const base::char16 kForwardSlash = '/'; | 38 const base::char16 kForwardSlash = '/'; |
| 39 | 39 |
| 40 namespace { | 40 namespace { |
| 41 | 41 |
| 42 // Helper class to split + elide text, while respecting UTF16 surrogate pairs. | |
| 43 class StringSlicer { | |
| 44 public: | |
| 45 StringSlicer(const base::string16& text, | |
| 46 const base::string16& ellipsis, | |
| 47 bool elide_in_middle) | |
| 48 : text_(text), | |
| 49 ellipsis_(ellipsis), | |
| 50 elide_in_middle_(elide_in_middle) { | |
| 51 } | |
| 52 | |
| 53 // Cuts |text_| to be |length| characters long. If |elide_in_middle_| is true, | |
| 54 // the middle of the string is removed to leave equal-length pieces from the | |
| 55 // beginning and end of the string; otherwise, the end of the string is | |
| 56 // removed and only the beginning remains. If |insert_ellipsis| is true, | |
| 57 // then an ellipsis character will be inserted at the cut point. | |
| 58 base::string16 CutString(size_t length, bool insert_ellipsis) { | |
| 59 const base::string16 ellipsis_text = insert_ellipsis ? ellipsis_ | |
| 60 : base::string16(); | |
| 61 | |
| 62 if (!elide_in_middle_) | |
| 63 return text_.substr(0, FindValidBoundaryBefore(length)) + ellipsis_text; | |
| 64 | |
| 65 // We put the extra character, if any, before the cut. | |
| 66 const size_t half_length = length / 2; | |
| 67 const size_t prefix_length = FindValidBoundaryBefore(length - half_length); | |
| 68 const size_t suffix_start_guess = text_.length() - half_length; | |
| 69 const size_t suffix_start = FindValidBoundaryAfter(suffix_start_guess); | |
| 70 const size_t suffix_length = | |
| 71 half_length - (suffix_start_guess - suffix_start); | |
| 72 return text_.substr(0, prefix_length) + ellipsis_text + | |
| 73 text_.substr(suffix_start, suffix_length); | |
| 74 } | |
| 75 | |
| 76 private: | |
| 77 // Returns a valid cut boundary at or before |index|. | |
| 78 size_t FindValidBoundaryBefore(size_t index) const { | |
| 79 DCHECK_LE(index, text_.length()); | |
| 80 if (index != text_.length()) | |
| 81 U16_SET_CP_START(text_.data(), 0, index); | |
| 82 return index; | |
| 83 } | |
| 84 | |
| 85 // Returns a valid cut boundary at or after |index|. | |
| 86 size_t FindValidBoundaryAfter(size_t index) const { | |
| 87 DCHECK_LE(index, text_.length()); | |
| 88 if (index != text_.length()) | |
| 89 U16_SET_CP_LIMIT(text_.data(), 0, index, text_.length()); | |
| 90 return index; | |
| 91 } | |
| 92 | |
| 93 // The text to be sliced. | |
| 94 const base::string16& text_; | |
| 95 | |
| 96 // Ellipsis string to use. | |
| 97 const base::string16& ellipsis_; | |
| 98 | |
| 99 // If true, the middle of the string will be elided. | |
| 100 bool elide_in_middle_; | |
| 101 | |
| 102 DISALLOW_COPY_AND_ASSIGN(StringSlicer); | |
| 103 }; | |
| 104 | 42 |
| 105 // Build a path from the first |num_components| elements in |path_elements|. | 43 // Build a path from the first |num_components| elements in |path_elements|. |
| 106 // Prepends |path_prefix|, appends |filename|, inserts ellipsis if appropriate. | 44 // Prepends |path_prefix|, appends |filename|, inserts ellipsis if appropriate. |
| 107 base::string16 BuildPathFromComponents( | 45 base::string16 BuildPathFromComponents( |
| 108 const base::string16& path_prefix, | 46 const base::string16& path_prefix, |
| 109 const std::vector<base::string16>& path_elements, | 47 const std::vector<base::string16>& path_elements, |
| 110 const base::string16& filename, | 48 const base::string16& filename, |
| 111 size_t num_components) { | 49 size_t num_components) { |
| 112 // Add the initial elements of the path. | 50 // Add the initial elements of the path. |
| 113 base::string16 path = path_prefix; | 51 base::string16 path = path_prefix; |
| (...skipping 29 matching lines...) Expand all Loading... |
| 143 if (available_pixel_width >= GetStringWidthF(elided_path, font_list)) | 81 if (available_pixel_width >= GetStringWidthF(elided_path, font_list)) |
| 144 return ElideText(elided_path + url_query, font_list, | 82 return ElideText(elided_path + url_query, font_list, |
| 145 available_pixel_width, ELIDE_AT_END); | 83 available_pixel_width, ELIDE_AT_END); |
| 146 } | 84 } |
| 147 | 85 |
| 148 return base::string16(); | 86 return base::string16(); |
| 149 } | 87 } |
| 150 | 88 |
| 151 } // namespace | 89 } // namespace |
| 152 | 90 |
| 91 StringSlicer::StringSlicer(const base::string16& text, |
| 92 const base::string16& ellipsis, |
| 93 bool elide_in_middle) |
| 94 : text_(text), |
| 95 ellipsis_(ellipsis), |
| 96 elide_in_middle_(elide_in_middle) { |
| 97 } |
| 98 |
| 99 base::string16 StringSlicer::CutString(size_t length, bool insert_ellipsis) { |
| 100 const base::string16 ellipsis_text = insert_ellipsis ? ellipsis_ |
| 101 : base::string16(); |
| 102 |
| 103 if (!elide_in_middle_) |
| 104 return text_.substr(0, FindValidBoundaryBefore(length)) + ellipsis_text; |
| 105 |
| 106 // We put the extra character, if any, before the cut. |
| 107 const size_t half_length = length / 2; |
| 108 const size_t prefix_length = FindValidBoundaryBefore(length - half_length); |
| 109 const size_t suffix_start_guess = text_.length() - half_length; |
| 110 const size_t suffix_start = FindValidBoundaryAfter(suffix_start_guess); |
| 111 const size_t suffix_length = |
| 112 half_length - (suffix_start_guess - suffix_start); |
| 113 return text_.substr(0, prefix_length) + ellipsis_text + |
| 114 text_.substr(suffix_start, suffix_length); |
| 115 } |
| 116 |
| 117 size_t StringSlicer::FindValidBoundaryBefore(size_t index) const { |
| 118 DCHECK_LE(index, text_.length()); |
| 119 if (index != text_.length()) |
| 120 U16_SET_CP_START(text_.data(), 0, index); |
| 121 return index; |
| 122 } |
| 123 |
| 124 size_t StringSlicer::FindValidBoundaryAfter(size_t index) const { |
| 125 DCHECK_LE(index, text_.length()); |
| 126 if (index != text_.length()) |
| 127 U16_SET_CP_LIMIT(text_.data(), 0, index, text_.length()); |
| 128 return index; |
| 129 } |
| 130 |
| 153 base::string16 ElideEmail(const base::string16& email, | 131 base::string16 ElideEmail(const base::string16& email, |
| 154 const FontList& font_list, | 132 const FontList& font_list, |
| 155 float available_pixel_width) { | 133 float available_pixel_width) { |
| 156 if (GetStringWidthF(email, font_list) <= available_pixel_width) | 134 if (GetStringWidthF(email, font_list) <= available_pixel_width) |
| 157 return email; | 135 return email; |
| 158 | 136 |
| 159 // Split the email into its local-part (username) and domain-part. The email | 137 // Split the email into its local-part (username) and domain-part. The email |
| 160 // spec technically allows for @ symbols in the local-part (username) of the | 138 // spec technically allows for @ symbols in the local-part (username) of the |
| 161 // email under some special requirements. It is guaranteed that there is no @ | 139 // email under some special requirements. It is guaranteed that there is no @ |
| 162 // symbol in the domain part of the email however so splitting at the last @ | 140 // symbol in the domain part of the email however so splitting at the last @ |
| (...skipping 975 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1138 index = char_iterator.getIndex(); | 1116 index = char_iterator.getIndex(); |
| 1139 } else { | 1117 } else { |
| 1140 // String has leading whitespace, return the elide string. | 1118 // String has leading whitespace, return the elide string. |
| 1141 return kElideString; | 1119 return kElideString; |
| 1142 } | 1120 } |
| 1143 } | 1121 } |
| 1144 return string.substr(0, index) + kElideString; | 1122 return string.substr(0, index) + kElideString; |
| 1145 } | 1123 } |
| 1146 | 1124 |
| 1147 } // namespace gfx | 1125 } // namespace gfx |
| OLD | NEW |