text_style.dart 11.2 KB
Newer Older
1 2 3 4
// 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.

5
import 'dart:ui' as ui show ParagraphStyle, TextStyle, lerpDouble;
6

7
import 'basic_types.dart';
8

Florian Loitsch's avatar
Florian Loitsch committed
9
/// An immutable style in which paint text.
10 11
class TextStyle {
  const TextStyle({
12
    this.inherit: true,
13 14 15 16
    this.color,
    this.fontFamily,
    this.fontSize,
    this.fontWeight,
17
    this.fontStyle,
18
    this.letterSpacing,
19
    this.wordSpacing,
20
    this.textAlign,
21
    this.textBaseline,
22 23 24 25 26 27
    this.height,
    this.decoration,
    this.decorationColor,
    this.decorationStyle
  });

28 29 30
  /// Whether null values are replaced with their value in an ancestor text style.
  final bool inherit;

31
  /// The color to use when painting the text.
32
  final Color color;
33

34
  /// The name of the font to use when painting the text.
35
  final String fontFamily;
36

37
  /// The size of gyphs (in logical pixels) to use when painting the text.
38 39
  final double fontSize;

40
  /// The font weight to use when painting the text.
41
  final FontWeight fontWeight;
42

43
  /// The font style to use when painting the text.
44
  final FontStyle fontStyle;
45

46 47 48
  /// The amount of space to add between each letter.
  final double letterSpacing;

49 50 51
  /// The amount of space to add at each sequence of white-space (i.e. between each word).
  final double wordSpacing;

52
  /// How the text should be aligned (applies only to the outermost
53
  /// StyledTextSpan, which establishes the container for the text).
54
  final TextAlign textAlign;
55

56
  /// The baseline to use for aligning the text.
57
  final TextBaseline textBaseline;
58

59
  /// The distance between the text baselines, as a multiple of the font size.
60 61
  final double height;

62 63
  /// The decorations to paint near the text.
  final TextDecoration decoration;
64

65
  /// The color in which to paint the text decorations.
66
  final Color decorationColor;
67

68
  /// The style in which to paint the text decorations.
69 70
  final TextDecorationStyle decorationStyle;

71
  /// Returns a new text style that matches this text style but with the given
72
  /// values replaced.
73 74 75 76 77
  TextStyle copyWith({
    Color color,
    String fontFamily,
    double fontSize,
    FontWeight fontWeight,
78
    FontStyle fontStyle,
79
    double letterSpacing,
80
    double wordSpacing,
81
    TextAlign textAlign,
82
    TextBaseline textBaseline,
83
    double height,
84
    TextDecoration decoration,
85 86 87 88
    Color decorationColor,
    TextDecorationStyle decorationStyle
  }) {
    return new TextStyle(
89
      inherit: inherit,
90 91 92 93
      color: color != null ? color : this.color,
      fontFamily: fontFamily != null ? fontFamily : this.fontFamily,
      fontSize: fontSize != null ? fontSize : this.fontSize,
      fontWeight: fontWeight != null ? fontWeight : this.fontWeight,
94
      fontStyle: fontStyle != null ? fontStyle : this.fontStyle,
95
      letterSpacing: letterSpacing != null ? letterSpacing : this.letterSpacing,
96
      wordSpacing: wordSpacing != null ? wordSpacing : this.wordSpacing,
97
      textAlign: textAlign != null ? textAlign : this.textAlign,
98
      textBaseline: textBaseline != null ? textBaseline : this.textBaseline,
99 100 101 102 103 104 105
      height: height != null ? height : this.height,
      decoration: decoration != null ? decoration : this.decoration,
      decorationColor: decorationColor != null ? decorationColor : this.decorationColor,
      decorationStyle: decorationStyle != null ? decorationStyle : this.decorationStyle
    );
  }

106
  /// Returns a new text style that matches this text style but with some values
107 108
  /// replaced by the non-null parameters of the given text style. If the given
  /// text style is null, simply returns this text style.
109
  TextStyle merge(TextStyle other) {
110 111 112
    if (other == null)
      return this;
    assert(other.inherit);
113 114 115 116 117
    return copyWith(
      color: other.color,
      fontFamily: other.fontFamily,
      fontSize: other.fontSize,
      fontWeight: other.fontWeight,
118
      fontStyle: other.fontStyle,
119
      letterSpacing: other.letterSpacing,
120
      wordSpacing: other.wordSpacing,
121
      textAlign: other.textAlign,
122
      textBaseline: other.textBaseline,
123 124 125 126 127 128 129
      height: other.height,
      decoration: other.decoration,
      decorationColor: other.decorationColor,
      decorationStyle: other.decorationStyle
    );
  }

130 131 132 133 134 135 136 137 138 139 140
  /// Interpolate between two text styles.
  ///
  /// This will not work well if the styles don't set the same fields.
  static TextStyle lerp(TextStyle begin, TextStyle end, double t) {
    assert(begin.inherit == end.inherit);
    return new TextStyle(
      inherit: end.inherit,
      color: Color.lerp(begin.color, end.color, t),
      fontFamily: t < 0.5 ? begin.fontFamily : end.fontFamily,
      fontSize: ui.lerpDouble(begin.fontSize ?? end.fontSize, end.fontSize ?? begin.fontSize, t),
      // TODO(ianh): Replace next line with "fontWeight: FontWeight.lerp(begin.fontWeight, end.fontWeight, t)," once engine is revved
141
      fontWeight: FontWeight.values[ui.lerpDouble(begin?.fontWeight?.index ?? FontWeight.normal.index, end?.fontWeight?.index ?? FontWeight.normal.index, t.clamp(0.0, 1.0)).round()],
142 143 144 145 146 147 148 149 150 151 152
      fontStyle: t < 0.5 ? begin.fontStyle : end.fontStyle,
      letterSpacing: ui.lerpDouble(begin.letterSpacing ?? end.letterSpacing, end.letterSpacing ?? begin.letterSpacing, t),
      wordSpacing: ui.lerpDouble(begin.wordSpacing ?? end.wordSpacing, end.wordSpacing ?? begin.wordSpacing, t),
      textAlign: t < 0.5 ? begin.textAlign : end.textAlign,
      textBaseline: t < 0.5 ? begin.textBaseline : end.textBaseline,
      height: ui.lerpDouble(begin.height ?? end.height, end.height ?? begin.height, t),
      decoration: t < 0.5 ? begin.decoration : end.decoration,
      decorationColor: Color.lerp(begin.decorationColor, end.decorationColor, t),
      decorationStyle: t < 0.5 ? begin.decorationStyle : end.decorationStyle
    );
  }
153

154

155 156 157 158 159 160 161 162 163
  ui.TextStyle get textStyle {
    return new ui.TextStyle(
      color: color,
      decoration: decoration,
      decorationColor: decorationColor,
      decorationStyle: decorationStyle,
      fontWeight: fontWeight,
      fontStyle: fontStyle,
      fontFamily: fontFamily,
164
      fontSize: fontSize,
165 166 167
      letterSpacing: letterSpacing,
      wordSpacing: wordSpacing,
      lineHeight: height
168 169 170 171 172
    );
  }

