comparison dynamin/painting/text_layout.d @ 91:85cd0b04777c

Add TextLayout.formatRuns and fontFormatRuns, and tests for formatRuns
author Jordan Miner <jminer7@gmail.com>
date Thu, 05 Aug 2010 03:31:21 -0500
parents 41b430aa319f
children 27445f24d5fd
comparison
equal deleted inserted replaced
90:41b430aa319f 91:85cd0b04777c
179 data.multiple = spacing; 179 data.multiple = spacing;
180 else 180 else
181 throw new Exception("unknown type"); 181 throw new Exception("unknown type");
182 return data; 182 return data;
183 } 183 }
184 void setDataForType(FormatType type, FormatData data) {
185 if(type == FormatType.FontFamily)
186 fontFamily = data.family;
187 else if(type == FormatType.FontSize)
188 fontSize = data.size;
189 else if(type == FormatType.Bold)
190 bold = data.on;
191 else if(type == FormatType.Italic)
192 italic = data.on;
193 else if(type == FormatType.Underline)
194 underline = data.style;
195 else if(type == FormatType.Strikethrough)
196 strikethrough = data.style;
197 else if(type == FormatType.Overline)
198 overline = data.style;
199 else if(type == FormatType.Small)
200 small = data.type;
201 else if(type == FormatType.ForeColor)
202 foreColor = data.color;
203 else if(type == FormatType.BackColor)
204 backColor = data.color;
205 else if(type == FormatType.Spacing)
206 spacing = data.multiple;
207 else
208 throw new Exception("unknown type");
209 }
184 } 210 }
185 //}}} 211 //}}}
186 212
187 /// 213 ///
188 enum TextAlignment { 214 enum TextAlignment {
244 uint index = 0; 270 uint index = 0;
245 foreach(change; formatting) { 271 foreach(change; formatting) {
246 assert(change.index >= index); 272 assert(change.index >= index);
247 index = change.index; 273 index = change.index;
248 } 274 }
275 }
276
277 ///
278 struct FormatRunsIter {
279 private:
280 TextLayout owner;
281 FormatType[] filter;
282 int delegate(uint index) splitter;
283 public:
284 ///
285 int opApply(int delegate(ref uint start, ref uint length, ref Format format) dg) {
286 bool inFilter(FormatType type) {
287 return filter.length == 0 || filter.containsT(type);
288 }
289 with(owner) {
290 int result;
291 uint fIndex = 0; // index of formatting array
292 uint sIndex = 0; // index passed to splitter
293 Format format = initialFormat;
294
295 uint start = 0;
296 uint end;
297 while(start != text.length) {
298 end = text.length;
299 // stop looping when one is found that is greater than start and in the filter
300 while(fIndex < formatting.count && (formatting[fIndex].index <= start ||
301 !inFilter(formatting[fIndex].type))) {
302 // the only ones that are greater are ones skipped due to filter
303 assert(formatting[fIndex].index >= start);
304 format.setDataForType(formatting[fIndex].type, formatting[fIndex].data);
305 fIndex++;
306 }
307 if(fIndex < formatting.count)
308 end = formatting[fIndex].index;
309
310 if(splitter) {
311 while(splitter(sIndex) != -1 && splitter(sIndex) <= start)
312 sIndex++;
313 if(splitter(sIndex) != -1 && splitter(sIndex) < end)
314 end = splitter(sIndex);
315 }
316 if(end == start)
317 end = text.length;
318
319 uint _start = start;
320 uint _length = end - start;
321 result = dg(_start, _length, format);
322 if(result)
323 break;
324
325 start = end;
326 }
327
328 return result;
329 }
330 }
331 }
332
333 /**
334 * Returns an iterator struct that can be used with foreach. The struct will iterate over
335 * each range of the text that has the same formatting.
336 *
337 * The difference between formatRuns and fontFormatRuns is that fontFormatRuns ignores
338 * all formatting except FontFamily, FontSize, Bold, Italic, and Small, whereas formatRuns
339 * does not ignore any formatting.
340 *
341 * If the optional delegate is specified, it will be called with index = 0, then index = 1,
342 * and so on. The delegate should return an index to split the ranges at. When there are no
343 * more indexes to split the ranges at, the delegate should return -1. It is a way of
344 * giving an array of indexes to split at without allocating an array.
345 *
346 * Example:
347 * -----
348 * foreach(start, length, format; textLayout.formatRuns) {
349 * // code goes here
350 * }
351 * -----
352 */
353 FormatRunsIter formatRuns(int delegate(uint index) splitter = null) {
354 FormatRunsIter iter;
355 iter.owner = this;
356 iter.splitter = splitter;
357 return iter;
358 }
359
360 /// ditto
361 FormatRunsIter fontFormatRuns(int delegate(uint index) splitter = null) {
362 FormatRunsIter iter;
363 iter.owner = this;
364 iter.filter = [FormatType.FontFamily,
365 FormatType.FontSize,
366 FormatType.Bold,
367 FormatType.Italic,
368 FormatType.Small];
369 iter.splitter = splitter;
370 return iter;
249 } 371 }
250 372
251 //{{{ character formatting 373 //{{{ character formatting
252 void setFontFamily(string family, uint start, uint length) { 374 void setFontFamily(string family, uint start, uint length) {
253 FormatData data; 375 FormatData data;
422 assert(t.formatting[0].data.style == LineStyle.Single); 544 assert(t.formatting[0].data.style == LineStyle.Single);
423 assert(t.formatting[0].index == 18); 545 assert(t.formatting[0].index == 18);
424 t.setUnderline(LineStyle.None, 4, 20); // "are you doing today?" 546 t.setUnderline(LineStyle.None, 4, 20); // "are you doing today?"
425 assert(t.formatting.count == 0); 547 assert(t.formatting.count == 0);
426 } 548 }
427 549 unittest {
550 auto t = new TextLayout("Arial", 14);
551 t.text = "The computer is black.";
552 int[][] runs = [[0, 22]];
553 int delegate(uint index) splitter;
554 void checkRuns() {
555 int i = 0;
556 foreach(s, l, f; t.formatRuns(splitter)) {
557 assert(runs[i][0] == s && runs[i][1] == l);
558 i++;
559 }
560 }
561 // no formatting or splitter
562 checkRuns();
563
564 // with a splitter but no formatting
565 splitter = delegate int(uint i) { return [0, -1][i]; };
566 checkRuns();
567 splitter = delegate int(uint i) { return [22, -1][i]; };
568 checkRuns();
569 splitter = delegate int(uint i) { return [10, 15, -1][i]; };
570 runs = [[0, 10], [10, 5], [15, 7]];
571 checkRuns();
572 splitter = null;
573
574 // with formatting but no splitter
575 t.setFontFamily("Tahoma", 4, 8); // "computer"
576 runs = [[0, 4], [4, 8], [12, 10]];
577 checkRuns();
578
579 t.setUnderline(LineStyle.Single, 12, 3); // " is"
580 runs = [[0, 4], [4, 8], [12, 3], [15,7]];
581 checkRuns(); // test two FormatChanges at the same index
582
583 t.setUnderline(LineStyle.Double, 0, 22);
584 runs = [[0, 4], [4, 8], [12, 10]];
585 checkRuns(); // test a FormatChange at beginning and at end
586
587 splitter = delegate int(uint i) { return [2, 4, 21, -1][i]; };
588 runs = [[0, 2], [2, 2], [4, 8], [12, 9], [21, 1]];
589 checkRuns(); // test a split in middle of a format run and at the same index as a format run
590
591 // TODO: test fontFormatRuns
592 }
593