blob: 916c8a94a465e473e2660d963516a94b657a88a7 [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart';
import 'package:flutter_test/flutter_test.dart';
const bool skipTestsWithKnownBugs = true;
const bool skipExpectsWithKnownBugs = false;
void main() {
test('TextPainter - basic words', () {
final TextPainter painter = TextPainter()
..textDirection = TextDirection.ltr;
painter.text = const TextSpan(
text: 'ABC DEF\nGHI',
style: TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
);
painter.layout();
expect(
painter.getWordBoundary(const TextPosition(offset: 1)),
const TextRange(start: 0, end: 3),
);
expect(
painter.getWordBoundary(const TextPosition(offset: 5)),
const TextRange(start: 4, end: 7),
);
expect(
painter.getWordBoundary(const TextPosition(offset: 9)),
const TextRange(start: 8, end: 11),
);
painter.dispose();
});
test('TextPainter - bidi overrides in LTR', () {
final TextPainter painter = TextPainter()
..textDirection = TextDirection.ltr;
painter.text = const TextSpan(
text: '${Unicode.RLO}HEBREW1 ${Unicode.LRO}english2${Unicode.PDF} HEBREW3${Unicode.PDF}',
// 0 12345678 9 101234567 18 90123456 27
style: TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
);
TextSpan textSpan = painter.text! as TextSpan;
expect(textSpan.text!.length, 28);
painter.layout();
// The skips here are because the old rendering code considers the bidi formatting characters
// to be part of the word sometimes and not others, which is fine, but we'd mildly prefer if
// we were consistently considering them part of words always.
final TextRange hebrew1 = painter.getWordBoundary(const TextPosition(offset: 4));
expect(hebrew1, const TextRange(start: 0, end: 8), skip: skipExpectsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
final TextRange english2 = painter.getWordBoundary(const TextPosition(offset: 14));
expect(english2, const TextRange(start: 9, end: 19), skip: skipExpectsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
final TextRange hebrew3 = painter.getWordBoundary(const TextPosition(offset: 24));
expect(hebrew3, const TextRange(start: 20, end: 28));
// >>>>>>>>>>>>>>> embedding level 2
// <============================================== embedding level 1
// ------------------------------------------------> embedding level 0
// 0 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 2
// 0 6 5 4 3 2 1 0 9 0 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1 7 <- index of character in string
// Paints as: 3 W E R B E H e n g l i s h 2 1 W E R B E H
// 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 <- pixel offset at boundary
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4
// 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
expect(
painter.getOffsetForCaret(const TextPosition(offset: 0, affinity: TextAffinity.upstream), Rect.zero),
Offset.zero,
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 0), Rect.zero),
Offset.zero,
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 1, affinity: TextAffinity.upstream), Rect.zero),
const Offset(240.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 1), Rect.zero),
const Offset(240.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 7, affinity: TextAffinity.upstream), Rect.zero),
const Offset(180.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 7), Rect.zero),
const Offset(180.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 8, affinity: TextAffinity.upstream), Rect.zero),
const Offset(170.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 8), Rect.zero),
const Offset(170.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 9, affinity: TextAffinity.upstream), Rect.zero),
const Offset(160.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 9), Rect.zero),
const Offset(160.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 10, affinity: TextAffinity.upstream), Rect.zero),
const Offset(80.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 10), Rect.zero),
const Offset(80.0, 0.0),
);
expect(
painter.getBoxesForSelection(const TextSelection(baseOffset: 0, extentOffset: 27)),
const <TextBox>[
TextBox.fromLTRBD(160.0, 0.0, 240.0, 10.0, TextDirection.rtl), // HEBREW1
TextBox.fromLTRBD( 80.0, 0.0, 160.0, 10.0, TextDirection.ltr), // english2
TextBox.fromLTRBD( 0.0, 0.0, 80.0, 10.0, TextDirection.rtl), // HEBREW3
],
// Horizontal offsets are currently one pixel off in places; vertical offsets are good.
// The list is currently in the wrong order (so selection boxes will paint in the wrong order).
);
textSpan = painter.text! as TextSpan;
final List<List<TextBox>> list = <List<TextBox>>[
for (int index = 0; index < textSpan.text!.length; index += 1)
painter.getBoxesForSelection(TextSelection(baseOffset: index, extentOffset: index + 1)),
];
expect(list, const <List<TextBox>>[
<TextBox>[], // U+202E, non-printing Unicode bidi formatting character
<TextBox>[TextBox.fromLTRBD(230.0, 0.0, 240.0, 10.0, TextDirection.rtl)],
<TextBox>[TextBox.fromLTRBD(220.0, 0.0, 230.0, 10.0, TextDirection.rtl)],
<TextBox>[TextBox.fromLTRBD(210.0, 0.0, 220.0, 10.0, TextDirection.rtl)],
<TextBox>[TextBox.fromLTRBD(200.0, 0.0, 210.0, 10.0, TextDirection.rtl)],
<TextBox>[TextBox.fromLTRBD(190.0, 0.0, 200.0, 10.0, TextDirection.rtl)],
<TextBox>[TextBox.fromLTRBD(180.0, 0.0, 190.0, 10.0, TextDirection.rtl)],
<TextBox>[TextBox.fromLTRBD(170.0, 0.0, 180.0, 10.0, TextDirection.rtl)],
<TextBox>[TextBox.fromLTRBD(160.0, 0.0, 170.0, 10.0, TextDirection.rtl)],
<TextBox>[], // U+202D, non-printing Unicode bidi formatting character
<TextBox>[TextBox.fromLTRBD(80.0, 0.0, 90.0, 10.0, TextDirection.ltr)],
<TextBox>[TextBox.fromLTRBD(90.0, 0.0, 100.0, 10.0, TextDirection.ltr)],
<TextBox>[TextBox.fromLTRBD(100.0, 0.0, 110.0, 10.0, TextDirection.ltr)],
<TextBox>[TextBox.fromLTRBD(110.0, 0.0, 120.0, 10.0, TextDirection.ltr)],
<TextBox>[TextBox.fromLTRBD(120.0, 0.0, 130.0, 10.0, TextDirection.ltr)],
<TextBox>[TextBox.fromLTRBD(130.0, 0.0, 140.0, 10.0, TextDirection.ltr)],
<TextBox>[TextBox.fromLTRBD(140.0, 0.0, 150.0, 10.0, TextDirection.ltr)],
<TextBox>[TextBox.fromLTRBD(150.0, 0.0, 160.0, 10.0, TextDirection.ltr)],
<TextBox>[], // U+202C, non-printing Unicode bidi formatting character
<TextBox>[TextBox.fromLTRBD(70.0, 0.0, 80.0, 10.0, TextDirection.rtl)],
<TextBox>[TextBox.fromLTRBD(60.0, 0.0, 70.0, 10.0, TextDirection.rtl)],
<TextBox>[TextBox.fromLTRBD(50.0, 0.0, 60.0, 10.0, TextDirection.rtl)],
<TextBox>[TextBox.fromLTRBD(40.0, 0.0, 50.0, 10.0, TextDirection.rtl)],
<TextBox>[TextBox.fromLTRBD(30.0, 0.0, 40.0, 10.0, TextDirection.rtl)],
<TextBox>[TextBox.fromLTRBD(20.0, 0.0, 30.0, 10.0, TextDirection.rtl)],
<TextBox>[TextBox.fromLTRBD(10.0, 0.0, 20.0, 10.0, TextDirection.rtl)],
<TextBox>[TextBox.fromLTRBD(0.0, 0.0, 10.0, 10.0, TextDirection.rtl)],
<TextBox>[], // U+202C, non-printing Unicode bidi formatting character
// The list currently has one extra bogus entry (the last entry, for the
// trailing U+202C PDF, should be empty but is one-pixel-wide instead).
], skip: skipExpectsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
painter.dispose();
}, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
test('TextPainter - bidi overrides in RTL', () {
final TextPainter painter = TextPainter()
..textDirection = TextDirection.rtl;
painter.text = const TextSpan(
text: '${Unicode.RLO}HEBREW1 ${Unicode.LRO}english2${Unicode.PDF} HEBREW3${Unicode.PDF}',
// 0 12345678 9 101234567 18 90123456 27
style: TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
);
final TextSpan textSpan = painter.text! as TextSpan;
expect(textSpan.text!.length, 28);
painter.layout();
final TextRange hebrew1 = painter.getWordBoundary(const TextPosition(offset: 4));
expect(hebrew1, const TextRange(start: 0, end: 8), skip: skipExpectsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
final TextRange english2 = painter.getWordBoundary(const TextPosition(offset: 14));
expect(english2, const TextRange(start: 9, end: 19), skip: skipExpectsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
final TextRange hebrew3 = painter.getWordBoundary(const TextPosition(offset: 24));
expect(hebrew3, const TextRange(start: 20, end: 28));
// >>>>>>>>>>>>>>> embedding level 2
// <================================================== embedding level 1
// 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0
// 7 6 5 4 3 2 1 0 9 0 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1 0 <- index of character in string
// Paints as: 3 W E R B E H e n g l i s h 2 1 W E R B E H
// 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 <- pixel offset at boundary
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4
// 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
expect(
painter.getOffsetForCaret(const TextPosition(offset: 0, affinity: TextAffinity.upstream), Rect.zero),
const Offset(240.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 0), Rect.zero),
const Offset(240.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 1, affinity: TextAffinity.upstream), Rect.zero),
const Offset(240.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 1), Rect.zero),
const Offset(240.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 7, affinity: TextAffinity.upstream), Rect.zero),
const Offset(180.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 7), Rect.zero),
const Offset(180.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 8, affinity: TextAffinity.upstream), Rect.zero),
const Offset(170.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 8), Rect.zero),
const Offset(170.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 9, affinity: TextAffinity.upstream), Rect.zero),
const Offset(160.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 9), Rect.zero),
const Offset(160.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 10, affinity: TextAffinity.upstream), Rect.zero),
const Offset(80.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 10), Rect.zero),
const Offset(80.0, 0.0),
);
expect(
painter.getBoxesForSelection(const TextSelection(baseOffset: 0, extentOffset: 27)),
const <TextBox>[
TextBox.fromLTRBD(160.0, 0.0, 240.0, 10.0, TextDirection.rtl), // HEBREW1
TextBox.fromLTRBD( 80.0, 0.0, 160.0, 10.0, TextDirection.ltr), // english2
TextBox.fromLTRBD( 0.0, 0.0, 80.0, 10.0, TextDirection.rtl), // HEBREW3
],
// Horizontal offsets are currently one pixel off in places; vertical offsets are good.
// The list is currently in the wrong order (so selection boxes will paint in the wrong order).
skip: skipExpectsWithKnownBugs, // https://github.com/flutter/flutter/issues/87536
);
painter.dispose();
}, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
test('TextPainter - forced line-wrapping with bidi', () {
final TextPainter painter = TextPainter()
..textDirection = TextDirection.ltr;
painter.text = const TextSpan(
text: 'A\u05D0', // A, Alef
style: TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
);
final TextSpan textSpan = painter.text! as TextSpan;
expect(textSpan.text!.length, 2);
painter.layout(maxWidth: 10.0);
for (int index = 0; index <= 2; index += 1) {
expect(
painter.getWordBoundary(const TextPosition(offset: 0)),
const TextRange(start: 0, end: 2),
);
}
expect( // before the A
painter.getOffsetForCaret(const TextPosition(offset: 0, affinity: TextAffinity.upstream), Rect.zero),
Offset.zero,
);
expect( // before the A
painter.getOffsetForCaret(const TextPosition(offset: 0), Rect.zero),
Offset.zero,
);
expect( // between A and Alef, after the A
painter.getOffsetForCaret(const TextPosition(offset: 1, affinity: TextAffinity.upstream), Rect.zero),
const Offset(10.0, 0.0),
);
expect( // between A and Alef, before the Alef
painter.getOffsetForCaret(const TextPosition(offset: 1), Rect.zero),
const Offset(10.0, 10.0),
);
expect( // after the Alef
painter.getOffsetForCaret(const TextPosition(offset: 2, affinity: TextAffinity.upstream), Rect.zero),
const Offset(0.0, 10.0),
);
expect( // after the Alef
painter.getOffsetForCaret(const TextPosition(offset: 2), Rect.zero),
const Offset(0.0, 10.0),
);
expect(
painter.getBoxesForSelection(const TextSelection(baseOffset: 0, extentOffset: 2)),
const <TextBox>[
TextBox.fromLTRBD(0.0, 0.0, 10.0, 10.0, TextDirection.ltr), // A
TextBox.fromLTRBD(0.0, 10.0, 10.0, 20.0, TextDirection.rtl), // Alef
],
);
expect(
painter.getBoxesForSelection(const TextSelection(baseOffset: 0, extentOffset: 1)),
const <TextBox>[
TextBox.fromLTRBD(0.0, 0.0, 10.0, 10.0, TextDirection.ltr), // A
],
);
expect(
painter.getBoxesForSelection(const TextSelection(baseOffset: 1, extentOffset: 2)),
const <TextBox>[
TextBox.fromLTRBD(0.0, 10.0, 10.0, 20.0, TextDirection.rtl), // Alef
],
);
painter.dispose();
}, skip: isBrowser); // https://github.com/flutter/flutter/issues/32238
test('TextPainter - line wrap mid-word', () {
final TextPainter painter = TextPainter()
..textDirection = TextDirection.ltr;
painter.text = const TextSpan(
style: TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
children: <TextSpan>[
TextSpan(
text: 'hello', // width 50
),
TextSpan(
text: 'lovely', // width 120
style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0),
),
TextSpan(
text: 'world', // width 50
),
],
);
painter.layout(maxWidth: 110.0); // half-way through "lovely"
expect(
painter.getBoxesForSelection(const TextSelection(baseOffset: 0, extentOffset: 16)),
const <TextBox>[
TextBox.fromLTRBD( 0.0, 8.0, 50.0, 18.0, TextDirection.ltr),
TextBox.fromLTRBD(50.0, 0.0, 110.0, 20.0, TextDirection.ltr),
TextBox.fromLTRBD( 0.0, 20.0, 60.0, 40.0, TextDirection.ltr),
TextBox.fromLTRBD(60.0, 28.0, 110.0, 38.0, TextDirection.ltr),
],
// horizontal offsets are one pixel off in places; vertical offsets are good
skip: skipExpectsWithKnownBugs, // https://github.com/flutter/flutter/issues/87536
);
painter.dispose();
}, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
test('TextPainter - line wrap mid-word, bidi - LTR base', () {
final TextPainter painter = TextPainter()
..textDirection = TextDirection.ltr;
painter.text = const TextSpan(
style: TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
children: <TextSpan>[
TextSpan(
text: 'hello', // width 50
),
TextSpan(
text: '\u062C\u0645\u064A\u0644', // width 80
style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0),
),
TextSpan(
text: 'world', // width 50
),
],
);
painter.layout(maxWidth: 90.0); // half-way through the Arabic word
expect(
painter.getBoxesForSelection(const TextSelection(baseOffset: 0, extentOffset: 16)),
const <TextBox>[
TextBox.fromLTRBD( 0.0, 8.0, 50.0, 18.0, TextDirection.ltr),
TextBox.fromLTRBD(50.0, 0.0, 90.0, 20.0, TextDirection.rtl),
TextBox.fromLTRBD( 0.0, 20.0, 40.0, 40.0, TextDirection.rtl),
TextBox.fromLTRBD(40.0, 28.0, 90.0, 38.0, TextDirection.ltr),
],
// horizontal offsets are one pixel off in places; vertical offsets are good
skip: skipExpectsWithKnownBugs, // https://github.com/flutter/flutter/issues/87536
);
final List<List<TextBox>> list = <List<TextBox>>[
for (int index = 0; index < 5+4+5; index += 1)
painter.getBoxesForSelection(TextSelection(baseOffset: index, extentOffset: index + 1)),
];
expect(list, const <List<TextBox>>[
<TextBox>[TextBox.fromLTRBD(0.0, 8.0, 10.0, 18.0, TextDirection.ltr)],
<TextBox>[TextBox.fromLTRBD(10.0, 8.0, 20.0, 18.0, TextDirection.ltr)],
<TextBox>[TextBox.fromLTRBD(20.0, 8.0, 30.0, 18.0, TextDirection.ltr)],
<TextBox>[TextBox.fromLTRBD(30.0, 8.0, 40.0, 18.0, TextDirection.ltr)],
<TextBox>[TextBox.fromLTRBD(40.0, 8.0, 50.0, 18.0, TextDirection.ltr)],
<TextBox>[TextBox.fromLTRBD(70.0, 0.0, 90.0, 20.0, TextDirection.rtl)],
<TextBox>[TextBox.fromLTRBD(50.0, 0.0, 70.0, 20.0, TextDirection.rtl)],
<TextBox>[TextBox.fromLTRBD(20.0, 20.0, 40.0, 40.0, TextDirection.rtl)],
<TextBox>[TextBox.fromLTRBD(0.0, 20.0, 20.0, 40.0, TextDirection.rtl)],
<TextBox>[TextBox.fromLTRBD(40.0, 28.0, 50.0, 38.0, TextDirection.ltr)],
<TextBox>[TextBox.fromLTRBD(50.0, 28.0, 60.0, 38.0, TextDirection.ltr)],
<TextBox>[TextBox.fromLTRBD(60.0, 28.0, 70.0, 38.0, TextDirection.ltr)],
<TextBox>[TextBox.fromLTRBD(70.0, 28.0, 80.0, 38.0, TextDirection.ltr)],
<TextBox>[TextBox.fromLTRBD(80.0, 28.0, 90.0, 38.0, TextDirection.ltr)],
]);
painter.dispose();
}, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
test('TextPainter - line wrap mid-word, bidi - RTL base', () {
final TextPainter painter = TextPainter()
..textDirection = TextDirection.rtl;
painter.text = const TextSpan(
style: TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
children: <TextSpan>[
TextSpan(
text: 'hello', // width 50
),
TextSpan(
text: '\u062C\u0645\u064A\u0644', // width 80
style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0),
),
TextSpan(
text: 'world', // width 50
),
],
);
painter.layout(maxWidth: 90.0); // half-way through the Arabic word
expect(
painter.getBoxesForSelection(const TextSelection(baseOffset: 0, extentOffset: 16)),
const <TextBox>[
TextBox.fromLTRBD(40.0, 8.0, 90.0, 18.0, TextDirection.ltr),
TextBox.fromLTRBD( 0.0, 0.0, 40.0, 20.0, TextDirection.rtl),
TextBox.fromLTRBD(50.0, 20.0, 90.0, 40.0, TextDirection.rtl),
TextBox.fromLTRBD( 0.0, 28.0, 50.0, 38.0, TextDirection.ltr),
],
// Horizontal offsets are currently one pixel off in places; vertical offsets are good.
// The list is currently in the wrong order (so selection boxes will paint in the wrong order).
skip: skipExpectsWithKnownBugs, // https://github.com/flutter/flutter/issues/87536
);
painter.dispose();
}, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
test('TextPainter - multiple levels', () {
final TextPainter painter = TextPainter()
..textDirection = TextDirection.rtl;
final String pyramid = rlo(lro(rlo(lro(rlo('')))));
painter.text = TextSpan(
text: pyramid,
style: const TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
);
painter.layout();
expect(
painter.getBoxesForSelection(TextSelection(baseOffset: 0, extentOffset: pyramid.length)),
const <TextBox>[
TextBox.fromLTRBD(90.0, 0.0, 100.0, 10.0, TextDirection.rtl), // outer R, start (right)
TextBox.fromLTRBD(10.0, 0.0, 20.0, 10.0, TextDirection.ltr), // level 1 L, start (left)
TextBox.fromLTRBD(70.0, 0.0, 80.0, 10.0, TextDirection.rtl), // level 2 R, start (right)
TextBox.fromLTRBD(30.0, 0.0, 40.0, 10.0, TextDirection.ltr), // level 3 L, start (left)
TextBox.fromLTRBD(40.0, 0.0, 60.0, 10.0, TextDirection.rtl), // inner-most RR
TextBox.fromLTRBD(60.0, 0.0, 70.0, 10.0, TextDirection.ltr), // lever 3 L, end (right)
TextBox.fromLTRBD(20.0, 0.0, 30.0, 10.0, TextDirection.rtl), // level 2 R, end (left)
TextBox.fromLTRBD(80.0, 0.0, 90.0, 10.0, TextDirection.ltr), // level 1 L, end (right)
TextBox.fromLTRBD( 0.0, 0.0, 10.0, 10.0, TextDirection.rtl), // outer R, end (left)
],
// Horizontal offsets are currently one pixel off in places; vertical offsets are good.
// The list is currently in the wrong order (so selection boxes will paint in the wrong order).
// Also currently there's an extraneous box at the start of the list.
skip: skipExpectsWithKnownBugs, // https://github.com/flutter/flutter/issues/87536
);
painter.dispose();
}, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
test('TextPainter - getPositionForOffset - RTL in LTR', () {
final TextPainter painter = TextPainter()
..textDirection = TextDirection.ltr;
painter.text = const TextSpan(
text: 'ABC\u05D0\u05D1\u05D2DEF', // A B C Alef Bet Gimel D E F -- but the Hebrew letters are RTL
style: TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
);
painter.layout();
// TODO(ianh): Remove the toString()s once https://github.com/flutter/engine/pull/4283 lands
expect(
// Aaa Bbb Ccc Gimel Bet Alef Ddd Eee Fff
// ^
painter.getPositionForOffset(const Offset(0.0, 5.0)).toString(),
const TextPosition(offset: 0).toString(),
);
expect(
// Aaa Bbb Ccc Gimel Bet Alef Ddd Eee Fff
// ^
painter.getPositionForOffset(const Offset(-100.0, 5.0)).toString(),
const TextPosition(offset: 0).toString(),
);
expect(
// Aaa Bbb Ccc Gimel Bet Alef Ddd Eee Fff
// ^
painter.getPositionForOffset(const Offset(4.0, 5.0)).toString(),
const TextPosition(offset: 0).toString(),
);
expect(
// Aaa Bbb Ccc Gimel Bet Alef Ddd Eee Fff
// ^
painter.getPositionForOffset(const Offset(8.0, 5.0)).toString(),
const TextPosition(offset: 1, affinity: TextAffinity.upstream).toString(),
);
expect(
// Aaa Bbb Ccc Gimel Bet Alef Ddd Eee Fff
// ^
painter.getPositionForOffset(const Offset(12.0, 5.0)).toString(),
const TextPosition(offset: 1).toString(),
// currently we say upstream instead of downstream
skip: skipExpectsWithKnownBugs, // https://github.com/flutter/flutter/issues/87536
);
expect(
// Aaa Bbb Ccc Gimel Bet Alef Ddd Eee Fff
// ^
painter.getPositionForOffset(const Offset(28.0, 5.0)).toString(),
const TextPosition(offset: 3, affinity: TextAffinity.upstream).toString(),
);
expect(
// Aaa Bbb Ccc Gimel Bet Alef Ddd Eee Fff
// ^
painter.getPositionForOffset(const Offset(32.0, 5.0)).toString(),
const TextPosition(offset: 6, affinity: TextAffinity.upstream).toString(),
skip: skipExpectsWithKnownBugs, // this is part of https://github.com/flutter/flutter/issues/11375
);
expect(
// Aaa Bbb Ccc Gimel Bet Alef Ddd Eee Fff
// ^
painter.getPositionForOffset(const Offset(58.0, 5.0)).toString(),
const TextPosition(offset: 3).toString(),
skip: skipExpectsWithKnownBugs, // this is part of https://github.com/flutter/flutter/issues/11375
);
expect(
// Aaa Bbb Ccc Gimel Bet Alef Ddd Eee Fff
// ^
painter.getPositionForOffset(const Offset(62.0, 5.0)).toString(),
const TextPosition(offset: 6).toString(),
);
expect(
// Aaa Bbb Ccc Gimel Bet Alef Ddd Eee Fff
// ^
painter.getPositionForOffset(const Offset(88.0, 5.0)).toString(),
const TextPosition(offset: 9, affinity: TextAffinity.upstream).toString(),
);
expect(
// Aaa Bbb Ccc Gimel Bet Alef Ddd Eee Fff
// ^
painter.getPositionForOffset(const Offset(100.0, 5.0)).toString(),
const TextPosition(offset: 9, affinity: TextAffinity.upstream).toString(),
);
painter.dispose();
}, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
test('TextPainter - getPositionForOffset - LTR in RTL', () {
final TextPainter painter = TextPainter()
..textDirection = TextDirection.rtl;
painter.text = const TextSpan(
text: '\u05D0\u05D1\u05D2ABC\u05D3\u05D4\u05D5',
style: TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
);
painter.layout();
// TODO(ianh): Remove the toString()s once https://github.com/flutter/engine/pull/4283 lands
expect(
// Vav He Dalet Aaa Bbb Ccc Gimel Bet Alef
// ^
painter.getPositionForOffset(const Offset(-4.0, 5.0)).toString(),
const TextPosition(offset: 9, affinity: TextAffinity.upstream).toString(),
);
expect(
// Vav He Dalet Aaa Bbb Ccc Gimel Bet Alef
// ^
painter.getPositionForOffset(const Offset(28.0, 5.0)).toString(),
const TextPosition(offset: 6).toString(),
);
expect(
// Vav He Dalet Aaa Bbb Ccc Gimel Bet Alef
// ^
painter.getPositionForOffset(const Offset(32.0, 5.0)).toString(),
const TextPosition(offset: 3).toString(),
skip: skipExpectsWithKnownBugs, // this is part of https://github.com/flutter/flutter/issues/11375
);
expect(
// Vav He Dalet Aaa Bbb Ccc Gimel Bet Alef
// ^
painter.getPositionForOffset(const Offset(58.0, 5.0)).toString(),
const TextPosition(offset: 6, affinity: TextAffinity.upstream).toString(),
skip: skipExpectsWithKnownBugs, // this is part of https://github.com/flutter/flutter/issues/11375
);
expect(
// Vav He Dalet Aaa Bbb Ccc Gimel Bet Alef
// ^
painter.getPositionForOffset(const Offset(62.0, 5.0)).toString(),
const TextPosition(offset: 3, affinity: TextAffinity.upstream).toString(),
);
painter.dispose();
}, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
test('TextPainter - Spaces', () {
final TextPainter painter = TextPainter()
..textDirection = TextDirection.ltr;
painter.text = const TextSpan(
text: ' ',
style: TextStyle(fontFamily: 'Ahem', fontSize: 100.0),
children: <TextSpan>[
TextSpan(
text: ' ',
style: TextStyle(fontSize: 10.0),
),
TextSpan(
text: ' ',
style: TextStyle(fontSize: 200.0),
),
// Add a non-whitespace character because the renderer's line breaker
// may strip trailing whitespace on a line.
TextSpan(text: 'A'),
],
);
painter.layout();
// This renders as three (invisible) boxes:
//
// |<--------200------->|
// ____________________
// | ^ |
// | : |
// | : |
// | : |
// | : |
// ___________ | : 160 |
// | ^ | | : |
// |<-+-100--->|10| : |
// | : |__| : |
// | : 80 | |8 : |
// _|__v________|__|________v___________| BASELINE
// | ^20 |__|2 ^ |
// |_____v_____| | | |
// | | 40 |
// | | |
// |________v___________|
expect(painter.width, 410.0);
expect(painter.height, 200.0);
expect(painter.computeDistanceToActualBaseline(TextBaseline.alphabetic), moreOrLessEquals(160.0, epsilon: 0.001));
expect(painter.preferredLineHeight, 100.0);
expect(
painter.getBoxesForSelection(const TextSelection(baseOffset: 0, extentOffset: 3)),
const <TextBox>[
TextBox.fromLTRBD( 0.0, 80.0, 100.0, 180.0, TextDirection.ltr),
TextBox.fromLTRBD(100.0, 152.0, 110.0, 162.0, TextDirection.ltr),
TextBox.fromLTRBD(110.0, 0.0, 310.0, 200.0, TextDirection.ltr),
],
// Horizontal offsets are currently one pixel off in places; vertical offsets are good.
skip: skipExpectsWithKnownBugs, // https://github.com/flutter/flutter/issues/87536
);
painter.dispose();
}, skip: skipTestsWithKnownBugs); // https://github.com/flutter/flutter/issues/87536
test('TextPainter - empty text baseline', () {
final TextPainter painter = TextPainter()
..textDirection = TextDirection.ltr;
painter.text = const TextSpan(
text: '',
style: TextStyle(fontFamily: 'Ahem', fontSize: 100.0, height: 1.0),
);
painter.layout();
expect(painter.computeDistanceToActualBaseline(TextBaseline.alphabetic), moreOrLessEquals(80.0, epsilon: 0.001));
painter.dispose();
});
}
String lro(String s) => '${Unicode.LRO}L${s}L${Unicode.PDF}';
String rlo(String s) => '${Unicode.RLO}R${s}R${Unicode.PDF}';