blob: 8e1e7262482f9f4c5db133020e2b43c2a77381e4 [file] [log] [blame]
// Copyright 2017 The Chromium 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 'dart:io';
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 = new TextPainter()
..textDirection = TextDirection.ltr;
painter.text = const TextSpan(
text: 'ABC DEF\nGHI',
style: const TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
);
painter.layout();
expect(
painter.getWordBoundary(const TextPosition(offset: 1, affinity: TextAffinity.downstream)),
const TextRange(start: 0, end: 3),
);
expect(
painter.getWordBoundary(const TextPosition(offset: 5, affinity: TextAffinity.downstream)),
const TextRange(start: 4, end: 7),
);
expect(
painter.getWordBoundary(const TextPosition(offset: 9, affinity: TextAffinity.downstream)),
const TextRange(start: 8, end: 11),
);
});
test('TextPainter - bidi overrides in LTR', () {
final TextPainter painter = new 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: const TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
);
expect(painter.text.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, affinity: TextAffinity.downstream));
expect(hebrew1, const TextRange(start: 0, end: 8), skip: skipExpectsWithKnownBugs);
final TextRange english2 = painter.getWordBoundary(const TextPosition(offset: 14, affinity: TextAffinity.downstream));
expect(english2, const TextRange(start: 9, end: 19), skip: skipExpectsWithKnownBugs);
final TextRange hebrew3 = painter.getWordBoundary(const TextPosition(offset: 24, affinity: TextAffinity.downstream));
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),
const Offset(0.0, 0.0),
);
expect(
painter.getOffsetForCaret(const TextPosition(offset: 0, affinity: TextAffinity.downstream), Rect.zero),
const Offset(0.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, affinity: TextAffinity.downstream), 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, affinity: TextAffinity.downstream), 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, affinity: TextAffinity.downstream), 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, affinity: TextAffinity.downstream), 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, affinity: TextAffinity.downstream), Rect.zero),
const Offset(80.0, 0.0),
);
expect(
painter.getBoxesForSelection(const TextSelection(baseOffset: 0, extentOffset: 27)),
const <TextBox>[
const TextBox.fromLTRBD(160.0, 0.0, 240.0, 10.0, TextDirection.rtl), // HEBREW1
const TextBox.fromLTRBD( 80.0, 0.0, 160.0, 10.0, TextDirection.ltr), // english2
const 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).
);
final List<List<TextBox>> list = <List<TextBox>>[];
for (int index = 0; index < painter.text.text.length; index += 1)
list.add(painter.getBoxesForSelection(new TextSelection(baseOffset: index, extentOffset: index + 1)));
expect(list, const <List<TextBox>>[
const <TextBox>[], // U+202E, non-printing Unicode bidi formatting character
const <TextBox>[const TextBox.fromLTRBD(230.0, 0.0, 240.0, 10.0, TextDirection.rtl)],
const <TextBox>[const TextBox.fromLTRBD(220.0, 0.0, 230.0, 10.0, TextDirection.rtl)],
const <TextBox>[const TextBox.fromLTRBD(210.0, 0.0, 220.0, 10.0, TextDirection.rtl)],
const <TextBox>[const TextBox.fromLTRBD(200.0, 0.0, 210.0, 10.0, TextDirection.rtl)],
const <TextBox>[const TextBox.fromLTRBD(190.0, 0.0, 200.0, 10.0, TextDirection.rtl)],
const <TextBox>[const TextBox.fromLTRBD(180.0, 0.0, 190.0, 10.0, TextDirection.rtl)],
const <TextBox>[const TextBox.fromLTRBD(170.0, 0.0, 180.0, 10.0, TextDirection.rtl)],
const <TextBox>[const TextBox.fromLTRBD(160.0, 0.0, 170.0, 10.0, TextDirection.rtl)],
const <TextBox>[], // U+202D, non-printing Unicode bidi formatting character
const <TextBox>[const TextBox.fromLTRBD(80.0, 0.0, 90.0, 10.0, TextDirection.ltr)],
const <TextBox>[const TextBox.fromLTRBD(90.0, 0.0, 100.0, 10.0, TextDirection.ltr)],
const <TextBox>[const TextBox.fromLTRBD(100.0, 0.0, 110.0, 10.0, TextDirection.ltr)],
const <TextBox>[const TextBox.fromLTRBD(110.0, 0.0, 120.0, 10.0, TextDirection.ltr)],
const <TextBox>[const TextBox.fromLTRBD(120.0, 0.0, 130.0, 10.0, TextDirection.ltr)],
const <TextBox>[const TextBox.fromLTRBD(130.0, 0.0, 140.0, 10.0, TextDirection.ltr)],
const <TextBox>[const TextBox.fromLTRBD(140.0, 0.0, 150.0, 10.0, TextDirection.ltr)],
const <TextBox>[const TextBox.fromLTRBD(150.0, 0.0, 160.0, 10.0, TextDirection.ltr)],
const <TextBox>[], // U+202C, non-printing Unicode bidi formatting character
const <TextBox>[const TextBox.fromLTRBD(70.0, 0.0, 80.0, 10.0, TextDirection.rtl)],
const <TextBox>[const TextBox.fromLTRBD(60.0, 0.0, 70.0, 10.0, TextDirection.rtl)],
const <TextBox>[const TextBox.fromLTRBD(50.0, 0.0, 60.0, 10.0, TextDirection.rtl)],
const <TextBox>[const TextBox.fromLTRBD(40.0, 0.0, 50.0, 10.0, TextDirection.rtl)],
const <TextBox>[const TextBox.fromLTRBD(30.0, 0.0, 40.0, 10.0, TextDirection.rtl)],
const <TextBox>[const TextBox.fromLTRBD(20.0, 0.0, 30.0, 10.0, TextDirection.rtl)],
const <TextBox>[const TextBox.fromLTRBD(10.0, 0.0, 20.0, 10.0, TextDirection.rtl)],
const <TextBox>[const TextBox.fromLTRBD(0.0, 0.0, 10.0, 10.0, TextDirection.rtl)],
const <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);
}, skip: skipTestsWithKnownBugs);
test('TextPainter - bidi overrides in RTL', () {
final TextPainter painter = new 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: const TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
);
expect(painter.text.text.length, 28);
painter.layout();
final TextRange hebrew1 = painter.getWordBoundary(const TextPosition(offset: 4, affinity: TextAffinity.downstream));
expect(hebrew1, const TextRange(start: 0, end: 8), skip: skipExpectsWithKnownBugs);
final TextRange english2 = painter.getWordBoundary(const TextPosition(offset: 14, affinity: TextAffinity.downstream));
expect(english2, const TextRange(start: 9, end: 19), skip: skipExpectsWithKnownBugs);
final TextRange hebrew3 = painter.getWordBoundary(const TextPosition(offset: 24, affinity: TextAffinity.downstream));
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, affinity: TextAffinity.downstream), 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, affinity: TextAffinity.downstream), 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, affinity: TextAffinity.downstream), 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, affinity: TextAffinity.downstream), 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, affinity: TextAffinity.downstream), 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, affinity: TextAffinity.downstream), Rect.zero),
const Offset(80.0, 0.0),
);
expect(
painter.getBoxesForSelection(const TextSelection(baseOffset: 0, extentOffset: 27)),
const <TextBox>[
const TextBox.fromLTRBD(160.0, 0.0, 240.0, 10.0, TextDirection.rtl), // HEBREW1
const TextBox.fromLTRBD( 80.0, 0.0, 160.0, 10.0, TextDirection.ltr), // english2
const 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,
);
}, skip: skipTestsWithKnownBugs);
test('TextPainter - forced line-wrapping with bidi', () {
final TextPainter painter = new TextPainter()
..textDirection = TextDirection.ltr;
painter.text = const TextSpan(
text: 'A\u05D0', // A, Alef
style: const TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
);
expect(painter.text.text.length, 2);
painter.layout(maxWidth: 10.0);
for (int index = 0; index <= 2; index += 1) {
expect(
painter.getWordBoundary(const TextPosition(offset: 0, affinity: TextAffinity.downstream)),
const TextRange(start: 0, end: 2),
);
}
expect( // before the A
painter.getOffsetForCaret(const TextPosition(offset: 0, affinity: TextAffinity.upstream), Rect.zero),
const Offset(0.0, 0.0),
);
expect( // before the A
painter.getOffsetForCaret(const TextPosition(offset: 0, affinity: TextAffinity.downstream), Rect.zero),
const Offset(0.0, 0.0),
);
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, affinity: TextAffinity.downstream), 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, affinity: TextAffinity.downstream), Rect.zero),
const Offset(0.0, 10.0),
);
expect(
painter.getBoxesForSelection(const TextSelection(baseOffset: 0, extentOffset: 2)),
const <TextBox>[
const TextBox.fromLTRBD(0.0, 0.0, 10.0, 10.0, TextDirection.ltr), // A
const TextBox.fromLTRBD(0.0, 10.0, 10.0, 20.0, TextDirection.rtl), // Alef
],
);
expect(
painter.getBoxesForSelection(const TextSelection(baseOffset: 0, extentOffset: 1)),
const <TextBox>[
const TextBox.fromLTRBD(0.0, 0.0, 10.0, 10.0, TextDirection.ltr), // A
],
);
expect(
painter.getBoxesForSelection(const TextSelection(baseOffset: 1, extentOffset: 2)),
const <TextBox>[
const TextBox.fromLTRBD(0.0, 10.0, 10.0, 20.0, TextDirection.rtl), // Alef
],
);
},
// Ahem-based tests don't yet quite work on Windows or some MacOS environments
skip: Platform.isWindows || Platform.isMacOS);
test('TextPainter - line wrap mid-word', () {
final TextPainter painter = new TextPainter()
..textDirection = TextDirection.ltr;
painter.text = const TextSpan(
style: const TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
children: const <TextSpan>[
const TextSpan(
text: 'hello', // width 50
),
const TextSpan(
text: 'lovely', // width 120
style: const TextStyle(fontFamily: 'Ahem', fontSize: 20.0),
),
const 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>[
const TextBox.fromLTRBD( 0.0, 8.0, 50.0, 18.0, TextDirection.ltr),
const TextBox.fromLTRBD(50.0, 0.0, 110.0, 20.0, TextDirection.ltr),
const TextBox.fromLTRBD( 0.0, 20.0, 60.0, 40.0, TextDirection.ltr),
const TextBox.fromLTRBD(60.0, 28.0, 110.0, 38.0, TextDirection.ltr),
],
skip: skipExpectsWithKnownBugs, // horizontal offsets are one pixel off in places; vertical offsets are good
);
}, skip: skipTestsWithKnownBugs);
test('TextPainter - line wrap mid-word, bidi - LTR base', () {
final TextPainter painter = new TextPainter()
..textDirection = TextDirection.ltr;
painter.text = const TextSpan(
style: const TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
children: const <TextSpan>[
const TextSpan(
text: 'hello', // width 50
),
const TextSpan(
text: '\u062C\u0645\u064A\u0644', // width 80
style: const TextStyle(fontFamily: 'Ahem', fontSize: 20.0),
),
const 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>[
const TextBox.fromLTRBD( 0.0, 8.0, 50.0, 18.0, TextDirection.ltr),
const TextBox.fromLTRBD(50.0, 0.0, 90.0, 20.0, TextDirection.rtl),
const TextBox.fromLTRBD( 0.0, 20.0, 40.0, 40.0, TextDirection.rtl),
const TextBox.fromLTRBD(40.0, 28.0, 90.0, 38.0, TextDirection.ltr),
],
skip: skipExpectsWithKnownBugs, // horizontal offsets are one pixel off in places; vertical offsets are good
);
final List<List<TextBox>> list = <List<TextBox>>[];
for (int index = 0; index < 5+4+5; index += 1)
list.add(painter.getBoxesForSelection(new TextSelection(baseOffset: index, extentOffset: index + 1)));
print(list);
expect(list, const <List<TextBox>>[
const <TextBox>[const TextBox.fromLTRBD(0.0, 8.0, 10.0, 18.0, TextDirection.ltr)],
const <TextBox>[const TextBox.fromLTRBD(10.0, 8.0, 20.0, 18.0, TextDirection.ltr)],
const <TextBox>[const TextBox.fromLTRBD(20.0, 8.0, 30.0, 18.0, TextDirection.ltr)],
const <TextBox>[const TextBox.fromLTRBD(30.0, 8.0, 40.0, 18.0, TextDirection.ltr)],
const <TextBox>[const TextBox.fromLTRBD(40.0, 8.0, 50.0, 18.0, TextDirection.ltr)],
const <TextBox>[const TextBox.fromLTRBD(70.0, 0.0, 90.0, 20.0, TextDirection.rtl)],
const <TextBox>[const TextBox.fromLTRBD(50.0, 0.0, 70.0, 20.0, TextDirection.rtl)],
const <TextBox>[const TextBox.fromLTRBD(20.0, 20.0, 40.0, 40.0, TextDirection.rtl)],
const <TextBox>[const TextBox.fromLTRBD(0.0, 20.0, 20.0, 40.0, TextDirection.rtl)],
const <TextBox>[const TextBox.fromLTRBD(40.0, 28.0, 50.0, 38.0, TextDirection.ltr)],
const <TextBox>[const TextBox.fromLTRBD(50.0, 28.0, 60.0, 38.0, TextDirection.ltr)],
const <TextBox>[const TextBox.fromLTRBD(60.0, 28.0, 70.0, 38.0, TextDirection.ltr)],
const <TextBox>[const TextBox.fromLTRBD(70.0, 28.0, 80.0, 38.0, TextDirection.ltr)],
const <TextBox>[const TextBox.fromLTRBD(80.0, 28.0, 90.0, 38.0, TextDirection.ltr)]
]);
}, skip: skipTestsWithKnownBugs);
test('TextPainter - line wrap mid-word, bidi - RTL base', () {
final TextPainter painter = new TextPainter()
..textDirection = TextDirection.rtl;
painter.text = const TextSpan(
style: const TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
children: const <TextSpan>[
const TextSpan(
text: 'hello', // width 50
),
const TextSpan(
text: '\u062C\u0645\u064A\u0644', // width 80
style: const TextStyle(fontFamily: 'Ahem', fontSize: 20.0),
),
const 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>[
const TextBox.fromLTRBD(40.0, 8.0, 90.0, 18.0, TextDirection.ltr),
const TextBox.fromLTRBD( 0.0, 0.0, 40.0, 20.0, TextDirection.rtl),
const TextBox.fromLTRBD(50.0, 20.0, 90.0, 40.0, TextDirection.rtl),
const 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,
);
}, skip: skipTestsWithKnownBugs);
test('TextPainter - multiple levels', () {
final TextPainter painter = new TextPainter()
..textDirection = TextDirection.rtl;
final String pyramid = rlo(lro(rlo(lro(rlo('')))));
painter.text = new TextSpan(
text: pyramid,
style: const TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
);
painter.layout();
expect(
painter.getBoxesForSelection(new TextSelection(baseOffset: 0, extentOffset: pyramid.length)),
const <TextBox>[
const TextBox.fromLTRBD(90.0, 0.0, 100.0, 10.0, TextDirection.rtl), // outer R, start (right)
const TextBox.fromLTRBD(10.0, 0.0, 20.0, 10.0, TextDirection.ltr), // level 1 L, start (left)
const TextBox.fromLTRBD(70.0, 0.0, 80.0, 10.0, TextDirection.rtl), // level 2 R, start (right)
const TextBox.fromLTRBD(30.0, 0.0, 40.0, 10.0, TextDirection.ltr), // level 3 L, start (left)
const TextBox.fromLTRBD(40.0, 0.0, 60.0, 10.0, TextDirection.rtl), // inner-most RR
const TextBox.fromLTRBD(60.0, 0.0, 70.0, 10.0, TextDirection.ltr), // lever 3 L, end (right)
const TextBox.fromLTRBD(20.0, 0.0, 30.0, 10.0, TextDirection.rtl), // level 2 R, end (left)
const TextBox.fromLTRBD(80.0, 0.0, 90.0, 10.0, TextDirection.ltr), // level 1 L, end (right)
const 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,
);
}, skip: skipTestsWithKnownBugs);
test('TextPainter - getPositionForOffset - RTL in LTR', () {
final TextPainter painter = new 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: const 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, affinity: TextAffinity.downstream).toString(),
);
expect(
// Aaa Bbb Ccc Gimel Bet Alef Ddd Eee Fff
// ^
painter.getPositionForOffset(const Offset(-100.0, 5.0)).toString(),
const TextPosition(offset: 0, affinity: TextAffinity.downstream).toString(),
);
expect(
// Aaa Bbb Ccc Gimel Bet Alef Ddd Eee Fff
// ^
painter.getPositionForOffset(const Offset(4.0, 5.0)).toString(),
const TextPosition(offset: 0, affinity: TextAffinity.downstream).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, affinity: TextAffinity.downstream).toString(),
skip: skipExpectsWithKnownBugs, // currently we say upstream instead of downstream
);
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, affinity: TextAffinity.downstream).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, affinity: TextAffinity.downstream).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(),
);
}, skip: skipTestsWithKnownBugs);
test('TextPainter - getPositionForOffset - LTR in RTL', () {
final TextPainter painter = new TextPainter()
..textDirection = TextDirection.rtl;
painter.text = const TextSpan(
text: '\u05D0\u05D1\u05D2ABC\u05D3\u05D4\u05D5',
style: const 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, affinity: TextAffinity.downstream).toString(),
);
expect(
// Vav He Dalet Aaa Bbb Ccc Gimel Bet Alef
// ^
painter.getPositionForOffset(const Offset(32.0, 5.0)).toString(),
const TextPosition(offset: 3, affinity: TextAffinity.downstream).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(),
);
}, skip: skipTestsWithKnownBugs);
test('TextPainter - Spaces', () {
final TextPainter painter = new TextPainter()
..textDirection = TextDirection.ltr;
painter.text = const TextSpan(
text: ' ',
style: const TextStyle(fontFamily: 'Ahem', fontSize: 100.0),
children: const <TextSpan>[
const TextSpan(
text: ' ',
style: const TextStyle(fontSize: 10.0),
),
const TextSpan(
text: ' ',
style: const TextStyle(fontSize: 200.0),
),
// Add a non-whitespace character because the renderer's line breaker
// may strip trailing whitespace on a line.
const 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), 160.0);
expect(painter.preferredLineHeight, 100.0);
expect(
painter.getBoxesForSelection(const TextSelection(baseOffset: 0, extentOffset: 3)),
const <TextBox>[
const TextBox.fromLTRBD( 0.0, 80.0, 100.0, 180.0, TextDirection.ltr),
const TextBox.fromLTRBD(100.0, 152.0, 110.0, 162.0, TextDirection.ltr),
const 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,
);
}, skip: skipTestsWithKnownBugs);
test('TextPainter - empty text baseline', () {
final TextPainter painter = new TextPainter()
..textDirection = TextDirection.ltr;
painter.text = const TextSpan(
text: '',
style: const TextStyle(fontFamily: 'Ahem', fontSize: 100.0, height: 1.0),
);
painter.layout();
expect(
// Returns -1
painter.computeDistanceToActualBaseline(TextBaseline.alphabetic), 80.0,
skip: skipExpectsWithKnownBugs,
);
}, skip: skipTestsWithKnownBugs);
}
String lro(String s) => '${Unicode.LRO}L${s}L${Unicode.PDF}';
String rlo(String s) => '${Unicode.RLO}R${s}R${Unicode.PDF}';