[web] Implement style inheritance during paragraph construction (#22365)

This commit is contained in:
Mouad Debbar 2020-11-06 15:44:31 -08:00 committed by GitHub
parent 74472ce6da
commit 10f06b5abf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 266 additions and 62 deletions

View File

@ -300,6 +300,26 @@ abstract class StyleNode {
/// The resolved text style is equivalent to the entire ascendent chain of
/// parent style nodes.
EngineTextStyle resolveStyle();
ui.Color? get _color;
ui.TextDecoration? get _decoration;
ui.Color? get _decorationColor;
ui.TextDecorationStyle? get _decorationStyle;
double? get _decorationThickness;
ui.FontWeight? get _fontWeight;
ui.FontStyle? get _fontStyle;
ui.TextBaseline? get _textBaseline;
String? get _fontFamily;
List<String>? get _fontFamilyFallback;
List<ui.FontFeature>? get _fontFeatures;
double? get _fontSize;
double? get _letterSpacing;
double? get _wordSpacing;
double? get _height;
ui.Locale? get _locale;
ui.Paint? get _background;
ui.Paint? get _foreground;
List<ui.Shadow>? get _shadows;
}
/// Represents a non-root [StyleNode].
@ -315,9 +335,91 @@ class ChildStyleNode extends StyleNode {
@override
EngineTextStyle resolveStyle() {
// TODO(mdebbar): combine all styles from the parent hierarchy.
return style;
return EngineTextStyle(
color: _color,
decoration: _decoration,
decorationColor: _decorationColor,
decorationStyle: _decorationStyle,
decorationThickness: _decorationThickness,
fontWeight: _fontWeight,
fontStyle: _fontStyle,
textBaseline: _textBaseline,
fontFamily: _fontFamily,
fontFamilyFallback: _fontFamilyFallback,
fontFeatures: _fontFeatures,
fontSize: _fontSize,
letterSpacing: _letterSpacing,
wordSpacing: _wordSpacing,
height: _height,
locale: _locale,
background: _background,
foreground: _foreground,
shadows: _shadows,
);
}
// Read these properties from the TextStyle associated with this node. If the
// property isn't defined, go to the parent node.
@override
ui.Color? get _color => style._color ?? parent._color;
@override
ui.TextDecoration? get _decoration => style._decoration ?? parent._decoration;
@override
ui.Color? get _decorationColor => style._decorationColor ?? parent._decorationColor;
@override
ui.TextDecorationStyle? get _decorationStyle => style._decorationStyle ?? parent._decorationStyle;
@override
double? get _decorationThickness => style._decorationThickness ?? parent._decorationThickness;
@override
ui.FontWeight? get _fontWeight => style._fontWeight ?? parent._fontWeight;
@override
ui.FontStyle? get _fontStyle => style._fontStyle ?? parent._fontStyle;
@override
ui.TextBaseline? get _textBaseline => style._textBaseline ?? parent._textBaseline;
@override
List<String>? get _fontFamilyFallback => style._fontFamilyFallback ?? parent._fontFamilyFallback;
@override
List<ui.FontFeature>? get _fontFeatures => style._fontFeatures ?? parent._fontFeatures;
@override
double? get _fontSize => style._fontSize ?? parent._fontSize;
@override
double? get _letterSpacing => style._letterSpacing ?? parent._letterSpacing;
@override
double? get _wordSpacing => style._wordSpacing ?? parent._wordSpacing;
@override
double? get _height => style._height ?? parent._height;
@override
ui.Locale? get _locale => style._locale ?? parent._locale;
@override
ui.Paint? get _background => style._background ?? parent._background;
@override
ui.Paint? get _foreground => style._foreground ?? parent._foreground;
@override
List<ui.Shadow>? get _shadows => style._shadows ?? parent._shadows;
// Font family is slightly different from the other properties above. It's
// never null on the TextStyle object, so we use `_isFontFamilyProvided` to
// check if font family is defined or not.
@override
String? get _fontFamily => style._isFontFamilyProvided ? style._fontFamily : parent._fontFamily;
}
/// The root style node for the paragraph.
@ -342,6 +444,62 @@ class RootStyleNode extends StyleNode {
}
return style;
}
@override
ui.Color? get _color => null;
@override
ui.TextDecoration? get _decoration => null;
@override
ui.Color? get _decorationColor => null;
@override
ui.TextDecorationStyle? get _decorationStyle => null;
@override
double? get _decorationThickness => null;
@override
ui.FontWeight? get _fontWeight => paragraphStyle._fontWeight;
@override
ui.FontStyle? get _fontStyle => paragraphStyle._fontStyle;
@override
ui.TextBaseline? get _textBaseline => null;
@override
String? get _fontFamily => paragraphStyle._fontFamily;
@override
List<String>? get _fontFamilyFallback => null;
@override
List<ui.FontFeature>? get _fontFeatures => null;
@override
double? get _fontSize => paragraphStyle._fontSize;
@override
double? get _letterSpacing => null;
@override
double? get _wordSpacing => null;
@override
double? get _height => paragraphStyle._height;
@override
ui.Locale? get _locale => paragraphStyle._locale;
@override
ui.Paint? get _background => null;
@override
ui.Paint? get _foreground => null;
@override
List<ui.Shadow>? get _shadows => null;
}
/// Builds a [CanvasParagraph] containing text with the given styling

