Unverified Commit fba4fdb4 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Fix off by one error in TextPainter. (#19429)

parent ca93c53c
......@@ -426,7 +426,7 @@ class TextPainter {
}
Offset _getOffsetFromDownstream(int offset, Rect caretPrototype) {
final int nextCodeUnit = _text.codeUnitAt(offset);
final int nextCodeUnit = _text.codeUnitAt(offset - 1);
if (nextCodeUnit == null)
return null;
final int nextRuneOffset = _isUtf16Surrogate(nextCodeUnit) ? offset + 2 : offset + 1;
......
......@@ -179,4 +179,117 @@ void main() {
expect(painter.minIntrinsicWidth, 90.0);
expect(painter.maxIntrinsicWidth, 180.0);
}, skip: true); // https://github.com/flutter/flutter/issues/13512
test('TextPainter handles newlines properly', () {
final TextPainter painter = new TextPainter()
..textDirection = TextDirection.ltr;
String text = 'aaa';
painter.text = new TextSpan(text: text);
painter.layout();
Offset caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 0), ui.Rect.zero);
expect(caretOffset.dx, closeTo(0.0, 0.0001));
caretOffset = painter.getOffsetForCaret(new ui.TextPosition(offset: text.length), ui.Rect.zero);
expect(caretOffset.dx, painter.width);
expect(caretOffset.dy, closeTo(0.0, 0.0001));
// Check that getOffsetForCaret handles a trailing newline when affinity is downstream.
text = 'aaa\n';
painter.text = new TextSpan(text: text);
painter.layout();
caretOffset = painter.getOffsetForCaret(new ui.TextPosition(offset: text.length), ui.Rect.zero);
expect(caretOffset.dx, closeTo(0.0, 0.0001));
expect(caretOffset.dy, closeTo(14.0, 0.0001));
// Check that getOffsetForCaret handles a trailing newline when affinity is upstream.
text = 'aaa\n';
painter.text = new TextSpan(text: text);
painter.layout();
caretOffset = painter.getOffsetForCaret(new ui.TextPosition(offset: text.length, affinity: TextAffinity.upstream), ui.Rect.zero);
expect(caretOffset.dx, painter.width);
expect(caretOffset.dy, closeTo(0.0, 0.0001));
// Correctly moves through second line with downstream affinity.
text = 'aaa\naaa';
painter.text = new TextSpan(text: text);
painter.layout();
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 4), ui.Rect.zero);
expect(caretOffset.dx, closeTo(0.0, 0.0001));
expect(caretOffset.dy, closeTo(14.0, 0.0001));
// Correctly moves through second line with upstream affinity.
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 4, affinity: TextAffinity.upstream), ui.Rect.zero);
expect(caretOffset.dx, closeTo(42.0, 0.0001));
expect(caretOffset.dy, closeTo(0.0, 0.0001));
// Correctly handles multiple trailing newlines.
text = 'aaa\n\n\n';
painter.text = new TextSpan(text: text);
painter.layout();
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 4), ui.Rect.zero);
expect(caretOffset.dx, closeTo(0.0, 0.0001));
expect(caretOffset.dy, closeTo(14.0, 0.001));
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 5), ui.Rect.zero);
expect(caretOffset.dx, closeTo(0.0, 0.0001));
expect(caretOffset.dy, closeTo(28.0, 0.001));
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 6), ui.Rect.zero);
expect(caretOffset.dx, closeTo(0.0, 0.0001));
expect(caretOffset.dy, closeTo(42.0, 0.0001));
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 6, affinity: TextAffinity.upstream), ui.Rect.zero);
expect(caretOffset.dx, closeTo(0.0, 0.0001));
expect(caretOffset.dy, closeTo(28.0, 0.0001));
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 5, affinity: TextAffinity.upstream), ui.Rect.zero);
expect(caretOffset.dx, closeTo(0.0, 0.0001));
expect(caretOffset.dy, closeTo(14.0, 0.0001));
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 4, affinity: TextAffinity.upstream), ui.Rect.zero);
expect(caretOffset.dx, closeTo(42.0, 0.0001));
expect(caretOffset.dy, closeTo(0.0, 0.0001));
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 3, affinity: TextAffinity.upstream), ui.Rect.zero);
expect(caretOffset.dx, closeTo(42.0, 0.0001));
expect(caretOffset.dy, closeTo(0.0, 0.0001));
// Correctly handles multiple leading newlines
text = '\n\n\naaa';
painter.text = new TextSpan(text: text);
painter.layout();
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 3), ui.Rect.zero);
expect(caretOffset.dx, closeTo(0.0, 0.0001));
expect(caretOffset.dy, closeTo(42.0, 0.0001));
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 2), ui.Rect.zero);
expect(caretOffset.dx, closeTo(0.0, 0.0001));
expect(caretOffset.dy, closeTo(28.0, 0.0001));
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 1), ui.Rect.zero);
expect(caretOffset.dx, closeTo(0.0, 0.0001));
expect(caretOffset.dy,closeTo(14.0, 0.0001));
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 0), ui.Rect.zero);
expect(caretOffset.dx, closeTo(0.0, 0.0001));
expect(caretOffset.dy, closeTo(0.0, 0.0001));
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 0, affinity: TextAffinity.upstream), ui.Rect.zero);
expect(caretOffset.dx, closeTo(0.0, 0.0001));
expect(caretOffset.dy, closeTo(0.0, 0.0001));
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 1, affinity: TextAffinity.upstream), ui.Rect.zero);
expect(caretOffset.dx, closeTo(0.0, 0.0001));
expect(caretOffset.dy, closeTo(0.0, 0.0001));
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 2, affinity: TextAffinity.upstream), ui.Rect.zero);
expect(caretOffset.dx, closeTo(0.0, 0.0001));
expect(caretOffset.dy, closeTo(14.0, 0.0001));
caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 3, affinity: TextAffinity.upstream), ui.Rect.zero);
expect(caretOffset.dx, closeTo(0.0, 0.0001));
expect(caretOffset.dy, closeTo(28.0, 0.0001));
});
}
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