Use TextPainter.preferredLineHeight to estimate line height for Cupertino selection handles (#12833)

Fixes https://github.com/flutter/flutter/issues/12046
This commit is contained in:
Jason Simmons 2017-11-03 09:47:46 -07:00 committed by GitHub
parent 89405002bc
commit b865b0eb2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 37 additions and 10 deletions

View File

@ -399,7 +399,7 @@ class RenderEditable extends RenderBox {
if (selection.isCollapsed) {
// TODO(mpcomplete): This doesn't work well at an RTL/LTR boundary.
final Offset caretOffset = _textPainter.getOffsetForCaret(selection.extent, _caretPrototype);
final Offset start = new Offset(0.0, _preferredLineHeight) + caretOffset + paintOffset;
final Offset start = new Offset(0.0, preferredLineHeight) + caretOffset + paintOffset;
return <TextSelectionPoint>[new TextSelectionPoint(start, null)];
} else {
final List<ui.TextBox> boxes = _textPainter.getBoxesForSelection(selection);
@ -441,7 +441,7 @@ class RenderEditable extends RenderBox {
_layoutText(constraints.maxWidth);
final Offset caretOffset = _textPainter.getOffsetForCaret(caretPosition, _caretPrototype);
// This rect is the same as _caretPrototype but without the vertical padding.
return new Rect.fromLTWH(0.0, 0.0, _kCaretWidth, _preferredLineHeight).shift(caretOffset + _paintOffset);
return new Rect.fromLTWH(0.0, 0.0, _kCaretWidth, preferredLineHeight).shift(caretOffset + _paintOffset);
}
@override
@ -456,12 +456,13 @@ class RenderEditable extends RenderBox {
return _textPainter.maxIntrinsicWidth;
}
// This does not required the layout to be updated.
double get _preferredLineHeight => _textPainter.preferredLineHeight;
/// An estimate of the height of a line in the text. See [TextPainter.preferredLineHeight].
/// This does not required the layout to be updated.
double get preferredLineHeight => _textPainter.preferredLineHeight;
double _preferredHeight(double width) {
if (maxLines != null)
return _preferredLineHeight * maxLines;
return preferredLineHeight * maxLines;
if (width == double.INFINITY) {
final String text = _textPainter.text.toPlainText();
int lines = 1;
@ -469,10 +470,10 @@ class RenderEditable extends RenderBox {
if (text.codeUnitAt(index) == 0x0A) // count explicit line breaks
lines += 1;
}
return _preferredLineHeight * lines;
return preferredLineHeight * lines;
}
_layoutText(width);
return math.max(_preferredLineHeight, _textPainter.height);
return math.max(preferredLineHeight, _textPainter.height);
}
@override
@ -558,7 +559,7 @@ class RenderEditable extends RenderBox {
@override
void performLayout() {
_layoutText(constraints.maxWidth);
_caretPrototype = new Rect.fromLTWH(0.0, _kCaretHeightOffset, _kCaretWidth, _preferredLineHeight - 2.0 * _kCaretHeightOffset);
_caretPrototype = new Rect.fromLTWH(0.0, _kCaretHeightOffset, _kCaretWidth, preferredLineHeight - 2.0 * _kCaretHeightOffset);
_selectionRects = null;
// We grab _textPainter.size here because assigning to `size` on the next
// line will trigger us to validate our intrinsic sizes, which will change

View File

@ -344,7 +344,7 @@ class TextSelectionOverlay implements TextSelectionDelegate {
(endpoints.length == 1) ?
endpoints[0].point.dx :
(endpoints[0].point.dx + endpoints[1].point.dx) / 2.0,
endpoints[0].point.dy - renderObject.size.height,
endpoints[0].point.dy - renderObject.preferredLineHeight,
);
final Rect editingRegion = new Rect.fromPoints(
@ -509,7 +509,7 @@ class _TextSelectionHandleOverlayState extends State<_TextSelectionHandleOverlay
child: widget.selectionControls.buildHandle(
context,
type,
widget.renderObject.size.height / widget.renderObject.maxLines,
widget.renderObject.preferredLineHeight,
),
),
],

View File

@ -1664,4 +1664,30 @@ void main() {
expect(semantics, includesNodeWith(flags: <SemanticsFlags>[SemanticsFlags.isTextField]));
});
testWidgets('Caret works when maxLines is null', (WidgetTester tester) async {
final TextEditingController controller = new TextEditingController();
await tester.pumpWidget(
overlay(
child: new TextField(
controller: controller,
maxLines: null,
),
)
);
final String testValue = 'x';
await tester.enterText(find.byType(TextField), testValue);
await skipPastScrollingAnimation(tester);
expect(controller.selection.baseOffset, -1);
// Tap the selection handle to bring up the "paste / select all" menu.
await tester.tapAt(textOffsetToPosition(tester, 0));
await tester.pump();
await tester.pump(const Duration(milliseconds: 200)); // skip past the frame where the opacity is
// Confirm that the selection was updated.
expect(controller.selection.baseOffset, 0);
});
}