Skip to content

Commit

Permalink
[web] Respect maxLines when calculating boxes for a range (flutter#16749
Browse files Browse the repository at this point in the history
)
  • Loading branch information
mdebbar authored Feb 24, 2020
1 parent 971122b commit d670059
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 27 deletions.
8 changes: 5 additions & 3 deletions lib/web_ui/lib/src/engine/text/paragraph.dart
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,10 @@ class EngineParagraph implements ui.Paragraph {
}

final List<EngineLineMetrics> lines = _measurementResult.lines;
if (start >= lines.last.endIndex) {
return <ui.TextBox>[];
}

final EngineLineMetrics startLine = _getLineForIndex(start);
EngineLineMetrics endLine = _getLineForIndex(end);

Expand Down Expand Up @@ -535,8 +539,7 @@ class EngineParagraph implements ui.Paragraph {
EngineLineMetrics _getLineForIndex(int index) {
assert(_hasLineMetrics);
final List<EngineLineMetrics> lines = _measurementResult.lines;
assert(index >= lines.first.startIndex);
assert(index <= lines.last.endIndex);
assert(index >= 0);

for (int i = 0; i < lines.length; i++) {
final EngineLineMetrics line = lines[i];
Expand All @@ -545,7 +548,6 @@ class EngineParagraph implements ui.Paragraph {
}
}

assert(index == lines.last.endIndex);
return lines.last;
}

Expand Down
16 changes: 16 additions & 0 deletions lib/web_ui/lib/src/engine/text/ruler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -733,14 +733,30 @@ class ParagraphRuler {
final List<html.Rectangle<num>> clientRects = rangeSpan.getClientRects();
final List<ui.TextBox> boxes = <ui.TextBox>[];

final double maxLinesLimit = style.maxLines == null
? double.infinity
: style.maxLines * lineHeightDimensions.height;

html.Rectangle<num> previousRect;
for (html.Rectangle<num> rect in clientRects) {
// If [rect] is an empty box on the same line as the previous box, don't
// include it in the result.
if (rect.top == previousRect?.top && rect.left == rect.right) {
continue;
}
// As soon as we go beyond [maxLines], stop adding boxes.
if (rect.top >= maxLinesLimit) {
break;
}

boxes.add(ui.TextBox.fromLTRBD(
rect.left + alignOffset,
rect.top,
rect.right + alignOffset,
rect.bottom,
textDirection,
));
previousRect = rect;
}

// Cleanup after measuring the boxes.
Expand Down
198 changes: 174 additions & 24 deletions lib/web_ui/test/paragraph_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -441,12 +441,6 @@ void main() async {
final Paragraph paragraph = builder.build();
paragraph.layout(const ParagraphConstraints(width: 100));

// In the dom-based measurement (except Firefox), there will be some
// discrepancies around line ends.
final isDiscrepancyExpected =
!TextMeasurementService.enableExperimentalCanvasImplementation &&
browserEngine != BrowserEngine.firefox;

// First line: "abcd\n"

// At the beginning of the first line.
Expand Down Expand Up @@ -497,8 +491,6 @@ void main() async {
paragraph.getBoxesForRange(2, 5),
<TextBox>[
TextBox.fromLTRBD(20.0, 0.0, 40.0, 10.0, TextDirection.ltr),
if (isDiscrepancyExpected)
TextBox.fromLTRBD(40.0, 0.0, 40.0, 10.0, TextDirection.ltr),
],
);

Expand Down Expand Up @@ -533,8 +525,6 @@ void main() async {
paragraph.getBoxesForRange(10, 13),
<TextBox>[
TextBox.fromLTRBD(50.0, 10.0, 70.0, 20.0, TextDirection.ltr),
if (isDiscrepancyExpected)
TextBox.fromLTRBD(70.0, 10.0, 70.0, 20.0, TextDirection.ltr),
],
);

Expand Down Expand Up @@ -573,8 +563,6 @@ void main() async {
paragraph.getBoxesForRange(2, 8),
<TextBox>[
TextBox.fromLTRBD(20.0, 0.0, 40.0, 10.0, TextDirection.ltr),
if (isDiscrepancyExpected)
TextBox.fromLTRBD(40.0, 0.0, 40.0, 10.0, TextDirection.ltr),
TextBox.fromLTRBD(0.0, 10.0, 30.0, 20.0, TextDirection.ltr),
],
);
Expand All @@ -593,11 +581,7 @@ void main() async {
paragraph.getBoxesForRange(3, 14),
<TextBox>[
TextBox.fromLTRBD(30.0, 0.0, 40.0, 10.0, TextDirection.ltr),
if (isDiscrepancyExpected)
TextBox.fromLTRBD(40.0, 0.0, 40.0, 10.0, TextDirection.ltr),
TextBox.fromLTRBD(0.0, 10.0, 70.0, 20.0, TextDirection.ltr),
if (isDiscrepancyExpected)
TextBox.fromLTRBD(70.0, 10.0, 70.0, 20.0, TextDirection.ltr),
TextBox.fromLTRBD(0.0, 20.0, 10.0, 30.0, TextDirection.ltr),
],
);
Expand All @@ -607,11 +591,7 @@ void main() async {
paragraph.getBoxesForRange(0, 13),
<TextBox>[
TextBox.fromLTRBD(0.0, 0.0, 40.0, 10.0, TextDirection.ltr),
if (isDiscrepancyExpected)
TextBox.fromLTRBD(40.0, 0.0, 40.0, 10.0, TextDirection.ltr),
TextBox.fromLTRBD(0.0, 10.0, 70.0, 20.0, TextDirection.ltr),
if (isDiscrepancyExpected)
TextBox.fromLTRBD(70.0, 10.0, 70.0, 20.0, TextDirection.ltr),
],
);

Expand All @@ -620,16 +600,186 @@ void main() async {
paragraph.getBoxesForRange(0, 15),
<TextBox>[
TextBox.fromLTRBD(0.0, 0.0, 40.0, 10.0, TextDirection.ltr),
if (isDiscrepancyExpected)
TextBox.fromLTRBD(40.0, 0.0, 40.0, 10.0, TextDirection.ltr),
TextBox.fromLTRBD(0.0, 10.0, 70.0, 20.0, TextDirection.ltr),
if (isDiscrepancyExpected)
TextBox.fromLTRBD(70.0, 10.0, 70.0, 20.0, TextDirection.ltr),
TextBox.fromLTRBD(0.0, 20.0, 20.0, 30.0, TextDirection.ltr),
],
);
});

testEachMeasurement('getBoxesForRange with maxLines', () {
final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(
fontFamily: 'Ahem',
fontStyle: FontStyle.normal,
fontWeight: FontWeight.normal,
fontSize: 10,
textDirection: TextDirection.ltr,
maxLines: 2,
));
builder.addText('abcd\n');
builder.addText('abcdefg\n');
builder.addText('ab');
final Paragraph paragraph = builder.build();
paragraph.layout(const ParagraphConstraints(width: 100));

// First line: "abcd\n"

// At the beginning of the first line.
expect(
paragraph.getBoxesForRange(0, 0),
<TextBox>[],
);
// At the end of the first line.
expect(
paragraph.getBoxesForRange(4, 4),
<TextBox>[],
);
// Between "b" and "c" in the first line.
expect(
paragraph.getBoxesForRange(2, 2),
<TextBox>[],
);
// The range "ab" in the first line.
expect(
paragraph.getBoxesForRange(0, 2),
<TextBox>[
TextBox.fromLTRBD(0.0, 0.0, 20.0, 10.0, TextDirection.ltr),
],
);
// The range "bc" in the first line.
expect(
paragraph.getBoxesForRange(1, 3),
<TextBox>[
TextBox.fromLTRBD(10.0, 0.0, 30.0, 10.0, TextDirection.ltr),
],
);
// The range "d" in the first line.
expect(
paragraph.getBoxesForRange(3, 4),
<TextBox>[
TextBox.fromLTRBD(30.0, 0.0, 40.0, 10.0, TextDirection.ltr),
],
);
// The range "\n" in the first line.
expect(
paragraph.getBoxesForRange(4, 5),
<TextBox>[
TextBox.fromLTRBD(40.0, 0.0, 40.0, 10.0, TextDirection.ltr),
],
);
// The range "cd\n" in the first line.
expect(
paragraph.getBoxesForRange(2, 5),
<TextBox>[
TextBox.fromLTRBD(20.0, 0.0, 40.0, 10.0, TextDirection.ltr),
],
);

// Second line: "abcdefg\n"

// At the beginning of the second line.
expect(
paragraph.getBoxesForRange(5, 5),
<TextBox>[],
);
// At the end of the second line.
expect(
paragraph.getBoxesForRange(12, 12),
<TextBox>[],
);
// The range "efg" in the second line.
expect(
paragraph.getBoxesForRange(9, 12),
<TextBox>[
TextBox.fromLTRBD(40.0, 10.0, 70.0, 20.0, TextDirection.ltr),
],
);
// The range "bcde" in the second line.
expect(
paragraph.getBoxesForRange(6, 10),
<TextBox>[
TextBox.fromLTRBD(10.0, 10.0, 50.0, 20.0, TextDirection.ltr),
],
);
// The range "fg\n" in the second line.
expect(
paragraph.getBoxesForRange(10, 13),
<TextBox>[
TextBox.fromLTRBD(50.0, 10.0, 70.0, 20.0, TextDirection.ltr),
],
);

// Last (third) line: "ab"

// At the beginning of the last line.
expect(
paragraph.getBoxesForRange(13, 13),
<TextBox>[],
);
// At the end of the last line.
expect(
paragraph.getBoxesForRange(15, 15),
<TextBox>[],
);
// The range "a" in the last line.
expect(
paragraph.getBoxesForRange(14, 15),
<TextBox>[],
);
// The range "ab" in the last line.
expect(
paragraph.getBoxesForRange(13, 15),
<TextBox>[],
);


// Combine multiple lines

// The range "cd\nabc".
expect(
paragraph.getBoxesForRange(2, 8),
<TextBox>[
TextBox.fromLTRBD(20.0, 0.0, 40.0, 10.0, TextDirection.ltr),
TextBox.fromLTRBD(0.0, 10.0, 30.0, 20.0, TextDirection.ltr),
],
);

// The range "\nabcd".
expect(
paragraph.getBoxesForRange(4, 9),
<TextBox>[
TextBox.fromLTRBD(40.0, 0.0, 40.0, 10.0, TextDirection.ltr),
TextBox.fromLTRBD(0.0, 10.0, 40.0, 20.0, TextDirection.ltr),
],
);

// The range "d\nabcdefg\na".
expect(
paragraph.getBoxesForRange(3, 14),
<TextBox>[
TextBox.fromLTRBD(30.0, 0.0, 40.0, 10.0, TextDirection.ltr),
TextBox.fromLTRBD(0.0, 10.0, 70.0, 20.0, TextDirection.ltr),
],
);

// The range "abcd\nabcdefg\n".
expect(
paragraph.getBoxesForRange(0, 13),
<TextBox>[
TextBox.fromLTRBD(0.0, 0.0, 40.0, 10.0, TextDirection.ltr),
TextBox.fromLTRBD(0.0, 10.0, 70.0, 20.0, TextDirection.ltr),
],
);

// The range "abcd\nabcdefg\nab".
expect(
paragraph.getBoxesForRange(0, 15),
<TextBox>[
TextBox.fromLTRBD(0.0, 0.0, 40.0, 10.0, TextDirection.ltr),
TextBox.fromLTRBD(0.0, 10.0, 70.0, 20.0, TextDirection.ltr),
],
);
});

test('longestLine', () {
// [Paragraph.longestLine] is only supported by canvas-based measurement.
TextMeasurementService.enableExperimentalCanvasImplementation = true;
Expand Down

0 comments on commit d670059

Please sign in to comment.