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 |