fix getFullHeightForCaret when strut is disabled. (#154039)

Fixes https://github.com/flutter/flutter/issues/150638
This commit is contained in:
LongCatIsLooong 2024-08-26 16:34:17 -07:00 committed by GitHub
parent 418fa9d626
commit b4c9a226cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 46 additions and 5 deletions

View File

@ -371,6 +371,7 @@ class _TextLayout {
final double baseline = lineMetrics.baseline;
final double dx;
final double height;
late final ui.GlyphInfo? lastGlyph = _paragraph.getGlyphInfoAt(rawString.length - 1);
// TODO(LongCatIsLooong): handle the case where maxLine is set to non-null
// and the last line ends with trailing whitespaces.
@ -381,13 +382,15 @@ class _TextLayout {
TextDirection.ltr => glyphBounds.right,
TextDirection.rtl => glyphBounds.left,
};
height = glyphBounds.height;
} else {
dx = switch (writingDirection) {
TextDirection.ltr => lineMetrics.left + lineMetrics.width,
TextDirection.rtl => lineMetrics.left,
};
height = lineMetrics.height;
}
return _LineCaretMetrics(offset: Offset(dx, baseline), writingDirection: writingDirection);
return _LineCaretMetrics(offset: Offset(dx, baseline), writingDirection: writingDirection, height: height);
}
double _contentWidthFor(double minWidth, double maxWidth, TextWidthBasis widthBasis) {
@ -501,8 +504,8 @@ class _TextPainterLayoutCacheWithOffset {
/// The _CaretMetrics for carets located in a non-empty paragraph. Such carets
/// are anchored to the trailing edge or the leading edge of a glyph, or a
/// ligature component.
final class _LineCaretMetrics {
const _LineCaretMetrics({required this.offset, required this.writingDirection});
class _LineCaretMetrics {
const _LineCaretMetrics({required this.offset, required this.writingDirection, required this.height});
/// The offset from the top left corner of the paragraph to the caret's top
/// start location.
final Offset offset;
@ -512,10 +515,13 @@ final class _LineCaretMetrics {
/// right of [offset].
final TextDirection writingDirection;
/// The recommended height of the caret.
final double height;
_LineCaretMetrics shift(Offset offset) {
return offset == Offset.zero
? this
: _LineCaretMetrics(offset: offset + this.offset, writingDirection: writingDirection);
: _LineCaretMetrics(offset: offset + this.offset, writingDirection: writingDirection, height: height);
}
}
@ -1367,6 +1373,16 @@ class TextPainter {
///
/// Valid only after [layout] has been called.
double getFullHeightForCaret(TextPosition position, Rect caretPrototype) {
// The if condition is derived from
// https://github.com/google/skia/blob/0086a17e0d4cc676cf88cae671ba5ee967eb7241/modules/skparagraph/src/TextLine.cpp#L1244-L1246
// which is set here:
// https://github.com/flutter/engine/blob/a821b8790c9fd0e095013cd5bd1f20273bc1ee47/third_party/txt/src/skia/paragraph_builder_skia.cc#L134
if (strutStyle == null || strutStyle == StrutStyle.disabled || strutStyle?.fontSize == 0.0) {
final double? heightFromCaretMetrics = _computeCaretMetrics(position)?.height;
if (heightFromCaretMetrics != null) {
return heightFromCaretMetrics;
}
}
final TextBox textBox = _getOrCreateLayoutTemplate().getBoxesForRange(0, 1, boxHeightStyle: ui.BoxHeightStyle.strut).single;
return textBox.toRect().height;
}
@ -1450,7 +1466,8 @@ class TextPainter {
if (glyphInfo == null) {
// If the glyph isn't laid out, then the position points to a character
// that is not laid out. Use the EOT caret.
// that is not laid out (the part of text is invisible due to maxLines or
// infinite paragraph x offset). Use the EOT caret.
// TODO(LongCatIsLooong): assert when an invalid position is given.
final ui.Paragraph template = _getOrCreateLayoutTemplate();
assert(template.numberOfLines == 1);
@ -1487,6 +1504,7 @@ class TextPainter {
metrics = _LineCaretMetrics(
offset: Offset(anchorToLeft ? box.left : box.right, box.top),
writingDirection: box.direction,
height: box.bottom - box.top,
);
} else {
// Fall back to glyphInfo. This should only happen when using the HTML renderer.
@ -1499,6 +1517,7 @@ class TextPainter {
metrics = _LineCaretMetrics(
offset: Offset(dx, graphemeBounds.top),
writingDirection: glyphInfo.writingDirection,
height: graphemeBounds.height,
);
}

View File

@ -857,6 +857,28 @@ void main() {
expect(caretOffset.dy, 0.0);
painter.dispose();
});
test('caret height reflects run height if strut is disabled', () {
const TextSpan span = TextSpan(text: 'M', style: TextStyle(fontSize: 128), children: <InlineSpan>[
TextSpan(text: 'M', style: TextStyle(fontSize: 32)),
TextSpan(text: 'M', style: TextStyle(fontSize: 64)),
]);
final TextPainter painter = TextPainter()
..textDirection = TextDirection.ltr
..text = span
..layout();
expect(painter.getFullHeightForCaret(const TextPosition(offset: 0, affinity: ui.TextAffinity.upstream), Rect.zero), 128.0);
expect(painter.getFullHeightForCaret(const TextPosition(offset: 0), Rect.zero), 128.0);
expect(painter.getFullHeightForCaret(const TextPosition(offset: 1, affinity: ui.TextAffinity.upstream), Rect.zero), 128.0);
expect(painter.getFullHeightForCaret(const TextPosition(offset: 1), Rect.zero), 32.0);
expect(painter.getFullHeightForCaret(const TextPosition(offset: 2, affinity: ui.TextAffinity.upstream), Rect.zero), 32.0);
expect(painter.getFullHeightForCaret(const TextPosition(offset: 2), Rect.zero), 64.0);
expect(painter.getFullHeightForCaret(const TextPosition(offset: 3, affinity: ui.TextAffinity.upstream), Rect.zero), 64.0);
expect(painter.getFullHeightForCaret(const TextPosition(offset: 3), Rect.zero), 128.0);
painter.dispose();
});
});
test('TextPainter error test', () {