Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

[web] Some cleanup for text tests #36425

Merged
merged 2 commits into from
Oct 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/web_ui/lib/src/engine/engine_canvas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ DomElement drawParagraphElement(
}) {
assert(paragraph.isLaidOut);

final DomHTMLElement paragraphElement = paragraph.toDomElement();
final DomElement paragraphElement = paragraph.toDomElement();

if (transform != null) {
setElementTransform(
Expand Down
2 changes: 1 addition & 1 deletion lib/web_ui/lib/src/engine/html/recording_canvas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1246,7 +1246,7 @@ class PaintDrawParagraph extends DrawCommand {
@override
String toString() {
if (assertionsEnabled) {
return 'DrawParagraph(${paragraph.toPlainText()}, $offset)';
return 'DrawParagraph(${paragraph.plainText}, $offset)';
} else {
return super.toString();
}
Expand Down
92 changes: 25 additions & 67 deletions lib/web_ui/lib/src/engine/text/canvas_paragraph.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import 'word_breaker.dart';

const ui.Color _defaultTextColor = ui.Color(0xFFFF0000);

final String _placeholderChar = String.fromCharCode(0xFFFC);
final String placeholderChar = String.fromCharCode(0xFFFC);

/// A paragraph made up of a flat list of text spans and placeholders.
///
Expand All @@ -33,7 +33,6 @@ class CanvasParagraph implements ui.Paragraph {
this.spans, {
required this.paragraphStyle,
required this.plainText,
required this.placeholderCount,
required this.canDrawOnCanvas,
}) : assert(spans.isNotEmpty);

Expand All @@ -46,9 +45,6 @@ class CanvasParagraph implements ui.Paragraph {
/// The full textual content of the paragraph.
late String plainText;

/// The number of placeholders in this paragraph.
final int placeholderCount;

/// Whether this paragraph can be drawn on a bitmap canvas.
///
/// Some text features cannot be rendered into a 2D canvas and must use HTML,
Expand Down Expand Up @@ -87,8 +83,6 @@ class CanvasParagraph implements ui.Paragraph {
/// Whether this paragraph has been laid out or not.
bool isLaidOut = false;

bool get isRtl => paragraphStyle.effectiveTextDirection == ui.TextDirection.rtl;

ui.ParagraphConstraints? _lastUsedConstraints;

late final TextLayoutService _layoutService = TextLayoutService(this);
Expand Down Expand Up @@ -138,27 +132,23 @@ class CanvasParagraph implements ui.Paragraph {
_paintService.paint(canvas, offset);
}

/// Generates a flat string computed from all the spans of the paragraph.
String toPlainText() => plainText;

DomHTMLElement? _cachedDomElement;
DomElement? _cachedDomElement;

/// Returns a DOM element that represents the entire paragraph and its
/// children.
///
/// Generates a new DOM element on every invocation.
DomHTMLElement toDomElement() {
DomElement toDomElement() {
assert(isLaidOut);
final DomHTMLElement? domElement = _cachedDomElement;
final DomElement? domElement = _cachedDomElement;
if (domElement == null) {
return _cachedDomElement ??= _createDomElement();
}
return domElement.cloneNode(true) as DomHTMLElement;
return domElement.cloneNode(true) as DomElement;
}

DomHTMLElement _createDomElement() {
final DomHTMLElement rootElement =
domDocument.createElement('flt-paragraph') as DomHTMLElement;
DomElement _createDomElement() {
final DomElement rootElement = domDocument.createElement('flt-paragraph');

// 1. Set paragraph-level styles.

Expand All @@ -183,12 +173,8 @@ class CanvasParagraph implements ui.Paragraph {
continue;
}

final DomHTMLElement spanElement = domDocument.createElement('flt-span') as DomHTMLElement;
applyTextStyleToElement(
element: spanElement,
style: fragment.style,
isSpan: true,
);
final DomElement spanElement = domDocument.createElement('flt-span');
applyTextStyleToElement(element: spanElement, style: fragment.style);
_positionSpanElement(spanElement, line, fragment);

spanElement.appendText(text);
Expand Down Expand Up @@ -221,7 +207,6 @@ class CanvasParagraph implements ui.Paragraph {

@override
ui.TextRange getWordBoundary(ui.TextPosition position) {
final String text = toPlainText();
final int characterPosition;
switch (position.affinity) {
case ui.TextAffinity.upstream:
Expand All @@ -231,8 +216,8 @@ class CanvasParagraph implements ui.Paragraph {
characterPosition = position.offset;
break;
}
final int start = WordBreaker.prevBreakIndex(text, characterPosition + 1);
final int end = WordBreaker.nextBreakIndex(text, characterPosition);
final int start = WordBreaker.prevBreakIndex(plainText, characterPosition + 1);
final int end = WordBreaker.nextBreakIndex(plainText, characterPosition);
return ui.TextRange(start: start, end: end);
}

Expand Down Expand Up @@ -288,51 +273,30 @@ void _positionSpanElement(DomElement element, ParagraphLine line, LayoutFragment
..lineHeight = '${boxRect.height}px';
}

/// A common interface for all types of spans that make up a paragraph.
///
/// These spans are stored as a flat list in the paragraph object.
abstract class ParagraphSpan {
/// The index of the beginning of the range of text represented by this span.
int get start;

/// The index of the end of the range of text represented by this span.
int get end;

/// The resolved style of the span.
EngineTextStyle get style;
}

/// Represent a span of text in the paragraph.
///
/// It's a "flat" text span as opposed to the framework text spans that are
/// hierarchical.
/// Represents a span in the paragraph.
///
/// Instead of keeping spans and styles in a tree hierarchy like the framework
/// does, we flatten the structure and resolve/merge all the styles from parent
/// nodes.
class FlatTextSpan implements ParagraphSpan {
/// Creates a [FlatTextSpan] with the given [style], representing the span of
///
/// These spans are stored as a flat list in the paragraph object.
class ParagraphSpan {
/// Creates a [ParagraphSpan] with the given [style], representing the span of
/// text in the range between [start] and [end].
FlatTextSpan({
ParagraphSpan({
required this.style,
required this.start,
required this.end,
});

@override
/// The resolved style of the span.
final EngineTextStyle style;

@override
/// The index of the beginning of the range of text represented by this span.
final int start;

@override
/// The index of the end of the range of text represented by this span.
final int end;

String textOf(CanvasParagraph paragraph) {
final String text = paragraph.toPlainText();
assert(end <= text.length);
return text.substring(start, end);
}
}

class PlaceholderSpan extends ParagraphPlaceholder implements ParagraphSpan {
Expand Down Expand Up @@ -622,16 +586,13 @@ class CanvasParagraphBuilder implements ui.ParagraphBuilder {
double? baselineOffset,
ui.TextBaseline? baseline,
}) {
// TODO(mdebbar): for measurement of placeholders, look at:
// - https://github.com/flutter/engine/blob/c0f7e8acf9318d264ad6a235facd097de597ffcc/third_party/txt/src/txt/paragraph_txt.cc#L325-L350

// Require a baseline to be specified if using a baseline-based alignment.
assert(!(alignment == ui.PlaceholderAlignment.aboveBaseline ||
alignment == ui.PlaceholderAlignment.belowBaseline ||
alignment == ui.PlaceholderAlignment.baseline) || baseline != null);

final int start = _plainTextBuffer.length;
_plainTextBuffer.write(_placeholderChar);
_plainTextBuffer.write(placeholderChar);
final int end = _plainTextBuffer.length;

final EngineTextStyle style = _currentStyleNode.resolveStyle();
Expand Down Expand Up @@ -674,7 +635,7 @@ class CanvasParagraphBuilder implements ui.ParagraphBuilder {
final EngineTextStyle style = _currentStyleNode.resolveStyle();
_updateCanDrawOnCanvas(style);

_spans.add(FlatTextSpan(style: style, start: start, end: end));
_spans.add(ParagraphSpan(style: style, start: start, end: end));
}

void _updateCanDrawOnCanvas(EngineTextStyle style) {
Expand Down Expand Up @@ -708,18 +669,15 @@ class CanvasParagraphBuilder implements ui.ParagraphBuilder {
//
// We want the paragraph to always have a non-empty list of spans to match
// the expectations of the [LayoutFragmenter].
_spans.add(FlatTextSpan(
style: _rootStyleNode.resolveStyle(),
start: 0,
end: 0,
));
_spans.add(
ParagraphSpan(style: _rootStyleNode.resolveStyle(), start: 0, end: 0),
);
}

return CanvasParagraph(
_spans,
paragraphStyle: _paragraphStyle,
plainText: _plainTextBuffer.toString(),
placeholderCount: _placeholderCount,
canDrawOnCanvas: _canDrawOnCanvas,
);
}
Expand Down
5 changes: 2 additions & 3 deletions lib/web_ui/lib/src/engine/text/layout_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ class TextLayoutService {
return <ui.TextBox>[];
}

final int length = paragraph.toPlainText().length;
final int length = paragraph.plainText.length;
// Ranges that are out of bounds should return an empty list.
if (start > length || end > length) {
return <ui.TextBox>[];
Expand Down Expand Up @@ -1046,10 +1046,9 @@ class Spanometer {
assert(start >= currentSpan.start && start <= currentSpan.end);
assert(end >= currentSpan.start && end <= currentSpan.end);

final String text = paragraph.toPlainText();
return measureSubstring(
context,
text,
paragraph.plainText,
start,
end,
letterSpacing: letterSpacing,
Expand Down
19 changes: 10 additions & 9 deletions lib/web_ui/lib/src/engine/text/paint_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,16 @@ class TextPaintService {
ui.Offset offset,
LayoutFragment fragment,
) {
final ParagraphSpan span = fragment.span;
if (span is FlatTextSpan) {
// Paint the background of the box, if the span has a background.
final SurfacePaint? background = span.style.background as SurfacePaint?;
if (background != null) {
final ui.Rect rect = fragment.toPaintingTextBox().toRect();
if (!rect.isEmpty) {
canvas.drawRect(rect.shift(offset), background.paintData);
}
if (fragment.isPlaceholder) {
return;
}

// Paint the background of the box, if the span has a background.
final SurfacePaint? background = fragment.style.background as SurfacePaint?;
if (background != null) {
final ui.Rect rect = fragment.toPaintingTextBox().toRect();
if (!rect.isEmpty) {
canvas.drawRect(rect.shift(offset), background.paintData);
}
}
}
Expand Down
12 changes: 4 additions & 8 deletions lib/web_ui/lib/src/engine/text/paragraph.dart
Original file line number Diff line number Diff line change
Expand Up @@ -774,13 +774,9 @@ String fontWeightIndexToCss({int fontWeightIndex = 3}) {

/// Applies a text [style] to an [element], translating the properties to their
/// corresponding CSS equivalents.
///
/// If [isSpan] is true, the text element is a span within richtext and
/// should not assign effectiveFontFamily if fontFamily was not specified.
void applyTextStyleToElement({
required DomHTMLElement element,
required DomElement element,
required EngineTextStyle style,
bool isSpan = false,
}) {
assert(element != null);
assert(style != null);
Expand Down Expand Up @@ -822,10 +818,10 @@ void applyTextStyleToElement({
}
// For test environment use effectiveFontFamily since we need to
// consistently use Ahem font.
if (isSpan && !ui.debugEmulateFlutterTesterEnvironment) {
cssStyle.fontFamily = canonicalizeFontFamily(style.fontFamily)!;
} else {
if (ui.debugEmulateFlutterTesterEnvironment) {
cssStyle.fontFamily = canonicalizeFontFamily(style.effectiveFontFamily)!;
} else {
cssStyle.fontFamily = canonicalizeFontFamily(style.fontFamily)!;
}
if (style.letterSpacing != null) {
cssStyle.letterSpacing = '${style.letterSpacing}px';
Expand Down
4 changes: 0 additions & 4 deletions lib/web_ui/test/html/paragraph/bidi_golden_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';
import 'package:ui/src/engine.dart';
Expand All @@ -12,8 +10,6 @@ import 'package:ui/ui.dart' hide window;
import '../screenshot.dart';
import 'helper.dart';

typedef CanvasTest = FutureOr<void> Function(EngineCanvas canvas);

const String _rtlWord1 = 'واحد';
const String _rtlWord2 = 'اثنان';

Expand Down
2 changes: 0 additions & 2 deletions lib/web_ui/test/html/paragraph/general_golden_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ import 'package:ui/ui.dart' hide window;
import '../screenshot.dart';
import 'helper.dart';

typedef CanvasTest = FutureOr<void> Function(EngineCanvas canvas);

const Rect bounds = Rect.fromLTWH(0, 0, 800, 600);

void main() {
Expand Down
18 changes: 14 additions & 4 deletions lib/web_ui/test/html/paragraph/helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

import 'package:ui/src/engine.dart';
import 'package:ui/ui.dart';
import 'package:web_engine_tester/golden_tester.dart';

typedef CanvasTest = FutureOr<void> Function(EngineCanvas canvas);

const LineBreakType prohibited = LineBreakType.prohibited;
const LineBreakType opportunity = LineBreakType.opportunity;
const LineBreakType mandatory = LineBreakType.mandatory;
Expand All @@ -32,6 +36,11 @@ const Color blue = Color(0xFF0000FF);
const Color yellow = Color(0xFFFFEB3B);
const Color lightPurple = Color(0xFFE1BEE7);

final EngineParagraphStyle ahemStyle = EngineParagraphStyle(
fontFamily: 'ahem',
fontSize: 10,
);

ParagraphConstraints constrain(double width) {
return ParagraphConstraints(width: width);
}
Expand Down Expand Up @@ -73,10 +82,7 @@ Future<void> takeScreenshot(
try {
sceneElement.append(canvas.rootElement);
domDocument.body!.append(sceneElement);
await matchGoldenFile(
'$fileName.png',
region: region,
);
await matchGoldenFile('$fileName.png', region: region);
} finally {
// The page is reused across tests, so remove the element after taking the
// Scuba screenshot.
Expand Down Expand Up @@ -109,3 +115,7 @@ void fillBoxes(EngineCanvas canvas, Offset offset, List<TextBox> boxes, Color co
canvas.drawRect(rect, SurfacePaintData()..color = color);
}
}

String getSpanText(CanvasParagraph paragraph, ParagraphSpan span) {
return paragraph.plainText.substring(span.start, span.end);
}
2 changes: 0 additions & 2 deletions lib/web_ui/test/html/paragraph/overflow_golden_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import 'package:ui/ui.dart' hide window;
import '../screenshot.dart';
import 'helper.dart';

typedef CanvasTest = FutureOr<void> Function(EngineCanvas canvas);

void main() {
internalBootstrapBrowserTest(() => testMain);
}
Expand Down
2 changes: 0 additions & 2 deletions lib/web_ui/test/html/paragraph/placeholders_golden_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import 'package:ui/ui.dart' hide window;
import '../screenshot.dart';
import 'helper.dart';

typedef CanvasTest = FutureOr<void> Function(EngineCanvas canvas);

const Rect bounds = Rect.fromLTWH(0, 0, 800, 600);

void main() {
Expand Down
Loading