  ui.ParagraphStyle get paragraphStyle {
    return new ui.ParagraphStyle(
173
      textAlign: textAlign,
174 175 176 177
      textBaseline: textBaseline,
      lineHeight: height
    );
  }
178

Hixie's avatar
Hixie committed
179
  bool operator ==(dynamic other) {
180 181
    if (identical(this, other))
      return true;
Hixie's avatar
Hixie committed
182 183 184
    if (other is! TextStyle)
      return false;
    final TextStyle typedOther = other;
185 186
    return inherit == typedOther.inherit &&
           color == typedOther.color &&
Hixie's avatar
Hixie committed
187 188 189 190
           fontFamily == typedOther.fontFamily &&
           fontSize == typedOther.fontSize &&
           fontWeight == typedOther.fontWeight &&
           fontStyle == typedOther.fontStyle &&
191
           letterSpacing == typedOther.letterSpacing &&
192
           wordSpacing == typedOther.wordSpacing &&
Hixie's avatar
Hixie committed
193 194
           textAlign == typedOther.textAlign &&
           textBaseline == typedOther.textBaseline &&
195
           height == typedOther.height &&
Hixie's avatar
Hixie committed
196 197 198
           decoration == typedOther.decoration &&
           decorationColor == typedOther.decorationColor &&
           decorationStyle == typedOther.decorationStyle;
199 200 201
  }

