From c68c59d8b1ba4827c00dea03497d95b5faa722f5 Mon Sep 17 00:00:00 2001 From: Matthew Whitaker Date: Sat, 8 Feb 2020 14:02:57 -0700 Subject: [PATCH] Add additional CSS styles --- example/ios/Flutter/Flutter.podspec | 18 +++++ example/lib/main.dart | 2 +- lib/html_parser.dart | 53 ++++++++++--- lib/src/layout_element.dart | 5 +- lib/src/styled_element.dart | 4 +- lib/style.dart | 118 ++++++++++++++++++++++++++-- test/html_parser_test.dart | 4 +- 7 files changed, 176 insertions(+), 28 deletions(-) create mode 100644 example/ios/Flutter/Flutter.podspec diff --git a/example/ios/Flutter/Flutter.podspec b/example/ios/Flutter/Flutter.podspec new file mode 100644 index 0000000000..5ca30416ba --- /dev/null +++ b/example/ios/Flutter/Flutter.podspec @@ -0,0 +1,18 @@ +# +# NOTE: This podspec is NOT to be published. It is only used as a local source! +# + +Pod::Spec.new do |s| + s.name = 'Flutter' + s.version = '1.0.0' + s.summary = 'High-performance, high-fidelity mobile apps.' + s.description = <<-DESC +Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS. + DESC + s.homepage = 'https://flutter.io' + s.license = { :type => 'MIT' } + s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } + s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } + s.ios.deployment_target = '8.0' + s.vendored_frameworks = 'Flutter.framework' +end diff --git a/example/lib/main.dart b/example/lib/main.dart index 17023a4f65..b8f958f93d 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -46,7 +46,7 @@ const htmlData = """

Support for sub/sup

Solve for xn: log2(x2+n) = 93 -

One of the most common equations in all of physics is E=mc2.

+

One of the most common equations in all of physics is E=mc2.

Table support:

diff --git a/lib/html_parser.dart b/lib/html_parser.dart index 15f59a421e..c8a6e87876 100644 --- a/lib/html_parser.dart +++ b/lib/html_parser.dart @@ -1,16 +1,16 @@ import 'dart:collection'; import 'dart:math'; +import 'package:csslib/parser.dart' as cssparser; +import 'package:csslib/visitor.dart' as css; +import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; +import 'package:flutter_html/src/html_elements.dart'; import 'package:flutter_html/src/layout_element.dart'; import 'package:flutter_html/src/utils.dart'; import 'package:flutter_html/style.dart'; -import 'package:flutter/material.dart'; -import 'package:csslib/visitor.dart' as css; import 'package:html/dom.dart' as dom; -import 'package:flutter_html/src/html_elements.dart'; import 'package:html/parser.dart' as htmlparser; -import 'package:csslib/parser.dart' as cssparser; typedef OnTap = void Function(String url); typedef CustomRender = Widget Function( @@ -67,7 +67,7 @@ class HtmlParser extends StatelessWidget { cleanedTree, ); - return RichText(text: parsedTree); + return StyledText(textSpan: parsedTree, style: cleanedTree.style); } /// [parseHTML] converts a string of HTML to a DOM document using the dart `html` library. @@ -281,14 +281,15 @@ class HtmlParser extends StatelessWidget { Padding( padding: EdgeInsets.only( left: 30), //TODO derive this from list padding. - child: RichText( - text: TextSpan( + child: StyledText( + textSpan: TextSpan( children: tree.children ?.map((tree) => parseTree(newContext, tree)) ?.toList() ?? [], style: newContext.style.generateTextStyle(), ), + style: newContext.style, ), ) ], @@ -317,14 +318,15 @@ class HtmlParser extends StatelessWidget { }, ), }, - child: RichText( - text: TextSpan( + child: StyledText( + textSpan: TextSpan( style: newContext.style.generateTextStyle(), children: tree.children .map((tree) => parseTree(newContext, tree)) .toList() ?? [], ), + style: newContext.style, ), ), ); @@ -349,14 +351,15 @@ class HtmlParser extends StatelessWidget { return WidgetSpan( child: Transform.translate( offset: Offset(0, verticalOffset), - child: RichText( - text: TextSpan( + child: StyledText( + textSpan: TextSpan( style: newContext.style.generateTextStyle(), children: tree.children .map((tree) => parseTree(newContext, tree)) .toList() ?? [], ), + style: newContext.style, ), ), ); @@ -690,12 +693,36 @@ class ContainerSpan extends StatelessWidget { margin: style?.margin, alignment: shrinkWrap ? null : style?.alignment, child: child ?? - RichText( - text: TextSpan( + StyledText( + textSpan: TextSpan( style: newContext.style.generateTextStyle(), children: children, ), + style: newContext.style, ), ); } } + +class StyledText extends StatelessWidget { + final InlineSpan textSpan; + final Style style; + + const StyledText({ + this.textSpan, + this.style, + }); + + @override + Widget build(BuildContext context) { + return SizedBox( + width: style.display == Display.BLOCK || style.display == Display.LIST_ITEM? double.infinity: null, + child: Text.rich( + textSpan, + style: style.generateTextStyle(), + textAlign: style.textAlign, + textDirection: style.direction, + ), + ); + } +} diff --git a/lib/src/layout_element.dart b/lib/src/layout_element.dart index 82bc202350..d3c652f929 100644 --- a/lib/src/layout_element.dart +++ b/lib/src/layout_element.dart @@ -125,8 +125,9 @@ class TableRowLayoutElement extends LayoutElement { color: c.style.backgroundColor, border: c.style.border, ), - child: RichText( - text: context.parser.parseTree(context, c), + child: StyledText( + textSpan: context.parser.parseTree(context, c), + style: c.style, ))); } return null; diff --git a/lib/src/styled_element.dart b/lib/src/styled_element.dart index 8c44da4370..aaa0dc90e9 100644 --- a/lib/src/styled_element.dart +++ b/lib/src/styled_element.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_html/style.dart'; import 'package:html/dom.dart' as dom; -//TODO(Sub6Resources) don't use the internal code of the html package as it may change unexpectedly. +//TODO(Sub6Resources): don't use the internal code of the html package as it may change unexpectedly. import 'package:html/src/query_selector.dart'; /// A [StyledElement] applies a style to all of its children. @@ -85,7 +85,7 @@ StyledElement parseStyledElement( ? TextDirection.rtl : TextDirection.ltr; styledElement.style = Style( - textDirection: textDirection, + direction: textDirection, ); break; case "big": diff --git a/lib/style.dart b/lib/style.dart index d6a43be824..616228b2d9 100644 --- a/lib/style.dart +++ b/lib/style.dart @@ -1,3 +1,5 @@ +import 'dart:ui'; + import 'package:flutter/material.dart'; ///This class represents all the available CSS attributes @@ -15,6 +17,12 @@ class Style { /// Default: unspecified, Color color; + /// CSS attribute "`direction`" + /// + /// Inherited: yes, + /// Default: TextDirection.ltr, + TextDirection direction; + /// CSS attribute "`display`" /// /// Inherited: no, @@ -27,6 +35,12 @@ class Style { /// Default: Theme.of(context).style.textTheme.body1.fontFamily String fontFamily; + /// CSS attribute "`font-feature-settings`" + /// + /// Inherited: yes, + /// Default: normal + List fontFeatureSettings; + /// CSS attribute "`font-size`" /// /// Inherited: yes, @@ -51,6 +65,12 @@ class Style { /// Default: Unspecified (null), double height; + /// CSS attribute "`letter-spacing`" + /// + /// Inherited: yes, + /// Default: normal (0), + double letterSpacing; + /// CSS attribute "`list-style-type`" /// /// Inherited: yes, @@ -69,18 +89,45 @@ class Style { /// Default: EdgeInsets.zero EdgeInsets margin; + /// CSS attribute "`text-align`" + /// + /// Inherited: yes, + /// Default: TextAlign.start, + TextAlign textAlign; + /// CSS attribute "`text-decoration`" /// /// Inherited: no, /// Default: TextDecoration.none, TextDecoration textDecoration; + /// CSS attribute "`text-decoration-color`" + /// + /// Inherited: no, + /// Default: Current color + Color textDecorationColor; + /// CSS attribute "`text-decoration-style`" /// /// Inherited: no, /// Default: TextDecorationStyle.solid, TextDecorationStyle textDecorationStyle; + /// Loosely based on CSS attribute "`text-decoration-thickness`" + /// + /// Uses a percent modifier based on the font size. + /// + /// Inherited: no, + /// Default: 1.0 (specified by font size) + // TODO(Sub6Resources): Possibly base this more closely on the CSS attribute. + double textDecorationThickness; + + /// CSS attribute "`text-shadow`" + /// + /// Inherited: yes, + /// Default: none, + List textShadow; + /// CSS attribute "`vertical-align`" /// /// Inherited: no, @@ -99,10 +146,15 @@ class Style { /// Default: unspecified (null) double width; + /// CSS attribute "`word-spacing`" + /// + /// Inherited: yes, + /// Default: normal (0) + double wordSpacing; + //TODO modify these to match CSS styles String before; String after; - TextDirection textDirection; Border border; Alignment alignment; String markerContent; @@ -110,23 +162,31 @@ class Style { Style({ this.backgroundColor = Colors.transparent, this.color, + this.direction, this.display, this.fontFamily, + this.fontFeatureSettings, this.fontSize, this.fontStyle, this.fontWeight, this.height, + this.letterSpacing, this.listStyleType, this.padding, this.margin, + this.textAlign, this.textDecoration, + this.textDecorationColor, this.textDecorationStyle, + this.textDecorationThickness, + this.textShadow, this.verticalAlign, this.whiteSpace, this.width, + this.wordSpacing, + this.before, this.after, - this.textDirection, this.border, this.alignment, this.markerContent, @@ -137,17 +197,25 @@ class Style { } } - //TODO: all attributes of TextStyle likely have a CSS attribute and should be supported. TextStyle generateTextStyle() { return TextStyle( backgroundColor: backgroundColor, color: color, decoration: textDecoration, + decorationColor: textDecorationColor, decorationStyle: textDecorationStyle, + decorationThickness: textDecorationThickness, fontFamily: fontFamily, + fontFeatures: fontFeatureSettings, fontSize: fontSize?.size, fontStyle: fontStyle, fontWeight: fontWeight, + letterSpacing: letterSpacing, + shadows: textShadow, + wordSpacing: wordSpacing, + //TODO background + //TODO textBaseline + //TODO height ); } @@ -162,25 +230,33 @@ class Style { return copyWith( backgroundColor: other.backgroundColor, color: other.color, + direction: other.direction, display: other.display, fontFamily: other.fontFamily, + fontFeatureSettings: other.fontFeatureSettings, fontSize: other.fontSize, fontStyle: other.fontStyle, fontWeight: other.fontWeight, height: other.height, + letterSpacing: other.letterSpacing, listStyleType: other.listStyleType, padding: other.padding, //TODO merge EdgeInsets margin: other.margin, //TODO merge EdgeInsets + textAlign: other.textAlign, textDecoration: other.textDecoration, + textDecorationColor: other.textDecorationColor, textDecorationStyle: other.textDecorationStyle, + textDecorationThickness: other.textDecorationThickness, + textShadow: other.textShadow, verticalAlign: other.verticalAlign, whiteSpace: other.whiteSpace, width: other.width, + wordSpacing: other.wordSpacing, + before: other.before, after: other.after, - textDirection: other.textDirection, border: other.border, //TODO merge border alignment: other.alignment, @@ -193,37 +269,49 @@ class Style { return child.copyWith( color: child.color ?? color, + direction: child.direction ?? direction, fontFamily: child.fontFamily ?? fontFamily, + fontFeatureSettings: child.fontFeatureSettings ?? fontFeatureSettings, fontSize: child.fontSize ?? fontSize, fontStyle: child.fontStyle ?? fontStyle, fontWeight: child.fontWeight ?? fontWeight, + letterSpacing: child.letterSpacing ?? letterSpacing, listStyleType: child.listStyleType ?? listStyleType, + textAlign: child.textAlign ?? textAlign, + textShadow: child.textShadow ?? textShadow, whiteSpace: child.whiteSpace ?? whiteSpace, + wordSpacing: child.wordSpacing ?? wordSpacing, ); } Style copyWith({ Color backgroundColor, Color color, + TextDirection direction, Display display, String fontFamily, + List fontFeatureSettings, FontSize fontSize, FontStyle fontStyle, FontWeight fontWeight, double height, + double letterSpacing, ListStyleType listStyleType, EdgeInsets padding, EdgeInsets margin, + TextAlign textAlign, TextDecoration textDecoration, + Color textDecorationColor, TextDecorationStyle textDecorationStyle, + double textDecorationThickness, + List textShadow, VerticalAlign verticalAlign, WhiteSpace whiteSpace, double width, - bool preserveWhitespace, - int baselineOffset, + double wordSpacing, + String before, String after, - TextDirection textDirection, Border border, Alignment alignment, String markerContent, @@ -231,23 +319,31 @@ class Style { return Style( backgroundColor: backgroundColor ?? this.backgroundColor, color: color ?? this.color, + direction: direction ?? this.direction, display: display ?? this.display, fontFamily: fontFamily ?? this.fontFamily, + fontFeatureSettings: fontFeatureSettings ?? this.fontFeatureSettings, fontSize: fontSize ?? this.fontSize, fontStyle: fontStyle ?? this.fontStyle, fontWeight: fontWeight ?? this.fontWeight, height: height ?? this.height, + letterSpacing: letterSpacing ?? this.letterSpacing, listStyleType: listStyleType ?? this.listStyleType, padding: padding ?? this.padding, margin: margin ?? this.margin, + textAlign: textAlign ?? this.textAlign, textDecoration: textDecoration ?? this.textDecoration, + textDecorationColor: textDecorationColor ?? this.textDecorationColor, textDecorationStyle: textDecorationStyle ?? this.textDecorationStyle, + textDecorationThickness: textDecorationThickness ?? this.textDecorationThickness, + textShadow: textShadow ?? this.textShadow, verticalAlign: verticalAlign ?? this.verticalAlign, whiteSpace: whiteSpace ?? this.whiteSpace, width: width ?? this.width, + wordSpacing: wordSpacing ?? this.wordSpacing, + before: before ?? this.before, after: after ?? this.after, - textDirection: textDirection ?? this.textDirection, border: border ?? this.border, alignment: alignment ?? this.alignment, markerContent: markerContent ?? this.markerContent, @@ -258,11 +354,17 @@ class Style { this.backgroundColor = textStyle.backgroundColor; this.color = textStyle.color; this.textDecoration = textStyle.decoration; + this.textDecorationColor = textStyle.decorationColor; this.textDecorationStyle = textStyle.decorationStyle; + this.textDecorationThickness = textStyle.decorationThickness; this.fontFamily = textStyle.fontFamily; + this.fontFeatureSettings = textStyle.fontFeatures; this.fontSize = FontSize(textStyle.fontSize); this.fontStyle = textStyle.fontStyle; this.fontWeight = textStyle.fontWeight; + this.letterSpacing = textStyle.letterSpacing; + this.textShadow = textStyle.shadows; + this.wordSpacing = textStyle.wordSpacing; } } diff --git a/test/html_parser_test.dart b/test/html_parser_test.dart index a9d734ce67..6bea22daf6 100644 --- a/test/html_parser_test.dart +++ b/test/html_parser_test.dart @@ -110,7 +110,7 @@ void testNewParser() { Style style2 = Style( before: "* ", - textDirection: TextDirection.rtl, + direction: TextDirection.rtl, fontStyle: FontStyle.italic, ); @@ -118,7 +118,7 @@ void testNewParser() { expect(finalStyle.display, equals(Display.BLOCK)); expect(finalStyle.before, equals("* ")); - expect(finalStyle.textDirection, equals(TextDirection.rtl)); + expect(finalStyle.direction, equals(TextDirection.rtl)); expect(finalStyle.fontStyle, equals(FontStyle.italic)); expect(finalStyle.fontWeight, equals(FontWeight.bold)); });