| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 import 'dart:sky' as sky; | |
| 6 import 'box.dart'; | |
| 7 import 'object.dart'; | |
| 8 | |
| 9 class RenderInline extends RenderObject { | |
| 10 RenderInline(this.data); | |
| 11 String data; | |
| 12 } | |
| 13 | |
| 14 enum FontWeight { | |
| 15 light, // 300 | |
| 16 regular, // 400 | |
| 17 medium, // 500 | |
| 18 } | |
| 19 | |
| 20 enum TextAlign { | |
| 21 left, | |
| 22 right, | |
| 23 center | |
| 24 } | |
| 25 | |
| 26 class TextStyle { | |
| 27 const TextStyle({ | |
| 28 this.color, | |
| 29 this.fontSize, | |
| 30 this.fontWeight, | |
| 31 this.textAlign | |
| 32 }); | |
| 33 | |
| 34 final Color color; | |
| 35 final double fontSize; // in pixels | |
| 36 final FontWeight fontWeight; | |
| 37 final TextAlign textAlign; | |
| 38 | |
| 39 TextStyle copyWith({ | |
| 40 Color color, | |
| 41 double fontSize, | |
| 42 FontWeight fontWeight, | |
| 43 TextAlign textAlign | |
| 44 }) { | |
| 45 return new TextStyle( | |
| 46 color: color != null ? color : this.color, | |
| 47 fontSize: fontSize != null ? fontSize : this.fontSize, | |
| 48 fontWeight: fontWeight != null ? fontWeight : this.fontWeight, | |
| 49 textAlign: textAlign != null ? textAlign : this.textAlign | |
| 50 ); | |
| 51 } | |
| 52 | |
| 53 bool operator ==(other) { | |
| 54 return other is TextStyle && | |
| 55 color == other.color && | |
| 56 fontSize == other.fontSize && | |
| 57 fontWeight == other.fontWeight && | |
| 58 textAlign == other.textAlign; | |
| 59 } | |
| 60 | |
| 61 int get hashCode { | |
| 62 // Use Quiver: https://github.com/domokit/mojo/issues/236 | |
| 63 int value = 373; | |
| 64 value = 37 * value + color.hashCode; | |
| 65 value = 37 * value + fontSize.hashCode; | |
| 66 value = 37 * value + fontWeight.hashCode; | |
| 67 value = 37 * value + textAlign.hashCode; | |
| 68 return value; | |
| 69 } | |
| 70 | |
| 71 String toString([String prefix = '']) { | |
| 72 List<String> result = []; | |
| 73 if (color != null) | |
| 74 result.add('${prefix}color: $color'); | |
| 75 if (fontSize != null) | |
| 76 result.add('${prefix}fontSize: $fontSize'); | |
| 77 if (fontWeight != null) | |
| 78 result.add('${prefix}fontWeight: fontWeight'); | |
| 79 if (textAlign != null) | |
| 80 result.add('${prefix}textAlign: textAlign'); | |
| 81 if (result.isEmpty) | |
| 82 return '${prefix}<no style specified>'; | |
| 83 return result.join('\n'); | |
| 84 } | |
| 85 } | |
| 86 | |
| 87 // Unfortunately, using full precision floating point here causes bad layouts | |
| 88 // because floating point math isn't associative. If we add and subtract | |
| 89 // padding, for example, we'll get different values when we estimate sizes and | |
| 90 // when we actually compute layout because the operations will end up associated | |
| 91 // differently. To work around this problem for now, we round fractional pixel | |
| 92 // values up to the nearest whole pixel value. The right long-term fix is to do | |
| 93 // layout using fixed precision arithmetic. | |
| 94 double _applyFloatingPointHack(double layoutValue) { | |
| 95 return layoutValue.ceilToDouble(); | |
| 96 } | |
| 97 | |
| 98 class RenderParagraph extends RenderBox { | |
| 99 | |
| 100 RenderParagraph({ | |
| 101 String text, | |
| 102 Color color, | |
| 103 TextStyle style | |
| 104 }) : _style = style { | |
| 105 _layoutRoot.rootElement = _document.createElement('p'); | |
| 106 this.text = text; | |
| 107 } | |
| 108 | |
| 109 final sky.Document _document = new sky.Document(); | |
| 110 final sky.LayoutRoot _layoutRoot = new sky.LayoutRoot(); | |
| 111 | |
| 112 String get text => (_layoutRoot.rootElement.firstChild as sky.Text).data; | |
| 113 void set text (String value) { | |
| 114 _layoutRoot.rootElement.setChild(_document.createText(value)); | |
| 115 markNeedsLayout(); | |
| 116 } | |
| 117 | |
| 118 TextStyle _style; | |
| 119 TextStyle get style => _style; | |
| 120 void set style (TextStyle value) { | |
| 121 if (_style != value) { | |
| 122 // TODO(hansmuller): decide if a new layout or paint is needed | |
| 123 markNeedsLayout(); | |
| 124 _style = value; | |
| 125 } | |
| 126 } | |
| 127 | |
| 128 BoxConstraints _constraintsForCurrentLayout; | |
| 129 | |
| 130 sky.Element _layout(BoxConstraints constraints) { | |
| 131 _layoutRoot.maxWidth = constraints.maxWidth; | |
| 132 _layoutRoot.minWidth = constraints.minWidth; | |
| 133 _layoutRoot.minHeight = constraints.minHeight; | |
| 134 _layoutRoot.maxHeight = constraints.maxHeight; | |
| 135 _layoutRoot.layout(); | |
| 136 _constraintsForCurrentLayout = constraints; | |
| 137 } | |
| 138 | |
| 139 double getMinIntrinsicWidth(BoxConstraints constraints) { | |
| 140 _layout(constraints); | |
| 141 return constraints.constrainWidth( | |
| 142 _applyFloatingPointHack(_layoutRoot.rootElement.minContentWidth)); | |
| 143 } | |
| 144 | |
| 145 double getMaxIntrinsicWidth(BoxConstraints constraints) { | |
| 146 _layout(constraints); | |
| 147 return constraints.constrainWidth( | |
| 148 _applyFloatingPointHack(_layoutRoot.rootElement.maxContentWidth)); | |
| 149 } | |
| 150 | |
| 151 double _getIntrinsicHeight(BoxConstraints constraints) { | |
| 152 _layout(constraints); | |
| 153 return constraints.constrainHeight( | |
| 154 _applyFloatingPointHack(_layoutRoot.rootElement.height)); | |
| 155 } | |
| 156 | |
| 157 double getMinIntrinsicHeight(BoxConstraints constraints) { | |
| 158 return _getIntrinsicHeight(constraints); | |
| 159 } | |
| 160 | |
| 161 double getMaxIntrinsicHeight(BoxConstraints constraints) { | |
| 162 return _getIntrinsicHeight(constraints); | |
| 163 } | |
| 164 | |
| 165 void performLayout() { | |
| 166 _layout(constraints); | |
| 167 sky.Element root = _layoutRoot.rootElement; | |
| 168 // rootElement.width always expands to fill, use maxContentWidth instead. | |
| 169 size = constraints.constrain(new Size(_applyFloatingPointHack(root.maxConten
tWidth), | |
| 170 _applyFloatingPointHack(root.height)))
; | |
| 171 } | |
| 172 | |
| 173 void paint(RenderObjectDisplayList canvas) { | |
| 174 // Ideally we could compute the min/max intrinsic width/height with a | |
| 175 // non-destructive operation. However, currently, computing these values | |
| 176 // will destroy state inside the layout root. If that happens, we need to | |
| 177 // get back the correct state by calling _layout again. | |
| 178 // | |
| 179 // TODO(abarth): Make computing the min/max intrinsic width/height a | |
| 180 // non-destructive operation. | |
| 181 if (_constraintsForCurrentLayout != constraints && constraints != null) | |
| 182 _layout(constraints); | |
| 183 | |
| 184 if (style != null) { | |
| 185 var cssStyle = _layoutRoot.rootElement.style; | |
| 186 if (style.color != null) { | |
| 187 Color c = style.color; | |
| 188 cssStyle['color'] = | |
| 189 'rgba(${c.red}, ${c.green}, ${c.blue}, ${c.alpha / 255.0})'; | |
| 190 } | |
| 191 if (style.fontSize != null) { | |
| 192 cssStyle['font-size'] = "${style.fontSize}px"; | |
| 193 } | |
| 194 if (style.fontWeight != null) { | |
| 195 cssStyle['font-weight'] = const { | |
| 196 FontWeight.light: '300', | |
| 197 FontWeight.regular: '400', | |
| 198 FontWeight.medium: '500', | |
| 199 }[style.fontWeight]; | |
| 200 } | |
| 201 if (style.textAlign != null) { | |
| 202 cssStyle['text-align'] = const { | |
| 203 TextAlign.left: 'left', | |
| 204 TextAlign.right: 'right', | |
| 205 TextAlign.center: 'center', | |
| 206 }[style.textAlign]; | |
| 207 } | |
| 208 } | |
| 209 _layoutRoot.paint(canvas); | |
| 210 } | |
| 211 | |
| 212 // we should probably expose a way to do precise (inter-glpyh) hit testing | |
| 213 | |
| 214 String debugDescribeSettings(String prefix) { | |
| 215 String result = '${super.debugDescribeSettings(prefix)}'; | |
| 216 if (style != null) | |
| 217 result += '${prefix}style:\n' + style.toString('$prefix ') + '\n'; | |
| 218 result += '${prefix}text: ${text}\n'; | |
| 219 return result; | |
| 220 } | |
| 221 } | |
| OLD | NEW |