Unverified Commit c6cc3cde authored by Gary Qian's avatar Gary Qian Committed by GitHub

Integrate Strut: Add StrutStyle, expose Strut API, wire up strut with dart:ui,...

Integrate Strut: Add StrutStyle, expose Strut API, wire up strut with dart:ui, Roll engine 31a7f4d..e7eb1c8 (7 commits) (#26332)

Includes a breaking change to dart:ui ParagraphStyle where lineHeight is renamed to height for consistency with TextStyle.
parent 496ddc58
15f2b92cce916982b7dd8ce658bbf2a465c06ba4
e7eb1c8bf65531195fc76ba96c8fc8478ac5f554
46a3d26acbb1b0d72b6b02c30f03b9dbda7d5bdf
cbd3fa445868962b7e910e498791755c988e9890
......@@ -51,6 +51,7 @@ export 'src/painting/paint_utilities.dart';
export 'src/painting/rounded_rectangle_border.dart';
export 'src/painting/shape_decoration.dart';
export 'src/painting/stadium_border.dart';
export 'src/painting/strut_style.dart';
export 'src/painting/superellipse_shape.dart';
export 'src/painting/text_painter.dart';
export 'src/painting/text_span.dart';
......
This diff is collapsed.
......@@ -10,6 +10,7 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/services.dart';
import 'basic_types.dart';
import 'strut_style.dart';
import 'text_span.dart';
export 'package:flutter/services.dart' show TextRange, TextSelection;
......@@ -48,6 +49,7 @@ class TextPainter {
int maxLines,
String ellipsis,
Locale locale,
StrutStyle strutStyle,
}) : assert(text == null || text.debugAssertIsValid()),
assert(textAlign != null),
assert(textScaleFactor != null),
......@@ -58,7 +60,8 @@ class TextPainter {
_textScaleFactor = textScaleFactor,
_maxLines = maxLines,
_ellipsis = ellipsis,
_locale = locale;
_locale = locale,
_strutStyle = strutStyle;
ui.Paragraph _paragraph;
bool _needsLayout = true;
......@@ -198,6 +201,29 @@ class TextPainter {
_needsLayout = true;
}
/// {@template flutter.painting.textPainter.strutStyle}
/// The strut style to use. Strut style defines the strut, which sets minimum
/// vertical layout metrics.
///
/// Omitting or providing null will disable strut.
///
/// Omitting or providing null for any properties of [StrutStyle] will result in
/// default values being used. It is highly recommended to at least specify a
/// [fontSize].
///
/// See [StrutStyle] for details.
/// {@endtemplate}
StrutStyle get strutStyle => _strutStyle;
StrutStyle _strutStyle;
set strutStyle(StrutStyle value) {
if (_strutStyle == value)
return;
_strutStyle = value;
_paragraph = null;
_needsLayout = true;
}
ui.Paragraph _layoutTemplate;
ui.ParagraphStyle _createParagraphStyle([TextDirection defaultTextDirection]) {
......@@ -212,6 +238,7 @@ class TextPainter {
maxLines: _maxLines,
ellipsis: _ellipsis,
locale: _locale,
strutStyle: _strutStyle,
) ?? ui.ParagraphStyle(
textAlign: textAlign,
textDirection: textDirection ?? defaultTextDirection,
......
......@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' as ui show ParagraphStyle, TextStyle, lerpDouble, Shadow;
import 'dart:ui' as ui show ParagraphStyle, TextStyle, StrutStyle, lerpDouble, Shadow;
import 'package:flutter/foundation.dart';
import 'basic_types.dart';
import 'strut_style.dart';
const String _kDefaultDebugLabel = 'unknown';
......@@ -812,17 +813,35 @@ class TextStyle extends Diagnosticable {
String ellipsis,
int maxLines,
Locale locale,
String fontFamily,
double fontSize,
FontWeight fontWeight,
FontStyle fontStyle,
double height,
StrutStyle strutStyle,
}) {
assert(textScaleFactor != null);
assert(maxLines == null || maxLines > 0);
return ui.ParagraphStyle(
textAlign: textAlign,
textDirection: textDirection,
fontWeight: fontWeight,
fontStyle: fontStyle,
fontFamily: fontFamily,
fontSize: (fontSize ?? _defaultFontSize) * textScaleFactor,
lineHeight: height,
// Here, we stablish the contents of this TextStyle as the paragraph's default font
// unless an override is passed in.
fontWeight: fontWeight ?? this.fontWeight,
fontStyle: fontStyle ?? this.fontStyle,
fontFamily: fontFamily ?? this.fontFamily,
fontSize: (fontSize ?? this.fontSize ?? _defaultFontSize) * textScaleFactor,
height: height ?? this.height,
strutStyle: strutStyle == null ? null : ui.StrutStyle(
fontFamily: strutStyle.fontFamily,
fontFamilyFallback: strutStyle.fontFamilyFallback,
fontSize: strutStyle.fontSize,
height: strutStyle.height,
leading: strutStyle.leading,
fontWeight: strutStyle.fontWeight,
fontStyle: strutStyle.fontStyle,
forceStrutHeight: strutStyle.forceStrutHeight,
),
maxLines: maxLines,
ellipsis: ellipsis,
locale: locale,
......@@ -928,35 +947,7 @@ class TextStyle extends Diagnosticable {
styles.add(DoubleProperty('${prefix}size', fontSize, defaultValue: null));
String weightDescription;
if (fontWeight != null) {
switch (fontWeight) {
case FontWeight.w100:
weightDescription = '100';
break;
case FontWeight.w200:
weightDescription = '200';
break;
case FontWeight.w300:
weightDescription = '300';
break;
case FontWeight.w400:
weightDescription = '400';
break;
case FontWeight.w500:
weightDescription = '500';
break;
case FontWeight.w600:
weightDescription = '600';
break;
case FontWeight.w700:
weightDescription = '700';
break;
case FontWeight.w800:
weightDescription = '800';
break;
case FontWeight.w900:
weightDescription = '900';
break;
}
weightDescription = '${fontWeight.index + 1}00';
}
// TODO(jacobr): switch this to use enumProperty which will either cause the
// weight description to change to w600 from 600 or require existing
......
......@@ -95,7 +95,7 @@ class RenderErrorBox extends RenderBox {
/// The paragraph style to use when painting [RenderErrorBox] objects.
static ui.ParagraphStyle paragraphStyle = ui.ParagraphStyle(
lineHeight: 1.0,
height: 1.0,
);
@override
......
......@@ -6,6 +6,7 @@ import 'dart:ui' as ui show Gradient, Shader, TextBox;
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/painting.dart';
import 'package:flutter/semantics.dart';
import 'package:flutter/services.dart';
......@@ -45,6 +46,7 @@ class RenderParagraph extends RenderBox {
double textScaleFactor = 1.0,
int maxLines,
Locale locale,
StrutStyle strutStyle,
}) : assert(text != null),
assert(text.debugAssertIsValid()),
assert(textAlign != null),
......@@ -63,6 +65,7 @@ class RenderParagraph extends RenderBox {
maxLines: maxLines,
ellipsis: overflow == TextOverflow.ellipsis ? _kEllipsis : null,
locale: locale,
strutStyle: strutStyle,
);
final TextPainter _textPainter;
......@@ -194,6 +197,17 @@ class RenderParagraph extends RenderBox {
markNeedsLayout();
}
/// {@macro flutter.painting.textPainter.strutStyle}
StrutStyle get strutStyle => _textPainter.strutStyle;
/// The value may be null.
set strutStyle(StrutStyle value) {
if (_textPainter.strutStyle == value)
return;
_textPainter.strutStyle = value;
_overflowShader = null;
markNeedsLayout();
}
void _layoutText({ double minWidth = 0.0, double maxWidth = double.infinity }) {
final bool widthMatters = softWrap || overflow == TextOverflow.ellipsis;
_textPainter.layout(minWidth: minWidth, maxWidth: widthMatters ? maxWidth : double.infinity);
......
......@@ -4599,6 +4599,7 @@ class RichText extends LeafRenderObjectWidget {
this.textScaleFactor = 1.0,
this.maxLines,
this.locale,
this.strutStyle,
}) : assert(text != null),
assert(textAlign != null),
assert(softWrap != null),
......@@ -4660,6 +4661,9 @@ class RichText extends LeafRenderObjectWidget {
/// See [RenderParagraph.locale] for more information.
final Locale locale;
/// {@macro flutter.painting.textPainter.strutStyle}
final StrutStyle strutStyle;
@override
RenderParagraph createRenderObject(BuildContext context) {
assert(textDirection != null || debugCheckHasDirectionality(context));
......@@ -4670,6 +4674,7 @@ class RichText extends LeafRenderObjectWidget {
overflow: overflow,
textScaleFactor: textScaleFactor,
maxLines: maxLines,
strutStyle: strutStyle,
locale: locale ?? Localizations.localeOf(context, nullOk: true),
);
}
......@@ -4685,6 +4690,7 @@ class RichText extends LeafRenderObjectWidget {
..overflow = overflow
..textScaleFactor = textScaleFactor
..maxLines = maxLines
..strutStyle = strutStyle
..locale = locale ?? Localizations.localeOf(context, nullOk: true);
}
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart';
import 'basic.dart';
import 'framework.dart';
......@@ -224,6 +225,7 @@ class Text extends StatelessWidget {
const Text(this.data, {
Key key,
this.style,
this.strutStyle,
this.textAlign,
this.textDirection,
this.locale,
......@@ -240,6 +242,7 @@ class Text extends StatelessWidget {
const Text.rich(this.textSpan, {
Key key,
this.style,
this.strutStyle,
this.textAlign,
this.textDirection,
this.locale,
......@@ -269,6 +272,9 @@ class Text extends StatelessWidget {
/// replace the closest enclosing [DefaultTextStyle].
final TextStyle style;
/// {@macro flutter.painting.textPainter.strutStyle}
final StrutStyle strutStyle;
/// How the text should be aligned horizontally.
final TextAlign textAlign;
......@@ -356,6 +362,7 @@ class Text extends StatelessWidget {
overflow: overflow ?? defaultTextStyle.overflow,
textScaleFactor: textScaleFactor ?? MediaQuery.textScaleFactorOf(context),
maxLines: maxLines ?? defaultTextStyle.maxLines,
strutStyle: strutStyle,
text: TextSpan(
style: effectiveTextStyle,
text: data,
......
......@@ -720,7 +720,7 @@ class _TextStyleProxy implements TextStyle {
}
@override
ui.ParagraphStyle getParagraphStyle({TextAlign textAlign, TextDirection textDirection, double textScaleFactor = 1.0, String ellipsis, int maxLines, Locale locale}) {
ui.ParagraphStyle getParagraphStyle({TextAlign textAlign, TextDirection textDirection, double textScaleFactor = 1.0, String ellipsis, int maxLines, Locale locale, String fontFamily, double fontSize, FontWeight fontWeight, FontStyle fontStyle, double height, StrutStyle strutStyle}) {
throw UnimplementedError();
}
......
......@@ -169,22 +169,22 @@ void main() {
expect(ts2.toString(), 'TextStyle(color: Color(0xff00ff00), decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, fontWeight: FontWeight.w800, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: 10.0, letterSpacing: unspecified, wordSpacing: unspecified, height: 100.0x, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified)');
final ui.ParagraphStyle ps2 = s2.getParagraphStyle(textAlign: TextAlign.center);
expect(ps2, equals(ui.ParagraphStyle(textAlign: TextAlign.center, fontWeight: FontWeight.w800, fontSize: 10.0, lineHeight: 100.0)));
expect(ps2.toString(), 'ParagraphStyle(textAlign: TextAlign.center, textDirection: unspecified, fontWeight: FontWeight.w800, fontStyle: unspecified, maxLines: unspecified, fontFamily: unspecified, fontSize: 10.0, lineHeight: 100.0x, ellipsis: unspecified, locale: unspecified)');
expect(ps2, equals(ui.ParagraphStyle(textAlign: TextAlign.center, fontWeight: FontWeight.w800, fontSize: 10.0, height: 100.0)));
expect(ps2.toString(), 'ParagraphStyle(textAlign: TextAlign.center, textDirection: unspecified, fontWeight: FontWeight.w800, fontStyle: unspecified, maxLines: unspecified, fontFamily: unspecified, fontSize: 10.0, height: 100.0x, ellipsis: unspecified, locale: unspecified)');
final ui.ParagraphStyle ps5 = s5.getParagraphStyle();
expect(ps5, equals(ui.ParagraphStyle(fontWeight: FontWeight.w700, fontSize: 12.0, lineHeight: 123.0)));
expect(ps5.toString(), 'ParagraphStyle(textAlign: unspecified, textDirection: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, maxLines: unspecified, fontFamily: unspecified, fontSize: 12.0, lineHeight: 123.0x, ellipsis: unspecified, locale: unspecified)');
expect(ps5, equals(ui.ParagraphStyle(fontWeight: FontWeight.w700, fontSize: 12.0, height: 123.0)));
expect(ps5.toString(), 'ParagraphStyle(textAlign: unspecified, textDirection: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, maxLines: unspecified, fontFamily: unspecified, fontSize: 12.0, height: 123.0x, ellipsis: unspecified, locale: unspecified)');
});
test('TextStyle with text direction', () {
final ui.ParagraphStyle ps6 = const TextStyle().getParagraphStyle(textDirection: TextDirection.ltr);
expect(ps6, equals(ui.ParagraphStyle(textDirection: TextDirection.ltr, fontSize: 14.0)));
expect(ps6.toString(), 'ParagraphStyle(textAlign: unspecified, textDirection: TextDirection.ltr, fontWeight: unspecified, fontStyle: unspecified, maxLines: unspecified, fontFamily: unspecified, fontSize: 14.0, lineHeight: unspecified, ellipsis: unspecified, locale: unspecified)');
expect(ps6.toString(), 'ParagraphStyle(textAlign: unspecified, textDirection: TextDirection.ltr, fontWeight: unspecified, fontStyle: unspecified, maxLines: unspecified, fontFamily: unspecified, fontSize: 14.0, height: unspecified, ellipsis: unspecified, locale: unspecified)');
final ui.ParagraphStyle ps7 = const TextStyle().getParagraphStyle(textDirection: TextDirection.rtl);
expect(ps7, equals(ui.ParagraphStyle(textDirection: TextDirection.rtl, fontSize: 14.0)));
expect(ps7.toString(), 'ParagraphStyle(textAlign: unspecified, textDirection: TextDirection.rtl, fontWeight: unspecified, fontStyle: unspecified, maxLines: unspecified, fontFamily: unspecified, fontSize: 14.0, lineHeight: unspecified, ellipsis: unspecified, locale: unspecified)');
expect(ps7.toString(), 'ParagraphStyle(textAlign: unspecified, textDirection: TextDirection.rtl, fontWeight: unspecified, fontStyle: unspecified, maxLines: unspecified, fontFamily: unspecified, fontSize: 14.0, height: unspecified, ellipsis: unspecified, locale: unspecified)');
});
test('TextStyle using package font', () {
......
......@@ -220,4 +220,221 @@ void main() {
matchesGoldenFile('text_golden.Fade.1.png'),
);
}, skip: !Platform.isLinux);
testWidgets('Default Strut text', (WidgetTester tester) async {
await tester.pumpWidget(
Center(
child: RepaintBoundary(
child: Container(
width: 200.0,
height: 100.0,
decoration: const BoxDecoration(
color: Color(0xff00ff00),
),
child: const Text('Hello\nLine 2\nLine 3',
textDirection: TextDirection.ltr,
style: TextStyle(),
strutStyle: StrutStyle(),
),
),
),
),
);
await expectLater(
find.byType(Container),
matchesGoldenFile('text_golden.StrutDefault.png'),
);
}, skip: !Platform.isLinux);
testWidgets('Strut text 1', (WidgetTester tester) async {
await tester.pumpWidget(
Center(
child: RepaintBoundary(
child: Container(
width: 200.0,
height: 100.0,
decoration: const BoxDecoration(
color: Color(0xff00ff00),
),
child: const Text('Hello\nLine2\nLine3',
textDirection: TextDirection.ltr,
style: TextStyle(),
strutStyle: StrutStyle(
height: 1.5,
),
),
),
),
),
);
await expectLater(
find.byType(Container),
matchesGoldenFile('text_golden.Strut.1.png'),
);
}, skip: !Platform.isLinux);
testWidgets('Strut text 2', (WidgetTester tester) async {
await tester.pumpWidget(
Center(
child: RepaintBoundary(
child: Container(
width: 200.0,
height: 100.0,
decoration: const BoxDecoration(
color: Color(0xff00ff00),
),
child: const Text('Hello\nLine 2\nLine 3',
textDirection: TextDirection.ltr,
style: TextStyle(),
strutStyle: StrutStyle(
height: 1.5,
fontSize: 14,
),
),
),
),
),
);
await expectLater(
find.byType(Container),
matchesGoldenFile('text_golden.Strut.2.png'),
);
}, skip: !Platform.isLinux);
testWidgets('Strut text rich', (WidgetTester tester) async {
await tester.pumpWidget(
Center(
child: RepaintBoundary(
child: Container(
width: 200.0,
height: 150.0,
decoration: const BoxDecoration(
color: Color(0xff00ff00),
),
child: const Text.rich(
TextSpan(
text: 'Hello\n',
style: TextStyle(
color: Colors.red,
fontSize: 30
),
children: <TextSpan>[
TextSpan(
text: 'Second line!\n',
style: TextStyle(
fontSize: 5,
color: Colors.blue,
),
),
TextSpan(
text: 'Third line!\n',
style: TextStyle(
fontSize: 25,
color: Colors.white,
),
),
],
),
textDirection: TextDirection.ltr,
strutStyle: StrutStyle(
fontSize: 14,
height: 1.1,
leading: 0.1,
),
),
),
),
),
);
await expectLater(
find.byType(Container),
matchesGoldenFile('text_golden.Strut.3.png'),
);
}, skip: !Platform.isLinux);
testWidgets('Strut text font fallback', (WidgetTester tester) async {
// Font Fallback
await tester.pumpWidget(
Center(
child: RepaintBoundary(
child: Container(
width: 200.0,
height: 100.0,
decoration: const BoxDecoration(
color: Color(0xff00ff00),
),
child: const Text('Hello\nLine 2\nLine 3',
textDirection: TextDirection.ltr,
style: TextStyle(),
strutStyle: StrutStyle(
fontFamily: 'FakeFont 1',
fontFamilyFallback: <String>[
'FakeFont 2',
'EvilFont 3',
'Nice Font 4',
'ahem'
],
fontSize: 14,
),
),
),
),
),
);
await expectLater(
find.byType(Container),
matchesGoldenFile('text_golden.Strut.4.png'),
);
}, skip: !Platform.isLinux);
testWidgets('Strut text rich forceStrutHeight', (WidgetTester tester) async {
await tester.pumpWidget(
Center(
child: RepaintBoundary(
child: Container(
width: 200.0,
height: 100.0,
decoration: const BoxDecoration(
color: Color(0xff00ff00),
),
child: const Text.rich(
TextSpan(
text: 'Hello\n',
style: TextStyle(
color: Colors.red,
fontSize: 30
),
children: <TextSpan>[
TextSpan(
text: 'Second line!\n',
style: TextStyle(
fontSize: 9,
color: Colors.blue,
),
),
TextSpan(
text: 'Third line!\n',
style: TextStyle(
fontSize: 27,
color: Colors.white,
),
),
],
),
textDirection: TextDirection.ltr,
strutStyle: StrutStyle(
fontSize: 14,
height: 1.1,
forceStrutHeight: true,
),
),
),
),
),
);
await expectLater(
find.byType(Container),
matchesGoldenFile('text_golden.StrutForce.1.png'),
);
}, skip: !Platform.isLinux);
}
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