Unverified Commit 43de3365 authored by LongCatIsLooong's avatar LongCatIsLooong Committed by GitHub

Remove rounding from TextPainter (#127099)

To opt-in, run the tests with: `SKPARAGRAPH_REMOVE_ROUNDING_HACK=1 flutter test --dart-define=SKPARAGRAPH_REMOVE_ROUNDING_HACK=1 `

Migration plans:
1. Turn the flags on in CI, migrate customer tests if needed
1. Migrate internal customers
2. Remove the flag from skparagraph. Remove the framework flag with a manual engine roll.

Also fixes https://github.com/flutter/flutter/issues/52038
parent 07f7ffde
...@@ -271,9 +271,13 @@ class _TextLayout { ...@@ -271,9 +271,13 @@ class _TextLayout {
// object when it's no logner needed. // object when it's no logner needed.
ui.Paragraph _paragraph; ui.Paragraph _paragraph;
/// Whether to enable the rounding in _applyFloatingPointHack and SkParagraph.
static const bool _shouldApplyFloatingPointHack = !bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK');
// TODO(LongCatIsLooong): https://github.com/flutter/flutter/issues/31707 // TODO(LongCatIsLooong): https://github.com/flutter/flutter/issues/31707
// remove this hack as well as the flooring in `layout`. // remove this hack as well as the flooring in `layout`.
static double _applyFloatingPointHack(double layoutValue) => layoutValue.ceilToDouble(); @pragma('vm:prefer-inline')
static double _applyFloatingPointHack(double layoutValue) => _shouldApplyFloatingPointHack ? layoutValue.ceilToDouble() : layoutValue;
/// Whether this layout has been invalidated and disposed. /// Whether this layout has been invalidated and disposed.
/// ///
......
...@@ -595,14 +595,19 @@ class TextStyle with Diagnosticable { ...@@ -595,14 +595,19 @@ class TextStyle with Diagnosticable {
// in the [fontFamilyFallback] getter. // in the [fontFamilyFallback] getter.
final String? _package; final String? _package;
/// The size of glyphs (in logical pixels) to use when painting the text. /// The size of fonts (in logical pixels) to use when painting the text.
///
/// The value specified matches the dimension of the
/// [em square](https://fonts.google.com/knowledge/glossary/em) of the
/// underlying font, and more often then not isn't exactly the height or the
/// width of glyphs in the font.
/// ///
/// During painting, the [fontSize] is multiplied by the current /// During painting, the [fontSize] is multiplied by the current
/// `textScaleFactor` to let users make it easier to read text by increasing /// `textScaleFactor` to let users make it easier to read text by increasing
/// its size. /// its size.
/// ///
/// [getParagraphStyle] will default to 14 logical pixels if the font size /// The [getParagraphStyle] method defaults to 14 logical pixels if [fontSize]
/// isn't specified here. /// is set to null.
final double? fontSize; final double? fontSize;
/// The typeface thickness to use when painting the text (e.g., bold). /// The typeface thickness to use when painting the text (e.g., bold).
......
...@@ -145,11 +145,17 @@ void main() { ...@@ -145,11 +145,17 @@ void main() {
// place. // place.
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).first), tester.getTopLeft(flying(tester, find.text('Page 1')).first),
const Offset(342.33420100808144, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 342.547737105096302912 : 342.33420100808144,
13.5,
),
); );
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).last), tester.getTopLeft(flying(tester, find.text('Page 1')).last),
const Offset(342.33420100808144, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 342.547737105096302912 : 342.33420100808144,
13.5,
),
); );
}); });
...@@ -166,11 +172,17 @@ void main() { ...@@ -166,11 +172,17 @@ void main() {
// Same as LTR but more to the right now. // Same as LTR but more to the right now.
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).first), tester.getTopLeft(flying(tester, find.text('Page 1')).first),
const Offset(357.66579899191856, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 357.912261979376353338 : 357.66579899191856,
13.5,
),
); );
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).last), tester.getTopLeft(flying(tester, find.text('Page 1')).last),
const Offset(357.66579899191856, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 357.912261979376353338 : 357.66579899191856,
13.5,
),
); );
}); });
...@@ -356,9 +368,13 @@ void main() { ...@@ -356,9 +368,13 @@ void main() {
final RenderParagraph bottomMiddle = final RenderParagraph bottomMiddle =
tester.renderObject(flying(tester, find.text('Page 1')).first); tester.renderObject(flying(tester, find.text('Page 1')).first);
expect(bottomMiddle.text.style!.color, const Color(0xff000306)); expect(bottomMiddle.text.style!.color, const Color(0xff000306));
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).first), tester.getTopLeft(flying(tester, find.text('Page 1')).first),
const Offset(342.33420100808144, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 342.547737105096302912 : 342.33420100808144,
13.5,
),
); );
// The top back label is styled exactly the same way. But the opacity tweens // The top back label is styled exactly the same way. But the opacity tweens
...@@ -368,7 +384,10 @@ void main() { ...@@ -368,7 +384,10 @@ void main() {
expect(topBackLabel.text.style!.color, const Color(0xff000306)); expect(topBackLabel.text.style!.color, const Color(0xff000306));
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).last), tester.getTopLeft(flying(tester, find.text('Page 1')).last),
const Offset(342.33420100808144, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 342.547737105096302912 : 342.33420100808144,
13.5,
),
); );
} }
...@@ -403,7 +422,10 @@ void main() { ...@@ -403,7 +422,10 @@ void main() {
expect(bottomMiddle.text.style!.color, const Color(0xff000306)); expect(bottomMiddle.text.style!.color, const Color(0xff000306));
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).first), tester.getTopLeft(flying(tester, find.text('Page 1')).first),
const Offset(357.66579899191856, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 357.912261979376353338 : 357.66579899191856,
13.5,
),
); );
// The top back label is styled exactly the same way. But the opacity tweens // The top back label is styled exactly the same way. But the opacity tweens
...@@ -413,7 +435,10 @@ void main() { ...@@ -413,7 +435,10 @@ void main() {
expect(topBackLabel.text.style!.color, const Color(0xff000306)); expect(topBackLabel.text.style!.color, const Color(0xff000306));
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).last), tester.getTopLeft(flying(tester, find.text('Page 1')).last),
const Offset(357.66579899191856, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 357.912261979376353338 : 357.66579899191856,
13.5,
),
); );
} }
...@@ -711,11 +736,17 @@ void main() { ...@@ -711,11 +736,17 @@ void main() {
); );
// Come in from the right and fade in. // Come in from the right and fade in.
checkOpacity(tester, backChevron, 0.0); checkOpacity(tester, backChevron, 0.0);
expect(tester.getTopLeft(backChevron), const Offset(88.04496401548386, 7.0)); expect(tester.getTopLeft(backChevron), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 87.2460581221158690823 : 88.04496401548386,
7.0,
));
await tester.pump(const Duration(milliseconds: 200)); await tester.pump(const Duration(milliseconds: 200));
checkOpacity(tester, backChevron, 0.09497911669313908); checkOpacity(tester, backChevron, 0.09497911669313908);
expect(tester.getTopLeft(backChevron), const Offset(31.055883467197418, 7.0)); expect(tester.getTopLeft(backChevron), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 30.8718595298545324113 : 31.055883467197418,
7.0,
));
}); });
testWidgets('First appearance of back chevron fades in from the left in RTL', (WidgetTester tester) async { testWidgets('First appearance of back chevron fades in from the left in RTL', (WidgetTester tester) async {
...@@ -753,14 +784,20 @@ void main() { ...@@ -753,14 +784,20 @@ void main() {
checkOpacity(tester, backChevron, 0.0); checkOpacity(tester, backChevron, 0.0);
expect( expect(
tester.getTopRight(backChevron), tester.getTopRight(backChevron),
const Offset(685.9550359845161, 7.0), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 687.163941725296126606 : 685.9550359845161,
7.0,
),
); );
await tester.pump(const Duration(milliseconds: 200)); await tester.pump(const Duration(milliseconds: 200));
checkOpacity(tester, backChevron, 0.09497911669313908); checkOpacity(tester, backChevron, 0.09497911669313908);
expect( expect(
tester.getTopRight(backChevron), tester.getTopRight(backChevron),
const Offset(742.9441165328026, 7.0), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 743.538140317557690651 : 742.9441165328026,
7.0,
),
); );
}); });
...@@ -862,14 +899,20 @@ void main() { ...@@ -862,14 +899,20 @@ void main() {
checkOpacity(tester, flying(tester, find.text('custom')), 0.9280824661254883); checkOpacity(tester, flying(tester, find.text('custom')), 0.9280824661254883);
expect( expect(
tester.getTopLeft(flying(tester, find.text('custom'))), tester.getTopLeft(flying(tester, find.text('custom'))),
const Offset(684.0, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 684.459999084472656250 : 684.0,
13.5,
),
); );
await tester.pump(const Duration(milliseconds: 150)); await tester.pump(const Duration(milliseconds: 150));
checkOpacity(tester, flying(tester, find.text('custom')), 0.0); checkOpacity(tester, flying(tester, find.text('custom')), 0.0);
expect( expect(
tester.getTopLeft(flying(tester, find.text('custom'))), tester.getTopLeft(flying(tester, find.text('custom'))),
const Offset(684.0, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 684.459999084472656250 : 684.0,
13.5,
),
); );
}); });
...@@ -898,14 +941,20 @@ void main() { ...@@ -898,14 +941,20 @@ void main() {
checkOpacity(tester, flying(tester, find.text('Page 1')), 0.7952219992876053); checkOpacity(tester, flying(tester, find.text('Page 1')), 0.7952219992876053);
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 1'))), tester.getTopLeft(flying(tester, find.text('Page 1'))),
const Offset(41.71033692359924, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 41.3003370761871337891 : 41.71033692359924,
13.5,
),
); );
await tester.pump(const Duration(milliseconds: 200)); await tester.pump(const Duration(milliseconds: 200));
checkOpacity(tester, flying(tester, find.text('Page 1')), 0.0); checkOpacity(tester, flying(tester, find.text('Page 1')), 0.0);
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 1'))), tester.getTopLeft(flying(tester, find.text('Page 1'))),
const Offset(-258.2321922779083, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? -258.642192125320434570 : -258.2321922779083,
13.5,
),
); );
}); });
...@@ -935,7 +984,10 @@ void main() { ...@@ -935,7 +984,10 @@ void main() {
checkOpacity(tester, flying(tester, find.text('Page 1')), 0.7952219992876053); checkOpacity(tester, flying(tester, find.text('Page 1')), 0.7952219992876053);
expect( expect(
tester.getTopRight(flying(tester, find.text('Page 1'))), tester.getTopRight(flying(tester, find.text('Page 1'))),
const Offset(758.2896630764008, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 758.699662923812866211 : 758.2896630764008,
13.5,
),
); );
await tester.pump(const Duration(milliseconds: 200)); await tester.pump(const Duration(milliseconds: 200));
...@@ -943,7 +995,10 @@ void main() { ...@@ -943,7 +995,10 @@ void main() {
expect( expect(
tester.getTopRight(flying(tester, find.text('Page 1'))), tester.getTopRight(flying(tester, find.text('Page 1'))),
// >1000. It's now off the screen. // >1000. It's now off the screen.
const Offset(1058.2321922779083, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 1058.64219212532043457 : 1058.2321922779083,
13.5,
),
); );
}); });
...@@ -963,25 +1018,39 @@ void main() { ...@@ -963,25 +1018,39 @@ void main() {
checkOpacity(tester, flying(tester, find.text('Page 1')).first, 0.9280824661254883); checkOpacity(tester, flying(tester, find.text('Page 1')).first, 0.9280824661254883);
checkOpacity(tester, flying(tester, find.text('Page 1')).last, 0.0); checkOpacity(tester, flying(tester, find.text('Page 1')).last, 0.0);
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).first), tester.getTopLeft(flying(tester, find.text('Page 1')).first),
const Offset(16.926069676876068, 52.73951627314091), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 16.9155227761479522997 : 16.926069676876068,
52.73951627314091,
),
); );
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).last), tester.getTopLeft(flying(tester, find.text('Page 1')).last),
const Offset(16.926069676876068, 52.73951627314091), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 16.9155227761479522997 : 16.926069676876068,
52.73951627314091,
),
); );
await tester.pump(const Duration(milliseconds: 200)); await tester.pump(const Duration(milliseconds: 200));
checkOpacity(tester, flying(tester, find.text('Page 1')).first, 0.0); checkOpacity(tester, flying(tester, find.text('Page 1')).first, 0.0);
checkOpacity(tester, flying(tester, find.text('Page 1')).last, 0.4604858811944723); checkOpacity(tester, flying(tester, find.text('Page 1')).last, 0.4604858811944723);
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).first), tester.getTopLeft(flying(tester, find.text('Page 1')).first),
const Offset(43.92089730501175, 22.49655644595623), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 43.6029094262710827934 : 43.92089730501175,
22.49655644595623,
),
); );
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).last), tester.getTopLeft(flying(tester, find.text('Page 1')).last),
const Offset(43.92089730501175, 22.49655644595623), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 43.6029094262710827934 : 43.92089730501175,
22.49655644595623,
),
); );
}); });
...@@ -1003,11 +1072,17 @@ void main() { ...@@ -1003,11 +1072,17 @@ void main() {
checkOpacity(tester, flying(tester, find.text('Back')), 0.0); checkOpacity(tester, flying(tester, find.text('Back')), 0.0);
expect( expect(
tester.getTopLeft(flying(tester, find.text('A title too long to fit'))), tester.getTopLeft(flying(tester, find.text('A title too long to fit'))),
const Offset(16.926069676876068, 52.73951627314091), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 16.9155227761479522997 : 16.926069676876068,
52.73951627314091,
),
); );
expect( expect(
tester.getTopLeft(flying(tester, find.text('Back'))), tester.getTopLeft(flying(tester, find.text('Back'))),
const Offset(16.926069676876068, 52.73951627314091), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 16.9155227761479522997 : 16.926069676876068,
52.73951627314091,
),
); );
await tester.pump(const Duration(milliseconds: 200)); await tester.pump(const Duration(milliseconds: 200));
...@@ -1015,11 +1090,17 @@ void main() { ...@@ -1015,11 +1090,17 @@ void main() {
checkOpacity(tester, flying(tester, find.text('Back')), 0.4604858811944723); checkOpacity(tester, flying(tester, find.text('Back')), 0.4604858811944723);
expect( expect(
tester.getTopLeft(flying(tester, find.text('A title too long to fit'))), tester.getTopLeft(flying(tester, find.text('A title too long to fit'))),
const Offset(43.92089730501175, 22.49655644595623), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 43.6029094262710827934 : 43.92089730501175,
22.49655644595623,
),
); );
expect( expect(
tester.getTopLeft(flying(tester, find.text('Back'))), tester.getTopLeft(flying(tester, find.text('Back'))),
const Offset(43.92089730501175, 22.49655644595623), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 43.6029094262710827934 : 43.92089730501175,
22.49655644595623,
),
); );
}); });
...@@ -1075,7 +1156,10 @@ void main() { ...@@ -1075,7 +1156,10 @@ void main() {
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.0); checkOpacity(tester, flying(tester, find.text('Page 2')), 0.0);
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 2'))), tester.getTopLeft(flying(tester, find.text('Page 2'))),
const Offset(739.7103369235992, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 739.940336465835571289 : 739.7103369235992,
13.5,
),
); );
await tester.pump(const Duration(milliseconds: 150)); await tester.pump(const Duration(milliseconds: 150));
...@@ -1083,7 +1167,10 @@ void main() { ...@@ -1083,7 +1167,10 @@ void main() {
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.29867843724787235); checkOpacity(tester, flying(tester, find.text('Page 2')), 0.29867843724787235);
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 2'))), tester.getTopLeft(flying(tester, find.text('Page 2'))),
const Offset(504.65044379234314, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 504.880443334579467773 : 504.65044379234314,
13.5,
),
); );
}); });
...@@ -1125,7 +1212,10 @@ void main() { ...@@ -1125,7 +1212,10 @@ void main() {
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.0); checkOpacity(tester, flying(tester, find.text('Page 2')), 0.0);
expect( expect(
tester.getTopRight(flying(tester, find.text('Page 2'))), tester.getTopRight(flying(tester, find.text('Page 2'))),
const Offset(60.28966307640076, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 60.0596635341644287109 : 60.28966307640076,
13.5,
),
); );
await tester.pump(const Duration(milliseconds: 150)); await tester.pump(const Duration(milliseconds: 150));
...@@ -1133,7 +1223,10 @@ void main() { ...@@ -1133,7 +1223,10 @@ void main() {
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.29867843724787235); checkOpacity(tester, flying(tester, find.text('Page 2')), 0.29867843724787235);
expect( expect(
tester.getTopRight(flying(tester, find.text('Page 2'))), tester.getTopRight(flying(tester, find.text('Page 2'))),
const Offset(295.34955620765686, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 295.119556665420532227 : 295.34955620765686,
13.5,
),
); );
}); });
...@@ -1257,7 +1350,10 @@ void main() { ...@@ -1257,7 +1350,10 @@ void main() {
// Page 2, which is the middle of the top route, start to fly back to the right. // Page 2, which is the middle of the top route, start to fly back to the right.
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 2'))), tester.getTopLeft(flying(tester, find.text('Page 2'))),
const Offset(353.5802058875561, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 353.810205429792404175 : 353.5802058875561,
13.5,
),
); );
// Page 1 is in transition in 2 places. Once as the top back label and once // Page 1 is in transition in 2 places. Once as the top back label and once
...@@ -1272,12 +1368,18 @@ void main() { ...@@ -1272,12 +1368,18 @@ void main() {
// Transition continues. // Transition continues.
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 2'))), tester.getTopLeft(flying(tester, find.text('Page 2'))),
const Offset(655.2055835723877, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 655.435583114624023438 : 655.2055835723877,
13.5,
),
); );
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 2'))), tester.getTopLeft(flying(tester, find.text('Page 2'))),
const Offset(749.6335566043854, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 749.863556146621704102 : 749.6335566043854,
13.5,
),
); );
await tester.pump(const Duration(milliseconds: 500)); await tester.pump(const Duration(milliseconds: 500));
...@@ -1319,7 +1421,10 @@ void main() { ...@@ -1319,7 +1421,10 @@ void main() {
// Page 2, which is the middle of the top route, start to fly back to the right. // Page 2, which is the middle of the top route, start to fly back to the right.
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 2'))), tester.getTopLeft(flying(tester, find.text('Page 2'))),
const Offset(353.5802058875561, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 353.810205429792404175 : 353.5802058875561,
13.5,
),
); );
await gesture.up(); await gesture.up();
...@@ -1328,12 +1433,18 @@ void main() { ...@@ -1328,12 +1433,18 @@ void main() {
// Transition continues from the point we let off. // Transition continues from the point we let off.
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 2'))), tester.getTopLeft(flying(tester, find.text('Page 2'))),
const Offset(353.5802058875561, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 353.810205429792404175 : 353.5802058875561,
13.5,
),
); );
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
expect( expect(
tester.getTopLeft(flying(tester, find.text('Page 2'))), tester.getTopLeft(flying(tester, find.text('Page 2'))),
const Offset(350.0011436641216, 13.5), const Offset(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 350.231143206357955933 : 350.0011436641216,
13.5,
),
); );
// Finish the snap back animation. // Finish the snap back animation.
......
...@@ -161,7 +161,8 @@ void main() { ...@@ -161,7 +161,8 @@ void main() {
// Also shows the previous page's title next to the back button. // Also shows the previous page's title next to the back button.
expect(find.widgetWithText(CupertinoButton, 'An iPod'), findsOneWidget); expect(find.widgetWithText(CupertinoButton, 'An iPod'), findsOneWidget);
// 3 paddings + 1 test font character at font size 34.0. // 3 paddings + 1 test font character at font size 34.0.
expect(tester.getTopLeft(find.text('An iPod')).dx, 8.0 + 4.0 + 34.0 + 6.0); // The epsilon is needed since the text theme has a negative letter spacing thus.
expect(tester.getTopLeft(find.text('An iPod')).dx, moreOrLessEquals(8.0 + 4.0 + 34.0 + 6.0, epsilon: 0.5));
}); });
testWidgets('Previous title is correct on first transition frame', (WidgetTester tester) async { testWidgets('Previous title is correct on first transition frame', (WidgetTester tester) async {
...@@ -267,7 +268,8 @@ void main() { ...@@ -267,7 +268,8 @@ void main() {
// from An iPod to Back (since An Internet communicator is too long to // from An iPod to Back (since An Internet communicator is too long to
// fit in the back button). // fit in the back button).
expect(find.widgetWithText(CupertinoButton, 'Back'), findsOneWidget); expect(find.widgetWithText(CupertinoButton, 'Back'), findsOneWidget);
expect(tester.getTopLeft(find.text('Back')).dx, 8.0 + 4.0 + 34.0 + 6.0); // The epsilon is needed since the text theme has a negative letter spacing thus.
expect(tester.getTopLeft(find.text('Back')).dx, moreOrLessEquals(8.0 + 4.0 + 34.0 + 6.0, epsilon: 0.5));
}); });
testWidgets('Back swipe dismiss interrupted by route push', (WidgetTester tester) async { testWidgets('Back swipe dismiss interrupted by route push', (WidgetTester tester) async {
......
...@@ -4726,7 +4726,7 @@ void main() { ...@@ -4726,7 +4726,7 @@ void main() {
child: CustomScrollView( child: CustomScrollView(
slivers: <Widget>[ slivers: <Widget>[
const SliverAppBar.large( const SliverAppBar.large(
title: Text(title), title: Text(title, maxLines: 1),
), ),
SliverToBoxAdapter( SliverToBoxAdapter(
child: Container( child: Container(
...@@ -4771,7 +4771,7 @@ void main() { ...@@ -4771,7 +4771,7 @@ void main() {
child: CustomScrollView( child: CustomScrollView(
slivers: <Widget>[ slivers: <Widget>[
const SliverAppBar.medium( const SliverAppBar.medium(
title: Text(title), title: Text(title, maxLines: 1),
), ),
SliverToBoxAdapter( SliverToBoxAdapter(
child: Container( child: Container(
...@@ -4820,7 +4820,7 @@ void main() { ...@@ -4820,7 +4820,7 @@ void main() {
child: CustomScrollView( child: CustomScrollView(
slivers: <Widget>[ slivers: <Widget>[
const SliverAppBar.large( const SliverAppBar.large(
title: Text(title), title: Text(title, maxLines: 1),
), ),
SliverToBoxAdapter( SliverToBoxAdapter(
child: Container( child: Container(
......
...@@ -50,7 +50,10 @@ void main() { ...@@ -50,7 +50,10 @@ void main() {
expect(tester.getTopLeft(find.text('0')), const Offset(16, -4)); expect(tester.getTopLeft(find.text('0')), const Offset(16, -4));
final RenderBox box = tester.renderObject(find.byType(Badge)); final RenderBox box = tester.renderObject(find.byType(Badge));
expect(box, paints..rrect(rrect: RRect.fromLTRBR(12, -4, 32, 12, const Radius.circular(8)), color: theme.colorScheme.error)); final RRect rrect = const bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK')
? RRect.fromLTRBR(12, -4, 31.5, 12, const Radius.circular(8))
: RRect.fromLTRBR(12, -4, 32, 12, const Radius.circular(8));
expect(box, paints..rrect(rrect: rrect, color: theme.colorScheme.error));
}); });
testWidgets('Large Badge defaults with RTL', (WidgetTester tester) async { testWidgets('Large Badge defaults with RTL', (WidgetTester tester) async {
...@@ -89,7 +92,10 @@ void main() { ...@@ -89,7 +92,10 @@ void main() {
expect(tester.getTopLeft(find.text('0')), const Offset(0, -4)); expect(tester.getTopLeft(find.text('0')), const Offset(0, -4));
final RenderBox box = tester.renderObject(find.byType(Badge)); final RenderBox box = tester.renderObject(find.byType(Badge));
expect(box, paints..rrect(rrect: RRect.fromLTRBR(-4, -4, 16, 12, const Radius.circular(8)), color: theme.colorScheme.error)); final RRect rrect = const bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK')
? RRect.fromLTRBR(-4, -4, 15.5, 12, const Radius.circular(8))
: RRect.fromLTRBR(-4, -4, 16, 12, const Radius.circular(8));
expect(box, paints..rrect(rrect: rrect, color: theme.colorScheme.error));
}); });
// Essentially the same as 'Large Badge defaults' // Essentially the same as 'Large Badge defaults'
...@@ -143,7 +149,10 @@ void main() { ...@@ -143,7 +149,10 @@ void main() {
// T = alignment.top // T = alignment.top
// R = L + '0'.width + padding.width // R = L + '0'.width + padding.width
// B = T + largeSize, R = largeSize/2 // B = T + largeSize, R = largeSize/2
expect(box, paints..rrect(rrect: RRect.fromLTRBR(12, -4, 32, 12, const Radius.circular(8)), color: theme.colorScheme.error)); final RRect rrect = const bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK')
? RRect.fromLTRBR(12, -4, 31.5, 12, const Radius.circular(8))
: RRect.fromLTRBR(12, -4, 32, 12, const Radius.circular(8));
expect(box, paints..rrect(rrect: rrect, color: theme.colorScheme.error));
await tester.pumpWidget(buildFrame(1000)); await tester.pumpWidget(buildFrame(1000));
expect(find.text('999+'), findsOneWidget); expect(find.text('999+'), findsOneWidget);
......
...@@ -465,18 +465,16 @@ void main() { ...@@ -465,18 +465,16 @@ void main() {
), ),
); );
final double textWidth = const bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK')
? width
: (width / 1.5).floorToDouble() * 1.5;
// The title is scaled and transformed to be 1.5 times bigger, when the // The title is scaled and transformed to be 1.5 times bigger, when the
// FlexibleSpaceBar is fully expanded, thus we expect the width to be // FlexibleSpaceBar is fully expanded, thus we expect the width to be
// 1.5 times smaller than the full width. The height of the text is the same // 1.5 times smaller than the full width. The height of the text is the same
// as the font size, with 10 dps bottom margin. // as the font size, with 10 dps bottom margin.
expect( expect(
tester.getRect(find.byType(Text)), tester.getRect(find.byType(Text)),
Rect.fromLTRB( rectMoreOrLessEquals(Rect.fromLTRB(0, height - titleFontSize - 10, textWidth, height), epsilon: 0.0001),
0,
height - titleFontSize - 10,
(width / 1.5).floorToDouble() * 1.5,
height,
),
); );
}); });
...@@ -537,18 +535,16 @@ void main() { ...@@ -537,18 +535,16 @@ void main() {
// bottom edge. // bottom edge.
const double bottomMargin = titleFontSize * (expandedTitleScale - 1); const double bottomMargin = titleFontSize * (expandedTitleScale - 1);
final double textWidth = const bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK')
? collapsedWidth
: (collapsedWidth / 3).floorToDouble() * 3;
// The title is scaled and transformed to be 3 times bigger, when the // The title is scaled and transformed to be 3 times bigger, when the
// FlexibleSpaceBar is fully expanded, thus we expect the width to be // FlexibleSpaceBar is fully expanded, thus we expect the width to be
// 3 times smaller than the full width. The height of the text is the same // 3 times smaller than the full width. The height of the text is the same
// as the font size, with 40 dps bottom margin to maintain its bottom position. // as the font size, with 40 dps bottom margin to maintain its bottom position.
expect( expect(
tester.getRect(title), tester.getRect(title),
Rect.fromLTRB( rectMoreOrLessEquals(Rect.fromLTRB(0, height - titleFontSize - bottomMargin, textWidth, height), epsilon: 0.0001),
0,
height - titleFontSize - bottomMargin,
(collapsedWidth / 3).floorToDouble() * 3,
height,
),
); );
}); });
......
...@@ -5697,8 +5697,11 @@ void main() { ...@@ -5697,8 +5697,11 @@ void main() {
return false; return false;
} }
final Rect clipRect = arguments[0] as Rect; final Rect clipRect = arguments[0] as Rect;
// 133.3 is approximately 100 / 0.75 (_kFinalLabelScale) // _kFinalLabelScale = 0.75
expect(clipRect, rectMoreOrLessEquals(const Rect.fromLTWH(0, 0, 133.0, 16.0))); const double width = bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK')
? 100 / 0.75
: 133.0;
expect(clipRect, rectMoreOrLessEquals(const Rect.fromLTWH(0, 0, width, 16.0), epsilon: 1e-5));
return true; return true;
}), }),
); );
......
...@@ -583,7 +583,7 @@ void main() { ...@@ -583,7 +583,7 @@ void main() {
// Padding at the top of the rail. // Padding at the top of the rail.
const double topPadding = 8.0; const double topPadding = 8.0;
// Width of a destination. // Width of a destination.
const double destinationWidth = 126.0; const double destinationWidth = bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 125.5 : 126.0;
// Height of a destination indicator with icon. // Height of a destination indicator with icon.
const double destinationHeight = 32.0; const double destinationHeight = 32.0;
// Space between the indicator and label. // Space between the indicator and label.
...@@ -858,7 +858,7 @@ void main() { ...@@ -858,7 +858,7 @@ void main() {
// Padding at the top of the rail. // Padding at the top of the rail.
const double topPadding = 8.0; const double topPadding = 8.0;
// Width of a destination. // Width of a destination.
const double destinationWidth = 126.0; const double destinationWidth = bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 125.5 : 126.0;
// Height of a destination indicator with icon. // Height of a destination indicator with icon.
const double destinationHeight = 32.0; const double destinationHeight = 32.0;
// Space between the indicator and label. // Space between the indicator and label.
......
...@@ -1045,7 +1045,7 @@ void main() { ...@@ -1045,7 +1045,7 @@ void main() {
child: Directionality( child: Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: MediaQuery( child: MediaQuery(
data: const MediaQueryData(textScaleFactor: 1.3), data: const MediaQueryData(textScaleFactor: 1.25),
child: Center( child: Center(
child: OutlinedButton( child: OutlinedButton(
style: const ButtonStyle( style: const ButtonStyle(
...@@ -1064,7 +1064,10 @@ void main() { ...@@ -1064,7 +1064,10 @@ void main() {
); );
expect(tester.getSize(find.byType(OutlinedButton)), equals(const Size(88.0, 48.0))); expect(tester.getSize(find.byType(OutlinedButton)), equals(const Size(88.0, 48.0)));
expect(tester.getSize(find.byType(Text)), const Size(55.0, 18.0)); expect(tester.getSize(find.byType(Text)), const Size(
bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 52.5 : 53.0,
18.0,
));
// Set text scale large enough to expand text and button. // Set text scale large enough to expand text and button.
await tester.pumpWidget( await tester.pumpWidget(
......
...@@ -607,8 +607,8 @@ void main() { ...@@ -607,8 +607,8 @@ void main() {
..line( ..line(
color: theme.colorScheme.primary, color: theme.colorScheme.primary,
strokeWidth: indicatorWeight, strokeWidth: indicatorWeight,
p1: const Offset(65.5, indicatorY), p1: const Offset(bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 65.75 : 65.5, indicatorY),
p2: const Offset(134.5, indicatorY), p2: const Offset(bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? 134.25 : 134.5, indicatorY),
), ),
); );
}); });
......
...@@ -473,19 +473,31 @@ void main() { ...@@ -473,19 +473,31 @@ void main() {
const double indicatorWeight = 3.0; const double indicatorWeight = 3.0;
expect(
tabBarBox, final RRect rrect = const bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK')
paints ? RRect.fromLTRBAndCorners(
..rrect( 64.75,
color: theme.colorScheme.primary, tabBarBox.size.height - indicatorWeight,
rrect: RRect.fromLTRBAndCorners( 135.25,
tabBarBox.size.height,
topLeft: const Radius.circular(3.0),
topRight: const Radius.circular(3.0),
)
: RRect.fromLTRBAndCorners(
64.5, 64.5,
tabBarBox.size.height - indicatorWeight, tabBarBox.size.height - indicatorWeight,
135.5, 135.5,
tabBarBox.size.height, tabBarBox.size.height,
topLeft: const Radius.circular(3.0), topLeft: const Radius.circular(3.0),
topRight: const Radius.circular(3.0), topRight: const Radius.circular(3.0),
), );
expect(
tabBarBox,
paints
..rrect(
color: theme.colorScheme.primary,
rrect: rrect,
)); ));
}); });
......
...@@ -608,7 +608,7 @@ void main() { ...@@ -608,7 +608,7 @@ void main() {
child: Directionality( child: Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: MediaQuery( child: MediaQuery(
data: const MediaQueryData(textScaleFactor: 1.3), data: const MediaQueryData(textScaleFactor: 1.25),
child: Center( child: Center(
child: TextButton( child: TextButton(
onPressed: () { }, onPressed: () { },
...@@ -620,8 +620,14 @@ void main() { ...@@ -620,8 +620,14 @@ void main() {
), ),
); );
expect(tester.getSize(find.byType(TextButton)), const Size(71.0, 48.0)); const Size textButtonSize = bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK')
expect(tester.getSize(find.byType(Text)), const Size(55.0, 18.0)); ? Size(68.5, 48.0)
: Size(69.0, 48.0);
const Size textSize = bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK')
? Size(52.5, 18.0)
: Size(53.0, 18.0);
expect(tester.getSize(find.byType(TextButton)), textButtonSize);
expect(tester.getSize(find.byType(Text)), textSize);
// Set text scale large enough to expand text and button. // Set text scale large enough to expand text and button.
await tester.pumpWidget( await tester.pumpWidget(
......
...@@ -744,7 +744,9 @@ void main() { ...@@ -744,7 +744,9 @@ void main() {
switch (materialType) { switch (materialType) {
case MaterialType.material2: case MaterialType.material2:
expect(tester.getTopLeft(find.text(selectTimeString)), equals(const Offset(154, 155))); expect(tester.getTopLeft(find.text(selectTimeString)), equals(const Offset(154, 155)));
expect(tester.getBottomRight(find.text(selectTimeString)), equals(const Offset(281, 165))); expect(tester.getBottomRight(find.text(selectTimeString)), equals(
const bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? const Offset(280.5, 165) : const Offset(281, 165),
));
expect(tester.getBottomRight(find.text(okString)).dx, 644); expect(tester.getBottomRight(find.text(okString)).dx, 644);
expect(tester.getBottomLeft(find.text(okString)).dx, 616); expect(tester.getBottomLeft(find.text(okString)).dx, 616);
expect(tester.getBottomRight(find.text(cancelString)).dx, 582); expect(tester.getBottomRight(find.text(cancelString)).dx, 582);
...@@ -764,7 +766,9 @@ void main() { ...@@ -764,7 +766,9 @@ void main() {
switch (materialType) { switch (materialType) {
case MaterialType.material2: case MaterialType.material2:
expect(tester.getTopLeft(find.text(selectTimeString)), equals(const Offset(519, 155))); expect(tester.getTopLeft(find.text(selectTimeString)), equals(
const bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') ? const Offset(519.5, 155) : const Offset(519, 155),
));
expect(tester.getBottomRight(find.text(selectTimeString)), equals(const Offset(646, 165))); expect(tester.getBottomRight(find.text(selectTimeString)), equals(const Offset(646, 165)));
expect(tester.getBottomLeft(find.text(okString)).dx, 156); expect(tester.getBottomLeft(find.text(okString)).dx, 156);
expect(tester.getBottomRight(find.text(okString)).dx, 184); expect(tester.getBottomRight(find.text(okString)).dx, 184);
......
...@@ -180,11 +180,11 @@ void main() { ...@@ -180,11 +180,11 @@ void main() {
); );
// Metrics should be refreshed // Metrics should be refreshed
// ignore: avoid_dynamic_calls // ignore: avoid_dynamic_calls
expect(state.numberLabelWidth - 46.0 < precisionErrorTolerance, isTrue); expect(state.numberLabelWidth, lessThan(46.0 + precisionErrorTolerance));
// ignore: avoid_dynamic_calls // ignore: avoid_dynamic_calls
expect(state.numberLabelHeight - 23.0 < precisionErrorTolerance, isTrue); expect(state.numberLabelHeight, lessThan(23.0 + precisionErrorTolerance));
// ignore: avoid_dynamic_calls // ignore: avoid_dynamic_calls
expect(state.numberLabelBaseline - 18.400070190429688 < precisionErrorTolerance, isTrue); expect(state.numberLabelBaseline, lessThan(18.400070190429688 + precisionErrorTolerance));
final Element element = tester.element(find.byType(CupertinoTimerPicker)); final Element element = tester.element(find.byType(CupertinoTimerPicker));
expect(element.dirty, isTrue); expect(element.dirty, isTrue);
}, skip: isBrowser); // TODO(yjbanov): cupertino does not work on the Web yet: https://github.com/flutter/flutter/issues/41920 }, skip: isBrowser); // TODO(yjbanov): cupertino does not work on the Web yet: https://github.com/flutter/flutter/issues/41920
......
...@@ -1507,6 +1507,27 @@ void main() { ...@@ -1507,6 +1507,27 @@ void main() {
painter.dispose(); painter.dispose();
}); });
test('TextPainter line breaking does not round to integers', () {
if (! const bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK')) {
return;
}
const double fontSize = 1.25;
const String text = '12345';
assert((fontSize * text.length).truncate() != fontSize * text.length);
final TextPainter painter = TextPainter(
textDirection: TextDirection.ltr,
text: const TextSpan(text: text, style: TextStyle(fontSize: fontSize)),
)..layout(maxWidth: text.length * fontSize);
expect(painter.maxIntrinsicWidth, text.length * fontSize);
switch (painter.computeLineMetrics()) {
case [ui.LineMetrics(width: final double width)]:
expect(width, text.length * fontSize);
case final List<ui.LineMetrics> metrics:
expect(metrics, hasLength(1));
}
}, skip: kIsWeb && !isCanvasKit); // [intended] Browsers seem to always round font/glyph metrics.
} }
class MockCanvas extends Fake implements Canvas { class MockCanvas extends Fake implements Canvas {
......
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