Skip to content

Commit

Permalink
[web] Calculate align offset for each paragraph line (LineMetrics.lef…
Browse files Browse the repository at this point in the history
  • Loading branch information
mdebbar committed Dec 18, 2019
1 parent 105eb66 commit 1ecfdcb
Show file tree
Hide file tree
Showing 5 changed files with 291 additions and 107 deletions.
4 changes: 2 additions & 2 deletions lib/web_ui/lib/src/engine/bitmap_canvas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,7 @@ class BitmapCanvas extends EngineCanvas with SaveStackTracking {
double x,
double y,
) {
x += line.left;
final double letterSpacing = style.letterSpacing;
if (letterSpacing == null || letterSpacing == 0.0) {
ctx.fillText(line.text, x, y);
Expand Down Expand Up @@ -712,11 +713,10 @@ class BitmapCanvas extends EngineCanvas with SaveStackTracking {
}
_applyPaint(paragraph._paint.paintData);

final double x = offset.dx + paragraph._alignOffset;
double y = offset.dy + paragraph.alphabeticBaseline;
final int len = lines.length;
for (int i = 0; i < len; i++) {
_drawTextLine(style, lines[i], x, y);
_drawTextLine(style, lines[i], offset.dx, y);
y += paragraph._lineHeight;
}
_resetPaint();
Expand Down
72 changes: 64 additions & 8 deletions lib/web_ui/lib/src/engine/text/measurement.dart
Original file line number Diff line number Diff line change
Expand Up @@ -416,13 +416,19 @@ class DomTextMeasurementService extends TextMeasurementService {
List<EngineLineMetrics> lines;
if (text != null) {
final double lineWidth = maxIntrinsicWidth;
final double alignOffset = _calculateAlignOffsetForLine(
paragraph: paragraph,
lineWidth: lineWidth,
maxWidth: width,
);
lines = <EngineLineMetrics>[
EngineLineMetrics.withText(
text,
startIndex: 0,
endIndex: text.length,
hardBreak: true,
width: lineWidth,
left: alignOffset,
lineNumber: 0,
),
];
Expand All @@ -440,6 +446,8 @@ class DomTextMeasurementService extends TextMeasurementService {
alphabeticBaseline: alphabeticBaseline,
ideographicBaseline: ideographicBaseline,
lines: lines,
textAlign: paragraph._textAlign,
textDirection: paragraph._textDirection,
);
}

Expand Down Expand Up @@ -488,6 +496,8 @@ class DomTextMeasurementService extends TextMeasurementService {
alphabeticBaseline: alphabeticBaseline,
ideographicBaseline: ideographicBaseline,
lines: null,
textAlign: paragraph._textAlign,
textDirection: paragraph._textDirection,
);
}

Expand Down Expand Up @@ -546,7 +556,7 @@ class CanvasTextMeasurementService extends TextMeasurementService {
// TODO(mdebbar): Check if the whole text can fit in a single-line. Then avoid all this ceremony.
_canvasContext.font = style.cssFontString;
final LinesCalculator linesCalculator =
LinesCalculator(_canvasContext, text, style, constraints.width);
LinesCalculator(_canvasContext, paragraph, constraints.width);
final MinIntrinsicCalculator minIntrinsicCalculator =
MinIntrinsicCalculator(_canvasContext, text, style);
final MaxIntrinsicCalculator maxIntrinsicCalculator =
Expand Down Expand Up @@ -597,6 +607,8 @@ class CanvasTextMeasurementService extends TextMeasurementService {
maxIntrinsicWidth: maxIntrinsicCalculator.value,
width: constraints.width,
lines: linesCalculator.lines,
textAlign: paragraph._textAlign,
textDirection: paragraph._textDirection,
);
return result;
}
Expand Down Expand Up @@ -702,16 +714,18 @@ int _excludeTrailing(String text, int start, int end, CharPredicate predicate) {
/// During the text layout phase, this class splits the lines of text so that it
/// ends up fitting into the given width constraint.
///
/// It mimicks the Flutter engine's behavior when it comes to handling ellipsis
/// and max lines.
/// It implements the Flutter engine's behavior when it comes to handling
/// ellipsis and max lines.
class LinesCalculator {
LinesCalculator(this._canvasContext, this._text, this._style, this._maxWidth);
LinesCalculator(this._canvasContext, this._paragraph, this._maxWidth);

final html.CanvasRenderingContext2D _canvasContext;
final String _text;
final ParagraphGeometricStyle _style;
final EngineParagraph _paragraph;
final double _maxWidth;

String get _text => _paragraph._plainText;
ParagraphGeometricStyle get _style => _paragraph._geometricStyle;

/// The lines that have been consumed so far.
List<EngineLineMetrics> lines = <EngineLineMetrics>[];

Expand Down Expand Up @@ -768,12 +782,20 @@ class LinesCalculator {
start: _lineStart,
end: chunkEndWithoutSpace,
);
final double widthOfResultingLine =
measureSubstring(_lineStart, breakingPoint) + _ellipsisWidth;
final double alignOffset = _calculateAlignOffsetForLine(
paragraph: _paragraph,
lineWidth: widthOfResultingLine,
maxWidth: _maxWidth,
);
lines.add(EngineLineMetrics.withText(
_text.substring(_lineStart, breakingPoint) + _style.ellipsis,
startIndex: _lineStart,
endIndex: chunkEnd,
hardBreak: false,
width: measureSubstring(_lineStart, breakingPoint) + _ellipsisWidth,
width: widthOfResultingLine,
left: alignOffset,
lineNumber: lines.length,
));
} else if (isChunkTooLong) {
Expand Down Expand Up @@ -826,12 +848,19 @@ class LinesCalculator {
_whitespacePredicate,
);
final int lineNumber = lines.length;
final double lineWidth = measureSubstring(_lineStart, endWithoutSpace);
final double alignOffset = _calculateAlignOffsetForLine(
paragraph: _paragraph,
lineWidth: lineWidth,
maxWidth: _maxWidth,
);
final EngineLineMetrics metrics = EngineLineMetrics.withText(
_text.substring(_lineStart, endWithoutNewlines),
startIndex: _lineStart,
endIndex: lineEnd,
hardBreak: isHardBreak,
width: measureSubstring(_lineStart, endWithoutSpace),
width: lineWidth,
left: alignOffset,
lineNumber: lineNumber,
);
lines.add(metrics);
Expand Down Expand Up @@ -958,3 +987,30 @@ class MaxIntrinsicCalculator {
_lastHardLineEnd = hardLineEnd;
}
}

/// Calculates the offset necessary for the given line to be correctly aligned.
double _calculateAlignOffsetForLine({
@required EngineParagraph paragraph,
@required double lineWidth,
@required double maxWidth,
}) {
final double emptySpace = maxWidth - lineWidth;
// WARNING: the [paragraph] may not be laid out yet at this point. This
// function must not use layout metrics, such as [paragraph.height].
switch (paragraph._textAlign) {
case ui.TextAlign.center:
return emptySpace / 2.0;
case ui.TextAlign.right:
return emptySpace;
case ui.TextAlign.start:
return paragraph._textDirection == ui.TextDirection.rtl
? emptySpace
: 0.0;
case ui.TextAlign.end:
return paragraph._textDirection == ui.TextDirection.rtl
? 0.0
: emptySpace;
default:
return 0.0;
}
}
3 changes: 2 additions & 1 deletion lib/web_ui/lib/src/engine/text/paragraph.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ class EngineLineMetrics implements ui.LineMetrics {
this.unscaledAscent,
this.height,
@required this.width,
this.left,
@required this.left,
this.baseline,
@required this.lineNumber,
}) : assert(text != null),
assert(hardBreak != null),
assert(width != null),
assert(left != null),
assert(lineNumber != null && lineNumber >= 0);

/// The textual content representing this line.
Expand Down
18 changes: 15 additions & 3 deletions lib/web_ui/lib/src/engine/text/ruler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -800,7 +800,9 @@ class ParagraphRuler {
final int len = constraintCache.length;
for (int i = 0; i < len; i++) {
final MeasurementResult item = constraintCache[i];
if (item.constraintWidth == constraints.width) {
if (item.constraintWidth == constraints.width &&
item.textAlign == paragraph._textAlign &&
item.textDirection == paragraph._textDirection) {
return item;
}
}
Expand Down Expand Up @@ -852,7 +854,13 @@ class MeasurementResult {
/// of each laid out line.
final List<EngineLineMetrics> lines;

const MeasurementResult(
/// The text align value of the paragraph.
final ui.TextAlign textAlign;

/// The text direction of the paragraph.
final ui.TextDirection textDirection;

MeasurementResult(
this.constraintWidth, {
@required this.isSingleLine,
@required this.width,
Expand All @@ -864,6 +872,8 @@ class MeasurementResult {
@required this.alphabeticBaseline,
@required this.ideographicBaseline,
@required this.lines,
@required this.textAlign,
@required this.textDirection,
}) : assert(constraintWidth != null),
assert(isSingleLine != null),
assert(width != null),
Expand All @@ -872,5 +882,7 @@ class MeasurementResult {
assert(minIntrinsicWidth != null),
assert(maxIntrinsicWidth != null),
assert(alphabeticBaseline != null),
assert(ideographicBaseline != null);
assert(ideographicBaseline != null),
assert(textAlign != null),
assert(textDirection != null);
}
Loading

0 comments on commit 1ecfdcb

Please sign in to comment.