View File

@ -894,7 +894,38 @@ class EngineParagraphStyle implements ui.ParagraphStyle {
/// The web implementation of [ui.TextStyle].
class EngineTextStyle implements ui.TextStyle {
EngineTextStyle({
/// Constructs an [EngineTextStyle] with all properties being required.
///
/// This is good for call sites that need to be updated whenever a new
/// property is added to [EngineTextStyle]. Non-updated call sites will fail
/// the build otherwise.
factory EngineTextStyle({
required ui.Color? color,
required ui.TextDecoration? decoration,
required ui.Color? decorationColor,
required ui.TextDecorationStyle? decorationStyle,
required double? decorationThickness,
required ui.FontWeight? fontWeight,
required ui.FontStyle? fontStyle,
required ui.TextBaseline? textBaseline,
required String? fontFamily,
required List<String>? fontFamilyFallback,
required double? fontSize,
required double? letterSpacing,
required double? wordSpacing,
required double? height,
required ui.Locale? locale,
required ui.Paint? background,
required ui.Paint? foreground,
required List<ui.Shadow>? shadows,
required List<ui.FontFeature>? fontFeatures,
}) = EngineTextStyle.only;
/// Constructs an [EngineTextStyle] with only the given properties.
///
/// This constructor should be used sparingly in tests, for example. Or when
/// we know for sure that not all properties are needed.
EngineTextStyle.only({
ui.Color? color,
ui.TextDecoration? decoration,
ui.Color? decorationColor,
@ -940,14 +971,20 @@ class EngineTextStyle implements ui.TextStyle {
_foreground = foreground,
_shadows = shadows;
factory EngineTextStyle.fromParagraphStyle(EngineParagraphStyle paragraphStyle) => EngineTextStyle(
fontWeight: paragraphStyle._fontWeight,
fontStyle: paragraphStyle._fontStyle,
fontFamily: paragraphStyle._fontFamily,
fontSize: paragraphStyle._fontSize,
height: paragraphStyle._height,
locale: paragraphStyle._locale,
);
/// Constructs an [EngineTextStyle] by reading properties from an
/// [EngineParagraphStyle].
factory EngineTextStyle.fromParagraphStyle(
EngineParagraphStyle paragraphStyle,
) {
return EngineTextStyle.only(
fontWeight: paragraphStyle._fontWeight,
fontStyle: paragraphStyle._fontStyle,
fontFamily: paragraphStyle._fontFamily,
fontSize: paragraphStyle._fontSize,
height: paragraphStyle._height,
locale: paragraphStyle._locale,
);
}
final ui.Color? _color;
final ui.TextDecoration? _decoration;
@ -1281,10 +1318,13 @@ class DomParagraphBuilder implements ui.ParagraphBuilder {
ui.TextDecoration? decoration;
ui.Color? decorationColor;
ui.TextDecorationStyle? decorationStyle;
double? decorationThickness;
ui.FontWeight? fontWeight = _paragraphStyle._fontWeight;
ui.FontStyle? fontStyle = _paragraphStyle._fontStyle;
ui.TextBaseline? textBaseline;
String fontFamily = _paragraphStyle._fontFamily ?? DomRenderer.defaultFontFamily;
List<String>? fontFamilyFallback;
List<ui.FontFeature>? fontFeatures;
double fontSize = _paragraphStyle._fontSize ?? DomRenderer.defaultFontSize;
final ui.TextAlign textAlign = _paragraphStyle._effectiveTextAlign;
final ui.TextDirection textDirection = _paragraphStyle._effectiveTextDirection;
@ -1316,6 +1356,9 @@ class DomParagraphBuilder implements ui.ParagraphBuilder {
if (style._decorationStyle != null) {
decorationStyle = style._decorationStyle;
}
if (style._decorationThickness != null) {
decorationThickness = style._decorationThickness;
}
if (style._fontWeight != null) {
fontWeight = style._fontWeight;
}
@ -1326,6 +1369,12 @@ class DomParagraphBuilder implements ui.ParagraphBuilder {
textBaseline = style._textBaseline;
}
fontFamily = style._fontFamily;
if (style._fontFamilyFallback != null) {
fontFamilyFallback = style._fontFamilyFallback;
}
if (style._fontFeatures != null) {
fontFeatures = style._fontFeatures;
}
if (style._fontSize != null) {
fontSize = style._fontSize!;
}
@ -1358,10 +1407,13 @@ class DomParagraphBuilder implements ui.ParagraphBuilder {
decoration: decoration,
decorationColor: decorationColor,
decorationStyle: decorationStyle,
decorationThickness: decorationThickness,
fontWeight: fontWeight,
fontStyle: fontStyle,
textBaseline: textBaseline,
fontFamily: fontFamily,
fontFamilyFallback: fontFamilyFallback,
fontFeatures: fontFeatures,
fontSize: fontSize,
letterSpacing: letterSpacing,
wordSpacing: wordSpacing,

View File

@ -35,7 +35,7 @@ void testMain() {
expect(span, isA<FlatTextSpan>());
final FlatTextSpan textSpan = span as FlatTextSpan;
expect(textSpan.text, 'Hello');
expect(textSpan.style, EngineTextStyle(fontSize: 13.0));
expect(textSpan.style, TextStyle(fontSize: 13.0));
});
test('Builds a single-span paragraph with complex styles', () {
@ -57,17 +57,16 @@ void testMain() {
final FlatTextSpan span = paragraph.spans.single as FlatTextSpan;
expect(span.text, 'Hello');
// TODO(mdebbar): Uncomment this once style resolution is implemented.
// expect(
// span.style,
// TextStyle(
// height: 1.5,
// fontSize: 9.0,
// fontWeight: FontWeight.bold,
// fontStyle: FontStyle.italic,
// letterSpacing: 2.0,
// ),
// );
expect(
span.style,
TextStyle(
height: 1.5,
fontSize: 9.0,
fontWeight: FontWeight.bold,
fontStyle: FontStyle.italic,
letterSpacing: 2.0,
),
);
});
test('Builds a multi-span paragraph', () {
@ -86,25 +85,23 @@ void testMain() {
final FlatTextSpan hello = paragraph.spans.first as FlatTextSpan;
expect(hello.text, 'Hello');
// TODO(mdebbar): Uncomment this once style resolution is implemented.
// expect(
// hello.style,
// TextStyle(
// fontSize: 13.0,
// fontWeight: FontWeight.bold,
// ),
// );
expect(
hello.style,
TextStyle(
fontSize: 13.0,
fontWeight: FontWeight.bold,
),
);
final FlatTextSpan world = paragraph.spans.last as FlatTextSpan;
expect(world.text, ' world');
// TODO(mdebbar): Uncomment this once style resolution is implemented.
// expect(
// world.style,
// TextStyle(
// fontSize: 13.0,
// fontStyle: FontStyle.italic,
// ),
// );
expect(
world.style,
TextStyle(
fontSize: 13.0,
fontStyle: FontStyle.italic,
),
);
});
test('Builds a multi-span paragraph with complex styles', () {
@ -126,34 +123,31 @@ void testMain() {
final FlatTextSpan hello = paragraph.spans[0] as FlatTextSpan;
expect(hello.text, 'Hello');
// TODO(mdebbar): Uncomment this once style resolution is implemented.
// expect(
// hello.style,
// TextStyle(fontSize: 13.0, fontWeight: FontWeight.bold, height: 2.0),
// );
expect(
hello.style,
TextStyle(fontSize: 13.0, fontWeight: FontWeight.bold, height: 2.0),
);
final FlatTextSpan world = paragraph.spans[1] as FlatTextSpan;
expect(world.text, ' world');
// TODO(mdebbar): Uncomment this once style resolution is implemented.
// expect(
// world.style,
// TextStyle(
// fontSize: 13.0,
// fontWeight: FontWeight.bold,
// fontStyle: FontStyle.italic,
// ),
// );
expect(
world.style,
TextStyle(
fontSize: 13.0,
fontWeight: FontWeight.bold,
fontStyle: FontStyle.italic,
),
);
final FlatTextSpan bang = paragraph.spans[2] as FlatTextSpan;
expect(bang.text, '!');
// TODO(mdebbar): Uncomment this once style resolution is implemented.
// expect(
// bang.style,
// TextStyle(
// fontSize: 13.0,
// fontWeight: FontWeight.normal,
// fontStyle: FontStyle.italic,
// ),
// );
expect(
bang.style,
TextStyle(
fontSize: 13.0,
fontWeight: FontWeight.normal,
fontStyle: FontStyle.italic,
),
);
});
}