From 96cb84a18c7629751b1283cbaf3ab8a06ff018cb Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Wed, 13 Mar 2019 11:52:47 -0700 Subject: [PATCH] Expose decorationThickness in TextStyle. Roll engine (12 commits) (#28751) 31b289f27 Fix indexing error in dart:ui TextStyle.toString (flutter/engine#8143) fc2e6b61c Typo "fast an inline" to "fast and inline" (flutter/engine#8142) 0f19b2de0 Reland PerformanceOverlayLayer golden test (flutter/engine#8140) 073aadde1 Fix TextStyle decode misalignment (flutter/engine#8141) d87d29054 Roll src/third_party/skia 406b068942f0..2eecc3ea3d71 (11 commits) (flutter/engine#8138) 5cef4a022 Use final state passed to dart before initialization as the initial lifecycleState. (flutter/engine#8124) ffef51be6 Roll src/third_party/skia 665bc64a2dc4..406b068942f0 (8 commits) (flutter/engine#8137) 48efd0fb7 Roll src/third_party/skia 762ddd7e4352..665bc64a2dc4 (2 commits) (flutter/engine#8129) f666adbbd Roll src/third_party/skia 2932a458957d..762ddd7e4352 (3 commits) (flutter/engine#8128) 8b0df6ded Bugfix #29203: NPE in getAccessibilityProvider in old FlutterView. (flutter/engine#8126) 8f7b18345 Roll src/third_party/skia c6d8781c4036..2932a458957d (2 commits) (flutter/engine#8125) 52b67fdd5 Expose decorationThickness to dart:ui (flutter/engine#8008) --- bin/internal/engine.version | 2 +- .../flutter/lib/src/painting/text_style.dart | 68 +++++++++++++++- .../flutter/test/material/theme_test.dart | 62 ++++++++++++++- .../test/painting/text_style_test.dart | 10 +-- .../test/widgets/text_golden_test.dart | 77 +++++++++++++++++++ 5 files changed, 208 insertions(+), 11 deletions(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 2eb1d563179..be0cfa472bd 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -5ccee95373348854bc4877cfe240024a36847d2d +31b289f277c653ea91e20d75309958ac0dd0b865 diff --git a/packages/flutter/lib/src/painting/text_style.dart b/packages/flutter/lib/src/painting/text_style.dart index 1ea4d523bc0..0a928fda787 100644 --- a/packages/flutter/lib/src/painting/text_style.dart +++ b/packages/flutter/lib/src/painting/text_style.dart @@ -317,6 +317,7 @@ class TextStyle extends Diagnosticable { this.decoration, this.decorationColor, this.decorationStyle, + this.decorationThickness, this.debugLabel, String fontFamily, List fontFamilyFallback, @@ -482,6 +483,8 @@ class TextStyle extends Diagnosticable { final Paint background; /// The decorations to paint near the text (e.g., an underline). + /// + /// Multiple decorations can be applied using [TextDecoration.combine]. final TextDecoration decoration; /// The color in which to paint the text decorations. @@ -490,6 +493,51 @@ class TextStyle extends Diagnosticable { /// The style in which to paint the text decorations (e.g., dashed). final TextDecorationStyle decorationStyle; + /// The thickness of the decoration stroke as a muliplier of the thickness + /// defined by the font. + /// + /// The font provides a base stroke width for [decoration]s which scales off + /// of the [fontSize]. This property may be used to achieve a thinner or + /// thicker decoration stroke, without changing the [fontSize]. For example, + /// a [decorationThickness] of 2.0 will draw a decoration twice as thick as + /// the font defined decoration thickness. + /// + /// {@tool sample} + /// To achieve a bolded strike-through, we can apply a thicker stroke for the + /// decoration. + /// + /// ```dart + /// Text( + /// 'This has a very BOLD strike through!', + /// style: TextStyle( + /// decoration: TextDecoration.lineThrough, + /// decorationThickness: 2.85, + /// ), + /// ) + /// ``` + /// {@end-tool} + /// + /// {@tool sample} + /// We can apply a very thin and subtle wavy underline (perhaps, when words + /// are misspelled) by using a [decorationThickness] < 1.0. + /// + /// ```dart + /// Text( + /// 'oopsIforgottousespaces!', + /// style: TextStyle( + /// decoration: TextDecoration.underline, + /// decorationStyle: TextDecorationStyle.wavy, + /// decorationColor: Colors.red, + /// decorationThickness: 0.5, + /// ), + /// ) + /// ``` + /// {@end-tool} + /// + /// The default [decorationThickness] is 1.0, which will use the font's base + /// stroke thickness/width. + final double decorationThickness; + /// A human-readable description of this text style. /// /// This property is maintained only in debug builds. @@ -541,6 +589,7 @@ class TextStyle extends Diagnosticable { TextDecoration decoration, Color decorationColor, TextDecorationStyle decorationStyle, + double decorationThickness, String debugLabel, }) { assert(color == null || foreground == null, _kColorForegroundWarning); @@ -571,6 +620,7 @@ class TextStyle extends Diagnosticable { decoration: decoration ?? this.decoration, decorationColor: decorationColor ?? this.decorationColor, decorationStyle: decorationStyle ?? this.decorationStyle, + decorationThickness: decorationThickness ?? this.decorationThickness, debugLabel: newDebugLabel, ); } @@ -610,6 +660,8 @@ class TextStyle extends Diagnosticable { TextDecoration decoration, Color decorationColor, TextDecorationStyle decorationStyle, + double decorationThicknessFactor = 1.0, + double decorationThicknessDelta = 0.0, String fontFamily, List fontFamilyFallback, double fontSizeFactor = 1.0, @@ -636,6 +688,9 @@ class TextStyle extends Diagnosticable { assert(heightFactor != null); assert(heightDelta != null); assert(heightFactor != null || (heightFactor == 1.0 && heightDelta == 0.0)); + assert(decorationThicknessFactor != null); + assert(decorationThicknessDelta != null); + assert(decorationThickness != null || (decorationThicknessFactor == 1.0 && decorationThicknessDelta == 0.0)); String modifiedDebugLabel; assert(() { @@ -664,6 +719,7 @@ class TextStyle extends Diagnosticable { decoration: decoration ?? this.decoration, decorationColor: decorationColor ?? this.decorationColor, decorationStyle: decorationStyle ?? this.decorationStyle, + decorationThickness: decorationThickness == null ? null : decorationThickness * decorationThicknessFactor + decorationThicknessDelta, debugLabel: modifiedDebugLabel, ); } @@ -721,6 +777,7 @@ class TextStyle extends Diagnosticable { decoration: other.decoration, decorationColor: other.decorationColor, decorationStyle: other.decorationStyle, + decorationThickness: other.decorationThickness, debugLabel: mergedDebugLabel, ); } @@ -772,6 +829,7 @@ class TextStyle extends Diagnosticable { shadows: t < 0.5 ? null : b.shadows, decorationColor: Color.lerp(null, b.decorationColor, t), decorationStyle: t < 0.5 ? null : b.decorationStyle, + decorationThickness: t < 0.5 ? null : b.decorationThickness, debugLabel: lerpDebugLabel, ); } @@ -797,6 +855,7 @@ class TextStyle extends Diagnosticable { decoration: t < 0.5 ? a.decoration : null, decorationColor: Color.lerp(a.decorationColor, null, t), decorationStyle: t < 0.5 ? a.decorationStyle : null, + decorationThickness: t < 0.5 ? a.decorationThickness : null, debugLabel: lerpDebugLabel, ); } @@ -829,6 +888,7 @@ class TextStyle extends Diagnosticable { decoration: t < 0.5 ? a.decoration : b.decoration, decorationColor: Color.lerp(a.decorationColor, b.decorationColor, t), decorationStyle: t < 0.5 ? a.decorationStyle : b.decorationStyle, + decorationThickness: ui.lerpDouble(a.decorationThickness ?? b.decorationThickness, b.decorationThickness ?? a.decorationThickness, t), debugLabel: lerpDebugLabel, ); } @@ -840,6 +900,7 @@ class TextStyle extends Diagnosticable { decoration: decoration, decorationColor: decorationColor, decorationStyle: decorationStyle, + decorationThickness: decorationThickness, fontWeight: fontWeight, fontStyle: fontStyle, textBaseline: textBaseline, @@ -937,7 +998,8 @@ class TextStyle extends Diagnosticable { backgroundColor != other.backgroundColor || decoration != other.decoration || decorationColor != other.decorationColor || - decorationStyle != other.decorationStyle) + decorationStyle != other.decorationStyle || + decorationThickness != other.decorationThickness) return RenderComparison.paint; return RenderComparison.identical; } @@ -966,6 +1028,7 @@ class TextStyle extends Diagnosticable { decoration == typedOther.decoration && decorationColor == typedOther.decorationColor && decorationStyle == typedOther.decorationStyle && + decorationThickness == typedOther.decorationThickness && listEquals(shadows, typedOther.shadows) && listEquals(fontFamilyFallback, typedOther.fontFamilyFallback); } @@ -1031,7 +1094,7 @@ class TextStyle extends Diagnosticable { styles.add(DiagnosticsProperty('${prefix}locale', locale, defaultValue: null)); styles.add(DiagnosticsProperty('${prefix}foreground', foreground, defaultValue: null)); styles.add(DiagnosticsProperty('${prefix}background', background, defaultValue: null)); - if (decoration != null || decorationColor != null || decorationStyle != null) { + if (decoration != null || decorationColor != null || decorationStyle != null || decorationThickness != null) { final List decorationDescription = []; if (decorationStyle != null) decorationDescription.add(describeEnum(decorationStyle)); @@ -1051,6 +1114,7 @@ class TextStyle extends Diagnosticable { decorationDescription.add('$decoration'); assert(decorationDescription.isNotEmpty); styles.add(MessageProperty('${prefix}decoration', decorationDescription.join(' '))); + styles.add(DoubleProperty('${prefix}decorationThickness', decorationThickness, unit: 'x', defaultValue: null)); } final bool styleSpecified = styles.any((DiagnosticsNode n) => !n.isFiltered(DiagnosticLevel.info)); diff --git a/packages/flutter/test/material/theme_test.dart b/packages/flutter/test/material/theme_test.dart index 0c7996f40e6..54fe5ca9b1f 100644 --- a/packages/flutter/test/material/theme_test.dart +++ b/packages/flutter/test/material/theme_test.dart @@ -678,6 +678,8 @@ class _TextStyleProxy implements TextStyle { @override TextDecorationStyle get decorationStyle => _delegate.decorationStyle; @override + double get decorationThickness => _delegate.decorationThickness; + @override String get fontFamily => _delegate.fontFamily; @override List get fontFamilyFallback => _delegate.fontFamilyFallback; @@ -721,7 +723,26 @@ class _TextStyleProxy implements TextStyle { } @override - TextStyle apply({ Color color, Color backgroundColor, TextDecoration decoration, Color decorationColor, TextDecorationStyle decorationStyle, String fontFamily, List fontFamilyFallback, double fontSizeFactor = 1.0, double fontSizeDelta = 0.0, int fontWeightDelta = 0, double letterSpacingFactor = 1.0, double letterSpacingDelta = 0.0, double wordSpacingFactor = 1.0, double wordSpacingDelta = 0.0, double heightFactor = 1.0, double heightDelta = 0.0 }) { + TextStyle apply({ + Color color, + Color backgroundColor, + TextDecoration decoration, + Color decorationColor, + TextDecorationStyle decorationStyle, + double decorationThicknessFactor = 1.0, + double decorationThicknessDelta = 0.0, + String fontFamily, + List fontFamilyFallback, + double fontSizeFactor = 1.0, + double fontSizeDelta = 0.0, + int fontWeightDelta = 0, + double letterSpacingFactor = 1.0, + double letterSpacingDelta = 0.0, + double wordSpacingFactor = 1.0, + double wordSpacingDelta = 0.0, + double heightFactor = 1.0, + double heightDelta = 0.0 + }) { throw UnimplementedError(); } @@ -731,7 +752,29 @@ class _TextStyleProxy implements TextStyle { } @override - TextStyle copyWith({ bool inherit, Color color, Color backgroundColor, String fontFamily, List fontFamilyFallback, double fontSize, FontWeight fontWeight, FontStyle fontStyle, double letterSpacing, double wordSpacing, TextBaseline textBaseline, double height, Locale locale, ui.Paint foreground, ui.Paint background, List shadows, TextDecoration decoration, Color decorationColor, TextDecorationStyle decorationStyle, String debugLabel }) { + TextStyle copyWith({ + bool inherit, + Color color, + Color backgroundColor, + String fontFamily, + List fontFamilyFallback, + double fontSize, + FontWeight fontWeight, + FontStyle fontStyle, + double letterSpacing, + double wordSpacing, + TextBaseline textBaseline, + double height, + Locale locale, + ui.Paint foreground, + ui.Paint background, + List shadows, + TextDecoration decoration, + Color decorationColor, + TextDecorationStyle decorationStyle, + double decorationThickness, + String debugLabel + }) { throw UnimplementedError(); } @@ -741,7 +784,20 @@ class _TextStyleProxy implements TextStyle { } @override - 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 }) { + 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(); } diff --git a/packages/flutter/test/painting/text_style_test.dart b/packages/flutter/test/painting/text_style_test.dart index db35550606b..51b3f7cd855 100644 --- a/packages/flutter/test/painting/text_style_test.dart +++ b/packages/flutter/test/painting/text_style_test.dart @@ -169,10 +169,10 @@ void main() { final ui.TextStyle ts5 = s5.getTextStyle(); expect(ts5, equals(ui.TextStyle(fontWeight: FontWeight.w700, fontSize: 12.0, height: 123.0))); - expect(ts5.toString(), 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: 12.0, letterSpacing: unspecified, wordSpacing: unspecified, height: 123.0x, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified)'); + expect(ts5.toString(), 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: 12.0, letterSpacing: unspecified, wordSpacing: unspecified, height: 123.0x, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified)'); final ui.TextStyle ts2 = s2.getTextStyle(); expect(ts2, equals(ui.TextStyle(color: const Color(0xFF00FF00), fontWeight: FontWeight.w800, fontSize: 10.0, height: 100.0))); - 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)'); + expect(ts2.toString(), 'TextStyle(color: Color(0xff00ff00), decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: 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, height: 100.0))); @@ -196,11 +196,11 @@ void main() { test('TextStyle using package font', () { const TextStyle s6 = TextStyle(fontFamily: 'test'); expect(s6.fontFamily, 'test'); - expect(s6.getTextStyle().toString(), 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: test, fontFamilyFallback: unspecified, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified)'); + expect(s6.getTextStyle().toString(), 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: test, fontFamilyFallback: unspecified, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified)'); const TextStyle s7 = TextStyle(fontFamily: 'test', package: 'p'); expect(s7.fontFamily, 'packages/p/test'); - expect(s7.getTextStyle().toString(), 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: packages/p/test, fontFamilyFallback: unspecified, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified)'); + expect(s7.getTextStyle().toString(), 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: packages/p/test, fontFamilyFallback: unspecified, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified)'); const TextStyle s8 = TextStyle(fontFamilyFallback: ['test', 'test2'], package: 'p'); expect(s8.fontFamilyFallback[0], 'packages/p/test'); @@ -236,7 +236,7 @@ void main() { expect(s4.fontFamilyFallback.isEmpty, true); final ui.TextStyle uis1 = s2.getTextStyle(); - expect(uis1.toString(), 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: foo, fontFamilyFallback: [Roboto, test], fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified)'); + expect(uis1.toString(), 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: foo, fontFamilyFallback: [Roboto, test], fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified)'); }); test('TextStyle.debugLabel', () { diff --git a/packages/flutter/test/widgets/text_golden_test.dart b/packages/flutter/test/widgets/text_golden_test.dart index 9f787952d15..5781c65aa1c 100644 --- a/packages/flutter/test/widgets/text_golden_test.dart +++ b/packages/flutter/test/widgets/text_golden_test.dart @@ -443,4 +443,81 @@ void main() { ); }, skip: true); // Should only be on linux (skip: !Platform.isLinux). // Disabled for now until font inconsistency is resolved. + + testWidgets('Decoration thickness', (WidgetTester tester) async { + final TextDecoration allDecorations = TextDecoration.combine( + [ + TextDecoration.underline, + TextDecoration.overline, + TextDecoration.lineThrough, + ] + ); + + await tester.pumpWidget( + Center( + child: RepaintBoundary( + child: Container( + width: 300.0, + height: 100.0, + decoration: const BoxDecoration( + color: Color(0xff00ff00), + ), + child: Text( + 'Hello, wor!\nabcd.', + style: TextStyle( + fontSize: 25, + decoration: allDecorations, + decorationColor: Colors.blue, + decorationStyle: TextDecorationStyle.dashed, + ), + textDirection: TextDirection.rtl, + ), + ), + ), + ), + ); + await expectLater( + find.byType(Container), + matchesGoldenFile('text_golden.Decoration.1.0.png'), + ); + }, skip: !Platform.isLinux); // Coretext uses different thicknesses for decoration + + testWidgets('Decoration thickness', (WidgetTester tester) async { + final TextDecoration allDecorations = TextDecoration.combine( + [ + TextDecoration.underline, + TextDecoration.overline, + TextDecoration.lineThrough, + ] + ); + + await tester.pumpWidget( + Center( + child: RepaintBoundary( + child: Container( + width: 300.0, + height: 100.0, + decoration: const BoxDecoration( + color: Color(0xff00ff00), + ), + child: Text( + 'Hello, wor!\nabcd.', + style: TextStyle( + fontSize: 25, + decoration: allDecorations, + decorationColor: Colors.blue, + decorationStyle: TextDecorationStyle.wavy, + decorationThickness: 4, + ), + textDirection: TextDirection.rtl, + ), + ), + ), + ), + ); + await expectLater( + find.byType(Container), + matchesGoldenFile('text_golden.DecorationThickness.1.0.png'), + ); + }, skip: !Platform.isLinux); // Coretext uses different thicknesses for decoration }