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 'render_node.dart'; | |
6 import 'dart:sky' as sky; | |
7 | |
8 // GENERIC BOX RENDERING | |
9 // Anything that has a concept of x, y, width, height is going to derive from th
is | |
10 | |
11 class EdgeDims { | |
12 // used for e.g. padding | |
13 const EdgeDims(this.top, this.right, this.bottom, this.left); | |
14 final double top; | |
15 final double right; | |
16 final double bottom; | |
17 final double left; | |
18 operator ==(EdgeDims other) => (top == other.top) || | |
19 (right == other.right) || | |
20 (bottom == other.bottom) || | |
21 (left == other.left); | |
22 } | |
23 | |
24 class BoxConstraints { | |
25 const BoxConstraints({ | |
26 this.minWidth: 0.0, | |
27 this.maxWidth: double.INFINITY, | |
28 this.minHeight: 0.0, | |
29 this.maxHeight: double.INFINITY}); | |
30 | |
31 BoxConstraints.tight(sky.Size size) | |
32 : minWidth = size.width, | |
33 maxWidth = size.width, | |
34 minHeight = size.height, | |
35 maxHeight = size.height; | |
36 | |
37 BoxConstraints deflate(EdgeDims edges) { | |
38 assert(edges != null); | |
39 return new BoxConstraints( | |
40 minWidth: minWidth, | |
41 maxWidth: maxWidth - (edges.left + edges.right), | |
42 minHeight: minHeight, | |
43 maxHeight: maxHeight - (edges.top + edges.bottom) | |
44 ); | |
45 } | |
46 | |
47 final double minWidth; | |
48 final double maxWidth; | |
49 final double minHeight; | |
50 final double maxHeight; | |
51 | |
52 double constrainWidth(double width) { | |
53 return clamp(min: minWidth, max: maxWidth, value: width); | |
54 } | |
55 | |
56 double constrainHeight(double height) { | |
57 return clamp(min: minHeight, max: maxHeight, value: height); | |
58 } | |
59 | |
60 sky.Size constrain(sky.Size size) { | |
61 return new sky.Size(constrainWidth(size.width), constrainHeight(size.height)
); | |
62 } | |
63 | |
64 bool get isInfinite => maxWidth >= double.INFINITY || maxHeight >= double.INFI
NITY; | |
65 } | |
66 | |
67 class BoxParentData extends ParentData { | |
68 sky.Point position = new sky.Point(0.0, 0.0); | |
69 } | |
70 | |
71 abstract class RenderBox extends RenderNode { | |
72 | |
73 void setParentData(RenderNode child) { | |
74 if (child.parentData is! BoxParentData) | |
75 child.parentData = new BoxParentData(); | |
76 } | |
77 | |
78 // override this to report what dimensions you would have if you | |
79 // were laid out with the given constraints this can walk the tree | |
80 // if it must, but it should be as cheap as possible; just get the | |
81 // dimensions and nothing else (e.g. don't calculate hypothetical | |
82 // child positions if they're not needed to determine dimensions) | |
83 sky.Size getIntrinsicDimensions(BoxConstraints constraints) { | |
84 return constraints.constrain(new sky.Size(0.0, 0.0)); | |
85 } | |
86 | |
87 BoxConstraints get constraints => super.constraints as BoxConstraints; | |
88 void performResize() { | |
89 // default behaviour for subclasses that have sizedByParent = true | |
90 size = constraints.constrain(new sky.Size(0.0, 0.0)); | |
91 assert(size.height < double.INFINITY); | |
92 assert(size.width < double.INFINITY); | |
93 } | |
94 void performLayout() { | |
95 // descendants have to either override performLayout() to set both | |
96 // width and height and lay out children, or, set sizedByParent to | |
97 // true so that performResize()'s logic above does its thing. | |
98 assert(sizedByParent); | |
99 } | |
100 | |
101 bool hitTest(HitTestResult result, { sky.Point position }) { | |
102 hitTestChildren(result, position: position); | |
103 result.add(this); | |
104 return true; | |
105 } | |
106 void hitTestChildren(HitTestResult result, { sky.Point position }) { } | |
107 | |
108 sky.Size size = new sky.Size(0.0, 0.0); | |
109 } | |
110 | |
111 abstract class RenderProxyBox extends RenderBox with RenderNodeWithChildMixin<Re
nderBox> { | |
112 RenderProxyBox(RenderBox child) { | |
113 this.child = child; | |
114 } | |
115 | |
116 sky.Size getIntrinsicDimensions(BoxConstraints constraints) { | |
117 if (child != null) | |
118 return child.getIntrinsicDimensions(constraints); | |
119 return super.getIntrinsicDimensions(constraints); | |
120 } | |
121 | |
122 void performLayout() { | |
123 if (child != null) { | |
124 child.layout(constraints, parentUsesSize: true); | |
125 size = child.size; | |
126 } else { | |
127 performResize(); | |
128 } | |
129 } | |
130 | |
131 void hitTestChildren(HitTestResult result, { sky.Point position }) { | |
132 if (child != null) | |
133 child.hitTest(result, position: position); | |
134 else | |
135 super.hitTestChildren(result, position: position); | |
136 } | |
137 | |
138 void paint(RenderNodeDisplayList canvas) { | |
139 if (child != null) | |
140 child.paint(canvas); | |
141 } | |
142 } | |
143 | |
144 class RenderSizedBox extends RenderProxyBox { | |
145 final sky.Size desiredSize; | |
146 | |
147 RenderSizedBox({ | |
148 RenderBox child, | |
149 this.desiredSize: const sky.Size.infinite() | |
150 }) : super(child); | |
151 | |
152 sky.Size getIntrinsicDimensions(BoxConstraints constraints) { | |
153 return constraints.constrain(desiredSize); | |
154 } | |
155 | |
156 void performLayout() { | |
157 size = constraints.constrain(desiredSize); | |
158 child.layout(new BoxConstraints.tight(size)); | |
159 } | |
160 } | |
161 | |
162 class RenderPadding extends RenderBox with RenderNodeWithChildMixin<RenderBox> { | |
163 | |
164 RenderPadding(EdgeDims padding, RenderBox child) { | |
165 assert(padding != null); | |
166 this.padding = padding; | |
167 this.child = child; | |
168 } | |
169 | |
170 EdgeDims _padding; | |
171 EdgeDims get padding => _padding; | |
172 void set padding (EdgeDims value) { | |
173 assert(value != null); | |
174 if (_padding != value) { | |
175 _padding = value; | |
176 markNeedsLayout(); | |
177 } | |
178 } | |
179 | |
180 sky.Size getIntrinsicDimensions(BoxConstraints constraints) { | |
181 assert(padding != null); | |
182 constraints = constraints.deflate(padding); | |
183 if (child == null) | |
184 return super.getIntrinsicDimensions(constraints); | |
185 return child.getIntrinsicDimensions(constraints); | |
186 } | |
187 | |
188 void performLayout() { | |
189 assert(padding != null); | |
190 BoxConstraints innerConstraints = constraints.deflate(padding); | |
191 if (child == null) { | |
192 size = innerConstraints.constrain( | |
193 new sky.Size(padding.left + padding.right, padding.top + padding.botto
m)); | |
194 return; | |
195 } | |
196 child.layout(innerConstraints, parentUsesSize: true); | |
197 assert(child.parentData is BoxParentData); | |
198 child.parentData.position = new sky.Point(padding.left, padding.top); | |
199 size = constraints.constrain(new sky.Size(padding.left + child.size.width +
padding.right, | |
200 padding.top + child.size.height +
padding.bottom)); | |
201 } | |
202 | |
203 void paint(RenderNodeDisplayList canvas) { | |
204 if (child != null) | |
205 canvas.paintChild(child, child.parentData.position); | |
206 } | |
207 | |
208 void hitTestChildren(HitTestResult result, { sky.Point position }) { | |
209 if (child != null) { | |
210 assert(child.parentData is BoxParentData); | |
211 sky.Rect childBounds = new sky.Rect.fromPointAndSize(child.parentData.posi
tion, child.size); | |
212 if (childBounds.contains(position)) { | |
213 child.hitTest(result, position: new sky.Point(position.x - child.parentD
ata.position.x, | |
214 position.y - child.parentD
ata.position.y)); | |
215 } | |
216 } | |
217 } | |
218 | |
219 } | |
220 | |
221 // This must be immutable, because we won't notice when it changes | |
222 class BoxDecoration { | |
223 // TODO(mpcomplete): go through and change the users of this class to pass | |
224 // a Color object. | |
225 BoxDecoration({ | |
226 backgroundColor | |
227 }) : backgroundColor = new sky.Color(backgroundColor); | |
228 | |
229 final sky.Color backgroundColor; | |
230 } | |
231 | |
232 class RenderDecoratedBox extends RenderProxyBox { | |
233 | |
234 RenderDecoratedBox({ | |
235 BoxDecoration decoration, | |
236 RenderBox child | |
237 }) : _decoration = decoration, super(child); | |
238 | |
239 BoxDecoration _decoration; | |
240 BoxDecoration get decoration => _decoration; | |
241 void set decoration (BoxDecoration value) { | |
242 if (value == _decoration) | |
243 return; | |
244 _decoration = value; | |
245 markNeedsPaint(); | |
246 } | |
247 | |
248 void paint(RenderNodeDisplayList canvas) { | |
249 assert(size.width != null); | |
250 assert(size.height != null); | |
251 | |
252 if (_decoration == null) | |
253 return; | |
254 | |
255 if (_decoration.backgroundColor != null) { | |
256 sky.Paint paint = new sky.Paint()..color = _decoration.backgroundColor; | |
257 canvas.drawRect(new sky.Rect.fromLTRB(0.0, 0.0, size.width, size.height),
paint); | |
258 } | |
259 super.paint(canvas); | |
260 } | |
261 | |
262 } | |
263 | |
264 | |
265 // RENDER VIEW LAYOUT MANAGER | |
266 | |
267 class ViewConstraints { | |
268 | |
269 const ViewConstraints({ | |
270 this.width: 0.0, this.height: 0.0, this.orientation: null | |
271 }); | |
272 | |
273 final double width; | |
274 final double height; | |
275 final int orientation; | |
276 | |
277 } | |
278 | |
279 class RenderView extends RenderNode with RenderNodeWithChildMixin<RenderBox> { | |
280 | |
281 RenderView({ | |
282 RenderBox child, | |
283 this.timeForRotation: const Duration(microseconds: 83333) | |
284 }) { | |
285 this.child = child; | |
286 } | |
287 | |
288 sky.Size _size = new sky.Size(0.0, 0.0); | |
289 double get width => _size.width; | |
290 double get height => _size.height; | |
291 | |
292 int _orientation; // 0..3 | |
293 int get orientation => _orientation; | |
294 Duration timeForRotation; | |
295 | |
296 ViewConstraints get constraints => super.constraints as ViewConstraints; | |
297 bool get sizedByParent => true; | |
298 void performResize() { | |
299 if (constraints.orientation != _orientation) { | |
300 if (_orientation != null && child != null) | |
301 child.rotate(oldAngle: _orientation, newAngle: constraints.orientation,
time: timeForRotation); | |
302 _orientation = constraints.orientation; | |
303 } | |
304 _size = new sky.Size(constraints.width, constraints.height); | |
305 assert(_size.height < double.INFINITY); | |
306 assert(_size.width < double.INFINITY); | |
307 } | |
308 void performLayout() { | |
309 if (child != null) { | |
310 child.layout(new BoxConstraints.tight(_size)); | |
311 assert(child.size.width == width); | |
312 assert(child.size.height == height); | |
313 } | |
314 } | |
315 | |
316 void rotate({ int oldAngle, int newAngle, Duration time }) { | |
317 assert(false); // nobody tells the screen to rotate, the whole rotate() danc
e is started from our performResize() | |
318 } | |
319 | |
320 bool hitTest(HitTestResult result, { sky.Point position }) { | |
321 if (child != null) { | |
322 sky.Rect childBounds = new sky.Rect.fromSize(child.size); | |
323 if (childBounds.contains(position)) | |
324 child.hitTest(result, position: position); | |
325 } | |
326 result.add(this); | |
327 return true; | |
328 } | |
329 | |
330 void paint(RenderNodeDisplayList canvas) { | |
331 if (child != null) | |
332 canvas.paintChild(child, new sky.Point(0.0, 0.0)); | |
333 } | |
334 | |
335 void paintFrame() { | |
336 RenderNode.debugDoingPaint = true; | |
337 var canvas = new RenderNodeDisplayList(sky.view.width, sky.view.height); | |
338 paint(canvas); | |
339 sky.view.picture = canvas.endRecording(); | |
340 RenderNode.debugDoingPaint = false; | |
341 } | |
342 | |
343 } | |
344 | |
345 // DEFAULT BEHAVIORS FOR RENDERBOX CONTAINERS | |
346 abstract class RenderBoxContainerDefaultsMixin<ChildType extends RenderBox, Pare
ntDataType extends ContainerParentDataMixin<ChildType>> implements ContainerRend
erNodeMixin<ChildType, ParentDataType> { | |
347 | |
348 void defaultHitTestChildren(HitTestResult result, { sky.Point position }) { | |
349 // the x, y parameters have the top left of the node's box as the origin | |
350 ChildType child = lastChild; | |
351 while (child != null) { | |
352 assert(child.parentData is ParentDataType); | |
353 sky.Rect childBounds = new sky.Rect.fromPointAndSize(child.parentData.posi
tion, child.size); | |
354 if (childBounds.contains(position)) { | |
355 if (child.hitTest(result, position: new sky.Point(position.x - child.par
entData.position.x, | |
356 position.y - child.par
entData.position.y))) | |
357 break; | |
358 } | |
359 child = child.parentData.previousSibling; | |
360 } | |
361 } | |
362 | |
363 void defaultPaint(RenderNodeDisplayList canvas) { | |
364 RenderBox child = firstChild; | |
365 while (child != null) { | |
366 assert(child.parentData is ParentDataType); | |
367 canvas.paintChild(child, child.parentData.position); | |
368 child = child.parentData.nextSibling; | |
369 } | |
370 } | |
371 } | |
OLD | NEW |