OLD | NEW |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of view; | 5 part of view; |
6 | 6 |
7 // TODO(jacobr): handle splitting lines on symbols such as '-' that aren't | 7 // TODO(jacobr): handle splitting lines on symbols such as '-' that aren't |
8 // whitespace but are valid word breaking points. | 8 // whitespace but are valid word breaking points. |
9 /** | 9 /** |
10 * Utility class to efficiently word break and measure text without requiring | 10 * Utility class to efficiently word break and measure text without requiring |
(...skipping 29 matching lines...) Expand all Loading... |
40 return character == ' ' || character == '\t' || character == '\n'; | 40 return character == ' ' || character == '\t' || character == '\n'; |
41 } | 41 } |
42 | 42 |
43 num get typicalCharLength { | 43 num get typicalCharLength { |
44 return _typicalCharLength; | 44 return _typicalCharLength; |
45 } | 45 } |
46 | 46 |
47 String quickTruncate(String text, num lineWidth, int maxLines) { | 47 String quickTruncate(String text, num lineWidth, int maxLines) { |
48 int targetLength = lineWidth * maxLines ~/ _typicalCharLength; | 48 int targetLength = lineWidth * maxLines ~/ _typicalCharLength; |
49 // Advance to next word break point. | 49 // Advance to next word break point. |
50 while(targetLength < text.length && !isWhitespace(text[targetLength])) { | 50 while (targetLength < text.length && !isWhitespace(text[targetLength])) { |
51 targetLength++; | 51 targetLength++; |
52 } | 52 } |
53 | 53 |
54 if (targetLength < text.length) { | 54 if (targetLength < text.length) { |
55 return '${text.substring(0, targetLength)}$ELLIPSIS'; | 55 return '${text.substring(0, targetLength)}$ELLIPSIS'; |
56 } else { | 56 } else { |
57 return text; | 57 return text; |
58 } | 58 } |
59 } | 59 } |
60 | 60 |
61 /** | 61 /** |
62 * Add line broken text as html separated by <br> elements. | 62 * Add line broken text as html separated by <br> elements. |
63 * Returns the number of lines in the output. | 63 * Returns the number of lines in the output. |
64 * This function is safe to call with [:sb == null:] in which case just the | 64 * This function is safe to call with [:sb == null:] in which case just the |
65 * line count is returned. | 65 * line count is returned. |
66 */ | 66 */ |
67 int addLineBrokenText(StringBuffer sb, String text, num lineWidth, | 67 int addLineBrokenText( |
68 int maxLines) { | 68 StringBuffer sb, String text, num lineWidth, int maxLines) { |
69 // Strip surrounding whitespace. This ensures we create zero lines if there | 69 // Strip surrounding whitespace. This ensures we create zero lines if there |
70 // is no visible text. | 70 // is no visible text. |
71 text = text.trim(); | 71 text = text.trim(); |
72 | 72 |
73 // We can often avoid performing a full line break calculation when only | 73 // We can often avoid performing a full line break calculation when only |
74 // the number of lines and not the actual linebreaks is required. | 74 // the number of lines and not the actual linebreaks is required. |
75 if (sb == null) { | 75 if (sb == null) { |
76 _context.font = font; | 76 _context.font = font; |
77 int textWidth = _context.measureText(text).width.toInt(); | 77 int textWidth = _context.measureText(text).width.toInt(); |
78 // By the pigeon hole principle, the resulting text will require at least | 78 // By the pigeon hole principle, the resulting text will require at least |
(...skipping 26 matching lines...) Expand all Loading... |
105 if (lines > 1) { | 105 if (lines > 1) { |
106 sb.write('<br>'); | 106 sb.write('<br>'); |
107 } | 107 } |
108 // TODO(jacobr): HTML escape this text. | 108 // TODO(jacobr): HTML escape this text. |
109 sb.write(text.substring(start, end)); | 109 sb.write(text.substring(start, end)); |
110 } | 110 } |
111 }); | 111 }); |
112 return lines; | 112 return lines; |
113 } | 113 } |
114 | 114 |
115 void lineBreak(String text, num lineWidth, int maxLines, | 115 void lineBreak(String text, num lineWidth, int maxLines, Function callback) { |
116 Function callback) { | |
117 _context.font = font; | 116 _context.font = font; |
118 int lines = 0; | 117 int lines = 0; |
119 num currentLength = 0; | 118 num currentLength = 0; |
120 int startIndex = 0; | 119 int startIndex = 0; |
121 int wordStartIndex = null; | 120 int wordStartIndex = null; |
122 int lastWordEndIndex = null; | 121 int lastWordEndIndex = null; |
123 bool lastWhitespace = true; | 122 bool lastWhitespace = true; |
124 // TODO(jacobr): optimize this further. | 123 // TODO(jacobr): optimize this further. |
125 // To simplify the logic, we simulate injecting a whitespace character | 124 // To simplify the logic, we simulate injecting a whitespace character |
126 // at the end of the string. | 125 // at the end of the string. |
127 for (int i = 0, len = text.length; i <= len; i++) { | 126 for (int i = 0, len = text.length; i <= len; i++) { |
128 // Treat the char after the end of the string as whitespace. | 127 // Treat the char after the end of the string as whitespace. |
129 bool whitespace = i == len || isWhitespace(text[i]); | 128 bool whitespace = i == len || isWhitespace(text[i]); |
130 if (whitespace && !lastWhitespace) { | 129 if (whitespace && !lastWhitespace) { |
131 num wordLength = _context.measureText(text.substring( | 130 num wordLength = |
132 wordStartIndex, i)).width; | 131 _context.measureText(text.substring(wordStartIndex, i)).width; |
133 // TODO(jimhug): Replace the line above with this one to workaround | 132 // TODO(jimhug): Replace the line above with this one to workaround |
134 // dartium bug - error: unimplemented code | 133 // dartium bug - error: unimplemented code |
135 // num wordLength = (i - wordStartIndex) * 17; | 134 // num wordLength = (i - wordStartIndex) * 17; |
136 currentLength += wordLength; | 135 currentLength += wordLength; |
137 if (currentLength > lineWidth) { | 136 if (currentLength > lineWidth) { |
138 // Edge case: | 137 // Edge case: |
139 // It could be the very first word we ran into was too long for a | 138 // It could be the very first word we ran into was too long for a |
140 // line in which case we let it have its own line. | 139 // line in which case we let it have its own line. |
141 if (lastWordEndIndex != null) { | 140 if (lastWordEndIndex != null) { |
142 lines++; | 141 lines++; |
143 callback(startIndex, lastWordEndIndex, currentLength - wordLength); | 142 callback(startIndex, lastWordEndIndex, currentLength - wordLength); |
144 } | 143 } |
145 if (lines == maxLines) { | 144 if (lines == maxLines) { |
146 return; | 145 return; |
147 } | 146 } |
148 startIndex = wordStartIndex; | 147 startIndex = wordStartIndex; |
149 » currentLength = wordLength; | 148 currentLength = wordLength; |
150 } | 149 } |
151 lastWordEndIndex = i; | 150 lastWordEndIndex = i; |
152 currentLength += _spaceLength; | 151 currentLength += _spaceLength; |
153 wordStartIndex = null; | 152 wordStartIndex = null; |
154 } else if (wordStartIndex == null && !whitespace) { | 153 } else if (wordStartIndex == null && !whitespace) { |
155 wordStartIndex = i; | 154 wordStartIndex = i; |
156 } | 155 } |
157 lastWhitespace = whitespace; | 156 lastWhitespace = whitespace; |
158 } | 157 } |
159 if (currentLength > 0) { | 158 if (currentLength > 0) { |
160 callback(startIndex, text.length, currentLength); | 159 callback(startIndex, text.length, currentLength); |
161 } | 160 } |
162 } | 161 } |
163 } | 162 } |
OLD | NEW |