diff --git a/lib/web_ui/lib/src/engine/text/measurement.dart b/lib/web_ui/lib/src/engine/text/measurement.dart index e22fd5ea8169e..ccd05f5eab336 100644 --- a/lib/web_ui/lib/src/engine/text/measurement.dart +++ b/lib/web_ui/lib/src/engine/text/measurement.dart @@ -412,12 +412,15 @@ class DomTextMeasurementService extends TextMeasurementService { _applySubPixelRoundingHack(minIntrinsicWidth, maxIntrinsicWidth); final double ideographicBaseline = alphabeticBaseline * _baselineRatioHack; + final String text = paragraph._plainText; List lines; - if (paragraph._plainText != null) { + if (text != null) { final double lineWidth = maxIntrinsicWidth; lines = [ EngineLineMetrics.withText( - paragraph._plainText, + text, + startIndex: 0, + endIndex: text.length, hardBreak: true, width: lineWidth, lineNumber: 0, @@ -767,6 +770,8 @@ class LinesCalculator { ); lines.add(EngineLineMetrics.withText( _text.substring(_lineStart, breakingPoint) + _style.ellipsis, + startIndex: _lineStart, + endIndex: chunkEnd, hardBreak: false, width: measureSubstring(_lineStart, breakingPoint) + _ellipsisWidth, lineNumber: lines.length, @@ -823,6 +828,8 @@ class LinesCalculator { final int lineNumber = lines.length; final EngineLineMetrics metrics = EngineLineMetrics.withText( _text.substring(_lineStart, endWithoutNewlines), + startIndex: _lineStart, + endIndex: lineEnd, hardBreak: isHardBreak, width: measureSubstring(_lineStart, endWithoutSpace), lineNumber: lineNumber, diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index f275a22900e73..46540ef72fc24 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -15,10 +15,14 @@ class EngineLineMetrics implements ui.LineMetrics { this.left, this.baseline, this.lineNumber, - }) : text = null; + }) : text = null, + startIndex = -1, + endIndex = -1; EngineLineMetrics.withText( this.text, { + @required this.startIndex, + @required this.endIndex, @required this.hardBreak, this.ascent, this.descent, @@ -36,6 +40,15 @@ class EngineLineMetrics implements ui.LineMetrics { /// The textual content representing this line. final String text; + /// The index (inclusive) in the text where this line begins. + final int startIndex; + + /// The index (exclusive) in the text where this line ends. + /// + /// When the line contains an overflow, then [endIndex] goes until the end of + /// the text and doesn't stop at the overflow cutoff. + final int endIndex; + @override final bool hardBreak; @@ -66,6 +79,8 @@ class EngineLineMetrics implements ui.LineMetrics { @override int get hashCode => ui.hashValues( text, + startIndex, + endIndex, hardBreak, ascent, descent, @@ -88,6 +103,8 @@ class EngineLineMetrics implements ui.LineMetrics { } final EngineLineMetrics typedOther = other; return text == typedOther.text && + startIndex == typedOther.startIndex && + endIndex == typedOther.endIndex && hardBreak == typedOther.hardBreak && ascent == typedOther.ascent && descent == typedOther.descent && @@ -431,9 +448,18 @@ class EngineParagraph implements ui.Paragraph { @override ui.TextRange getLineBoundary(ui.TextPosition position) { - // TODO(flutter_web): https://github.com/flutter/flutter/issues/39537 - // Depends upon LineMetrics measurement. - return null; + final List lines = _measurementResult.lines; + if (lines != null) { + final int offset = position.offset; + + for (int i = 0; i < lines.length; i++) { + final EngineLineMetrics line = lines[i]; + if (offset >= line.startIndex && offset < line.endIndex) { + return ui.TextRange(start: line.startIndex, end: line.endIndex); + } + } + } + return ui.TextRange.empty; } @override diff --git a/lib/web_ui/test/paragraph_test.dart b/lib/web_ui/test/paragraph_test.dart index dba932d6b3f8e..ce8ebde0d8c7b 100644 --- a/lib/web_ui/test/paragraph_test.dart +++ b/lib/web_ui/test/paragraph_test.dart @@ -186,4 +186,74 @@ void main() async { TextMeasurementService.clearCache(); TextMeasurementService.enableExperimentalCanvasImplementation = false; }); + + testEachMeasurement('getLineBoundary (single-line)', () { + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( + fontFamily: 'Ahem', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: 10, + )); + builder.addText('One single line'); + final Paragraph paragraph = builder.build(); + paragraph.layout(const ParagraphConstraints(width: 400.0)); + + // "One single line".length == 15 + for (int i = 0; i < 15; i++) { + expect( + paragraph.getLineBoundary(TextPosition(offset: i)), + TextRange(start: 0, end: 15), + reason: 'failed at offset $i', + ); + } + }); + + test('getLineBoundary (multi-line)', () { + // [Paragraph.getLineBoundary] for multi-line paragraphs is only supported + // by canvas-based measurement. + TextMeasurementService.enableExperimentalCanvasImplementation = true; + TextMeasurementService.initialize(rulerCacheCapacity: 2); + + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( + fontFamily: 'Ahem', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: 10, + )); + builder.addText('First line\n'); + builder.addText('Second line\n'); + builder.addText('Third line'); + final Paragraph paragraph = builder.build(); + paragraph.layout(const ParagraphConstraints(width: 400.0)); + + // "First line\n".length == 11 + for (int i = 0; i < 11; i++) { + expect( + paragraph.getLineBoundary(TextPosition(offset: i)), + TextRange(start: 0, end: 11), + reason: 'failed at offset $i', + ); + } + + // "Second line\n".length == 12 + for (int i = 11; i < 23; i++) { + expect( + paragraph.getLineBoundary(TextPosition(offset: i)), + TextRange(start: 11, end: 23), + reason: 'failed at offset $i', + ); + } + + // "Third line".length == 10 + for (int i = 23; i < 33; i++) { + expect( + paragraph.getLineBoundary(TextPosition(offset: i)), + TextRange(start: 23, end: 33), + reason: 'failed at offset $i', + ); + } + + TextMeasurementService.clearCache(); + TextMeasurementService.enableExperimentalCanvasImplementation = false; + }); } diff --git a/lib/web_ui/test/text/measurement_test.dart b/lib/web_ui/test/text/measurement_test.dart index ee7b923c467d2..081e899a66c78 100644 --- a/lib/web_ui/test/text/measurement_test.dart +++ b/lib/web_ui/test/text/measurement_test.dart @@ -156,7 +156,7 @@ void main() async { expect(result.minIntrinsicWidth, 30); expect(result.height, 10); expect(result.lines, [ - line(' abc', hardBreak: true, width: 60.0, lineNumber: 0), + line(' abc', 0, 6, hardBreak: true, width: 60.0, lineNumber: 0), ]); // trailing whitespaces @@ -167,13 +167,13 @@ void main() async { expect(result.height, 10); if (instance.isCanvas) { expect(result.lines, [ - line('abc ', hardBreak: true, width: 30.0, lineNumber: 0), + line('abc ', 0, 6, hardBreak: true, width: 30.0, lineNumber: 0), ]); } else { // DOM-based measurement always includes trailing whitespace in the // width, while Flutter and Canvas-based measurement don't. expect(result.lines, [ - line('abc ', hardBreak: true, width: 60.0, lineNumber: 0), + line('abc ', 0, 6, hardBreak: true, width: 60.0, lineNumber: 0), ]); } @@ -185,13 +185,13 @@ void main() async { expect(result.height, 10); if (instance.isCanvas) { expect(result.lines, [ - line(' ab c ', hardBreak: true, width: 80.0, lineNumber: 0), + line(' ab c ', 0, 10, hardBreak: true, width: 80.0, lineNumber: 0), ]); } else { // DOM-based measurement always includes trailing whitespace in the // width, while Flutter and Canvas-based measurement don't. expect(result.lines, [ - line(' ab c ', hardBreak: true, width: 100.0, lineNumber: 0), + line(' ab c ', 0, 10, hardBreak: true, width: 100.0, lineNumber: 0), ]); } @@ -203,13 +203,13 @@ void main() async { expect(result.height, 10); if (instance.isCanvas) { expect(result.lines, [ - line(' ', hardBreak: true, width: 0.0, lineNumber: 0), + line(' ', 0, 1, hardBreak: true, width: 0.0, lineNumber: 0), ]); } else { // DOM-based measurement always includes trailing whitespace in the // width, while Flutter and Canvas-based measurement don't. expect(result.lines, [ - line(' ', hardBreak: true, width: 10.0, lineNumber: 0), + line(' ', 0, 1, hardBreak: true, width: 10.0, lineNumber: 0), ]); } @@ -221,13 +221,13 @@ void main() async { expect(result.height, 10); if (instance.isCanvas) { expect(result.lines, [ - line(' ', hardBreak: true, width: 0.0, lineNumber: 0), + line(' ', 0, 5, hardBreak: true, width: 0.0, lineNumber: 0), ]); } else { // DOM-based measurement always includes trailing whitespace in the // width, while Flutter and Canvas-based measurement don't. expect(result.lines, [ - line(' ', hardBreak: true, width: 50.0, lineNumber: 0), + line(' ', 0, 5, hardBreak: true, width: 50.0, lineNumber: 0), ]); } }, @@ -246,7 +246,7 @@ void main() async { expect(result.width, 50); expect(result.height, 10); expect(result.lines, [ - line('12345', hardBreak: true, width: 50.0, lineNumber: 0), + line('12345', 0, 5, hardBreak: true, width: 50.0, lineNumber: 0), ]); }, ); @@ -267,8 +267,8 @@ void main() async { expect(result.height, 20); if (instance.isCanvas) { expect(result.lines, [ - line('foo bar ', hardBreak: false, width: 70.0, lineNumber: 0), - line('baz', hardBreak: true, width: 30.0, lineNumber: 1), + line('foo bar ', 0, 8, hardBreak: false, width: 70.0, lineNumber: 0), + line('baz', 8, 11, hardBreak: true, width: 30.0, lineNumber: 1), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -292,8 +292,8 @@ void main() async { expect(result.height, 20); if (instance.isCanvas) { expect(result.lines, [ - line('12345', hardBreak: false, width: 50.0, lineNumber: 0), - line('67890', hardBreak: true, width: 50.0, lineNumber: 1), + line('12345', 0, 5, hardBreak: false, width: 50.0, lineNumber: 0), + line('67890', 5, 10, hardBreak: true, width: 50.0, lineNumber: 1), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -311,9 +311,9 @@ void main() async { expect(result.height, 30); if (instance.isCanvas) { expect(result.lines, [ - line('abcde', hardBreak: false, width: 50.0, lineNumber: 0), - line('fghij', hardBreak: false, width: 50.0, lineNumber: 1), - line('k lm', hardBreak: true, width: 40.0, lineNumber: 2), + line('abcde', 0, 5, hardBreak: false, width: 50.0, lineNumber: 0), + line('fghij', 5, 10, hardBreak: false, width: 50.0, lineNumber: 1), + line('k lm', 10, 14, hardBreak: true, width: 40.0, lineNumber: 2), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -333,8 +333,8 @@ void main() async { expect(result.height, 20); if (instance.isCanvas) { expect(result.lines, [ - line('A', hardBreak: false, width: 10.0, lineNumber: 0), - line('A', hardBreak: true, width: 10.0, lineNumber: 1), + line('A', 0, 1, hardBreak: false, width: 10.0, lineNumber: 0), + line('A', 1, 2, hardBreak: true, width: 10.0, lineNumber: 1), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -351,9 +351,9 @@ void main() async { expect(result.height, 30); if (instance.isCanvas) { expect(result.lines, [ - line('A', hardBreak: false, width: 10.0, lineNumber: 0), - line('A', hardBreak: true, width: 10.0, lineNumber: 1), - line('A', hardBreak: true, width: 10.0, lineNumber: 2), + line('A', 0, 1, hardBreak: false, width: 10.0, lineNumber: 0), + line('A', 1, 3, hardBreak: true, width: 10.0, lineNumber: 1), + line('A', 3, 4, hardBreak: true, width: 10.0, lineNumber: 2), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -370,10 +370,10 @@ void main() async { expect(result.height, 40); if (instance.isCanvas) { expect(result.lines, [ - line('A', hardBreak: false, width: 10.0, lineNumber: 0), - line('A', hardBreak: false, width: 10.0, lineNumber: 1), - line('A', hardBreak: true, width: 10.0, lineNumber: 2), - line('', hardBreak: true, width: 0.0, lineNumber: 3), + line('A', 0, 1, hardBreak: false, width: 10.0, lineNumber: 0), + line('A', 1, 2, hardBreak: false, width: 10.0, lineNumber: 1), + line('A', 2, 4, hardBreak: true, width: 10.0, lineNumber: 2), + line('', 4, 4, hardBreak: true, width: 0.0, lineNumber: 3), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -397,8 +397,8 @@ void main() async { expect(result.height, 20); if (instance.isCanvas) { expect(result.lines, [ - line('12', hardBreak: true, width: 20.0, lineNumber: 0), - line('34', hardBreak: true, width: 20.0, lineNumber: 1), + line('12', 0, 3, hardBreak: true, width: 20.0, lineNumber: 0), + line('34', 3, 5, hardBreak: true, width: 20.0, lineNumber: 1), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -418,9 +418,9 @@ void main() async { expect(result.height, 30); if (instance.isCanvas) { expect(result.lines, [ - line('', hardBreak: true, width: 0.0, lineNumber: 0), - line('', hardBreak: true, width: 0.0, lineNumber: 1), - line('1234', hardBreak: true, width: 40.0, lineNumber: 2), + line('', 0, 1, hardBreak: true, width: 0.0, lineNumber: 0), + line('', 1, 2, hardBreak: true, width: 0.0, lineNumber: 1), + line('1234', 2, 6, hardBreak: true, width: 40.0, lineNumber: 2), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -435,9 +435,9 @@ void main() async { expect(result.height, 30); if (instance.isCanvas) { expect(result.lines, [ - line('12', hardBreak: true, width: 20.0, lineNumber: 0), - line('', hardBreak: true, width: 0.0, lineNumber: 1), - line('345', hardBreak: true, width: 30.0, lineNumber: 2), + line('12', 0, 3, hardBreak: true, width: 20.0, lineNumber: 0), + line('', 3, 4, hardBreak: true, width: 0.0, lineNumber: 1), + line('345', 4, 7, hardBreak: true, width: 30.0, lineNumber: 2), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -453,9 +453,9 @@ void main() async { // This can only be done correctly in the canvas-based implementation. expect(result.height, 30); expect(result.lines, [ - line('1234', hardBreak: true, width: 40.0, lineNumber: 0), - line('', hardBreak: true, width: 0.0, lineNumber: 1), - line('', hardBreak: true, width: 0.0, lineNumber: 2), + line('1234', 0, 5, hardBreak: true, width: 40.0, lineNumber: 0), + line('', 5, 6, hardBreak: true, width: 0.0, lineNumber: 1), + line('', 6, 6, hardBreak: true, width: 0.0, lineNumber: 2), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -479,8 +479,8 @@ void main() async { if (instance.isCanvas) { expect(result.lines, [ - line('123', hardBreak: true, width: 30.0, lineNumber: 0), - line('456 789', hardBreak: true, width: 70.0, lineNumber: 1), + line('123', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0), + line('456 789', 4, 11, hardBreak: true, width: 70.0, lineNumber: 1), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -544,9 +544,9 @@ void main() async { expect(result.minIntrinsicWidth, 40); if (instance.isCanvas) { expect(result.lines, [ - line('abc ', hardBreak: false, width: 30.0, lineNumber: 0), - line('de ', hardBreak: false, width: 20.0, lineNumber: 1), - line('fghi', hardBreak: true, width: 40.0, lineNumber: 2), + line('abc ', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0), + line('de ', 4, 7, hardBreak: false, width: 20.0, lineNumber: 1), + line('fghi', 7, 11, hardBreak: true, width: 40.0, lineNumber: 2), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -559,9 +559,9 @@ void main() async { expect(result.minIntrinsicWidth, 40); if (instance.isCanvas) { expect(result.lines, [ - line('abcd', hardBreak: true, width: 40.0, lineNumber: 0), - line('ef', hardBreak: true, width: 20.0, lineNumber: 1), - line('ghi', hardBreak: true, width: 30.0, lineNumber: 2), + line('abcd', 0, 5, hardBreak: true, width: 40.0, lineNumber: 0), + line('ef', 5, 8, hardBreak: true, width: 20.0, lineNumber: 1), + line('ghi', 8, 11, hardBreak: true, width: 30.0, lineNumber: 2), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -574,8 +574,8 @@ void main() async { expect(result.minIntrinsicWidth, 40); if (instance.isCanvas) { expect(result.lines, [ - line('abcd ', hardBreak: false, width: 40.0, lineNumber: 0), - line('efg', hardBreak: true, width: 30.0, lineNumber: 1), + line('abcd ', 0, 10, hardBreak: false, width: 40.0, lineNumber: 0), + line('efg', 10, 13, hardBreak: true, width: 30.0, lineNumber: 1), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -588,8 +588,8 @@ void main() async { expect(result.minIntrinsicWidth, 40); if (instance.isCanvas) { expect(result.lines, [ - line('abc ', hardBreak: true, width: 30.0, lineNumber: 0), - line('defg', hardBreak: true, width: 40.0, lineNumber: 1), + line('abc ', 0, 8, hardBreak: true, width: 30.0, lineNumber: 0), + line('defg', 8, 12, hardBreak: true, width: 40.0, lineNumber: 1), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -602,9 +602,9 @@ void main() async { expect(result.minIntrinsicWidth, 120); if (instance.isCanvas) { expect(result.lines, [ - line('AAAAA', hardBreak: false, width: 50.0, lineNumber: 0), - line('AAAAA', hardBreak: false, width: 50.0, lineNumber: 1), - line('AA', hardBreak: true, width: 20.0, lineNumber: 2), + line('AAAAA', 0, 5, hardBreak: false, width: 50.0, lineNumber: 0), + line('AAAAA', 5, 10, hardBreak: false, width: 50.0, lineNumber: 1), + line('AA', 10, 12, hardBreak: true, width: 20.0, lineNumber: 2), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -621,9 +621,9 @@ void main() async { expect(result.maxIntrinsicWidth, 110); if (instance.isCanvas) { expect(result.lines, [ - line('abc ', hardBreak: false, width: 30.0, lineNumber: 0), - line('de ', hardBreak: false, width: 20.0, lineNumber: 1), - line('fghi', hardBreak: true, width: 40.0, lineNumber: 2), + line('abc ', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0), + line('de ', 4, 7, hardBreak: false, width: 20.0, lineNumber: 1), + line('fghi', 7, 11, hardBreak: true, width: 40.0, lineNumber: 2), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -636,9 +636,9 @@ void main() async { expect(result.maxIntrinsicWidth, 40); if (instance.isCanvas) { expect(result.lines, [ - line('abcd', hardBreak: true, width: 40.0, lineNumber: 0), - line('ef', hardBreak: true, width: 20.0, lineNumber: 1), - line('ghi', hardBreak: true, width: 30.0, lineNumber: 2), + line('abcd', 0, 5, hardBreak: true, width: 40.0, lineNumber: 0), + line('ef', 5, 8, hardBreak: true, width: 20.0, lineNumber: 1), + line('ghi', 8, 11, hardBreak: true, width: 30.0, lineNumber: 2), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -651,8 +651,8 @@ void main() async { expect(result.maxIntrinsicWidth, 100); if (instance.isCanvas) { expect(result.lines, [ - line('abcd ', hardBreak: false, width: 40.0, lineNumber: 0), - line('efg', hardBreak: true, width: 30.0, lineNumber: 1), + line('abcd ', 0, 7, hardBreak: false, width: 40.0, lineNumber: 0), + line('efg', 7, 10, hardBreak: true, width: 30.0, lineNumber: 1), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -665,8 +665,8 @@ void main() async { expect(result.maxIntrinsicWidth, 100); if (instance.isCanvas) { expect(result.lines, [ - line('abc ', hardBreak: false, width: 30.0, lineNumber: 0), - line('def ', hardBreak: true, width: 30.0, lineNumber: 1), + line('abc ', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0), + line('def ', 4, 10, hardBreak: true, width: 30.0, lineNumber: 1), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -679,8 +679,8 @@ void main() async { expect(result.maxIntrinsicWidth, 60); if (instance.isCanvas) { expect(result.lines, [ - line('abc ', hardBreak: true, width: 30.0, lineNumber: 0), - line('def ', hardBreak: true, width: 30.0, lineNumber: 1), + line('abc ', 0, 5, hardBreak: true, width: 30.0, lineNumber: 0), + line('def ', 5, 11, hardBreak: true, width: 30.0, lineNumber: 1), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -693,9 +693,9 @@ void main() async { expect(result.maxIntrinsicWidth, 120); if (instance.isCanvas) { expect(result.lines, [ - line('AAAAA', hardBreak: false, width: 50.0, lineNumber: 0), - line('AAAAA', hardBreak: false, width: 50.0, lineNumber: 1), - line('AA', hardBreak: true, width: 20.0, lineNumber: 2), + line('AAAAA', 0, 5, hardBreak: false, width: 50.0, lineNumber: 0), + line('AAAAA', 5, 10, hardBreak: false, width: 50.0, lineNumber: 1), + line('AA', 10, 12, hardBreak: true, width: 20.0, lineNumber: 2), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -727,7 +727,7 @@ void main() async { expect(result.height, 10); if (instance.isCanvas) { expect(result.lines, [ - line('AA...', hardBreak: false, width: 50.0, lineNumber: 0), + line('AA...', 0, 48, hardBreak: false, width: 50.0, lineNumber: 0), ]); } else { // DOM-based measurement can't handle the ellipsis case very well. The @@ -747,8 +747,8 @@ void main() async { expect(result.height, 20); if (instance.isCanvas) { expect(result.lines, [ - line('AAA', hardBreak: true, width: 30.0, lineNumber: 0), - line('AA...', hardBreak: false, width: 50.0, lineNumber: 1), + line('AAA', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0), + line('AA...', 4, 49, hardBreak: false, width: 50.0, lineNumber: 1), ]); } else { // DOM-based measurement can't handle the ellipsis case very well. The @@ -766,7 +766,7 @@ void main() async { expect(result.height, 10); if (instance.isCanvas) { expect(result.lines, [ - line('...', hardBreak: false, width: 30.0, lineNumber: 0), + line('...', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0), ]); } else { // DOM-based measurement can't handle the ellipsis case very well. The @@ -787,7 +787,7 @@ void main() async { // line('.', hardBreak: false, width: 10.0, lineNumber: 0), // ]); expect(result.lines, [ - line('...', hardBreak: false, width: 30.0, lineNumber: 0), + line('...', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0), ]); } else { // DOM-based measurement can't handle the ellipsis case very well. The @@ -811,7 +811,7 @@ void main() async { result = instance.measure(oneline, infiniteConstraints); expect(result.height, 10); expect(result.lines, [ - line('One line', hardBreak: true, width: 80.0, lineNumber: 0), + line('One line', 0, 8, hardBreak: true, width: 80.0, lineNumber: 0), ]); // The height should respect max lines and be limited to two lines here. @@ -821,8 +821,8 @@ void main() async { expect(result.height, 20); if (instance.isCanvas) { expect(result.lines, [ - line('First', hardBreak: true, width: 50.0, lineNumber: 0), - line('Second', hardBreak: true, width: 60.0, lineNumber: 1), + line('First', 0, 6, hardBreak: true, width: 50.0, lineNumber: 0), + line('Second', 6, 13, hardBreak: true, width: 60.0, lineNumber: 1), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -839,8 +839,8 @@ void main() async { expect(result.height, 20); if (instance.isCanvas) { expect(result.lines, [ - line('Lorem ', hardBreak: false, width: 50.0, lineNumber: 0), - line('ipsum ', hardBreak: false, width: 50.0, lineNumber: 1), + line('Lorem ', 0, 6, hardBreak: false, width: 50.0, lineNumber: 0), + line('ipsum ', 6, 12, hardBreak: false, width: 50.0, lineNumber: 1), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -857,8 +857,8 @@ void main() async { expect(result.height, 20); if (instance.isCanvas) { expect(result.lines, [ - line('AAA ', hardBreak: false, width: 30.0, lineNumber: 0), - line('AAAAA', hardBreak: false, width: 50.0, lineNumber: 1), + line('AAA ', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0), + line('AAAAA', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -893,7 +893,7 @@ void main() async { result = instance.measure(p, constraints); expect(result.height, 10); expect(result.lines, [ - line('abcdef', hardBreak: true, width: 60.0, lineNumber: 0), + line('abcdef', 0, 6, hardBreak: true, width: 60.0, lineNumber: 0), ]); // Simple overflow case. @@ -902,7 +902,7 @@ void main() async { expect(result.height, 10); if (instance.isCanvas) { expect(result.lines, [ - line('abc...', hardBreak: false, width: 60.0, lineNumber: 0), + line('abc...', 0, 8, hardBreak: false, width: 60.0, lineNumber: 0), ]); } else { // DOM-based measurement can't handle the ellipsis case very well. The @@ -916,7 +916,7 @@ void main() async { expect(result.height, 10); if (instance.isCanvas) { expect(result.lines, [ - line('a b...', hardBreak: false, width: 60.0, lineNumber: 0), + line('a b...', 0, 10, hardBreak: false, width: 60.0, lineNumber: 0), ]); } else { // DOM-based measurement can't handle the ellipsis case very well. The @@ -933,8 +933,8 @@ void main() async { expect(result.height, 20); expect(result.lines, [ - line('abcdef ', hardBreak: false, width: 60.0, lineNumber: 0), - line('ghijkl', hardBreak: true, width: 60.0, lineNumber: 1), + line('abcdef ', 0, 7, hardBreak: false, width: 60.0, lineNumber: 0), + line('ghijkl', 7, 13, hardBreak: true, width: 60.0, lineNumber: 1), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -950,8 +950,8 @@ void main() async { expect(result.height, 20); expect(result.lines, [ - line('abcd ', hardBreak: false, width: 40.0, lineNumber: 0), - line('efg...', hardBreak: false, width: 60.0, lineNumber: 1), + line('abcd ', 0, 5, hardBreak: false, width: 40.0, lineNumber: 0), + line('efg...', 5, 13, hardBreak: false, width: 60.0, lineNumber: 1), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -968,8 +968,8 @@ void main() async { expect(result.height, 20); expect(result.lines, [ - line('abcde ', hardBreak: false, width: 50.0, lineNumber: 0), - line('f g...', hardBreak: false, width: 60.0, lineNumber: 1), + line('abcde ', 0, 6, hardBreak: false, width: 50.0, lineNumber: 0), + line('f g...', 6, 14, hardBreak: false, width: 60.0, lineNumber: 1), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -985,8 +985,8 @@ void main() async { expect(result.height, 20); expect(result.lines, [ - line('abcdef', hardBreak: false, width: 60.0, lineNumber: 0), - line('g hijk', hardBreak: true, width: 60.0, lineNumber: 1), + line('abcdef', 0, 6, hardBreak: false, width: 60.0, lineNumber: 0), + line('g hijk', 6, 12, hardBreak: true, width: 60.0, lineNumber: 1), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -1002,8 +1002,8 @@ void main() async { expect(result.height, 20); expect(result.lines, [ - line('abcdef', hardBreak: false, width: 60.0, lineNumber: 0), - line('g h...', hardBreak: false, width: 60.0, lineNumber: 1), + line('abcdef', 0, 6, hardBreak: false, width: 60.0, lineNumber: 0), + line('g h...', 6, 17, hardBreak: false, width: 60.0, lineNumber: 1), ]); } else { // DOM-based measurement can't produce line metrics for multi-line @@ -1017,13 +1017,17 @@ void main() async { /// Shortcut to avoid many line wraps in the tests above. EngineLineMetrics line( - String text, { + String text, + int startIndex, + int endIndex, { double width, int lineNumber, bool hardBreak, }) { return EngineLineMetrics.withText( text, + startIndex: startIndex, + endIndex: endIndex, hardBreak: hardBreak, width: width, lineNumber: lineNumber,