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

Side by Side Diff: client/view/MeasureText.dart

Issue 9382027: Move client/{base, observable, layout, touch, util, view} to samples/ui_lib . (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: '' Created 8 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 | Annotate | Revision Log
« no previous file with comments | « client/view/ConveyorView.dart ('k') | client/view/PagedViews.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 // TODO(jacobr): handle splitting lines on symbols such as '-' that aren't
6 // whitespace but are valid word breaking points.
7 /**
8 * Utility class to efficiently word break and measure text without requiring
9 * access to the DOM.
10 */
11 class MeasureText {
12 static CanvasRenderingContext2D _context;
13
14 final String font;
15 num _spaceLength;
16 num _typicalCharLength;
17
18 static final String ELLIPSIS = '...';
19
20 MeasureText(this.font) {
21 if (_context === null) {
22 CanvasElement canvas = new Element.tag('canvas');
23 _context = canvas.getContext('2d');
24 }
25 if (_spaceLength === null) {
26 _context.font = font;
27 _spaceLength = _context.measureText(' ').width;
28 _typicalCharLength = _context.measureText('k').width;
29 }
30 }
31
32 // TODO(jacobr): we are DOA for i18N...
33 // the right solution is for the server to send us text perparsed into words
34 // perhaps even with hints on the guess for the correct breaks so on the
35 // client all we have to do is verify and fix errors rather than perform the
36 // full calculation.
37 static bool isWhitespace(String character) {
38 return character == ' ' || character == '\t' || character == '\n';
39 }
40
41 num get typicalCharLength() {
42 return _typicalCharLength;
43 }
44
45 String quickTruncate(String text, num lineWidth, int maxLines) {
46 int targetLength = (lineWidth * maxLines / _typicalCharLength).toInt();
47 // Advance to next word break point.
48 while(targetLength < text.length && !isWhitespace(text[targetLength])) {
49 targetLength++;
50 }
51
52 if (targetLength < text.length) {
53 return text.substring(0, targetLength) + ELLIPSIS;
54 } else {
55 return text;
56 }
57 }
58
59 /**
60 * Add line broken text as html separated by <br> elements.
61 * Returns the number of lines in the output.
62 * This function is safe to call with [:sb === null:] in which case just the
63 * line count is returned.
64 */
65 int addLineBrokenText(StringBuffer sb, String text, num lineWidth,
66 int maxLines) {
67 // Strip surrounding whitespace. This ensures we create zero lines if there
68 // is no visible text.
69 text = text.trim();
70
71 // We can often avoid performing a full line break calculation when only
72 // the number of lines and not the actual linebreaks is required.
73 if (sb === null) {
74 _context.font = font;
75 int textWidth = _context.measureText(text).width.toInt();
76 // By the pigeon hole principle, the resulting text will require at least
77 // maxLines if the raw text is longer than the amount of text that will
78 // fit on maxLines - 1. We add the length of a whitespace
79 // character to the lineWidth as each line is separated by a whitespace
80 // character. We assume all whitespace characters have the same length.
81 if (textWidth >= (lineWidth + _spaceLength) * (maxLines - 1)) {
82 return maxLines;
83 } else if (textWidth == 0) {
84 return 0;
85 } else if (textWidth < lineWidth) {
86 return 1;
87 }
88 // Fall through to the regular line breaking calculation as the number
89 // of lines required is unclear.
90 }
91 int lines = 0;
92 lineBreak(text, lineWidth, maxLines, (int start, int end, num width) {
93 lines++;
94 if (lines == maxLines) {
95 // Overflow case... there may be more lines of text than we can handle.
96 // Add a few characters to the last line so that the browser will
97 // render ellipses correctly.
98 // TODO(jacobr): make this optional and only add characters until
99 // the first whitespace character encountered.
100 end = Math.min(end + 50, text.length);
101 }
102 if (sb !== null) {
103 if (lines > 1) {
104 sb.add('<br>');
105 }
106 // TODO(jacobr): HTML escape this text.
107 sb.add(text.substring(start, end));
108 }
109 });
110 return lines;
111 }
112
113 void lineBreak(String text, num lineWidth, int maxLines,
114 Function callback) {
115 _context.font = font;
116 int lines = 0;
117 num currentLength = 0;
118 int startIndex = 0;
119 int wordStartIndex = null;
120 int lastWordEndIndex = null;
121 bool lastWhitespace = true;
122 // TODO(jacobr): optimize this further.
123 // To simplify the logic, we simulate injecting a whitespace character
124 // at the end of the string.
125 for (int i = 0, len = text.length; i <= len; i++) {
126 // Treat the char after the end of the string as whitespace.
127 bool whitespace = i == len || isWhitespace(text[i]);
128 if (whitespace && !lastWhitespace) {
129 num wordLength = _context.measureText(text.substring(
130 wordStartIndex, i)).width;
131 // TODO(jimhug): Replace the line above with this one to workaround
132 // dartium bug - error: unimplemented code
133 // num wordLength = (i - wordStartIndex) * 17;
134 currentLength += wordLength;
135 if (currentLength > lineWidth) {
136 // Edge case:
137 // It could be the very first word we ran into was too long for a
138 // line in which case we let it have its own line.
139 if (lastWordEndIndex !== null) {
140 lines++;
141 callback(startIndex, lastWordEndIndex, currentLength - wordLength);
142 }
143 if (lines == maxLines) {
144 return;
145 }
146 startIndex = wordStartIndex;
147 currentLength = wordLength;
148 }
149 lastWordEndIndex = i;
150 currentLength += _spaceLength;
151 wordStartIndex = null;
152 } else if (wordStartIndex === null && !whitespace) {
153 wordStartIndex = i;
154 }
155 lastWhitespace = whitespace;
156 }
157 if (currentLength > 0) {
158 callback(startIndex, text.length, currentLength);
159 }
160 }
161 }
OLDNEW
« no previous file with comments | « client/view/ConveyorView.dart ('k') | client/view/PagedViews.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698