// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:sky'; /// The thickness of the glyphs used to draw the text enum FontWeight { /// Thin, the least thick w100, /// Extra-light w200, /// Light w300, /// Normal / regular / plain w400, /// Medium w500, /// Semi-bold w600, /// Bold w700, /// Extra-bold w800, /// Black, the most thick w900 } /// A normal font weight const normal = FontWeight.w400; /// A bold font weight const bold = FontWeight.w700; /// Whether to slant the glyphs in the font enum FontStyle { /// Use the upright glyphs normal, /// Use glyphs designed for slanting italic, /// Use the upright glyphs but slant them during painting oblique // TODO(abarth): Remove. We don't really support this value. } /// Whether to align text horizontally enum TextAlign { /// Align the text on the left edge of the container left, /// Align the text on the right edge of the container right, /// Align the text in the center of the container center } /// A horizontal line used for aligning text enum TextBaseline { // The horizontal line used to align the bottom of glyphs for alphabetic characters alphabetic, // The horizontal line used to align ideographic characters ideographic } /// A linear decoration to draw near the text enum TextDecoration { /// Do not draw a decoration none, /// Draw a line underneath each line of text underline, /// Draw a line above each line of text overline, /// Draw a line through each line of text lineThrough } /// Draw a line underneath each line of text const underline = const <TextDecoration>[TextDecoration.underline]; /// Draw a line above each line of text const overline = const <TextDecoration>[TextDecoration.overline]; /// Draw a line through each line of text const lineThrough = const <TextDecoration>[TextDecoration.lineThrough]; /// The style in which to draw a text decoration enum TextDecorationStyle { /// Draw a solid line solid, /// Draw two lines double, /// Draw a dotted line dotted, /// Draw a dashed line dashed, /// Draw a sinusoidal line wavy } /// An immutable style in which paint text class TextStyle { const TextStyle({ this.color, this.fontFamily, this.fontSize, this.fontWeight, this.fontStyle, this.textAlign, this.textBaseline, this.height, this.decoration, this.decorationColor, this.decorationStyle }); /// The color to use when painting the text final Color color; /// The name of the font to use when painting the text final String fontFamily; /// The size of gyphs (in logical pixels) to use when painting the text final double fontSize; /// The font weight to use when painting the text final FontWeight fontWeight; /// The font style to use when painting the text final FontStyle fontStyle; /// How the text should be aligned (applies only to the outermost /// StyledTextSpan, which establishes the container for the text) final TextAlign textAlign; /// The baseline to use for aligning the text final TextBaseline textBaseline; /// The distance between the text baselines, as a multiple of the font size final double height; /// A list of decorations to paint near the text final List<TextDecoration> decoration; // TODO(ianh): Switch this to a Set<> once Dart supports constant Sets /// The color in which to paint the text decorations final Color decorationColor; /// The style in which to paint the text decorations final TextDecorationStyle decorationStyle; /// Returns a new text style that matches this text style but with the given /// values replaced TextStyle copyWith({ Color color, String fontFamily, double fontSize, FontWeight fontWeight, FontStyle fontStyle, TextAlign textAlign, TextBaseline textBaseline, double height, List<TextDecoration> decoration, Color decorationColor, TextDecorationStyle decorationStyle }) { return new TextStyle( color: color != null ? color : this.color, fontFamily: fontFamily != null ? fontFamily : this.fontFamily, fontSize: fontSize != null ? fontSize : this.fontSize, fontWeight: fontWeight != null ? fontWeight : this.fontWeight, fontStyle: fontStyle != null ? fontStyle : this.fontStyle, textAlign: textAlign != null ? textAlign : this.textAlign, textBaseline: textBaseline != null ? textBaseline : this.textBaseline, height: height != null ? height : this.height, decoration: decoration != null ? decoration : this.decoration, decorationColor: decorationColor != null ? decorationColor : this.decorationColor, decorationStyle: decorationStyle != null ? decorationStyle : this.decorationStyle ); } /// Returns a new text style that matches this text style but with some values /// replaced by the non-null parameters of the given text style TextStyle merge(TextStyle other) { return copyWith( color: other.color, fontFamily: other.fontFamily, fontSize: other.fontSize, fontWeight: other.fontWeight, fontStyle: other.fontStyle, textAlign: other.textAlign, textBaseline: other.textBaseline, height: other.height, decoration: other.decoration, decorationColor: other.decorationColor, decorationStyle: other.decorationStyle ); } static String _colorToCSSString(Color color) { return 'rgba(${color.red}, ${color.green}, ${color.blue}, ${color.alpha / 255.0})'; } static String _fontFamilyToCSSString(String fontFamily) { // TODO(hansmuller): escape the fontFamily string. return fontFamily; } static String _decorationToCSSString(List<TextDecoration> decoration) { assert(decoration != null); const toCSS = const <TextDecoration, String>{ TextDecoration.none: 'none', TextDecoration.underline: 'underline', TextDecoration.overline: 'overline', TextDecoration.lineThrough: 'lineThrough' }; return decoration.map((d) => toCSS[d]).join(' '); } static String _decorationStyleToCSSString(TextDecorationStyle decorationStyle) { assert(decorationStyle != null); const toCSS = const <TextDecorationStyle, String>{ TextDecorationStyle.solid: 'solid', TextDecorationStyle.double: 'double', TextDecorationStyle.dotted: 'dotted', TextDecorationStyle.dashed: 'dashed', TextDecorationStyle.wavy: 'wavy' }; return toCSS[decorationStyle]; } /// Program this text style into the engine /// /// Note: This function will likely be removed when we refactor the interface /// between the framework and the engine void applyToCSSStyle(CSSStyleDeclaration cssStyle) { if (color != null) { cssStyle['color'] = _colorToCSSString(color); } if (fontFamily != null) { cssStyle['font-family'] = _fontFamilyToCSSString(fontFamily); } if (fontSize != null) { cssStyle['font-size'] = '${fontSize}px'; } if (fontWeight != null) { cssStyle['font-weight'] = const { FontWeight.w100: '100', FontWeight.w200: '200', FontWeight.w300: '300', FontWeight.w400: '400', FontWeight.w500: '500', FontWeight.w600: '600', FontWeight.w700: '700', FontWeight.w800: '800', FontWeight.w900: '900' }[fontWeight]; } if (fontStyle != null) { cssStyle['font-style'] = const { FontStyle.normal: 'normal', FontStyle.italic: 'italic', FontStyle.oblique: 'oblique', }[fontStyle]; } if (decoration != null) { cssStyle['text-decoration'] = _decorationToCSSString(decoration); if (decorationColor != null) cssStyle['text-decoration-color'] = _colorToCSSString(decorationColor); if (decorationStyle != null) cssStyle['text-decoration-style'] = _decorationStyleToCSSString(decorationStyle); } } /// Program the container aspects of this text style into the engine /// /// Note: This function will likely be removed when we refactor the interface /// between the framework and the engine void applyToContainerCSSStyle(CSSStyleDeclaration cssStyle) { if (textAlign != null) { cssStyle['text-align'] = const { TextAlign.left: 'left', TextAlign.right: 'right', TextAlign.center: 'center', }[textAlign]; } if (height != null) { cssStyle['line-height'] = '${height}'; } } bool operator ==(other) { if (identical(this, other)) return true; return other is TextStyle && color == other.color && fontFamily == other.fontFamily && fontSize == other.fontSize && fontWeight == other.fontWeight && fontStyle == other.fontStyle && textAlign == other.textAlign && textBaseline == other.textBaseline && decoration == other.decoration && decorationColor == other.decorationColor && decorationStyle == other.decorationStyle; } int get hashCode { // Use Quiver: https://github.com/domokit/mojo/issues/236 int value = 373; value = 37 * value + color.hashCode; value = 37 * value + fontFamily.hashCode; value = 37 * value + fontSize.hashCode; value = 37 * value + fontWeight.hashCode; value = 37 * value + fontStyle.hashCode; value = 37 * value + textAlign.hashCode; value = 37 * value + textBaseline.hashCode; value = 37 * value + decoration.hashCode; value = 37 * value + decorationColor.hashCode; value = 37 * value + decorationStyle.hashCode; return value; } String toString([String prefix = '']) { List<String> result = []; if (color != null) result.add('${prefix}color: $color'); // TODO(hansmuller): escape the fontFamily string. if (fontFamily != null) result.add('${prefix}fontFamily: "${fontFamily}"'); if (fontSize != null) result.add('${prefix}fontSize: $fontSize'); if (fontWeight != null) result.add('${prefix}fontWeight: $fontWeight'); if (fontStyle != null) result.add('${prefix}fontStyle: $fontStyle'); if (textAlign != null) result.add('${prefix}textAlign: $textAlign'); if (textBaseline != null) result.add('${prefix}textBaseline: $textBaseline'); if (decoration != null) result.add('${prefix}decoration: $decoration'); if (decorationColor != null) result.add('${prefix}decorationColor: $decorationColor'); if (decorationStyle != null) result.add('${prefix}decorationStyle: $decorationStyle'); if (result.isEmpty) return '${prefix}<no style specified>'; return result.join('\n'); } }