Unverified Commit b865b0eb authored by Jason Simmons's avatar Jason Simmons Committed by GitHub

Use TextPainter.preferredLineHeight to estimate line height for Cupertino...

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

Fixes https://github.com/flutter/flutter/issues/12046
parent 89405002
...@@ -399,7 +399,7 @@ class RenderEditable extends RenderBox { ...@@ -399,7 +399,7 @@ class RenderEditable extends RenderBox {
if (selection.isCollapsed) { if (selection.isCollapsed) {
// TODO(mpcomplete): This doesn't work well at an RTL/LTR boundary. // TODO(mpcomplete): This doesn't work well at an RTL/LTR boundary.
final Offset caretOffset = _textPainter.getOffsetForCaret(selection.extent, _caretPrototype); 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)]; return <TextSelectionPoint>[new TextSelectionPoint(start, null)];
} else { } else {
final List<ui.TextBox> boxes = _textPainter.getBoxesForSelection(selection); final List<ui.TextBox> boxes = _textPainter.getBoxesForSelection(selection);
...@@ -441,7 +441,7 @@ class RenderEditable extends RenderBox { ...@@ -441,7 +441,7 @@ class RenderEditable extends RenderBox {
_layoutText(constraints.maxWidth); _layoutText(constraints.maxWidth);
final Offset caretOffset = _textPainter.getOffsetForCaret(caretPosition, _caretPrototype); final Offset caretOffset = _textPainter.getOffsetForCaret(caretPosition, _caretPrototype);
// This rect is the same as _caretPrototype but without the vertical padding. // 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 @override
...@@ -456,12 +456,13 @@ class RenderEditable extends RenderBox { ...@@ -456,12 +456,13 @@ class RenderEditable extends RenderBox {
return _textPainter.maxIntrinsicWidth; return _textPainter.maxIntrinsicWidth;
} }
// This does not required the layout to be updated. /// An estimate of the height of a line in the text. See [TextPainter.preferredLineHeight].
double get _preferredLineHeight => _textPainter.preferredLineHeight; /// This does not required the layout to be updated.
double get preferredLineHeight => _textPainter.preferredLineHeight;
double _preferredHeight(double width) { double _preferredHeight(double width) {
if (maxLines != null) if (maxLines != null)
return _preferredLineHeight * maxLines; return preferredLineHeight * maxLines;
if (width == double.INFINITY) { if (width == double.INFINITY) {
final String text = _textPainter.text.toPlainText(); final String text = _textPainter.text.toPlainText();
int lines = 1; int lines = 1;
...@@ -469,10 +470,10 @@ class RenderEditable extends RenderBox { ...@@ -469,10 +470,10 @@ class RenderEditable extends RenderBox {
if (text.codeUnitAt(index) == 0x0A) // count explicit line breaks if (text.codeUnitAt(index) == 0x0A) // count explicit line breaks
lines += 1; lines += 1;
} }
return _preferredLineHeight * lines; return preferredLineHeight * lines;
} }
_layoutText(width); _layoutText(width);
return math.max(_preferredLineHeight, _textPainter.height); return math.max(preferredLineHeight, _textPainter.height);
} }
@override @override
...@@ -558,7 +559,7 @@ class RenderEditable extends RenderBox { ...@@ -558,7 +559,7 @@ class RenderEditable extends RenderBox {
@override @override
void performLayout() { void performLayout() {
_layoutText(constraints.maxWidth); _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; _selectionRects = null;
// We grab _textPainter.size here because assigning to `size` on the next // We grab _textPainter.size here because assigning to `size` on the next
// line will trigger us to validate our intrinsic sizes, which will change // line will trigger us to validate our intrinsic sizes, which will change
......
...@@ -344,7 +344,7 @@ class TextSelectionOverlay implements TextSelectionDelegate { ...@@ -344,7 +344,7 @@ class TextSelectionOverlay implements TextSelectionDelegate {
(endpoints.length == 1) ? (endpoints.length == 1) ?
endpoints[0].point.dx : endpoints[0].point.dx :
(endpoints[0].point.dx + endpoints[1].point.dx) / 2.0, (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( final Rect editingRegion = new Rect.fromPoints(
...@@ -509,7 +509,7 @@ class _TextSelectionHandleOverlayState extends State<_TextSelectionHandleOverlay ...@@ -509,7 +509,7 @@ class _TextSelectionHandleOverlayState extends State<_TextSelectionHandleOverlay
child: widget.selectionControls.buildHandle( child: widget.selectionControls.buildHandle(
context, context,
type, type,
widget.renderObject.size.height / widget.renderObject.maxLines, widget.renderObject.preferredLineHeight,
), ),
), ),
], ],
......
...@@ -1664,4 +1664,30 @@ void main() { ...@@ -1664,4 +1664,30 @@ void main() {
expect(semantics, includesNodeWith(flags: <SemanticsFlags>[SemanticsFlags.isTextField])); 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);
});
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment