未验证 提交 3a035ee0 编写于 作者: M Mouad Debbar 提交者: GitHub

[web] Add complex rich text test cases and fix them (#22948)

上级 8619a81e
......@@ -188,6 +188,7 @@ class TextLayoutService {
if (span is PlaceholderSpan) {
// TODO(mdebbar): Do placeholders affect min/max intrinsic width?
} else if (span is FlatTextSpan) {
spanometer.currentSpan = span;
final LineBreakResult nextBreak = currentLine.findNextBreak(span.end);
// For the purpose of max intrinsic width, we don't care if the line
......@@ -251,6 +252,9 @@ class LineSegment {
/// The width of the trailing white space in the segment.
double get widthOfTrailingSpace => widthIncludingSpace - width;
/// Whether this segment is made of only white space.
bool get isSpaceOnly => start.index == end.indexWithoutTrailingSpaces;
}
/// Builds instances of [EngineLineMetrics] for the given [paragraph].
......@@ -358,8 +362,12 @@ class LineBuilder {
void _addSegment(LineSegment segment) {
_segments.add(segment);
// Add the width of previous trailing space.
width += widthOfTrailingSpace + segment.width;
// Adding a space-only segment has no effect on `width` because it doesn't
// include trailing white space.
if (!segment.isSpaceOnly) {
// Add the width of previous trailing space.
width += widthOfTrailingSpace + segment.width;
}
widthIncludingSpace += segment.widthIncludingSpace;
end = segment.end;
}
......@@ -370,17 +378,39 @@ class LineBuilder {
LineSegment _popSegment() {
final LineSegment poppedSegment = _segments.removeLast();
double widthOfPrevTrailingSpace;
if (_segments.isEmpty) {
widthOfPrevTrailingSpace = 0.0;
width = 0.0;
widthIncludingSpace = 0.0;
end = start;
} else {
widthOfPrevTrailingSpace = lastSegment.widthOfTrailingSpace;
widthIncludingSpace -= poppedSegment.widthIncludingSpace;
end = lastSegment.end;
}
width = width - poppedSegment.width - widthOfPrevTrailingSpace;
widthIncludingSpace -= poppedSegment.widthIncludingSpace;
// Now, let's figure out what to do with `width`.
// Popping a space-only segment has no effect on `width`.
if (!poppedSegment.isSpaceOnly) {
// First, we subtract the width of the popped segment.
width -= poppedSegment.width;
// Second, we subtract all trailing spaces from `width`. There could be
// multiple trailing segments that are space-only.
double widthOfTrailingSpace = 0.0;
int i = _segments.length - 1;
while (i >= 0 && _segments[i].isSpaceOnly) {
// Since the segment is space-only, `widthIncludingSpace` contains
// the width of the space and nothing else.
widthOfTrailingSpace += _segments[i].widthIncludingSpace;
i--;
}
if (i >= 0) {
// Having `i >= 0` means in the above loop we stopped at a
// non-space-only segment. We should also subtract its trailing spaces.
widthOfTrailingSpace += _segments[i].widthOfTrailingSpace;
}
width -= widthOfTrailingSpace;
}
}
return poppedSegment;
}
......
// 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.12
import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';
import 'package:ui/src/engine.dart';
import 'package:ui/ui.dart' as ui;
import 'layout_service_helper.dart';
const ui.Color white = ui.Color(0xFFFFFFFF);
const ui.Color black = ui.Color(0xFF000000);
const ui.Color red = ui.Color(0xFFFF0000);
const ui.Color green = ui.Color(0xFF00FF00);
const ui.Color blue = ui.Color(0xFF0000FF);
final EngineParagraphStyle ahemStyle = EngineParagraphStyle(
fontFamily: 'ahem',
fontSize: 10,
);
ui.ParagraphConstraints constrain(double width) {
return ui.ParagraphConstraints(width: width);
}
CanvasParagraph rich(
EngineParagraphStyle style,
void Function(CanvasParagraphBuilder) callback,
) {
final CanvasParagraphBuilder builder = CanvasParagraphBuilder(style);
callback(builder);
return builder.build();
}
void main() {
internalBootstrapBrowserTest(() => testMain);
}
void testMain() async {
await ui.webOnlyInitializeTestDomRenderer();
test('measures spans in the same line correctly', () {
final CanvasParagraph paragraph = rich(ahemStyle, (builder) {
builder.pushStyle(EngineTextStyle.only(fontSize: 12.0));
// 12.0 * 6 = 72.0 (with spaces)
// 12.0 * 5 = 60.0 (without spaces)
builder.addText('Lorem ');
builder.pushStyle(EngineTextStyle.only(fontSize: 13.0));
// 13.0 * 6 = 78.0 (with spaces)
// 13.0 * 5 = 65.0 (without spaces)
builder.addText('ipsum ');
builder.pushStyle(EngineTextStyle.only(fontSize: 11.0));
// 11.0 * 5 = 55.0
builder.addText('dolor');
})..layout(constrain(double.infinity));
expect(paragraph.maxIntrinsicWidth, 205.0);
expect(paragraph.minIntrinsicWidth, 65.0); // "ipsum"
expect(paragraph.width, double.infinity);
expectLines(paragraph, [
l('Lorem ipsum dolor', 0, 17, hardBreak: true, width: 205.0, left: 0.0),
]);
});
test('breaks lines correctly at the end of spans', () {
final CanvasParagraph paragraph = rich(ahemStyle, (builder) {
builder.addText('Lorem ');
builder.pushStyle(EngineTextStyle.only(fontSize: 15.0));
builder.addText('sit ');
builder.pop();
builder.addText('.');
})..layout(constrain(60.0));
expect(paragraph.maxIntrinsicWidth, 130.0);
expect(paragraph.minIntrinsicWidth, 50.0); // "Lorem"
expect(paragraph.width, 60.0);
expectLines(paragraph, [
l('Lorem ', 0, 6, hardBreak: false, width: 50.0, left: 0.0),
l('sit ', 6, 10, hardBreak: false, width: 45.0, left: 0.0),
l('.', 10, 11, hardBreak: true, width: 10.0, left: 0.0),
]);
});
test('breaks lines correctly in the middle of spans', () {
final CanvasParagraph paragraph = rich(ahemStyle, (builder) {
builder.addText('Lorem ipsum ');
builder.pushStyle(EngineTextStyle.only(fontSize: 11.0));
builder.addText('sit dolor');
})..layout(constrain(100.0));
expect(paragraph.maxIntrinsicWidth, 219.0);
expect(paragraph.minIntrinsicWidth, 55.0); // "dolor"
expect(paragraph.width, 100.0);
expectLines(paragraph, [
l('Lorem ', 0, 6, hardBreak: false, width: 50.0, left: 0.0),
l('ipsum sit ', 6, 16, hardBreak: false, width: 93.0, left: 0.0),
l('dolor', 16, 21, hardBreak: true, width: 55.0, left: 0.0),
]);
});
test('handles space-only spans', () {
final CanvasParagraph paragraph = rich(ahemStyle, (builder) {
builder.pushStyle(EngineTextStyle.only(color: red));
builder.addText('Lorem ');
builder.pop();
builder.pushStyle(EngineTextStyle.only(color: blue));
builder.addText(' ');
builder.pushStyle(EngineTextStyle.only(color: green));
builder.addText(' ');
builder.pushStyle(EngineTextStyle.only(color: black));
builder.addText('ipsum');
});
paragraph.layout(constrain(80.0));
expect(paragraph.maxIntrinsicWidth, 160.0);
expect(paragraph.minIntrinsicWidth, 50.0); // "Lorem" or "ipsum"
expect(paragraph.width, 80.0);
expectLines(paragraph, [
l('Lorem ', 0, 11, hardBreak: false, width: 50.0, widthWithTrailingSpaces: 110.0, left: 0.0),
l('ipsum', 11, 16, hardBreak: true, width: 50.0, left: 0.0),
]);
});
test('should not break at span end if it is not a line break', () {
final CanvasParagraph paragraph = rich(ahemStyle, (builder) {
builder.pushStyle(EngineTextStyle.only(color: red));
builder.addText('Lorem');
builder.pop();
builder.pushStyle(EngineTextStyle.only(color: blue));
builder.addText(' ');
builder.pushStyle(EngineTextStyle.only(color: black));
builder.addText('ip');
builder.pushStyle(EngineTextStyle.only(color: green));
builder.addText('su');
builder.pushStyle(EngineTextStyle.only(color: white));
builder.addText('m');
})..layout(constrain(50.0));
expect(paragraph.maxIntrinsicWidth, 110.0);
expect(paragraph.minIntrinsicWidth, 50.0); // "Lorem" or "ipsum"
expect(paragraph.width, 50.0);
expectLines(paragraph, [
l('Lorem ', 0, 6, hardBreak: false, width: 50.0, left: 0.0),
l('ipsum', 6, 11, hardBreak: true, width: 50.0, left: 0.0),
]);
});
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册