  int get hashCode {
202
    return hashValues(
203
      inherit,
204 205 206 207 208 209
      color,
      fontFamily,
      fontSize,
      fontWeight,
      fontStyle,
      letterSpacing,
210
      wordSpacing,
211 212
      textAlign,
      textBaseline,
213
      height,
214 215 216 217
      decoration,
      decorationColor,
      decorationStyle
    );
218 219 220
  }

  String toString([String prefix = '']) {
Hixie's avatar
Hixie committed
221
    List<String> result = <String>[];
222
    result.add('${prefix}inherit: $inherit');
223 224 225
    if (color != null)
      result.add('${prefix}color: $color');
    if (fontFamily != null)
Hixie's avatar
Hixie committed
226
      result.add('${prefix}family: "$fontFamily"');
227
    if (fontSize != null)
Hixie's avatar
Hixie committed
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
      result.add('${prefix}size: $fontSize');
    if (fontWeight != null) {
      switch (fontWeight) {
        case FontWeight.w100:
          result.add('${prefix}weight: 100');
          break;
        case FontWeight.w200:
          result.add('${prefix}weight: 200');
          break;
        case FontWeight.w300:
          result.add('${prefix}weight: 300');
          break;
        case FontWeight.w400:
          result.add('${prefix}weight: 400');
          break;
        case FontWeight.w500:
          result.add('${prefix}weight: 500');
          break;
        case FontWeight.w600:
          result.add('${prefix}weight: 600');
          break;
        case FontWeight.w700:
          result.add('${prefix}weight: 700');
          break;
        case FontWeight.w800:
          result.add('${prefix}weight: 800');
          break;
        case FontWeight.w900:
          result.add('${prefix}weight: 900');
          break;
      }
    }
    if (fontStyle != null) {
      switch (fontStyle) {
        case FontStyle.normal:
          result.add('${prefix}style: normal');
          break;
        case FontStyle.italic:
          result.add('${prefix}style: italic');
          break;
      }
    }
270
    if (letterSpacing != null)
271 272 273
      result.add('${prefix}letterSpacing: ${letterSpacing}x');
    if (wordSpacing != null)
      result.add('${prefix}wordSpacing: ${wordSpacing}x');
Hixie's avatar
Hixie committed
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
    if (textAlign != null) {
      switch (textAlign) {
        case TextAlign.left:
          result.add('${prefix}align: left');
          break;
        case TextAlign.right:
          result.add('${prefix}align: right');
          break;
        case TextAlign.center:
          result.add('${prefix}align: center');
          break;
      }
    }
    if (textBaseline != null) {
      switch (textBaseline) {
        case TextBaseline.alphabetic:
          result.add('${prefix}baseline: alphabetic');
          break;
        case TextBaseline.ideographic:
          result.add('${prefix}baseline: ideographic');
          break;
      }
    }
297 298
    if (height != null)
      result.add('${prefix}height: ${height}x');
Hixie's avatar
Hixie committed
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
    if (decoration != null || decorationColor != null || decorationStyle != null) {
      String decorationDescription = '${prefix}decoration: ';
      bool haveDecorationDescription = false;
      if (decorationStyle != null) {
        switch (decorationStyle) {
          case TextDecorationStyle.solid:
            decorationDescription += 'solid';
            break;
          case TextDecorationStyle.double:
            decorationDescription += 'double';
            break;
          case TextDecorationStyle.dotted:
            decorationDescription += 'dotted';
            break;
          case TextDecorationStyle.dashed:
            decorationDescription += 'dashed';
            break;
          case TextDecorationStyle.wavy:
            decorationDescription += 'wavy';
            break;
        }
        haveDecorationDescription = true;
      }
      if (decorationColor != null) {
        if (haveDecorationDescription)
          decorationDescription += ' ';
        decorationDescription += '$decorationColor';
        haveDecorationDescription = true;
      }
      if (decoration != null) {
        if (haveDecorationDescription)
          decorationDescription += ' ';
331
        decorationDescription += '$decoration';
Hixie's avatar
Hixie committed
332 333 334 335 336
        haveDecorationDescription = true;
      }
      assert(haveDecorationDescription);
      result.add(decorationDescription);
    }
337
    if (result.isEmpty)
Hixie's avatar
Hixie committed
338
      return '$prefix<no style specified>';
339 340 341
    return result.join('\n');
  }
}