diff --git a/lib/web_ui/lib/src/engine/text/line_breaker.dart b/lib/web_ui/lib/src/engine/text/line_breaker.dart index d8f7804f2a9c9..183fbf205f060 100644 --- a/lib/web_ui/lib/src/engine/text/line_breaker.dart +++ b/lib/web_ui/lib/src/engine/text/line_breaker.dart @@ -10,6 +10,9 @@ enum LineBreakType { /// Indicates that a line break is possible but not mandatory. opportunity, + /// Indicates that a line break isn't possible. + prohibited, + /// Indicates that this is a hard line break that can't be skipped. mandatory, @@ -74,6 +77,9 @@ class LineBreakResult { /// to decide whether to take the line break or not. final LineBreakType type; + bool get isHard => + type == LineBreakType.mandatory || type == LineBreakType.endOfText; + @override int get hashCode => ui.hashValues( index, @@ -160,7 +166,7 @@ bool _hasEastAsianWidthFWH(int charCode) { /// /// * https://www.unicode.org/reports/tr14/tr14-45.html#Algorithm /// * https://www.unicode.org/Public/11.0.0/ucd/LineBreak.txt -LineBreakResult nextLineBreak(String text, int index) { +LineBreakResult nextLineBreak(String text, int index, {int? maxEnd}) { int? codePoint = getCodePoint(text, index); LineCharProperty curr = lineLookup.findForChar(codePoint); @@ -199,6 +205,15 @@ LineBreakResult nextLineBreak(String text, int index) { // Always break at the end of text. // LB3: ! eot while (index < text.length) { + if (index == maxEnd) { + return LineBreakResult( + index, + lastNonNewlineIndex, + lastNonSpaceIndex, + LineBreakType.prohibited, + ); + } + // Keep count of the RI (regional indicator) sequence. if (curr == LineCharProperty.RI) { regionalIndicatorCount++; diff --git a/lib/web_ui/lib/src/engine/text/measurement.dart b/lib/web_ui/lib/src/engine/text/measurement.dart index 9707069c4a04d..22cb42639318f 100644 --- a/lib/web_ui/lib/src/engine/text/measurement.dart +++ b/lib/web_ui/lib/src/engine/text/measurement.dart @@ -771,8 +771,6 @@ class LinesCalculator { /// This method should be called for every line break. As soon as it reaches /// the maximum number of lines required void update(LineBreakResult brk) { - final bool isHardBreak = brk.type == LineBreakType.mandatory || - brk.type == LineBreakType.endOfText; final int chunkEnd = brk.index; final int chunkEndWithoutNewlines = brk.indexWithoutTrailingNewlines; final int chunkEndWithoutSpace = brk.indexWithoutTrailingSpaces; @@ -855,7 +853,7 @@ class LinesCalculator { return; } - if (isHardBreak) { + if (brk.isHard) { _addLineBreak(brk); } _lastBreak = brk; @@ -872,15 +870,13 @@ class LinesCalculator { lineWidth: lineWidth, maxWidth: _maxWidth, ); - final bool isHardBreak = brk.type == LineBreakType.mandatory || - brk.type == LineBreakType.endOfText; final EngineLineMetrics metrics = EngineLineMetrics.withText( _text!.substring(_lineStart, brk.indexWithoutTrailingNewlines), startIndex: _lineStart, endIndex: brk.index, endIndexWithoutNewlines: brk.indexWithoutTrailingNewlines, - hardBreak: isHardBreak, + hardBreak: brk.isHard, width: lineWidth, widthWithTrailingSpaces: lineWidthWithTrailingSpaces, left: alignOffset, @@ -995,7 +991,7 @@ class MaxIntrinsicCalculator { /// intrinsic width calculated so far. When the whole text is consumed, /// [value] will contain the final maximum intrinsic width. void update(LineBreakResult brk) { - if (brk.type == LineBreakType.opportunity) { + if (!brk.isHard) { return; } diff --git a/lib/web_ui/test/text/line_breaker_test.dart b/lib/web_ui/test/text/line_breaker_test.dart index e58b8e04895aa..6f8a41706a574 100644 --- a/lib/web_ui/test/text/line_breaker_test.dart +++ b/lib/web_ui/test/text/line_breaker_test.dart @@ -252,6 +252,14 @@ void testMain() { '"$text"\n' '\nExpected line break at {$lastLineBreak - $i} but found line break at {$lastLineBreak - ${result.index}}.', ); + + // Since this is a line break, passing a `maxEnd` that's greater + // should return the same line break. + final LineBreakResult maxEndResult = + nextLineBreak(text, lastLineBreak, maxEnd: i + 1); + expect(maxEndResult.index, i); + expect(maxEndResult.type, isNot(LineBreakType.prohibited)); + lastLineBreak = i; } else { // This isn't a line break opportunity so the line break should be @@ -264,6 +272,13 @@ void testMain() { '"$text"\n' '\nUnexpected line break found at {$lastLineBreak - $i}.', ); + + // Since this isn't a line break, passing it as a `maxEnd` should + // return `maxEnd` as a prohibited line break type. + final LineBreakResult maxEndResult = + nextLineBreak(text, lastLineBreak, maxEnd: i); + expect(maxEndResult.index, i); + expect(maxEndResult.type, LineBreakType.prohibited); } } }