Unverified Commit ed91a3be authored by Justin McCandless's avatar Justin McCandless Committed by GitHub

Fix cursor outside of input width (#30525)

* Disallow cursor from appearing beyond the width of the input.

* Test that verifies the cursor can't exceed the width of the input

* Use constant from editable.dart to explain 1 pixel difference in test

* Fix failing test that tested the case of overflowing spaces
parent 5a6c140d
...@@ -486,7 +486,7 @@ class TextPainter { ...@@ -486,7 +486,7 @@ class TextPainter {
final double caretEnd = box.end; final double caretEnd = box.end;
final double dx = box.direction == TextDirection.rtl ? caretEnd - caretPrototype.width : caretEnd; final double dx = box.direction == TextDirection.rtl ? caretEnd - caretPrototype.width : caretEnd;
return Offset(dx, box.top); return Offset(min(dx, width), box.top);
} }
return null; return null;
} }
...@@ -526,7 +526,7 @@ class TextPainter { ...@@ -526,7 +526,7 @@ class TextPainter {
final TextBox box = boxes.last; final TextBox box = boxes.last;
final double caretStart = box.start; final double caretStart = box.start;
final double dx = box.direction == TextDirection.rtl ? caretStart - caretPrototype.width : caretStart; final double dx = box.direction == TextDirection.rtl ? caretStart - caretPrototype.width : caretStart;
return Offset(dx, box.top); return Offset(min(dx, width), box.top);
} }
return null; return null;
} }
......
...@@ -426,6 +426,82 @@ void main() { ...@@ -426,6 +426,82 @@ void main() {
}, skip: !Platform.isLinux); }, skip: !Platform.isLinux);
*/ */
testWidgets('Overflowing a line with spaces stops the cursor at the end', (WidgetTester tester) async {
final TextEditingController controller = TextEditingController();
await tester.pumpWidget(
overlay(
child: TextField(
key: textFieldKey,
controller: controller,
maxLines: null,
),
)
);
expect(controller.selection.baseOffset, -1);
expect(controller.selection.extentOffset, -1);
const String testValueOneLine = 'enough text to be exactly at the end of the line.';
await tester.enterText(find.byType(TextField), testValueOneLine);
await skipPastScrollingAnimation(tester);
RenderBox findInputBox() => tester.renderObject(find.byKey(textFieldKey));
RenderBox inputBox = findInputBox();
final Size oneLineInputSize = inputBox.size;
await tester.tapAt(textOffsetToPosition(tester, testValueOneLine.length));
await tester.pump();
const String testValueTwoLines = 'enough text to overflow the first line and go to the second';
await tester.enterText(find.byType(TextField), testValueTwoLines);
await skipPastScrollingAnimation(tester);
expect(inputBox, findInputBox());
inputBox = findInputBox();
expect(inputBox.size.height, greaterThan(oneLineInputSize.height));
final Size twoLineInputSize = inputBox.size;
// Enter a string with the same number of characters as testValueTwoLines,
// but where the overflowing part is all spaces. Assert that it only renders
// on one line.
const String testValueSpaces = testValueOneLine + ' ';
expect(testValueSpaces.length, testValueTwoLines.length);
await tester.enterText(find.byType(TextField), testValueSpaces);
await skipPastScrollingAnimation(tester);
expect(inputBox, findInputBox());
inputBox = findInputBox();
expect(inputBox.size.height, oneLineInputSize.height);
// Swapping the final space for a letter causes it to wrap to 2 lines.
const String testValueSpacesOverflow = testValueOneLine + ' a';
expect(testValueSpacesOverflow.length, testValueTwoLines.length);
await tester.enterText(find.byType(TextField), testValueSpacesOverflow);
await skipPastScrollingAnimation(tester);
expect(inputBox, findInputBox());
inputBox = findInputBox();
expect(inputBox.size.height, twoLineInputSize.height);
// Positioning the cursor at the end of a line overflowing with spaces puts
// it inside the input still.
await tester.enterText(find.byType(TextField), testValueSpaces);
await skipPastScrollingAnimation(tester);
await tester.tapAt(textOffsetToPosition(tester, testValueSpaces.length));
await tester.pump();
final double inputWidth = findRenderEditable(tester).size.width;
final Offset cursorOffsetSpaces = findRenderEditable(tester).getLocalRectForCaret(
const TextPosition(offset: testValueSpaces.length),
).bottomRight;
// Gap between caret and edge of input, defined in editable.dart.
const int _kCaretGap = 1;
expect(cursorOffsetSpaces.dx, inputWidth - _kCaretGap);
});
testWidgets('obscureText control test', (WidgetTester tester) async { testWidgets('obscureText control test', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
overlay( overlay(
......
...@@ -133,7 +133,9 @@ void main() { ...@@ -133,7 +133,9 @@ void main() {
Offset caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 0), ui.Rect.zero); Offset caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 0), ui.Rect.zero);
expect(caretOffset.dx, 21); expect(caretOffset.dx, 21);
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: text.length), ui.Rect.zero); caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: text.length), ui.Rect.zero);
expect(caretOffset.dx, 441); // The end of the line is 441, but the width is only 420, so the cursor is
// stopped there without overflowing.
expect(caretOffset.dx, painter.width);
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 1), ui.Rect.zero); caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 1), ui.Rect.zero);
expect(caretOffset.dx, 35); expect(caretOffset.dx, 35);
......
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