| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // @dart = 2.6 |
| @TestOn('chrome || firefox') |
| |
| import 'package:test/bootstrap/browser.dart'; |
| import 'package:test/test.dart'; |
| |
| import 'package:ui/ui.dart' as ui; |
| import 'package:ui/src/engine.dart'; |
| |
| |
| final ui.ParagraphStyle ahemStyle = ui.ParagraphStyle( |
| fontFamily: 'ahem', |
| fontSize: 10, |
| ); |
| const ui.ParagraphConstraints constraints = ui.ParagraphConstraints(width: 50); |
| const ui.ParagraphConstraints infiniteConstraints = |
| ui.ParagraphConstraints(width: double.infinity); |
| |
| DomParagraph build(ui.ParagraphStyle style, String text, |
| {ui.TextStyle textStyle}) { |
| final DomParagraphBuilder builder = DomParagraphBuilder(style); |
| if (textStyle != null) { |
| builder.pushStyle(textStyle); |
| } |
| builder.addText(text); |
| return builder.build(); |
| } |
| |
| typedef MeasurementTestBody = void Function(TextMeasurementService instance); |
| |
| /// Runs the same test twice - once with dom measurement and once with canvas |
| /// measurement. |
| void testMeasurements(String description, MeasurementTestBody body, { |
| bool skipDom, |
| bool skipCanvas, |
| }) { |
| test( |
| '$description (dom)', |
| () { |
| try { |
| WebExperiments.instance.useCanvasRichText = false; |
| return body(TextMeasurementService.domInstance); |
| } finally { |
| WebExperiments.instance.useCanvasRichText = null; |
| } |
| }, |
| skip: skipDom, |
| ); |
| test( |
| '$description (canvas)', |
| () { |
| try { |
| WebExperiments.instance.useCanvasRichText = false; |
| return body(TextMeasurementService.canvasInstance); |
| } finally { |
| WebExperiments.instance.useCanvasRichText = null; |
| } |
| }, |
| skip: skipCanvas, |
| ); |
| } |
| void main() { |
| internalBootstrapBrowserTest(() => testMain); |
| } |
| |
| void testMain() async { |
| await ui.webOnlyInitializeTestDomRenderer(); |
| |
| group('$RulerManager', () { |
| final ui.ParagraphStyle s1 = ui.ParagraphStyle(fontFamily: 'sans-serif'); |
| final ui.ParagraphStyle s2 = ui.ParagraphStyle( |
| fontWeight: ui.FontWeight.bold, |
| ); |
| final ui.ParagraphStyle s3 = ui.ParagraphStyle(fontSize: 22.0); |
| |
| ParagraphGeometricStyle style1, style2, style3; |
| DomParagraph style1Text1, style1Text2; // two paragraphs sharing style |
| DomParagraph style2Text1, style3Text3; |
| |
| setUp(() { |
| style1Text1 = build(s1, '1'); |
| style1Text2 = build(s1, '2'); |
| style2Text1 = build(s2, '1'); |
| style3Text3 = build(s3, '3'); |
| |
| style1 = style1Text1.geometricStyle; |
| style2 = style2Text1.geometricStyle; |
| style3 = style3Text3.geometricStyle; |
| |
| final ParagraphGeometricStyle style1_2 = style1Text2.geometricStyle; |
| expect(style1_2, style1); // styles must be equal despite different text |
| }); |
| |
| test('caches rulers', () { |
| final RulerManager rulerManager = RulerManager(rulerCacheCapacity: 2); |
| ParagraphRuler ruler1, ruler2, ruler3; |
| |
| expect(rulerManager.rulerCacheCapacity, 2); |
| expect(rulerManager.rulers.length, 0); |
| |
| // First ruler cached |
| ruler1 = rulerManager.findOrCreateRuler(style1); |
| expect(rulerManager.rulers.length, 1); |
| expect(ruler1.hitCount, 1); |
| |
| // Increase hit count for style 1 |
| ruler1 = rulerManager.findOrCreateRuler(style1); |
| expect(rulerManager.rulers.length, 1); |
| expect(ruler1.hitCount, 2); |
| |
| // Previous ruler reused |
| rulerManager.findOrCreateRuler(style1); |
| expect(rulerManager.rulers.length, 1); |
| expect(ruler1.hitCount, 3); |
| |
| // Second ruler created and cached |
| ruler2 = rulerManager.findOrCreateRuler(style2); |
| expect(rulerManager.rulers.length, 2); |
| expect(ruler1.hitCount, 3); |
| expect(ruler2.hitCount, 1); |
| |
| // Increase hit count for style 2 |
| rulerManager.findOrCreateRuler(style2); |
| rulerManager.findOrCreateRuler(style2); |
| rulerManager.findOrCreateRuler(style2); |
| expect(rulerManager.rulers.length, 2); |
| expect(ruler2.hitCount, 4); |
| |
| // Third ruler cached: it is ok to store more rulers that the cache |
| // capacity because the cache is cleaned-up at the next microtask. |
| ruler3 = rulerManager.findOrCreateRuler(style3); |
| |
| // Final ruler states |
| expect(rulerManager.rulers.length, 3); |
| expect(ruler1.hitCount, 3); |
| expect(ruler2.hitCount, 4); |
| expect(ruler3.hitCount, 1); |
| // The least hit ruler isn't disposed yet. |
| expect(ruler3.debugIsDisposed, isFalse); |
| |
| // Cleaning up the cache should bring its size down to capacity limit. |
| rulerManager.cleanUpRulerCache(); |
| expect(rulerManager.rulers.length, 2); |
| expect(rulerManager.rulers, containsValue(ruler1)); // retained |
| expect(rulerManager.rulers, containsValue(ruler2)); // retained |
| expect(rulerManager.rulers, isNot(containsValue(ruler3))); // evicted |
| expect(ruler1.debugIsDisposed, isFalse); |
| expect(ruler2.debugIsDisposed, isFalse); |
| expect(ruler3.debugIsDisposed, isTrue); |
| |
| ruler1 = rulerManager.rulers[style1]; |
| expect(ruler1.style, style1); |
| expect(ruler1.hitCount, 0); // hit counts are reset |
| |
| ruler2 = rulerManager.rulers[style2]; |
| expect(ruler2.style, style2); |
| expect(ruler2.hitCount, 0); // hit counts are reset |
| }); |
| }); |
| |
| group('$TextMeasurementService', () { |
| setUp(() { |
| TextMeasurementService.initialize(rulerCacheCapacity: 2); |
| }); |
| tearDown(() { |
| TextMeasurementService.clearCache(); |
| }); |
| |
| testMeasurements( |
| 'preserves whitespace when measuring', |
| (TextMeasurementService instance) { |
| ui.Paragraph text; |
| MeasurementResult result; |
| |
| // leading whitespaces |
| text = build(ahemStyle, ' abc'); |
| result = instance.measure(text, infiniteConstraints); |
| expect(result.maxIntrinsicWidth, 60); |
| expect(result.minIntrinsicWidth, 30); |
| expect(result.height, 10); |
| expect(result.lines, <EngineLineMetrics>[ |
| line(' abc', 0, 6, hardBreak: true, width: 60.0, lineNumber: 0, left: 0.0), |
| ]); |
| |
| // trailing whitespaces |
| text = build(ahemStyle, 'abc '); |
| result = instance.measure(text, infiniteConstraints); |
| expect(result.maxIntrinsicWidth, 60); |
| expect(result.minIntrinsicWidth, 30); |
| expect(result.height, 10); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abc ', 0, 6, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement always includes trailing whitespace in the |
| // width, while Flutter and Canvas-based measurement don't. |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abc ', 0, 6, hardBreak: true, width: 60.0, lineNumber: 0, left: 0.0), |
| ]); |
| } |
| |
| // mixed whitespaces |
| text = build(ahemStyle, ' ab c '); |
| result = instance.measure(text, infiniteConstraints); |
| expect(result.maxIntrinsicWidth, 100); |
| expect(result.minIntrinsicWidth, 20); |
| expect(result.height, 10); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line(' ab c ', 0, 10, hardBreak: true, width: 80.0, lineNumber: 0, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement always includes trailing whitespace in the |
| // width, while Flutter and Canvas-based measurement don't. |
| expect(result.lines, <EngineLineMetrics>[ |
| line(' ab c ', 0, 10, hardBreak: true, width: 100.0, lineNumber: 0, left: 0.0), |
| ]); |
| } |
| |
| // single whitespace |
| text = build(ahemStyle, ' '); |
| result = instance.measure(text, infiniteConstraints); |
| expect(result.maxIntrinsicWidth, 10); |
| expect(result.minIntrinsicWidth, 0); |
| expect(result.height, 10); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line(' ', 0, 1, hardBreak: true, width: 0.0, lineNumber: 0, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement always includes trailing whitespace in the |
| // width, while Flutter and Canvas-based measurement don't. |
| expect(result.lines, <EngineLineMetrics>[ |
| line(' ', 0, 1, hardBreak: true, width: 10.0, lineNumber: 0, left: 0.0), |
| ]); |
| } |
| |
| // whitespace only |
| text = build(ahemStyle, ' '); |
| result = instance.measure(text, infiniteConstraints); |
| expect(result.maxIntrinsicWidth, 50); |
| expect(result.minIntrinsicWidth, 0); |
| expect(result.height, 10); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line(' ', 0, 5, hardBreak: true, width: 0.0, lineNumber: 0, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement always includes trailing whitespace in the |
| // width, while Flutter and Canvas-based measurement don't. |
| expect(result.lines, <EngineLineMetrics>[ |
| line(' ', 0, 5, hardBreak: true, width: 50.0, lineNumber: 0, left: 0.0), |
| ]); |
| } |
| }, |
| ); |
| |
| testMeasurements( |
| 'uses single-line when text can fit without wrapping', |
| (TextMeasurementService instance) { |
| final MeasurementResult result = |
| instance.measure(build(ahemStyle, '12345'), constraints); |
| |
| // Should fit on a single line. |
| expect(result.isSingleLine, true); |
| expect(result.alphabeticBaseline, 8); |
| expect(result.maxIntrinsicWidth, 50); |
| expect(result.minIntrinsicWidth, 50); |
| expect(result.width, 50); |
| expect(result.height, 10); |
| expect(result.lines, <EngineLineMetrics>[ |
| line('12345', 0, 5, hardBreak: true, width: 50.0, lineNumber: 0, left: 0.0), |
| ]); |
| }, |
| ); |
| |
| testMeasurements( |
| 'simple multi-line text', |
| (TextMeasurementService instance) { |
| const ui.ParagraphConstraints constraints = |
| ui.ParagraphConstraints(width: 70); |
| MeasurementResult result; |
| |
| // The long text doesn't fit in 70px of width, so it needs to wrap. |
| result = instance.measure(build(ahemStyle, 'foo bar baz'), constraints); |
| expect(result.isSingleLine, false); |
| expect(result.alphabeticBaseline, 8); |
| expect(result.maxIntrinsicWidth, 110); |
| expect(result.minIntrinsicWidth, 30); |
| expect(result.width, 70); |
| expect(result.height, 20); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('foo bar ', 0, 8, hardBreak: false, width: 70.0, lineNumber: 0, left: 0.0), |
| line('baz', 8, 11, hardBreak: true, width: 30.0, lineNumber: 1, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| }, |
| ); |
| |
| testMeasurements( |
| 'uses multi-line for long text', |
| (TextMeasurementService instance) { |
| MeasurementResult result; |
| |
| // The long text doesn't fit in 50px of width, so it needs to wrap. |
| result = instance.measure(build(ahemStyle, '1234567890'), constraints); |
| expect(result.isSingleLine, false); |
| expect(result.alphabeticBaseline, 8); |
| expect(result.maxIntrinsicWidth, 100); |
| expect(result.minIntrinsicWidth, 100); |
| expect(result.width, 50); |
| expect(result.height, 20); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('12345', 0, 5, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0), |
| line('67890', 5, 10, hardBreak: true, width: 50.0, lineNumber: 1, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // The first word is force-broken twice. |
| result = |
| instance.measure(build(ahemStyle, 'abcdefghijk lm'), constraints); |
| expect(result.isSingleLine, false); |
| expect(result.alphabeticBaseline, 8); |
| expect(result.maxIntrinsicWidth, 140); |
| expect(result.minIntrinsicWidth, 110); |
| expect(result.width, 50); |
| expect(result.height, 30); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abcde', 0, 5, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0), |
| line('fghij', 5, 10, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), |
| line('k lm', 10, 14, hardBreak: true, width: 40.0, lineNumber: 2, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // Constraints aren't enough even for a single character. In this case, |
| // we show a minimum of one character per line. |
| const ui.ParagraphConstraints narrowConstraints = |
| ui.ParagraphConstraints(width: 8); |
| result = instance.measure(build(ahemStyle, 'AA'), narrowConstraints); |
| expect(result.isSingleLine, false); |
| expect(result.alphabeticBaseline, 8); |
| expect(result.maxIntrinsicWidth, 20); |
| expect(result.minIntrinsicWidth, 20); |
| expect(result.width, 8); |
| expect(result.height, 20); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('A', 0, 1, hardBreak: false, width: 10.0, lineNumber: 0, left: 0.0), |
| line('A', 1, 2, hardBreak: true, width: 10.0, lineNumber: 1, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // Extremely narrow constraints with new line in the middle. |
| result = instance.measure(build(ahemStyle, 'AA\nA'), narrowConstraints); |
| expect(result.isSingleLine, false); |
| expect(result.alphabeticBaseline, 8); |
| expect(result.maxIntrinsicWidth, 20); |
| expect(result.minIntrinsicWidth, 20); |
| expect(result.width, 8); |
| expect(result.height, 30); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('A', 0, 1, hardBreak: false, width: 10.0, lineNumber: 0, left: 0.0), |
| line('A', 1, 3, hardBreak: true, width: 10.0, lineNumber: 1, left: 0.0), |
| line('A', 3, 4, hardBreak: true, width: 10.0, lineNumber: 2, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // Extremely narrow constraints with new line in the end. |
| result = instance.measure(build(ahemStyle, 'AAA\n'), narrowConstraints); |
| expect(result.isSingleLine, false); |
| expect(result.alphabeticBaseline, 8); |
| expect(result.maxIntrinsicWidth, 30); |
| expect(result.minIntrinsicWidth, 30); |
| expect(result.width, 8); |
| expect(result.height, 40); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('A', 0, 1, hardBreak: false, width: 10.0, lineNumber: 0, left: 0.0), |
| line('A', 1, 2, hardBreak: false, width: 10.0, lineNumber: 1, left: 0.0), |
| line('A', 2, 4, hardBreak: true, width: 10.0, lineNumber: 2, left: 0.0), |
| line('', 4, 4, hardBreak: true, width: 0.0, lineNumber: 3, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| }, |
| skipDom: browserEngine == BrowserEngine.webkit, |
| ); |
| |
| testMeasurements( |
| 'uses multi-line for text that contains new-line', |
| (TextMeasurementService instance) { |
| final MeasurementResult result = |
| instance.measure(build(ahemStyle, '12\n34'), constraints); |
| |
| // Text containing newlines should always be drawn in multi-line mode. |
| expect(result.isSingleLine, false); |
| expect(result.maxIntrinsicWidth, 20); |
| expect(result.minIntrinsicWidth, 20); |
| expect(result.width, 50); |
| expect(result.height, 20); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('12', 0, 3, hardBreak: true, width: 20.0, lineNumber: 0, left: 0.0), |
| line('34', 3, 5, hardBreak: true, width: 20.0, lineNumber: 1, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| }, |
| ); |
| |
| testMeasurements('empty lines', (TextMeasurementService instance) { |
| MeasurementResult result; |
| |
| // Empty lines in the beginning. |
| result = instance.measure(build(ahemStyle, '\n\n1234'), constraints); |
| expect(result.maxIntrinsicWidth, 40); |
| expect(result.minIntrinsicWidth, 40); |
| expect(result.height, 30); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('', 0, 1, hardBreak: true, width: 0.0, lineNumber: 0, left: 0.0), |
| line('', 1, 2, hardBreak: true, width: 0.0, lineNumber: 1, left: 0.0), |
| line('1234', 2, 6, hardBreak: true, width: 40.0, lineNumber: 2, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // Empty lines in the middle. |
| result = instance.measure(build(ahemStyle, '12\n\n345'), constraints); |
| expect(result.maxIntrinsicWidth, 30); |
| expect(result.minIntrinsicWidth, 30); |
| expect(result.height, 30); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('12', 0, 3, hardBreak: true, width: 20.0, lineNumber: 0, left: 0.0), |
| line('', 3, 4, hardBreak: true, width: 0.0, lineNumber: 1, left: 0.0), |
| line('345', 4, 7, hardBreak: true, width: 30.0, lineNumber: 2, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // Empty lines in the end. |
| result = instance.measure(build(ahemStyle, '1234\n\n'), constraints); |
| expect(result.maxIntrinsicWidth, 40); |
| expect(result.minIntrinsicWidth, 40); |
| if (instance.isCanvas) { |
| // This can only be done correctly in the canvas-based implementation. |
| expect(result.height, 30); |
| expect(result.lines, <EngineLineMetrics>[ |
| line('1234', 0, 5, hardBreak: true, width: 40.0, lineNumber: 0, left: 0.0), |
| line('', 5, 6, hardBreak: true, width: 0.0, lineNumber: 1, left: 0.0), |
| line('', 6, 6, hardBreak: true, width: 0.0, lineNumber: 2, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| }); |
| |
| testMeasurements( |
| 'wraps multi-line text correctly when constraint width is infinite', |
| (TextMeasurementService instance) { |
| final DomParagraph paragraph = build(ahemStyle, '123\n456 789'); |
| final MeasurementResult result = |
| instance.measure(paragraph, infiniteConstraints); |
| |
| expect(result.isSingleLine, false); |
| expect(result.maxIntrinsicWidth, 70); |
| expect(result.minIntrinsicWidth, 30); |
| expect(result.width, double.infinity); |
| expect(result.height, 20); |
| |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('123', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0), |
| line('456 789', 4, 11, hardBreak: true, width: 70.0, lineNumber: 1, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| }, |
| ); |
| |
| testMeasurements( |
| 'takes letter spacing into account', |
| (TextMeasurementService instance) { |
| const ui.ParagraphConstraints constraints = |
| ui.ParagraphConstraints(width: 100); |
| final ui.TextStyle spacedTextStyle = ui.TextStyle(letterSpacing: 3); |
| final ui.Paragraph spacedText = |
| build(ahemStyle, 'abc', textStyle: spacedTextStyle); |
| |
| final MeasurementResult spacedResult = |
| instance.measure(spacedText, constraints); |
| |
| expect(spacedResult.minIntrinsicWidth, 39); |
| expect(spacedResult.maxIntrinsicWidth, 39); |
| }, |
| ); |
| |
| test('takes word spacing into account', () { |
| const ui.ParagraphConstraints constraints = |
| ui.ParagraphConstraints(width: 100); |
| |
| final DomParagraph normalText = build(ahemStyle, 'a b c'); |
| final DomParagraph spacedText = |
| build(ahemStyle, 'a b c', textStyle: ui.TextStyle(wordSpacing: 1.5)); |
| |
| // Word spacing is only supported via DOM measurement. |
| final TextMeasurementService instance = |
| TextMeasurementService.forParagraph(spacedText); |
| expect(instance, const TypeMatcher<DomTextMeasurementService>()); |
| |
| final MeasurementResult normalResult = |
| instance.measure(normalText, constraints); |
| final MeasurementResult spacedResult = |
| instance.measure(spacedText, constraints); |
| |
| expect( |
| normalResult.maxIntrinsicWidth < spacedResult.maxIntrinsicWidth, |
| isTrue, |
| ); |
| }); |
| |
| testMeasurements('minIntrinsicWidth', (TextMeasurementService instance) { |
| MeasurementResult result; |
| |
| // Simple case. |
| result = instance.measure(build(ahemStyle, 'abc de fghi'), constraints); |
| expect(result.minIntrinsicWidth, 40); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abc ', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0, left: 0.0), |
| line('de ', 4, 7, hardBreak: false, width: 20.0, lineNumber: 1, left: 0.0), |
| line('fghi', 7, 11, hardBreak: true, width: 40.0, lineNumber: 2, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // With new lines. |
| result = instance.measure(build(ahemStyle, 'abcd\nef\nghi'), constraints); |
| expect(result.minIntrinsicWidth, 40); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abcd', 0, 5, hardBreak: true, width: 40.0, lineNumber: 0, left: 0.0), |
| line('ef', 5, 8, hardBreak: true, width: 20.0, lineNumber: 1, left: 0.0), |
| line('ghi', 8, 11, hardBreak: true, width: 30.0, lineNumber: 2, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // With trailing whitespace. |
| result = instance.measure(build(ahemStyle, 'abcd efg'), constraints); |
| expect(result.minIntrinsicWidth, 40); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abcd ', 0, 10, hardBreak: false, width: 40.0, lineNumber: 0, left: 0.0), |
| line('efg', 10, 13, hardBreak: true, width: 30.0, lineNumber: 1, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // With trailing whitespace and new lines. |
| result = instance.measure(build(ahemStyle, 'abc \ndefg'), constraints); |
| expect(result.minIntrinsicWidth, 40); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abc ', 0, 8, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0), |
| line('defg', 8, 12, hardBreak: true, width: 40.0, lineNumber: 1, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // Very long text. |
| result = instance.measure(build(ahemStyle, 'AAAAAAAAAAAA'), constraints); |
| expect(result.minIntrinsicWidth, 120); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('AAAAA', 0, 5, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0), |
| line('AAAAA', 5, 10, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), |
| line('AA', 10, 12, hardBreak: true, width: 20.0, lineNumber: 2, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| }); |
| |
| testMeasurements('maxIntrinsicWidth', (TextMeasurementService instance) { |
| MeasurementResult result; |
| |
| // Simple case. |
| result = instance.measure(build(ahemStyle, 'abc de fghi'), constraints); |
| expect(result.maxIntrinsicWidth, 110); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abc ', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0, left: 0.0), |
| line('de ', 4, 7, hardBreak: false, width: 20.0, lineNumber: 1, left: 0.0), |
| line('fghi', 7, 11, hardBreak: true, width: 40.0, lineNumber: 2, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // With new lines. |
| result = instance.measure(build(ahemStyle, 'abcd\nef\nghi'), constraints); |
| expect(result.maxIntrinsicWidth, 40); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abcd', 0, 5, hardBreak: true, width: 40.0, lineNumber: 0, left: 0.0), |
| line('ef', 5, 8, hardBreak: true, width: 20.0, lineNumber: 1, left: 0.0), |
| line('ghi', 8, 11, hardBreak: true, width: 30.0, lineNumber: 2, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // With long whitespace. |
| result = instance.measure(build(ahemStyle, 'abcd efg'), constraints); |
| expect(result.maxIntrinsicWidth, 100); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abcd ', 0, 7, hardBreak: false, width: 40.0, lineNumber: 0, left: 0.0), |
| line('efg', 7, 10, hardBreak: true, width: 30.0, lineNumber: 1, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // With trailing whitespace. |
| result = instance.measure(build(ahemStyle, 'abc def '), constraints); |
| expect(result.maxIntrinsicWidth, 100); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abc ', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0, left: 0.0), |
| line('def ', 4, 10, hardBreak: true, width: 30.0, lineNumber: 1, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // With trailing whitespace and new lines. |
| result = instance.measure(build(ahemStyle, 'abc \ndef '), constraints); |
| expect(result.maxIntrinsicWidth, 60); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abc ', 0, 5, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0), |
| line('def ', 5, 11, hardBreak: true, width: 30.0, lineNumber: 1, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // Very long text. |
| result = instance.measure(build(ahemStyle, 'AAAAAAAAAAAA'), constraints); |
| expect(result.maxIntrinsicWidth, 120); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('AAAAA', 0, 5, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0), |
| line('AAAAA', 5, 10, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), |
| line('AA', 10, 12, hardBreak: true, width: 20.0, lineNumber: 2, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| }); |
| |
| testMeasurements( |
| 'respects text overflow', |
| (TextMeasurementService instance) { |
| final ui.ParagraphStyle overflowStyle = ui.ParagraphStyle( |
| fontFamily: 'ahem', |
| fontSize: 10, |
| ellipsis: '...', |
| ); |
| |
| MeasurementResult result; |
| |
| // The text shouldn't be broken into multiple lines, so the height should |
| // be equal to a height of a single line. |
| final ui.Paragraph longText = build( |
| overflowStyle, |
| 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', |
| ); |
| result = instance.measure(longText, constraints); |
| expect(result.minIntrinsicWidth, 480); |
| expect(result.maxIntrinsicWidth, 480); |
| expect(result.height, 10); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('AA...', 0, 48, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't handle the ellipsis case very well. The |
| // text wraps into multiple lines instead. |
| expect(result.lines, isNull); |
| } |
| |
| // The short prefix should make the text break into two lines, but the |
| // second line should remain unbroken. |
| final ui.Paragraph longTextShortPrefix = build( |
| overflowStyle, |
| 'AAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', |
| ); |
| result = instance.measure(longTextShortPrefix, constraints); |
| expect(result.minIntrinsicWidth, 450); |
| expect(result.maxIntrinsicWidth, 450); |
| expect(result.height, 20); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('AAA', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0), |
| line('AA...', 4, 49, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't handle the ellipsis case very well. The |
| // text wraps into multiple lines instead. |
| expect(result.lines, isNull); |
| } |
| |
| // Tiny constraints. |
| const ui.ParagraphConstraints tinyConstraints = |
| ui.ParagraphConstraints(width: 30); |
| final ui.Paragraph text = build(overflowStyle, 'AAAA'); |
| result = instance.measure(text, tinyConstraints); |
| expect(result.minIntrinsicWidth, 40); |
| expect(result.maxIntrinsicWidth, 40); |
| expect(result.height, 10); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('...', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't handle the ellipsis case very well. The |
| // text wraps into multiple lines instead. |
| expect(result.lines, isNull); |
| } |
| |
| // Tinier constraints (not enough for the ellipsis). |
| const ui.ParagraphConstraints tinierConstraints = |
| ui.ParagraphConstraints(width: 10); |
| result = instance.measure(text, tinierConstraints); |
| expect(result.minIntrinsicWidth, 40); |
| expect(result.maxIntrinsicWidth, 40); |
| expect(result.height, 10); |
| if (instance.isCanvas) { |
| // TODO(flutter_web): https://github.com/flutter/flutter/issues/34346 |
| // expect(result.lines, <EngineLineMetrics>[ |
| // line('.', 0, 4, hardBreak: false, width: 10.0, lineNumber: 0, left: 0.0), |
| // ]); |
| expect(result.lines, <EngineLineMetrics>[ |
| line('...', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't handle the ellipsis case very well. The |
| // text wraps into multiple lines instead. |
| expect(result.lines, isNull); |
| } |
| }, |
| skipDom: browserEngine == BrowserEngine.webkit, |
| ); |
| |
| testMeasurements('respects max lines', (TextMeasurementService instance) { |
| final ui.ParagraphStyle maxlinesStyle = ui.ParagraphStyle( |
| fontFamily: 'ahem', |
| fontSize: 10, |
| maxLines: 2, |
| ); |
| |
| MeasurementResult result; |
| |
| // The height should be that of a single line. |
| final ui.Paragraph oneline = build(maxlinesStyle, 'One line'); |
| result = instance.measure(oneline, infiniteConstraints); |
| expect(result.height, 10); |
| expect(result.lines, <EngineLineMetrics>[ |
| line('One line', 0, 8, hardBreak: true, width: 80.0, lineNumber: 0, left: 0.0), |
| ]); |
| |
| // The height should respect max lines and be limited to two lines here. |
| final ui.Paragraph threelines = |
| build(maxlinesStyle, 'First\nSecond\nThird'); |
| result = instance.measure(threelines, infiniteConstraints); |
| expect(result.height, 20); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('First', 0, 6, hardBreak: true, width: 50.0, lineNumber: 0, left: 0.0), |
| line('Second', 6, 13, hardBreak: true, width: 60.0, lineNumber: 1, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // The height should respect max lines and be limited to two lines here. |
| final ui.Paragraph veryLong = build( |
| maxlinesStyle, |
| 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', |
| ); |
| result = instance.measure(veryLong, constraints); |
| expect(result.height, 20); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('Lorem ', 0, 6, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0), |
| line('ipsum ', 6, 12, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // Case when last line is a long unbreakable word. |
| final ui.Paragraph veryLongLastLine = build( |
| maxlinesStyle, |
| 'AAA AAAAAAAAAAAAAAAAAAA', |
| ); |
| result = instance.measure(veryLongLastLine, constraints); |
| expect(result.height, 20); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('AAA ', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0, left: 0.0), |
| line('AAAAA', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| }); |
| |
| testMeasurements( |
| 'respects text overflow and max lines combined', |
| (TextMeasurementService instance) { |
| const ui.ParagraphConstraints constraints = |
| ui.ParagraphConstraints(width: 60); |
| final ui.ParagraphStyle onelineStyle = ui.ParagraphStyle( |
| fontFamily: 'ahem', |
| fontSize: 10, |
| maxLines: 1, |
| ellipsis: '...', |
| ); |
| final ui.ParagraphStyle multilineStyle = ui.ParagraphStyle( |
| fontFamily: 'ahem', |
| fontSize: 10, |
| maxLines: 2, |
| ellipsis: '...', |
| ); |
| |
| ui.Paragraph p; |
| MeasurementResult result; |
| |
| // Simple no overflow case. |
| p = build(onelineStyle, 'abcdef'); |
| result = instance.measure(p, constraints); |
| expect(result.height, 10); |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abcdef', 0, 6, hardBreak: true, width: 60.0, lineNumber: 0, left: 0.0), |
| ]); |
| |
| // Simple overflow case. |
| p = build(onelineStyle, 'abcd efg'); |
| result = instance.measure(p, constraints); |
| expect(result.height, 10); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abc...', 0, 8, hardBreak: false, width: 60.0, lineNumber: 0, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't handle the ellipsis case very well. The |
| // text wraps into multiple lines instead. |
| expect(result.lines, isNull); |
| } |
| |
| // Another simple overflow case. |
| p = build(onelineStyle, 'a bcde fgh'); |
| result = instance.measure(p, constraints); |
| expect(result.height, 10); |
| if (instance.isCanvas) { |
| expect(result.lines, <EngineLineMetrics>[ |
| line('a b...', 0, 10, hardBreak: false, width: 60.0, lineNumber: 0, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't handle the ellipsis case very well. The |
| // text wraps into multiple lines instead. |
| expect(result.lines, isNull); |
| } |
| |
| // The ellipsis is supposed to go on the second line, but because the |
| // 2nd line doesn't overflow, no ellipsis is shown. |
| p = build(multilineStyle, 'abcdef ghijkl'); |
| result = instance.measure(p, constraints); |
| // This can only be done correctly in the canvas-based implementation. |
| if (instance.isCanvas) { |
| expect(result.height, 20); |
| |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abcdef ', 0, 7, hardBreak: false, width: 60.0, lineNumber: 0, left: 0.0), |
| line('ghijkl', 7, 13, hardBreak: true, width: 60.0, lineNumber: 1, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // But when the 2nd line is long enough, the ellipsis is shown. |
| p = build(multilineStyle, 'abcd efghijkl'); |
| result = instance.measure(p, constraints); |
| // This can only be done correctly in the canvas-based implementation. |
| if (instance.isCanvas) { |
| expect(result.height, 20); |
| |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abcd ', 0, 5, hardBreak: false, width: 40.0, lineNumber: 0, left: 0.0), |
| line('efg...', 5, 13, hardBreak: false, width: 60.0, lineNumber: 1, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // Even if the second line can be broken, we don't break it, we just |
| // insert the ellipsis. |
| p = build(multilineStyle, 'abcde f gh ijk'); |
| result = instance.measure(p, constraints); |
| // This can only be done correctly in the canvas-based implementation. |
| if (instance.isCanvas) { |
| expect(result.height, 20); |
| |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abcde ', 0, 6, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0), |
| line('f g...', 6, 14, hardBreak: false, width: 60.0, lineNumber: 1, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // First line overflows but second line doesn't. |
| p = build(multilineStyle, 'abcdefg hijk'); |
| result = instance.measure(p, constraints); |
| // This can only be done correctly in the canvas-based implementation. |
| if (instance.isCanvas) { |
| expect(result.height, 20); |
| |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abcdef', 0, 6, hardBreak: false, width: 60.0, lineNumber: 0, left: 0.0), |
| line('g hijk', 6, 12, hardBreak: true, width: 60.0, lineNumber: 1, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| |
| // Both first and second lines overflow. |
| p = build(multilineStyle, 'abcdefg hijklmnop'); |
| result = instance.measure(p, constraints); |
| // This can only be done correctly in the canvas-based implementation. |
| if (instance.isCanvas) { |
| expect(result.height, 20); |
| |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abcdef', 0, 6, hardBreak: false, width: 60.0, lineNumber: 0, left: 0.0), |
| line('g h...', 6, 17, hardBreak: false, width: 60.0, lineNumber: 1, left: 0.0), |
| ]); |
| } else { |
| // DOM-based measurement can't produce line metrics for multi-line |
| // paragraphs. |
| expect(result.lines, isNull); |
| } |
| }, |
| ); |
| |
| test('handles textAlign', () { |
| TextMeasurementService instance = TextMeasurementService.canvasInstance; |
| ui.Paragraph p; |
| MeasurementResult result; |
| |
| ui.ParagraphStyle createStyle(ui.TextAlign textAlign) { |
| return ui.ParagraphStyle( |
| fontFamily: 'ahem', |
| fontSize: 10, |
| textAlign: textAlign, |
| textDirection: ui.TextDirection.ltr, |
| ); |
| } |
| |
| p = build(createStyle(ui.TextAlign.start), 'abc\ndefghi'); |
| result = instance.measure(p, constraints); |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0), |
| line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), |
| line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 0.0), |
| ]); |
| |
| p = build(createStyle(ui.TextAlign.end), 'abc\ndefghi'); |
| result = instance.measure(p, constraints); |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 20.0), |
| line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), |
| line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 40.0), |
| ]); |
| |
| p = build(createStyle(ui.TextAlign.center), 'abc\ndefghi'); |
| result = instance.measure(p, constraints); |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 10.0), |
| line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), |
| line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 20.0), |
| ]); |
| |
| p = build(createStyle(ui.TextAlign.left), 'abc\ndefghi'); |
| result = instance.measure(p, constraints); |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0), |
| line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), |
| line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 0.0), |
| ]); |
| |
| p = build(createStyle(ui.TextAlign.right), 'abc\ndefghi'); |
| result = instance.measure(p, constraints); |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 20.0), |
| line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), |
| line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 40.0), |
| ]); |
| }); |
| |
| testMeasurements( |
| 'handles rtl with textAlign', |
| (TextMeasurementService instance) { |
| TextMeasurementService instance = TextMeasurementService.canvasInstance; |
| ui.Paragraph p; |
| MeasurementResult result; |
| |
| ui.ParagraphStyle createStyle(ui.TextAlign textAlign) { |
| return ui.ParagraphStyle( |
| fontFamily: 'ahem', |
| fontSize: 10, |
| textAlign: textAlign, |
| textDirection: ui.TextDirection.rtl, |
| ); |
| } |
| |
| p = build(createStyle(ui.TextAlign.start), 'abc\ndefghi'); |
| result = instance.measure(p, constraints); |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 20.0), |
| line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), |
| line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 40.0), |
| ]); |
| |
| p = build(createStyle(ui.TextAlign.end), 'abc\ndefghi'); |
| result = instance.measure(p, constraints); |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0), |
| line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), |
| line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 0.0), |
| ]); |
| |
| p = build(createStyle(ui.TextAlign.center), 'abc\ndefghi'); |
| result = instance.measure(p, constraints); |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 10.0), |
| line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), |
| line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 20.0), |
| ]); |
| |
| p = build(createStyle(ui.TextAlign.left), 'abc\ndefghi'); |
| result = instance.measure(p, constraints); |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0), |
| line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), |
| line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 0.0), |
| ]); |
| |
| p = build(createStyle(ui.TextAlign.right), 'abc\ndefghi'); |
| result = instance.measure(p, constraints); |
| expect(result.lines, <EngineLineMetrics>[ |
| line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 20.0), |
| line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0), |
| line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 40.0), |
| ]); |
| }, |
| ); |
| }); |
| } |
| |
| /// Shortcut to avoid many line wraps in the tests above. |
| EngineLineMetrics line( |
| String displayText, |
| int startIndex, |
| int endIndex, { |
| double width, |
| int lineNumber, |
| bool hardBreak, |
| double left, |
| }) { |
| return EngineLineMetrics.withText( |
| displayText, |
| startIndex: startIndex, |
| endIndex: endIndex, |
| hardBreak: hardBreak, |
| width: width, |
| lineNumber: lineNumber, |
| left: left, |
| endIndexWithoutNewlines: -1, |
| widthWithTrailingSpaces: width, |
| ); |
| } |