You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
591 lines
17 KiB
591 lines
17 KiB
import 'dart:ui';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import './flutter_html.dart';
|
|
import './src/css_parser.dart';
|
|
|
|
///This class represents all the available CSS attributes
|
|
///for this package.
|
|
class Style {
|
|
/// CSS attribute "`background-color`"
|
|
///
|
|
/// Inherited: no,
|
|
/// Default: Colors.transparent,
|
|
Color? backgroundColor;
|
|
|
|
/// CSS attribute "`color`"
|
|
///
|
|
/// Inherited: yes,
|
|
/// Default: unspecified,
|
|
Color? color;
|
|
|
|
/// CSS attribute "`direction`"
|
|
///
|
|
/// Inherited: yes,
|
|
/// Default: TextDirection.ltr,
|
|
TextDirection? direction;
|
|
|
|
/// CSS attribute "`display`"
|
|
///
|
|
/// Inherited: no,
|
|
/// Default: unspecified,
|
|
Display? display;
|
|
|
|
/// CSS attribute "`font-family`"
|
|
///
|
|
/// Inherited: yes,
|
|
/// Default: Theme.of(context).style.textTheme.body1.fontFamily
|
|
String? fontFamily;
|
|
|
|
|
|
/// The list of font families to fall back on when a glyph cannot be found in default font family.
|
|
///
|
|
/// Inherited: yes,
|
|
/// Default: null
|
|
List<String>? fontFamilyFallback;
|
|
|
|
|
|
/// CSS attribute "`font-feature-settings`"
|
|
///
|
|
/// Inherited: yes,
|
|
/// Default: normal
|
|
List<FontFeature>? fontFeatureSettings;
|
|
|
|
/// CSS attribute "`font-size`"
|
|
///
|
|
/// Inherited: yes,
|
|
/// Default: FontSize.medium
|
|
FontSize? fontSize;
|
|
|
|
/// CSS attribute "`font-style`"
|
|
///
|
|
/// Inherited: yes,
|
|
/// Default: FontStyle.normal,
|
|
FontStyle? fontStyle;
|
|
|
|
/// CSS attribute "`font-weight`"
|
|
///
|
|
/// Inherited: yes,
|
|
/// Default: FontWeight.normal,
|
|
FontWeight? fontWeight;
|
|
|
|
/// CSS attribute "`height`"
|
|
///
|
|
/// Inherited: no,
|
|
/// Default: Unspecified (null),
|
|
double? height;
|
|
|
|
/// CSS attribute "`letter-spacing`"
|
|
///
|
|
/// Inherited: yes,
|
|
/// Default: normal (0),
|
|
double? letterSpacing;
|
|
|
|
/// CSS attribute "`list-style-type`"
|
|
///
|
|
/// Inherited: yes,
|
|
/// Default: ListStyleType.DISC
|
|
ListStyleType? listStyleType;
|
|
|
|
/// CSS attribute "`list-style-position`"
|
|
///
|
|
/// Inherited: yes,
|
|
/// Default: ListStylePosition.OUTSIDE
|
|
ListStylePosition? listStylePosition;
|
|
|
|
/// CSS attribute "`padding`"
|
|
///
|
|
/// Inherited: no,
|
|
/// Default: EdgeInsets.zero
|
|
EdgeInsets? padding;
|
|
|
|
/// CSS attribute "`margin`"
|
|
///
|
|
/// Inherited: no,
|
|
/// 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<Shadow>? textShadow;
|
|
|
|
/// CSS attribute "`vertical-align`"
|
|
///
|
|
/// Inherited: no,
|
|
/// Default: VerticalAlign.BASELINE,
|
|
VerticalAlign? verticalAlign;
|
|
|
|
/// CSS attribute "`white-space`"
|
|
///
|
|
/// Inherited: yes,
|
|
/// Default: WhiteSpace.NORMAL,
|
|
WhiteSpace? whiteSpace;
|
|
|
|
/// CSS attribute "`width`"
|
|
///
|
|
/// Inherited: no,
|
|
/// Default: unspecified (null)
|
|
double? width;
|
|
|
|
/// CSS attribute "`word-spacing`"
|
|
///
|
|
/// Inherited: yes,
|
|
/// Default: normal (0)
|
|
double? wordSpacing;
|
|
|
|
/// CSS attribute "`line-height`"
|
|
///
|
|
/// Supported values: double values
|
|
///
|
|
/// Unsupported values: normal, 80%, ..
|
|
///
|
|
/// Inherited: no,
|
|
/// Default: Unspecified (null),
|
|
LineHeight? lineHeight;
|
|
|
|
//TODO modify these to match CSS styles
|
|
String? before;
|
|
String? after;
|
|
Border? border;
|
|
Alignment? alignment;
|
|
Widget? markerContent;
|
|
|
|
/// MaxLine
|
|
///
|
|
///
|
|
///
|
|
///
|
|
int? maxLines;
|
|
|
|
/// TextOverflow
|
|
///
|
|
///
|
|
///
|
|
///
|
|
TextOverflow? textOverflow;
|
|
|
|
TextTransform? textTransform;
|
|
|
|
Style({
|
|
this.backgroundColor = Colors.transparent,
|
|
this.color,
|
|
this.direction,
|
|
this.display,
|
|
this.fontFamily,
|
|
this.fontFamilyFallback,
|
|
this.fontFeatureSettings,
|
|
this.fontSize,
|
|
this.fontStyle,
|
|
this.fontWeight,
|
|
this.height,
|
|
this.lineHeight,
|
|
this.letterSpacing,
|
|
this.listStyleType,
|
|
this.listStylePosition,
|
|
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.border,
|
|
this.alignment,
|
|
this.markerContent,
|
|
this.maxLines,
|
|
this.textOverflow,
|
|
this.textTransform = TextTransform.none,
|
|
}) {
|
|
if (this.alignment == null &&
|
|
(display == Display.BLOCK || display == Display.LIST_ITEM)) {
|
|
this.alignment = Alignment.centerLeft;
|
|
}
|
|
}
|
|
|
|
static Map<String, Style> fromThemeData(ThemeData theme) => {
|
|
'h1': Style.fromTextStyle(theme.textTheme.headline1!),
|
|
'h2': Style.fromTextStyle(theme.textTheme.headline2!),
|
|
'h3': Style.fromTextStyle(theme.textTheme.headline3!),
|
|
'h4': Style.fromTextStyle(theme.textTheme.headline4!),
|
|
'h5': Style.fromTextStyle(theme.textTheme.headline5!),
|
|
'h6': Style.fromTextStyle(theme.textTheme.headline6!),
|
|
'body': Style.fromTextStyle(theme.textTheme.bodyText2!),
|
|
};
|
|
|
|
static Map<String, Style> fromCss(String css, OnCssParseError? onCssParseError) {
|
|
final declarations = parseExternalCss(css, onCssParseError);
|
|
Map<String, Style> styleMap = {};
|
|
declarations.forEach((key, value) {
|
|
styleMap[key] = declarationsToStyle(value);
|
|
});
|
|
return styleMap;
|
|
}
|
|
|
|
TextStyle generateTextStyle() {
|
|
return TextStyle(
|
|
backgroundColor: backgroundColor,
|
|
color: color,
|
|
decoration: textDecoration,
|
|
decorationColor: textDecorationColor,
|
|
decorationStyle: textDecorationStyle,
|
|
decorationThickness: textDecorationThickness,
|
|
fontFamily: fontFamily,
|
|
fontFamilyFallback: fontFamilyFallback,
|
|
fontFeatures: fontFeatureSettings,
|
|
fontSize: fontSize?.size,
|
|
fontStyle: fontStyle,
|
|
fontWeight: fontWeight,
|
|
letterSpacing: letterSpacing,
|
|
shadows: textShadow,
|
|
wordSpacing: wordSpacing,
|
|
height: lineHeight?.size ?? 1.0,
|
|
//TODO background
|
|
//TODO textBaseline
|
|
);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
return "Style";
|
|
}
|
|
|
|
Style merge(Style other) {
|
|
return copyWith(
|
|
backgroundColor: other.backgroundColor,
|
|
color: other.color,
|
|
direction: other.direction,
|
|
display: other.display,
|
|
fontFamily: other.fontFamily,
|
|
fontFamilyFallback: other.fontFamilyFallback,
|
|
fontFeatureSettings: other.fontFeatureSettings,
|
|
fontSize: other.fontSize,
|
|
fontStyle: other.fontStyle,
|
|
fontWeight: other.fontWeight,
|
|
height: other.height,
|
|
lineHeight: other.lineHeight,
|
|
letterSpacing: other.letterSpacing,
|
|
listStyleType: other.listStyleType,
|
|
listStylePosition: other.listStylePosition,
|
|
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,
|
|
border: other.border,
|
|
//TODO merge border
|
|
alignment: other.alignment,
|
|
markerContent: other.markerContent,
|
|
maxLines: other.maxLines,
|
|
textOverflow: other.textOverflow,
|
|
textTransform: other.textTransform,
|
|
);
|
|
}
|
|
|
|
Style copyOnlyInherited(Style child) {
|
|
FontSize? finalFontSize = child.fontSize != null ?
|
|
fontSize != null && child.fontSize?.units == "em" ?
|
|
FontSize(child.fontSize!.size! * fontSize!.size!) : child.fontSize
|
|
: fontSize != null && fontSize!.size! < 0 ?
|
|
FontSize.percent(100) : fontSize;
|
|
LineHeight? finalLineHeight = child.lineHeight != null ?
|
|
child.lineHeight?.units == "length" ?
|
|
LineHeight(child.lineHeight!.size! / (finalFontSize == null ? 14 : finalFontSize.size!) * 1.2) : child.lineHeight
|
|
: lineHeight;
|
|
return child.copyWith(
|
|
backgroundColor: child.backgroundColor != Colors.transparent ?
|
|
child.backgroundColor : backgroundColor,
|
|
color: child.color ?? color,
|
|
direction: child.direction ?? direction,
|
|
display: display == Display.NONE ? display : child.display,
|
|
fontFamily: child.fontFamily ?? fontFamily,
|
|
fontFamilyFallback: child.fontFamilyFallback ?? fontFamilyFallback,
|
|
fontFeatureSettings: child.fontFeatureSettings ?? fontFeatureSettings,
|
|
fontSize: finalFontSize,
|
|
fontStyle: child.fontStyle ?? fontStyle,
|
|
fontWeight: child.fontWeight ?? fontWeight,
|
|
lineHeight: finalLineHeight,
|
|
letterSpacing: child.letterSpacing ?? letterSpacing,
|
|
listStyleType: child.listStyleType ?? listStyleType,
|
|
listStylePosition: child.listStylePosition ?? listStylePosition,
|
|
textAlign: child.textAlign ?? textAlign,
|
|
textDecoration: TextDecoration.combine(
|
|
[child.textDecoration ?? TextDecoration.none,
|
|
textDecoration ?? TextDecoration.none]),
|
|
textShadow: child.textShadow ?? textShadow,
|
|
whiteSpace: child.whiteSpace ?? whiteSpace,
|
|
wordSpacing: child.wordSpacing ?? wordSpacing,
|
|
maxLines: child.maxLines ?? maxLines,
|
|
textOverflow: child.textOverflow ?? textOverflow,
|
|
textTransform: child.textTransform ?? textTransform,
|
|
);
|
|
}
|
|
|
|
Style copyWith({
|
|
Color? backgroundColor,
|
|
Color? color,
|
|
TextDirection? direction,
|
|
Display? display,
|
|
String? fontFamily,
|
|
List<String>? fontFamilyFallback,
|
|
List<FontFeature>? fontFeatureSettings,
|
|
FontSize? fontSize,
|
|
FontStyle? fontStyle,
|
|
FontWeight? fontWeight,
|
|
double? height,
|
|
LineHeight? lineHeight,
|
|
double? letterSpacing,
|
|
ListStyleType? listStyleType,
|
|
ListStylePosition? listStylePosition,
|
|
EdgeInsets? padding,
|
|
EdgeInsets? margin,
|
|
TextAlign? textAlign,
|
|
TextDecoration? textDecoration,
|
|
Color? textDecorationColor,
|
|
TextDecorationStyle? textDecorationStyle,
|
|
double? textDecorationThickness,
|
|
List<Shadow>? textShadow,
|
|
VerticalAlign? verticalAlign,
|
|
WhiteSpace? whiteSpace,
|
|
double? width,
|
|
double? wordSpacing,
|
|
String? before,
|
|
String? after,
|
|
Border? border,
|
|
Alignment? alignment,
|
|
Widget? markerContent,
|
|
int? maxLines,
|
|
TextOverflow? textOverflow,
|
|
TextTransform? textTransform,
|
|
bool? beforeAfterNull,
|
|
}) {
|
|
return Style(
|
|
backgroundColor: backgroundColor ?? this.backgroundColor,
|
|
color: color ?? this.color,
|
|
direction: direction ?? this.direction,
|
|
display: display ?? this.display,
|
|
fontFamily: fontFamily ?? this.fontFamily,
|
|
fontFamilyFallback: fontFamilyFallback ?? this.fontFamilyFallback,
|
|
fontFeatureSettings: fontFeatureSettings ?? this.fontFeatureSettings,
|
|
fontSize: fontSize ?? this.fontSize,
|
|
fontStyle: fontStyle ?? this.fontStyle,
|
|
fontWeight: fontWeight ?? this.fontWeight,
|
|
height: height ?? this.height,
|
|
lineHeight: lineHeight ?? this.lineHeight,
|
|
letterSpacing: letterSpacing ?? this.letterSpacing,
|
|
listStyleType: listStyleType ?? this.listStyleType,
|
|
listStylePosition: listStylePosition ?? this.listStylePosition,
|
|
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: beforeAfterNull == true ? null : before ?? this.before,
|
|
after: beforeAfterNull == true ? null : after ?? this.after,
|
|
border: border ?? this.border,
|
|
alignment: alignment ?? this.alignment,
|
|
markerContent: markerContent ?? this.markerContent,
|
|
maxLines: maxLines ?? this.maxLines,
|
|
textOverflow: textOverflow ?? this.textOverflow,
|
|
textTransform: textTransform ?? this.textTransform,
|
|
);
|
|
}
|
|
|
|
Style.fromTextStyle(TextStyle textStyle) {
|
|
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.fontFamilyFallback = textStyle.fontFamilyFallback;
|
|
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;
|
|
this.lineHeight = LineHeight(textStyle.height ?? 1.2);
|
|
this.textTransform = TextTransform.none;
|
|
}
|
|
}
|
|
|
|
enum Display {
|
|
BLOCK,
|
|
INLINE,
|
|
INLINE_BLOCK,
|
|
LIST_ITEM,
|
|
NONE,
|
|
}
|
|
|
|
class FontSize {
|
|
final double? size;
|
|
final String units;
|
|
|
|
const FontSize(this.size, {this.units = ""});
|
|
|
|
/// A percentage of the parent style's font size.
|
|
factory FontSize.percent(double percent) {
|
|
return FontSize(percent / -100.0, units: "%");
|
|
}
|
|
|
|
factory FontSize.em(double? em) {
|
|
return FontSize(em, units: "em");
|
|
}
|
|
|
|
factory FontSize.rem(double rem) {
|
|
return FontSize(rem * 16 - 2, units: "rem");
|
|
}
|
|
// These values are calculated based off of the default (`medium`)
|
|
// being 14px.
|
|
//
|
|
// TODO(Sub6Resources): This seems to override Flutter's accessibility text scaling.
|
|
//
|
|
// Negative values are computed during parsing to be a percentage of
|
|
// the parent style's font size.
|
|
static const xxSmall = FontSize(7.875);
|
|
static const xSmall = FontSize(8.75);
|
|
static const small = FontSize(11.375);
|
|
static const medium = FontSize(14.0);
|
|
static const large = FontSize(15.75);
|
|
static const xLarge = FontSize(21.0);
|
|
static const xxLarge = FontSize(28.0);
|
|
static const smaller = FontSize(-0.83);
|
|
static const larger = FontSize(-1.2);
|
|
}
|
|
|
|
class LineHeight {
|
|
final double? size;
|
|
final String units;
|
|
|
|
const LineHeight(this.size, {this.units = ""});
|
|
|
|
factory LineHeight.percent(double percent) {
|
|
return LineHeight(percent / 100.0 * 1.2, units: "%");
|
|
}
|
|
|
|
factory LineHeight.em(double em) {
|
|
return LineHeight(em * 1.2, units: "em");
|
|
}
|
|
|
|
factory LineHeight.rem(double rem) {
|
|
return LineHeight(rem * 1.2, units: "rem");
|
|
}
|
|
|
|
factory LineHeight.number(double num) {
|
|
return LineHeight(num * 1.2, units: "number");
|
|
}
|
|
|
|
static const normal = LineHeight(1.2);
|
|
}
|
|
|
|
class ListStyleType {
|
|
final String text;
|
|
final String type;
|
|
final Widget? widget;
|
|
|
|
const ListStyleType(this.text, {this.type = "marker", this.widget});
|
|
|
|
factory ListStyleType.fromImage(String url) => ListStyleType(url, type: "image");
|
|
|
|
factory ListStyleType.fromWidget(Widget widget) => ListStyleType("", widget: widget, type: "widget");
|
|
|
|
static const LOWER_ALPHA = ListStyleType("LOWER_ALPHA");
|
|
static const UPPER_ALPHA = ListStyleType("UPPER_ALPHA");
|
|
static const LOWER_LATIN = ListStyleType("LOWER_LATIN");
|
|
static const UPPER_LATIN = ListStyleType("UPPER_LATIN");
|
|
static const CIRCLE = ListStyleType("CIRCLE");
|
|
static const DISC = ListStyleType("DISC");
|
|
static const DECIMAL = ListStyleType("DECIMAL");
|
|
static const LOWER_ROMAN = ListStyleType("LOWER_ROMAN");
|
|
static const UPPER_ROMAN = ListStyleType("UPPER_ROMAN");
|
|
static const SQUARE = ListStyleType("SQUARE");
|
|
static const NONE = ListStyleType("NONE");
|
|
}
|
|
|
|
enum ListStylePosition {
|
|
OUTSIDE,
|
|
INSIDE,
|
|
}
|
|
|
|
enum TextTransform {
|
|
uppercase,
|
|
lowercase,
|
|
capitalize,
|
|
none,
|
|
}
|
|
|
|
enum VerticalAlign {
|
|
BASELINE,
|
|
SUB,
|
|
SUPER,
|
|
}
|
|
|
|
enum WhiteSpace {
|
|
NORMAL,
|
|
PRE,
|
|
}
|